diff --git a/bca-token-app/src/lib/wallet.ts b/bca-token-app/src/lib/wallet.ts index 4199f20..9be9d56 100644 --- a/bca-token-app/src/lib/wallet.ts +++ b/bca-token-app/src/lib/wallet.ts @@ -56,7 +56,7 @@ export async function get_wallet_balance(wallet: WalletInformation, ev: any): Pr // await contract.methods.decimals().call().then(console.log); // const decimals = await contract.methods.decimals().call(); // const decimals = 10; - console.log("decimals = " + decimals) + // console.log("decimals = " + decimals) wallet.walletbalance = Number(await window.web3.eth.getBalance(wallet.walletaddr)); if (!!wallet.walletbalance) { wallet.walletbalance = wallet.walletbalance / (10 ** decimals) } wallet.warning = undefined @@ -85,8 +85,8 @@ export async function get_wallet_addr(wallet: WalletInformation, ev: any): Promi wallet.walletaddr = selectedAccount; wallet.warning = undefined - console.log("address: " + wallet.walletaddr) - console.log("network: " + wallet.walletnetwork) + // console.log("address: " + wallet.walletaddr) + // console.log("network: " + wallet.walletnetwork) } catch (error) { wallet.warning = "error while accessing wallet: " + error; diff --git a/bca-token-market/.gitignore b/bca-token-market/.gitignore index b81ebcb..a5bfebc 100644 --- a/bca-token-market/.gitignore +++ b/bca-token-market/.gitignore @@ -22,3 +22,4 @@ node_modules package-lock.json prisma/migrations +src/lib/bca_*-abi.json diff --git a/bca-token-market/src/lib/bca_service-abi.json b/bca-token-market/src/lib/bca_service-abi.json deleted file mode 100644 index d3f23ad..0000000 --- a/bca-token-market/src/lib/bca_service-abi.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "BCAServiceContract", - "sourceName": "contracts/BCA_Service.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "address", - "name": "_providerAddress", - "type": "address" - }, - { - "internalType": "address", - "name": "_tokAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "_dayPrice", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AlreadyStopped", - "type": "error" - }, - { - "inputs": [], - "name": "AlreadySubscribed", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "minimumAmount", - "type": "uint256" - } - ], - "name": "InsufficientAmount", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "availableBalance", - "type": "uint256" - } - ], - "name": "InsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "NotStarted", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "inputs": [], - "name": "UnAuthorized", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "DepositMade", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Retracted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "ticks", - "type": "uint256" - } - ], - "name": "ServiceStopped", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "recipient", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Withdrawn", - "type": "event" - }, - { - "inputs": [], - "name": "balanceProvider", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "balanceUser", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "dayPrice", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "deposit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "endTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "makeDeposit", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "providerAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "retracted", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "setupFee", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "startTime", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "stop", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "tokToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "userAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawProvider", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawUser", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x60e060405234801561001057600080fd5b5060405161100438038061100483398101604081905261002f9161006a565b60016000556001600160a01b0391821660805260a0521660c0526100a6565b80516001600160a01b038116811461006557600080fd5b919050565b60008060006060848603121561007f57600080fd5b6100888461004e565b92506100966020850161004e565b9150604084015190509250925092565b60805160a05160c051610edb6101296000396000818161014001528181610493015281816105b30152610af801526000818161017f015281816102ae015281816102f30152818161052b0152818161062b0152818161080a01526109bd01526000818161021101528181610395015281816106b601526109040152610edb6000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80639cf81fb311610097578063d39f075b11610066578063d39f075b146101dd578063dab70e3c146101e6578063e4128fb3146101f9578063ff1f7f0c1461020c57600080fd5b80639cf81fb3146101aa578063a40b56b9146101bd578063d0e30db0146101cc578063d25120ce146101d557600080fd5b80633197cbb6116100d35780633197cbb614610132578063706e52571461013b578063742c9c8f1461017a57806378e97925146101a157600080fd5b8063059a500c146100fa57806307da68f51461010f578063229edc9e14610117575b600080fd5b61010d610108366004610dc6565b610233565b005b61010d61047b565b61011f610486565b6040519081526020015b60405180910390f35b61011f60045481565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610129565b61011f7f000000000000000000000000000000000000000000000000000000000000000081565b61011f60035481565b61010d6101b8366004610dc6565b6105a0565b61011f6702c68af0bb14000081565b61011f60015481565b61011f610770565b61011f60025481565b61010d6101f4366004610dc6565b61086e565b600554610162906001600160a01b031681565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b61023b610a0e565b6005546001600160a01b0316158061025d57506005546001600160a01b031633145b610293576040517f5fd8a13200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b03161580156102e757506102d260187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6102e4906702c68af0bb140000610e17565b81105b156103675761031760187f0000000000000000000000000000000000000000000000000000000000000000610df5565b610329906702c68af0bb140000610e17565b6040517f77b8dde300000000000000000000000000000000000000000000000000000000815260040161035e91815260200190565b60405180910390fd5b60045415610388576040516307ccaad360e21b815260040160405180910390fd5b6103bd6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084610a51565b80600160008282546103cf9190610e17565b90915550506005546001600160a01b031661043557600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905542600355600180546702c68af0bb140000919060009061042f908490610e2a565b90915550505b60408051338152602081018390527fd15c9547ea5c06670c0010ce19bc32d54682a4b3801ece7f3ab0c3f17106b4bb910160405180910390a16104786001600055565b50565b61048433610adf565b565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104d15760405163be24598360e01b815260040160405180910390fd5b6003546000036104f457604051636f312cbd60e01b815260040160405180910390fd5b6000600454600003610506574261050a565b6004545b905060006003548261051c9190610e2a565b9050600080610e1060186105507f000000000000000000000000000000000000000000000000000000000000000086610e3d565b61055a9190610df5565b6105649190610df5565b90506002546001546105769190610e2a565b8111156105945760025460015461058d9190610e2a565b9150610598565b8091505b509392505050565b6105a8610a0e565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105f15760405163be24598360e01b815260040160405180910390fd5b60006105fb610486565b90508181101561062157604051639266535160e01b81526004810182905260240161035e565b610e1061064f60187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6106599190610df5565b8260025460015461066a9190610e2a565b6106749190610e2a565b10156106835761068333610adf565b81600260008282546106959190610e17565b909155505060405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072b9190610e54565b5060408051338152602081018490527fbeab809f862bcac7e41d0cc40843a6c895216194f5e2ec16d9b8e41de89d2794910160405180910390a1506104786001600055565b6005546000906001600160a01b0316158061078b5750600354155b156107a957604051636f312cbd60e01b815260040160405180910390fd5b6005546001600160a01b031633146107d45760405163be24598360e01b815260040160405180910390fd5b60006004546000036107e657426107ea565b6004545b90506000600354826107fc9190610e2a565b90506000610e10601861082f7f000000000000000000000000000000000000000000000000000000000000000085610e3d565b6108399190610df5565b6108439190610df5565b90506001548110610858576000935050505090565b806001546108669190610e2a565b935050505090565b610876610a0e565b6005546001600160a01b031633146108a15760405163be24598360e01b815260040160405180910390fd5b60006108ab610770565b9050818110156108d157604051639266535160e01b81526004810182905260240161035e565b81600160008282546108e39190610e2a565b909155505060405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610955573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109799190610e54565b5060408051338152602081018490527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5910160405180910390a1610e106109e160187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6109eb9190610df5565b6109f58383610e2a565b11610a0357610a0333610adf565b506104786001600055565b600260005403610a4a576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052610ad9908590610bbf565b50505050565b6005546001600160a01b0382811691161480610b2c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b610b495760405163be24598360e01b815260040160405180910390fd5b60045415610b6a576040516307ccaad360e21b815260040160405180910390fd5b426004819055600354600091610b809190610e2a565b90507f65cd0e12bdc2afa1e1eba1853832b1c67e6318096add2681d6e020a7ed1ce8a181604051610bb391815260200190565b60405180910390a15050565b6000610bd46001600160a01b03841683610c40565b90508051600014158015610bf9575080806020019051810190610bf79190610e54565b155b15610c3b576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161035e565b505050565b6060610c4e83836000610c57565b90505b92915050565b606081471015610c95576040517fcd78605900000000000000000000000000000000000000000000000000000000815230600482015260240161035e565b600080856001600160a01b03168486604051610cb19190610e76565b60006040518083038185875af1925050503d8060008114610cee576040519150601f19603f3d011682016040523d82523d6000602084013e610cf3565b606091505b5091509150610d03868383610d0f565b925050505b9392505050565b606082610d2457610d1f82610d84565b610d08565b8151158015610d3b57506001600160a01b0384163b155b15610d7d576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260240161035e565b5080610d08565b805115610d945780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060208284031215610dd857600080fd5b5035919050565b634e487b7160e01b600052601160045260246000fd5b600082610e1257634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610c5157610c51610ddf565b81810381811115610c5157610c51610ddf565b8082028115828204841417610c5157610c51610ddf565b600060208284031215610e6657600080fd5b81518015158114610d0857600080fd5b6000825160005b81811015610e975760208186018101518583015201610e7d565b50600092019182525091905056fea26469706673582212205b832f12834ce20b67b8df19d4bfafd4de31be0ae9e7949e55ccfe242216dbf664736f6c63430008180033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80639cf81fb311610097578063d39f075b11610066578063d39f075b146101dd578063dab70e3c146101e6578063e4128fb3146101f9578063ff1f7f0c1461020c57600080fd5b80639cf81fb3146101aa578063a40b56b9146101bd578063d0e30db0146101cc578063d25120ce146101d557600080fd5b80633197cbb6116100d35780633197cbb614610132578063706e52571461013b578063742c9c8f1461017a57806378e97925146101a157600080fd5b8063059a500c146100fa57806307da68f51461010f578063229edc9e14610117575b600080fd5b61010d610108366004610dc6565b610233565b005b61010d61047b565b61011f610486565b6040519081526020015b60405180910390f35b61011f60045481565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610129565b61011f7f000000000000000000000000000000000000000000000000000000000000000081565b61011f60035481565b61010d6101b8366004610dc6565b6105a0565b61011f6702c68af0bb14000081565b61011f60015481565b61011f610770565b61011f60025481565b61010d6101f4366004610dc6565b61086e565b600554610162906001600160a01b031681565b6101627f000000000000000000000000000000000000000000000000000000000000000081565b61023b610a0e565b6005546001600160a01b0316158061025d57506005546001600160a01b031633145b610293576040517f5fd8a13200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6005546001600160a01b03161580156102e757506102d260187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6102e4906702c68af0bb140000610e17565b81105b156103675761031760187f0000000000000000000000000000000000000000000000000000000000000000610df5565b610329906702c68af0bb140000610e17565b6040517f77b8dde300000000000000000000000000000000000000000000000000000000815260040161035e91815260200190565b60405180910390fd5b60045415610388576040516307ccaad360e21b815260040160405180910390fd5b6103bd6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084610a51565b80600160008282546103cf9190610e17565b90915550506005546001600160a01b031661043557600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000163317905542600355600180546702c68af0bb140000919060009061042f908490610e2a565b90915550505b60408051338152602081018390527fd15c9547ea5c06670c0010ce19bc32d54682a4b3801ece7f3ab0c3f17106b4bb910160405180910390a16104786001600055565b50565b61048433610adf565b565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104d15760405163be24598360e01b815260040160405180910390fd5b6003546000036104f457604051636f312cbd60e01b815260040160405180910390fd5b6000600454600003610506574261050a565b6004545b905060006003548261051c9190610e2a565b9050600080610e1060186105507f000000000000000000000000000000000000000000000000000000000000000086610e3d565b61055a9190610df5565b6105649190610df5565b90506002546001546105769190610e2a565b8111156105945760025460015461058d9190610e2a565b9150610598565b8091505b509392505050565b6105a8610a0e565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105f15760405163be24598360e01b815260040160405180910390fd5b60006105fb610486565b90508181101561062157604051639266535160e01b81526004810182905260240161035e565b610e1061064f60187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6106599190610df5565b8260025460015461066a9190610e2a565b6106749190610e2a565b10156106835761068333610adf565b81600260008282546106959190610e17565b909155505060405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610707573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061072b9190610e54565b5060408051338152602081018490527fbeab809f862bcac7e41d0cc40843a6c895216194f5e2ec16d9b8e41de89d2794910160405180910390a1506104786001600055565b6005546000906001600160a01b0316158061078b5750600354155b156107a957604051636f312cbd60e01b815260040160405180910390fd5b6005546001600160a01b031633146107d45760405163be24598360e01b815260040160405180910390fd5b60006004546000036107e657426107ea565b6004545b90506000600354826107fc9190610e2a565b90506000610e10601861082f7f000000000000000000000000000000000000000000000000000000000000000085610e3d565b6108399190610df5565b6108439190610df5565b90506001548110610858576000935050505090565b806001546108669190610e2a565b935050505090565b610876610a0e565b6005546001600160a01b031633146108a15760405163be24598360e01b815260040160405180910390fd5b60006108ab610770565b9050818110156108d157604051639266535160e01b81526004810182905260240161035e565b81600160008282546108e39190610e2a565b909155505060405163a9059cbb60e01b8152336004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af1158015610955573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109799190610e54565b5060408051338152602081018490527f7084f5476618d8e60b11ef0d7d3f06914655adb8793e28ff7f018d4c76d505d5910160405180910390a1610e106109e160187f0000000000000000000000000000000000000000000000000000000000000000610df5565b6109eb9190610df5565b6109f58383610e2a565b11610a0357610a0333610adf565b506104786001600055565b600260005403610a4a576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600055565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000179052610ad9908590610bbf565b50505050565b6005546001600160a01b0382811691161480610b2c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b610b495760405163be24598360e01b815260040160405180910390fd5b60045415610b6a576040516307ccaad360e21b815260040160405180910390fd5b426004819055600354600091610b809190610e2a565b90507f65cd0e12bdc2afa1e1eba1853832b1c67e6318096add2681d6e020a7ed1ce8a181604051610bb391815260200190565b60405180910390a15050565b6000610bd46001600160a01b03841683610c40565b90508051600014158015610bf9575080806020019051810190610bf79190610e54565b155b15610c3b576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161035e565b505050565b6060610c4e83836000610c57565b90505b92915050565b606081471015610c95576040517fcd78605900000000000000000000000000000000000000000000000000000000815230600482015260240161035e565b600080856001600160a01b03168486604051610cb19190610e76565b60006040518083038185875af1925050503d8060008114610cee576040519150601f19603f3d011682016040523d82523d6000602084013e610cf3565b606091505b5091509150610d03868383610d0f565b925050505b9392505050565b606082610d2457610d1f82610d84565b610d08565b8151158015610d3b57506001600160a01b0384163b155b15610d7d576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260240161035e565b5080610d08565b805115610d945780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060208284031215610dd857600080fd5b5035919050565b634e487b7160e01b600052601160045260246000fd5b600082610e1257634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610c5157610c51610ddf565b81810381811115610c5157610c51610ddf565b8082028115828204841417610c5157610c51610ddf565b600060208284031215610e6657600080fd5b81518015158114610d0857600080fd5b6000825160005b81811015610e975760208186018101518583015201610e7d565b50600092019182525091905056fea26469706673582212205b832f12834ce20b67b8df19d4bfafd4de31be0ae9e7949e55ccfe242216dbf664736f6c63430008180033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/bca-token-market/src/lib/bca_token-abi.json b/bca-token-market/src/lib/bca_token-abi.json deleted file mode 100644 index 0ad48c5..0000000 --- a/bca-token-market/src/lib/bca_token-abi.json +++ /dev/null @@ -1,710 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "BCAServiceToken", - "sourceName": "contracts/BCA_ERC20_nf.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - }, - { - "internalType": "address", - "name": "minter", - "type": "address" - }, - { - "internalType": "address", - "name": "burner", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "AccessControlBadConfirmation", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "neededRole", - "type": "bytes32" - } - ], - "name": "AccessControlUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "inputs": [], - "name": "BURNER_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MINTER_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "burnerAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "minterAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "callerConfirmation", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "serviceAddress", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "new_burnerAddress", - "type": "address" - } - ], - "name": "setBurnerAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "new_minterAddress", - "type": "address" - } - ], - "name": "setMinterAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "new_serviceAddress", - "type": "address" - } - ], - "name": "setServiceAddress", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - } - ], - "bytecode": "0x60806040523480156200001157600080fd5b50604051620015353803806200153583398101604081905262000034916200029c565b83836003620000448382620003bc565b506004620000538282620003bc565b505060068054336001600160a01b031991821681179092556007805482166001600160a01b0387811691909117909155600880549092169085161790556200009f915060009062000104565b50620000cc7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68362000104565b50620000f97f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8488262000104565b505050505062000488565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16620001ad5760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055620001643390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001620001b1565b5060005b92915050565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001df57600080fd5b81516001600160401b0380821115620001fc57620001fc620001b7565b604051601f8301601f19908116603f01168101908282118183101715620002275762000227620001b7565b81604052838152602092508660208588010111156200024557600080fd5b600091505b838210156200026957858201830151818301840152908201906200024a565b6000602085830101528094505050505092915050565b80516001600160a01b03811681146200029757600080fd5b919050565b60008060008060808587031215620002b357600080fd5b84516001600160401b0380821115620002cb57600080fd5b620002d988838901620001cd565b95506020870151915080821115620002f057600080fd5b50620002ff87828801620001cd565b93505062000310604086016200027f565b915062000320606086016200027f565b905092959194509250565b600181811c908216806200034057607f821691505b6020821081036200036157634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620003b7576000816000526020600020601f850160051c81016020861015620003925750805b601f850160051c820191505b81811015620003b3578281556001016200039e565b5050505b505050565b81516001600160401b03811115620003d857620003d8620001b7565b620003f081620003e984546200032b565b8462000367565b602080601f8311600181146200042857600084156200040f5750858301515b600019600386901b1c1916600185901b178555620003b3565b600085815260208120601f198616915b82811015620004595788860151825594840194600190910190840162000438565b5085821015620004785787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61109d80620004986000396000f3fe608060405234801561001057600080fd5b50600436106101ae5760003560e01c80638d5b87b6116100ee578063a3106b9511610097578063d547741f11610071578063d547741f146103df578063dd62ed3e146103f2578063e6293e231461042b578063f6e396401461043e57600080fd5b8063a3106b9514610392578063a9059cbb146103a5578063d5391393146103b857600080fd5b80639dc29fac116100c85780639dc29fac146103645780639df806d614610377578063a217fddf1461038a57600080fd5b80638d5b87b61461031057806391d148541461032357806395d89b411461035c57600080fd5b8063282c51f31161015b57806334d722c91161013557806334d722c91461029657806336568abe146102c157806340c10f19146102d457806370a08231146102e757600080fd5b8063282c51f31461024b5780632f2ff15d14610272578063313ce5671461028757600080fd5b806318160ddd1161018c57806318160ddd1461020357806323b872dd14610215578063248a9ca31461022857600080fd5b806301ffc9a7146101b357806306fdde03146101db578063095ea7b3146101f0575b600080fd5b6101c66101c1366004610e68565b610451565b60405190151581526020015b60405180910390f35b6101e36104ea565b6040516101d29190610eb1565b6101c66101fe366004610f1c565b61057c565b6002545b6040519081526020016101d2565b6101c6610223366004610f46565b610594565b610207610236366004610f82565b60009081526005602052604090206001015490565b6102077f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84881565b610285610280366004610f9b565b6105b8565b005b604051601281526020016101d2565b6007546102a9906001600160a01b031681565b6040516001600160a01b0390911681526020016101d2565b6102856102cf366004610f9b565b6105e3565b6102856102e2366004610f1c565b610634565b6102076102f5366004610fc7565b6001600160a01b031660009081526020819052604090205490565b6006546102a9906001600160a01b031681565b6101c6610331366004610f9b565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6101e3610668565b610285610372366004610f1c565b610677565b610285610385366004610fc7565b61074d565b610207600081565b6102856103a0366004610fc7565b6107e2565b6101c66103b3366004610f1c565b610877565b6102077f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6102856103ed366004610f9b565b610885565b610207610400366004610fe2565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6008546102a9906001600160a01b031681565b61028561044c366004610fc7565b6108aa565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806104e457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6060600380546104f99061100c565b80601f01602080910402602001604051908101604052809291908181526020018280546105259061100c565b80156105725780601f1061054757610100808354040283529160200191610572565b820191906000526020600020905b81548152906001019060200180831161055557829003601f168201915b5050505050905090565b60003361058a818585610901565b5060019392505050565b6000336105a285828561090e565b6105ad85858561099f565b506001949350505050565b6000828152600560205260409020600101546105d3816109fe565b6105dd8383610a0b565b50505050565b6001600160a01b0381163314610625576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61062f8282610ab9565b505050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a661065e816109fe565b61062f8383610b40565b6060600480546104f99061100c565b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8486106a1816109fe565b6006546001600160a01b03848116911614610743576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4275726e696e67206f6e6c7920616c6c6f776564206f6e20736572766963652060448201527f6163636f756e740000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61062f8383610b7a565b6000610758816109fe565b60085461078f907f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848906001600160a01b0316610ab9565b506008805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84883610a0b565b60006107ed816109fe565b600754610824907f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6906001600160a01b0316610ab9565b506007805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a683610a0b565b60003361058a81858561099f565b6000828152600560205260409020600101546108a0816109fe565b6105dd8383610ab9565b60006108b5816109fe565b6006546108cd906000906001600160a01b0316610ab9565b506006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f600083610a0b565b61062f8383836001610bb0565b6001600160a01b0383811660009081526001602090815260408083209386168352929052205460001981146105dd5781811015610990576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526044810183905260640161073a565b6105dd84848484036000610bb0565b6001600160a01b0383166109c957604051634b637e8f60e11b81526000600482015260240161073a565b6001600160a01b0382166109f35760405163ec442f0560e01b81526000600482015260240161073a565b61062f838383610cb7565b610a088133610dfa565b50565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16610ab15760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055610a693390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104e4565b5060006104e4565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff1615610ab15760008381526005602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104e4565b6001600160a01b038216610b6a5760405163ec442f0560e01b81526000600482015260240161073a565b610b7660008383610cb7565b5050565b6001600160a01b038216610ba457604051634b637e8f60e11b81526000600482015260240161073a565b610b7682600083610cb7565b6001600160a01b038416610bf3576040517fe602df050000000000000000000000000000000000000000000000000000000081526000600482015260240161073a565b6001600160a01b038316610c36576040517f94280d620000000000000000000000000000000000000000000000000000000081526000600482015260240161073a565b6001600160a01b03808516600090815260016020908152604080832093871683529290522082905580156105dd57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610ca991815260200190565b60405180910390a350505050565b6001600160a01b038316610ce2578060026000828254610cd79190611046565b90915550610d6d9050565b6001600160a01b03831660009081526020819052604090205481811015610d4e576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526044810183905260640161073a565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b038216610d8957600280548290039055610da8565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610ded91815260200190565b60405180910390a3505050565b60008281526005602090815260408083206001600160a01b038516845290915290205460ff16610b76576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024810183905260440161073a565b600060208284031215610e7a57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610eaa57600080fd5b9392505050565b60006020808352835180602085015260005b81811015610edf57858101830151858201604001528201610ec3565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610f1757600080fd5b919050565b60008060408385031215610f2f57600080fd5b610f3883610f00565b946020939093013593505050565b600080600060608486031215610f5b57600080fd5b610f6484610f00565b9250610f7260208501610f00565b9150604084013590509250925092565b600060208284031215610f9457600080fd5b5035919050565b60008060408385031215610fae57600080fd5b82359150610fbe60208401610f00565b90509250929050565b600060208284031215610fd957600080fd5b610eaa82610f00565b60008060408385031215610ff557600080fd5b610ffe83610f00565b9150610fbe60208401610f00565b600181811c9082168061102057607f821691505b60208210810361104057634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156104e457634e487b7160e01b600052601160045260246000fdfea2646970667358221220dfc176463da83bc79bdb555993d34677afd1137fdd7c0faff5bed07ae3fc4e9f64736f6c63430008180033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101ae5760003560e01c80638d5b87b6116100ee578063a3106b9511610097578063d547741f11610071578063d547741f146103df578063dd62ed3e146103f2578063e6293e231461042b578063f6e396401461043e57600080fd5b8063a3106b9514610392578063a9059cbb146103a5578063d5391393146103b857600080fd5b80639dc29fac116100c85780639dc29fac146103645780639df806d614610377578063a217fddf1461038a57600080fd5b80638d5b87b61461031057806391d148541461032357806395d89b411461035c57600080fd5b8063282c51f31161015b57806334d722c91161013557806334d722c91461029657806336568abe146102c157806340c10f19146102d457806370a08231146102e757600080fd5b8063282c51f31461024b5780632f2ff15d14610272578063313ce5671461028757600080fd5b806318160ddd1161018c57806318160ddd1461020357806323b872dd14610215578063248a9ca31461022857600080fd5b806301ffc9a7146101b357806306fdde03146101db578063095ea7b3146101f0575b600080fd5b6101c66101c1366004610e68565b610451565b60405190151581526020015b60405180910390f35b6101e36104ea565b6040516101d29190610eb1565b6101c66101fe366004610f1c565b61057c565b6002545b6040519081526020016101d2565b6101c6610223366004610f46565b610594565b610207610236366004610f82565b60009081526005602052604090206001015490565b6102077f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84881565b610285610280366004610f9b565b6105b8565b005b604051601281526020016101d2565b6007546102a9906001600160a01b031681565b6040516001600160a01b0390911681526020016101d2565b6102856102cf366004610f9b565b6105e3565b6102856102e2366004610f1c565b610634565b6102076102f5366004610fc7565b6001600160a01b031660009081526020819052604090205490565b6006546102a9906001600160a01b031681565b6101c6610331366004610f9b565b60009182526005602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6101e3610668565b610285610372366004610f1c565b610677565b610285610385366004610fc7565b61074d565b610207600081565b6102856103a0366004610fc7565b6107e2565b6101c66103b3366004610f1c565b610877565b6102077f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a681565b6102856103ed366004610f9b565b610885565b610207610400366004610fe2565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6008546102a9906001600160a01b031681565b61028561044c366004610fc7565b6108aa565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806104e457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b6060600380546104f99061100c565b80601f01602080910402602001604051908101604052809291908181526020018280546105259061100c565b80156105725780601f1061054757610100808354040283529160200191610572565b820191906000526020600020905b81548152906001019060200180831161055557829003601f168201915b5050505050905090565b60003361058a818585610901565b5060019392505050565b6000336105a285828561090e565b6105ad85858561099f565b506001949350505050565b6000828152600560205260409020600101546105d3816109fe565b6105dd8383610a0b565b50505050565b6001600160a01b0381163314610625576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61062f8282610ab9565b505050565b7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a661065e816109fe565b61062f8383610b40565b6060600480546104f99061100c565b7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a8486106a1816109fe565b6006546001600160a01b03848116911614610743576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4275726e696e67206f6e6c7920616c6c6f776564206f6e20736572766963652060448201527f6163636f756e740000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61062f8383610b7a565b6000610758816109fe565b60085461078f907f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a848906001600160a01b0316610ab9565b506008805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f7f3c11d16cbaffd01df69ce1c404f6340ee057498f5f00246190ea54220576a84883610a0b565b60006107ed816109fe565b600754610824907f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6906001600160a01b0316610ab9565b506007805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f7f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a683610a0b565b60003361058a81858561099f565b6000828152600560205260409020600101546108a0816109fe565b6105dd8383610ab9565b60006108b5816109fe565b6006546108cd906000906001600160a01b0316610ab9565b506006805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03841617905561062f600083610a0b565b61062f8383836001610bb0565b6001600160a01b0383811660009081526001602090815260408083209386168352929052205460001981146105dd5781811015610990576040517ffb8f41b20000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602481018290526044810183905260640161073a565b6105dd84848484036000610bb0565b6001600160a01b0383166109c957604051634b637e8f60e11b81526000600482015260240161073a565b6001600160a01b0382166109f35760405163ec442f0560e01b81526000600482015260240161073a565b61062f838383610cb7565b610a088133610dfa565b50565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff16610ab15760008381526005602090815260408083206001600160a01b03861684529091529020805460ff19166001179055610a693390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016104e4565b5060006104e4565b60008281526005602090815260408083206001600160a01b038516845290915281205460ff1615610ab15760008381526005602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016104e4565b6001600160a01b038216610b6a5760405163ec442f0560e01b81526000600482015260240161073a565b610b7660008383610cb7565b5050565b6001600160a01b038216610ba457604051634b637e8f60e11b81526000600482015260240161073a565b610b7682600083610cb7565b6001600160a01b038416610bf3576040517fe602df050000000000000000000000000000000000000000000000000000000081526000600482015260240161073a565b6001600160a01b038316610c36576040517f94280d620000000000000000000000000000000000000000000000000000000081526000600482015260240161073a565b6001600160a01b03808516600090815260016020908152604080832093871683529290522082905580156105dd57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610ca991815260200190565b60405180910390a350505050565b6001600160a01b038316610ce2578060026000828254610cd79190611046565b90915550610d6d9050565b6001600160a01b03831660009081526020819052604090205481811015610d4e576040517fe450d38c0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602481018290526044810183905260640161073a565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b038216610d8957600280548290039055610da8565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610ded91815260200190565b60405180910390a3505050565b60008281526005602090815260408083206001600160a01b038516845290915290205460ff16610b76576040517fe2517d3f0000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024810183905260440161073a565b600060208284031215610e7a57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610eaa57600080fd5b9392505050565b60006020808352835180602085015260005b81811015610edf57858101830151858201604001528201610ec3565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b0381168114610f1757600080fd5b919050565b60008060408385031215610f2f57600080fd5b610f3883610f00565b946020939093013593505050565b600080600060608486031215610f5b57600080fd5b610f6484610f00565b9250610f7260208501610f00565b9150604084013590509250925092565b600060208284031215610f9457600080fd5b5035919050565b60008060408385031215610fae57600080fd5b82359150610fbe60208401610f00565b90509250929050565b600060208284031215610fd957600080fd5b610eaa82610f00565b60008060408385031215610ff557600080fd5b610ffe83610f00565b9150610fbe60208401610f00565b600181811c9082168061102057607f821691505b60208210810361104057634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156104e457634e487b7160e01b600052601160045260246000fdfea2646970667358221220dfc176463da83bc79bdb555993d34677afd1137fdd7c0faff5bed07ae3fc4e9f64736f6c63430008180033", - "linkReferences": {}, - "deployedLinkReferences": {} -} \ No newline at end of file diff --git a/bca-token-market/src/lib/contracts.ts b/bca-token-market/src/lib/contracts.ts index ab7f6d6..2dd1678 100644 --- a/bca-token-market/src/lib/contracts.ts +++ b/bca-token-market/src/lib/contracts.ts @@ -1,14 +1,57 @@ // // local hardhat node - testnet import deployed_address from "$lib/../../../bca-token-solidity/ignition/deployments/chain-31337/deployed_addresses.json" +// import abi_token_json from "$lib/../../../bca-token-solidity/ignition/deployments/chain-31337/artifacts/BCA_Token#BCAServiceToken.json" +// import abi_servicemanager_json from "$lib/../../../bca-token-solidity/ignition/deployments/chain-31337/artifacts/BCA_ServiceManager#BCAServiceManager.json" -// // Amoy - Polygon testnet +// // Amoy - Polygon testnet // import deployed_address from "$lib/../../../bca-token-solidity/ignition/deployments/chain-80002/deployed_addresses.json" export const tokenContractAddress: string = deployed_address["BCA_Token#BCAServiceToken"] +export const serviceManagerAddress: string = deployed_address["BCA_ServiceManager#BCAServiceManager"] export const contractAddresses: Record = deployed_address import abi_token_json from "$lib/bca_token-abi.json" export const tokenContractABI = abi_token_json["abi"]; +import abi_servicemanager_json from "$lib/bca_servicemanager-abi.json" +export const serviceManagerABI = abi_servicemanager_json["abi"]; +import abi_servicecontroller_json from "$lib/bca_servicecontroller-abi.json" +export const serviceControllerABI = abi_servicecontroller_json["abi"]; import abi_service_json from "$lib/bca_service-abi.json" -export const serviceContractABI = abi_service_json["abi"]; +export const serviceABI = abi_service_json["abi"]; +import abi_serviceinstance_json from "$lib/bca_serviceinstance-abi.json" +export const serviceInstanceABI = abi_serviceinstance_json["abi"]; + +// token constants +export const token_symbol: string = 'BCA1' +export const token_decimals: number = 18 + +// calculations +export function calculate_user_balance(deposit: number, startTime: number, dayPrice: number) { + if (startTime <= 0) { return deposit } + const now = new Date().getTime() + const deltaMilsecs = now - startTime * 1000 + return Math.max(0, deposit - (dayPrice * deltaMilsecs / 24 / 3600 / 1000)) +} +export function calculate_provider_balance(deposit: number, retracted: number, startTime: number, dayPrice: number) { + if (startTime <= 0) { return deposit } + const now = new Date().getTime() + const deltaMilsecs = now - startTime * 1000 + return Math.max(0, Math.min(deposit, dayPrice * deltaMilsecs / 24 / 3600 / 1000) - retracted) +} + +// chain viewer url +export function mk_chainviewer_url(address: string, network: string|undefined): string { + if (network === "0x89") { + return `${address} on Polygon network: ${network} ` + } else if (network === "0x80002") { + return `${address} on Polygon's Amoy network: ${network} ` + } else { + return `${address} on network: ${network} ` + } +} + +// utilities +export function shorten_address(address: string, len: number = 6) { + return address.substring(0,len) + ".." + address.substring(address.length - len + 1) +} diff --git a/bca-token-market/src/lib/wallet.ts b/bca-token-market/src/lib/wallet.ts index b21be54..f560ea4 100644 --- a/bca-token-market/src/lib/wallet.ts +++ b/bca-token-market/src/lib/wallet.ts @@ -63,8 +63,8 @@ export async function get_wallet_addr(wallet: WalletInformation, ev: any): Promi wallet.walletaddr = selectedAccount; wallet.warning = undefined - console.log("address: " + wallet.walletaddr) - console.log("network: " + wallet.walletnetwork) + // console.log("address: " + wallet.walletaddr) + // console.log("network: " + wallet.walletnetwork) } catch (error) { wallet.warning = "error while accessing wallet: " + error; diff --git a/bca-token-market/src/routes/+layout.svelte b/bca-token-market/src/routes/+layout.svelte index 241f07e..c87facb 100644 --- a/bca-token-market/src/routes/+layout.svelte +++ b/bca-token-market/src/routes/+layout.svelte @@ -5,12 +5,6 @@ @@ -18,7 +12,7 @@

- + \ No newline at end of file diff --git a/bca-token-market/src/routes/+page.svelte b/bca-token-market/src/routes/+page.svelte index ad03b58..fd4ce60 100644 --- a/bca-token-market/src/routes/+page.svelte +++ b/bca-token-market/src/routes/+page.svelte @@ -1,6 +1,33 @@
@@ -21,9 +48,15 @@

{$page.data.session.user?.name ?? "User"} {$page.data.session.user?.email ?? "Email"}

- -
Sign out
-
+ {#if has_wallet} +

Wallet:

+ {#if wallet && wallet.walletaddr} +

Address: {@html mk_chainviewer_url(wallet.walletaddr, wallet.walletnetwork)}

+ {/if} +

Continue to this system's Service Manager

+ {:else} +

Wallet:

+ {/if} {:else}

You are not signed in

diff --git a/bca-token-market/src/routes/service/+page.server.ts b/bca-token-market/src/routes/service/+page.server.ts new file mode 100644 index 0000000..4794fd1 --- /dev/null +++ b/bca-token-market/src/routes/service/+page.server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async (event) => { + const session = await event.locals.auth() + + if (!session?.user?.id) { + throw redirect(303, '/') + } + + return { session } + }; diff --git a/bca-token-market/src/routes/service/[addr]/+page.server.ts b/bca-token-market/src/routes/service/[addr]/+page.server.ts new file mode 100644 index 0000000..f6ae3c3 --- /dev/null +++ b/bca-token-market/src/routes/service/[addr]/+page.server.ts @@ -0,0 +1,5 @@ +export function load({ params }) { + return { + addr: params.addr + }; +} \ No newline at end of file diff --git a/bca-token-market/src/routes/service/[addr]/+page.svelte b/bca-token-market/src/routes/service/[addr]/+page.svelte new file mode 100644 index 0000000..3566522 --- /dev/null +++ b/bca-token-market/src/routes/service/[addr]/+page.svelte @@ -0,0 +1,167 @@ + + +

+

Service Manager - Controller - Service

+ + {#if $page.data.session && is_provider} + +

Service - {shorten_address(data.addr)}

+ + + +

+ + {#if details !== undefined && has_wallet} + +

number of instances: {details.ninstances}

+

max instances: {details.maxinstances}

+

available instances: {details.maxinstances - details.ninstances} ({((details.maxinstances - details.ninstances) * 100 / details.maxinstances).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} %)

+ {#each details.instanceAddresses as instanceAddress,idx} +

instance {idx+1}: {shorten_address(instanceAddress)}

+ {/each} + +
+

Create instance

+
+ + + {#if wallet.walletnetwork !== "0x89" } + + + {/if} + +
+
+ {/if} + + {/if} +
+ + \ No newline at end of file diff --git a/bca-token-market/src/routes/servicecontroller/+page.server.ts b/bca-token-market/src/routes/servicecontroller/+page.server.ts new file mode 100644 index 0000000..4794fd1 --- /dev/null +++ b/bca-token-market/src/routes/servicecontroller/+page.server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async (event) => { + const session = await event.locals.auth() + + if (!session?.user?.id) { + throw redirect(303, '/') + } + + return { session } + }; diff --git a/bca-token-market/src/routes/servicecontroller/[addr]/+page.server.ts b/bca-token-market/src/routes/servicecontroller/[addr]/+page.server.ts new file mode 100644 index 0000000..f6ae3c3 --- /dev/null +++ b/bca-token-market/src/routes/servicecontroller/[addr]/+page.server.ts @@ -0,0 +1,5 @@ +export function load({ params }) { + return { + addr: params.addr + }; +} \ No newline at end of file diff --git a/bca-token-market/src/routes/servicecontroller/[addr]/+page.svelte b/bca-token-market/src/routes/servicecontroller/[addr]/+page.svelte new file mode 100644 index 0000000..96d7011 --- /dev/null +++ b/bca-token-market/src/routes/servicecontroller/[addr]/+page.svelte @@ -0,0 +1,179 @@ + + +
+

Service Manager - Controller

+ + {#if $page.data.session && is_provider} + +

Service Controller - {shorten_address(data.addr)}

+ + + +

+ + {#if details !== undefined && has_wallet} + +

controller address: {@html mk_chainviewer_url(data.addr, wallet.walletnetwork)}

+

number of services: {details.nservices}

+ + {#each details.serviceAddresses as serviceAddress,idx} + + {/each} +
service {idx+1}: {shorten_address(serviceAddress)}
+ +
+

Create service

+
+ + + + + {#if wallet.walletnetwork !== "0x89" } + + + {/if} + +
+
+ {/if} + + {/if} +
+ + \ No newline at end of file diff --git a/bca-token-market/src/routes/serviceinstance/+page.server.ts b/bca-token-market/src/routes/serviceinstance/+page.server.ts new file mode 100644 index 0000000..4794fd1 --- /dev/null +++ b/bca-token-market/src/routes/serviceinstance/+page.server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async (event) => { + const session = await event.locals.auth() + + if (!session?.user?.id) { + throw redirect(303, '/') + } + + return { session } + }; diff --git a/bca-token-market/src/routes/serviceinstance/[addr]/+page.server.ts b/bca-token-market/src/routes/serviceinstance/[addr]/+page.server.ts new file mode 100644 index 0000000..f6ae3c3 --- /dev/null +++ b/bca-token-market/src/routes/serviceinstance/[addr]/+page.server.ts @@ -0,0 +1,5 @@ +export function load({ params }) { + return { + addr: params.addr + }; +} \ No newline at end of file diff --git a/bca-token-market/src/routes/serviceinstance/[addr]/+page.svelte b/bca-token-market/src/routes/serviceinstance/[addr]/+page.svelte new file mode 100644 index 0000000..f2f355a --- /dev/null +++ b/bca-token-market/src/routes/serviceinstance/[addr]/+page.svelte @@ -0,0 +1,472 @@ + + +
+ {#if $page.data.session} +

+

 

+ +

Service Instance

+ +

Details

+ + + +
+
    +
  • Contract address: {@html mk_chainviewer_url(data.addr, wallet.walletnetwork) }
  • +
+
+ + + +


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+ +

 

+ +

Service Instance

+ +

Contract

+ + + +
+

+ + {#if details !== undefined && has_wallet} +
    +
  • instance address: {@html mk_chainviewer_url(data.addr, wallet.walletnetwork)}
  • +
  • daily price: {details.dayPrice / 10**(token_decimals)} {token_symbol}
  • +
  • user deposit: {details.deposit / 10**(token_decimals)} {token_symbol}
  • +
  • retracted: {details.retracted / 10**(token_decimals)} {token_symbol}
  • +
  • start time: { details.startTime > 0 ? new Date(details.startTime * 1000).toISOString() : '' }
  • +
  • end time: {details.endTime > 0 ? new Date(details.endTime * 1000).toISOString() : (details.startTime > 0 ? "(" + new Date(details.startTime * 1000 + (details.deposit * 1000 * 24 * 3600 / details.dayPrice)).toISOString() + ") estimated" : '') }
  • +
  • provider address: {@html mk_chainviewer_url(details.providerAddress, wallet.walletnetwork)}
  • +
  • user address: {@html mk_chainviewer_url(details.userAddress, wallet.walletnetwork)}
  • +
  • estimated user balance: { calculate_user_balance(details.deposit, details.startTime, details.dayPrice) / 10**(token_decimals)} {token_symbol}
  • +
  • estimated provider balance: { calculate_provider_balance(details.deposit, details.retracted, details.startTime, details.dayPrice) / 10**(token_decimals)} {token_symbol}
  • +
+ {/if} + +
+

Withdrawal from contract

+
+ + + {#if wallet.walletnetwork !== "0x89" } + + + {/if} + +
+
+ {#if ! is_provider} +
+

Deposit to contract

+
+ + + {#if wallet.walletnetwork !== "0x89" } + + + {/if} + +
+
+ {/if} +
+ +


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+ +

 

+ +

Service Instance

+

Events

+ + + +
+

+ + {#if contractevents && contractevents.length} + {#each contractevents as cev} +

{cev.event ? cev.event : "some event"}

+ + + + + + + +
Timestamp: + {#await get_block_time(cev.blockHash) then timestamp} + {new Date(Number(timestamp * 1000n)).toISOString()} + {:catch} + error + {/await} +
Block:#{cev.blockNumber ? cev.blockNumber : "#?"} {cev.blockHash ? cev.blockHash : "0x.."}
Transaction: + {#if cev.transactionHash} + + {:else} + "no transaction info" + {/if} +
Address:{cev.address ? cev.address : "0x.."}
Topics:{cev.topics ? JSON.stringify(cev.topics,null,2) : "[ ]"}
Values:{cev.returnValues ? JSON.stringify(cev.returnValues,null,2) : "{ }"}
+ {/each} + {/if}
+ +


+


+


+


+


+


+


+


+


+


+


+


+


+


+


+ +

 

+ +

Service Instance

+

Transactions

+ + + +
+


+


+


+
+ +


+


+


+


+


+


+


+


+


+


+


+


+


+


+ + {/if} +
+ + \ No newline at end of file diff --git a/bca-token-market/src/routes/servicemanager/+page.server.ts b/bca-token-market/src/routes/servicemanager/+page.server.ts new file mode 100644 index 0000000..4794fd1 --- /dev/null +++ b/bca-token-market/src/routes/servicemanager/+page.server.ts @@ -0,0 +1,12 @@ +import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async (event) => { + const session = await event.locals.auth() + + if (!session?.user?.id) { + throw redirect(303, '/') + } + + return { session } + }; diff --git a/bca-token-market/src/routes/servicemanager/+page.svelte b/bca-token-market/src/routes/servicemanager/+page.svelte new file mode 100644 index 0000000..a91d52b --- /dev/null +++ b/bca-token-market/src/routes/servicemanager/+page.svelte @@ -0,0 +1,17 @@ + + +
+

Service Manager

+ + {#if $page.data.session && is_provider} + +

Service Manager

+

Continue to this system's Service Manager

+ + {/if} +
\ No newline at end of file diff --git a/bca-token-market/src/routes/servicemanager/[addr]/+page.server.ts b/bca-token-market/src/routes/servicemanager/[addr]/+page.server.ts new file mode 100644 index 0000000..f6ae3c3 --- /dev/null +++ b/bca-token-market/src/routes/servicemanager/[addr]/+page.server.ts @@ -0,0 +1,5 @@ +export function load({ params }) { + return { + addr: params.addr + }; +} \ No newline at end of file diff --git a/bca-token-market/src/routes/servicemanager/[addr]/+page.svelte b/bca-token-market/src/routes/servicemanager/[addr]/+page.svelte new file mode 100644 index 0000000..41e3200 --- /dev/null +++ b/bca-token-market/src/routes/servicemanager/[addr]/+page.svelte @@ -0,0 +1,162 @@ + + +
+

Service Manager

+ + {#if $page.data.session && is_provider} + +

Service Manager - {shorten_address(data.addr)}

+ + + +

+ + {#if details !== undefined && has_wallet} +

service manager address: {@html mk_chainviewer_url(data.addr, wallet.walletnetwork)}

+ {#if details.ncontrollers > 0} + +

provider address: {@html mk_chainviewer_url(details.providerAddress, wallet.walletnetwork)}

+

provider's controller: {shorten_address(details.controllerAddress)}

+ {:else if is_provider} +
+

Create controller

+
+ + + {#if wallet.walletnetwork !== "0x89" } + + + {/if} + +
+
+ {/if} + {/if} + + {/if} +
+ + \ No newline at end of file diff --git a/bca-token-market/update-abi.sh b/bca-token-market/update-abi.sh new file mode 100755 index 0000000..1983e4f --- /dev/null +++ b/bca-token-market/update-abi.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +SRCPATH="../bca-token-solidity/artifacts/contracts" +cp -v ${SRCPATH}/BCA_Service.sol/BCAService.json src/lib/bca_service-abi.json +cp -v ${SRCPATH}/BCA_ServiceInstance.sol/BCAServiceInstance.json src/lib/bca_serviceinstance-abi.json +cp -v ${SRCPATH}/BCA_ServiceController.sol/BCAServiceController.json src/lib/bca_servicecontroller-abi.json + +SRCPATH="../bca-token-solidity/ignition/deployments/chain-31337/artifacts" +cp -v ${SRCPATH}/BCA_Token#BCAServiceToken.json src/lib/bca_token-abi.json +cp -v ${SRCPATH}/BCA_ServiceManager#BCAServiceManager.json src/lib/bca_servicemanager-abi.json diff --git a/bca-token-solidity/README.md b/bca-token-solidity/README.md index e44ff4b..0caac7f 100644 --- a/bca-token-solidity/README.md +++ b/bca-token-solidity/README.md @@ -23,7 +23,7 @@ npx hardhat node in the other deploy the contract ```sh npx hardhat ignition deploy ignition/modules/BCA_Token.ts --network localhost -npx hardhat ignition deploy ignition/modules/BCA_Service.ts --network localhost +npx hardhat ignition deploy ignition/modules/BCA_ServiceManager.ts --network localhost ``` #### Connect metamask to local node diff --git a/bca-token-solidity/contracts/BCA_ERC20_nf.sol b/bca-token-solidity/contracts/BCA_ERC20_nf.sol index 85403f6..d892323 100644 --- a/bca-token-solidity/contracts/BCA_ERC20_nf.sol +++ b/bca-token-solidity/contracts/BCA_ERC20_nf.sol @@ -13,50 +13,53 @@ contract BCAServiceToken is ERC20, AccessControl { /** * @dev Constructor that sets up roles and gives the deployer the default admin role. - * @param name The name of the token. - * @param symbol The symbol of the token. - * @param minter The address that will be granted the minter role. - * @param burner The address that will be granted the burner role. + * @param setName The name of the token. + * @param setSymbol The symbol of the token. + * @param setMinter The address that will be granted the minter role. + * @param setBurner The address that will be granted the burner role. */ - constructor(string memory name, string memory symbol, address minter, address burner) - ERC20(name, symbol) + constructor(string memory setName, string memory setSymbol, address setMinter, address setBurner) + ERC20(setName, setSymbol) { + require(setMinter != address(0), "Invalid minter address"); + require(setBurner != address(0), "Invalid burner address"); + serviceAddress = msg.sender; - minterAddress = minter; - burnerAddress = burner; + minterAddress = setMinter; + burnerAddress = setBurner; _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - _grantRole(MINTER_ROLE, minter); - _grantRole(BURNER_ROLE, burner); + _grantRole(MINTER_ROLE, setMinter); + _grantRole(BURNER_ROLE, setBurner); } /** * @dev Allows the admin to set the service address. - * @param new_serviceAddress The address to set as the service address. + * @param newServiceAddress The address to set as the service address. */ - function setServiceAddress(address new_serviceAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { + function setServiceAddress(address newServiceAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { _revokeRole(DEFAULT_ADMIN_ROLE, serviceAddress); - serviceAddress = new_serviceAddress; - _grantRole(DEFAULT_ADMIN_ROLE, new_serviceAddress); + serviceAddress = newServiceAddress; + _grantRole(DEFAULT_ADMIN_ROLE, newServiceAddress); } /** * @dev Allows the admin to set the minter address. - * @param new_minterAddress The address to set as the minter address. + * @param newMinterAddress The address to set as the minter address. */ - function setMinterAddress(address new_minterAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { + function setMinterAddress(address newMinterAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { _revokeRole(MINTER_ROLE, minterAddress); - minterAddress = new_minterAddress; - _grantRole(MINTER_ROLE, new_minterAddress); + minterAddress = newMinterAddress; + _grantRole(MINTER_ROLE, newMinterAddress); } /** * @dev Allows the admin to set the burner address. - * @param new_burnerAddress The address to set as the burner address. + * @param newBurnerAddress The address to set as the burner address. */ - function setBurnerAddress(address new_burnerAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { + function setBurnerAddress(address newBurnerAddress) public onlyRole(DEFAULT_ADMIN_ROLE) { _revokeRole(BURNER_ROLE, burnerAddress); - burnerAddress = new_burnerAddress; - _grantRole(BURNER_ROLE, new_burnerAddress); + burnerAddress = newBurnerAddress; + _grantRole(BURNER_ROLE, newBurnerAddress); } /** diff --git a/bca-token-solidity/contracts/BCA_Funding24.sol b/bca-token-solidity/contracts/BCA_Funding24.sol new file mode 100644 index 0000000..923781b --- /dev/null +++ b/bca-token-solidity/contracts/BCA_Funding24.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./Iface_Funding24.sol"; +import "./Iface_ServiceInstance.sol"; + +contract BCAServiceFunding24 is IFunding24, Ownable { + using SafeERC20 for IERC20; + + address public immutable targetContract; + uint256 public immutable dailyAmount; + IERC20 public immutable token; + uint256 public lastDepositTime; + + event DepositMade(address targetContract, uint256 amount, uint256 timestamp); + + constructor( + address initialOwner, + address setTargetContract, + address setToken, + uint256 setDailyAmount + ) Ownable(initialOwner) { + require(setTargetContract != address(0), "Invalid target contract address"); + require(setToken != address(0), "Invalid token address"); + require(setDailyAmount > 0, "Invalid daily amount"); + + targetContract = setTargetContract; + token = IERC20(setToken); + dailyAmount = setDailyAmount; + lastDepositTime = 0; + } + + function deposit() external { + require( + lastDepositTime == 0 || block.timestamp >= lastDepositTime + 24 hours, + "24 hours have not passed since last deposit" + ); + + // Check if owner has sufficient balance and has approved this contract + require( + token.balanceOf(owner()) >= dailyAmount, + "Insufficient balance in owner's wallet" + ); + require( + token.allowance(owner(), address(this)) >= dailyAmount, + "Insufficient allowance from owner" + ); + + // set new state + lastDepositTime = block.timestamp; + + // Transfer tokens from owner to this contract + token.safeTransferFrom(owner(), address(this), dailyAmount); + + // Set allowance for the target contract + token.approve(targetContract, dailyAmount); + + try IServiceInstance(targetContract).makeDeposit(dailyAmount) { + emit DepositMade(targetContract, dailyAmount, block.timestamp); + } catch Error(string memory reason) { + revert(reason); + } catch (bytes memory) { + revert("failed to make deposit"); + } + } + + // Function to check if deposit is currently possible + function canDeposit() external view returns (bool) { + return (lastDepositTime == 0 || block.timestamp >= lastDepositTime + 24 hours) && + token.balanceOf(owner()) >= dailyAmount && + token.allowance(owner(), address(this)) >= dailyAmount; + } +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/BCA_Service.sol b/bca-token-solidity/contracts/BCA_Service.sol index 9fd22fd..5d29fd6 100644 --- a/bca-token-solidity/contracts/BCA_Service.sol +++ b/bca-token-solidity/contracts/BCA_Service.sol @@ -4,164 +4,53 @@ pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./Iface_Service.sol"; +import "./BCA_ServiceInstance.sol"; -contract BCAServiceContract is ReentrancyGuard { +contract BCAService is IService, ReentrancyGuard { using SafeERC20 for IERC20; - // the price for launching the contract; will remain with the contract - // adjust for the various transaction costs depending on chain - uint256 public constant setupFee = 2 * 10 ** 17; - // will be set in the constructor IERC20 public immutable tokToken; uint256 public immutable dayPrice; address public immutable providerAddress; - uint256 public deposit; - uint256 public retracted; - uint256 public startTime; - uint256 public endTime; - address public userAddress; - - event DepositMade(address user, uint256 amount); - event ServiceStopped(uint256 ticks); - event Withdrawn(address recipient, uint256 amount); - event Retracted(address recipient, uint256 amount); - - error AlreadySubscribed(); - error AlreadyStopped(); - error InsufficientAmount(uint256 minimumAmount); - error InsufficientBalance(uint256 availableBalance); - error NotStarted(); - error UnAuthorized(); - - constructor(address _providerAddress, address _tokAddress, uint256 _dayPrice) { - tokToken = IERC20(_tokAddress); - dayPrice = _dayPrice; - providerAddress = _providerAddress; - } - - function makeDeposit(uint256 amount) external nonReentrant { - // require(amount > 0, "Deposit must be greater than 0"); by type! - if (! (userAddress == address(0) || userAddress == msg.sender)) { - revert AlreadySubscribed(); - } - - // first deposit pays the setup fee - if (userAddress == address(0) && amount < (setupFee + (dayPrice / 24))) { - revert InsufficientAmount(setupFee + (dayPrice / 24)); - } - - // cannot wake up the service once it has stopped - if (endTime != 0) { - revert AlreadyStopped(); - } + uint16 public immutable maxInstances; - // allowance for the amount must have been approved by the sender - tokToken.safeTransferFrom(msg.sender, address(this), amount); - deposit += amount; + address[] public deployedInstances; - // remember user who made first deposit (e.g. subscribed) - if (userAddress == address(0)) { - userAddress = msg.sender; - startTime = block.timestamp; - deposit -= setupFee; // pay setup fee - } - emit DepositMade(msg.sender, amount); - } + // Event to notify when a new instance is deployed + event InstanceDeployed(address contractAddress); - function stop() external { - _stop(msg.sender); - } + error Exhausted(); - function _stop(address from) private { - if (! (from == userAddress || from == providerAddress)) { - revert UnAuthorized(); - } - if (endTime != 0) { - revert AlreadyStopped(); - } + constructor(address setProviderAddress, address tokAddress, + uint16 setMaxInstances, uint256 setDayPrice) { + require(setProviderAddress != address(0), "Invalid provider address"); + require(tokAddress != address(0), "Invalid token address"); - endTime = block.timestamp; - uint256 ticks = endTime - startTime; - emit ServiceStopped(ticks); + tokToken = IERC20(tokAddress); + dayPrice = setDayPrice; + providerAddress = setProviderAddress; + maxInstances = setMaxInstances; } - function balanceUser() public view returns (uint256) { - if (userAddress == address(0) || startTime == 0) { - revert NotStarted(); - } - if (msg.sender != userAddress) { - revert UnAuthorized(); - } - - uint256 calcTime = endTime != 0 ? endTime : block.timestamp; - uint256 duration = calcTime - startTime; - uint256 paid = duration * dayPrice / 24 / 3600; - - if (paid >= deposit) { - return 0; - } else { - return deposit - paid; + function newInstance(address userAddress) external nonReentrant returns (address) { + if (deployedInstances.length >= maxInstances) { + revert Exhausted(); } + // Create a new SimpleContract + BCAServiceInstance instanceContract = new BCAServiceInstance(providerAddress, address(tokToken), userAddress, dayPrice); + + // Store the address + deployedInstances.push(address(instanceContract)); + + // Emit event + emit InstanceDeployed(address(instanceContract)); + + return address(instanceContract); } - function balanceProvider() public view returns (uint256) { - if (msg.sender != providerAddress) { - revert UnAuthorized(); - } - if (startTime == 0) { - revert NotStarted(); - } - - uint256 calcTime = endTime != 0 ? endTime : block.timestamp; - uint256 ticks = calcTime - startTime; - uint256 balance = 0; - uint256 bal1 = ticks * dayPrice / 24 / 3600; - // cap the available balance - if (bal1 > deposit - retracted) { - balance = deposit - retracted; - } else { - balance = bal1; - } - return balance; - } - - function withdrawUser(uint256 amount) external nonReentrant { - if (msg.sender != userAddress) { - revert UnAuthorized(); - } - uint256 balance = balanceUser(); - if (balance < amount) { - revert InsufficientBalance(balance); - } - - deposit -= amount; - tokToken.transfer(msg.sender, amount); - emit Withdrawn(msg.sender, amount); - - // stop service? - if (balance - amount <= dayPrice / 24 / 3600) { - _stop(msg.sender); - } - } - - function withdrawProvider(uint256 amount) external nonReentrant { - if (msg.sender != providerAddress) { - revert UnAuthorized(); - } - - uint256 balance = balanceProvider(); - if (balance < amount) { - revert InsufficientBalance(balance); - } - - // stop service? - if (deposit - retracted - amount < dayPrice / 24 / 3600) { - _stop(msg.sender); - } - - retracted += amount; - tokToken.transfer(msg.sender, amount); - emit Retracted(msg.sender, amount); + function countServiceInstances() public view returns (uint) { + return deployedInstances.length; } } \ No newline at end of file diff --git a/bca-token-solidity/contracts/BCA_ServiceController.sol b/bca-token-solidity/contracts/BCA_ServiceController.sol new file mode 100644 index 0000000..0099fe7 --- /dev/null +++ b/bca-token-solidity/contracts/BCA_ServiceController.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./Iface_ServiceController.sol"; +import "./BCA_Service.sol"; + +// Factory contract that deploys service contracts +contract BCAServiceController is IServiceController, ReentrancyGuard { + using SafeERC20 for IERC20; + + IERC20 public immutable tokToken; + address public immutable providerAddress; + address[] public deployedServices; + + // Event to notify when a new service is deployed + event ServiceDeployed(address contractAddress); + + constructor(address setProviderAddress, address tokAddress) { + require(setProviderAddress != address(0), "Invalid provider address"); + require(tokAddress != address(0), "Invalid token address"); + + tokToken = IERC20(tokAddress); + providerAddress = setProviderAddress; + } + + function newService(uint16 maxInstances, uint256 dayPrice) external nonReentrant returns (address) { + // Create a new SimpleContract + BCAService serviceContract = new BCAService(providerAddress, address(tokToken), maxInstances, dayPrice); + + // Store the address + deployedServices.push(address(serviceContract)); + + // Emit event + emit ServiceDeployed(address(serviceContract)); + + return address(serviceContract); + } + + function countServiceContracts() public view returns (uint) { + return deployedServices.length; + } +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/BCA_ServiceInstance.sol b/bca-token-solidity/contracts/BCA_ServiceInstance.sol new file mode 100644 index 0000000..92985e8 --- /dev/null +++ b/bca-token-solidity/contracts/BCA_ServiceInstance.sol @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./Iface_ServiceInstance.sol"; + +contract BCAServiceInstance is IServiceInstance, ReentrancyGuard { + using SafeERC20 for IERC20; + + // will be set in the constructor + IERC20 public immutable tokToken; + uint256 public immutable dayPrice; + address public immutable providerAddress; + address public immutable userAddress; + + uint256 public deposit; + uint256 public retracted; + uint256 public startTime; + uint256 public endTime; + + event DepositMade(address user, uint256 amount); + event ServiceStopped(uint256 ticks); + event Withdrawn(address recipient, uint256 amount); + event Retracted(address recipient, uint256 amount); + + // error AlreadySubscribed(); + error AlreadyStopped(); + // error InsufficientAmount(uint256 minimumAmount); + error InsufficientBalance(uint256 availableBalance); + error NotStarted(); + error UnAuthorized(); + + constructor(address setProviderAddress, address tokAddress, + address setUserAddress, + uint256 setDayPrice) { + require(setProviderAddress != address(0), "Invalid provider address"); + require(tokAddress != address(0), "Invalid token address"); + require(setUserAddress != address(0), "Invalid user address"); + + tokToken = IERC20(tokAddress); + dayPrice = setDayPrice; + providerAddress = setProviderAddress; + userAddress = setUserAddress; + } + + function makeDeposit(uint256 amount) external nonReentrant { + // if (userAddress != msg.sender) { + // revert AlreadySubscribed(); + // } + + // cannot wake up the service once it has stopped + if (endTime != 0) { + revert AlreadyStopped(); + } + + // allowance for the amount must have been approved by the sender + tokToken.safeTransferFrom(msg.sender, address(this), amount); + deposit += amount; + + // start contract on first deposit + if (startTime == 0) { + startTime = block.timestamp; + } + emit DepositMade(msg.sender, amount); + } + + function stop() external nonReentrant { + _stop(msg.sender); + } + + function _stop(address from) private { + if (! (from == userAddress || from == providerAddress)) { + revert UnAuthorized(); + } + if (endTime != 0) { + revert AlreadyStopped(); + } + + endTime = block.timestamp; + uint256 ticks = endTime - startTime; + emit ServiceStopped(ticks); + } + + function balanceUser() public view returns (uint256) { + if (startTime == 0) { + revert NotStarted(); + } + if (msg.sender != userAddress) { + revert UnAuthorized(); + } + + uint256 calcTime = endTime != 0 ? endTime : block.timestamp; + uint256 duration = calcTime - startTime; + uint256 paid = duration * dayPrice / 24 / 3600; + + if (paid >= deposit) { + return 0; + } else { + return deposit - paid; + } + } + + function balanceProvider() public view returns (uint256) { + if (msg.sender != providerAddress) { + revert UnAuthorized(); + } + if (startTime == 0) { + revert NotStarted(); + } + + uint256 calcTime = endTime != 0 ? endTime : block.timestamp; + uint256 ticks = calcTime - startTime; + uint256 balance = 0; + uint256 bal1 = ticks * dayPrice / 24 / 3600; + // cap the available balance + if (bal1 > deposit - retracted) { + balance = deposit - retracted; + } else { + balance = bal1; + } + return balance; + } + + function withdrawUser(uint256 amount) external nonReentrant { + if (msg.sender != userAddress) { + revert UnAuthorized(); + } + uint256 balance = balanceUser(); + if (balance < amount) { + revert InsufficientBalance(balance); + } + + // set new state + deposit -= amount; + + if (tokToken.transfer(msg.sender, amount)) { + emit Withdrawn(msg.sender, amount); + } else { + revert("transfer failed"); + } + + // stop service? + if (balance - amount <= dayPrice / 24 / 3600) { + _stop(msg.sender); + } + } + + function withdrawProvider(uint256 amount) external nonReentrant { + if (msg.sender != providerAddress) { + revert UnAuthorized(); + } + + uint256 balance = balanceProvider(); + if (balance < amount) { + revert InsufficientBalance(balance); + } + + // stop service? + if (deposit - retracted - amount < dayPrice / 24 / 3600) { + _stop(msg.sender); + } + + // set new state + retracted += amount; + + if (tokToken.transfer(msg.sender, amount)) { + emit Retracted(msg.sender, amount); + } else { + revert("transfer failed"); + } + } +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/BCA_ServiceManager.sol b/bca-token-solidity/contracts/BCA_ServiceManager.sol new file mode 100644 index 0000000..1025ac6 --- /dev/null +++ b/bca-token-solidity/contracts/BCA_ServiceManager.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./Iface_ServiceManager.sol"; +import "./BCA_ServiceController.sol"; + +// Factory contract that deploys controller contracts +contract BCAServiceManager is IServiceManager, ReentrancyGuard { + using SafeERC20 for IERC20; + + IERC20 public immutable tokToken; + + struct ControllerStruct { + address addrContract; + bool isDeployed; + } + mapping (address => ControllerStruct) public deployedControllers; + address[] public providerControllers; + + // Event to notify when a new service is deployed + event ControllerDeployed(address contractAddress); + + constructor(address tokAddress) { + require(tokAddress != address(0), "Invalid token address"); + + tokToken = IERC20(tokAddress); + } + + function getControllerAddress(address providerAddress) public view returns(address addrController) { + require (deployedControllers[providerAddress].isDeployed == true, "no controller for this provider"); + return deployedControllers[providerAddress].addrContract; + } + + function isDeployed(address providerAddress) public view returns(bool isdeployed) { + if (providerControllers.length == 0) return false; + return (deployedControllers[providerAddress].isDeployed); + } + + function newController(address providerAddress) external nonReentrant { + require(! isDeployed(providerAddress), "already deployed controller for this provider"); + + // Create a new SimpleContract + BCAServiceController controllerContract = new BCAServiceController(providerAddress, address(tokToken)); + + // Store the address + providerControllers.push(providerAddress); + deployedControllers[providerAddress].addrContract = address(controllerContract); + deployedControllers[providerAddress].isDeployed = true; + + // Emit event + emit ControllerDeployed(address(controllerContract)); + } + + function countServiceControllers() public view returns (uint) { + return providerControllers.length; + } +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/Iface_Funding24.sol b/bca-token-solidity/contracts/Iface_Funding24.sol new file mode 100644 index 0000000..f968989 --- /dev/null +++ b/bca-token-solidity/contracts/Iface_Funding24.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +interface IFunding24 { + function deposit() external; +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/Iface_Service.sol b/bca-token-solidity/contracts/Iface_Service.sol new file mode 100644 index 0000000..6ac4bc6 --- /dev/null +++ b/bca-token-solidity/contracts/Iface_Service.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +interface IService { + function newInstance(address _userAddress) external returns (address); +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/Iface_ServiceController.sol b/bca-token-solidity/contracts/Iface_ServiceController.sol new file mode 100644 index 0000000..863e016 --- /dev/null +++ b/bca-token-solidity/contracts/Iface_ServiceController.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +interface IServiceController { + function newService(uint16 _maxInstances, uint256 _dayPrice) external returns (address); +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/Iface_ServiceInstance.sol b/bca-token-solidity/contracts/Iface_ServiceInstance.sol new file mode 100644 index 0000000..ec6eecf --- /dev/null +++ b/bca-token-solidity/contracts/Iface_ServiceInstance.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +interface IServiceInstance { + function makeDeposit(uint256 amount) external; + function stop() external; + function withdrawUser(uint256 amount) external; + function withdrawProvider(uint256 amount) external; +} \ No newline at end of file diff --git a/bca-token-solidity/contracts/Iface_ServiceManager.sol b/bca-token-solidity/contracts/Iface_ServiceManager.sol new file mode 100644 index 0000000..0cc1b62 --- /dev/null +++ b/bca-token-solidity/contracts/Iface_ServiceManager.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +interface IServiceManager { + function newController(address providerAddress) external; +} \ No newline at end of file diff --git a/bca-token-solidity/ignition/deploy_services-example.json b/bca-token-solidity/ignition/deploy_services-example.json deleted file mode 100644 index 1417802..0000000 --- a/bca-token-solidity/ignition/deploy_services-example.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "name": "test service at 2 BCA1 per day", - "providerAddress": "0xbe371e774e8b0c87912eb64be1e2851c5b702b38", - "dayPrice": "2000000000000000000" - } -] \ No newline at end of file diff --git a/bca-token-solidity/ignition/deploy_services.json b/bca-token-solidity/ignition/deploy_services.json deleted file mode 100644 index d996428..0000000 --- a/bca-token-solidity/ignition/deploy_services.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "name": "test service at 2 BCA1 per day", - "providerAddress": "0xBe371e774E8b0c87912Eb64BE1e2851c5b702B38", - "dayPrice": "2000000000000000000" - }, - { - "name": "test service at 1 BCA1 per day", - "providerAddress": "0xBe371e774E8b0c87912Eb64BE1e2851c5b702B38", - "dayPrice": "1000000000000000000" - }, - { - "name": "test service at 3 BCA1 per day", - "providerAddress": "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199", - "dayPrice": "3000000000000000000" - } -] \ No newline at end of file diff --git a/bca-token-solidity/ignition/modules/BCA_Service.ts b/bca-token-solidity/ignition/modules/BCA_Service.ts deleted file mode 100644 index d919cee..0000000 --- a/bca-token-solidity/ignition/modules/BCA_Service.ts +++ /dev/null @@ -1,27 +0,0 @@ -const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); - -import service_contracts from "../deploy_services.json"; - -// local node -import deployed_address from "../deployments/chain-31337/deployed_addresses.json" - -// Amoy testnet -// import deployed_address from "../deployments/chain-80002/deployed_addresses.json" - -const BCAServiceModule = buildModule("BCA_Service", (m) => { - - let bcaservices = [] - let counter = 0; - - service_contracts.forEach(def => { - console.log(` deploying contract ${def.name} with provider=${def.providerAddress}`); - const id = "BCAServiceContract" + ("000" + counter).slice(-4); - const bcaservice = m.contract("BCAServiceContract", [def.providerAddress, deployed_address["BCA_Token#BCAServiceToken"], BigInt(def.dayPrice)], {id}); - bcaservices.push(bcaservice); - counter += 1; - }); - - return { bcaservices }; -}); - -module.exports = BCAServiceModule; diff --git a/bca-token-solidity/ignition/modules/BCA_ServiceManager.ts b/bca-token-solidity/ignition/modules/BCA_ServiceManager.ts new file mode 100644 index 0000000..57aadc3 --- /dev/null +++ b/bca-token-solidity/ignition/modules/BCA_ServiceManager.ts @@ -0,0 +1,20 @@ +const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); + +// local node +import deployed_address from "../deployments/chain-31337/deployed_addresses.json" +// let owner = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +let provider = "0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199" + +// Amoy testnet +// import deployed_address from "../deployments/chain-80002/deployed_addresses.json" +// let owner = "0x1A8725f9A4295bb3b4E5321Ecb2c9185004fC76F" +// let provider = "0x480F8D64E9AE32E12C2B537d8347B8E035d43183" + +const BCAServiceManagerModule = buildModule("BCA_ServiceManager", (m) => { + + const bcasrvmgr = m.contract("BCAServiceManager", [deployed_address["BCA_Token#BCAServiceToken"]]); + + return { bcasrvmgr }; + }); + +module.exports = BCAServiceManagerModule; diff --git a/bca-token-solidity/package.json b/bca-token-solidity/package.json index 23969e3..9a9171c 100644 --- a/bca-token-solidity/package.json +++ b/bca-token-solidity/package.json @@ -5,8 +5,8 @@ "devDependencies": { "@nomicfoundation/hardhat-ignition-ethers": "^0.15.7", "@nomicfoundation/hardhat-toolbox": "^5.0.0", - "hardhat": "^2.22.15", - "mocha": "^10.7.3", + "hardhat": "^2.22.16", + "mocha": "^10.8.2", "solidity-coverage": "^0.8.13" }, "dependencies": { diff --git a/bca-token-solidity/test/BCA_Funding24.ts b/bca-token-solidity/test/BCA_Funding24.ts new file mode 100644 index 0000000..b5520ec --- /dev/null +++ b/bca-token-solidity/test/BCA_Funding24.ts @@ -0,0 +1,261 @@ +import { + time, + loadFixture, + } from "@nomicfoundation/hardhat-toolbox/network-helpers"; + import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; + import { expect } from "chai"; + import hre from "hardhat"; + + describe("BCA Funding", function () { + // We define a fixture to reuse the same setup in every test. + // We use loadFixture to run this setup once, snapshot that state, + // and reset Hardhat Network to that snapshot in every test. + async function deployContract() { + // Contracts are deployed using the first signer/account by default + const [owner, minter, burner, provider, user1, user2, cron] = await hre.ethers.getSigners(); + + const Token = await hre.ethers.getContractFactory("BCAServiceToken"); + const tokenContract = await Token.deploy("Test token", "TOK1", minter, burner); + const precision: bigint = await tokenContract.decimals().then(d => { if (d == 0n) {return 18n;} else {return d}; }); + + const Contract1 = await hre.ethers.getContractFactory("BCAServiceInstance"); + const daily_price = 1n; // 1 token per 24h + const day_price = (daily_price * BigInt(10n**precision)); + const serviceContract = await Contract1.deploy(provider, tokenContract.getAddress(), user1, day_price); + + const Contract2 = await hre.ethers.getContractFactory("BCAServiceFunding24"); + const daily_funds = 101n; // 1.01 token per 24h + const day_funds = (daily_funds * BigInt(10n**precision) / 100n); + const target_contract = serviceContract.getAddress(); + const fundingContract = await Contract2.deploy(user1, target_contract, tokenContract.getAddress(), day_funds); + + // minting some tokens to the users + const one_token = 1n * BigInt(10n**precision); + expect(await tokenContract.connect(minter).mint(user1.address, 10n*one_token)).to.changeTokenBalance(tokenContract, user1, 10n*one_token); + expect(await tokenContract.connect(minter).mint(user2.address, 10n*one_token)).to.changeTokenBalance(tokenContract, user2, 10n*one_token); + + const startblocktime: bigint = BigInt(await time.increase(30)); + + return { token: { tokenContract, one_token, owner, minter, burner, provider, user1, user2 }, + funding: { fundingContract, day_funds, cron }, + service: { serviceContract, provider, user1, user2 } }; + } + + describe("Deployment", function () { + it("Funding contract: should set the right target contract", async function () { + const { funding, service } = await loadFixture(deployContract); + expect(await funding.fundingContract.targetContract()).to.equal( + await service.serviceContract.getAddress() + ); + }); + + it("Funding contract: should set the right daily amount", async function () { + const { funding } = await loadFixture(deployContract); + expect(await funding.fundingContract.dailyAmount()).to.equal( + funding.day_funds + ); + }); + + it("Funding contract: should set the right token", async function () { + const { token, funding } = await loadFixture(deployContract); + expect(await funding.fundingContract.token()).to.equal( + await token.tokenContract.getAddress() + ); + }); + }); + + describe("Deposit functionality", function () { + it("Should allow deposit when properly funded and approved", async function () { + const { token, funding, service } = await loadFixture(deployContract); + + // Approve funding contract to spend user1's tokens + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 10n + ); + + // Initial deposit should succeed + await expect(funding.fundingContract.connect(funding.cron).deposit()) + .to.emit(funding.fundingContract, "DepositMade") + .withArgs(service.serviceContract.getAddress(), funding.day_funds, anyValue); + }); + + it("Should revert if user has insufficient balance for allowance", async function () { + const { token, funding } = await loadFixture(deployContract); + + // Transfer all tokens from user1 to user2 + await token.tokenContract.connect(token.user1).transfer( + token.user2.address, + await token.tokenContract.balanceOf(token.user1.address) + ); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds + ); + + await expect( + funding.fundingContract.connect(funding.cron).deposit() + ).to.be.revertedWith("Insufficient balance in owner's wallet"); + }); + + it("Should revert if allowance is insufficient", async function () { + const { token, funding } = await loadFixture(deployContract); + + // Approve one less than required amount + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds - 1n + ); + + await expect( + funding.fundingContract.connect(funding.cron).deposit() + ).to.be.revertedWith("Insufficient allowance from owner"); + }); + + it("Should revert if called twice within 24 hours", async function () { + const { token, funding } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 2n + ); + + // First deposit + await funding.fundingContract.connect(funding.cron).deposit(); + + // Second deposit should fail + await expect( + funding.fundingContract.connect(token.user1).deposit() + ).to.be.revertedWith("24 hours have not passed since last deposit"); + }); + + it("Should allow deposit after 24 hours", async function () { + const { token, funding } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 2n + ); + + // First deposit + await funding.fundingContract.connect(funding.cron).deposit(); + + // Increase time by 24 hours + await time.increase(24 * 60 * 60); + + // Second deposit should succeed + await expect( + funding.fundingContract.connect(funding.cron).deposit() + ).to.not.be.reverted; + }); + }); + + describe("canDeposit functionality", function () { + it("Should return true when all conditions are met", async function () { + const { token, funding } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds + ); + + expect(await funding.fundingContract.connect(funding.cron).canDeposit()) + .to.be.true; + }); + + it("Should return false right after a deposit", async function () { + const { token, funding } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 2n + ); + + await funding.fundingContract.connect(funding.cron).deposit(); + + expect(await funding.fundingContract.connect(funding.cron).canDeposit()) + .to.be.false; + }); + + it("Should return true again after 24 hours", async function () { + const { token, funding } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 2n + ); + + await funding.fundingContract.connect(funding.cron).deposit(); + await time.increase(24 * 60 * 60); + + expect(await funding.fundingContract.connect(funding.cron).canDeposit()) + .to.be.true; + }); + }); + + describe("Integration with service contract", function () { + it("Should successfully deposit to service contract", async function () { + const { token, funding, service } = await loadFixture(deployContract); + + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds + ); + + const initialBalance = await token.tokenContract.balanceOf( + service.serviceContract.getAddress() + ); + + await funding.fundingContract.connect(funding.cron).deposit(); + + // advance time and create a new block + const block1 = BigInt(await time.increase(30)); + + // the start time must be registered on the first succesfull call to "makeDeposit" + expect(await service.serviceContract.startTime()).to.lessThanOrEqual(block1, "not right"); + + expect(await token.tokenContract.balanceOf( + service.serviceContract.getAddress() + )).to.equal(initialBalance + funding.day_funds); + + }); + + it("Should correctly handle the entire flow", async function () { + const { token, funding, service } = await loadFixture(deployContract); + + // Initial approval + await token.tokenContract.connect(token.user1).approve( + funding.fundingContract.getAddress(), + funding.day_funds * 3n + ); + + // First deposit + await funding.fundingContract.connect(funding.cron).deposit(); + + // advance time and create a new block + const block1 = BigInt(await time.increase(30)); + + // Try immediate deposit (should fail) + await expect( + funding.fundingContract.connect(funding.cron).deposit() + ).to.be.revertedWith("24 hours have not passed since last deposit"); + + // Wait 24 hours + await time.increase(24 * 60 * 60); + + // Second deposit should succeed + await expect( + funding.fundingContract.connect(funding.cron).deposit() + ).to.not.be.reverted; + + // the start time must be registered on the first succesfull call to "deposit" + expect(await service.serviceContract.startTime()).to.lessThanOrEqual(block1, "not right"); + + // Verify final balance in service contract + expect(await token.tokenContract.balanceOf( + service.serviceContract.getAddress() + )).to.equal(funding.day_funds * 2n); + }); + }); +}); diff --git a/bca-token-solidity/test/BCA_Service.ts b/bca-token-solidity/test/BCA_ServiceInstance.ts similarity index 89% rename from bca-token-solidity/test/BCA_Service.ts rename to bca-token-solidity/test/BCA_ServiceInstance.ts index 4bc147b..837d0ca 100644 --- a/bca-token-solidity/test/BCA_Service.ts +++ b/bca-token-solidity/test/BCA_ServiceInstance.ts @@ -18,12 +18,12 @@ import { const tokenContract = await Token.deploy("Test token", "TOK1", minter, burner); const precision: bigint = await tokenContract.decimals().then(d => { if (d == 0n) {return 18n;} else {return d}; }); - const Contract = await hre.ethers.getContractFactory("BCAServiceContract"); + const Contract = await hre.ethers.getContractFactory("BCAServiceInstance"); const daily_price = 1n; // 1 token per 24h const day_price = (daily_price * BigInt(10n**precision)); assert(day_price > 0n, "tick price must be > 0: " + (day_price.toString())); - console.log(`tick price: ${day_price}`) - const serviceContract = await Contract.deploy(provider, tokenContract.getAddress(), day_price); + // console.log(`tick price: ${day_price}`) + const serviceContract = await Contract.deploy(provider, tokenContract.getAddress(), user1, day_price); // minting some tokens to the users const one_token = 1n * BigInt(10n**precision); @@ -107,17 +107,6 @@ import { }); describe("Deposit from user", function () { - it("The first deposit starts the service, and must be sufficient", async function () { - const { token, service } = await loadFixture(deployContract); - - let deposit = 1n * token.one_token / 10n; - - expect(await service.serviceContract.startTime()).to.eq(0, "good! not yet started"); - - // approve deposit amounts to be spent by the service contract - await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); - await expect(service.serviceContract.connect(service.user1).makeDeposit(deposit)).to.be.revertedWithCustomError(service.serviceContract, 'InsufficientAmount') - }); it("The user deposits to the contract and thus starts the service", async function () { const { token, service } = await loadFixture(deployContract); @@ -135,12 +124,6 @@ import { // advance time and create a new block const block2 = BigInt(await time.increase(30)); - // service contract is subscribed by user1; cannot be subscribed by another user - await expect(service.serviceContract.connect(service.user2).makeDeposit(token.one_token * 1n)).to.be.revertedWithCustomError(service.serviceContract, 'AlreadySubscribed()'); - - // advance time and create a new block - const block3 = BigInt(await time.increase(30)); - // increase the deposit - also increase the allowance deposit = token.one_token * 3n; await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -156,7 +139,7 @@ import { expect(await service.serviceContract.userAddress()).to.equal(service.user1.address); // check the deposit - expect(await service.serviceContract.deposit()).to.equal(5n * token.one_token - (2n * token.one_token / 10n)); + expect(await service.serviceContract.deposit()).to.equal(5n * token.one_token); // check the user's balance, and the provider's expect(await service.serviceContract.connect(service.user1).balanceUser()).to.lessThan(5n * token.one_token); @@ -169,7 +152,7 @@ import { it("The user deposits to the contract and thus starts the service, then stops the service", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -193,7 +176,7 @@ import { it("The user deposits to the contract and thus starts the service, then withdraws which stops the service", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -205,7 +188,7 @@ import { // check the user's balance: should be ~ 0.5; will pay a tick until next tx const ubal = await service.serviceContract.connect(service.user1).balanceUser() - (await service.serviceContract.dayPrice() / 24n / 3600n); const rem = await service.serviceContract.deposit() - console.log(`user's balance: ${ubal} deposit: ${rem} 24h price: ${await service.serviceContract.dayPrice()}`) + // console.log(`user's balance: ${ubal} deposit: ${rem} 24h price: ${await service.serviceContract.dayPrice()}`) // the user withdraws from the contract which stops the service expect(await service.serviceContract.connect(service.user1).withdrawUser(ubal)).to.emit(service.serviceContract, "ServiceStopped").withArgs('ticks'); @@ -227,7 +210,7 @@ import { it("The user stops a service, then sends deposits to the service which must fail", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -263,7 +246,7 @@ import { it("The user deposits to the contract and thus starts the service. The provider withdraws some amount.", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -300,7 +283,7 @@ import { it("The user cannot withdraw the provider's balance.", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -316,7 +299,7 @@ import { it("The provider cannot withdraw the user's balance.", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -332,7 +315,7 @@ import { it("The user cannot withdraw more than the balance.", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); @@ -348,7 +331,7 @@ import { it("The provider cannot withdraw more than the balance.", async function () { const { token, service } = await loadFixture(deployContract); - let deposit = token.one_token * 1n + await service.serviceContract.setupFee(); // this is sufficient for one day + let deposit = token.one_token * 1n; // this is sufficient for one day // approve deposit amounts to be spent by the service contract await token.tokenContract.connect(service.user1).approve(service.serviceContract.getAddress(), deposit); diff --git a/bca-token-solidity/test/BCA_ServiceManager.ts b/bca-token-solidity/test/BCA_ServiceManager.ts new file mode 100644 index 0000000..64b644c --- /dev/null +++ b/bca-token-solidity/test/BCA_ServiceManager.ts @@ -0,0 +1,80 @@ +import { + time, + loadFixture, + } from "@nomicfoundation/hardhat-toolbox/network-helpers"; + import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; + import { expect } from "chai"; + import hre from "hardhat"; + + describe("BCA Service Manager", function () { + // We define a fixture to reuse the same setup in every test. + // We use loadFixture to run this setup once, snapshot that state, + // and reset Hardhat Network to that snapshot in every test. + async function deployContract() { + // Contracts are deployed using the first signer/account by default + const [owner, minter, burner, provider1, provider2, user1, user2] = await hre.ethers.getSigners(); + + const Token = await hre.ethers.getContractFactory("BCAServiceToken"); + const tokenContract = await Token.deploy("Test token", "TOK1", minter, burner); + const precision: bigint = await tokenContract.decimals().then(d => { if (d == 0n) {return 18n;} else {return d}; }); + + const Manager = await hre.ethers.getContractFactory("BCAServiceManager"); + const serviceManager = await Manager.deploy(tokenContract.getAddress()); + + const addrController1 = await (await serviceManager.connect(owner).newController(provider1)).wait().then(_ => serviceManager.connect(owner).getControllerAddress(provider1)); + const serviceController1 = await hre.ethers.getContractAt("BCAServiceController", addrController1) + const addrController2 = await (await serviceManager.connect(owner).newController(provider2)).wait().then(_ => serviceManager.connect(owner).getControllerAddress(provider2)); + const serviceController2 = await hre.ethers.getContractAt("BCAServiceController", addrController2) + + // minting some tokens to the users + const one_token = 1n * BigInt(10n**precision); + expect(await tokenContract.connect(minter).mint(provider1.address, 10n*one_token)).to.changeTokenBalance(tokenContract, provider1, 10n*one_token); + expect(await tokenContract.connect(minter).mint(provider2.address, 10n*one_token)).to.changeTokenBalance(tokenContract, provider2, 10n*one_token); + expect(await tokenContract.connect(minter).mint(user1.address, 10n*one_token)).to.changeTokenBalance(tokenContract, user1, 10n*one_token); + expect(await tokenContract.connect(minter).mint(user2.address, 10n*one_token)).to.changeTokenBalance(tokenContract, user2, 10n*one_token); + + const startblocktime: bigint = BigInt(await time.increase(30)); + + return { token: { tokenContract, one_token, owner, minter, burner, user1, user2 }, + sm: { serviceManager, provider1, provider2 }, + sc1: { serviceController1, provider1 }, + sc2: { serviceController2, provider2 } }; + } + + describe("Deployment", function () { + it("Should have already deployed controllers", async function () { + const { sm } = await loadFixture(deployContract); + expect(await sm.serviceManager.countServiceControllers()).to.equal( + 2 + ); + }); + + it("Should set the right provider", async function () { + const { sc1 } = await loadFixture(deployContract); + expect(await sc1.serviceController1.providerAddress()).to.equal( + sc1.provider1.address + ); + }); + + it("Should set the right provider", async function () { + const { sc2 } = await loadFixture(deployContract); + expect(await sc2.serviceController2.providerAddress()).to.equal( + sc2.provider2.address + ); + }); + }); + + describe("Create new services", function () { + it("Should emit ServiceDeployed on new service creation", async function () { + const { sc1, sc2 } = await loadFixture(deployContract); + + await expect(sc1.serviceController1.connect(sc1.provider1).newService(3, 1n * 10n**18n)) + .to.emit(sc1.serviceController1, "ServiceDeployed") + .withArgs(anyValue); + await expect(sc2.serviceController2.connect(sc2.provider2).newService(99, 1n * 10n**18n / 10n)) + .to.emit(sc2.serviceController2, "ServiceDeployed") + .withArgs(anyValue); + }); + + }); +});