From 36db4ee1a849d108c7e6fdec0add7c9d5715fb7b Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Wed, 17 Dec 2025 11:34:31 +0800 Subject: [PATCH 1/3] Add staking queries and distr events to precompiles --- precompiles/distribution/Distribution.sol | 10 +- precompiles/distribution/abi.json | 2 +- precompiles/distribution/distribution.go | 92 +- precompiles/distribution/distribution_test.go | 22 +- precompiles/staking/Staking.sol | 213 +++ precompiles/staking/abi.json | 263 +-- precompiles/staking/staking.go | 762 ++++++++- precompiles/staking/staking_test.go | 1495 ++++++++++++++++- precompiles/utils/expected_keepers.go | 13 + 9 files changed, 2578 insertions(+), 294 deletions(-) diff --git a/precompiles/distribution/Distribution.sol b/precompiles/distribution/Distribution.sol index d47c389951..7c7ee8ff85 100644 --- a/precompiles/distribution/Distribution.sol +++ b/precompiles/distribution/Distribution.sol @@ -1,12 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -/// @title Distribution Precompile Interface -/// @notice This interface provides access to Cosmos SDK distribution module functionality /// @dev The distribution precompile is deployed at a fixed address and allows EVM contracts to interact with staking rewards address constant DISTR_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000001007; -/// @notice Global instance of the distribution precompile contract IDistr constant DISTR_CONTRACT = IDistr( DISTR_PRECOMPILE_ADDRESS ); @@ -15,6 +12,11 @@ IDistr constant DISTR_CONTRACT = IDistr( /// @notice Interface for interacting with the Cosmos SDK distribution module /// @dev This interface allows managing staking rewards, commission, and withdrawal addresses interface IDistr { + event WithdrawAddressSet(address indexed delegator, address withdrawAddr); + event DelegationRewardsWithdrawn(address indexed delegator, string validator, uint256 amount); + event MultipleDelegationRewardsWithdrawn(address indexed delegator, string[] validators, uint256[] amounts); + event ValidatorCommissionWithdrawn(string indexed validator, uint256 amount); + // Transactions /// @notice Sets the withdrawal address for the caller's staking rewards @@ -46,7 +48,7 @@ interface IDistr { /// @dev Returns rewards from all validators the address has delegated to /// @param delegatorAddress The EVM address of the delegator /// @return rewards Structured data containing all pending rewards - function rewards(address delegatorAddress) external view returns (Rewards rewards); + function rewards(address delegatorAddress) external view returns (Rewards memory rewards); /// @notice Represents a coin/token with amount, decimals, and denomination /// @dev Used to represent various tokens in the Cosmos ecosystem diff --git a/precompiles/distribution/abi.json b/precompiles/distribution/abi.json index 14a803b219..cc5781977c 100755 --- a/precompiles/distribution/abi.json +++ b/precompiles/distribution/abi.json @@ -1 +1 @@ -[{"inputs":[{"internalType":"address","name":"withdrawAddr","type":"address"}],"name":"setWithdrawAddress","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"validator","type":"string"}],"name":"withdrawDelegationRewards","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"validators","type":"string[]"}],"name":"withdrawMultipleDelegationRewards","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawValidatorCommission","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatorAddress","type":"address"}],"name":"rewards","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct Coin[]","name":"coins","type":"tuple[]"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct Reward[]","name":"rewards","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct Coin[]","name":"total","type":"tuple[]"}],"internalType":"struct Rewards","name":"rewards","type":"tuple"}],"stateMutability":"view","type":"function"}] +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DelegationRewardsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string[]","name":"validators","type":"string[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"MultipleDelegationRewardsWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ValidatorCommissionWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"address","name":"withdrawAddr","type":"address"}],"name":"WithdrawAddressSet","type":"event"},{"inputs":[{"internalType":"address","name":"delegatorAddress","type":"address"}],"name":"rewards","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IDistr.Coin[]","name":"coins","type":"tuple[]"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IDistr.Reward[]","name":"rewards","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IDistr.Coin[]","name":"total","type":"tuple[]"}],"internalType":"struct IDistr.Rewards","name":"rewards","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"withdrawAddr","type":"address"}],"name":"setWithdrawAddress","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"validator","type":"string"}],"name":"withdrawDelegationRewards","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"validators","type":"string[]"}],"name":"withdrawMultipleDelegationRewards","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawValidatorCommission","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/precompiles/distribution/distribution.go b/precompiles/distribution/distribution.go index b908bc7e14..726d7c31ea 100644 --- a/precompiles/distribution/distribution.go +++ b/precompiles/distribution/distribution.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" pcommon "github.com/sei-protocol/sei-chain/precompiles/common" "github.com/sei-protocol/sei-chain/precompiles/utils" @@ -25,12 +26,24 @@ const ( WithdrawMultipleDelegationRewardsMethod = "withdrawMultipleDelegationRewards" WithdrawValidatorCommissionMethod = "withdrawValidatorCommission" RewardsMethod = "rewards" + + SetWithdrawAddressEvent = "WithdrawAddressSet" + DelegationRewardsEvent = "DelegationRewardsWithdrawn" + MultipleDelegationRewardsEvent = "MultipleDelegationRewardsWithdrawn" + ValidatorCommissionEvent = "ValidatorCommissionWithdrawn" ) const ( DistrAddress = "0x0000000000000000000000000000000000001007" ) +var ( + SetWithdrawAddressEventSig = crypto.Keccak256Hash([]byte("WithdrawAddressSet(address,address)")) + DelegationRewardsEventSig = crypto.Keccak256Hash([]byte("DelegationRewardsWithdrawn(address,string,uint256)")) + MultipleDelegationRewardsEventSig = crypto.Keccak256Hash([]byte("MultipleDelegationRewardsWithdrawn(address,string[],uint256[])")) + ValidatorCommissionEventSig = crypto.Keccak256Hash([]byte("ValidatorCommissionWithdrawn(string,uint256)")) +) + // Embed abi json file to the executable binary. Needed when importing as dependency. // //go:embed abi.json @@ -46,6 +59,8 @@ type PrecompileExecutor struct { WithdrawMultipleDelegationRewardsID []byte WithdrawValidatorCommissionID []byte RewardsID []byte + + abi abi.ABI } func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { @@ -55,6 +70,7 @@ func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) distrKeeper: keepers.DistributionK(), evmKeeper: keepers.EVMK(), address: common.HexToAddress(DistrAddress), + abi: newAbi, } for name, m := range newAbi.Methods { @@ -84,17 +100,17 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller if readOnly { return nil, 0, errors.New("cannot call distr precompile from staticcall") } - return p.setWithdrawAddress(ctx, method, caller, args, value) + return p.setWithdrawAddress(ctx, method, caller, args, value, evm) case WithdrawDelegationRewardsMethod: if readOnly { return nil, 0, errors.New("cannot call distr precompile from staticcall") } - return p.withdrawDelegationRewards(ctx, method, caller, args, value) + return p.withdrawDelegationRewards(ctx, method, caller, args, value, evm) case WithdrawMultipleDelegationRewardsMethod: if readOnly { return nil, 0, errors.New("cannot call distr precompile from staticcall") } - return p.withdrawMultipleDelegationRewards(ctx, method, caller, args, value) + return p.withdrawMultipleDelegationRewards(ctx, method, caller, args, value, evm) case WithdrawValidatorCommissionMethod: if err = pcommon.ValidateNonPayable(value); err != nil { return nil, 0, err @@ -102,7 +118,7 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller if readOnly { return nil, 0, errors.New("cannot call distr precompile from staticcall") } - return p.withdrawValidatorCommission(ctx, method, caller) + return p.withdrawValidatorCommission(ctx, method, caller, evm) case RewardsMethod: return p.rewards(ctx, method, args) } @@ -113,7 +129,7 @@ func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { return p.evmKeeper } -func (p PrecompileExecutor) setWithdrawAddress(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, rerr error) { +func (p PrecompileExecutor) setWithdrawAddress(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) (ret []byte, remainingGas uint64, rerr error) { defer func() { if err := recover(); err != nil { ret = nil @@ -148,10 +164,23 @@ func (p PrecompileExecutor) setWithdrawAddress(ctx sdk.Context, method *abi.Meth } ret, rerr = method.Outputs.Pack(true) remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + + logData, err := p.abi.Events[SetWithdrawAddressEvent].Inputs.NonIndexed().Pack(args[0].(common.Address)) + if err != nil { + rerr = err + return + } + if err := pcommon.EmitEVMLog(evm, p.address, []common.Hash{ + SetWithdrawAddressEventSig, + common.BytesToHash(caller.Bytes()), + }, logData); err != nil { + rerr = err + return + } return } -func (p PrecompileExecutor) withdrawDelegationRewards(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, rerr error) { +func (p PrecompileExecutor) withdrawDelegationRewards(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) (ret []byte, remainingGas uint64, rerr error) { defer func() { if err := recover(); err != nil { ret = nil @@ -171,13 +200,26 @@ func (p PrecompileExecutor) withdrawDelegationRewards(ctx sdk.Context, method *a rerr = err return } - _, err = p.withdraw(ctx, delegator, args[0].(string)) + amts, err := p.withdraw(ctx, delegator, args[0].(string)) if err != nil { rerr = err return } ret, rerr = method.Outputs.Pack(true) remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + + logData, err := p.abi.Events[DelegationRewardsEvent].Inputs.NonIndexed().Pack(args[0].(string), amts.AmountOf(sdk.DefaultBondDenom).BigInt()) + if err != nil { + rerr = err + return + } + if err := pcommon.EmitEVMLog(evm, p.address, []common.Hash{ + DelegationRewardsEventSig, + common.BytesToHash(caller.Bytes()), + }, logData); err != nil { + rerr = err + return + } return } @@ -210,7 +252,7 @@ func (p PrecompileExecutor) getDelegator(ctx sdk.Context, caller common.Address) return delegator, nil } -func (p PrecompileExecutor) withdrawMultipleDelegationRewards(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int) (ret []byte, remainingGas uint64, rerr error) { +func (p PrecompileExecutor) withdrawMultipleDelegationRewards(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) (ret []byte, remainingGas uint64, rerr error) { defer func() { if err := recover(); err != nil { ret = nil @@ -231,16 +273,31 @@ func (p PrecompileExecutor) withdrawMultipleDelegationRewards(ctx sdk.Context, m return } validators := args[0].([]string) + amts := make([]*big.Int, 0, len(validators)) for _, valAddr := range validators { - _, err := p.withdraw(ctx, delegator, valAddr) + amt, err := p.withdraw(ctx, delegator, valAddr) if err != nil { rerr = err return } + amts = append(amts, amt.AmountOf(sdk.DefaultBondDenom).BigInt()) } ret, rerr = method.Outputs.Pack(true) remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + + logData, err := p.abi.Events[MultipleDelegationRewardsEvent].Inputs.NonIndexed().Pack(validators, amts) + if err != nil { + rerr = err + return + } + if err := pcommon.EmitEVMLog(evm, p.address, []common.Hash{ + MultipleDelegationRewardsEventSig, + common.BytesToHash(caller.Bytes()), + }, logData); err != nil { + rerr = err + return + } return } @@ -342,7 +399,7 @@ func getResponseOutput(response *distrtypes.QueryDelegationTotalRewardsResponse) } } -func (p PrecompileExecutor) withdrawValidatorCommission(ctx sdk.Context, method *abi.Method, caller common.Address) (ret []byte, remainingGas uint64, rerr error) { +func (p PrecompileExecutor) withdrawValidatorCommission(ctx sdk.Context, method *abi.Method, caller common.Address, evm *vm.EVM) (ret []byte, remainingGas uint64, rerr error) { defer func() { if err := recover(); err != nil { ret = nil @@ -367,7 +424,7 @@ func (p PrecompileExecutor) withdrawValidatorCommission(ctx sdk.Context, method } // Call the distribution keeper to withdraw validator commission - _, err = p.distrKeeper.WithdrawValidatorCommission(ctx, validator) + amts, err := p.distrKeeper.WithdrawValidatorCommission(ctx, validator) if err != nil { rerr = err return @@ -375,5 +432,18 @@ func (p PrecompileExecutor) withdrawValidatorCommission(ctx sdk.Context, method ret, rerr = method.Outputs.Pack(true) remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper) + + logData, err := p.abi.Events[ValidatorCommissionEvent].Inputs.NonIndexed().Pack(amts.AmountOf(sdk.DefaultBondDenom).BigInt()) + if err != nil { + rerr = err + return + } + if err := pcommon.EmitEVMLog(evm, p.address, []common.Hash{ + ValidatorCommissionEventSig, + common.BytesToHash([]byte(validator.String())), + }, logData); err != nil { + rerr = err + return + } return } diff --git a/precompiles/distribution/distribution_test.go b/precompiles/distribution/distribution_test.go index bcd706e6c7..c018931448 100644 --- a/precompiles/distribution/distribution_test.go +++ b/precompiles/distribution/distribution_test.go @@ -126,6 +126,12 @@ func TestWithdraw(t *testing.T) { require.Nil(t, err) require.Empty(t, res.VmError) require.Equal(t, withdrawSeiAddr.String(), testApp.DistrKeeper.GetDelegatorWithdrawAddr(ctx, seiAddr).String()) + receipt, err := k.GetTransientReceipt(ctx, tx.Hash(), 0) + require.Nil(t, err) + require.Equal(t, 1, len(receipt.Logs)) + require.Equal(t, distribution.SetWithdrawAddressEventSig, common.HexToHash(receipt.Logs[0].Topics[0])) + require.Equal(t, common.BytesToHash(evmAddr.Bytes()), common.HexToHash(receipt.Logs[0].Topics[1])) + require.NotEmpty(t, receipt.Logs[0].Data) // withdraw args, err = abi.Pack("withdrawDelegationRewards", val.String()) @@ -154,6 +160,13 @@ func TestWithdraw(t *testing.T) { // reinitialized d, found = testApp.StakingKeeper.GetDelegation(ctx, seiAddr, val) require.True(t, found) + + receipt, err = k.GetTransientReceipt(ctx, tx.Hash(), 0) + require.Nil(t, err) + require.Equal(t, 1, len(receipt.Logs)) + require.Equal(t, distribution.DelegationRewardsEventSig, common.HexToHash(receipt.Logs[0].Topics[0])) + require.Equal(t, common.BytesToHash(evmAddr.Bytes()), common.HexToHash(receipt.Logs[0].Topics[1])) + require.NotEmpty(t, receipt.Logs[0].Data) } func TestWithdrawMultipleDelegationRewards(t *testing.T) { @@ -273,7 +286,7 @@ func setWithdrawAddressAndWithdraw( res, err := msgServer.EVMTransaction(sdk.WrapSDKContext(ctx), req) require.Nil(t, err) require.Empty(t, res.VmError) - seiAddr, _ := testkeeper.PrivateKeyToAddresses(privKey) + seiAddr, evmAddr := testkeeper.PrivateKeyToAddresses(privKey) require.Equal(t, withdrawSeiAddr.String(), testApp.DistrKeeper.GetDelegatorWithdrawAddr(ctx, seiAddr).String()) var validators []string @@ -304,6 +317,13 @@ func setWithdrawAddressAndWithdraw( require.Empty(t, res.VmError) require.Equal(t, uint64(148290), res.GasUsed) + receipt, err := k.GetTransientReceipt(ctx, tx.Hash(), 0) + require.Nil(t, err) + require.Equal(t, 1, len(receipt.Logs)) + require.Equal(t, distribution.MultipleDelegationRewardsEventSig, common.HexToHash(receipt.Logs[0].Topics[0])) + require.Equal(t, common.BytesToHash(evmAddr.Bytes()), common.HexToHash(receipt.Logs[0].Topics[1])) + require.NotEmpty(t, receipt.Logs[0].Data) + // reinitialized for _, val := range vals { _, found := testApp.StakingKeeper.GetDelegation(ctx, seiAddr, val) diff --git a/precompiles/staking/Staking.sol b/precompiles/staking/Staking.sol index f698c704e1..02428c06a9 100644 --- a/precompiles/staking/Staking.sol +++ b/precompiles/staking/Staking.sol @@ -151,6 +151,133 @@ interface IStaking { string memory valAddress ) external view returns (Delegation memory delegation); + function validators( + string memory status, + bytes memory nextKey + ) external view returns (ValidatorsResponse memory response); + + /** + * @notice Get validator information for a given validator address + * @param validatorAddress The validator address + * @return validator Validator details + */ + function validator( + string memory validatorAddress + ) external view returns (Validator memory validator); + + /** + * @notice Get delegations for a validator + * @param validatorAddress The validator address + * @param nextKey Pagination key + * @return response Delegations response with pagination + */ + function validatorDelegations( + string memory validatorAddress, + bytes memory nextKey + ) external view returns (DelegationsResponse memory response); + + /** + * @notice Get unbonding delegations for a validator + * @param validatorAddress The validator address + * @param nextKey Pagination key + * @return response Unbonding delegations response with pagination + */ + function validatorUnbondingDelegations( + string memory validatorAddress, + bytes memory nextKey + ) external view returns (UnbondingDelegationsResponse memory response); + + /** + * @notice Get unbonding delegation information for a delegator and validator pair + * @param delegator The delegator's address + * @param validatorAddress The validator address + * @return unbondingDelegation Unbonding delegation details + */ + function unbondingDelegation( + address delegator, + string memory validatorAddress + ) external view returns (UnbondingDelegation memory unbondingDelegation); + + /** + * @notice Get all delegations for a delegator + * @param delegator The delegator's address + * @param nextKey Pagination key + * @return response Delegations response with pagination + */ + function delegatorDelegations( + address delegator, + bytes memory nextKey + ) external view returns (DelegationsResponse memory response); + + /** + * @notice Get validator information for a delegator and validator pair + * @param delegator The delegator's address + * @param validatorAddress The validator address + * @return validator Validator details + */ + function delegatorValidator( + address delegator, + string memory validatorAddress + ) external view returns (Validator memory validator); + + /** + * @notice Get all unbonding delegations for a delegator + * @param delegator The delegator's address + * @param nextKey Pagination key + * @return response Unbonding delegations response with pagination + */ + function delegatorUnbondingDelegations( + address delegator, + bytes memory nextKey + ) external view returns (UnbondingDelegationsResponse memory response); + + /** + * @notice Get redelegations + * @param delegator The delegator's address (empty string for all) + * @param srcValidator The source validator address (empty string for all) + * @param dstValidator The destination validator address (empty string for all) + * @param nextKey Pagination key + * @return response Redelegations response with pagination + */ + function redelegations( + string memory delegator, + string memory srcValidator, + string memory dstValidator, + bytes memory nextKey + ) external view returns (RedelegationsResponse memory response); + + /** + * @notice Get all validators for a delegator + * @param delegator The delegator's address + * @param nextKey Pagination key + * @return response Validators response with pagination + */ + function delegatorValidators( + address delegator, + bytes memory nextKey + ) external view returns (ValidatorsResponse memory response); + + /** + * @notice Get historical info for a given height + * @param height The block height + * @return historicalInfo Historical info + */ + function historicalInfo( + int64 height + ) external view returns (HistoricalInfo memory historicalInfo); + + /** + * @notice Get pool information + * @return pool Pool details + */ + function pool() external view returns (Pool memory pool); + + /** + * @notice Get staking parameters + * @return params Staking parameters + */ + function params() external view returns (Params memory params); + struct Delegation { Balance balance; DelegationDetails delegation; @@ -167,4 +294,90 @@ interface IStaking { uint256 decimals; string validator_address; } + + struct Validator { + string operatorAddress; + string consensusPubkey; + bool jailed; + int32 status; + string tokens; + string delegatorShares; + string description; + int64 unbondingHeight; + int64 unbondingTime; + string commissionRate; + string commissionMaxRate; + string commissionMaxChangeRate; + int64 commissionUpdateTime; + string minSelfDelegation; + } + + struct ValidatorsResponse { + Validator[] validators; + bytes nextKey; + } + + struct DelegationsResponse { + Delegation[] delegations; + bytes nextKey; + } + + struct UnbondingDelegationEntry { + int64 creationHeight; + int64 completionTime; + string initialBalance; + string balance; + } + + struct UnbondingDelegation { + string delegatorAddress; + string validatorAddress; + UnbondingDelegationEntry[] entries; + } + + struct UnbondingDelegationsResponse { + UnbondingDelegation[] unbondingDelegations; + bytes nextKey; + } + + struct RedelegationEntry { + int64 creationHeight; + int64 completionTime; + string initialBalance; + string sharesDst; + } + + struct Redelegation { + string delegatorAddress; + string validatorSrcAddress; + string validatorDstAddress; + RedelegationEntry[] entries; + } + + struct RedelegationsResponse { + Redelegation[] redelegations; + bytes nextKey; + } + + struct HistoricalInfo { + int64 height; + bytes header; + Validator[] validators; + } + + struct Pool { + string notBondedTokens; + string bondedTokens; + } + + struct Params { + uint64 unbondingTime; + uint32 maxValidators; + uint32 maxEntries; + uint32 historicalEntries; + string bondDenom; + string minCommissionRate; + string maxVotingPowerRatio; + string maxVotingPowerEnforcementThreshold; + } } diff --git a/precompiles/staking/abi.json b/precompiles/staking/abi.json index 8778d7d683..ae0f3cd2d2 100755 --- a/precompiles/staking/abi.json +++ b/precompiles/staking/abi.json @@ -1,262 +1 @@ -[ - { - "inputs": [ - { "internalType": "string", "name": "valAddress", "type": "string" } - ], - "name": "delegate", - "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "srcAddress", "type": "string" }, - { "internalType": "string", "name": "dstAddress", "type": "string" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "redelegate", - "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "valAddress", "type": "string" }, - { "internalType": "uint256", "name": "amount", "type": "uint256" } - ], - "name": "undelegate", - "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "pubKeyHex", "type": "string" }, - { "internalType": "string", "name": "moniker", "type": "string" }, - { "internalType": "string", "name": "commissionRate", "type": "string" }, - { - "internalType": "string", - "name": "commissionMaxRate", - "type": "string" - }, - { - "internalType": "string", - "name": "commissionMaxChangeRate", - "type": "string" - }, - { - "internalType": "uint256", - "name": "minSelfDelegation", - "type": "uint256" - } - ], - "name": "createValidator", - "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "string", "name": "moniker", "type": "string" }, - { "internalType": "string", "name": "commissionRate", "type": "string" }, - { - "internalType": "uint256", - "name": "minSelfDelegation", - "type": "uint256" - } - ], - "name": "editValidator", - "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { "internalType": "address", "name": "delegator", "type": "address" }, - { "internalType": "string", "name": "valAddress", "type": "string" } - ], - "name": "delegation", - "outputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { "internalType": "string", "name": "denom", "type": "string" } - ], - "internalType": "struct Balance", - "name": "balance", - "type": "tuple" - }, - { - "components": [ - { - "internalType": "string", - "name": "delegator_address", - "type": "string" - }, - { - "internalType": "uint256", - "name": "shares", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "decimals", - "type": "uint256" - }, - { - "internalType": "string", - "name": "validator_address", - "type": "string" - } - ], - "internalType": "struct DelegationDetails", - "name": "delegation", - "type": "tuple" - } - ], - "internalType": "struct Delegation", - "name": "delegation", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "validator", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Delegate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "srcValidator", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "dstValidator", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Redelegate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "delegator", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "validator", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "Undelegate", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "creator", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "validatorAddress", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "moniker", - "type": "string" - } - ], - "name": "ValidatorCreated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "editor", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "validatorAddress", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "moniker", - "type": "string" - } - ], - "name": "ValidatorEdited", - "type": "event" - } -] +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Delegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"srcValidator","type":"string"},{"indexed":false,"internalType":"string","name":"dstValidator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Undelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"editor","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorEdited","type":"event"},{"inputs":[{"internalType":"string","name":"pubKeyHex","type":"string"},{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"createValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegation","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation","name":"delegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"delegatorValidator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorValidators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"editValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int64","name":"height","type":"int64"}],"name":"historicalInfo","outputs":[{"components":[{"internalType":"int64","name":"height","type":"int64"},{"internalType":"bytes","name":"header","type":"bytes"},{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IStaking.HistoricalInfo","name":"historicalInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"components":[{"internalType":"uint64","name":"unbondingTime","type":"uint64"},{"internalType":"uint32","name":"maxValidators","type":"uint32"},{"internalType":"uint32","name":"maxEntries","type":"uint32"},{"internalType":"uint32","name":"historicalEntries","type":"uint32"},{"internalType":"string","name":"bondDenom","type":"string"},{"internalType":"string","name":"minCommissionRate","type":"string"},{"internalType":"string","name":"maxVotingPowerRatio","type":"string"},{"internalType":"string","name":"maxVotingPowerEnforcementThreshold","type":"string"}],"internalType":"struct IStaking.Params","name":"params","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"components":[{"internalType":"string","name":"notBondedTokens","type":"string"},{"internalType":"string","name":"bondedTokens","type":"string"}],"internalType":"struct IStaking.Pool","name":"pool","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"srcAddress","type":"string"},{"internalType":"string","name":"dstAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"delegator","type":"string"},{"internalType":"string","name":"srcValidator","type":"string"},{"internalType":"string","name":"dstValidator","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"redelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorSrcAddress","type":"string"},{"internalType":"string","name":"validatorDstAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"sharesDst","type":"string"}],"internalType":"struct IStaking.RedelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.Redelegation[]","name":"redelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.RedelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"unbondingDelegation","outputs":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation","name":"unbondingDelegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"validator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"status","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 5c744c4120..83fcb39efe 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -20,12 +21,25 @@ import ( ) const ( - DelegateMethod = "delegate" - RedelegateMethod = "redelegate" - UndelegateMethod = "undelegate" - DelegationMethod = "delegation" - CreateValidatorMethod = "createValidator" - EditValidatorMethod = "editValidator" + DelegateMethod = "delegate" + RedelegateMethod = "redelegate" + UndelegateMethod = "undelegate" + DelegationMethod = "delegation" + CreateValidatorMethod = "createValidator" + EditValidatorMethod = "editValidator" + ValidatorsMethod = "validators" + ValidatorMethod = "validator" + ValidatorDelegationsMethod = "validatorDelegations" + ValidatorUnbondingDelegationsMethod = "validatorUnbondingDelegations" + UnbondingDelegationMethod = "unbondingDelegation" + DelegatorDelegationsMethod = "delegatorDelegations" + DelegatorValidatorMethod = "delegatorValidator" + DelegatorUnbondingDelegationsMethod = "delegatorUnbondingDelegations" + RedelegationsMethod = "redelegations" + DelegatorValidatorsMethod = "delegatorValidators" + HistoricalInfoMethod = "historicalInfo" + PoolMethod = "pool" + ParamsMethod = "params" ) const ( @@ -44,12 +58,25 @@ type PrecompileExecutor struct { bankKeeper utils.BankKeeper address common.Address - DelegateID []byte - RedelegateID []byte - UndelegateID []byte - DelegationID []byte - CreateValidatorID []byte - EditValidatorID []byte + DelegateID []byte + RedelegateID []byte + UndelegateID []byte + DelegationID []byte + CreateValidatorID []byte + EditValidatorID []byte + ValidatorsID []byte + ValidatorID []byte + ValidatorDelegationsID []byte + ValidatorUnbondingDelegationsID []byte + UnbondingDelegationID []byte + DelegatorDelegationsID []byte + DelegatorValidatorID []byte + DelegatorUnbondingDelegationsID []byte + RedelegationsID []byte + DelegatorValidatorsID []byte + HistoricalInfoID []byte + PoolID []byte + ParamsID []byte } func NewPrecompile(keepers utils.Keepers) (*pcommon.Precompile, error) { @@ -77,6 +104,32 @@ func NewPrecompile(keepers utils.Keepers) (*pcommon.Precompile, error) { p.CreateValidatorID = m.ID case EditValidatorMethod: p.EditValidatorID = m.ID + case ValidatorsMethod: + p.ValidatorsID = m.ID + case ValidatorMethod: + p.ValidatorID = m.ID + case ValidatorDelegationsMethod: + p.ValidatorDelegationsID = m.ID + case ValidatorUnbondingDelegationsMethod: + p.ValidatorUnbondingDelegationsID = m.ID + case UnbondingDelegationMethod: + p.UnbondingDelegationID = m.ID + case DelegatorDelegationsMethod: + p.DelegatorDelegationsID = m.ID + case DelegatorValidatorMethod: + p.DelegatorValidatorID = m.ID + case DelegatorUnbondingDelegationsMethod: + p.DelegatorUnbondingDelegationsID = m.ID + case RedelegationsMethod: + p.RedelegationsID = m.ID + case DelegatorValidatorsMethod: + p.DelegatorValidatorsID = m.ID + case HistoricalInfoMethod: + p.HistoricalInfoID = m.ID + case PoolMethod: + p.PoolID = m.ID + case ParamsMethod: + p.ParamsID = m.ID } } @@ -95,7 +148,20 @@ func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64 return 100000 } else if bytes.Equal(method.ID, p.EditValidatorID) { return 100000 - } else if bytes.Equal(method.ID, p.DelegationID) { + } else if bytes.Equal(method.ID, p.DelegationID) || + bytes.Equal(method.ID, p.ValidatorID) || + bytes.Equal(method.ID, p.ValidatorDelegationsID) || + bytes.Equal(method.ID, p.ValidatorUnbondingDelegationsID) || + bytes.Equal(method.ID, p.UnbondingDelegationID) || + bytes.Equal(method.ID, p.DelegatorDelegationsID) || + bytes.Equal(method.ID, p.DelegatorValidatorID) || + bytes.Equal(method.ID, p.DelegatorUnbondingDelegationsID) || + bytes.Equal(method.ID, p.RedelegationsID) || + bytes.Equal(method.ID, p.DelegatorValidatorsID) || + bytes.Equal(method.ID, p.HistoricalInfoID) || + bytes.Equal(method.ID, p.PoolID) || + bytes.Equal(method.ID, p.ParamsID) || + bytes.Equal(method.ID, p.ValidatorsID) { return pcommon.DefaultGasCost(input, false) } // This should never happen since this is going to fail during Run @@ -134,6 +200,32 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller return p.editValidator(ctx, method, caller, args, value, hooks, evm) case DelegationMethod: return p.delegation(ctx, method, args, value) + case ValidatorsMethod: + return p.validators(ctx, method, args, value) + case ValidatorMethod: + return p.validator(ctx, method, args, value) + case ValidatorDelegationsMethod: + return p.validatorDelegations(ctx, method, args, value) + case ValidatorUnbondingDelegationsMethod: + return p.validatorUnbondingDelegations(ctx, method, args, value) + case UnbondingDelegationMethod: + return p.unbondingDelegation(ctx, method, args, value) + case DelegatorDelegationsMethod: + return p.delegatorDelegations(ctx, method, args, value) + case DelegatorValidatorMethod: + return p.delegatorValidator(ctx, method, args, value) + case DelegatorUnbondingDelegationsMethod: + return p.delegatorUnbondingDelegations(ctx, method, args, value) + case RedelegationsMethod: + return p.redelegations(ctx, method, args, value) + case DelegatorValidatorsMethod: + return p.delegatorValidators(ctx, method, args, value) + case HistoricalInfoMethod: + return p.historicalInfo(ctx, method, args, value) + case PoolMethod: + return p.pool(ctx, method, args, value) + case ParamsMethod: + return p.params(ctx, method, args, value) } return } @@ -299,6 +391,90 @@ func (p PrecompileExecutor) delegation(ctx sdk.Context, method *abi.Method, args return method.Outputs.Pack(delegation) } +type ValidatorsResponse struct { + Validators []Validator + NextKey []byte +} + +type DelegationsResponse struct { + Delegations []Delegation + NextKey []byte +} + +type UnbondingDelegationsResponse struct { + UnbondingDelegations []UnbondingDelegation + NextKey []byte +} + +type RedelegationsResponse struct { + Redelegations []Redelegation + NextKey []byte +} + +type Validator struct { + OperatorAddress string + ConsensusPubkey string + Jailed bool + Status int32 + Tokens string + DelegatorShares string + Description string + UnbondingHeight int64 + UnbondingTime int64 + CommissionRate string + CommissionMaxRate string + CommissionMaxChangeRate string + CommissionUpdateTime int64 + MinSelfDelegation string +} + +func (p PrecompileExecutor) validators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + validatorsRequest := &stakingtypes.QueryValidatorsRequest{ + Status: args[0].(string), + Pagination: &query.PageRequest{ + Key: args[1].([]byte), + }, + } + + validatorsResponse, err := p.stakingQuerier.Validators(sdk.WrapSDKContext(ctx), validatorsRequest) + if err != nil { + return nil, err + } + + res := ValidatorsResponse{ + Validators: make([]Validator, len(validatorsResponse.Validators)), + NextKey: validatorsResponse.Pagination.NextKey, + } + for i, validator := range validatorsResponse.Validators { + res.Validators[i] = Validator{ + OperatorAddress: validator.OperatorAddress, + ConsensusPubkey: string(validator.ConsensusPubkey.Value), + Jailed: validator.Jailed, + Status: int32(validator.Status), + Tokens: validator.Tokens.String(), + DelegatorShares: validator.DelegatorShares.String(), + Description: validator.Description.String(), + UnbondingHeight: validator.UnbondingHeight, + UnbondingTime: validator.UnbondingTime.Unix(), + CommissionRate: validator.Commission.Rate.String(), + CommissionMaxRate: validator.Commission.MaxRate.String(), + CommissionMaxChangeRate: validator.Commission.MaxChangeRate.String(), + CommissionUpdateTime: validator.Commission.UpdateTime.Unix(), + MinSelfDelegation: validator.MinSelfDelegation.String(), + } + } + + return method.Outputs.Pack(res) +} + func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, error) { if err := pcommon.ValidateArgsLength(args, 6); err != nil { return nil, err @@ -468,3 +644,563 @@ func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, c return method.Outputs.Pack(true) } + +func (p PrecompileExecutor) validator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 1); err != nil { + return nil, err + } + + validatorBech32 := args[0].(string) + validatorRequest := &stakingtypes.QueryValidatorRequest{ + ValidatorAddr: validatorBech32, + } + + validatorResponse, err := p.stakingQuerier.Validator(sdk.WrapSDKContext(ctx), validatorRequest) + if err != nil { + return nil, err + } + + validator := convertValidatorToPrecompileType(validatorResponse.Validator) + return method.Outputs.Pack(validator) +} + +func (p PrecompileExecutor) validatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + validatorBech32 := args[0].(string) + nextKey := args[1].([]byte) + + request := &stakingtypes.QueryValidatorDelegationsRequest{ + ValidatorAddr: validatorBech32, + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.ValidatorDelegations(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + delegations := make([]Delegation, len(response.DelegationResponses)) + for i, dr := range response.DelegationResponses { + delegations[i] = Delegation{ + Balance: Balance{ + Amount: dr.Balance.Amount.BigInt(), + Denom: dr.Balance.Denom, + }, + Delegation: DelegationDetails{ + DelegatorAddress: dr.Delegation.DelegatorAddress, + Shares: dr.Delegation.Shares.BigInt(), + Decimals: big.NewInt(sdk.Precision), + ValidatorAddress: dr.Delegation.ValidatorAddress, + }, + } + } + + result := DelegationsResponse{ + Delegations: delegations, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) validatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + validatorBech32 := args[0].(string) + nextKey := args[1].([]byte) + + request := &stakingtypes.QueryValidatorUnbondingDelegationsRequest{ + ValidatorAddr: validatorBech32, + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.ValidatorUnbondingDelegations(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + unbondingDelegations := make([]UnbondingDelegation, len(response.UnbondingResponses)) + for i, ubd := range response.UnbondingResponses { + entries := make([]UnbondingDelegationEntry, len(ubd.Entries)) + for j, entry := range ubd.Entries { + entries[j] = UnbondingDelegationEntry{ + CreationHeight: entry.CreationHeight, + CompletionTime: entry.CompletionTime.Unix(), + InitialBalance: entry.InitialBalance.String(), + Balance: entry.Balance.String(), + } + } + unbondingDelegations[i] = UnbondingDelegation{ + DelegatorAddress: ubd.DelegatorAddress, + ValidatorAddress: ubd.ValidatorAddress, + Entries: entries, + } + } + + result := UnbondingDelegationsResponse{ + UnbondingDelegations: unbondingDelegations, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) unbondingDelegation(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) + if err != nil { + return nil, err + } + + validatorBech32 := args[1].(string) + request := &stakingtypes.QueryUnbondingDelegationRequest{ + DelegatorAddr: seiDelegatorAddress.String(), + ValidatorAddr: validatorBech32, + } + + response, err := p.stakingQuerier.UnbondingDelegation(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + entries := make([]UnbondingDelegationEntry, len(response.Unbond.Entries)) + for i, entry := range response.Unbond.Entries { + entries[i] = UnbondingDelegationEntry{ + CreationHeight: entry.CreationHeight, + CompletionTime: entry.CompletionTime.Unix(), + InitialBalance: entry.InitialBalance.String(), + Balance: entry.Balance.String(), + } + } + + unbondingDelegation := UnbondingDelegation{ + DelegatorAddress: response.Unbond.DelegatorAddress, + ValidatorAddress: response.Unbond.ValidatorAddress, + Entries: entries, + } + + return method.Outputs.Pack(unbondingDelegation) +} + +func (p PrecompileExecutor) delegatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) + if err != nil { + return nil, err + } + + nextKey := args[1].([]byte) + request := &stakingtypes.QueryDelegatorDelegationsRequest{ + DelegatorAddr: seiDelegatorAddress.String(), + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.DelegatorDelegations(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + delegations := make([]Delegation, len(response.DelegationResponses)) + for i, dr := range response.DelegationResponses { + delegations[i] = Delegation{ + Balance: Balance{ + Amount: dr.Balance.Amount.BigInt(), + Denom: dr.Balance.Denom, + }, + Delegation: DelegationDetails{ + DelegatorAddress: dr.Delegation.DelegatorAddress, + Shares: dr.Delegation.Shares.BigInt(), + Decimals: big.NewInt(sdk.Precision), + ValidatorAddress: dr.Delegation.ValidatorAddress, + }, + } + } + + result := DelegationsResponse{ + Delegations: delegations, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) delegatorValidator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) + if err != nil { + return nil, err + } + + validatorBech32 := args[1].(string) + request := &stakingtypes.QueryDelegatorValidatorRequest{ + DelegatorAddr: seiDelegatorAddress.String(), + ValidatorAddr: validatorBech32, + } + + response, err := p.stakingQuerier.DelegatorValidator(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + validator := convertValidatorToPrecompileType(response.Validator) + return method.Outputs.Pack(validator) +} + +func (p PrecompileExecutor) delegatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) + if err != nil { + return nil, err + } + + nextKey := args[1].([]byte) + request := &stakingtypes.QueryDelegatorUnbondingDelegationsRequest{ + DelegatorAddr: seiDelegatorAddress.String(), + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.DelegatorUnbondingDelegations(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + unbondingDelegations := make([]UnbondingDelegation, len(response.UnbondingResponses)) + for i, ubd := range response.UnbondingResponses { + entries := make([]UnbondingDelegationEntry, len(ubd.Entries)) + for j, entry := range ubd.Entries { + entries[j] = UnbondingDelegationEntry{ + CreationHeight: entry.CreationHeight, + CompletionTime: entry.CompletionTime.Unix(), + InitialBalance: entry.InitialBalance.String(), + Balance: entry.Balance.String(), + } + } + unbondingDelegations[i] = UnbondingDelegation{ + DelegatorAddress: ubd.DelegatorAddress, + ValidatorAddress: ubd.ValidatorAddress, + Entries: entries, + } + } + + result := UnbondingDelegationsResponse{ + UnbondingDelegations: unbondingDelegations, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) redelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 4); err != nil { + return nil, err + } + + delegatorStr := args[0].(string) + srcValidatorStr := args[1].(string) + dstValidatorStr := args[2].(string) + nextKey := args[3].([]byte) + + request := &stakingtypes.QueryRedelegationsRequest{ + DelegatorAddr: delegatorStr, + SrcValidatorAddr: srcValidatorStr, + DstValidatorAddr: dstValidatorStr, + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.Redelegations(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + redelegations := make([]Redelegation, len(response.RedelegationResponses)) + for i, redel := range response.RedelegationResponses { + entries := make([]RedelegationEntry, len(redel.Entries)) + for j, entry := range redel.Entries { + entries[j] = RedelegationEntry{ + CreationHeight: entry.RedelegationEntry.CreationHeight, + CompletionTime: entry.RedelegationEntry.CompletionTime.Unix(), + InitialBalance: entry.RedelegationEntry.InitialBalance.String(), + SharesDst: entry.Balance.String(), + } + } + redelegations[i] = Redelegation{ + DelegatorAddress: redel.Redelegation.DelegatorAddress, + ValidatorSrcAddress: redel.Redelegation.ValidatorSrcAddress, + ValidatorDstAddress: redel.Redelegation.ValidatorDstAddress, + Entries: entries, + } + } + + result := RedelegationsResponse{ + Redelegations: redelegations, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) delegatorValidators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 2); err != nil { + return nil, err + } + + seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) + if err != nil { + return nil, err + } + + nextKey := args[1].([]byte) + request := &stakingtypes.QueryDelegatorValidatorsRequest{ + DelegatorAddr: seiDelegatorAddress.String(), + Pagination: &query.PageRequest{ + Key: nextKey, + }, + } + + response, err := p.stakingQuerier.DelegatorValidators(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + validators := make([]Validator, len(response.Validators)) + for i, val := range response.Validators { + validators[i] = convertValidatorToPrecompileType(val) + } + + result := ValidatorsResponse{ + Validators: validators, + NextKey: response.Pagination.NextKey, + } + + return method.Outputs.Pack(result) +} + +func (p PrecompileExecutor) historicalInfo(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 1); err != nil { + return nil, err + } + + height := args[0].(int64) + request := &stakingtypes.QueryHistoricalInfoRequest{ + Height: height, + } + + response, err := p.stakingQuerier.HistoricalInfo(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + if response.Hist == nil { + return nil, errors.New("historical info not found") + } + + validators := make([]Validator, len(response.Hist.Valset)) + for i, val := range response.Hist.Valset { + validators[i] = convertValidatorToPrecompileType(val) + } + + // Marshal header to bytes + headerBytes := []byte{} + if response.Hist.Header.Height > 0 { + // Header is a complex type, we'll serialize it if needed + // For now, we'll use empty bytes as header serialization is complex + headerBytes = []byte{} + } + + historicalInfo := HistoricalInfo{ + Height: height, // Use the requested height + Header: headerBytes, + Validators: validators, + } + + return method.Outputs.Pack(historicalInfo) +} + +func (p PrecompileExecutor) pool(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 0); err != nil { + return nil, err + } + + request := &stakingtypes.QueryPoolRequest{} + response, err := p.stakingQuerier.Pool(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + pool := Pool{ + NotBondedTokens: response.Pool.NotBondedTokens.String(), + BondedTokens: response.Pool.BondedTokens.String(), + } + + return method.Outputs.Pack(pool) +} + +func (p PrecompileExecutor) params(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { + if err := pcommon.ValidateNonPayable(value); err != nil { + return nil, err + } + + if err := pcommon.ValidateArgsLength(args, 0); err != nil { + return nil, err + } + + request := &stakingtypes.QueryParamsRequest{} + response, err := p.stakingQuerier.Params(sdk.WrapSDKContext(ctx), request) + if err != nil { + return nil, err + } + + params := Params{ + UnbondingTime: uint64(response.Params.UnbondingTime.Seconds()), + MaxValidators: response.Params.MaxValidators, + MaxEntries: response.Params.MaxEntries, + HistoricalEntries: response.Params.HistoricalEntries, + BondDenom: response.Params.BondDenom, + MinCommissionRate: response.Params.MinCommissionRate.String(), + MaxVotingPowerRatio: response.Params.MaxVotingPowerRatio.String(), + MaxVotingPowerEnforcementThreshold: response.Params.MaxVotingPowerEnforcementThreshold.String(), + } + + return method.Outputs.Pack(params) +} + +// Helper function to convert stakingtypes.Validator to precompile Validator type +func convertValidatorToPrecompileType(val stakingtypes.Validator) Validator { + return Validator{ + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + } +} + +// Additional types for new query methods +type UnbondingDelegationEntry struct { + CreationHeight int64 + CompletionTime int64 + InitialBalance string + Balance string +} + +type UnbondingDelegation struct { + DelegatorAddress string + ValidatorAddress string + Entries []UnbondingDelegationEntry +} + +type RedelegationEntry struct { + CreationHeight int64 + CompletionTime int64 + InitialBalance string + SharesDst string +} + +type Redelegation struct { + DelegatorAddress string + ValidatorSrcAddress string + ValidatorDstAddress string + Entries []RedelegationEntry +} + +type HistoricalInfo struct { + Height int64 + Header []byte + Validators []Validator +} + +type Pool struct { + NotBondedTokens string + BondedTokens string +} + +type Params struct { + UnbondingTime uint64 + MaxValidators uint32 + MaxEntries uint32 + HistoricalEntries uint32 + BondDenom string + MinCommissionRate string + MaxVotingPowerRatio string + MaxVotingPowerEnforcementThreshold string +} diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index a25087b21e..c5ac50c031 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -11,9 +11,11 @@ import ( "testing" "time" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" crptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking/teststaking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -261,14 +263,79 @@ func setupValidator(t *testing.T, ctx sdk.Context, a *app.App, bondStatus stakin } type TestStakingQuerier struct { - Response *stakingtypes.QueryDelegationResponse - Err error + Response *stakingtypes.QueryDelegationResponse + ValidatorsResponse *stakingtypes.QueryValidatorsResponse + ValidatorResponse *stakingtypes.QueryValidatorResponse + ValidatorDelegationsResponse *stakingtypes.QueryValidatorDelegationsResponse + ValidatorUnbondingDelegationsResponse *stakingtypes.QueryValidatorUnbondingDelegationsResponse + UnbondingDelegationResponse *stakingtypes.QueryUnbondingDelegationResponse + DelegatorDelegationsResponse *stakingtypes.QueryDelegatorDelegationsResponse + DelegatorValidatorResponse *stakingtypes.QueryDelegatorValidatorResponse + DelegatorUnbondingDelegationsResponse *stakingtypes.QueryDelegatorUnbondingDelegationsResponse + RedelegationsResponse *stakingtypes.QueryRedelegationsResponse + DelegatorValidatorsResponse *stakingtypes.QueryDelegatorValidatorsResponse + HistoricalInfoResponse *stakingtypes.QueryHistoricalInfoResponse + PoolResponse *stakingtypes.QueryPoolResponse + ParamsResponse *stakingtypes.QueryParamsResponse + Err error } func (tq *TestStakingQuerier) Delegation(c context.Context, _ *stakingtypes.QueryDelegationRequest) (*stakingtypes.QueryDelegationResponse, error) { return tq.Response, tq.Err } +func (tq *TestStakingQuerier) Validators(c context.Context, _ *stakingtypes.QueryValidatorsRequest) (*stakingtypes.QueryValidatorsResponse, error) { + return tq.ValidatorsResponse, tq.Err +} + +func (tq *TestStakingQuerier) Validator(c context.Context, _ *stakingtypes.QueryValidatorRequest) (*stakingtypes.QueryValidatorResponse, error) { + return tq.ValidatorResponse, tq.Err +} + +func (tq *TestStakingQuerier) ValidatorDelegations(c context.Context, _ *stakingtypes.QueryValidatorDelegationsRequest) (*stakingtypes.QueryValidatorDelegationsResponse, error) { + return tq.ValidatorDelegationsResponse, tq.Err +} + +func (tq *TestStakingQuerier) ValidatorUnbondingDelegations(c context.Context, _ *stakingtypes.QueryValidatorUnbondingDelegationsRequest) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) { + return tq.ValidatorUnbondingDelegationsResponse, tq.Err +} + +func (tq *TestStakingQuerier) UnbondingDelegation(c context.Context, _ *stakingtypes.QueryUnbondingDelegationRequest) (*stakingtypes.QueryUnbondingDelegationResponse, error) { + return tq.UnbondingDelegationResponse, tq.Err +} + +func (tq *TestStakingQuerier) DelegatorDelegations(c context.Context, _ *stakingtypes.QueryDelegatorDelegationsRequest) (*stakingtypes.QueryDelegatorDelegationsResponse, error) { + return tq.DelegatorDelegationsResponse, tq.Err +} + +func (tq *TestStakingQuerier) DelegatorValidator(c context.Context, _ *stakingtypes.QueryDelegatorValidatorRequest) (*stakingtypes.QueryDelegatorValidatorResponse, error) { + return tq.DelegatorValidatorResponse, tq.Err +} + +func (tq *TestStakingQuerier) DelegatorUnbondingDelegations(c context.Context, _ *stakingtypes.QueryDelegatorUnbondingDelegationsRequest) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) { + return tq.DelegatorUnbondingDelegationsResponse, tq.Err +} + +func (tq *TestStakingQuerier) Redelegations(c context.Context, _ *stakingtypes.QueryRedelegationsRequest) (*stakingtypes.QueryRedelegationsResponse, error) { + return tq.RedelegationsResponse, tq.Err +} + +func (tq *TestStakingQuerier) DelegatorValidators(c context.Context, _ *stakingtypes.QueryDelegatorValidatorsRequest) (*stakingtypes.QueryDelegatorValidatorsResponse, error) { + return tq.DelegatorValidatorsResponse, tq.Err +} + +func (tq *TestStakingQuerier) HistoricalInfo(c context.Context, _ *stakingtypes.QueryHistoricalInfoRequest) (*stakingtypes.QueryHistoricalInfoResponse, error) { + return tq.HistoricalInfoResponse, tq.Err +} + +func (tq *TestStakingQuerier) Pool(c context.Context, _ *stakingtypes.QueryPoolRequest) (*stakingtypes.QueryPoolResponse, error) { + return tq.PoolResponse, tq.Err +} + +func (tq *TestStakingQuerier) Params(c context.Context, _ *stakingtypes.QueryParamsRequest) (*stakingtypes.QueryParamsResponse, error) { + return tq.ParamsResponse, tq.Err +} + func TestPrecompile_Run_Delegation(t *testing.T) { callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() _, unassociatedEvmAddress := testkeeper.MockAddressPair() @@ -480,6 +547,1430 @@ func TestPrecompile_Run_Delegation(t *testing.T) { } } +func TestPrecompile_Run_Validators(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + validatorsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).ValidatorsID) + + // Build a single validator in the staking module format. + val := stakingtypes.Validator{ + OperatorAddress: "seivaloper1validator", + ConsensusPubkey: &codectypes.Any{Value: []byte("pubkey-bytes")}, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: sdk.NewInt(1000), + DelegatorShares: sdk.NewDec(1000), + Description: stakingtypes.NewDescription( + "moniker", + "identity", + "website", + "security", + "details", + ), + UnbondingHeight: 10, + UnbondingTime: time.Unix(1234, 0), + Commission: stakingtypes.Commission{ + CommissionRates: stakingtypes.CommissionRates{ + Rate: sdk.NewDecWithPrec(10, 2), + MaxRate: sdk.NewDecWithPrec(20, 2), + MaxChangeRate: sdk.NewDecWithPrec(1, 2), + }, + UpdateTime: time.Unix(5678, 0), + }, + MinSelfDelegation: sdk.NewInt(5), + } + + nextKey := []byte("next-key") + validatorsResponse := &stakingtypes.QueryValidatorsResponse{ + Validators: []stakingtypes.Validator{val}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + expected := staking.ValidatorsResponse{ + Validators: []staking.Validator{ + { + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := validatorsMethod.Outputs.Pack(expected) + + type fields struct { + stakingQuerier utils.StakingQuerier + } + type args struct { + status string + key []byte + value *big.Int + readOnly bool + } + + tests := []struct { + name string + fields fields + args args + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: fields{ + stakingQuerier: &TestStakingQuerier{ + ValidatorsResponse: validatorsResponse, + }, + }, + args: args{ + status: "BOND_STATUS_BONDED", + key: []byte{}, + value: big.NewInt(1), + }, + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return validators and next key (static call allowed)", + fields: fields{ + stakingQuerier: &TestStakingQuerier{ + ValidatorsResponse: validatorsResponse, + }, + }, + args: args{ + status: "BOND_STATUS_BONDED", + key: []byte{}, + readOnly: true, + }, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields.stakingQuerier, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).ValidatorsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args.status, tt.args.key) + require.NoError(t, err) + + // Caller and callingContract are irrelevant for validators (query-only). + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.args.value, tt.args.readOnly, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + if tt.wantErrMsg != "" { + require.Contains(t, tt.wantErrMsg, tt.wantErrMsg) + } + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +// Helper function to create a test validator +func createTestValidator() stakingtypes.Validator { + return stakingtypes.Validator{ + OperatorAddress: "seivaloper1validator", + ConsensusPubkey: &codectypes.Any{Value: []byte("pubkey-bytes")}, + Jailed: false, + Status: stakingtypes.Bonded, + Tokens: sdk.NewInt(1000), + DelegatorShares: sdk.NewDec(1000), + Description: stakingtypes.NewDescription( + "moniker", + "identity", + "website", + "security", + "details", + ), + UnbondingHeight: 10, + UnbondingTime: time.Unix(1234, 0), + Commission: stakingtypes.Commission{ + CommissionRates: stakingtypes.CommissionRates{ + Rate: sdk.NewDecWithPrec(10, 2), + MaxRate: sdk.NewDecWithPrec(20, 2), + MaxChangeRate: sdk.NewDecWithPrec(1, 2), + }, + UpdateTime: time.Unix(5678, 0), + }, + MinSelfDelegation: sdk.NewInt(5), + } +} + +func TestPrecompile_Run_Validator(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + validatorMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).ValidatorID) + + val := createTestValidator() + validatorResponse := &stakingtypes.QueryValidatorResponse{ + Validator: val, + } + + expected := staking.Validator{ + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + } + + happyPathPackedOutput, _ := validatorMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + ValidatorResponse: validatorResponse, + }, + args: []interface{}{val.OperatorAddress}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return validator (static call allowed)", + fields: &TestStakingQuerier{ + ValidatorResponse: validatorResponse, + }, + args: []interface{}{val.OperatorAddress}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).ValidatorID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_ValidatorDelegations(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + validatorDelegationsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).ValidatorDelegationsID) + + callerSeiAddress, _ := testkeeper.MockAddressPair() + validatorAddress := "seivaloper134ykhqrkyda72uq7f463ne77e4tn99steprmz7" + shares := 100 + delegationResponse := &stakingtypes.DelegationResponse{ + Delegation: stakingtypes.Delegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Shares: sdk.NewDec(int64(shares)), + }, + Balance: sdk.NewCoin("usei", sdk.NewInt(int64(shares))), + } + + nextKey := []byte("next-key") + validatorDelegationsResponse := &stakingtypes.QueryValidatorDelegationsResponse{ + DelegationResponses: []stakingtypes.DelegationResponse{*delegationResponse}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + hundredSharesValue := new(big.Int) + hundredSharesValue.SetString("100000000000000000000", 10) + expected := staking.DelegationsResponse{ + Delegations: []staking.Delegation{ + { + Balance: staking.Balance{ + Amount: big.NewInt(int64(shares)), + Denom: "usei", + }, + Delegation: staking.DelegationDetails{ + DelegatorAddress: callerSeiAddress.String(), + Shares: hundredSharesValue, + Decimals: big.NewInt(sdk.Precision), + ValidatorAddress: validatorAddress, + }, + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := validatorDelegationsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + ValidatorDelegationsResponse: validatorDelegationsResponse, + }, + args: []interface{}{validatorAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return validator delegations (static call allowed)", + fields: &TestStakingQuerier{ + ValidatorDelegationsResponse: validatorDelegationsResponse, + }, + args: []interface{}{validatorAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).ValidatorDelegationsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_ValidatorUnbondingDelegations(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + validatorUnbondingDelegationsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).ValidatorUnbondingDelegationsID) + + callerSeiAddress, _ := testkeeper.MockAddressPair() + validatorAddress := "seivaloper134ykhqrkyda72uq7f463ne77e4tn99steprmz7" + unbondingDelegation := stakingtypes.UnbondingDelegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []stakingtypes.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: time.Unix(2000, 0), + InitialBalance: sdk.NewInt(50), + Balance: sdk.NewInt(40), + }, + }, + } + + nextKey := []byte("next-key") + validatorUnbondingDelegationsResponse := &stakingtypes.QueryValidatorUnbondingDelegationsResponse{ + UnbondingResponses: []stakingtypes.UnbondingDelegation{unbondingDelegation}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + expected := staking.UnbondingDelegationsResponse{ + UnbondingDelegations: []staking.UnbondingDelegation{ + { + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []staking.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: 2000, + InitialBalance: "50", + Balance: "40", + }, + }, + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := validatorUnbondingDelegationsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + ValidatorUnbondingDelegationsResponse: validatorUnbondingDelegationsResponse, + }, + args: []interface{}{validatorAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return validator unbonding delegations (static call allowed)", + fields: &TestStakingQuerier{ + ValidatorUnbondingDelegationsResponse: validatorUnbondingDelegationsResponse, + }, + args: []interface{}{validatorAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).ValidatorUnbondingDelegationsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_UnbondingDelegation(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + unbondingDelegationMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).UnbondingDelegationID) + + callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + validatorAddress := "seivaloper134ykhqrkyda72uq7f463ne77e4tn99steprmz7" + unbondingDelegation := stakingtypes.UnbondingDelegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []stakingtypes.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: time.Unix(2000, 0), + InitialBalance: sdk.NewInt(50), + Balance: sdk.NewInt(40), + }, + }, + } + + unbondingDelegationResponse := &stakingtypes.QueryUnbondingDelegationResponse{ + Unbond: unbondingDelegation, + } + + expected := staking.UnbondingDelegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []staking.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: 2000, + InitialBalance: "50", + Balance: "40", + }, + }, + } + + happyPathPackedOutput, _ := unbondingDelegationMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + UnbondingDelegationResponse: unbondingDelegationResponse, + }, + args: []interface{}{callerEvmAddress, validatorAddress}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return unbonding delegation (static call allowed)", + fields: &TestStakingQuerier{ + UnbondingDelegationResponse: unbondingDelegationResponse, + }, + args: []interface{}{callerEvmAddress, validatorAddress}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + k.SetAddressMapping(ctx, callerSeiAddress, callerEvmAddress) + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).UnbondingDelegationID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_DelegatorDelegations(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + delegatorDelegationsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).DelegatorDelegationsID) + + callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + validatorAddress := "seivaloper134ykhqrkyda72uq7f463ne77e4tn99steprmz7" + shares := 100 + delegationResponse := &stakingtypes.DelegationResponse{ + Delegation: stakingtypes.Delegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Shares: sdk.NewDec(int64(shares)), + }, + Balance: sdk.NewCoin("usei", sdk.NewInt(int64(shares))), + } + + nextKey := []byte("next-key") + delegatorDelegationsResponse := &stakingtypes.QueryDelegatorDelegationsResponse{ + DelegationResponses: []stakingtypes.DelegationResponse{*delegationResponse}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + hundredSharesValue := new(big.Int) + hundredSharesValue.SetString("100000000000000000000", 10) + expected := staking.DelegationsResponse{ + Delegations: []staking.Delegation{ + { + Balance: staking.Balance{ + Amount: big.NewInt(int64(shares)), + Denom: "usei", + }, + Delegation: staking.DelegationDetails{ + DelegatorAddress: callerSeiAddress.String(), + Shares: hundredSharesValue, + Decimals: big.NewInt(sdk.Precision), + ValidatorAddress: validatorAddress, + }, + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := delegatorDelegationsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + DelegatorDelegationsResponse: delegatorDelegationsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return delegator delegations (static call allowed)", + fields: &TestStakingQuerier{ + DelegatorDelegationsResponse: delegatorDelegationsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + k.SetAddressMapping(ctx, callerSeiAddress, callerEvmAddress) + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).DelegatorDelegationsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_DelegatorValidator(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + delegatorValidatorMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).DelegatorValidatorID) + + callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + val := createTestValidator() + validatorResponse := &stakingtypes.QueryDelegatorValidatorResponse{ + Validator: val, + } + + expected := staking.Validator{ + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + } + + happyPathPackedOutput, _ := delegatorValidatorMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + DelegatorValidatorResponse: validatorResponse, + }, + args: []interface{}{callerEvmAddress, val.OperatorAddress}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return delegator validator (static call allowed)", + fields: &TestStakingQuerier{ + DelegatorValidatorResponse: validatorResponse, + }, + args: []interface{}{callerEvmAddress, val.OperatorAddress}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + k.SetAddressMapping(ctx, callerSeiAddress, callerEvmAddress) + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).DelegatorValidatorID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_DelegatorUnbondingDelegations(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + delegatorUnbondingDelegationsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).DelegatorUnbondingDelegationsID) + + callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + validatorAddress := "seivaloper134ykhqrkyda72uq7f463ne77e4tn99steprmz7" + unbondingDelegation := stakingtypes.UnbondingDelegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []stakingtypes.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: time.Unix(2000, 0), + InitialBalance: sdk.NewInt(50), + Balance: sdk.NewInt(40), + }, + }, + } + + nextKey := []byte("next-key") + delegatorUnbondingDelegationsResponse := &stakingtypes.QueryDelegatorUnbondingDelegationsResponse{ + UnbondingResponses: []stakingtypes.UnbondingDelegation{unbondingDelegation}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + expected := staking.UnbondingDelegationsResponse{ + UnbondingDelegations: []staking.UnbondingDelegation{ + { + DelegatorAddress: callerSeiAddress.String(), + ValidatorAddress: validatorAddress, + Entries: []staking.UnbondingDelegationEntry{ + { + CreationHeight: 100, + CompletionTime: 2000, + InitialBalance: "50", + Balance: "40", + }, + }, + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := delegatorUnbondingDelegationsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + DelegatorUnbondingDelegationsResponse: delegatorUnbondingDelegationsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return delegator unbonding delegations (static call allowed)", + fields: &TestStakingQuerier{ + DelegatorUnbondingDelegationsResponse: delegatorUnbondingDelegationsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + k.SetAddressMapping(ctx, callerSeiAddress, callerEvmAddress) + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).DelegatorUnbondingDelegationsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_Redelegations(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + redelegationsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).RedelegationsID) + + callerSeiAddress, _ := testkeeper.MockAddressPair() + srcValidatorAddress := "seivaloper1src" + dstValidatorAddress := "seivaloper1dst" + redelegation := stakingtypes.Redelegation{ + DelegatorAddress: callerSeiAddress.String(), + ValidatorSrcAddress: srcValidatorAddress, + ValidatorDstAddress: dstValidatorAddress, + Entries: []stakingtypes.RedelegationEntry{ + { + CreationHeight: 100, + CompletionTime: time.Unix(2000, 0), + InitialBalance: sdk.NewInt(50), + SharesDst: sdk.NewDec(40), + }, + }, + } + + redelegationEntryResponse := stakingtypes.RedelegationEntryResponse{ + RedelegationEntry: redelegation.Entries[0], + Balance: sdk.NewInt(40), + } + + nextKey := []byte("next-key") + redelegationsResponse := &stakingtypes.QueryRedelegationsResponse{ + RedelegationResponses: []stakingtypes.RedelegationResponse{ + { + Redelegation: redelegation, + Entries: []stakingtypes.RedelegationEntryResponse{redelegationEntryResponse}, + }, + }, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + expected := staking.RedelegationsResponse{ + Redelegations: []staking.Redelegation{ + { + DelegatorAddress: callerSeiAddress.String(), + ValidatorSrcAddress: srcValidatorAddress, + ValidatorDstAddress: dstValidatorAddress, + Entries: []staking.RedelegationEntry{ + { + CreationHeight: 100, + CompletionTime: 2000, + InitialBalance: "50", + SharesDst: "40", + }, + }, + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := redelegationsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + RedelegationsResponse: redelegationsResponse, + }, + args: []interface{}{callerSeiAddress.String(), srcValidatorAddress, dstValidatorAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return redelegations (static call allowed)", + fields: &TestStakingQuerier{ + RedelegationsResponse: redelegationsResponse, + }, + args: []interface{}{callerSeiAddress.String(), srcValidatorAddress, dstValidatorAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).RedelegationsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_DelegatorValidators(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + delegatorValidatorsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).DelegatorValidatorsID) + + callerSeiAddress, callerEvmAddress := testkeeper.MockAddressPair() + val := createTestValidator() + nextKey := []byte("next-key") + delegatorValidatorsResponse := &stakingtypes.QueryDelegatorValidatorsResponse{ + Validators: []stakingtypes.Validator{val}, + Pagination: &query.PageResponse{NextKey: nextKey}, + } + + expected := staking.ValidatorsResponse{ + Validators: []staking.Validator{ + { + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + }, + }, + NextKey: nextKey, + } + + happyPathPackedOutput, _ := delegatorValidatorsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + DelegatorValidatorsResponse: delegatorValidatorsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return delegator validators (static call allowed)", + fields: &TestStakingQuerier{ + DelegatorValidatorsResponse: delegatorValidatorsResponse, + }, + args: []interface{}{callerEvmAddress, []byte{}}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + k.SetAddressMapping(ctx, callerSeiAddress, callerEvmAddress) + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).DelegatorValidatorsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_HistoricalInfo(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + historicalInfoMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).HistoricalInfoID) + + val := createTestValidator() + historicalInfo := stakingtypes.HistoricalInfo{ + Header: tmtypes.Header{Height: 100}, + Valset: []stakingtypes.Validator{val}, + } + + historicalInfoResponse := &stakingtypes.QueryHistoricalInfoResponse{ + Hist: &historicalInfo, + } + + expected := staking.HistoricalInfo{ + Height: 100, + Header: []byte{}, + Validators: []staking.Validator{ + { + OperatorAddress: val.OperatorAddress, + ConsensusPubkey: string(val.ConsensusPubkey.Value), + Jailed: val.Jailed, + Status: int32(val.Status), + Tokens: val.Tokens.String(), + DelegatorShares: val.DelegatorShares.String(), + Description: val.Description.String(), + UnbondingHeight: val.UnbondingHeight, + UnbondingTime: val.UnbondingTime.Unix(), + CommissionRate: val.Commission.Rate.String(), + CommissionMaxRate: val.Commission.MaxRate.String(), + CommissionMaxChangeRate: val.Commission.MaxChangeRate.String(), + CommissionUpdateTime: val.Commission.UpdateTime.Unix(), + MinSelfDelegation: val.MinSelfDelegation.String(), + }, + }, + } + + happyPathPackedOutput, _ := historicalInfoMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + HistoricalInfoResponse: historicalInfoResponse, + }, + args: []interface{}{int64(100)}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return historical info (static call allowed)", + fields: &TestStakingQuerier{ + HistoricalInfoResponse: historicalInfoResponse, + }, + args: []interface{}{int64(100)}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).HistoricalInfoID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_Pool(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + poolMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).PoolID) + + pool := stakingtypes.Pool{ + NotBondedTokens: sdk.NewInt(1000), + BondedTokens: sdk.NewInt(2000), + } + + poolResponse := &stakingtypes.QueryPoolResponse{ + Pool: pool, + } + + expected := staking.Pool{ + NotBondedTokens: pool.NotBondedTokens.String(), + BondedTokens: pool.BondedTokens.String(), + } + + happyPathPackedOutput, _ := poolMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + PoolResponse: poolResponse, + }, + args: []interface{}{}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return pool (static call allowed)", + fields: &TestStakingQuerier{ + PoolResponse: poolResponse, + }, + args: []interface{}{}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).PoolID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + +func TestPrecompile_Run_Params(t *testing.T) { + pre, _ := staking.NewPrecompile(&utils.EmptyKeepers{}) + paramsMethod, _ := pre.ABI.MethodById(pre.GetExecutor().(*staking.PrecompileExecutor).ParamsID) + + params := stakingtypes.Params{ + UnbondingTime: time.Duration(1000000000000), // 1000 seconds in nanoseconds + MaxValidators: 100, + MaxEntries: 7, + HistoricalEntries: 10000, + BondDenom: "usei", + MinCommissionRate: sdk.NewDecWithPrec(5, 2), + MaxVotingPowerRatio: sdk.NewDecWithPrec(30, 2), + MaxVotingPowerEnforcementThreshold: sdk.NewInt(1000000), + } + + paramsResponse := &stakingtypes.QueryParamsResponse{ + Params: params, + } + + expected := staking.Params{ + UnbondingTime: uint64(params.UnbondingTime.Seconds()), + MaxValidators: params.MaxValidators, + MaxEntries: params.MaxEntries, + HistoricalEntries: params.HistoricalEntries, + BondDenom: params.BondDenom, + MinCommissionRate: params.MinCommissionRate.String(), + MaxVotingPowerRatio: params.MaxVotingPowerRatio.String(), + MaxVotingPowerEnforcementThreshold: params.MaxVotingPowerEnforcementThreshold.String(), + } + + happyPathPackedOutput, _ := paramsMethod.Outputs.Pack(expected) + + tests := []struct { + name string + fields utils.StakingQuerier + args []interface{} + value *big.Int + wantRet []byte + wantErr bool + wantErrMsg string + }{ + { + name: "fails if value passed", + fields: &TestStakingQuerier{ + ParamsResponse: paramsResponse, + }, + args: []interface{}{}, + value: big.NewInt(1), + wantErr: true, + wantErrMsg: "sending funds to a non-payable function", + }, + { + name: "should return params (static call allowed)", + fields: &TestStakingQuerier{ + ParamsResponse: paramsResponse, + }, + args: []interface{}{}, + wantRet: happyPathPackedOutput, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testApp := testkeeper.EVMTestApp + ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) + k := &testApp.EvmKeeper + stateDb := state.NewDBImpl(ctx, k, true) + evm := vm.EVM{ + StateDB: stateDb, + } + p, _ := staking.NewPrecompile(&app.PrecompileKeepers{ + StakingQuerier: tt.fields, + EVMKeeper: k, + }) + method, err := p.ABI.MethodById(p.GetExecutor().(*staking.PrecompileExecutor).ParamsID) + require.NoError(t, err) + + inputs, err := method.Inputs.Pack(tt.args...) + require.NoError(t, err) + + gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + if (err != nil) != tt.wantErr { + t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + require.Equal(t, vm.ErrExecutionReverted, err) + require.Nil(t, gotRet) + return + } + if !reflect.DeepEqual(gotRet, tt.wantRet) { + t.Errorf("Run() gotRet = %v, want %v", gotRet, tt.wantRet) + } + }) + } +} + // createValidatorTestSetup contains common setup for createValidator tests type createValidatorTestSetup struct { testApp *app.App diff --git a/precompiles/utils/expected_keepers.go b/precompiles/utils/expected_keepers.go index d1831d0817..178423483c 100644 --- a/precompiles/utils/expected_keepers.go +++ b/precompiles/utils/expected_keepers.go @@ -149,6 +149,19 @@ type StakingKeeper interface { type StakingQuerier interface { Delegation(c context.Context, req *stakingtypes.QueryDelegationRequest) (*stakingtypes.QueryDelegationResponse, error) + Validators(c context.Context, req *stakingtypes.QueryValidatorsRequest) (*stakingtypes.QueryValidatorsResponse, error) + Validator(c context.Context, req *stakingtypes.QueryValidatorRequest) (*stakingtypes.QueryValidatorResponse, error) + ValidatorDelegations(c context.Context, req *stakingtypes.QueryValidatorDelegationsRequest) (*stakingtypes.QueryValidatorDelegationsResponse, error) + ValidatorUnbondingDelegations(c context.Context, req *stakingtypes.QueryValidatorUnbondingDelegationsRequest) (*stakingtypes.QueryValidatorUnbondingDelegationsResponse, error) + UnbondingDelegation(c context.Context, req *stakingtypes.QueryUnbondingDelegationRequest) (*stakingtypes.QueryUnbondingDelegationResponse, error) + DelegatorDelegations(c context.Context, req *stakingtypes.QueryDelegatorDelegationsRequest) (*stakingtypes.QueryDelegatorDelegationsResponse, error) + DelegatorValidator(c context.Context, req *stakingtypes.QueryDelegatorValidatorRequest) (*stakingtypes.QueryDelegatorValidatorResponse, error) + DelegatorUnbondingDelegations(c context.Context, req *stakingtypes.QueryDelegatorUnbondingDelegationsRequest) (*stakingtypes.QueryDelegatorUnbondingDelegationsResponse, error) + Redelegations(c context.Context, req *stakingtypes.QueryRedelegationsRequest) (*stakingtypes.QueryRedelegationsResponse, error) + DelegatorValidators(c context.Context, req *stakingtypes.QueryDelegatorValidatorsRequest) (*stakingtypes.QueryDelegatorValidatorsResponse, error) + HistoricalInfo(c context.Context, req *stakingtypes.QueryHistoricalInfoRequest) (*stakingtypes.QueryHistoricalInfoResponse, error) + Pool(c context.Context, req *stakingtypes.QueryPoolRequest) (*stakingtypes.QueryPoolResponse, error) + Params(c context.Context, req *stakingtypes.QueryParamsRequest) (*stakingtypes.QueryParamsResponse, error) } type GovKeeper interface { From 4b759e4aa31c53d19a35be2e09633e4d3e7864d7 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Thu, 18 Dec 2025 11:38:53 +0800 Subject: [PATCH 2/3] tests --- precompiles/distribution/distribution.go | 2 +- precompiles/staking/Staking.sol | 1 - precompiles/staking/abi.json | 2 +- precompiles/staking/staking.go | 373 +++++++++++++---------- precompiles/staking/staking_test.go | 111 ++----- 5 files changed, 225 insertions(+), 264 deletions(-) diff --git a/precompiles/distribution/distribution.go b/precompiles/distribution/distribution.go index 726d7c31ea..63cfff3181 100644 --- a/precompiles/distribution/distribution.go +++ b/precompiles/distribution/distribution.go @@ -440,7 +440,7 @@ func (p PrecompileExecutor) withdrawValidatorCommission(ctx sdk.Context, method } if err := pcommon.EmitEVMLog(evm, p.address, []common.Hash{ ValidatorCommissionEventSig, - common.BytesToHash([]byte(validator.String())), + crypto.Keccak256Hash([]byte(validator.String())), }, logData); err != nil { rerr = err return diff --git a/precompiles/staking/Staking.sol b/precompiles/staking/Staking.sol index 02428c06a9..447eeeb05d 100644 --- a/precompiles/staking/Staking.sol +++ b/precompiles/staking/Staking.sol @@ -361,7 +361,6 @@ interface IStaking { struct HistoricalInfo { int64 height; - bytes header; Validator[] validators; } diff --git a/precompiles/staking/abi.json b/precompiles/staking/abi.json index ae0f3cd2d2..229b0370cf 100755 --- a/precompiles/staking/abi.json +++ b/precompiles/staking/abi.json @@ -1 +1 @@ -[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Delegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"srcValidator","type":"string"},{"indexed":false,"internalType":"string","name":"dstValidator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Undelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"editor","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorEdited","type":"event"},{"inputs":[{"internalType":"string","name":"pubKeyHex","type":"string"},{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"createValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegation","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation","name":"delegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"delegatorValidator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorValidators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"editValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int64","name":"height","type":"int64"}],"name":"historicalInfo","outputs":[{"components":[{"internalType":"int64","name":"height","type":"int64"},{"internalType":"bytes","name":"header","type":"bytes"},{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IStaking.HistoricalInfo","name":"historicalInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"components":[{"internalType":"uint64","name":"unbondingTime","type":"uint64"},{"internalType":"uint32","name":"maxValidators","type":"uint32"},{"internalType":"uint32","name":"maxEntries","type":"uint32"},{"internalType":"uint32","name":"historicalEntries","type":"uint32"},{"internalType":"string","name":"bondDenom","type":"string"},{"internalType":"string","name":"minCommissionRate","type":"string"},{"internalType":"string","name":"maxVotingPowerRatio","type":"string"},{"internalType":"string","name":"maxVotingPowerEnforcementThreshold","type":"string"}],"internalType":"struct IStaking.Params","name":"params","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"components":[{"internalType":"string","name":"notBondedTokens","type":"string"},{"internalType":"string","name":"bondedTokens","type":"string"}],"internalType":"struct IStaking.Pool","name":"pool","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"srcAddress","type":"string"},{"internalType":"string","name":"dstAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"delegator","type":"string"},{"internalType":"string","name":"srcValidator","type":"string"},{"internalType":"string","name":"dstValidator","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"redelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorSrcAddress","type":"string"},{"internalType":"string","name":"validatorDstAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"sharesDst","type":"string"}],"internalType":"struct IStaking.RedelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.Redelegation[]","name":"redelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.RedelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"unbondingDelegation","outputs":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation","name":"unbondingDelegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"validator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"status","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"}] \ No newline at end of file +[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Delegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"srcValidator","type":"string"},{"indexed":false,"internalType":"string","name":"dstValidator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":false,"internalType":"string","name":"validator","type":"string"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Undelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"editor","type":"address"},{"indexed":false,"internalType":"string","name":"validatorAddress","type":"string"},{"indexed":false,"internalType":"string","name":"moniker","type":"string"}],"name":"ValidatorEdited","type":"event"},{"inputs":[{"internalType":"string","name":"pubKeyHex","type":"string"},{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"createValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"valAddress","type":"string"}],"name":"delegation","outputs":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation","name":"delegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"delegatorValidator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"delegatorValidators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"moniker","type":"string"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"uint256","name":"minSelfDelegation","type":"uint256"}],"name":"editValidator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int64","name":"height","type":"int64"}],"name":"historicalInfo","outputs":[{"components":[{"internalType":"int64","name":"height","type":"int64"},{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"}],"internalType":"struct IStaking.HistoricalInfo","name":"historicalInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"params","outputs":[{"components":[{"internalType":"uint64","name":"unbondingTime","type":"uint64"},{"internalType":"uint32","name":"maxValidators","type":"uint32"},{"internalType":"uint32","name":"maxEntries","type":"uint32"},{"internalType":"uint32","name":"historicalEntries","type":"uint32"},{"internalType":"string","name":"bondDenom","type":"string"},{"internalType":"string","name":"minCommissionRate","type":"string"},{"internalType":"string","name":"maxVotingPowerRatio","type":"string"},{"internalType":"string","name":"maxVotingPowerEnforcementThreshold","type":"string"}],"internalType":"struct IStaking.Params","name":"params","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pool","outputs":[{"components":[{"internalType":"string","name":"notBondedTokens","type":"string"},{"internalType":"string","name":"bondedTokens","type":"string"}],"internalType":"struct IStaking.Pool","name":"pool","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"srcAddress","type":"string"},{"internalType":"string","name":"dstAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"delegator","type":"string"},{"internalType":"string","name":"srcValidator","type":"string"},{"internalType":"string","name":"dstValidator","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"redelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorSrcAddress","type":"string"},{"internalType":"string","name":"validatorDstAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"sharesDst","type":"string"}],"internalType":"struct IStaking.RedelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.Redelegation[]","name":"redelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.RedelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"unbondingDelegation","outputs":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation","name":"unbondingDelegation","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"valAddress","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"undelegate","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"}],"name":"validator","outputs":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator","name":"validator","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorDelegations","outputs":[{"components":[{"components":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"denom","type":"string"}],"internalType":"struct IStaking.Balance","name":"balance","type":"tuple"},{"components":[{"internalType":"string","name":"delegator_address","type":"string"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"string","name":"validator_address","type":"string"}],"internalType":"struct IStaking.DelegationDetails","name":"delegation","type":"tuple"}],"internalType":"struct IStaking.Delegation[]","name":"delegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.DelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"validatorAddress","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validatorUnbondingDelegations","outputs":[{"components":[{"components":[{"internalType":"string","name":"delegatorAddress","type":"string"},{"internalType":"string","name":"validatorAddress","type":"string"},{"components":[{"internalType":"int64","name":"creationHeight","type":"int64"},{"internalType":"int64","name":"completionTime","type":"int64"},{"internalType":"string","name":"initialBalance","type":"string"},{"internalType":"string","name":"balance","type":"string"}],"internalType":"struct IStaking.UnbondingDelegationEntry[]","name":"entries","type":"tuple[]"}],"internalType":"struct IStaking.UnbondingDelegation[]","name":"unbondingDelegations","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.UnbondingDelegationsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"status","type":"string"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"name":"validators","outputs":[{"components":[{"components":[{"internalType":"string","name":"operatorAddress","type":"string"},{"internalType":"string","name":"consensusPubkey","type":"string"},{"internalType":"bool","name":"jailed","type":"bool"},{"internalType":"int32","name":"status","type":"int32"},{"internalType":"string","name":"tokens","type":"string"},{"internalType":"string","name":"delegatorShares","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"int64","name":"unbondingHeight","type":"int64"},{"internalType":"int64","name":"unbondingTime","type":"int64"},{"internalType":"string","name":"commissionRate","type":"string"},{"internalType":"string","name":"commissionMaxRate","type":"string"},{"internalType":"string","name":"commissionMaxChangeRate","type":"string"},{"internalType":"int64","name":"commissionUpdateTime","type":"int64"},{"internalType":"string","name":"minSelfDelegation","type":"string"}],"internalType":"struct IStaking.Validator[]","name":"validators","type":"tuple[]"},{"internalType":"bytes","name":"nextKey","type":"bytes"}],"internalType":"struct IStaking.ValidatorsResponse","name":"response","type":"tuple"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 83fcb39efe..e7cdb7d66e 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -1,7 +1,6 @@ package staking import ( - "bytes" "embed" "encoding/hex" "errors" @@ -79,7 +78,7 @@ type PrecompileExecutor struct { ParamsID []byte } -func NewPrecompile(keepers utils.Keepers) (*pcommon.Precompile, error) { +func NewPrecompile(keepers utils.Keepers) (*pcommon.DynamicGasPrecompile, error) { newAbi := pcommon.MustGetABI(f, "abi.json") p := &PrecompileExecutor{ @@ -133,69 +132,41 @@ func NewPrecompile(keepers utils.Keepers) (*pcommon.Precompile, error) { } } - return pcommon.NewPrecompile(newAbi, p, p.address, "staking"), nil + return pcommon.NewDynamicGasPrecompile(newAbi, p, p.address, "staking"), nil } -// RequiredGas returns the required bare minimum gas to execute the precompile. -func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64 { - if bytes.Equal(method.ID, p.DelegateID) { - return 50000 - } else if bytes.Equal(method.ID, p.RedelegateID) { - return 70000 - } else if bytes.Equal(method.ID, p.UndelegateID) { - return 50000 - } else if bytes.Equal(method.ID, p.CreateValidatorID) { - return 100000 - } else if bytes.Equal(method.ID, p.EditValidatorID) { - return 100000 - } else if bytes.Equal(method.ID, p.DelegationID) || - bytes.Equal(method.ID, p.ValidatorID) || - bytes.Equal(method.ID, p.ValidatorDelegationsID) || - bytes.Equal(method.ID, p.ValidatorUnbondingDelegationsID) || - bytes.Equal(method.ID, p.UnbondingDelegationID) || - bytes.Equal(method.ID, p.DelegatorDelegationsID) || - bytes.Equal(method.ID, p.DelegatorValidatorID) || - bytes.Equal(method.ID, p.DelegatorUnbondingDelegationsID) || - bytes.Equal(method.ID, p.RedelegationsID) || - bytes.Equal(method.ID, p.DelegatorValidatorsID) || - bytes.Equal(method.ID, p.HistoricalInfoID) || - bytes.Equal(method.ID, p.PoolID) || - bytes.Equal(method.ID, p.ParamsID) || - bytes.Equal(method.ID, p.ValidatorsID) { - return pcommon.DefaultGasCost(input, false) - } - // This should never happen since this is going to fail during Run - return pcommon.UnknownMethodCallGas +func (p PrecompileExecutor) EVMKeeper() utils.EVMKeeper { + return p.evmKeeper } -func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, hooks *tracing.Hooks) (bz []byte, err error) { +func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64, hooks *tracing.Hooks) (ret []byte, remainingGas uint64, err error) { if ctx.EVMPrecompileCalledFromDelegateCall() { - return nil, errors.New("cannot delegatecall staking") + return nil, 0, errors.New("cannot delegatecall staking") } switch method.Name { case DelegateMethod: if readOnly { - return nil, errors.New("cannot call staking precompile from staticcall") + return nil, 0, errors.New("cannot call staking precompile from staticcall") } return p.delegate(ctx, method, caller, args, value, hooks, evm) case RedelegateMethod: if readOnly { - return nil, errors.New("cannot call staking precompile from staticcall") + return nil, 0, errors.New("cannot call staking precompile from staticcall") } return p.redelegate(ctx, method, caller, args, value, evm) case UndelegateMethod: if readOnly { - return nil, errors.New("cannot call staking precompile from staticcall") + return nil, 0, errors.New("cannot call staking precompile from staticcall") } return p.undelegate(ctx, method, caller, args, value, evm) case CreateValidatorMethod: if readOnly { - return nil, errors.New("cannot call staking precompile from staticcall") + return nil, 0, errors.New("cannot call staking precompile from staticcall") } return p.createValidator(ctx, method, caller, args, value, hooks, evm) case EditValidatorMethod: if readOnly { - return nil, errors.New("cannot call staking precompile from staticcall") + return nil, 0, errors.New("cannot call staking precompile from staticcall") } return p.editValidator(ctx, method, caller, args, value, hooks, evm) case DelegationMethod: @@ -230,24 +201,24 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller return } -func (p PrecompileExecutor) delegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, error) { +func (p PrecompileExecutor) delegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, uint64, error) { if err := pcommon.ValidateArgsLength(args, 1); err != nil { - return nil, err + return nil, 0, err } // if delegator is associated, then it must have Account set already // if delegator is not associated, then it can't delegate anyway (since // there is no good way to merge delegations if it becomes associated) delegator, associated := p.evmKeeper.GetSeiAddress(ctx, caller) if !associated { - return nil, types.NewAssociationMissingErr(caller.Hex()) + return nil, 0, types.NewAssociationMissingErr(caller.Hex()) } validatorBech32 := args[0].(string) if value == nil || value.Sign() == 0 { - return nil, errors.New("set `value` field to non-zero to send delegate fund") + return nil, 0, errors.New("set `value` field to non-zero to send delegate fund") } coin, err := pcommon.HandlePaymentUsei(ctx, p.evmKeeper.GetSeiAddressOrDefault(ctx, p.address), delegator, value, p.bankKeeper, p.evmKeeper, hooks, evm.GetDepth()) if err != nil { - return nil, err + return nil, 0, err } _, err = p.stakingKeeper.Delegate(sdk.WrapSDKContext(ctx), &stakingtypes.MsgDelegate{ DelegatorAddress: delegator.String(), @@ -255,7 +226,7 @@ func (p PrecompileExecutor) delegate(ctx sdk.Context, method *abi.Method, caller Amount: coin, }) if err != nil { - return nil, err + return nil, 0, err } // Emit EVM event @@ -264,20 +235,24 @@ func (p PrecompileExecutor) delegate(ctx sdk.Context, method *abi.Method, caller ctx.Logger().Error("Failed to emit EVM delegate event", "error", emitErr) } - return method.Outputs.Pack(true) + bz, err := method.Outputs.Pack(true) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) redelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) ([]byte, error) { +func (p PrecompileExecutor) redelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 3); err != nil { - return nil, err + return nil, 0, err } delegator, associated := p.evmKeeper.GetSeiAddress(ctx, caller) if !associated { - return nil, types.NewAssociationMissingErr(caller.Hex()) + return nil, 0, types.NewAssociationMissingErr(caller.Hex()) } srcValidatorBech32 := args[0].(string) dstValidatorBech32 := args[1].(string) @@ -289,7 +264,7 @@ func (p PrecompileExecutor) redelegate(ctx sdk.Context, method *abi.Method, call Amount: sdk.NewCoin(sdk.MustGetBaseDenom(), sdk.NewIntFromBigInt(amount)), }) if err != nil { - return nil, err + return nil, 0, err } // Emit EVM event @@ -298,20 +273,24 @@ func (p PrecompileExecutor) redelegate(ctx sdk.Context, method *abi.Method, call ctx.Logger().Error("Failed to emit EVM redelegate event", "error", emitErr) } - return method.Outputs.Pack(true) + bz, err := method.Outputs.Pack(true) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) undelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) ([]byte, error) { +func (p PrecompileExecutor) undelegate(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, evm *vm.EVM) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } delegator, associated := p.evmKeeper.GetSeiAddress(ctx, caller) if !associated { - return nil, types.NewAssociationMissingErr(caller.Hex()) + return nil, 0, types.NewAssociationMissingErr(caller.Hex()) } validatorBech32 := args[0].(string) amount := args[1].(*big.Int) @@ -321,7 +300,7 @@ func (p PrecompileExecutor) undelegate(ctx sdk.Context, method *abi.Method, call Amount: sdk.NewCoin(p.evmKeeper.GetBaseDenom(ctx), sdk.NewIntFromBigInt(amount)), }) if err != nil { - return nil, err + return nil, 0, err } // Emit EVM event @@ -330,7 +309,11 @@ func (p PrecompileExecutor) undelegate(ctx sdk.Context, method *abi.Method, call ctx.Logger().Error("Failed to emit EVM undelegate event", "error", emitErr) } - return method.Outputs.Pack(true) + bz, err := method.Outputs.Pack(true) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } type Delegation struct { @@ -350,18 +333,18 @@ type DelegationDetails struct { ValidatorAddress string } -func (p PrecompileExecutor) delegation(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) delegation(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[1].(string) @@ -372,7 +355,7 @@ func (p PrecompileExecutor) delegation(ctx sdk.Context, method *abi.Method, args delegationResponse, err := p.stakingQuerier.Delegation(sdk.WrapSDKContext(ctx), delegationRequest) if err != nil { - return nil, err + return nil, 0, err } delegation := Delegation{ @@ -388,7 +371,11 @@ func (p PrecompileExecutor) delegation(ctx sdk.Context, method *abi.Method, args }, } - return method.Outputs.Pack(delegation) + bz, err := method.Outputs.Pack(delegation) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } type ValidatorsResponse struct { @@ -428,13 +415,13 @@ type Validator struct { MinSelfDelegation string } -func (p PrecompileExecutor) validators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) validators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } validatorsRequest := &stakingtypes.QueryValidatorsRequest{ @@ -446,7 +433,7 @@ func (p PrecompileExecutor) validators(ctx sdk.Context, method *abi.Method, args validatorsResponse, err := p.stakingQuerier.Validators(sdk.WrapSDKContext(ctx), validatorsRequest) if err != nil { - return nil, err + return nil, 0, err } res := ValidatorsResponse{ @@ -472,12 +459,16 @@ func (p PrecompileExecutor) validators(ctx sdk.Context, method *abi.Method, args } } - return method.Outputs.Pack(res) + bz, err := method.Outputs.Pack(res) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, error) { +func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, uint64, error) { if err := pcommon.ValidateArgsLength(args, 6); err != nil { - return nil, err + return nil, 0, err } // Extract arguments @@ -491,13 +482,13 @@ func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, // Get validator address (caller's associated Sei address) valAddress, associated := p.evmKeeper.GetSeiAddress(ctx, caller) if !associated { - return nil, types.NewAssociationMissingErr(caller.Hex()) + return nil, 0, types.NewAssociationMissingErr(caller.Hex()) } // Parse public key from hex pubKeyBytes, err := hex.DecodeString(pubKeyHex) if err != nil { - return nil, errors.New("invalid public key hex format") + return nil, 0, errors.New("invalid public key hex format") } // Create ed25519 public key @@ -506,28 +497,28 @@ func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, // Parse commission rates commissionRate, err := sdk.NewDecFromStr(commissionRateStr) if err != nil { - return nil, errors.New("invalid commission rate") + return nil, 0, errors.New("invalid commission rate") } commissionMaxRate, err := sdk.NewDecFromStr(commissionMaxRateStr) if err != nil { - return nil, errors.New("invalid commission max rate") + return nil, 0, errors.New("invalid commission max rate") } commissionMaxChangeRate, err := sdk.NewDecFromStr(commissionMaxChangeRateStr) if err != nil { - return nil, errors.New("invalid commission max change rate") + return nil, 0, errors.New("invalid commission max change rate") } commission := stakingtypes.NewCommissionRates(commissionRate, commissionMaxRate, commissionMaxChangeRate) if value == nil || value.Sign() == 0 { - return nil, errors.New("set `value` field to non-zero to send delegate fund") + return nil, 0, errors.New("set `value` field to non-zero to send delegate fund") } // Validate minimum self delegation if minSelfDelegation == nil || minSelfDelegation.Sign() <= 0 { - return nil, errors.New("minimum self delegation must be a positive integer: invalid request") + return nil, 0, errors.New("minimum self delegation must be a positive integer: invalid request") } coin, err := pcommon.HandlePaymentUsei( @@ -540,7 +531,7 @@ func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, hooks, evm.GetDepth()) if err != nil { - return nil, err + return nil, 0, err } description := stakingtypes.NewDescription(moniker, "", "", "", "") @@ -554,16 +545,16 @@ func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, sdk.NewIntFromBigInt(minSelfDelegation), ) if err != nil { - return nil, err + return nil, 0, err } if err := msg.ValidateBasic(); err != nil { - return nil, err + return nil, 0, err } _, err = p.stakingKeeper.CreateValidator(sdk.WrapSDKContext(ctx), msg) if err != nil { - return nil, err + return nil, 0, err } // Emit EVM event @@ -572,16 +563,20 @@ func (p PrecompileExecutor) createValidator(ctx sdk.Context, method *abi.Method, ctx.Logger().Error("Failed to emit EVM validator created event", "error", emitErr) } - return method.Outputs.Pack(true) + bz, err := method.Outputs.Pack(true) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, error) { +func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, caller common.Address, args []interface{}, value *big.Int, hooks *tracing.Hooks, evm *vm.EVM) ([]byte, uint64, error) { if err := pcommon.ValidateArgsLength(args, 3); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } // Extract arguments @@ -592,7 +587,7 @@ func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, c // Get validator address (caller's associated Sei address) valAddress, associated := p.evmKeeper.GetSeiAddress(ctx, caller) if !associated { - return nil, types.NewAssociationMissingErr(caller.Hex()) + return nil, 0, types.NewAssociationMissingErr(caller.Hex()) } // Parse commission rate if provided @@ -600,7 +595,7 @@ func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, c if commissionRateStr != "" { rate, err := sdk.NewDecFromStr(commissionRateStr) if err != nil { - return nil, errors.New("invalid commission rate") + return nil, 0, errors.New("invalid commission rate") } commissionRate = &rate } @@ -628,12 +623,12 @@ func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, c ) if err := msg.ValidateBasic(); err != nil { - return nil, err + return nil, 0, err } _, err := p.stakingKeeper.EditValidator(sdk.WrapSDKContext(ctx), msg) if err != nil { - return nil, err + return nil, 0, err } // Emit EVM event @@ -642,16 +637,20 @@ func (p PrecompileExecutor) editValidator(ctx sdk.Context, method *abi.Method, c ctx.Logger().Error("Failed to emit EVM validator edited event", "error", emitErr) } - return method.Outputs.Pack(true) + bz, err := method.Outputs.Pack(true) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) validator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) validator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 1); err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[0].(string) @@ -661,20 +660,24 @@ func (p PrecompileExecutor) validator(ctx sdk.Context, method *abi.Method, args validatorResponse, err := p.stakingQuerier.Validator(sdk.WrapSDKContext(ctx), validatorRequest) if err != nil { - return nil, err + return nil, 0, err } validator := convertValidatorToPrecompileType(validatorResponse.Validator) - return method.Outputs.Pack(validator) + bz, err := method.Outputs.Pack(validator) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) validatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) validatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[0].(string) @@ -689,7 +692,7 @@ func (p PrecompileExecutor) validatorDelegations(ctx sdk.Context, method *abi.Me response, err := p.stakingQuerier.ValidatorDelegations(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } delegations := make([]Delegation, len(response.DelegationResponses)) @@ -713,16 +716,20 @@ func (p PrecompileExecutor) validatorDelegations(ctx sdk.Context, method *abi.Me NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) validatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) validatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[0].(string) @@ -737,7 +744,7 @@ func (p PrecompileExecutor) validatorUnbondingDelegations(ctx sdk.Context, metho response, err := p.stakingQuerier.ValidatorUnbondingDelegations(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } unbondingDelegations := make([]UnbondingDelegation, len(response.UnbondingResponses)) @@ -763,21 +770,25 @@ func (p PrecompileExecutor) validatorUnbondingDelegations(ctx sdk.Context, metho NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) unbondingDelegation(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) unbondingDelegation(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[1].(string) @@ -788,7 +799,7 @@ func (p PrecompileExecutor) unbondingDelegation(ctx sdk.Context, method *abi.Met response, err := p.stakingQuerier.UnbondingDelegation(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } entries := make([]UnbondingDelegationEntry, len(response.Unbond.Entries)) @@ -807,21 +818,25 @@ func (p PrecompileExecutor) unbondingDelegation(ctx sdk.Context, method *abi.Met Entries: entries, } - return method.Outputs.Pack(unbondingDelegation) + bz, err := method.Outputs.Pack(unbondingDelegation) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) delegatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) delegatorDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } nextKey := args[1].([]byte) @@ -834,7 +849,7 @@ func (p PrecompileExecutor) delegatorDelegations(ctx sdk.Context, method *abi.Me response, err := p.stakingQuerier.DelegatorDelegations(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } delegations := make([]Delegation, len(response.DelegationResponses)) @@ -858,21 +873,25 @@ func (p PrecompileExecutor) delegatorDelegations(ctx sdk.Context, method *abi.Me NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) delegatorValidator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) delegatorValidator(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } validatorBech32 := args[1].(string) @@ -883,25 +902,29 @@ func (p PrecompileExecutor) delegatorValidator(ctx sdk.Context, method *abi.Meth response, err := p.stakingQuerier.DelegatorValidator(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } validator := convertValidatorToPrecompileType(response.Validator) - return method.Outputs.Pack(validator) + bz, err := method.Outputs.Pack(validator) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) delegatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) delegatorUnbondingDelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } nextKey := args[1].([]byte) @@ -914,7 +937,7 @@ func (p PrecompileExecutor) delegatorUnbondingDelegations(ctx sdk.Context, metho response, err := p.stakingQuerier.DelegatorUnbondingDelegations(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } unbondingDelegations := make([]UnbondingDelegation, len(response.UnbondingResponses)) @@ -940,16 +963,20 @@ func (p PrecompileExecutor) delegatorUnbondingDelegations(ctx sdk.Context, metho NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) redelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) redelegations(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 4); err != nil { - return nil, err + return nil, 0, err } delegatorStr := args[0].(string) @@ -968,7 +995,7 @@ func (p PrecompileExecutor) redelegations(ctx sdk.Context, method *abi.Method, a response, err := p.stakingQuerier.Redelegations(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } redelegations := make([]Redelegation, len(response.RedelegationResponses)) @@ -995,21 +1022,25 @@ func (p PrecompileExecutor) redelegations(ctx sdk.Context, method *abi.Method, a NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) delegatorValidators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) delegatorValidators(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 2); err != nil { - return nil, err + return nil, 0, err } seiDelegatorAddress, err := pcommon.GetSeiAddressFromArg(ctx, args[0], p.evmKeeper) if err != nil { - return nil, err + return nil, 0, err } nextKey := args[1].([]byte) @@ -1022,7 +1053,7 @@ func (p PrecompileExecutor) delegatorValidators(ctx sdk.Context, method *abi.Met response, err := p.stakingQuerier.DelegatorValidators(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } validators := make([]Validator, len(response.Validators)) @@ -1035,16 +1066,20 @@ func (p PrecompileExecutor) delegatorValidators(ctx sdk.Context, method *abi.Met NextKey: response.Pagination.NextKey, } - return method.Outputs.Pack(result) + bz, err := method.Outputs.Pack(result) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) historicalInfo(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) historicalInfo(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 1); err != nil { - return nil, err + return nil, 0, err } height := args[0].(int64) @@ -1054,11 +1089,11 @@ func (p PrecompileExecutor) historicalInfo(ctx sdk.Context, method *abi.Method, response, err := p.stakingQuerier.HistoricalInfo(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } if response.Hist == nil { - return nil, errors.New("historical info not found") + return nil, 0, errors.New("historical info not found") } validators := make([]Validator, len(response.Hist.Valset)) @@ -1066,36 +1101,31 @@ func (p PrecompileExecutor) historicalInfo(ctx sdk.Context, method *abi.Method, validators[i] = convertValidatorToPrecompileType(val) } - // Marshal header to bytes - headerBytes := []byte{} - if response.Hist.Header.Height > 0 { - // Header is a complex type, we'll serialize it if needed - // For now, we'll use empty bytes as header serialization is complex - headerBytes = []byte{} - } - historicalInfo := HistoricalInfo{ Height: height, // Use the requested height - Header: headerBytes, Validators: validators, } - return method.Outputs.Pack(historicalInfo) + bz, err := method.Outputs.Pack(historicalInfo) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) pool(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) pool(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 0); err != nil { - return nil, err + return nil, 0, err } request := &stakingtypes.QueryPoolRequest{} response, err := p.stakingQuerier.Pool(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } pool := Pool{ @@ -1103,22 +1133,26 @@ func (p PrecompileExecutor) pool(ctx sdk.Context, method *abi.Method, args []int BondedTokens: response.Pool.BondedTokens.String(), } - return method.Outputs.Pack(pool) + bz, err := method.Outputs.Pack(pool) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } -func (p PrecompileExecutor) params(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) { +func (p PrecompileExecutor) params(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) { if err := pcommon.ValidateNonPayable(value); err != nil { - return nil, err + return nil, 0, err } if err := pcommon.ValidateArgsLength(args, 0); err != nil { - return nil, err + return nil, 0, err } request := &stakingtypes.QueryParamsRequest{} response, err := p.stakingQuerier.Params(sdk.WrapSDKContext(ctx), request) if err != nil { - return nil, err + return nil, 0, err } params := Params{ @@ -1132,7 +1166,11 @@ func (p PrecompileExecutor) params(ctx sdk.Context, method *abi.Method, args []i MaxVotingPowerEnforcementThreshold: response.Params.MaxVotingPowerEnforcementThreshold.String(), } - return method.Outputs.Pack(params) + bz, err := method.Outputs.Pack(params) + if err != nil { + return nil, 0, err + } + return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), nil } // Helper function to convert stakingtypes.Validator to precompile Validator type @@ -1185,7 +1223,6 @@ type Redelegation struct { type HistoricalInfo struct { Height int64 - Header []byte Validators []Validator } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index c5ac50c031..aad2e93658 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -6,6 +6,7 @@ import ( "embed" "encoding/hex" "fmt" + "math" "math/big" "reflect" "testing" @@ -532,7 +533,7 @@ func TestPrecompile_Run_Delegation(t *testing.T) { require.Nil(t, err) inputs, err := delegation.Inputs.Pack(tt.args.delegatorAddress, tt.args.validatorAddress) require.Nil(t, err) - gotRet, err := p.Run(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*staking.PrecompileExecutor).DelegationID, inputs...), tt.args.value, tt.args.readOnly, tt.args.isFromDelegateCall, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, tt.args.caller, tt.args.callingContract, append(p.GetExecutor().(*staking.PrecompileExecutor).DelegationID, inputs...), math.MaxUint64, tt.args.value, nil, tt.args.readOnly, tt.args.isFromDelegateCall) if (err != nil) != tt.wantErr { t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr) return @@ -679,7 +680,7 @@ func TestPrecompile_Run_Validators(t *testing.T) { require.NoError(t, err) // Caller and callingContract are irrelevant for validators (query-only). - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.args.value, tt.args.readOnly, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.args.value, nil, tt.args.readOnly, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -805,7 +806,7 @@ func TestPrecompile_Run_Validator(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -914,7 +915,7 @@ func TestPrecompile_Run_ValidatorDelegations(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1024,7 +1025,7 @@ func TestPrecompile_Run_ValidatorUnbondingDelegations(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1128,7 +1129,7 @@ func TestPrecompile_Run_UnbondingDelegation(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1238,7 +1239,7 @@ func TestPrecompile_Run_DelegatorDelegations(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1333,7 +1334,7 @@ func TestPrecompile_Run_DelegatorValidator(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1444,7 +1445,7 @@ func TestPrecompile_Run_DelegatorUnbondingDelegations(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1567,7 +1568,7 @@ func TestPrecompile_Run_Redelegations(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1669,7 +1670,7 @@ func TestPrecompile_Run_DelegatorValidators(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1701,7 +1702,6 @@ func TestPrecompile_Run_HistoricalInfo(t *testing.T) { expected := staking.HistoricalInfo{ Height: 100, - Header: []byte{}, Validators: []staking.Validator{ { OperatorAddress: val.OperatorAddress, @@ -1773,7 +1773,7 @@ func TestPrecompile_Run_HistoricalInfo(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1858,7 +1858,7 @@ func TestPrecompile_Run_Pool(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -1955,7 +1955,7 @@ func TestPrecompile_Run_Params(t *testing.T) { inputs, err := method.Inputs.Pack(tt.args...) require.NoError(t, err) - gotRet, err := p.Run(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), tt.value, true, false, nil) + gotRet, _, err := p.RunAndCalculateGas(&evm, common.Address{}, common.Address{}, append(method.ID, inputs...), math.MaxUint64, tt.value, nil, true, false) if (err != nil) != tt.wantErr { t.Fatalf("Run() error = %v, wantErr %v", err, tt.wantErr) } @@ -2416,81 +2416,6 @@ func TestEditValidator(t *testing.T) { require.Equal(t, validator.ConsensusPubkey, updatedValidator.ConsensusPubkey, "Consensus pubkey should remain the same") } -func TestStakingPrecompile_RequiredGas(t *testing.T) { - pre, err := staking.NewPrecompile(&utils.EmptyKeepers{}) - require.NoError(t, err) - - executor := pre.GetExecutor().(*staking.PrecompileExecutor) - - // Test gas costs for all methods - testCases := []struct { - name string - methodID []byte - methodName string - inputSize int - expectedGas uint64 - }{ - { - name: "delegate method", - methodID: executor.DelegateID, - methodName: staking.DelegateMethod, - expectedGas: 50000, - }, - { - name: "redelegate method", - methodID: executor.RedelegateID, - methodName: staking.RedelegateMethod, - expectedGas: 70000, - }, - { - name: "undelegate method", - methodID: executor.UndelegateID, - methodName: staking.UndelegateMethod, - expectedGas: 50000, - }, - { - name: "createValidator method", - methodID: executor.CreateValidatorID, - methodName: staking.CreateValidatorMethod, - expectedGas: 100000, - }, - { - name: "editValidator method", - methodID: executor.EditValidatorID, - methodName: "editValidator", - expectedGas: 100000, - }, - { - name: "delegation method (query)", - methodID: executor.DelegationID, - methodName: staking.DelegationMethod, - inputSize: 100, - expectedGas: 1300, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - // Get the method from ABI - method, err := pre.ABI.MethodById(tc.methodID) - require.NoError(t, err, "Should be able to find method by ID") - require.Equal(t, tc.methodName, method.Name, "Method name should match") - - // Create mock input (method ID + some data) - input := make([]byte, 4+tc.inputSize) // method ID (4 bytes) + input data - copy(input[:4], tc.methodID) - - // Test RequiredGas - gasRequired := executor.RequiredGas(input[4:], method) - - // Verify gas - require.Equal(t, tc.expectedGas, gasRequired, - "Gas should be at least %d, got %d", tc.expectedGas, gasRequired) - - }) - } -} - func TestStakingPrecompileDelegateCallPrevention(t *testing.T) { testApp := testkeeper.EVMTestApp ctx := testApp.NewContext(false, tmtypes.Header{}).WithBlockHeight(2) @@ -2566,7 +2491,7 @@ func TestStakingPrecompileDelegateCallPrevention(t *testing.T) { StateDB: state.NewDBImpl(ctx, k, false), } - _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, tc.args, tc.value, false, evm, nil) + _, _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, tc.args, tc.value, false, evm, math.MaxUint64, nil) require.Error(t, err) require.Contains(t, err.Error(), "cannot delegatecall staking") }) @@ -2646,7 +2571,7 @@ func TestStakingPrecompileStaticCallPrevention(t *testing.T) { } // Test with readOnly = true (staticcall) - _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, tc.args, tc.value, true, evm, nil) + _, _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, tc.args, tc.value, true, evm, math.MaxUint64, nil) require.Error(t, err) require.Contains(t, err.Error(), "cannot call staking precompile from staticcall") }) @@ -2672,7 +2597,7 @@ func TestStakingPrecompileStaticCallPrevention(t *testing.T) { // Should succeed with readOnly = true for query method (even if no delegation exists) // The important thing is that the static call is allowed - _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, args, nil, true, evm, nil) + _, _, err := precompile.GetExecutor().(*staking.PrecompileExecutor).Execute(ctx, &method, evmAddr, evmAddr, args, nil, true, evm, math.MaxUint64, nil) // We don't check the error because the delegation might not exist // We're just testing that static calls are allowed for query methods _ = err From 09e30881d77b2ecbc56b8ce21029f05b12caa83d Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Thu, 18 Dec 2025 18:32:16 +0800 Subject: [PATCH 3/3] fix nil pagination --- sei-cosmos/x/staking/keeper/grpc_query.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sei-cosmos/x/staking/keeper/grpc_query.go b/sei-cosmos/x/staking/keeper/grpc_query.go index 8fa16354d4..4abcf9c919 100644 --- a/sei-cosmos/x/staking/keeper/grpc_query.go +++ b/sei-cosmos/x/staking/keeper/grpc_query.go @@ -390,6 +390,7 @@ func (k Querier) Redelegations(c context.Context, req *types.QueryRedelegationsR switch { case req.DelegatorAddr != "" && req.SrcValidatorAddr != "" && req.DstValidatorAddr != "": redels, err = queryRedelegation(ctx, k, req) + pageRes = &query.PageResponse{} case req.DelegatorAddr == "" && req.SrcValidatorAddr != "" && req.DstValidatorAddr == "": redels, pageRes, err = queryRedelegationsFromSrcValidator(store, k, req) default: