diff --git a/abi/README.md b/abi/README.md index e41b9d7d..dbbf341d 100644 --- a/abi/README.md +++ b/abi/README.md @@ -1,14 +1,15 @@ # ABI Files for Build-In System Contracts -| Contract Name | Address | ABI file name | -| ----------------------|-------------------------------------------|--------------------------------------------------- | -| BSCValidatorSet |0x0000000000000000000000000000000000001000 | [bscvalidatorset](bscvalidatorset.abi) | -| SlashIndicator |0x0000000000000000000000000000000000001001 | [slashindicator](slashindicator.abi) | -| SystemReward |0x0000000000000000000000000000000000001002 | [systemreward](systemreward.abi) | -| TendermintLightClient |0x0000000000000000000000000000000000001003 | [tendermintlightclient](tendermintlightclient.abi) | -| TokenHub |0x0000000000000000000000000000000000001004 | [tokenhub](tokenhub.abi) | -| RelayerIncentivize |0x0000000000000000000000000000000000001005 | [relayerincentivize](relayerincentivize.abi) | -| RelayerHub |0x0000000000000000000000000000000000001006 | [relayerhub](relayerhub.abi) | -| GovHub |0x0000000000000000000000000000000000001007 | [govhub](govhub.abi) | -| TokenManager |0x0000000000000000000000000000000000001008 | [tokenmanager](tokenmanager.abi) | -| CrossChain |0x0000000000000000000000000000000000002000 | [crosschain](crosschain.abi) | \ No newline at end of file +| Contract Name | Address | ABI file name | +|-----------------------|--------------------------------------------|----------------------------------------------------| +| BSCValidatorSet | 0x0000000000000000000000000000000000001000 | [bscvalidatorset](bscvalidatorset.abi) | +| SlashIndicator | 0x0000000000000000000000000000000000001001 | [slashindicator](slashindicator.abi) | +| SystemReward | 0x0000000000000000000000000000000000001002 | [systemreward](systemreward.abi) | +| TendermintLightClient | 0x0000000000000000000000000000000000001003 | [tendermintlightclient](tendermintlightclient.abi) | +| TokenHub | 0x0000000000000000000000000000000000001004 | [tokenhub](tokenhub.abi) | +| RelayerIncentivize | 0x0000000000000000000000000000000000001005 | [relayerincentivize](relayerincentivize.abi) | +| RelayerHub | 0x0000000000000000000000000000000000001006 | [relayerhub](relayerhub.abi) | +| GovHub | 0x0000000000000000000000000000000000001007 | [govhub](govhub.abi) | +| TokenManager | 0x0000000000000000000000000000000000001008 | [tokenmanager](tokenmanager.abi) | +| CrossChain | 0x0000000000000000000000000000000000002000 | [crosschain](crosschain.abi) | +| Staking | 0x0000000000000000000000000000000000002001 | [staking](staking.abi) | \ No newline at end of file diff --git a/abi/crosschain.abi b/abi/crosschain.abi index 815d0054..e84fcd2d 100644 --- a/abi/crosschain.abi +++ b/abi/crosschain.abi @@ -182,7 +182,6 @@ "type": "event" }, { - "constant": true, "inputs": [], "name": "ACK_PACKAGE", "outputs": [ @@ -192,12 +191,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "BIND_CHANNELID", "outputs": [ @@ -207,12 +204,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "CODE_OK", "outputs": [ @@ -222,12 +217,10 @@ "type": "uint32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "CROSS_CHAIN_CONTRACT_ADDR", "outputs": [ @@ -237,12 +230,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "CROSS_CHAIN_KEY_PREFIX", "outputs": [ @@ -252,12 +243,23 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, + "inputs": [], + "name": "CROSS_STAKE_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "ERROR_FAIL_DECODE", "outputs": [ @@ -267,12 +269,10 @@ "type": "uint32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "FAIL_ACK_PACKAGE", "outputs": [ @@ -282,12 +282,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "GOV_CHANNELID", "outputs": [ @@ -297,12 +295,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "GOV_HUB_ADDR", "outputs": [ @@ -312,12 +308,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "INCENTIVIZE_ADDR", "outputs": [ @@ -327,12 +321,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "INIT_BATCH_SIZE", "outputs": [ @@ -342,12 +334,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "LIGHT_CLIENT_ADDR", "outputs": [ @@ -357,12 +347,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "RELAYERHUB_CONTRACT_ADDR", "outputs": [ @@ -372,12 +360,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SLASH_CHANNELID", "outputs": [ @@ -387,12 +373,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SLASH_CONTRACT_ADDR", "outputs": [ @@ -402,12 +386,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "STAKING_CHANNELID", "outputs": [ @@ -417,12 +399,23 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, + "inputs": [], + "name": "STAKING_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "STORE_NAME", "outputs": [ @@ -432,12 +425,10 @@ "type": "string" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SYN_PACKAGE", "outputs": [ @@ -447,12 +438,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SYSTEM_REWARD_ADDR", "outputs": [ @@ -462,12 +451,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TOKEN_HUB_ADDR", "outputs": [ @@ -477,12 +464,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TOKEN_MANAGER_ADDR", "outputs": [ @@ -492,12 +477,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_CHANNELID", "outputs": [ @@ -507,12 +490,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_OUT_CHANNELID", "outputs": [ @@ -522,12 +503,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "VALIDATOR_CONTRACT_ADDR", "outputs": [ @@ -537,12 +516,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "alreadyInit", "outputs": [ @@ -552,12 +529,10 @@ "type": "bool" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "batchSizeForOracle", "outputs": [ @@ -567,12 +542,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "bscChainID", "outputs": [ @@ -582,12 +555,10 @@ "type": "uint16" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "uint8", @@ -603,12 +574,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "uint8", @@ -624,12 +593,10 @@ "type": "uint64" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "uint8", @@ -645,12 +612,10 @@ "type": "uint64" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "uint8", @@ -666,12 +631,10 @@ "type": "bool" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "oracleSequence", "outputs": [ @@ -681,12 +644,10 @@ "type": "int64" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "previousTxHeight", "outputs": [ @@ -696,12 +657,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "address", @@ -722,12 +681,10 @@ "type": "bool" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "txCounter", "outputs": [ @@ -737,21 +694,17 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": false, "inputs": [], "name": "init", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "uint8", @@ -777,12 +730,10 @@ "type": "bytes" } ], - "payable": false, "stateMutability": "pure", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "bytes", @@ -812,12 +763,10 @@ ], "name": "handlePackage", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "uint8", @@ -837,12 +786,10 @@ ], "name": "sendSynPackage", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "string", @@ -857,7 +804,6 @@ ], "name": "updateParam", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" } diff --git a/abi/staking.abi b/abi/staking.abi new file mode 100644 index 00000000..e6e623aa --- /dev/null +++ b/abi/staking.abi @@ -0,0 +1,1035 @@ +[ + { + "anonymous": false, + "inputs": [], + "name": "crashResponse", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oracleRelayerFee", + "type": "uint256" + } + ], + "name": "delegateSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "errCode", + "type": "uint8" + } + ], + "name": "failedDelegate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "valSrc", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "valDst", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "errCode", + "type": "uint8" + } + ], + "name": "failedRedelegate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "errCode", + "type": "uint256" + } + ], + "name": "failedSynPackage", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "errCode", + "type": "uint8" + } + ], + "name": "failedUndelegate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "key", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "value", + "type": "bytes" + } + ], + "name": "paramChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validatorSrc", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validatorDst", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oracleRelayerFee", + "type": "uint256" + } + ], + "name": "redelegateSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "rewardClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "rewardReceived", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oracleRelayerFee", + "type": "uint256" + } + ], + "name": "undelegateSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegatedClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegatedReceived", + "type": "event" + }, + { + "inputs": [], + "name": "BIND_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CODE_OK", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CROSS_CHAIN_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "CROSS_STAKE_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ERROR_FAIL_DECODE", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ERROR_UNKNOWN_PACKAGE_TYPE", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ERROR_WITHDRAW_BNB", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVENT_DELEGATE", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVENT_DISTRIBUTE_REWARD", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVENT_DISTRIBUTE_UNDELEGATED", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVENT_REDELEGATE", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "EVENT_UNDELEGATE", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GOV_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "GOV_HUB_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INCENTIVIZE_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INIT_MIN_DELEGATION_CHANGE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INIT_ORACLE_RELAYER_FEE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LIGHT_CLIENT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RELAYERHUB_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SLASH_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SLASH_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "STAKING_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "STAKING_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SYSTEM_REWARD_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TEN_DECIMALS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TOKEN_HUB_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TOKEN_MANAGER_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TRANSFER_IN_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TRANSFER_OUT_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "VALIDATOR_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "alreadyInit", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bscChainID", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minDelegationChange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "oracleRelayerFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "msgBytes", + "type": "bytes" + } + ], + "name": "handleSynPackage", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "msgBytes", + "type": "bytes" + } + ], + "name": "handleAckPackage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "handleFailAckPackage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "validatorSrc", + "type": "address" + }, + { + "internalType": "address", + "name": "validatorDst", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "redelegate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "claimReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "claimUndeldegated", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "getDelegated", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + } + ], + "name": "getDistributedReward", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + } + ], + "name": "getUndelegated", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "address", + "name": "validator", + "type": "address" + } + ], + "name": "getPendingUndelegated", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getOracleRelayerFee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinDelegationChange", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "key", + "type": "string" + }, + { + "internalType": "bytes", + "name": "value", + "type": "bytes" + } + ], + "name": "updateParam", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/abi/tokenhub.abi b/abi/tokenhub.abi index b6fd77de..1faeb599 100644 --- a/abi/tokenhub.abi +++ b/abi/tokenhub.abi @@ -1,7 +1,6 @@ [ { "inputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, @@ -200,12 +199,6 @@ "type": "event" }, { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - }, - { - "constant": true, "inputs": [], "name": "BEP2_TOKEN_DECIMALS", "outputs": [ @@ -215,12 +208,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "BEP2_TOKEN_SYMBOL_FOR_BNB", "outputs": [ @@ -230,12 +221,10 @@ "type": "bytes32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "BIND_CHANNELID", "outputs": [ @@ -245,12 +234,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "CODE_OK", "outputs": [ @@ -260,12 +247,10 @@ "type": "uint32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "CROSS_CHAIN_CONTRACT_ADDR", "outputs": [ @@ -275,12 +260,23 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, + "inputs": [], + "name": "CROSS_STAKE_CHANNELID", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "ERROR_FAIL_DECODE", "outputs": [ @@ -290,12 +286,10 @@ "type": "uint32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "GOV_CHANNELID", "outputs": [ @@ -305,12 +299,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "GOV_HUB_ADDR", "outputs": [ @@ -320,12 +312,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "INCENTIVIZE_ADDR", "outputs": [ @@ -335,12 +325,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "INIT_MINIMUM_RELAY_FEE", "outputs": [ @@ -350,12 +338,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "LIGHT_CLIENT_ADDR", "outputs": [ @@ -365,12 +351,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "MAXIMUM_BEP20_SYMBOL_LEN", "outputs": [ @@ -380,12 +364,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "MAX_BEP2_TOTAL_SUPPLY", "outputs": [ @@ -395,12 +377,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "MAX_GAS_FOR_CALLING_BEP20", "outputs": [ @@ -410,12 +390,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "MAX_GAS_FOR_TRANSFER_BNB", "outputs": [ @@ -425,12 +403,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "MINIMUM_BEP20_SYMBOL_LEN", "outputs": [ @@ -440,12 +416,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "RELAYERHUB_CONTRACT_ADDR", "outputs": [ @@ -455,12 +429,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "REWARD_UPPER_LIMIT", "outputs": [ @@ -470,12 +442,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SLASH_CHANNELID", "outputs": [ @@ -485,12 +455,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "SLASH_CONTRACT_ADDR", "outputs": [ @@ -500,12 +468,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "STAKING_CHANNELID", "outputs": [ @@ -515,12 +481,23 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, + "inputs": [], + "name": "STAKING_CONTRACT_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [], "name": "SYSTEM_REWARD_ADDR", "outputs": [ @@ -530,12 +507,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TEN_DECIMALS", "outputs": [ @@ -545,12 +520,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TOKEN_HUB_ADDR", "outputs": [ @@ -560,12 +533,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TOKEN_MANAGER_ADDR", "outputs": [ @@ -575,12 +546,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_CHANNELID", "outputs": [ @@ -590,12 +559,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_FAILURE_INSUFFICIENT_BALANCE", "outputs": [ @@ -605,12 +572,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_FAILURE_NON_PAYABLE_RECIPIENT", "outputs": [ @@ -620,12 +585,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_FAILURE_TIMEOUT", "outputs": [ @@ -635,12 +598,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_FAILURE_UNBOUND_TOKEN", "outputs": [ @@ -650,12 +611,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_FAILURE_UNKNOWN", "outputs": [ @@ -665,12 +624,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_IN_SUCCESS", "outputs": [ @@ -680,12 +637,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "TRANSFER_OUT_CHANNELID", "outputs": [ @@ -695,12 +650,10 @@ "type": "uint8" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "VALIDATOR_CONTRACT_ADDR", "outputs": [ @@ -710,12 +663,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "alreadyInit", "outputs": [ @@ -725,12 +676,10 @@ "type": "bool" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "address", @@ -746,12 +695,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "bscChainID", "outputs": [ @@ -761,12 +708,10 @@ "type": "uint16" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [], "name": "relayFee", "outputs": [ @@ -776,21 +721,21 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": false, + "stateMutability": "payable", + "type": "receive" + }, + { "inputs": [], "name": "init", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "address payable", @@ -811,12 +756,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": true, "inputs": [], "name": "getMiniRelayFee", "outputs": [ @@ -826,12 +769,10 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "uint8", @@ -852,12 +793,10 @@ "type": "bytes" } ], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "uint8", @@ -872,12 +811,10 @@ ], "name": "handleAckPackage", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "uint8", @@ -892,12 +829,10 @@ ], "name": "handleFailAckPackage", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "address", @@ -928,12 +863,10 @@ "type": "bool" } ], - "payable": true, "stateMutability": "payable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "address[]", @@ -964,12 +897,10 @@ "type": "bool" } ], - "payable": true, "stateMutability": "payable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "string", @@ -984,12 +915,10 @@ ], "name": "updateParam", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "bytes32", @@ -1005,12 +934,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "address", @@ -1026,12 +953,10 @@ "type": "bytes32" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "bytes32", @@ -1051,12 +976,10 @@ ], "name": "bindToken", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, "inputs": [ { "internalType": "bytes32", @@ -1071,12 +994,10 @@ ], "name": "unbindToken", "outputs": [], - "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "string", @@ -1092,12 +1013,10 @@ "type": "address" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, "inputs": [ { "internalType": "address", @@ -1113,8 +1032,26 @@ "type": "string" } ], - "payable": false, "stateMutability": "view", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawStakingBNB", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" } ] \ No newline at end of file diff --git a/contracts/CrossChain.sol b/contracts/CrossChain.sol index 53ede1eb..398890d7 100644 --- a/contracts/CrossChain.sol +++ b/contracts/CrossChain.sol @@ -127,7 +127,7 @@ contract CrossChain is System, ICrossChain, IParamSubscriber{ alreadyInit=true; } -function encodePayload(uint8 packageType, uint256 relayFee, bytes memory msgBytes) public pure returns(bytes memory) { + function encodePayload(uint8 packageType, uint256 relayFee, bytes memory msgBytes) public pure returns(bytes memory) { uint256 payloadLength = msgBytes.length + 33; bytes memory payload = new bytes(payloadLength); uint256 ptr; diff --git a/contracts/CrossChain.template b/contracts/CrossChain.template index c363268a..d2a269ba 100644 --- a/contracts/CrossChain.template +++ b/contracts/CrossChain.template @@ -128,7 +128,7 @@ contract CrossChain is System, ICrossChain, IParamSubscriber{ alreadyInit=true; } -function encodePayload(uint8 packageType, uint256 relayFee, bytes memory msgBytes) public pure returns(bytes memory) { + function encodePayload(uint8 packageType, uint256 relayFee, bytes memory msgBytes) public pure returns(bytes memory) { uint256 payloadLength = msgBytes.length + 33; bytes memory payload = new bytes(payloadLength); uint256 ptr; diff --git a/contracts/Staking.sol b/contracts/Staking.sol new file mode 100644 index 00000000..08118694 --- /dev/null +++ b/contracts/Staking.sol @@ -0,0 +1,578 @@ +pragma solidity 0.6.4; + +import "./System.sol"; +import "./lib/Memory.sol"; +import "./lib/BytesToTypes.sol"; +import "./interface/IApplication.sol"; +import "./interface/ICrossChain.sol"; +import "./interface/IParamSubscriber.sol"; +import "./interface/ITokenHub.sol"; +import "./lib/CmnPkg.sol"; +import "./lib/SafeMath.sol"; +import "./lib/RLPEncode.sol"; +import "./lib/RLPDecode.sol"; +import "./interface/IStaking.sol"; + +contract Staking is IStaking, System, IParamSubscriber, IApplication { + using SafeMath for uint256; + using RLPEncode for *; + using RLPDecode for *; + + // Cross Stake Event type + uint8 public constant EVENT_DELEGATE = 0x01; + uint8 public constant EVENT_UNDELEGATE = 0x02; + uint8 public constant EVENT_REDELEGATE = 0x03; + uint8 public constant EVENT_DISTRIBUTE_REWARD = 0x04; + uint8 public constant EVENT_DISTRIBUTE_UNDELEGATED = 0x05; + + // Error code + uint32 public constant ERROR_WITHDRAW_BNB = 101; + + uint256 public constant TEN_DECIMALS = 1e10; + + uint256 public constant INIT_ORACLE_RELAYER_FEE = 6e15; + uint256 public constant INIT_MIN_DELEGATION = 100 * 1e18; + + uint256 public oracleRelayerFee; + uint256 public minDelegation; + + mapping(address => uint256) delegated; // delegator => totalAmount + mapping(address => mapping(address => uint256)) delegatedOfValidator; // delegator => validator => amount + mapping(address => uint256) distributedReward; // delegator => reward + mapping(address => mapping(address => uint256)) pendingUndelegateTime; // delegator => validator => minTime + mapping(address => uint256) undelegated; // delegator => totalUndelegated + mapping(address => mapping(address => mapping(address => uint256))) pendingRedelegateTime; // delegator => srcValidator => dstValidator => minTime + + bool internal locked; + + modifier noReentrant() { + require(!locked, "No re-entrancy"); + locked = true; + _; + locked = false; + } + + modifier tenDecimalPrecision(uint256 amount) { + require(msg.value%TEN_DECIMALS==0 && amount%TEN_DECIMALS==0, "precision loss in conversion"); + _; + } + + modifier initParams() { + if (!alreadyInit) { + oracleRelayerFee = INIT_ORACLE_RELAYER_FEE; + minDelegation = INIT_MIN_DELEGATION; + alreadyInit = true; + } + _; + } + + /*********************************** Events **********************************/ + event delegateSubmitted(address indexed delegator, address indexed validator, uint256 amount, uint256 oracleRelayerFee); + event undelegateSubmitted(address indexed delegator, address indexed validator, uint256 amount, uint256 oracleRelayerFee); + event redelegateSubmitted(address indexed delegator, address indexed validatorSrc, address indexed validatorDst, uint256 amount, uint256 oracleRelayerFee); + event rewardReceived(address indexed delegator, uint256 amount); + event rewardClaimed(address indexed delegator, uint256 amount); + event undelegatedReceived(address indexed delegator, address indexed validator, uint256 amount); + event undelegatedClaimed(address indexed delegator, uint256 amount); + event failedDelegate(address indexed delegator, address indexed validator, uint256 amount, uint8 errCode); + event failedUndelegate(address indexed delegator, address indexed validator, uint256 amount, uint8 errCode); + event failedRedelegate(address indexed delegator, address indexed valSrc, address indexed valDst, uint256 amount, uint8 errCode); + event paramChange(string key, bytes value); + event failedSynPackage(uint8 indexed eventType, uint256 errCode); + event crashResponse(uint8 indexed eventType); + + receive() external payable {} + + /************************* Implement cross chain app *************************/ + function handleSynPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override returns(bytes memory) { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + uint32 resCode; + bytes memory ackPackage; + if (eventType == EVENT_DISTRIBUTE_REWARD) { + (resCode, ackPackage) = _handleDistributeRewardSynPackage(iter); + } else if (eventType == EVENT_DISTRIBUTE_UNDELEGATED) { + (resCode, ackPackage) = _handleDistributeUndelegatedSynPackage(iter); + } else { + require(false, "unknown event type"); + } + + if (resCode != CODE_OK) { + emit failedSynPackage(eventType, resCode); + } + return ackPackage; + } + + function handleAckPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + if (eventType == EVENT_DELEGATE) { + _handleDelegateAckPackage(iter); + } else if (eventType == EVENT_UNDELEGATE) { + _handleUndelegateAckPackage(iter); + } else if (eventType == EVENT_REDELEGATE) { + _handleRedelegateAckPackage(iter); + } else { + require(false, "unknown event type"); + } + return; + } + + function handleFailAckPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + if (eventType == EVENT_DELEGATE) { + _handleDelegateFailAckPackage(iter); + } else if (eventType == EVENT_UNDELEGATE) { + _handleUndelegateFailAckPackage(iter); + } else if (eventType == EVENT_REDELEGATE) { + _handleRedelegateFailAckPackage(iter); + } else { + require(false, "unknown event type"); + } + return; + } + + /***************************** External functions *****************************/ + function delegate(address validator, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(amount >= minDelegation, "invalid delegate amount"); + require(msg.value >= amount.add(oracleRelayerFee), "not enough msg value"); + require(payable(msg.sender).send(0), "invalid delegator"); // the msg sender must be payable + + uint256 convertedAmount = amount.div(TEN_DECIMALS); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = (msg.value).sub(amount); + + bytes[] memory elements = new bytes[](3); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validator.encodeAddress(); + elements[2] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_DELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(msg.value); + + delegated[msg.sender] = delegated[msg.sender].add(amount); + delegatedOfValidator[msg.sender][validator] = delegatedOfValidator[msg.sender][validator].add(amount); + + emit delegateSubmitted(msg.sender, validator, amount, _oracleRelayerFee); + } + + function undelegate(address validator, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(msg.value >= oracleRelayerFee, "not enough relay fee"); + if (amount < minDelegation) { + require(amount >= oracleRelayerFee, "not enough funds"); + require(amount == delegatedOfValidator[msg.sender][validator], "invalid amount"); + } + require(block.timestamp >= pendingUndelegateTime[msg.sender][validator], "pending undelegation exist"); + delegatedOfValidator[msg.sender][validator] = delegatedOfValidator[msg.sender][validator].sub(amount, "not enough funds"); + + uint256 convertedAmount = amount.div(TEN_DECIMALS); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = msg.value; + + bytes[] memory elements = new bytes[](3); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validator.encodeAddress(); + elements[2] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_UNDELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(_oracleRelayerFee); + + delegated[msg.sender] = delegated[msg.sender].sub(amount); + pendingUndelegateTime[msg.sender][validator] = block.timestamp.add(691200); // 8*24*3600 + + emit undelegateSubmitted(msg.sender, validator, amount, _oracleRelayerFee); + } + + function redelegate(address validatorSrc, address validatorDst, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(validatorSrc != validatorDst, "invalid redelegation"); + require(msg.value >= oracleRelayerFee, "not enough relay fee"); + require(amount >= minDelegation, "invalid amount"); + require(block.timestamp >= pendingRedelegateTime[msg.sender][validatorSrc][validatorDst] && + block.timestamp >= pendingRedelegateTime[msg.sender][validatorDst][validatorSrc], "pending redelegation exist"); + delegatedOfValidator[msg.sender][validatorSrc] = delegatedOfValidator[msg.sender][validatorSrc].sub(amount, "not enough funds"); + delegatedOfValidator[msg.sender][validatorDst] = delegatedOfValidator[msg.sender][validatorDst].add(amount); + + uint256 convertedAmount = amount.div(TEN_DECIMALS);// native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = msg.value; + + bytes[] memory elements = new bytes[](4); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validatorSrc.encodeAddress(); + elements[2] = validatorDst.encodeAddress(); + elements[3] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_REDELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(_oracleRelayerFee); + + pendingRedelegateTime[msg.sender][validatorSrc][validatorDst] = block.timestamp.add(691200); // 8*24*3600 + pendingRedelegateTime[msg.sender][validatorDst][validatorSrc] = block.timestamp.add(691200); // 8*24*3600 + + emit redelegateSubmitted(msg.sender, validatorSrc, validatorDst, amount, _oracleRelayerFee); + } + + function claimReward() override external noReentrant returns(uint256 amount) { + require(distributedReward[msg.sender] > 0, "no pending reward"); + + amount = distributedReward[msg.sender]; + distributedReward[msg.sender] = 0; + payable(msg.sender).transfer(amount); + emit rewardClaimed(msg.sender, amount); + } + + function claimUndeldegated() override external noReentrant returns(uint256 amount) { + require(undelegated[msg.sender] > 0, "no undelegated funds"); + + amount = undelegated[msg.sender]; + undelegated[msg.sender] = 0; + payable(msg.sender).transfer(amount); + emit undelegatedClaimed(msg.sender, amount); + } + + function getDelegated(address delegator, address validator) override external view returns(uint256) { + return delegatedOfValidator[delegator][validator]; + } + + function getTotalDelegated(address delegator) override external view returns(uint256) { + return delegated[delegator]; + } + + function getDistributedReward(address delegator) override external view returns(uint256) { + return distributedReward[delegator]; + } + + function getPendingRedelegateTime(address delegator, address valSrc, address valDst) override external view returns(uint256) { + return pendingRedelegateTime[delegator][valSrc][valDst]; + } + + function getUndelegated(address delegator) override external view returns(uint256) { + return undelegated[delegator]; + } + + function getPendingUndelegateTime(address delegator, address validator) override external view returns(uint256) { + return pendingUndelegateTime[delegator][validator]; + } + + function getOracleRelayerFee() override external view returns(uint256) { + return oracleRelayerFee; + } + + function getMinDelegation() override external view returns(uint256) { + return minDelegation; + } + + /***************************** Internal functions *****************************/ + function _RLPEncode(uint8 eventType, bytes memory msgBytes) internal pure returns(bytes memory output) { + bytes[] memory elements = new bytes[](2); + elements[0] = eventType.encodeUint(); + elements[1] = msgBytes.encodeBytes(); + output = elements.encodeList(); + } + + function _encodeRefundPackage(uint8 eventType, uint256 amount, address recipient, uint32 errorCode) internal pure returns(uint32, bytes memory) { + amount = amount.div(TEN_DECIMALS); + bytes[] memory elements = new bytes[](4); + elements[0] = eventType.encodeUint(); + elements[1] = amount.encodeUint(); + elements[2] = recipient.encodeAddress(); + elements[3] = errorCode.encodeUint(); + bytes memory packageBytes = elements.encodeList(); + return (errorCode, packageBytes); + } + + /******************************** Param update ********************************/ + function updateParam(string calldata key, bytes calldata value) override external onlyInit onlyGov { + if (Memory.compareStrings(key, "oracleRelayerFee")) { + require(value.length == 32, "length of oracleRelayerFee mismatch"); + uint256 newOracleRelayerFee = BytesToTypes.bytesToUint256(32, value); + require(newOracleRelayerFee >0, "the oracleRelayerFee must be greater than 0"); + oracleRelayerFee = newOracleRelayerFee; + } else if (Memory.compareStrings(key, "minDelegation")) { + require(value.length == 32, "length of minDelegation mismatch"); + uint256 newMinDelegation = BytesToTypes.bytesToUint256(32, value); + require(newMinDelegation > 0, "the minDelegation must be greater than 0"); + minDelegation = newMinDelegation; + } else { + require(false, "unknown param"); + } + emit paramChange(key, value); + } + + /************************* Handle cross-chain package *************************/ + function _handleDelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + amount = uint256(iter.next().toUint()); + } else if (idx == 3) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + require(ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount), "withdraw from tokenhub failed"); + + delegated[delegator] = delegated[delegator].sub(amount); + undelegated[delegator] = undelegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].sub(amount); + + emit failedDelegate(delegator, validator, amount, errCode); + } + + function _handleDelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + require(ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount), "withdraw from tokenhub failed"); + + delegated[delegator] = delegated[delegator].sub(amount); + undelegated[delegator] = undelegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].sub(amount); + + emit crashResponse(EVENT_DELEGATE); + } + + function _handleUndelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + amount = uint256(iter.next().toUint()); + } else if (idx == 3) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + delegated[delegator] = delegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].add(amount); + pendingUndelegateTime[delegator][validator] = 0; + + emit failedUndelegate(delegator, validator, amount, errCode); + } + + function _handleUndelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + delegated[delegator] = delegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].add(amount); + pendingUndelegateTime[delegator][validator] = 0; + + emit crashResponse(EVENT_UNDELEGATE); + } + + function _handleRedelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address valSrc; + address valDst; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + valSrc = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + valDst = address(uint160(iter.next().toAddress())); + } else if (idx == 3) { + amount = uint256(iter.next().toUint()); + } else if (idx == 4) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + delegatedOfValidator[delegator][valSrc] = delegatedOfValidator[delegator][valSrc].add(amount); + delegatedOfValidator[delegator][valDst] = delegatedOfValidator[delegator][valDst].sub(amount); + pendingRedelegateTime[delegator][valSrc][valDst] = 0; + pendingRedelegateTime[delegator][valDst][valSrc] = 0; + + emit failedRedelegate(delegator, valSrc, valDst, amount, errCode); + } + + function _handleRedelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address valSrc; + address valDst; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + valSrc = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + valDst = address(uint160(iter.next().toAddress())); + } else if (idx == 3) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + delegatedOfValidator[delegator][valSrc] = delegatedOfValidator[delegator][valSrc].add(amount); + delegatedOfValidator[delegator][valDst] = delegatedOfValidator[delegator][valDst].sub(amount); + pendingRedelegateTime[delegator][valSrc][valDst] = 0; + pendingRedelegateTime[delegator][valDst][valSrc] = 0; + + emit crashResponse(EVENT_REDELEGATE); + } + + function _handleDistributeRewardSynPackage(RLPDecode.Iterator memory iter) internal returns(uint32, bytes memory) { + bool success = false; + uint256 idx = 0; + uint256 amount; + address recipient; + while (iter.hasNext()) { + if (idx == 0) { + amount = uint256(iter.next().toUint()); + } else if (idx == 1) { + recipient = address(uint160(iter.next().toAddress())); + success = true; + } else { + break; + } + idx++; + } + if (!success) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_REWARD, amount, recipient, ERROR_FAIL_DECODE); + } + + bool ok = ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount); + if (!ok) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_REWARD, amount, recipient, ERROR_WITHDRAW_BNB); + } + + distributedReward[recipient] = distributedReward[recipient].add(amount); + + emit rewardReceived(recipient, amount); + return (CODE_OK, new bytes(0)); + } + + function _handleDistributeUndelegatedSynPackage(RLPDecode.Iterator memory iter) internal returns(uint32, bytes memory) { + bool success = false; + uint256 idx = 0; + uint256 amount; + address recipient; + address validator; + while (iter.hasNext()) { + if (idx == 0) { + amount = uint256(iter.next().toUint()); + } else if (idx == 1) { + recipient = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + validator = address(uint160(iter.next().toAddress())); + success = true; + } else { + break; + } + idx++; + } + if (!success) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_UNDELEGATED, amount, recipient, ERROR_FAIL_DECODE); + } + + bool ok = ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount); + if (!ok) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_UNDELEGATED, amount, recipient, ERROR_WITHDRAW_BNB); + } + + pendingUndelegateTime[recipient][validator] = 0; + undelegated[recipient] = undelegated[recipient].add(amount); + + emit undelegatedReceived(recipient, validator, amount); + return (CODE_OK, new bytes(0)); + } +} diff --git a/contracts/Staking.template b/contracts/Staking.template new file mode 100644 index 00000000..fcf686f7 --- /dev/null +++ b/contracts/Staking.template @@ -0,0 +1,585 @@ +pragma solidity 0.6.4; + +import "./System.sol"; +import "./lib/Memory.sol"; +import "./lib/BytesToTypes.sol"; +import "./interface/IApplication.sol"; +import "./interface/ICrossChain.sol"; +import "./interface/IParamSubscriber.sol"; +import "./interface/ITokenHub.sol"; +import "./lib/CmnPkg.sol"; +import "./lib/SafeMath.sol"; +import "./lib/RLPEncode.sol"; +import "./lib/RLPDecode.sol"; +import "./interface/IStaking.sol"; + +contract Staking is IStaking, System, IParamSubscriber, IApplication { + using SafeMath for uint256; + using RLPEncode for *; + using RLPDecode for *; + + // Cross Stake Event type + uint8 public constant EVENT_DELEGATE = 0x01; + uint8 public constant EVENT_UNDELEGATE = 0x02; + uint8 public constant EVENT_REDELEGATE = 0x03; + uint8 public constant EVENT_DISTRIBUTE_REWARD = 0x04; + uint8 public constant EVENT_DISTRIBUTE_UNDELEGATED = 0x05; + + // Error code + uint32 public constant ERROR_WITHDRAW_BNB = 101; + + uint256 public constant TEN_DECIMALS = 1e10; + + uint256 public constant INIT_ORACLE_RELAYER_FEE = 6e15; + uint256 public constant INIT_MIN_DELEGATION = 100 * 1e18; + + uint256 public oracleRelayerFee; + uint256 public minDelegation; + + mapping(address => uint256) delegated; // delegator => totalAmount + mapping(address => mapping(address => uint256)) delegatedOfValidator; // delegator => validator => amount + mapping(address => uint256) distributedReward; // delegator => reward + mapping(address => mapping(address => uint256)) pendingUndelegateTime; // delegator => validator => minTime + mapping(address => uint256) undelegated; // delegator => totalUndelegated + mapping(address => mapping(address => mapping(address => uint256))) pendingRedelegateTime; // delegator => srcValidator => dstValidator => minTime + + bool internal locked; + + modifier noReentrant() { + require(!locked, "No re-entrancy"); + locked = true; + _; + locked = false; + } + + modifier tenDecimalPrecision(uint256 amount) { + require(msg.value%TEN_DECIMALS==0 && amount%TEN_DECIMALS==0, "precision loss in conversion"); + _; + } + + modifier initParams() { + if (!alreadyInit) { + oracleRelayerFee = INIT_ORACLE_RELAYER_FEE; + minDelegation = INIT_MIN_DELEGATION; + alreadyInit = true; + } + _; + } + + /*********************************** Events **********************************/ + event delegateSubmitted(address indexed delegator, address indexed validator, uint256 amount, uint256 oracleRelayerFee); + event undelegateSubmitted(address indexed delegator, address indexed validator, uint256 amount, uint256 oracleRelayerFee); + event redelegateSubmitted(address indexed delegator, address indexed validatorSrc, address indexed validatorDst, uint256 amount, uint256 oracleRelayerFee); + event rewardReceived(address indexed delegator, uint256 amount); + event rewardClaimed(address indexed delegator, uint256 amount); + event undelegatedReceived(address indexed delegator, address indexed validator, uint256 amount); + event undelegatedClaimed(address indexed delegator, uint256 amount); + event failedDelegate(address indexed delegator, address indexed validator, uint256 amount, uint8 errCode); + event failedUndelegate(address indexed delegator, address indexed validator, uint256 amount, uint8 errCode); + event failedRedelegate(address indexed delegator, address indexed valSrc, address indexed valDst, uint256 amount, uint8 errCode); + event paramChange(string key, bytes value); + event failedSynPackage(uint8 indexed eventType, uint256 errCode); + event crashResponse(uint8 indexed eventType); +{% if mock %} + /*********************** init **************************/ + function init() external onlyNotInit { + oracleRelayerFee = INIT_ORACLE_RELAYER_FEE; + minDelegation = INIT_MIN_DELEGATION; + alreadyInit = true; + } +{% endif %} + receive() external payable {} + + /************************* Implement cross chain app *************************/ + function handleSynPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override returns(bytes memory) { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + uint32 resCode; + bytes memory ackPackage; + if (eventType == EVENT_DISTRIBUTE_REWARD) { + (resCode, ackPackage) = _handleDistributeRewardSynPackage(iter); + } else if (eventType == EVENT_DISTRIBUTE_UNDELEGATED) { + (resCode, ackPackage) = _handleDistributeUndelegatedSynPackage(iter); + } else { + require(false, "unknown event type"); + } + + if (resCode != CODE_OK) { + emit failedSynPackage(eventType, resCode); + } + return ackPackage; + } + + function handleAckPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + if (eventType == EVENT_DELEGATE) { + _handleDelegateAckPackage(iter); + } else if (eventType == EVENT_UNDELEGATE) { + _handleUndelegateAckPackage(iter); + } else if (eventType == EVENT_REDELEGATE) { + _handleRedelegateAckPackage(iter); + } else { + require(false, "unknown event type"); + } + return; + } + + function handleFailAckPackage(uint8, bytes calldata msgBytes) external onlyCrossChainContract initParams override { + RLPDecode.Iterator memory iter = msgBytes.toRLPItem().iterator(); + uint8 eventType = uint8(iter.next().toUint()); + if (eventType == EVENT_DELEGATE) { + _handleDelegateFailAckPackage(iter); + } else if (eventType == EVENT_UNDELEGATE) { + _handleUndelegateFailAckPackage(iter); + } else if (eventType == EVENT_REDELEGATE) { + _handleRedelegateFailAckPackage(iter); + } else { + require(false, "unknown event type"); + } + return; + } + + /***************************** External functions *****************************/ + function delegate(address validator, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(amount >= minDelegation, "invalid delegate amount"); + require(msg.value >= amount.add(oracleRelayerFee), "not enough msg value"); + require(payable(msg.sender).send(0), "invalid delegator"); // the msg sender must be payable + + uint256 convertedAmount = amount.div(TEN_DECIMALS); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = (msg.value).sub(amount); + + bytes[] memory elements = new bytes[](3); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validator.encodeAddress(); + elements[2] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_DELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(msg.value); + + delegated[msg.sender] = delegated[msg.sender].add(amount); + delegatedOfValidator[msg.sender][validator] = delegatedOfValidator[msg.sender][validator].add(amount); + + emit delegateSubmitted(msg.sender, validator, amount, _oracleRelayerFee); + } + + function undelegate(address validator, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(msg.value >= oracleRelayerFee, "not enough relay fee"); + if (amount < minDelegation) { + require(amount >= oracleRelayerFee, "not enough funds"); + require(amount == delegatedOfValidator[msg.sender][validator], "invalid amount"); + } + require(block.timestamp >= pendingUndelegateTime[msg.sender][validator], "pending undelegation exist"); + delegatedOfValidator[msg.sender][validator] = delegatedOfValidator[msg.sender][validator].sub(amount, "not enough funds"); + + uint256 convertedAmount = amount.div(TEN_DECIMALS); // native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = msg.value; + + bytes[] memory elements = new bytes[](3); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validator.encodeAddress(); + elements[2] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_UNDELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(_oracleRelayerFee); + + delegated[msg.sender] = delegated[msg.sender].sub(amount); + pendingUndelegateTime[msg.sender][validator] = block.timestamp.add(691200); // 8*24*3600 + + emit undelegateSubmitted(msg.sender, validator, amount, _oracleRelayerFee); + } + + function redelegate(address validatorSrc, address validatorDst, uint256 amount) override external payable tenDecimalPrecision(amount) initParams { + require(validatorSrc != validatorDst, "invalid redelegation"); + require(msg.value >= oracleRelayerFee, "not enough relay fee"); + require(amount >= minDelegation, "invalid amount"); + require(block.timestamp >= pendingRedelegateTime[msg.sender][validatorSrc][validatorDst] && + block.timestamp >= pendingRedelegateTime[msg.sender][validatorDst][validatorSrc], "pending redelegation exist"); + delegatedOfValidator[msg.sender][validatorSrc] = delegatedOfValidator[msg.sender][validatorSrc].sub(amount, "not enough funds"); + delegatedOfValidator[msg.sender][validatorDst] = delegatedOfValidator[msg.sender][validatorDst].add(amount); + + uint256 convertedAmount = amount.div(TEN_DECIMALS);// native bnb decimals is 8 on BBC, while the native bnb decimals on BSC is 18 + uint256 _oracleRelayerFee = msg.value; + + bytes[] memory elements = new bytes[](4); + elements[0] = msg.sender.encodeAddress(); + elements[1] = validatorSrc.encodeAddress(); + elements[2] = validatorDst.encodeAddress(); + elements[3] = convertedAmount.encodeUint(); + bytes memory msgBytes = elements.encodeList(); + ICrossChain(CROSS_CHAIN_CONTRACT_ADDR).sendSynPackage(CROSS_STAKE_CHANNELID, _RLPEncode(EVENT_REDELEGATE, msgBytes), _oracleRelayerFee.div(TEN_DECIMALS)); + payable(TOKEN_HUB_ADDR).transfer(_oracleRelayerFee); + + pendingRedelegateTime[msg.sender][validatorSrc][validatorDst] = block.timestamp.add(691200); // 8*24*3600 + pendingRedelegateTime[msg.sender][validatorDst][validatorSrc] = block.timestamp.add(691200); // 8*24*3600 + + emit redelegateSubmitted(msg.sender, validatorSrc, validatorDst, amount, _oracleRelayerFee); + } + + function claimReward() override external noReentrant returns(uint256 amount) { + require(distributedReward[msg.sender] > 0, "no pending reward"); + + amount = distributedReward[msg.sender]; + distributedReward[msg.sender] = 0; + payable(msg.sender).transfer(amount); + emit rewardClaimed(msg.sender, amount); + } + + function claimUndeldegated() override external noReentrant returns(uint256 amount) { + require(undelegated[msg.sender] > 0, "no undelegated funds"); + + amount = undelegated[msg.sender]; + undelegated[msg.sender] = 0; + payable(msg.sender).transfer(amount); + emit undelegatedClaimed(msg.sender, amount); + } + + function getDelegated(address delegator, address validator) override external view returns(uint256) { + return delegatedOfValidator[delegator][validator]; + } + + function getTotalDelegated(address delegator) override external view returns(uint256) { + return delegated[delegator]; + } + + function getDistributedReward(address delegator) override external view returns(uint256) { + return distributedReward[delegator]; + } + + function getPendingRedelegateTime(address delegator, address valSrc, address valDst) override external view returns(uint256) { + return pendingRedelegateTime[delegator][valSrc][valDst]; + } + + function getUndelegated(address delegator) override external view returns(uint256) { + return undelegated[delegator]; + } + + function getPendingUndelegateTime(address delegator, address validator) override external view returns(uint256) { + return pendingUndelegateTime[delegator][validator]; + } + + function getOracleRelayerFee() override external view returns(uint256) { + return oracleRelayerFee; + } + + function getMinDelegation() override external view returns(uint256) { + return minDelegation; + } + + /***************************** Internal functions *****************************/ + function _RLPEncode(uint8 eventType, bytes memory msgBytes) internal pure returns(bytes memory output) { + bytes[] memory elements = new bytes[](2); + elements[0] = eventType.encodeUint(); + elements[1] = msgBytes.encodeBytes(); + output = elements.encodeList(); + } + + function _encodeRefundPackage(uint8 eventType, uint256 amount, address recipient, uint32 errorCode) internal pure returns(uint32, bytes memory) { + amount = amount.div(TEN_DECIMALS); + bytes[] memory elements = new bytes[](4); + elements[0] = eventType.encodeUint(); + elements[1] = amount.encodeUint(); + elements[2] = recipient.encodeAddress(); + elements[3] = errorCode.encodeUint(); + bytes memory packageBytes = elements.encodeList(); + return (errorCode, packageBytes); + } + + /******************************** Param update ********************************/ + function updateParam(string calldata key, bytes calldata value) override external onlyInit onlyGov { + if (Memory.compareStrings(key, "oracleRelayerFee")) { + require(value.length == 32, "length of oracleRelayerFee mismatch"); + uint256 newOracleRelayerFee = BytesToTypes.bytesToUint256(32, value); + require(newOracleRelayerFee >0, "the oracleRelayerFee must be greater than 0"); + oracleRelayerFee = newOracleRelayerFee; + } else if (Memory.compareStrings(key, "minDelegation")) { + require(value.length == 32, "length of minDelegation mismatch"); + uint256 newMinDelegation = BytesToTypes.bytesToUint256(32, value); + require(newMinDelegation > 0, "the minDelegation must be greater than 0"); + minDelegation = newMinDelegation; + } else { + require(false, "unknown param"); + } + emit paramChange(key, value); + } + + /************************* Handle cross-chain package *************************/ + function _handleDelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + amount = uint256(iter.next().toUint()); + } else if (idx == 3) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + require(ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount), "withdraw from tokenhub failed"); + + delegated[delegator] = delegated[delegator].sub(amount); + undelegated[delegator] = undelegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].sub(amount); + + emit failedDelegate(delegator, validator, amount, errCode); + } + + function _handleDelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + require(ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount), "withdraw from tokenhub failed"); + + delegated[delegator] = delegated[delegator].sub(amount); + undelegated[delegator] = undelegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].sub(amount); + + emit crashResponse(EVENT_DELEGATE); + } + + function _handleUndelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + amount = uint256(iter.next().toUint()); + } else if (idx == 3) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + delegated[delegator] = delegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].add(amount); + pendingUndelegateTime[delegator][validator] = 0; + + emit failedUndelegate(delegator, validator, amount, errCode); + } + + function _handleUndelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address validator; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + validator = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + delegated[delegator] = delegated[delegator].add(amount); + delegatedOfValidator[delegator][validator] = delegatedOfValidator[delegator][validator].add(amount); + pendingUndelegateTime[delegator][validator] = 0; + + emit crashResponse(EVENT_UNDELEGATE); + } + + function _handleRedelegateAckPackage(RLPDecode.Iterator memory iter) internal { + bool success = false; + uint256 idx = 0; + address delegator; + address valSrc; + address valDst; + uint256 amount; + uint8 errCode; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + valSrc = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + valDst = address(uint160(iter.next().toAddress())); + } else if (idx == 3) { + amount = uint256(iter.next().toUint()); + } else if (idx == 4) { + errCode = uint8(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + delegatedOfValidator[delegator][valSrc] = delegatedOfValidator[delegator][valSrc].add(amount); + delegatedOfValidator[delegator][valDst] = delegatedOfValidator[delegator][valDst].sub(amount); + pendingRedelegateTime[delegator][valSrc][valDst] = 0; + pendingRedelegateTime[delegator][valDst][valSrc] = 0; + + emit failedRedelegate(delegator, valSrc, valDst, amount, errCode); + } + + function _handleRedelegateFailAckPackage(RLPDecode.Iterator memory paramBytes) internal { + RLPDecode.Iterator memory iter; + if (paramBytes.hasNext()) { + iter = paramBytes.next().toBytes().toRLPItem().iterator(); + } else { + require(false, "empty fail ack package"); + } + + bool success = false; + uint256 idx = 0; + address delegator; + address valSrc; + address valDst; + uint256 bcAmount; + while (iter.hasNext()) { + if (idx == 0) { + delegator = address(uint160(iter.next().toAddress())); + } else if (idx == 1) { + valSrc = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + valDst = address(uint160(iter.next().toAddress())); + } else if (idx == 3) { + bcAmount = uint256(iter.next().toUint()); + success = true; + } else { + break; + } + idx++; + } + require(success, "rlp decode package failed"); + + uint256 amount = bcAmount.mul(TEN_DECIMALS); + delegatedOfValidator[delegator][valSrc] = delegatedOfValidator[delegator][valSrc].add(amount); + delegatedOfValidator[delegator][valDst] = delegatedOfValidator[delegator][valDst].sub(amount); + pendingRedelegateTime[delegator][valSrc][valDst] = 0; + pendingRedelegateTime[delegator][valDst][valSrc] = 0; + + emit crashResponse(EVENT_REDELEGATE); + } + + function _handleDistributeRewardSynPackage(RLPDecode.Iterator memory iter) internal returns(uint32, bytes memory) { + bool success = false; + uint256 idx = 0; + uint256 amount; + address recipient; + while (iter.hasNext()) { + if (idx == 0) { + amount = uint256(iter.next().toUint()); + } else if (idx == 1) { + recipient = address(uint160(iter.next().toAddress())); + success = true; + } else { + break; + } + idx++; + } + if (!success) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_REWARD, amount, recipient, ERROR_FAIL_DECODE); + } + + bool ok = ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount); + if (!ok) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_REWARD, amount, recipient, ERROR_WITHDRAW_BNB); + } + + distributedReward[recipient] = distributedReward[recipient].add(amount); + + emit rewardReceived(recipient, amount); + return (CODE_OK, new bytes(0)); + } + + function _handleDistributeUndelegatedSynPackage(RLPDecode.Iterator memory iter) internal returns(uint32, bytes memory) { + bool success = false; + uint256 idx = 0; + uint256 amount; + address recipient; + address validator; + while (iter.hasNext()) { + if (idx == 0) { + amount = uint256(iter.next().toUint()); + } else if (idx == 1) { + recipient = address(uint160(iter.next().toAddress())); + } else if (idx == 2) { + validator = address(uint160(iter.next().toAddress())); + success = true; + } else { + break; + } + idx++; + } + if (!success) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_UNDELEGATED, amount, recipient, ERROR_FAIL_DECODE); + } + + bool ok = ITokenHub(TOKEN_HUB_ADDR).withdrawStakingBNB(amount); + if (!ok) { + return _encodeRefundPackage(EVENT_DISTRIBUTE_UNDELEGATED, amount, recipient, ERROR_WITHDRAW_BNB); + } + + pendingUndelegateTime[recipient][validator] = 0; + undelegated[recipient] = undelegated[recipient].add(amount); + + emit undelegatedReceived(recipient, validator, amount); + return (CODE_OK, new bytes(0)); + } +} diff --git a/contracts/System.sol b/contracts/System.sol index 7d64ddc2..bbfea7cd 100644 --- a/contracts/System.sol +++ b/contracts/System.sol @@ -17,6 +17,7 @@ contract System { uint8 constant public STAKING_CHANNELID = 0x08; uint8 constant public GOV_CHANNELID = 0x09; uint8 constant public SLASH_CHANNELID = 0x0b; + uint8 constant public CROSS_STAKE_CHANNELID = 0x10; uint16 constant public bscChainID = 0x0060; address public constant VALIDATOR_CONTRACT_ADDR = 0x0000000000000000000000000000000000001000; @@ -29,7 +30,7 @@ contract System { address public constant GOV_HUB_ADDR = 0x0000000000000000000000000000000000001007; address public constant TOKEN_MANAGER_ADDR = 0x0000000000000000000000000000000000001008; address public constant CROSS_CHAIN_CONTRACT_ADDR = 0x0000000000000000000000000000000000002000; - + address public constant STAKING_CONTRACT_ADDR = 0x0000000000000000000000000000000000002001; modifier onlyCoinbase() { require(msg.sender == block.coinbase, "the message sender must be the block producer"); diff --git a/contracts/System.template b/contracts/System.template index 4ceba2da..7daf4f7d 100644 --- a/contracts/System.template +++ b/contracts/System.template @@ -17,6 +17,7 @@ contract System { uint8 constant public STAKING_CHANNELID = 0x08; uint8 constant public GOV_CHANNELID = 0x09; uint8 constant public SLASH_CHANNELID = 0x0b; + uint8 constant public CROSS_STAKE_CHANNELID = 0x10; uint16 constant public bscChainID = 0x{{bscChainId}}; {% if mock %} address public VALIDATOR_CONTRACT_ADDR; @@ -29,9 +30,10 @@ contract System { address public GOV_HUB_ADDR; address public TOKEN_MANAGER_ADDR; address public CROSS_CHAIN_CONTRACT_ADDR; + address public STAKING_CONTRACT_ADDR; function updateContractAddr(address valAddr, address slashAddr, address rewardAddr, address lightAddr, address tokenHubAddr, - address incentivizeAddr, address relayerHubAddr, address govHub, address tokenManagerAddr, address crossChain) external { + address incentivizeAddr, address relayerHubAddr, address govHub, address tokenManagerAddr, address crossChain, address staking) external { VALIDATOR_CONTRACT_ADDR = valAddr; SLASH_CONTRACT_ADDR = slashAddr; SYSTEM_REWARD_ADDR = rewardAddr; @@ -42,6 +44,7 @@ contract System { GOV_HUB_ADDR = govHub; TOKEN_MANAGER_ADDR = tokenManagerAddr; CROSS_CHAIN_CONTRACT_ADDR = crossChain; + STAKING_CONTRACT_ADDR = staking; }{% else %} address public constant VALIDATOR_CONTRACT_ADDR = 0x0000000000000000000000000000000000001000; address public constant SLASH_CONTRACT_ADDR = 0x0000000000000000000000000000000000001001; @@ -53,6 +56,7 @@ contract System { address public constant GOV_HUB_ADDR = 0x0000000000000000000000000000000000001007; address public constant TOKEN_MANAGER_ADDR = 0x0000000000000000000000000000000000001008; address public constant CROSS_CHAIN_CONTRACT_ADDR = 0x0000000000000000000000000000000000002000; + address public constant STAKING_CONTRACT_ADDR = 0x0000000000000000000000000000000000002001; {% endif %} {% if mock %} address public constant SYSTEM_ADDRESS = 0x9fB29AAc15b9A4B7F17c3385939b007540f4d791; diff --git a/contracts/TokenHub.sol b/contracts/TokenHub.sol index 72a93e76..cd6a19dd 100644 --- a/contracts/TokenHub.sol +++ b/contracts/TokenHub.sol @@ -561,4 +561,10 @@ contract TokenHub is ITokenHub, System, IParamSubscriber, IApplication, ISystemR } return string(bep2Symbol); } + + function withdrawStakingBNB(uint256 amount) external override returns(bool) { + require(msg.sender == STAKING_CONTRACT_ADDR, "only staking system contract can call this function"); + payable(STAKING_CONTRACT_ADDR).transfer(amount); + return true; + } } diff --git a/contracts/TokenHub.template b/contracts/TokenHub.template index a2979006..03b8f3fc 100644 --- a/contracts/TokenHub.template +++ b/contracts/TokenHub.template @@ -561,4 +561,10 @@ contract TokenHub is ITokenHub, System, IParamSubscriber, IApplication, ISystemR } return string(bep2Symbol); } + + function withdrawStakingBNB(uint256 amount) external override returns(bool) { + require(msg.sender == STAKING_CONTRACT_ADDR, "only staking system contract can call this function"); + payable(STAKING_CONTRACT_ADDR).transfer(amount); + return true; + } } diff --git a/contracts/interface/ICrossChain.sol b/contracts/interface/ICrossChain.sol index 893c09f8..ad887f15 100644 --- a/contracts/interface/ICrossChain.sol +++ b/contracts/interface/ICrossChain.sol @@ -5,4 +5,4 @@ interface ICrossChain { * @dev Send package to Binance Chain */ function sendSynPackage(uint8 channelId, bytes calldata msgBytes, uint256 relayFee) external; -} \ No newline at end of file +} diff --git a/contracts/interface/IStaking.sol b/contracts/interface/IStaking.sol new file mode 100644 index 00000000..5d0db93c --- /dev/null +++ b/contracts/interface/IStaking.sol @@ -0,0 +1,30 @@ +pragma solidity 0.6.4; + +interface IStaking { + + function delegate(address validator, uint256 amount) external payable; + + function undelegate(address validator, uint256 amount) external payable; + + function redelegate(address validatorSrc, address validatorDst, uint256 amount) external payable; + + function claimReward() external returns(uint256); + + function claimUndeldegated() external returns(uint256); + + function getDelegated(address delegator, address validator) external view returns(uint256); + + function getTotalDelegated(address delegator) external view returns(uint256); + + function getDistributedReward(address delegator) external view returns(uint256); + + function getPendingRedelegateTime(address delegator, address valSrc, address valDst) external view returns(uint256); + + function getUndelegated(address delegator) external view returns(uint256); + + function getPendingUndelegateTime(address delegator, address validator) external view returns(uint256); + + function getOracleRelayerFee() external view returns(uint256); + + function getMinDelegation() external view returns(uint256); +} diff --git a/contracts/interface/ITokenHub.sol b/contracts/interface/ITokenHub.sol index 429001af..ac935c74 100644 --- a/contracts/interface/ITokenHub.sol +++ b/contracts/interface/ITokenHub.sol @@ -19,6 +19,5 @@ interface ITokenHub { function batchTransferOutBNB(address[] calldata recipientAddrs, uint256[] calldata amounts, address[] calldata refundAddrs, uint64 expireTime) external payable returns (bool); + function withdrawStakingBNB(uint256 amount) external returns(bool); } - - diff --git a/contracts/mock/MockTokenHub.sol b/contracts/mock/MockTokenHub.sol index 5d569fcb..46033607 100644 --- a/contracts/mock/MockTokenHub.sol +++ b/contracts/mock/MockTokenHub.sol @@ -37,6 +37,14 @@ contract MockTokenHub is ITokenHub { function setPanicBatchTransferOut(bool doPanic)external{ panicBatchTransferOut = doPanic; } + + function withdrawStakingBNB(uint256 amount) external override returns(bool) { + address STAKING_CONTRACT_ADDR = address(0x0000000000000000000000000000000000002001); + require(msg.sender == STAKING_CONTRACT_ADDR, + "only staking system contract can call this function"); + payable(STAKING_CONTRACT_ADDR).transfer(amount); + return true; + } } diff --git a/generate-genesis.js b/generate-genesis.js index 1c4cacbc..b4f447a6 100644 --- a/generate-genesis.js +++ b/generate-genesis.js @@ -38,6 +38,7 @@ require("./generate-tendermintlightclient"); require("./generate-relayerincentivizecontract"); require("./generate-crosschain"); require("./generate-slash"); +require("./generate-staking"); program.version("0.0.1") program.option( @@ -139,6 +140,11 @@ Promise.all([ "crossChain", "contracts/CrossChain.sol", "CrossChain" + ), + compileContract( + "staking", + "contracts/Staking.sol", + "Staking" ) ]).then(result => { diff --git a/generate-staking.js b/generate-staking.js new file mode 100644 index 00000000..6ba2540c --- /dev/null +++ b/generate-staking.js @@ -0,0 +1,31 @@ +const program = require("commander"); +const fs = require("fs"); +const nunjucks = require("nunjucks"); + +program.version("0.0.1"); +program.option( + "-t, --template