From 44dc9ced203f7b48b786e81cbc9f2570be52de26 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 14:11:02 +0200 Subject: [PATCH 01/27] add boilerplate for staking precompile with empty delegate method --- app/app.go | 2 +- precompiles/precompiles.go | 22 ++- precompiles/precompiles_test.go | 4 +- precompiles/staking/IStaking.abi | 31 ++++ precompiles/staking/IStaking.go | 202 +++++++++++++++++++++++ precompiles/staking/IStaking.json | 33 ++++ precompiles/staking/IStaking.sol | 22 +++ precompiles/staking/bindings.go | 7 + precompiles/staking/staking.go | 144 ++++++++++++++++ scripts/bindings-stateful-precompiles.sh | 2 + 10 files changed, 462 insertions(+), 7 deletions(-) create mode 100644 precompiles/staking/IStaking.abi create mode 100644 precompiles/staking/IStaking.go create mode 100644 precompiles/staking/IStaking.json create mode 100644 precompiles/staking/IStaking.sol create mode 100644 precompiles/staking/bindings.go create mode 100644 precompiles/staking/staking.go diff --git a/app/app.go b/app/app.go index 32d85214eb..53dadab3ba 100644 --- a/app/app.go +++ b/app/app.go @@ -567,7 +567,7 @@ func New( &app.FeeMarketKeeper, tracer, evmSs, - precompiles.StatefulContracts(&app.FungibleKeeper, appCodec, storetypes.TransientGasConfig()), + precompiles.StatefulContracts(&app.FungibleKeeper, app.StakingKeeper, appCodec, storetypes.TransientGasConfig()), app.ConsensusParamsKeeper, aggregateAllKeys(keys, tKeys, memKeys), ) diff --git a/precompiles/precompiles.go b/precompiles/precompiles.go index e3901aa873..440c5cd7a8 100644 --- a/precompiles/precompiles.go +++ b/precompiles/precompiles.go @@ -9,8 +9,10 @@ import ( ethparams "github.com/ethereum/go-ethereum/params" evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/zeta-chain/zetacore/precompiles/prototype" - "github.com/zeta-chain/zetacore/x/fungible/keeper" + "github.com/zeta-chain/zetacore/precompiles/staking" + fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" ) // EnabledStatefulContracts contains the list of all enabled stateful precompiles. @@ -18,26 +20,38 @@ import ( // Setting to false a contract here will disable it, not being included in the blockchain. var EnabledStatefulContracts = map[common.Address]bool{ prototype.ContractAddress: true, + staking.ContractAddress: true, } // StatefulContracts returns all the registered precompiled contracts. func StatefulContracts( - fungibleKeeper *keeper.Keeper, + fungibleKeeper *fungiblekeeper.Keeper, + stakingKeeper *stakingkeeper.Keeper, cdc codec.Codec, gasConfig storetypes.GasConfig, ) (precompiledContracts []evmkeeper.CustomContractFn) { // Initialize at 0 the custom compiled contracts and the addresses. precompiledContracts = make([]evmkeeper.CustomContractFn, 0) - // Define the regular contract function. + // Define the prototype contract function. if EnabledStatefulContracts[prototype.ContractAddress] { prototypeContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract { return prototype.NewIPrototypeContract(fungibleKeeper, cdc, gasConfig) } - // Append the regular contract to the precompiledContracts slice. + // Append the prototype contract to the precompiledContracts slice. precompiledContracts = append(precompiledContracts, prototypeContract) } + // Define the staking contract function. + if EnabledStatefulContracts[prototype.ContractAddress] { + stakingContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract { + return staking.NewIStakingContract(stakingKeeper, cdc, gasConfig) + } + + // Append the prototype contract to the precompiledContracts slice. + precompiledContracts = append(precompiledContracts, stakingContract) + } + return precompiledContracts } diff --git a/precompiles/precompiles_test.go b/precompiles/precompiles_test.go index 06c8ccb0bb..f5decc49ec 100644 --- a/precompiles/precompiles_test.go +++ b/precompiles/precompiles_test.go @@ -11,7 +11,7 @@ import ( ) func Test_StatefulContracts(t *testing.T) { - k, ctx, _, _ := keeper.FungibleKeeper(t) + k, ctx, sdkk, _ := keeper.FungibleKeeper(t) gasConfig := storetypes.TransientGasConfig() var encoding ethermint.EncodingConfig @@ -25,7 +25,7 @@ func Test_StatefulContracts(t *testing.T) { } // StatefulContracts() should return all the enabled contracts. - contracts := StatefulContracts(k, appCodec, gasConfig) + contracts := StatefulContracts(k, &sdkk.StakingKeeper, appCodec, gasConfig) require.NotNil(t, contracts, "StatefulContracts() should not return a nil slice") require.Len(t, contracts, expectedContracts, "StatefulContracts() should return all the enabled contracts") diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi new file mode 100644 index 0000000000..fd3d016dcd --- /dev/null +++ b/precompiles/staking/IStaking.abi @@ -0,0 +1,31 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "delegate", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go new file mode 100644 index 0000000000..4de85e998a --- /dev/null +++ b/precompiles/staking/IStaking.go @@ -0,0 +1,202 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package staking + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// IStakingMetaData contains all meta data concerning the IStaking contract. +var IStakingMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", +} + +// IStakingABI is the input ABI used to generate the binding from. +// Deprecated: Use IStakingMetaData.ABI instead. +var IStakingABI = IStakingMetaData.ABI + +// IStaking is an auto generated Go binding around an Ethereum contract. +type IStaking struct { + IStakingCaller // Read-only binding to the contract + IStakingTransactor // Write-only binding to the contract + IStakingFilterer // Log filterer for contract events +} + +// IStakingCaller is an auto generated read-only Go binding around an Ethereum contract. +type IStakingCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IStakingTransactor is an auto generated write-only Go binding around an Ethereum contract. +type IStakingTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IStakingFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type IStakingFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// IStakingSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type IStakingSession struct { + Contract *IStaking // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IStakingCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type IStakingCallerSession struct { + Contract *IStakingCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// IStakingTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type IStakingTransactorSession struct { + Contract *IStakingTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// IStakingRaw is an auto generated low-level Go binding around an Ethereum contract. +type IStakingRaw struct { + Contract *IStaking // Generic contract binding to access the raw methods on +} + +// IStakingCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type IStakingCallerRaw struct { + Contract *IStakingCaller // Generic read-only contract binding to access the raw methods on +} + +// IStakingTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type IStakingTransactorRaw struct { + Contract *IStakingTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewIStaking creates a new instance of IStaking, bound to a specific deployed contract. +func NewIStaking(address common.Address, backend bind.ContractBackend) (*IStaking, error) { + contract, err := bindIStaking(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &IStaking{IStakingCaller: IStakingCaller{contract: contract}, IStakingTransactor: IStakingTransactor{contract: contract}, IStakingFilterer: IStakingFilterer{contract: contract}}, nil +} + +// NewIStakingCaller creates a new read-only instance of IStaking, bound to a specific deployed contract. +func NewIStakingCaller(address common.Address, caller bind.ContractCaller) (*IStakingCaller, error) { + contract, err := bindIStaking(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &IStakingCaller{contract: contract}, nil +} + +// NewIStakingTransactor creates a new write-only instance of IStaking, bound to a specific deployed contract. +func NewIStakingTransactor(address common.Address, transactor bind.ContractTransactor) (*IStakingTransactor, error) { + contract, err := bindIStaking(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &IStakingTransactor{contract: contract}, nil +} + +// NewIStakingFilterer creates a new log filterer instance of IStaking, bound to a specific deployed contract. +func NewIStakingFilterer(address common.Address, filterer bind.ContractFilterer) (*IStakingFilterer, error) { + contract, err := bindIStaking(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &IStakingFilterer{contract: contract}, nil +} + +// bindIStaking binds a generic wrapper to an already deployed contract. +func bindIStaking(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := IStakingMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IStaking *IStakingRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IStaking.Contract.IStakingCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IStaking *IStakingRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IStaking.Contract.IStakingTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IStaking *IStakingRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IStaking.Contract.IStakingTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_IStaking *IStakingCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _IStaking.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_IStaking *IStakingTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _IStaking.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_IStaking *IStakingTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _IStaking.Contract.contract.Transact(opts, method, params...) +} + +// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// +// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingTransactor) Delegate(opts *bind.TransactOpts, delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "delegate", delegator, validator, amount) +} + +// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// +// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingSession) Delegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Delegate(&_IStaking.TransactOpts, delegator, validator, amount) +} + +// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// +// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingTransactorSession) Delegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Delegate(&_IStaking.TransactOpts, delegator, validator, amount) +} diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json new file mode 100644 index 0000000000..b4cce21b24 --- /dev/null +++ b/precompiles/staking/IStaking.json @@ -0,0 +1,33 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "delegate", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol new file mode 100644 index 0000000000..ad75176103 --- /dev/null +++ b/precompiles/staking/IStaking.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.26; + +/// @dev The IStaking contract's address. +address constant ISTAKING_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000066; // 102 + +/// @dev The IStaking contract's instance. +IStaking constant ISTAKING_CONTRACT = IStaking( + ISTAKING_PRECOMPILE_ADDRESS +); + +interface IStaking { + /// @dev Delegate coin to validator + /// @param delegator Delegator address + /// @param validator Validator address + /// @param amount Coins amount + /// @return success Delegation success + function delegate( + address delegator, + string memory validator, + uint256 amount + ) external returns (bool success); +} diff --git a/precompiles/staking/bindings.go b/precompiles/staking/bindings.go new file mode 100644 index 0000000000..5e182735be --- /dev/null +++ b/precompiles/staking/bindings.go @@ -0,0 +1,7 @@ +//go:generate sh -c "solc IStaking.sol --combined-json abi | jq '.contracts.\"IStaking.sol:IStaking\"' > IStaking.json" +//go:generate sh -c "cat IStaking.json | jq .abi > IStaking.abi" +//go:generate sh -c "abigen --abi IStaking.abi --pkg staking --type IStaking --out IStaking.go" + +package staking + +var _ Contract diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go new file mode 100644 index 0000000000..389b8c8970 --- /dev/null +++ b/precompiles/staking/staking.go @@ -0,0 +1,144 @@ +package staking + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + ptypes "github.com/zeta-chain/zetacore/precompiles/types" +) + +const ( + DelegateMethodName = "delegate" +) + +var ( + ABI abi.ABI + ContractAddress = common.HexToAddress("0x0000000000000000000000000000000000000066") + GasRequiredByMethod = map[[4]byte]uint64{} +) + +func init() { + initABI() +} + +func initABI() { + if err := ABI.UnmarshalJSON([]byte(IStakingMetaData.ABI)); err != nil { + panic(err) + } + + GasRequiredByMethod = map[[4]byte]uint64{} + for methodName := range ABI.Methods { + var methodID [4]byte + copy(methodID[:], ABI.Methods[methodName].ID[:4]) + switch methodName { + // TODO: just temporary + case DelegateMethodName: + GasRequiredByMethod[methodID] = 10000 + default: + GasRequiredByMethod[methodID] = 0 + } + } +} + +type Contract struct { + ptypes.BaseContract + + stakingKeeper stakingkeeper.Keeper + cdc codec.Codec + kvGasConfig storetypes.GasConfig +} + +func NewIStakingContract( + stakingKeeper *stakingkeeper.Keeper, + cdc codec.Codec, + kvGasConfig storetypes.GasConfig, +) *Contract { + return &Contract{ + BaseContract: ptypes.NewBaseContract(ContractAddress), + stakingKeeper: *stakingKeeper, + cdc: cdc, + kvGasConfig: kvGasConfig, + } +} + +// Address() is required to implement the PrecompiledContract interface. +func (c *Contract) Address() common.Address { + return ContractAddress +} + +// Abi() is required to implement the PrecompiledContract interface. +func (c *Contract) Abi() abi.ABI { + return ABI +} + +// RequiredGas is required to implement the PrecompiledContract interface. +// The gas has to be calculated deterministically based on the input. +func (c *Contract) RequiredGas(input []byte) uint64 { + // base cost to prevent large input size + baseCost := uint64(len(input)) * c.kvGasConfig.WriteCostPerByte + + // get methodID (first 4 bytes) + var methodID [4]byte + copy(methodID[:], input[:4]) + + if requiredGas, ok := GasRequiredByMethod[methodID]; ok { + return requiredGas + baseCost + } + + // Can not happen, but return 0 if the method is not found. + return 0 +} + +func (c *Contract) Delegate( + ctx sdk.Context, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != 1 { + return nil, &(ptypes.ErrInvalidNumberOfArgs{ + Got: len(args), + Expect: 1, + }) + } + + return []byte{}, nil +} + +// Run is the entrypoint of the precompiled contract, it switches over the input method, +// and execute them accordingly. +func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, error) { + method, err := ABI.MethodById(contract.Input[:4]) + if err != nil { + return nil, err + } + + args, err := method.Inputs.Unpack(contract.Input[4:]) + if err != nil { + return nil, err + } + + stateDB := evm.StateDB.(ptypes.ExtStateDB) + + switch method.Name { + case DelegateMethodName: + var res []byte + execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { + res, err = c.Delegate(ctx, method, args) + return err + }) + if execErr != nil { + return nil, err + } + return res, nil + + default: + return nil, ptypes.ErrInvalidMethod{ + Method: method.Name, + } + } +} diff --git a/scripts/bindings-stateful-precompiles.sh b/scripts/bindings-stateful-precompiles.sh index 3083f8bf43..63fe3aa23e 100755 --- a/scripts/bindings-stateful-precompiles.sh +++ b/scripts/bindings-stateful-precompiles.sh @@ -43,8 +43,10 @@ function bindings() { cd $1 go generate > /dev/null 2>&1 echo "Generated bindings for $1" + cd - > /dev/null 2>&1 } # List of bindings to generate bindings ./precompiles/prototype +bindings ./precompiles/staking From 766a30ab56e9dd8ecd76abbec21c0f555a1dfc58 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 17:33:52 +0200 Subject: [PATCH 02/27] add simple undelegate and redelegate methods --- precompiles/staking/IStaking.abi | 63 +++++++++++++ precompiles/staking/IStaking.go | 44 +++++++++- precompiles/staking/IStaking.json | 63 +++++++++++++ precompiles/staking/IStaking.sol | 26 +++++- precompiles/staking/staking.go | 141 +++++++++++++++++++++++++++++- 5 files changed, 332 insertions(+), 5 deletions(-) diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index fd3d016dcd..f4041f012d 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -27,5 +27,68 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validatorSrd", + "type": "string" + }, + { + "internalType": "string", + "name": "validatorDst", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "redelegate", + "outputs": [ + { + "internalType": "int64", + "name": "completionTime", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegate", + "outputs": [ + { + "internalType": "int64", + "name": "completionTime", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" } ] diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index 4de85e998a..19317b817c 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -31,7 +31,7 @@ var ( // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrd\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"redelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"undelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -200,3 +200,45 @@ func (_IStaking *IStakingSession) Delegate(delegator common.Address, validator s func (_IStaking *IStakingTransactorSession) Delegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { return _IStaking.Contract.Delegate(&_IStaking.TransactOpts, delegator, validator, amount) } + +// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// +// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) Redelegate(opts *bind.TransactOpts, delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "redelegate", delegator, validatorSrd, validatorDst, amount) +} + +// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// +// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) Redelegate(delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrd, validatorDst, amount) +} + +// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// +// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) Redelegate(delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrd, validatorDst, amount) +} + +// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// +// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) Undelegate(opts *bind.TransactOpts, delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "undelegate", delegator, validator, amount) +} + +// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// +// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) Undelegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Undelegate(&_IStaking.TransactOpts, delegator, validator, amount) +} + +// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// +// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) Undelegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Undelegate(&_IStaking.TransactOpts, delegator, validator, amount) +} diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index b4cce21b24..1a94cb693d 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -28,6 +28,69 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validatorSrd", + "type": "string" + }, + { + "internalType": "string", + "name": "validatorDst", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "redelegate", + "outputs": [ + { + "internalType": "int64", + "name": "completionTime", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "undelegate", + "outputs": [ + { + "internalType": "int64", + "name": "completionTime", + "type": "int64" + } + ], + "stateMutability": "nonpayable", + "type": "function" } ] } diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index ad75176103..03179e0b43 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -9,7 +9,7 @@ IStaking constant ISTAKING_CONTRACT = IStaking( ); interface IStaking { - /// @dev Delegate coin to validator + /// @dev Delegate coins to validator /// @param delegator Delegator address /// @param validator Validator address /// @param amount Coins amount @@ -19,4 +19,28 @@ interface IStaking { string memory validator, uint256 amount ) external returns (bool success); + + /// @dev Undelegate coins from validator + /// @param delegator Delegator address + /// @param validator Validator address + /// @param amount Coins amount + /// @return completionTime Time when undelegation is done + function undelegate( + address delegator, + string memory validator, + uint256 amount + ) external returns (int64 completionTime); + + /// @dev Redelegate coins from validatorSrd to validatorDst + /// @param delegator Delegator address + /// @param validatorSrd Validator from address + /// @param validatorDst Validator to address + /// @param amount Coins amount + /// @return completionTime Time when redelegation is done + function redelegate( + address delegator, + string memory validatorSrd, + string memory validatorDst, + uint256 amount + ) external returns (int64 completionTime); } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 389b8c8970..4e0b866125 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -1,6 +1,9 @@ package staking import ( + "fmt" + "math/big" + "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -9,6 +12,9 @@ import ( "github.com/ethereum/go-ethereum/core/vm" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + "cosmossdk.io/math" ptypes "github.com/zeta-chain/zetacore/precompiles/types" ) @@ -99,14 +105,143 @@ func (c *Contract) Delegate( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 1 { + if len(args) != 3 { + return nil, &(ptypes.ErrInvalidNumberOfArgs{ + Got: len(args), + Expect: 3, + }) + } + + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) + + delegatorAddress, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + } + + validatorAddress, ok := args[1].(string) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + } + + amount, ok := args[2].(int64) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + } + + res, err := msgServer.Delegate(ctx, &stakingtypes.MsgDelegate{ + DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + ValidatorAddress: validatorAddress, + Amount: sdk.Coin{ + Denom: c.stakingKeeper.BondDenom(ctx), + Amount: math.NewIntFromBigInt(big.NewInt(amount)), + }, + }) + if err != nil { + return nil, err + } + + fmt.Println("delegate res", res) + + return method.Outputs.Pack(true) +} + +func (c *Contract) Undelegate( + ctx sdk.Context, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != 3 { + return nil, &(ptypes.ErrInvalidNumberOfArgs{ + Got: len(args), + Expect: 3, + }) + } + + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) + + delegatorAddress, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + } + + validatorAddress, ok := args[1].(string) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + } + + amount, ok := args[2].(int64) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + } + + res, err := msgServer.Undelegate(ctx, &stakingtypes.MsgUndelegate{ + DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + ValidatorAddress: validatorAddress, + Amount: sdk.Coin{ + Denom: c.stakingKeeper.BondDenom(ctx), + Amount: math.NewIntFromBigInt(big.NewInt(amount)), + }, + }) + if err != nil { + return nil, err + } + + fmt.Println("undelegate res", res) + + return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) +} + +func (c *Contract) Redelegate( + ctx sdk.Context, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != 4 { return nil, &(ptypes.ErrInvalidNumberOfArgs{ Got: len(args), - Expect: 1, + Expect: 4, }) } - return []byte{}, nil + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) + + delegatorAddress, ok := args[0].(common.Address) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + } + + validatorSrcAddress, ok := args[1].(string) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + } + + validatorDstAddress, ok := args[2].(string) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + } + + amount, ok := args[3].(int64) + if !ok { + return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + } + + res, err := msgServer.BeginRedelegate(ctx, &stakingtypes.MsgBeginRedelegate{ + DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + ValidatorSrcAddress: validatorSrcAddress, + ValidatorDstAddress: validatorDstAddress, + Amount: sdk.Coin{ + Denom: c.stakingKeeper.BondDenom(ctx), + Amount: math.NewIntFromBigInt(big.NewInt(amount)), + }, + }) + if err != nil { + return nil, err + } + + fmt.Println("undelegate res", res) + + return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } // Run is the entrypoint of the precompiled contract, it switches over the input method, From 9e970653a1b6299d311922612335eac18e1b0a72 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 18:09:29 +0200 Subject: [PATCH 03/27] add new methods to run --- precompiles/staking/staking.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 4e0b866125..e3554746f6 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -19,7 +19,9 @@ import ( ) const ( - DelegateMethodName = "delegate" + DelegateMethodName = "delegate" + UndelegateMethodName = "undelegate" + RedelegateMethodName = "redelegate" ) var ( @@ -45,6 +47,10 @@ func initABI() { // TODO: just temporary case DelegateMethodName: GasRequiredByMethod[methodID] = 10000 + case UndelegateMethodName: + GasRequiredByMethod[methodID] = 10000 + case RedelegateMethodName: + GasRequiredByMethod[methodID] = 10000 default: GasRequiredByMethod[methodID] = 0 } @@ -270,6 +276,26 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro return nil, err } return res, nil + case UndelegateMethodName: + var res []byte + execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { + res, err = c.Undelegate(ctx, method, args) + return err + }) + if execErr != nil { + return nil, err + } + return res, nil + case RedelegateMethodName: + var res []byte + execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { + res, err = c.Redelegate(ctx, method, args) + return err + }) + if execErr != nil { + return nil, err + } + return res, nil default: return nil, ptypes.ErrInvalidMethod{ From 6984671f01276c1e112074f14ecdb13c9c8e269a Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 20:24:06 +0200 Subject: [PATCH 04/27] add simple tests --- precompiles/prototype/prototype_test.go | 1 - precompiles/staking/staking.go | 5 +- precompiles/staking/staking_test.go | 135 ++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 precompiles/staking/staking_test.go diff --git a/precompiles/prototype/prototype_test.go b/precompiles/prototype/prototype_test.go index 8999fa14eb..78b681715f 100644 --- a/precompiles/prototype/prototype_test.go +++ b/precompiles/prototype/prototype_test.go @@ -296,7 +296,6 @@ func Test_GetGasStabilityPoolBalance(t *testing.T) { ) t.Run("should fail with invalid arguments", func(t *testing.T) { - t.Run("invalid number of arguments", func(t *testing.T) { args := []interface{}{int64(1337), "second argument"} _, err := contract.GetGasStabilityPoolBalance(ctx, &methodID, args) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index e3554746f6..d27ecced16 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -44,7 +44,8 @@ func initABI() { var methodID [4]byte copy(methodID[:], ABI.Methods[methodName].ID[:4]) switch methodName { - // TODO: just temporary + // TODO: just temporary values, double check these flat values + // can we just use WriteCostFlat from gas config? case DelegateMethodName: GasRequiredByMethod[methodID] = 10000 case UndelegateMethodName: @@ -245,7 +246,7 @@ func (c *Contract) Redelegate( return nil, err } - fmt.Println("undelegate res", res) + fmt.Println("redelegate res", res) return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go new file mode 100644 index 0000000000..ad323d8f52 --- /dev/null +++ b/precompiles/staking/staking_test.go @@ -0,0 +1,135 @@ +package staking + +import ( + "encoding/json" + "testing" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/stretchr/testify/require" + ethermint "github.com/zeta-chain/ethermint/types" + "github.com/zeta-chain/zetacore/testutil/keeper" +) + +func Test_IStakingContract(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + keys, memKeys, tkeys, allKeys := keeper.StoreKeys() + cdc := keeper.NewCodec() + sdkKeepers := keeper.NewSDKKeepersWithKeys(cdc, keys, memKeys, tkeys, allKeys) + + gasConfig := storetypes.TransientGasConfig() + + t.Run("should create contract and check address and ABI", func(t *testing.T) { + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + address := contract.Address() + require.Equal(t, ContractAddress, address, "contract address should match the precompiled address") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + }) + + t.Run("should check methods are present in ABI", func(t *testing.T) { + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + abi := contract.Abi() + + require.NotNil(t, abi.Methods[DelegateMethodName], "delegate method should be present in the ABI") + require.NotNil(t, abi.Methods[UndelegateMethodName], "undelegate method should be present in the ABI") + require.NotNil( + t, + abi.Methods[RedelegateMethodName], + "redelegate method should be present in the ABI", + ) + }) + + t.Run("should check gas requirements for methods", func(t *testing.T) { + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + abi := contract.Abi() + var method [4]byte + + t.Run("delegate", func(t *testing.T) { + delegate := contract.RequiredGas(abi.Methods[DelegateMethodName].ID) + copy(method[:], abi.Methods[DelegateMethodName].ID[:4]) + baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte + require.Equal( + t, + GasRequiredByMethod[method]+baseCost, + delegate, + "delegate method should require %d gas, got %d", + GasRequiredByMethod[method]+baseCost, + delegate, + ) + }) + + t.Run("undelegate", func(t *testing.T) { + undelegate := contract.RequiredGas(abi.Methods[UndelegateMethodName].ID) + copy(method[:], abi.Methods[UndelegateMethodName].ID[:4]) + baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte + require.Equal( + t, + GasRequiredByMethod[method]+baseCost, + undelegate, + "undelegate method should require %d gas, got %d", + GasRequiredByMethod[method]+baseCost, + undelegate, + ) + }) + + t.Run("redelegate", func(t *testing.T) { + redelegate := contract.RequiredGas(abi.Methods[RedelegateMethodName].ID) + copy(method[:], abi.Methods[RedelegateMethodName].ID[:4]) + baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte + require.Equal( + t, + GasRequiredByMethod[method]+baseCost, + redelegate, + "redelegate method should require %d gas, got %d", + GasRequiredByMethod[method]+baseCost, + redelegate, + ) + }) + + t.Run("invalid method", func(t *testing.T) { + invalidMethodBytes := []byte("invalidMethod") + gasInvalidMethod := contract.RequiredGas(invalidMethodBytes) + require.Equal( + t, + uint64(0), + gasInvalidMethod, + "invalid method should require %d gas, got %d", + uint64(0), + gasInvalidMethod, + ) + }) + }) +} + +func Test_InvalidMethod(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + keys, memKeys, tkeys, allKeys := keeper.StoreKeys() + cdc := keeper.NewCodec() + sdkKeepers := keeper.NewSDKKeepersWithKeys(cdc, keys, memKeys, tkeys, allKeys) + gasConfig := storetypes.TransientGasConfig() + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + _, doNotExist := abi.Methods["invalidMethod"] + require.False(t, doNotExist, "invalidMethod should not be present in the ABI") +} + +func Test_InvalidABI(t *testing.T) { + IStakingMetaData.ABI = "invalid json" + defer func() { + if r := recover(); r != nil { + require.IsType(t, &json.SyntaxError{}, r, "expected error type: json.SyntaxError, got: %T", r) + } + }() + + initABI() +} From 6c2128bb170112b54e10f47a896196ee50184d0e Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 20:28:18 +0200 Subject: [PATCH 05/27] add delegator caller checks --- precompiles/staking/staking.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index d27ecced16..a13e205f8d 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -109,6 +109,7 @@ func (c *Contract) RequiredGas(input []byte) uint64 { func (c *Contract) Delegate( ctx sdk.Context, + contract *vm.Contract, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -126,6 +127,10 @@ func (c *Contract) Delegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } + if contract.CallerAddress != delegatorAddress { + return nil, fmt.Errorf("caller is not delegator address") + } + validatorAddress, ok := args[1].(string) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) @@ -155,6 +160,7 @@ func (c *Contract) Delegate( func (c *Contract) Undelegate( ctx sdk.Context, + contract *vm.Contract, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -172,6 +178,10 @@ func (c *Contract) Undelegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } + if contract.CallerAddress != delegatorAddress { + return nil, fmt.Errorf("caller is not delegator address") + } + validatorAddress, ok := args[1].(string) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) @@ -201,6 +211,7 @@ func (c *Contract) Undelegate( func (c *Contract) Redelegate( ctx sdk.Context, + contract *vm.Contract, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -218,6 +229,10 @@ func (c *Contract) Redelegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } + if contract.CallerAddress != delegatorAddress { + return nil, fmt.Errorf("caller is not delegator address") + } + validatorSrcAddress, ok := args[1].(string) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) @@ -270,7 +285,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case DelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Delegate(ctx, method, args) + res, err = c.Delegate(ctx, contract, method, args) return err }) if execErr != nil { @@ -280,7 +295,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case UndelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Undelegate(ctx, method, args) + res, err = c.Undelegate(ctx, contract, method, args) return err }) if execErr != nil { @@ -290,7 +305,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case RedelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Redelegate(ctx, method, args) + res, err = c.Redelegate(ctx, contract, method, args) return err }) if execErr != nil { From dacf5fb0f3b805fd4dc95dd56b955cdae525080d Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 20:43:24 +0200 Subject: [PATCH 06/27] fix del addr origin check --- precompiles/staking/staking.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index a13e205f8d..ee234494d8 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -109,7 +109,7 @@ func (c *Contract) RequiredGas(input []byte) uint64 { func (c *Contract) Delegate( ctx sdk.Context, - contract *vm.Contract, + origin common.Address, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -127,8 +127,8 @@ func (c *Contract) Delegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if contract.CallerAddress != delegatorAddress { - return nil, fmt.Errorf("caller is not delegator address") + if origin != delegatorAddress { + return nil, fmt.Errorf("origin is not delegator address") } validatorAddress, ok := args[1].(string) @@ -160,7 +160,7 @@ func (c *Contract) Delegate( func (c *Contract) Undelegate( ctx sdk.Context, - contract *vm.Contract, + origin common.Address, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -178,8 +178,8 @@ func (c *Contract) Undelegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if contract.CallerAddress != delegatorAddress { - return nil, fmt.Errorf("caller is not delegator address") + if origin != delegatorAddress { + return nil, fmt.Errorf("origin is not delegator address") } validatorAddress, ok := args[1].(string) @@ -211,7 +211,7 @@ func (c *Contract) Undelegate( func (c *Contract) Redelegate( ctx sdk.Context, - contract *vm.Contract, + origin common.Address, method *abi.Method, args []interface{}, ) ([]byte, error) { @@ -229,8 +229,8 @@ func (c *Contract) Redelegate( return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if contract.CallerAddress != delegatorAddress { - return nil, fmt.Errorf("caller is not delegator address") + if origin != delegatorAddress { + return nil, fmt.Errorf("origin is not delegator address") } validatorSrcAddress, ok := args[1].(string) @@ -285,7 +285,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case DelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Delegate(ctx, contract, method, args) + res, err = c.Delegate(ctx, evm.Origin, method, args) return err }) if execErr != nil { @@ -295,7 +295,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case UndelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Undelegate(ctx, contract, method, args) + res, err = c.Undelegate(ctx, evm.Origin, method, args) return err }) if execErr != nil { @@ -305,7 +305,7 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro case RedelegateMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Redelegate(ctx, contract, method, args) + res, err = c.Redelegate(ctx, evm.Origin, method, args) return err }) if execErr != nil { From f16df7f67e369b165a9e4dd37ec0c13fad13dea7 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 23:31:31 +0200 Subject: [PATCH 07/27] add unit tests for delegate method --- precompiles/staking/staking.go | 4 +- precompiles/staking/staking_test.go | 177 ++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 3 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index ee234494d8..23e3a7a237 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -141,7 +141,7 @@ func (c *Contract) Delegate( return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) } - res, err := msgServer.Delegate(ctx, &stakingtypes.MsgDelegate{ + _, err := msgServer.Delegate(ctx, &stakingtypes.MsgDelegate{ DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), ValidatorAddress: validatorAddress, Amount: sdk.Coin{ @@ -153,8 +153,6 @@ func (c *Contract) Delegate( return nil, err } - fmt.Println("delegate res", res) - return method.Outputs.Pack(true) } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index ad323d8f52..f2b015fbfe 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -4,10 +4,20 @@ import ( "encoding/json" "testing" + "math/rand" + + tmdb "github.com/cometbft/cometbft-db" + "github.com/cosmos/cosmos-sdk/store" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ethermint "github.com/zeta-chain/ethermint/types" + "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) func Test_IStakingContract(t *testing.T) { @@ -133,3 +143,170 @@ func Test_InvalidABI(t *testing.T) { initABI() } + +func Test_Delegate(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + + cdc := keeper.NewCodec() + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) + gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + methodID := abi.Methods[DelegateMethodName] + + t.Run("should delegate", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.NoError(t, err) + }) + + t.Run("should fail if delegation fails", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + // delegator without funds + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, int64(42)} + + _, err := contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if wrong args amount", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if delegator is not eth addr", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegator, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if validator is not valid string", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if amount is not int64", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if validator doesn't exist", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.NoError(t, err) + }) +} From b335fc9ec9da03fa8049440ceb02a89357c2beaf Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 23:32:20 +0200 Subject: [PATCH 08/27] changelog --- changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index fa1d0af435..af64b1bd6e 100644 --- a/changelog.md +++ b/changelog.md @@ -14,7 +14,8 @@ * [2681](https://github.com/zeta-chain/node/pull/2681) - implement `MsgUpdateERC20CustodyPauseStatus` to pause or unpause ERC20 Custody contract (to be used for the migration process for smart contract V2) * [2644](https://github.com/zeta-chain/node/pull/2644) - add created_timestamp to cctx status * [2673](https://github.com/zeta-chain/node/pull/2673) - add relayer key importer, encryption and decryption -* [2633](https://github.com/zeta-chain/node/pull/2633) - support for stateful precompiled contracts. +* [2633](https://github.com/zeta-chain/node/pull/2633) - support for stateful precompiled contracts +* [2784](https://github.com/zeta-chain/node/pull/2784) - staking precompiled contract ### Refactor From f5dc0c3a88a34521220528f9e6a3f3ecb56faf7c Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 28 Aug 2024 23:48:35 +0200 Subject: [PATCH 09/27] add undelegate tests --- precompiles/staking/staking.go | 2 - precompiles/staking/staking_test.go | 228 +++++++++++++++++++++++++++- 2 files changed, 227 insertions(+), 3 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 23e3a7a237..474e401e65 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -202,8 +202,6 @@ func (c *Contract) Undelegate( return nil, err } - fmt.Println("undelegate res", res) - return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index f2b015fbfe..c6ca5e913d 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -169,6 +169,26 @@ func Test_Delegate(t *testing.T) { methodID := abi.Methods[DelegateMethodName] + t.Run("should fail if validator doesn't exist", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + t.Run("should delegate", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) @@ -190,6 +210,28 @@ func Test_Delegate(t *testing.T) { require.NoError(t, err) }) + t.Run("should fail if origin is not delegator", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + + args := []interface{}{originEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + require.ErrorContains(t, err, "origin is not delegator address") + }) + t.Run("should fail if delegation fails", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) @@ -289,6 +331,32 @@ func Test_Delegate(t *testing.T) { _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) require.Error(t, err) }) +} + +func Test_Undelegate(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + + cdc := keeper.NewCodec() + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) + gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + methodID := abi.Methods[UndelegateMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { r := rand.New(rand.NewSource(42)) @@ -306,7 +374,165 @@ func Test_Delegate(t *testing.T) { args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should undelegate", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate first + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, args) + require.NoError(t, err) + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.NoError(t, err) + }) + + t.Run("should fail if origin is not delegator", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate first + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, args) + require.NoError(t, err) + + originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + + _, err = contract.Undelegate(ctx, originEthAddr, &methodID, args) + require.ErrorContains(t, err, "origin is not delegator address") + }) + + t.Run("should fail if no previous delegation", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if wrong args amount", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress} + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if delegator is not eth addr", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegator, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if validator is not valid string", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if amount is not int64", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + + _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + require.Error(t, err) }) } From 758864d3d3073af34c84fdfbf45546df94086550 Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 01:00:42 +0200 Subject: [PATCH 10/27] add redelegate unit tests --- precompiles/staking/IStaking.abi | 2 +- precompiles/staking/IStaking.go | 20 +-- precompiles/staking/IStaking.json | 2 +- precompiles/staking/IStaking.sol | 4 +- precompiles/staking/staking.go | 2 - precompiles/staking/staking_test.go | 267 ++++++++++++++++++++++++++++ 6 files changed, 281 insertions(+), 16 deletions(-) diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index f4041f012d..39449db59d 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -37,7 +37,7 @@ }, { "internalType": "string", - "name": "validatorSrd", + "name": "validatorSrc", "type": "string" }, { diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index 19317b817c..c49c5e6597 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -31,7 +31,7 @@ var ( // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrd\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"redelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"undelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"redelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"undelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -203,23 +203,23 @@ func (_IStaking *IStakingTransactorSession) Delegate(delegator common.Address, v // Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. // -// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactor) Redelegate(opts *bind.TransactOpts, delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.contract.Transact(opts, "redelegate", delegator, validatorSrd, validatorDst, amount) +// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) Redelegate(opts *bind.TransactOpts, delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "redelegate", delegator, validatorSrc, validatorDst, amount) } // Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. // -// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingSession) Redelegate(delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrd, validatorDst, amount) +// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) Redelegate(delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrc, validatorDst, amount) } // Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. // -// Solidity: function redelegate(address delegator, string validatorSrd, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactorSession) Redelegate(delegator common.Address, validatorSrd string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrd, validatorDst, amount) +// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) Redelegate(delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrc, validatorDst, amount) } // Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 1a94cb693d..0a089dff93 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -38,7 +38,7 @@ }, { "internalType": "string", - "name": "validatorSrd", + "name": "validatorSrc", "type": "string" }, { diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index 03179e0b43..5164db1321 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -33,13 +33,13 @@ interface IStaking { /// @dev Redelegate coins from validatorSrd to validatorDst /// @param delegator Delegator address - /// @param validatorSrd Validator from address + /// @param validatorSrc Validator from address /// @param validatorDst Validator to address /// @param amount Coins amount /// @return completionTime Time when redelegation is done function redelegate( address delegator, - string memory validatorSrd, + string memory validatorSrc, string memory validatorDst, uint256 amount ) external returns (int64 completionTime); diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 474e401e65..4b24b020bc 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -257,8 +257,6 @@ func (c *Contract) Redelegate( return nil, err } - fmt.Println("redelegate res", res) - return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index c6ca5e913d..25cf60f94d 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -536,3 +536,270 @@ func Test_Undelegate(t *testing.T) { require.Error(t, err) }) } + +func Test_Redelegate(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + + cdc := keeper.NewCodec() + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) + gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + methodID := abi.Methods[RedelegateMethodName] + + t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.Error(t, err) + }) + + t.Run("should redelegate", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // redelegate to validator dest + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.NoError(t, err) + }) + + t.Run("should fail if delegator is invalid arg", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.Error(t, err) + }) + + t.Run("should fail if validator src is invalid arg", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, 42, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.Error(t, err) + }) + + t.Run("should fail if validator dest is invalid arg", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.NoError(t, err) + }) + + t.Run("should fail if amount is invalid arg", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.Error(t, err) + }) + + t.Run("should fail if wrong args amount", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} + + _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + require.Error(t, err) + }) + + t.Run("should fail if origin is not delegator", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validatorSrc := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) + validatorDest := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) + + delegator := sample.Bech32AccAddress() + delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + require.NoError(t, err) + + delegatorAddr := common.BytesToAddress(delegator.Bytes()) + + argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + // delegate to validator src + delegateMethodID := abi.Methods[DelegateMethodName] + _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + require.NoError(t, err) + + argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + + originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + _, err = contract.Redelegate(ctx, originEthAddr, &methodID, argsRedelegate) + require.ErrorContains(t, err, "origin is not delegator") + }) +} From f7aa77ef0c9a373cfe2ba49a1afc1afffe31e9b4 Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 15:53:25 +0200 Subject: [PATCH 11/27] renamings --- precompiles/staking/IStaking.abi | 12 +- precompiles/staking/IStaking.go | 74 ++--- precompiles/staking/IStaking.json | 12 +- precompiles/staking/IStaking.sol | 30 +- precompiles/staking/staking.go | 54 ++-- precompiles/staking/staking_test.go | 442 ++++++++++++++-------------- 6 files changed, 312 insertions(+), 312 deletions(-) diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index 39449db59d..0efdcf1843 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -3,7 +3,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -17,7 +17,7 @@ "type": "uint256" } ], - "name": "delegate", + "name": "stake", "outputs": [ { "internalType": "bool", @@ -32,7 +32,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -51,7 +51,7 @@ "type": "uint256" } ], - "name": "redelegate", + "name": "transferStake", "outputs": [ { "internalType": "int64", @@ -66,7 +66,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -80,7 +80,7 @@ "type": "uint256" } ], - "name": "undelegate", + "name": "unstake", "outputs": [ { "internalType": "int64", diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index c49c5e6597..a701873f0a 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -31,7 +31,7 @@ var ( // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"delegate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"redelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"undelegate\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -180,65 +180,65 @@ func (_IStaking *IStakingTransactorRaw) Transact(opts *bind.TransactOpts, method return _IStaking.Contract.contract.Transact(opts, method, params...) } -// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// Stake is a paid mutator transaction binding the contract method 0x90b8436f. // -// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) -func (_IStaking *IStakingTransactor) Delegate(opts *bind.TransactOpts, delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.contract.Transact(opts, "delegate", delegator, validator, amount) +// Solidity: function stake(address staker, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingTransactor) Stake(opts *bind.TransactOpts, staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "stake", staker, validator, amount) } -// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// Stake is a paid mutator transaction binding the contract method 0x90b8436f. // -// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) -func (_IStaking *IStakingSession) Delegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Delegate(&_IStaking.TransactOpts, delegator, validator, amount) +// Solidity: function stake(address staker, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingSession) Stake(staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Stake(&_IStaking.TransactOpts, staker, validator, amount) } -// Delegate is a paid mutator transaction binding the contract method 0x53266bbb. +// Stake is a paid mutator transaction binding the contract method 0x90b8436f. // -// Solidity: function delegate(address delegator, string validator, uint256 amount) returns(bool success) -func (_IStaking *IStakingTransactorSession) Delegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Delegate(&_IStaking.TransactOpts, delegator, validator, amount) +// Solidity: function stake(address staker, string validator, uint256 amount) returns(bool success) +func (_IStaking *IStakingTransactorSession) Stake(staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Stake(&_IStaking.TransactOpts, staker, validator, amount) } -// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. // -// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactor) Redelegate(opts *bind.TransactOpts, delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.contract.Transact(opts, "redelegate", delegator, validatorSrc, validatorDst, amount) +// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) TransferStake(opts *bind.TransactOpts, staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "transferStake", staker, validatorSrc, validatorDst, amount) } -// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. // -// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingSession) Redelegate(delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrc, validatorDst, amount) +// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) TransferStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.TransferStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) } -// Redelegate is a paid mutator transaction binding the contract method 0x54b826f5. +// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. // -// Solidity: function redelegate(address delegator, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactorSession) Redelegate(delegator common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Redelegate(&_IStaking.TransactOpts, delegator, validatorSrc, validatorDst, amount) +// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) TransferStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.TransferStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) } -// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// Unstake is a paid mutator transaction binding the contract method 0x57c6ea3e. // -// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactor) Undelegate(opts *bind.TransactOpts, delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.contract.Transact(opts, "undelegate", delegator, validator, amount) +// Solidity: function unstake(address staker, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) Unstake(opts *bind.TransactOpts, staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "unstake", staker, validator, amount) } -// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// Unstake is a paid mutator transaction binding the contract method 0x57c6ea3e. // -// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingSession) Undelegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Undelegate(&_IStaking.TransactOpts, delegator, validator, amount) +// Solidity: function unstake(address staker, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) Unstake(staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Unstake(&_IStaking.TransactOpts, staker, validator, amount) } -// Undelegate is a paid mutator transaction binding the contract method 0x3edab33c. +// Unstake is a paid mutator transaction binding the contract method 0x57c6ea3e. // -// Solidity: function undelegate(address delegator, string validator, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactorSession) Undelegate(delegator common.Address, validator string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.Undelegate(&_IStaking.TransactOpts, delegator, validator, amount) +// Solidity: function unstake(address staker, string validator, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) Unstake(staker common.Address, validator string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.Unstake(&_IStaking.TransactOpts, staker, validator, amount) } diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 0a089dff93..245a1df650 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -4,7 +4,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -18,7 +18,7 @@ "type": "uint256" } ], - "name": "delegate", + "name": "stake", "outputs": [ { "internalType": "bool", @@ -33,7 +33,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -52,7 +52,7 @@ "type": "uint256" } ], - "name": "redelegate", + "name": "transferStake", "outputs": [ { "internalType": "int64", @@ -67,7 +67,7 @@ "inputs": [ { "internalType": "address", - "name": "delegator", + "name": "staker", "type": "address" }, { @@ -81,7 +81,7 @@ "type": "uint256" } ], - "name": "undelegate", + "name": "unstake", "outputs": [ { "internalType": "int64", diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index 5164db1321..b38d8f1a50 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -9,36 +9,36 @@ IStaking constant ISTAKING_CONTRACT = IStaking( ); interface IStaking { - /// @dev Delegate coins to validator - /// @param delegator Delegator address + /// @dev Stake coins to validator + /// @param staker Staker address /// @param validator Validator address /// @param amount Coins amount - /// @return success Delegation success - function delegate( - address delegator, + /// @return success Staking success + function stake( + address staker, string memory validator, uint256 amount ) external returns (bool success); - /// @dev Undelegate coins from validator - /// @param delegator Delegator address + /// @dev Unstake coins from validator + /// @param staker Staker address /// @param validator Validator address /// @param amount Coins amount - /// @return completionTime Time when undelegation is done - function undelegate( - address delegator, + /// @return completionTime Time when unstaking is done + function unstake( + address staker, string memory validator, uint256 amount ) external returns (int64 completionTime); - /// @dev Redelegate coins from validatorSrd to validatorDst - /// @param delegator Delegator address + /// @dev Transfer coins from validatorSrc to validatorDst + /// @param staker Staker address /// @param validatorSrc Validator from address /// @param validatorDst Validator to address /// @param amount Coins amount - /// @return completionTime Time when redelegation is done - function redelegate( - address delegator, + /// @return completionTime Time when staket transfer is done + function transferStake( + address staker, string memory validatorSrc, string memory validatorDst, uint256 amount diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 4b24b020bc..af6a0f85d5 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -19,9 +19,9 @@ import ( ) const ( - DelegateMethodName = "delegate" - UndelegateMethodName = "undelegate" - RedelegateMethodName = "redelegate" + StakeMethodName = "stake" + UnstakeMethodName = "unstake" + TransferStakeMethodName = "transferStake" ) var ( @@ -46,11 +46,11 @@ func initABI() { switch methodName { // TODO: just temporary values, double check these flat values // can we just use WriteCostFlat from gas config? - case DelegateMethodName: + case StakeMethodName: GasRequiredByMethod[methodID] = 10000 - case UndelegateMethodName: + case UnstakeMethodName: GasRequiredByMethod[methodID] = 10000 - case RedelegateMethodName: + case TransferStakeMethodName: GasRequiredByMethod[methodID] = 10000 default: GasRequiredByMethod[methodID] = 0 @@ -107,7 +107,7 @@ func (c *Contract) RequiredGas(input []byte) uint64 { return 0 } -func (c *Contract) Delegate( +func (c *Contract) Stake( ctx sdk.Context, origin common.Address, method *abi.Method, @@ -122,13 +122,13 @@ func (c *Contract) Delegate( msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - delegatorAddress, ok := args[0].(common.Address) + stakerAddress, ok := args[0].(common.Address) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if origin != delegatorAddress { - return nil, fmt.Errorf("origin is not delegator address") + if origin != stakerAddress { + return nil, fmt.Errorf("origin is not staker address") } validatorAddress, ok := args[1].(string) @@ -142,7 +142,7 @@ func (c *Contract) Delegate( } _, err := msgServer.Delegate(ctx, &stakingtypes.MsgDelegate{ - DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorAddress: validatorAddress, Amount: sdk.Coin{ Denom: c.stakingKeeper.BondDenom(ctx), @@ -156,7 +156,7 @@ func (c *Contract) Delegate( return method.Outputs.Pack(true) } -func (c *Contract) Undelegate( +func (c *Contract) Unstake( ctx sdk.Context, origin common.Address, method *abi.Method, @@ -171,13 +171,13 @@ func (c *Contract) Undelegate( msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - delegatorAddress, ok := args[0].(common.Address) + stakerAddress, ok := args[0].(common.Address) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if origin != delegatorAddress { - return nil, fmt.Errorf("origin is not delegator address") + if origin != stakerAddress { + return nil, fmt.Errorf("origin is not staker address") } validatorAddress, ok := args[1].(string) @@ -191,7 +191,7 @@ func (c *Contract) Undelegate( } res, err := msgServer.Undelegate(ctx, &stakingtypes.MsgUndelegate{ - DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorAddress: validatorAddress, Amount: sdk.Coin{ Denom: c.stakingKeeper.BondDenom(ctx), @@ -205,7 +205,7 @@ func (c *Contract) Undelegate( return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } -func (c *Contract) Redelegate( +func (c *Contract) TransferStake( ctx sdk.Context, origin common.Address, method *abi.Method, @@ -220,13 +220,13 @@ func (c *Contract) Redelegate( msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - delegatorAddress, ok := args[0].(common.Address) + stakerAddress, ok := args[0].(common.Address) if !ok { return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) } - if origin != delegatorAddress { - return nil, fmt.Errorf("origin is not delegator address") + if origin != stakerAddress { + return nil, fmt.Errorf("origin is not staker address") } validatorSrcAddress, ok := args[1].(string) @@ -245,7 +245,7 @@ func (c *Contract) Redelegate( } res, err := msgServer.BeginRedelegate(ctx, &stakingtypes.MsgBeginRedelegate{ - DelegatorAddress: sdk.AccAddress(delegatorAddress.Bytes()).String(), + DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorSrcAddress: validatorSrcAddress, ValidatorDstAddress: validatorDstAddress, Amount: sdk.Coin{ @@ -276,30 +276,30 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro stateDB := evm.StateDB.(ptypes.ExtStateDB) switch method.Name { - case DelegateMethodName: + case StakeMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Delegate(ctx, evm.Origin, method, args) + res, err = c.Stake(ctx, evm.Origin, method, args) return err }) if execErr != nil { return nil, err } return res, nil - case UndelegateMethodName: + case UnstakeMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Undelegate(ctx, evm.Origin, method, args) + res, err = c.Unstake(ctx, evm.Origin, method, args) return err }) if execErr != nil { return nil, err } return res, nil - case RedelegateMethodName: + case TransferStakeMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.Redelegate(ctx, evm.Origin, method, args) + res, err = c.TransferStake(ctx, evm.Origin, method, args) return err }) if execErr != nil { diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 25cf60f94d..ae0fe1ba78 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -44,12 +44,12 @@ func Test_IStakingContract(t *testing.T) { contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) abi := contract.Abi() - require.NotNil(t, abi.Methods[DelegateMethodName], "delegate method should be present in the ABI") - require.NotNil(t, abi.Methods[UndelegateMethodName], "undelegate method should be present in the ABI") + require.NotNil(t, abi.Methods[StakeMethodName], "stake method should be present in the ABI") + require.NotNil(t, abi.Methods[UnstakeMethodName], "unstake method should be present in the ABI") require.NotNil( t, - abi.Methods[RedelegateMethodName], - "redelegate method should be present in the ABI", + abi.Methods[TransferStakeMethodName], + "transferStake method should be present in the ABI", ) }) @@ -58,45 +58,45 @@ func Test_IStakingContract(t *testing.T) { abi := contract.Abi() var method [4]byte - t.Run("delegate", func(t *testing.T) { - delegate := contract.RequiredGas(abi.Methods[DelegateMethodName].ID) - copy(method[:], abi.Methods[DelegateMethodName].ID[:4]) + t.Run("stake", func(t *testing.T) { + stake := contract.RequiredGas(abi.Methods[StakeMethodName].ID) + copy(method[:], abi.Methods[StakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( t, GasRequiredByMethod[method]+baseCost, - delegate, - "delegate method should require %d gas, got %d", + stake, + "stake method should require %d gas, got %d", GasRequiredByMethod[method]+baseCost, - delegate, + stake, ) }) - t.Run("undelegate", func(t *testing.T) { - undelegate := contract.RequiredGas(abi.Methods[UndelegateMethodName].ID) - copy(method[:], abi.Methods[UndelegateMethodName].ID[:4]) + t.Run("unstake", func(t *testing.T) { + unstake := contract.RequiredGas(abi.Methods[UnstakeMethodName].ID) + copy(method[:], abi.Methods[UnstakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( t, GasRequiredByMethod[method]+baseCost, - undelegate, - "undelegate method should require %d gas, got %d", + unstake, + "unstake method should require %d gas, got %d", GasRequiredByMethod[method]+baseCost, - undelegate, + unstake, ) }) - t.Run("redelegate", func(t *testing.T) { - redelegate := contract.RequiredGas(abi.Methods[RedelegateMethodName].ID) - copy(method[:], abi.Methods[RedelegateMethodName].ID[:4]) + t.Run("transferStake", func(t *testing.T) { + transferStake := contract.RequiredGas(abi.Methods[TransferStakeMethodName].ID) + copy(method[:], abi.Methods[TransferStakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( t, GasRequiredByMethod[method]+baseCost, - redelegate, - "redelegate method should require %d gas, got %d", + transferStake, + "transferStake method should require %d gas, got %d", GasRequiredByMethod[method]+baseCost, - redelegate, + transferStake, ) }) @@ -144,7 +144,7 @@ func Test_InvalidABI(t *testing.T) { initABI() } -func Test_Delegate(t *testing.T) { +func Test_Stake(t *testing.T) { var encoding ethermint.EncodingConfig appCodec := encoding.Codec @@ -167,85 +167,85 @@ func Test_Delegate(t *testing.T) { abi := contract.Abi() require.NotNil(t, abi, "contract ABI should not be nil") - methodID := abi.Methods[DelegateMethodName] + methodID := abi.Methods[StakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) - t.Run("should delegate", func(t *testing.T) { + t.Run("should stake", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.NoError(t, err) }) - t.Run("should fail if origin is not delegator", func(t *testing.T) { + t.Run("should fail if origin is not staker", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() + staker := sample.Bech32AccAddress() coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) args := []interface{}{originEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) - require.ErrorContains(t, err, "origin is not delegator address") + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + require.ErrorContains(t, err, "origin is not staker address") }) - t.Run("should fail if delegation fails", func(t *testing.T) { + t.Run("should fail if staking fails", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - // delegator without funds - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + // staker without funds + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, int64(42)} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, int64(42)} - _, err := contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err := contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -254,39 +254,39 @@ func Test_Delegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress} + args := []interface{}{stakerEthAddr, validator.OperatorAddress} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) - t.Run("should fail if delegator is not eth addr", func(t *testing.T) { + t.Run("should fail if staker is not eth addr", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() + staker := sample.Bech32AccAddress() coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegator, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -295,19 +295,19 @@ func Test_Delegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -316,24 +316,24 @@ func Test_Delegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} - _, err = contract.Delegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) } -func Test_Undelegate(t *testing.T) { +func Test_Unstake(t *testing.T) { var encoding ethermint.EncodingConfig appCodec := encoding.Codec @@ -356,100 +356,100 @@ func Test_Undelegate(t *testing.T) { abi := contract.Abi() require.NotNil(t, abi, "contract ABI should not be nil") - methodID := abi.Methods[UndelegateMethodName] + methodID := abi.Methods[UnstakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) - t.Run("should undelegate", func(t *testing.T) { + t.Run("should unstake", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate first - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, args) + // stake first + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, args) require.NoError(t, err) - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.NoError(t, err) }) - t.Run("should fail if origin is not delegator", func(t *testing.T) { + t.Run("should fail if origin is not staker", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate first - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, args) + // stake first + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, args) require.NoError(t, err) originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - _, err = contract.Undelegate(ctx, originEthAddr, &methodID, args) - require.ErrorContains(t, err, "origin is not delegator address") + _, err = contract.Unstake(ctx, originEthAddr, &methodID, args) + require.ErrorContains(t, err, "origin is not staker address") }) - t.Run("should fail if no previous delegation", func(t *testing.T) { + t.Run("should fail if no previous staking", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -458,39 +458,39 @@ func Test_Undelegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress} + args := []interface{}{stakerEthAddr, validator.OperatorAddress} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) - t.Run("should fail if delegator is not eth addr", func(t *testing.T) { + t.Run("should fail if staker is not eth addr", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() + staker := sample.Bech32AccAddress() coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegator, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -499,19 +499,19 @@ func Test_Undelegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) @@ -520,24 +520,24 @@ func Test_Undelegate(t *testing.T) { validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{delegatorEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} - _, err = contract.Undelegate(ctx, delegatorAddr, &methodID, args) + _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) }) } -func Test_Redelegate(t *testing.T) { +func Test_TransferStake(t *testing.T) { var encoding ethermint.EncodingConfig appCodec := encoding.Codec @@ -560,7 +560,7 @@ func Test_Redelegate(t *testing.T) { abi := contract.Abi() require.NotNil(t, abi, "contract ABI should not be nil") - methodID := abi.Methods[RedelegateMethodName] + methodID := abi.Methods[TransferStakeMethodName] t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { r := rand.New(rand.NewSource(42)) @@ -568,87 +568,87 @@ func Test_Redelegate(t *testing.T) { sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) validatorDest := sample.Validator(t, r) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) }) - t.Run("should redelegate", func(t *testing.T) { + t.Run("should transfer stake", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // redelegate to validator dest - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + // transfer stake to validator dest + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.NoError(t, err) }) - t.Run("should fail if delegator is invalid arg", func(t *testing.T) { + t.Run("should fail if staker is invalid arg", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) }) @@ -659,26 +659,26 @@ func Test_Redelegate(t *testing.T) { validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, 42, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{stakerEthAddr, 42, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) }) @@ -689,26 +689,26 @@ func Test_Redelegate(t *testing.T) { validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.NoError(t, err) }) @@ -719,26 +719,26 @@ func Test_Redelegate(t *testing.T) { validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) }) @@ -749,57 +749,57 @@ func Test_Redelegate(t *testing.T) { validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} - _, err = contract.Redelegate(ctx, delegatorAddr, &methodID, argsRedelegate) + _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) }) - t.Run("should fail if origin is not delegator", func(t *testing.T) { + t.Run("should fail if origin is not staker", func(t *testing.T) { r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) validatorDest := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorDest) - delegator := sample.Bech32AccAddress() - delegatorEthAddr := common.BytesToAddress(delegator.Bytes()) + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) coins := sample.Coins() err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) require.NoError(t, err) - err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, delegator, coins) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) require.NoError(t, err) - delegatorAddr := common.BytesToAddress(delegator.Bytes()) + stakerAddr := common.BytesToAddress(staker.Bytes()) - argsDelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} - // delegate to validator src - delegateMethodID := abi.Methods[DelegateMethodName] - _, err = contract.Delegate(ctx, delegatorAddr, &delegateMethodID, argsDelegate) + // stake to validator src + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsRedelegate := []interface{}{delegatorEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - _, err = contract.Redelegate(ctx, originEthAddr, &methodID, argsRedelegate) - require.ErrorContains(t, err, "origin is not delegator") + _, err = contract.TransferStake(ctx, originEthAddr, &methodID, argsTransferStake) + require.ErrorContains(t, err, "origin is not staker") }) } From d1df9ec8a308811583efa812b967de0a7f20d9a6 Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 15:55:21 +0200 Subject: [PATCH 12/27] generate --- app/app.go | 7 ++- precompiles/precompiles.go | 2 +- precompiles/staking/staking.go | 7 +-- precompiles/staking/staking_test.go | 97 ++++++++++++++++++++++++----- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/app/app.go b/app/app.go index 53dadab3ba..3ee2858e3a 100644 --- a/app/app.go +++ b/app/app.go @@ -567,7 +567,12 @@ func New( &app.FeeMarketKeeper, tracer, evmSs, - precompiles.StatefulContracts(&app.FungibleKeeper, app.StakingKeeper, appCodec, storetypes.TransientGasConfig()), + precompiles.StatefulContracts( + &app.FungibleKeeper, + app.StakingKeeper, + appCodec, + storetypes.TransientGasConfig(), + ), app.ConsensusParamsKeeper, aggregateAllKeys(keys, tKeys, memKeys), ) diff --git a/precompiles/precompiles.go b/precompiles/precompiles.go index 440c5cd7a8..d56358a98d 100644 --- a/precompiles/precompiles.go +++ b/precompiles/precompiles.go @@ -4,12 +4,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdktypes "github.com/cosmos/cosmos-sdk/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" ethparams "github.com/ethereum/go-ethereum/params" evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" "github.com/zeta-chain/zetacore/precompiles/prototype" "github.com/zeta-chain/zetacore/precompiles/staking" fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index af6a0f85d5..28dfe32dd8 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -4,17 +4,16 @@ import ( "fmt" "math/big" + "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "cosmossdk.io/math" ptypes "github.com/zeta-chain/zetacore/precompiles/types" ) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index ae0fe1ba78..d36ba60a6d 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -578,14 +578,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) @@ -608,14 +617,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // transfer stake to validator dest _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) @@ -639,14 +657,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + 42, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) @@ -669,14 +696,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, 42, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + 42, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) @@ -699,14 +735,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.NoError(t, err) @@ -729,14 +774,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Uint64(), + } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) require.Error(t, err) @@ -759,7 +813,11 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] @@ -789,14 +847,23 @@ func Test_TransferStake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - argsStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } // stake to validator src stakeMethodID := abi.Methods[StakeMethodName] _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + argsTransferStake := []interface{}{ + stakerEthAddr, + validatorSrc.OperatorAddress, + validatorDest.OperatorAddress, + coins.AmountOf(config.BaseDenom).Int64(), + } originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) _, err = contract.TransferStake(ctx, originEthAddr, &methodID, argsTransferStake) From 0003316e25c5a23b9196ed2ec6c8fbe26b3c1e82 Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 15:59:29 +0200 Subject: [PATCH 13/27] pr comments --- precompiles/staking/staking.go | 49 +++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 28dfe32dd8..a965be70e0 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -119,11 +119,11 @@ func (c *Contract) Stake( }) } - msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - stakerAddress, ok := args[0].(common.Address) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[0], + } } if origin != stakerAddress { @@ -132,14 +132,19 @@ func (c *Contract) Stake( validatorAddress, ok := args[1].(string) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[1], + } } amount, ok := args[2].(int64) if !ok { - return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[2], + } } + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) _, err := msgServer.Delegate(ctx, &stakingtypes.MsgDelegate{ DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorAddress: validatorAddress, @@ -168,11 +173,11 @@ func (c *Contract) Unstake( }) } - msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - stakerAddress, ok := args[0].(common.Address) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[0], + } } if origin != stakerAddress { @@ -181,14 +186,19 @@ func (c *Contract) Unstake( validatorAddress, ok := args[1].(string) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[1], + } } amount, ok := args[2].(int64) if !ok { - return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[2], + } } + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) res, err := msgServer.Undelegate(ctx, &stakingtypes.MsgUndelegate{ DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorAddress: validatorAddress, @@ -217,11 +227,11 @@ func (c *Contract) TransferStake( }) } - msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) - stakerAddress, ok := args[0].(common.Address) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[0]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[0], + } } if origin != stakerAddress { @@ -230,19 +240,26 @@ func (c *Contract) TransferStake( validatorSrcAddress, ok := args[1].(string) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[1], + } } validatorDstAddress, ok := args[2].(string) if !ok { - return nil, fmt.Errorf("invalid argument, wanted a string, got: %T", args[1]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[2], + } } amount, ok := args[3].(int64) if !ok { - return nil, fmt.Errorf("invalid argument, wanted an int64, got %T", args[2]) + return nil, ptypes.ErrInvalidArgument{ + Got: args[3], + } } + msgServer := stakingkeeper.NewMsgServerImpl(&c.stakingKeeper) res, err := msgServer.BeginRedelegate(ctx, &stakingtypes.MsgBeginRedelegate{ DelegatorAddress: sdk.AccAddress(stakerAddress.Bytes()).String(), ValidatorSrcAddress: validatorSrcAddress, From 378ea6dd6780670bdb51381607fc444743345adb Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 16:01:13 +0200 Subject: [PATCH 14/27] codecov --- codecov.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/codecov.yml b/codecov.yml index 474cfe97ee..3fc15ff248 100644 --- a/codecov.yml +++ b/codecov.yml @@ -81,3 +81,4 @@ ignore: - "precompiles/**/*.json" - "precompiles/**/*.sol" - "precompiles/prototype/IPrototype.go" + - "precompiles/prototype/IStaking.go" From 781cdd484dceac0b7896c4143e671a3f65bc5249 Mon Sep 17 00:00:00 2001 From: skosito Date: Thu, 29 Aug 2024 18:22:53 +0200 Subject: [PATCH 15/27] renaming --- e2e/e2etests/e2etests.go | 2 +- e2e/e2etests/test_precompiles_prototype.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 5b810276db..f9bd81ef46 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -817,6 +817,6 @@ var AllE2ETests = []runner.E2ETest{ TestZetaPrecompilesPrototypeName, "test stateful precompiled contracts prototype", []runner.ArgDefinition{}, - TestPrecompilesRegular, + TestPrecompilesPrototype, ), } diff --git a/e2e/e2etests/test_precompiles_prototype.go b/e2e/e2etests/test_precompiles_prototype.go index a78a20385f..ccf42dc106 100644 --- a/e2e/e2etests/test_precompiles_prototype.go +++ b/e2e/e2etests/test_precompiles_prototype.go @@ -8,7 +8,7 @@ import ( "github.com/zeta-chain/zetacore/precompiles/prototype" ) -func TestPrecompilesRegular(r *runner.E2ERunner, args []string) { +func TestPrecompilesPrototype(r *runner.E2ERunner, args []string) { require.Len(r, args, 0, "No arguments expected") iPrototype, err := prototype.NewIPrototype(prototype.ContractAddress, r.ZEVMClient) From 7f1999def023cacdfb16a486de0b21754da27832 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 01:37:14 +0200 Subject: [PATCH 16/27] e2e test and fixes --- cmd/zetae2e/local/local.go | 1 + .../localnet/orchestrator/start-zetae2e.sh | 5 + contrib/localnet/scripts/start-zetacored.sh | 3 + e2e/e2etests/e2etests.go | 7 ++ e2e/e2etests/test_precompiles_staking.go | 68 ++++++++++++ precompiles/precompiles.go | 4 +- precompiles/staking/IStaking.abi | 44 ++++++++ precompiles/staking/IStaking.go | 69 +++++++++++- precompiles/staking/IStaking.json | 44 ++++++++ precompiles/staking/IStaking.sol | 8 ++ precompiles/staking/staking.go | 102 ++++++++++++++++-- 11 files changed, 346 insertions(+), 9 deletions(-) create mode 100644 e2e/e2etests/test_precompiles_staking.go diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index dd00666e25..a3097c8b44 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -300,6 +300,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { if !skipPrecompiles { precompiledContractTests = []string{ e2etests.TestZetaPrecompilesPrototypeName, + e2etests.TestZetaPrecompilesStakingName, } } diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 98ba9060e7..3bb8bc4c6d 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -91,6 +91,11 @@ address=$(yq -r '.additional_accounts.user_migration.evm_address' config.yml) echo "funding migration tester address ${address} with 10000 Ether" geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null +# unlock precompile tests accounts +address=$(yq -r '.additional_accounts.user_precompile.evm_address' config.yml) +echo "funding migration tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + # unlock local solana relayer accounts if host solana > /dev/null; then solana_url=$(yq -r '.rpcs.solana' config.yml) diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 14980d195f..081a94246c 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -254,6 +254,9 @@ then # migration tester address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# precompiles tester + address=$(yq -r '.additional_accounts.user_precompile.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta # 3. Copy the genesis.json to all the nodes .And use it to create a gentx for every node zetacored gentx operator 1000000000000000000000azeta --chain-id=$CHAINID --keyring-backend=$KEYRING --gas-prices 20000000000azeta diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index f9bd81ef46..76038af2df 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -148,6 +148,7 @@ const ( Stateful precompiled contracts tests */ TestZetaPrecompilesPrototypeName = "precompile_contracts_prototype" + TestZetaPrecompilesStakingName = "precompile_contracts_staking" ) // AllE2ETests is an ordered list of all e2e tests @@ -819,4 +820,10 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestPrecompilesPrototype, ), + runner.NewE2ETest( + TestZetaPrecompilesStakingName, + "test stateful precompiled contracts staking", + []runner.ArgDefinition{}, + TestPrecompilesStaking, + ), } diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go new file mode 100644 index 0000000000..c2aebed83f --- /dev/null +++ b/e2e/e2etests/test_precompiles_staking.go @@ -0,0 +1,68 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/precompiles/staking" +) + +func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { + require.Len(r, args, 0, "No arguments expected") + + stakingContract, err := staking.NewIStaking(staking.ContractAddress, r.ZEVMClient) + require.NoError(r, err, "Failed to create staking contract caller") + + r.ZEVMAuth.GasLimit = 10000000 + + validators, err := stakingContract.GetAllValidators(nil) + require.NoError(r, err) + require.GreaterOrEqual(r, len(validators), 2) + + // stakes are 0 for both validators at the start + stakesBeforeVal1, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + require.NoError(r, err) + require.Equal(r, int64(0), stakesBeforeVal1.Int64()) + + stakesBeforeVal2, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + require.NoError(r, err) + require.Equal(r, int64(0), stakesBeforeVal2.Int64()) + + // stake 3 to validator1 + tx, err := stakingContract.Stake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(3)) + require.NoError(r, err) + utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + + // check stakes are set to 3 + stakesAfterVal1, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + require.NoError(r, err) + require.Equal(r, big.NewInt(3e18).String(), stakesAfterVal1.String()) + + // unstake 1 from validator1 + tx, err = stakingContract.Unstake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(1)) + require.NoError(r, err) + utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + + // check stakes are set to 2 + stakesAfterVal1, err = stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + require.NoError(r, err) + require.Equal(r, big.NewInt(2e18).String(), stakesAfterVal1.String()) + + // transfer 1 stake from validator1 to validator2 + tx, err = stakingContract.TransferStake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, validators[1].OperatorAddress, big.NewInt(1)) + require.NoError(r, err) + + utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + + // check stakes for both validator1 and validator2 are 1 + stakesAfterVal1, err = stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + require.NoError(r, err) + require.Equal(r, big.NewInt(1e18).String(), stakesAfterVal1.String()) + + stakesAfterVal2, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + require.NoError(r, err) + require.Equal(r, big.NewInt(1e18).String(), stakesAfterVal2.String()) +} diff --git a/precompiles/precompiles.go b/precompiles/precompiles.go index d56358a98d..e3e2223692 100644 --- a/precompiles/precompiles.go +++ b/precompiles/precompiles.go @@ -44,12 +44,12 @@ func StatefulContracts( } // Define the staking contract function. - if EnabledStatefulContracts[prototype.ContractAddress] { + if EnabledStatefulContracts[staking.ContractAddress] { stakingContract := func(_ sdktypes.Context, _ ethparams.Rules) vm.PrecompiledContract { return staking.NewIStakingContract(stakingKeeper, cdc, gasConfig) } - // Append the prototype contract to the precompiledContracts slice. + // Append the staking contract to the precompiledContracts slice. precompiledContracts = append(precompiledContracts, stakingContract) } diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index 0efdcf1843..1c6b207481 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -1,4 +1,48 @@ [ + { + "inputs": [], + "name": "getAllValidators", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "operatorAddress", + "type": "string" + } + ], + "internalType": "struct Validator[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + } + ], + "name": "getStakes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index a701873f0a..0c25cd246a 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -29,9 +29,14 @@ var ( _ = abi.ConvertType ) +// Validator is an auto generated low-level Go binding around an user-defined struct. +type Validator struct { + OperatorAddress string +} + // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"}],\"internalType\":\"structValidator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -180,6 +185,68 @@ func (_IStaking *IStakingTransactorRaw) Transact(opts *bind.TransactOpts, method return _IStaking.Contract.contract.Transact(opts, method, params...) } +// GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. +// +// Solidity: function getAllValidators() view returns((string)[]) +func (_IStaking *IStakingCaller) GetAllValidators(opts *bind.CallOpts) ([]Validator, error) { + var out []interface{} + err := _IStaking.contract.Call(opts, &out, "getAllValidators") + + if err != nil { + return *new([]Validator), err + } + + out0 := *abi.ConvertType(out[0], new([]Validator)).(*[]Validator) + + return out0, err + +} + +// GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. +// +// Solidity: function getAllValidators() view returns((string)[]) +func (_IStaking *IStakingSession) GetAllValidators() ([]Validator, error) { + return _IStaking.Contract.GetAllValidators(&_IStaking.CallOpts) +} + +// GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. +// +// Solidity: function getAllValidators() view returns((string)[]) +func (_IStaking *IStakingCallerSession) GetAllValidators() ([]Validator, error) { + return _IStaking.Contract.GetAllValidators(&_IStaking.CallOpts) +} + +// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// +// Solidity: function getStakes(address staker, string validator) view returns(uint256) +func (_IStaking *IStakingCaller) GetStakes(opts *bind.CallOpts, staker common.Address, validator string) (*big.Int, error) { + var out []interface{} + err := _IStaking.contract.Call(opts, &out, "getStakes", staker, validator) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// +// Solidity: function getStakes(address staker, string validator) view returns(uint256) +func (_IStaking *IStakingSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { + return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) +} + +// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// +// Solidity: function getStakes(address staker, string validator) view returns(uint256) +func (_IStaking *IStakingCallerSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { + return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) +} + // Stake is a paid mutator transaction binding the contract method 0x90b8436f. // // Solidity: function stake(address staker, string validator, uint256 amount) returns(bool success) diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 245a1df650..94a7c8ac16 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -1,5 +1,49 @@ { "abi": [ + { + "inputs": [], + "name": "getAllValidators", + "outputs": [ + { + "components": [ + { + "internalType": "string", + "name": "operatorAddress", + "type": "string" + } + ], + "internalType": "struct Validator[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "string", + "name": "validator", + "type": "string" + } + ], + "name": "getStakes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index b38d8f1a50..7f426bd869 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -8,6 +8,10 @@ IStaking constant ISTAKING_CONTRACT = IStaking( ISTAKING_PRECOMPILE_ADDRESS ); +struct Validator { + string operatorAddress; +} + interface IStaking { /// @dev Stake coins to validator /// @param staker Staker address @@ -43,4 +47,8 @@ interface IStaking { string memory validatorDst, uint256 amount ) external returns (int64 completionTime); + + function getAllValidators() external view returns (Validator[] calldata); + + function getStakes(address staker, string memory validator) external view returns (uint256); } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index a965be70e0..545fc89196 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -18,9 +18,14 @@ import ( ) const ( + // write StakeMethodName = "stake" UnstakeMethodName = "unstake" TransferStakeMethodName = "transferStake" + + // read + GetAllValidatorsMethodName = "getAllValidators" + GetStakesMethodName = "getStakes" ) var ( @@ -51,6 +56,10 @@ func initABI() { GasRequiredByMethod[methodID] = 10000 case TransferStakeMethodName: GasRequiredByMethod[methodID] = 10000 + case GetAllValidatorsMethodName: + GasRequiredByMethod[methodID] = 0 + case GetStakesMethodName: + GasRequiredByMethod[methodID] = 0 default: GasRequiredByMethod[methodID] = 0 } @@ -92,6 +101,7 @@ func (c *Contract) Abi() abi.ABI { // The gas has to be calculated deterministically based on the input. func (c *Contract) RequiredGas(input []byte) uint64 { // base cost to prevent large input size + // TODO: 0 for read methods baseCost := uint64(len(input)) * c.kvGasConfig.WriteCostPerByte // get methodID (first 4 bytes) @@ -106,6 +116,66 @@ func (c *Contract) RequiredGas(input []byte) uint64 { return 0 } +func (c *Contract) GetAllValidators( + ctx sdk.Context, + method *abi.Method, +) ([]byte, error) { + validators := c.stakingKeeper.GetAllValidators(ctx) + + validatorsRes := []Validator{} + for _, v := range validators { + validatorsRes = append(validatorsRes, Validator{ + OperatorAddress: v.OperatorAddress, + }) + } + + return method.Outputs.Pack(validatorsRes) +} + +func (c *Contract) GetStake( + ctx sdk.Context, + method *abi.Method, + args []interface{}, +) ([]byte, error) { + if len(args) != 2 { + return nil, &(ptypes.ErrInvalidNumberOfArgs{ + Got: len(args), + Expect: 2, + }) + } + stakerAddress, ok := args[0].(common.Address) + if !ok { + return nil, ptypes.ErrInvalidArgument{ + Got: args[0], + } + } + + validatorAddress, ok := args[1].(string) + if !ok { + return nil, ptypes.ErrInvalidArgument{ + Got: args[1], + } + } + + staker, err := sdk.AccAddressFromBech32(sdk.AccAddress(stakerAddress.Bytes()).String()) + if err != nil { + return nil, err + } + + validator, err := sdk.ValAddressFromBech32(validatorAddress) + if err != nil { + return nil, err + } + + delegation := c.stakingKeeper.Delegation(ctx, staker, validator) + shares := big.NewInt(0) + if delegation != nil { + shares = delegation.GetShares().BigInt() + } + + return method.Outputs.Pack(shares) +} + func (c *Contract) Stake( ctx sdk.Context, origin common.Address, @@ -137,7 +207,7 @@ func (c *Contract) Stake( } } - amount, ok := args[2].(int64) + amount, ok := args[2].(*big.Int) if !ok { return nil, ptypes.ErrInvalidArgument{ Got: args[2], @@ -150,7 +220,7 @@ func (c *Contract) Stake( ValidatorAddress: validatorAddress, Amount: sdk.Coin{ Denom: c.stakingKeeper.BondDenom(ctx), - Amount: math.NewIntFromBigInt(big.NewInt(amount)), + Amount: math.NewIntFromBigInt(amount), }, }) if err != nil { @@ -191,7 +261,7 @@ func (c *Contract) Unstake( } } - amount, ok := args[2].(int64) + amount, ok := args[2].(*big.Int) if !ok { return nil, ptypes.ErrInvalidArgument{ Got: args[2], @@ -204,7 +274,7 @@ func (c *Contract) Unstake( ValidatorAddress: validatorAddress, Amount: sdk.Coin{ Denom: c.stakingKeeper.BondDenom(ctx), - Amount: math.NewIntFromBigInt(big.NewInt(amount)), + Amount: math.NewIntFromBigInt(amount), }, }) if err != nil { @@ -252,7 +322,7 @@ func (c *Contract) TransferStake( } } - amount, ok := args[3].(int64) + amount, ok := args[3].(*big.Int) if !ok { return nil, ptypes.ErrInvalidArgument{ Got: args[3], @@ -266,7 +336,7 @@ func (c *Contract) TransferStake( ValidatorDstAddress: validatorDstAddress, Amount: sdk.Coin{ Denom: c.stakingKeeper.BondDenom(ctx), - Amount: math.NewIntFromBigInt(big.NewInt(amount)), + Amount: math.NewIntFromBigInt(amount), }, }) if err != nil { @@ -292,6 +362,26 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro stateDB := evm.StateDB.(ptypes.ExtStateDB) switch method.Name { + case GetAllValidatorsMethodName: + var res []byte + execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { + res, err = c.GetAllValidators(ctx, method) + return err + }) + if execErr != nil { + return nil, err + } + return res, nil + case GetStakesMethodName: + var res []byte + execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { + res, err = c.GetStake(ctx, method, args) + return err + }) + if execErr != nil { + return nil, err + } + return res, nil case StakeMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { From 9eb8775bafaff2d370ff633dfa3967d4c250fcd3 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 01:40:34 +0200 Subject: [PATCH 17/27] generate --- e2e/e2etests/test_precompiles_staking.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go index c2aebed83f..36e2da5bbb 100644 --- a/e2e/e2etests/test_precompiles_staking.go +++ b/e2e/e2etests/test_precompiles_staking.go @@ -52,7 +52,13 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { require.Equal(r, big.NewInt(2e18).String(), stakesAfterVal1.String()) // transfer 1 stake from validator1 to validator2 - tx, err = stakingContract.TransferStake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, validators[1].OperatorAddress, big.NewInt(1)) + tx, err = stakingContract.TransferStake( + r.ZEVMAuth, + r.ZEVMAuth.From, + validators[0].OperatorAddress, + validators[1].OperatorAddress, + big.NewInt(1), + ) require.NoError(r, err) utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) From a063f848317c8bb14f574c85c4af5745f7b61c39 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 13:43:03 +0200 Subject: [PATCH 18/27] fix tests --- precompiles/staking/staking_test.go | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index d36ba60a6d..a0be7009d7 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -183,7 +183,7 @@ func Test_Stake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -204,7 +204,7 @@ func Test_Stake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.NoError(t, err) @@ -226,7 +226,7 @@ func Test_Stake(t *testing.T) { originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - args := []interface{}{originEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{originEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.ErrorContains(t, err, "origin is not staker address") @@ -284,7 +284,7 @@ func Test_Stake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -305,7 +305,7 @@ func Test_Stake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Stake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -372,7 +372,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -393,7 +393,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} // stake first stakeMethodID := abi.Methods[StakeMethodName] @@ -419,7 +419,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} // stake first stakeMethodID := abi.Methods[StakeMethodName] @@ -447,7 +447,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -488,7 +488,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -509,7 +509,7 @@ func Test_Unstake(t *testing.T) { stakerAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).Int64()} + args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) require.Error(t, err) @@ -581,7 +581,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -593,7 +593,7 @@ func Test_TransferStake(t *testing.T) { stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) @@ -620,7 +620,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -632,7 +632,7 @@ func Test_TransferStake(t *testing.T) { stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // transfer stake to validator dest @@ -660,7 +660,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -672,7 +672,7 @@ func Test_TransferStake(t *testing.T) { 42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) @@ -699,7 +699,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -711,7 +711,7 @@ func Test_TransferStake(t *testing.T) { stakerEthAddr, 42, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) @@ -738,7 +738,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -750,7 +750,7 @@ func Test_TransferStake(t *testing.T) { stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) @@ -777,7 +777,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -816,7 +816,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -850,7 +850,7 @@ func Test_TransferStake(t *testing.T) { argsStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } // stake to validator src @@ -862,7 +862,7 @@ func Test_TransferStake(t *testing.T) { stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, - coins.AmountOf(config.BaseDenom).Int64(), + coins.AmountOf(config.BaseDenom).BigInt(), } originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) From c42569689b8a9b4afafe2358ac40ccdff15a31a3 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 14:48:59 +0200 Subject: [PATCH 19/27] fix view methods required gas --- precompiles/staking/IStaking.abi | 19 ++++++++++++++++-- precompiles/staking/IStaking.go | 17 +++++++++------- precompiles/staking/IStaking.json | 19 ++++++++++++++++-- precompiles/staking/IStaking.sol | 26 +++++++++++++++++++----- precompiles/staking/staking.go | 18 ++++++++++++----- precompiles/staking/staking_test.go | 31 +++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 21 deletions(-) diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index 1c6b207481..b2daa4d5bd 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -9,10 +9,25 @@ "internalType": "string", "name": "operatorAddress", "type": "string" + }, + { + "internalType": "string", + "name": "consensusPubKey", + "type": "string" + }, + { + "internalType": "bool", + "name": "jailed", + "type": "bool" + }, + { + "internalType": "enum BondStatus", + "name": "bondStatus", + "type": "uint8" } ], "internalType": "struct Validator[]", - "name": "", + "name": "validators", "type": "tuple[]" } ], @@ -36,7 +51,7 @@ "outputs": [ { "internalType": "uint256", - "name": "", + "name": "stakes", "type": "uint256" } ], diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index 0c25cd246a..006430faed 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -32,11 +32,14 @@ var ( // Validator is an auto generated low-level Go binding around an user-defined struct. type Validator struct { OperatorAddress string + ConsensusPubKey string + Jailed bool + BondStatus uint8 } // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"}],\"internalType\":\"structValidator[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"consensusPubKey\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"jailed\",\"type\":\"bool\"},{\"internalType\":\"enumBondStatus\",\"name\":\"bondStatus\",\"type\":\"uint8\"}],\"internalType\":\"structValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"stakes\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -187,7 +190,7 @@ func (_IStaking *IStakingTransactorRaw) Transact(opts *bind.TransactOpts, method // GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. // -// Solidity: function getAllValidators() view returns((string)[]) +// Solidity: function getAllValidators() view returns((string,string,bool,uint8)[] validators) func (_IStaking *IStakingCaller) GetAllValidators(opts *bind.CallOpts) ([]Validator, error) { var out []interface{} err := _IStaking.contract.Call(opts, &out, "getAllValidators") @@ -204,21 +207,21 @@ func (_IStaking *IStakingCaller) GetAllValidators(opts *bind.CallOpts) ([]Valida // GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. // -// Solidity: function getAllValidators() view returns((string)[]) +// Solidity: function getAllValidators() view returns((string,string,bool,uint8)[] validators) func (_IStaking *IStakingSession) GetAllValidators() ([]Validator, error) { return _IStaking.Contract.GetAllValidators(&_IStaking.CallOpts) } // GetAllValidators is a free data retrieval call binding the contract method 0xf3513a37. // -// Solidity: function getAllValidators() view returns((string)[]) +// Solidity: function getAllValidators() view returns((string,string,bool,uint8)[] validators) func (_IStaking *IStakingCallerSession) GetAllValidators() ([]Validator, error) { return _IStaking.Contract.GetAllValidators(&_IStaking.CallOpts) } // GetStakes is a free data retrieval call binding the contract method 0xf668d543. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256) +// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) func (_IStaking *IStakingCaller) GetStakes(opts *bind.CallOpts, staker common.Address, validator string) (*big.Int, error) { var out []interface{} err := _IStaking.contract.Call(opts, &out, "getStakes", staker, validator) @@ -235,14 +238,14 @@ func (_IStaking *IStakingCaller) GetStakes(opts *bind.CallOpts, staker common.Ad // GetStakes is a free data retrieval call binding the contract method 0xf668d543. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256) +// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) func (_IStaking *IStakingSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) } // GetStakes is a free data retrieval call binding the contract method 0xf668d543. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256) +// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) func (_IStaking *IStakingCallerSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) } diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 94a7c8ac16..3193134223 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -10,10 +10,25 @@ "internalType": "string", "name": "operatorAddress", "type": "string" + }, + { + "internalType": "string", + "name": "consensusPubKey", + "type": "string" + }, + { + "internalType": "bool", + "name": "jailed", + "type": "bool" + }, + { + "internalType": "enum BondStatus", + "name": "bondStatus", + "type": "uint8" } ], "internalType": "struct Validator[]", - "name": "", + "name": "validators", "type": "tuple[]" } ], @@ -37,7 +52,7 @@ "outputs": [ { "internalType": "uint256", - "name": "", + "name": "stakes", "type": "uint256" } ], diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index 7f426bd869..34e8ebd0f2 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -8,12 +8,24 @@ IStaking constant ISTAKING_CONTRACT = IStaking( ISTAKING_PRECOMPILE_ADDRESS ); +/// @notice Bond status for validator +enum BondStatus { + Unspecified, + Unbonded, + Unbonding, + Bonded +} + +/// @notice Validator info struct Validator { string operatorAddress; + string consensusPubKey; + bool jailed; + BondStatus bondStatus; } interface IStaking { - /// @dev Stake coins to validator + /// @notice Stake coins to validator /// @param staker Staker address /// @param validator Validator address /// @param amount Coins amount @@ -24,7 +36,7 @@ interface IStaking { uint256 amount ) external returns (bool success); - /// @dev Unstake coins from validator + /// @notice Unstake coins from validator /// @param staker Staker address /// @param validator Validator address /// @param amount Coins amount @@ -35,7 +47,7 @@ interface IStaking { uint256 amount ) external returns (int64 completionTime); - /// @dev Transfer coins from validatorSrc to validatorDst + /// @notice Transfer coins from validatorSrc to validatorDst /// @param staker Staker address /// @param validatorSrc Validator from address /// @param validatorDst Validator to address @@ -48,7 +60,11 @@ interface IStaking { uint256 amount ) external returns (int64 completionTime); - function getAllValidators() external view returns (Validator[] calldata); + /// @notice Get all validators + /// @return validators All validators + function getAllValidators() external view returns (Validator[] calldata validators); - function getStakes(address staker, string memory validator) external view returns (uint256); + /// @notice Get stakes for staker in validator + /// @return stakes Staker stakes in validator + function getStakes(address staker, string memory validator) external view returns (uint256 stakes); } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 545fc89196..19dbcc802f 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -32,6 +32,7 @@ var ( ABI abi.ABI ContractAddress = common.HexToAddress("0x0000000000000000000000000000000000000066") GasRequiredByMethod = map[[4]byte]uint64{} + ViewMethod = map[[4]byte]bool{} ) func init() { @@ -48,8 +49,8 @@ func initABI() { var methodID [4]byte copy(methodID[:], ABI.Methods[methodName].ID[:4]) switch methodName { - // TODO: just temporary values, double check these flat values - // can we just use WriteCostFlat from gas config? + // TODO: just temporary flat values, double check these flat values + // can we just use WriteCostFlat/ReadCostFlat from gas config for flat values? case StakeMethodName: GasRequiredByMethod[methodID] = 10000 case UnstakeMethodName: @@ -58,8 +59,10 @@ func initABI() { GasRequiredByMethod[methodID] = 10000 case GetAllValidatorsMethodName: GasRequiredByMethod[methodID] = 0 + ViewMethod[methodID] = true case GetStakesMethodName: GasRequiredByMethod[methodID] = 0 + ViewMethod[methodID] = true default: GasRequiredByMethod[methodID] = 0 } @@ -100,13 +103,15 @@ func (c *Contract) Abi() abi.ABI { // RequiredGas is required to implement the PrecompiledContract interface. // The gas has to be calculated deterministically based on the input. func (c *Contract) RequiredGas(input []byte) uint64 { - // base cost to prevent large input size - // TODO: 0 for read methods - baseCost := uint64(len(input)) * c.kvGasConfig.WriteCostPerByte // get methodID (first 4 bytes) var methodID [4]byte copy(methodID[:], input[:4]) + // base cost to prevent large input size + baseCost := uint64(len(input)) * c.kvGasConfig.WriteCostPerByte + if ViewMethod[methodID] { + baseCost = uint64(len(input)) * c.kvGasConfig.ReadCostPerByte + } if requiredGas, ok := GasRequiredByMethod[methodID]; ok { return requiredGas + baseCost @@ -126,6 +131,9 @@ func (c *Contract) GetAllValidators( for _, v := range validators { validatorsRes = append(validatorsRes, Validator{ OperatorAddress: v.OperatorAddress, + ConsensusPubKey: v.ConsensusPubkey.String(), + BondStatus: uint8(v.Status), + Jailed: v.Jailed, }) } diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index a0be7009d7..74ca42ad2a 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -51,6 +51,9 @@ func Test_IStakingContract(t *testing.T) { abi.Methods[TransferStakeMethodName], "transferStake method should be present in the ABI", ) + + require.NotNil(t, abi.Methods[GetAllValidatorsMethodName], "getAllValidators method should be present in the ABI") + require.NotNil(t, abi.Methods[GetStakesMethodName], "getStakes method should be present in the ABI") }) t.Run("should check gas requirements for methods", func(t *testing.T) { @@ -100,6 +103,34 @@ func Test_IStakingContract(t *testing.T) { ) }) + t.Run("getAllValidators", func(t *testing.T) { + getAllValidators := contract.RequiredGas(abi.Methods[GetAllValidatorsMethodName].ID) + copy(method[:], abi.Methods[GetAllValidatorsMethodName].ID[:4]) + baseCost := uint64(len(method)) * gasConfig.ReadCostPerByte + require.Equal( + t, + GasRequiredByMethod[method]+baseCost, + getAllValidators, + "getAllValidators method should require %d gas, got %d", + GasRequiredByMethod[method]+baseCost, + getAllValidators, + ) + }) + + t.Run("getStakes", func(t *testing.T) { + getStakes := contract.RequiredGas(abi.Methods[GetStakesMethodName].ID) + copy(method[:], abi.Methods[GetStakesMethodName].ID[:4]) + baseCost := uint64(len(method)) * gasConfig.ReadCostPerByte + require.Equal( + t, + GasRequiredByMethod[method]+baseCost, + getStakes, + "getStakes method should require %d gas, got %d", + GasRequiredByMethod[method]+baseCost, + getStakes, + ) + }) + t.Run("invalid method", func(t *testing.T) { invalidMethodBytes := []byte("invalidMethod") gasInvalidMethod := contract.RequiredGas(invalidMethodBytes) From 43f063e92b647f36517a456af644a2c693b154d7 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 15:34:56 +0200 Subject: [PATCH 20/27] add queries unit tests --- precompiles/staking/staking_test.go | 108 ++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 74ca42ad2a..6fe9cda69a 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -2,8 +2,10 @@ package staking import ( "encoding/json" + "fmt" "testing" + "math/big" "math/rand" tmdb "github.com/cometbft/cometbft-db" @@ -901,3 +903,109 @@ func Test_TransferStake(t *testing.T) { require.ErrorContains(t, err, "origin is not staker") }) } + +func Test_GetAllValidators(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + + cdc := keeper.NewCodec() + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) + gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + methodID := abi.Methods[GetAllValidatorsMethodName] + + t.Run("should return empty array if validators not set", func(t *testing.T) { + validators, err := contract.GetAllValidators(ctx, &methodID) + require.NoError(t, err) + + res, err := methodID.Outputs.Unpack(validators) + require.NoError(t, err) + + require.Empty(t, res[0]) + }) + + t.Run("should return validators if set", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + validators, err := contract.GetAllValidators(ctx, &methodID) + require.NoError(t, err) + + res, err := methodID.Outputs.Unpack(validators) + require.NoError(t, err) + + require.NotEmpty(t, res[0]) + }) +} + +func Test_GetStakes(t *testing.T) { + var encoding ethermint.EncodingConfig + appCodec := encoding.Codec + + cdc := keeper.NewCodec() + + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) + gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) + + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") + + methodID := abi.Methods[GetStakesMethodName] + + t.Run("should return stakes", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) + coins := sample.Coins() + err := sdkKeepers.BankKeeper.MintCoins(ctx, fungibletypes.ModuleName, sample.Coins()) + require.NoError(t, err) + err = sdkKeepers.BankKeeper.SendCoinsFromModuleToAccount(ctx, fungibletypes.ModuleName, staker, coins) + require.NoError(t, err) + + stakerAddr := common.BytesToAddress(staker.Bytes()) + + stakeArgs := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + + stakeMethodID := abi.Methods[StakeMethodName] + _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, stakeArgs) + require.NoError(t, err) + + args := []interface{}{stakerEthAddr, validator.OperatorAddress} + stakes, err := contract.GetStake(ctx, &methodID, args) + require.NoError(t, err) + + res, err := methodID.Outputs.Unpack(stakes) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("%d000000000000000000", coins.AmountOf(config.BaseDenom).BigInt().Int64()), res[0].(*big.Int).String()) + }) +} From 5af835fd250ecbcdde09dcf68b7538e98731472b Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 16:12:11 +0200 Subject: [PATCH 21/27] add more unit tests and refactor a bit --- precompiles/precompiles_test.go | 13 +- precompiles/staking/staking.go | 1 - precompiles/staking/staking_test.go | 194 ++++++++-------------------- 3 files changed, 63 insertions(+), 145 deletions(-) diff --git a/precompiles/precompiles_test.go b/precompiles/precompiles_test.go index f5decc49ec..1ec0f12885 100644 --- a/precompiles/precompiles_test.go +++ b/precompiles/precompiles_test.go @@ -29,11 +29,12 @@ func Test_StatefulContracts(t *testing.T) { require.NotNil(t, contracts, "StatefulContracts() should not return a nil slice") require.Len(t, contracts, expectedContracts, "StatefulContracts() should return all the enabled contracts") - // Extract the contract function from the first contract. - customContractFn := contracts[0] - contract := customContractFn(ctx, ethparams.Rules{}) + for _, customContractFn := range contracts { + // Extract the contract function. + contract := customContractFn(ctx, ethparams.Rules{}) - // Check the contract function returns a valid address. - contractAddr := contract.Address() - require.NotNil(t, contractAddr, "The called contract should have a valid address") + // Check the contract function returns a valid address. + contractAddr := contract.Address() + require.NotNil(t, contractAddr, "The called contract should have a valid address") + } } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 19dbcc802f..23c424aa30 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -103,7 +103,6 @@ func (c *Contract) Abi() abi.ABI { // RequiredGas is required to implement the PrecompiledContract interface. // The gas has to be calculated deterministically based on the input. func (c *Contract) RequiredGas(input []byte) uint64 { - // get methodID (first 4 bytes) var methodID [4]byte copy(methodID[:], input[:4]) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 6fe9cda69a..bcf4352587 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -12,7 +12,9 @@ import ( "github.com/cosmos/cosmos-sdk/store" storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ethermint "github.com/zeta-chain/ethermint/types" @@ -22,30 +24,37 @@ import ( fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" ) -func Test_IStakingContract(t *testing.T) { +func setup(t *testing.T) (sdk.Context, *Contract, abi.ABI, keeper.SDKKeepers) { var encoding ethermint.EncodingConfig appCodec := encoding.Codec - keys, memKeys, tkeys, allKeys := keeper.StoreKeys() + cdc := keeper.NewCodec() - sdkKeepers := keeper.NewSDKKeepersWithKeys(cdc, keys, memKeys, tkeys, allKeys) + db := tmdb.NewMemDB() + stateStore := store.NewCommitMultiStore(db) + sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) gasConfig := storetypes.TransientGasConfig() + ctx := keeper.NewContext(stateStore) + require.NoError(t, stateStore.LoadLatestVersion()) + + stakingGenesisState := stakingtypes.DefaultGenesisState() + stakingGenesisState.Params.BondDenom = config.BaseDenom + sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - t.Run("should create contract and check address and ABI", func(t *testing.T) { - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") + contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) + require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - address := contract.Address() - require.Equal(t, ContractAddress, address, "contract address should match the precompiled address") + abi := contract.Abi() + require.NotNil(t, abi, "contract ABI should not be nil") - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - }) + return ctx, contract, abi, sdkKeepers +} - t.Run("should check methods are present in ABI", func(t *testing.T) { - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - abi := contract.Abi() +func Test_IStakingContract(t *testing.T) { + _, contract, abi, _ := setup(t) + gasConfig := storetypes.TransientGasConfig() + t.Run("should check methods are present in ABI", func(t *testing.T) { require.NotNil(t, abi.Methods[StakeMethodName], "stake method should be present in the ABI") require.NotNil(t, abi.Methods[UnstakeMethodName], "unstake method should be present in the ABI") require.NotNil( @@ -59,8 +68,6 @@ func Test_IStakingContract(t *testing.T) { }) t.Run("should check gas requirements for methods", func(t *testing.T) { - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - abi := contract.Abi() var method [4]byte t.Run("stake", func(t *testing.T) { @@ -149,18 +156,7 @@ func Test_IStakingContract(t *testing.T) { } func Test_InvalidMethod(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - keys, memKeys, tkeys, allKeys := keeper.StoreKeys() - cdc := keeper.NewCodec() - sdkKeepers := keeper.NewSDKKeepersWithKeys(cdc, keys, memKeys, tkeys, allKeys) - gasConfig := storetypes.TransientGasConfig() - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") + _, _, abi, _ := setup(t) _, doNotExist := abi.Methods["invalidMethod"] require.False(t, doNotExist, "invalidMethod should not be present in the ABI") @@ -178,28 +174,7 @@ func Test_InvalidABI(t *testing.T) { } func Test_Stake(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - - cdc := keeper.NewCodec() - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) - gasConfig := storetypes.TransientGasConfig() - ctx := keeper.NewContext(stateStore) - require.NoError(t, stateStore.LoadLatestVersion()) - - stakingGenesisState := stakingtypes.DefaultGenesisState() - stakingGenesisState.Params.BondDenom = config.BaseDenom - sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - + ctx, contract, abi, sdkKeepers := setup(t) methodID := abi.Methods[StakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { @@ -367,28 +342,7 @@ func Test_Stake(t *testing.T) { } func Test_Unstake(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - - cdc := keeper.NewCodec() - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) - gasConfig := storetypes.TransientGasConfig() - ctx := keeper.NewContext(stateStore) - require.NoError(t, stateStore.LoadLatestVersion()) - - stakingGenesisState := stakingtypes.DefaultGenesisState() - stakingGenesisState.Params.BondDenom = config.BaseDenom - sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - + ctx, contract, abi, sdkKeepers := setup(t) methodID := abi.Methods[UnstakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { @@ -571,28 +525,7 @@ func Test_Unstake(t *testing.T) { } func Test_TransferStake(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - - cdc := keeper.NewCodec() - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) - gasConfig := storetypes.TransientGasConfig() - ctx := keeper.NewContext(stateStore) - require.NoError(t, stateStore.LoadLatestVersion()) - - stakingGenesisState := stakingtypes.DefaultGenesisState() - stakingGenesisState.Params.BondDenom = config.BaseDenom - sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - + ctx, contract, abi, sdkKeepers := setup(t) methodID := abi.Methods[TransferStakeMethodName] t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { @@ -905,28 +838,7 @@ func Test_TransferStake(t *testing.T) { } func Test_GetAllValidators(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - - cdc := keeper.NewCodec() - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) - gasConfig := storetypes.TransientGasConfig() - ctx := keeper.NewContext(stateStore) - require.NoError(t, stateStore.LoadLatestVersion()) - - stakingGenesisState := stakingtypes.DefaultGenesisState() - stakingGenesisState.Params.BondDenom = config.BaseDenom - sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - + ctx, contract, abi, sdkKeepers := setup(t) methodID := abi.Methods[GetAllValidatorsMethodName] t.Run("should return empty array if validators not set", func(t *testing.T) { @@ -955,28 +867,7 @@ func Test_GetAllValidators(t *testing.T) { } func Test_GetStakes(t *testing.T) { - var encoding ethermint.EncodingConfig - appCodec := encoding.Codec - - cdc := keeper.NewCodec() - - db := tmdb.NewMemDB() - stateStore := store.NewCommitMultiStore(db) - sdkKeepers := keeper.NewSDKKeepers(cdc, db, stateStore) - gasConfig := storetypes.TransientGasConfig() - ctx := keeper.NewContext(stateStore) - require.NoError(t, stateStore.LoadLatestVersion()) - - stakingGenesisState := stakingtypes.DefaultGenesisState() - stakingGenesisState.Params.BondDenom = config.BaseDenom - sdkKeepers.StakingKeeper.InitGenesis(ctx, stakingGenesisState) - - contract := NewIStakingContract(&sdkKeepers.StakingKeeper, appCodec, gasConfig) - require.NotNil(t, contract, "NewIStakingContract() should not return a nil contract") - - abi := contract.Abi() - require.NotNil(t, abi, "contract ABI should not be nil") - + ctx, contract, abi, sdkKeepers := setup(t) methodID := abi.Methods[GetStakesMethodName] t.Run("should return stakes", func(t *testing.T) { @@ -1008,4 +899,31 @@ func Test_GetStakes(t *testing.T) { require.NoError(t, err) require.Equal(t, fmt.Sprintf("%d000000000000000000", coins.AmountOf(config.BaseDenom).BigInt().Int64()), res[0].(*big.Int).String()) }) + + t.Run("should fail if wrong args amount", func(t *testing.T) { + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) + + args := []interface{}{stakerEthAddr} + _, err := contract.GetStake(ctx, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if invalid staker arg", func(t *testing.T) { + r := rand.New(rand.NewSource(42)) + validator := sample.Validator(t, r) + + args := []interface{}{42, validator.OperatorAddress} + _, err := contract.GetStake(ctx, &methodID, args) + require.Error(t, err) + }) + + t.Run("should fail if invalid val address", func(t *testing.T) { + staker := sample.Bech32AccAddress() + stakerEthAddr := common.BytesToAddress(staker.Bytes()) + + args := []interface{}{stakerEthAddr, staker} + _, err := contract.GetStake(ctx, &methodID, args) + require.Error(t, err) + }) } From 17380ca37e95ee8d7b23fe1d86710e7fc4f9bfae Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 16:16:40 +0200 Subject: [PATCH 22/27] fmt --- precompiles/staking/staking_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index bcf4352587..95a915550a 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -63,7 +63,11 @@ func Test_IStakingContract(t *testing.T) { "transferStake method should be present in the ABI", ) - require.NotNil(t, abi.Methods[GetAllValidatorsMethodName], "getAllValidators method should be present in the ABI") + require.NotNil( + t, + abi.Methods[GetAllValidatorsMethodName], + "getAllValidators method should be present in the ABI", + ) require.NotNil(t, abi.Methods[GetStakesMethodName], "getStakes method should be present in the ABI") }) @@ -897,7 +901,11 @@ func Test_GetStakes(t *testing.T) { res, err := methodID.Outputs.Unpack(stakes) require.NoError(t, err) - require.Equal(t, fmt.Sprintf("%d000000000000000000", coins.AmountOf(config.BaseDenom).BigInt().Int64()), res[0].(*big.Int).String()) + require.Equal( + t, + fmt.Sprintf("%d000000000000000000", coins.AmountOf(config.BaseDenom).BigInt().Int64()), + res[0].(*big.Int).String(), + ) }) t.Run("should fail if wrong args amount", func(t *testing.T) { From ecf6d12a222748a9bc1b95d08bc7047b979545fd Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 18:47:50 +0200 Subject: [PATCH 23/27] fixes after merge --- codecov.yml | 2 +- e2e/e2etests/test_precompiles_staking.go | 6 +++--- precompiles/precompiles.go | 6 +++--- precompiles/staking/staking.go | 2 +- precompiles/staking/staking_test.go | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/codecov.yml b/codecov.yml index 3fc15ff248..fedb830848 100644 --- a/codecov.yml +++ b/codecov.yml @@ -81,4 +81,4 @@ ignore: - "precompiles/**/*.json" - "precompiles/**/*.sol" - "precompiles/prototype/IPrototype.go" - - "precompiles/prototype/IStaking.go" + - "precompiles/staking/IStaking.go" diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go index 36e2da5bbb..d390368307 100644 --- a/e2e/e2etests/test_precompiles_staking.go +++ b/e2e/e2etests/test_precompiles_staking.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" - "github.com/zeta-chain/zetacore/e2e/runner" - "github.com/zeta-chain/zetacore/e2e/utils" - "github.com/zeta-chain/zetacore/precompiles/staking" + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/precompiles/staking" ) func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { diff --git a/precompiles/precompiles.go b/precompiles/precompiles.go index e3e2223692..1adf65005f 100644 --- a/precompiles/precompiles.go +++ b/precompiles/precompiles.go @@ -10,9 +10,9 @@ import ( ethparams "github.com/ethereum/go-ethereum/params" evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper" - "github.com/zeta-chain/zetacore/precompiles/prototype" - "github.com/zeta-chain/zetacore/precompiles/staking" - fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" + "github.com/zeta-chain/node/precompiles/prototype" + "github.com/zeta-chain/node/precompiles/staking" + fungiblekeeper "github.com/zeta-chain/node/x/fungible/keeper" ) // EnabledStatefulContracts contains the list of all enabled stateful precompiles. diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 23c424aa30..d227ca6e1d 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" - ptypes "github.com/zeta-chain/zetacore/precompiles/types" + ptypes "github.com/zeta-chain/node/precompiles/types" ) const ( diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 95a915550a..8578491e03 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -18,10 +18,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ethermint "github.com/zeta-chain/ethermint/types" - "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/testutil/keeper" - "github.com/zeta-chain/zetacore/testutil/sample" - fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + "github.com/zeta-chain/node/cmd/zetacored/config" + "github.com/zeta-chain/node/testutil/keeper" + "github.com/zeta-chain/node/testutil/sample" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" ) func setup(t *testing.T) (sdk.Context, *Contract, abi.ABI, keeper.SDKKeepers) { From 0bef0e7180db49f05ace97a880513247ad63d419 Mon Sep 17 00:00:00 2001 From: skosito Date: Fri, 30 Aug 2024 19:05:39 +0200 Subject: [PATCH 24/27] cleanup --- e2e/e2etests/test_precompiles_staking.go | 32 +++++++++++----------- precompiles/staking/IStaking.abi | 4 +-- precompiles/staking/IStaking.go | 26 +++++++++--------- precompiles/staking/IStaking.json | 4 +-- precompiles/staking/IStaking.sol | 6 ++-- precompiles/staking/staking.go | 10 +++---- precompiles/staking/staking_test.go | 35 +++++++++++++----------- 7 files changed, 60 insertions(+), 57 deletions(-) diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go index d390368307..d230706c25 100644 --- a/e2e/e2etests/test_precompiles_staking.go +++ b/e2e/e2etests/test_precompiles_staking.go @@ -22,34 +22,34 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { require.NoError(r, err) require.GreaterOrEqual(r, len(validators), 2) - // stakes are 0 for both validators at the start - stakesBeforeVal1, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + // shares are 0 for both validators at the start + sharesBeforeVal1, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) - require.Equal(r, int64(0), stakesBeforeVal1.Int64()) + require.Equal(r, int64(0), sharesBeforeVal1.Int64()) - stakesBeforeVal2, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + sharesBeforeVal2, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) require.NoError(r, err) - require.Equal(r, int64(0), stakesBeforeVal2.Int64()) + require.Equal(r, int64(0), sharesBeforeVal2.Int64()) // stake 3 to validator1 tx, err := stakingContract.Stake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(3)) require.NoError(r, err) utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - // check stakes are set to 3 - stakesAfterVal1, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + // check shares are set to 3 + sharesAfterVal1, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) - require.Equal(r, big.NewInt(3e18).String(), stakesAfterVal1.String()) + require.Equal(r, big.NewInt(3e18).String(), sharesAfterVal1.String()) // unstake 1 from validator1 tx, err = stakingContract.Unstake(r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, big.NewInt(1)) require.NoError(r, err) utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - // check stakes are set to 2 - stakesAfterVal1, err = stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + // check shares are set to 2 + sharesAfterVal1, err = stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) - require.Equal(r, big.NewInt(2e18).String(), stakesAfterVal1.String()) + require.Equal(r, big.NewInt(2e18).String(), sharesAfterVal1.String()) // transfer 1 stake from validator1 to validator2 tx, err = stakingContract.TransferStake( @@ -63,12 +63,12 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) - // check stakes for both validator1 and validator2 are 1 - stakesAfterVal1, err = stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + // check shares for both validator1 and validator2 are 1 + sharesAfterVal1, err = stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) - require.Equal(r, big.NewInt(1e18).String(), stakesAfterVal1.String()) + require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal1.String()) - stakesAfterVal2, err := stakingContract.GetStakes(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + sharesAfterVal2, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) require.NoError(r, err) - require.Equal(r, big.NewInt(1e18).String(), stakesAfterVal2.String()) + require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal2.String()) } diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index b2daa4d5bd..f6de61fb00 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -47,11 +47,11 @@ "type": "string" } ], - "name": "getStakes", + "name": "getShares", "outputs": [ { "internalType": "uint256", - "name": "stakes", + "name": "shares", "type": "uint256" } ], diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index 006430faed..1a2226b98c 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -39,7 +39,7 @@ type Validator struct { // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"consensusPubKey\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"jailed\",\"type\":\"bool\"},{\"internalType\":\"enumBondStatus\",\"name\":\"bondStatus\",\"type\":\"uint8\"}],\"internalType\":\"structValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getStakes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"stakes\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"consensusPubKey\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"jailed\",\"type\":\"bool\"},{\"internalType\":\"enumBondStatus\",\"name\":\"bondStatus\",\"type\":\"uint8\"}],\"internalType\":\"structValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -219,12 +219,12 @@ func (_IStaking *IStakingCallerSession) GetAllValidators() ([]Validator, error) return _IStaking.Contract.GetAllValidators(&_IStaking.CallOpts) } -// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// GetShares is a free data retrieval call binding the contract method 0x0d1b3daf. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) -func (_IStaking *IStakingCaller) GetStakes(opts *bind.CallOpts, staker common.Address, validator string) (*big.Int, error) { +// Solidity: function getShares(address staker, string validator) view returns(uint256 shares) +func (_IStaking *IStakingCaller) GetShares(opts *bind.CallOpts, staker common.Address, validator string) (*big.Int, error) { var out []interface{} - err := _IStaking.contract.Call(opts, &out, "getStakes", staker, validator) + err := _IStaking.contract.Call(opts, &out, "getShares", staker, validator) if err != nil { return *new(*big.Int), err @@ -236,18 +236,18 @@ func (_IStaking *IStakingCaller) GetStakes(opts *bind.CallOpts, staker common.Ad } -// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// GetShares is a free data retrieval call binding the contract method 0x0d1b3daf. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) -func (_IStaking *IStakingSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { - return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) +// Solidity: function getShares(address staker, string validator) view returns(uint256 shares) +func (_IStaking *IStakingSession) GetShares(staker common.Address, validator string) (*big.Int, error) { + return _IStaking.Contract.GetShares(&_IStaking.CallOpts, staker, validator) } -// GetStakes is a free data retrieval call binding the contract method 0xf668d543. +// GetShares is a free data retrieval call binding the contract method 0x0d1b3daf. // -// Solidity: function getStakes(address staker, string validator) view returns(uint256 stakes) -func (_IStaking *IStakingCallerSession) GetStakes(staker common.Address, validator string) (*big.Int, error) { - return _IStaking.Contract.GetStakes(&_IStaking.CallOpts, staker, validator) +// Solidity: function getShares(address staker, string validator) view returns(uint256 shares) +func (_IStaking *IStakingCallerSession) GetShares(staker common.Address, validator string) (*big.Int, error) { + return _IStaking.Contract.GetShares(&_IStaking.CallOpts, staker, validator) } // Stake is a paid mutator transaction binding the contract method 0x90b8436f. diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 3193134223..87e1599ce9 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -48,11 +48,11 @@ "type": "string" } ], - "name": "getStakes", + "name": "getShares", "outputs": [ { "internalType": "uint256", - "name": "stakes", + "name": "shares", "type": "uint256" } ], diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index 34e8ebd0f2..9cd26a9889 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -64,7 +64,7 @@ interface IStaking { /// @return validators All validators function getAllValidators() external view returns (Validator[] calldata validators); - /// @notice Get stakes for staker in validator - /// @return stakes Staker stakes in validator - function getStakes(address staker, string memory validator) external view returns (uint256 stakes); + /// @notice Get shares for staker in validator + /// @return shares Staker shares in validator + function getShares(address staker, string memory validator) external view returns (uint256 shares); } diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index d227ca6e1d..6d72658e6c 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -25,7 +25,7 @@ const ( // read GetAllValidatorsMethodName = "getAllValidators" - GetStakesMethodName = "getStakes" + GetSharesMethodName = "getShares" ) var ( @@ -60,7 +60,7 @@ func initABI() { case GetAllValidatorsMethodName: GasRequiredByMethod[methodID] = 0 ViewMethod[methodID] = true - case GetStakesMethodName: + case GetSharesMethodName: GasRequiredByMethod[methodID] = 0 ViewMethod[methodID] = true default: @@ -139,7 +139,7 @@ func (c *Contract) GetAllValidators( return method.Outputs.Pack(validatorsRes) } -func (c *Contract) GetStake( +func (c *Contract) GetShares( ctx sdk.Context, method *abi.Method, args []interface{}, @@ -379,10 +379,10 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro return nil, err } return res, nil - case GetStakesMethodName: + case GetSharesMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.GetStake(ctx, method, args) + res, err = c.GetShares(ctx, method, args) return err }) if execErr != nil { diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 8578491e03..462e815774 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -47,6 +47,9 @@ func setup(t *testing.T) (sdk.Context, *Contract, abi.ABI, keeper.SDKKeepers) { abi := contract.Abi() require.NotNil(t, abi, "contract ABI should not be nil") + address := contract.Address() + require.NotNil(t, address, "contract address should not be nil") + return ctx, contract, abi, sdkKeepers } @@ -68,7 +71,7 @@ func Test_IStakingContract(t *testing.T) { abi.Methods[GetAllValidatorsMethodName], "getAllValidators method should be present in the ABI", ) - require.NotNil(t, abi.Methods[GetStakesMethodName], "getStakes method should be present in the ABI") + require.NotNil(t, abi.Methods[GetSharesMethodName], "getShares method should be present in the ABI") }) t.Run("should check gas requirements for methods", func(t *testing.T) { @@ -130,17 +133,17 @@ func Test_IStakingContract(t *testing.T) { ) }) - t.Run("getStakes", func(t *testing.T) { - getStakes := contract.RequiredGas(abi.Methods[GetStakesMethodName].ID) - copy(method[:], abi.Methods[GetStakesMethodName].ID[:4]) + t.Run("getShares", func(t *testing.T) { + getShares := contract.RequiredGas(abi.Methods[GetSharesMethodName].ID) + copy(method[:], abi.Methods[GetSharesMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.ReadCostPerByte require.Equal( t, GasRequiredByMethod[method]+baseCost, - getStakes, - "getStakes method should require %d gas, got %d", + getShares, + "getShares method should require %d gas, got %d", GasRequiredByMethod[method]+baseCost, - getStakes, + getShares, ) }) @@ -719,12 +722,12 @@ func Test_TransferStake(t *testing.T) { argsTransferStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, - validatorDest.OperatorAddress, + 42, coins.AmountOf(config.BaseDenom).BigInt(), } _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) - require.NoError(t, err) + require.Error(t, err) }) t.Run("should fail if amount is invalid arg", func(t *testing.T) { @@ -870,9 +873,9 @@ func Test_GetAllValidators(t *testing.T) { }) } -func Test_GetStakes(t *testing.T) { +func Test_GetShares(t *testing.T) { ctx, contract, abi, sdkKeepers := setup(t) - methodID := abi.Methods[GetStakesMethodName] + methodID := abi.Methods[GetSharesMethodName] t.Run("should return stakes", func(t *testing.T) { r := rand.New(rand.NewSource(42)) @@ -896,7 +899,7 @@ func Test_GetStakes(t *testing.T) { require.NoError(t, err) args := []interface{}{stakerEthAddr, validator.OperatorAddress} - stakes, err := contract.GetStake(ctx, &methodID, args) + stakes, err := contract.GetShares(ctx, &methodID, args) require.NoError(t, err) res, err := methodID.Outputs.Unpack(stakes) @@ -913,7 +916,7 @@ func Test_GetStakes(t *testing.T) { stakerEthAddr := common.BytesToAddress(staker.Bytes()) args := []interface{}{stakerEthAddr} - _, err := contract.GetStake(ctx, &methodID, args) + _, err := contract.GetShares(ctx, &methodID, args) require.Error(t, err) }) @@ -922,7 +925,7 @@ func Test_GetStakes(t *testing.T) { validator := sample.Validator(t, r) args := []interface{}{42, validator.OperatorAddress} - _, err := contract.GetStake(ctx, &methodID, args) + _, err := contract.GetShares(ctx, &methodID, args) require.Error(t, err) }) @@ -930,8 +933,8 @@ func Test_GetStakes(t *testing.T) { staker := sample.Bech32AccAddress() stakerEthAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, staker} - _, err := contract.GetStake(ctx, &methodID, args) + args := []interface{}{stakerEthAddr, staker.String()} + _, err := contract.GetShares(ctx, &methodID, args) require.Error(t, err) }) } From f991479af0585c0ce5c6c7dcd3bb9fffadd7b706 Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 2 Sep 2024 15:46:27 +0200 Subject: [PATCH 25/27] PR comments --- cmd/zetae2e/local/local.go | 4 +- e2e/e2etests/e2etests.go | 8 +- e2e/e2etests/test_precompiles_staking.go | 23 +-- precompiles/staking/IStaking.abi | 30 ++-- precompiles/staking/IStaking.go | 44 ++--- precompiles/staking/IStaking.json | 30 ++-- precompiles/staking/IStaking.sol | 6 +- precompiles/staking/staking.go | 16 +- precompiles/staking/staking_test.go | 195 +++++++++++++++++++---- 9 files changed, 247 insertions(+), 109 deletions(-) diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index b67be69f25..fdde8e5840 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -309,8 +309,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { if !skipPrecompiles { precompiledContractTests = []string{ - e2etests.TestZetaPrecompilesPrototypeName, - e2etests.TestZetaPrecompilesStakingName, + e2etests.TestPrecompilesPrototypeName, + e2etests.TestPrecompilesStakingName, } } diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index ee39269371..7d09bd966c 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -147,8 +147,8 @@ const ( /* Stateful precompiled contracts tests */ - TestZetaPrecompilesPrototypeName = "precompile_contracts_prototype" - TestZetaPrecompilesStakingName = "precompile_contracts_staking" + TestPrecompilesPrototypeName = "precompile_contracts_prototype" + TestPrecompilesStakingName = "precompile_contracts_staking" ) // AllE2ETests is an ordered list of all e2e tests @@ -815,13 +815,13 @@ var AllE2ETests = []runner.E2ETest{ Stateful precompiled contracts tests */ runner.NewE2ETest( - TestZetaPrecompilesPrototypeName, + TestPrecompilesPrototypeName, "test stateful precompiled contracts prototype", []runner.ArgDefinition{}, TestPrecompilesPrototype, ), runner.NewE2ETest( - TestZetaPrecompilesStakingName, + TestPrecompilesStakingName, "test stateful precompiled contracts staking", []runner.ArgDefinition{}, TestPrecompilesStaking, diff --git a/e2e/e2etests/test_precompiles_staking.go b/e2e/e2etests/test_precompiles_staking.go index d230706c25..429c63932a 100644 --- a/e2e/e2etests/test_precompiles_staking.go +++ b/e2e/e2etests/test_precompiles_staking.go @@ -3,6 +3,7 @@ package e2etests import ( "math/big" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" "github.com/zeta-chain/node/e2e/runner" @@ -16,18 +17,22 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { stakingContract, err := staking.NewIStaking(staking.ContractAddress, r.ZEVMClient) require.NoError(r, err, "Failed to create staking contract caller") + previousGasLimit := r.ZEVMAuth.GasLimit r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() - validators, err := stakingContract.GetAllValidators(nil) + validators, err := stakingContract.GetAllValidators(&bind.CallOpts{}) require.NoError(r, err) require.GreaterOrEqual(r, len(validators), 2) // shares are 0 for both validators at the start - sharesBeforeVal1, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + sharesBeforeVal1, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) require.Equal(r, int64(0), sharesBeforeVal1.Int64()) - sharesBeforeVal2, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + sharesBeforeVal2, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[1].OperatorAddress) require.NoError(r, err) require.Equal(r, int64(0), sharesBeforeVal2.Int64()) @@ -37,7 +42,7 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) // check shares are set to 3 - sharesAfterVal1, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + sharesAfterVal1, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) require.Equal(r, big.NewInt(3e18).String(), sharesAfterVal1.String()) @@ -47,12 +52,12 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) // check shares are set to 2 - sharesAfterVal1, err = stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + sharesAfterVal1, err = stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) require.Equal(r, big.NewInt(2e18).String(), sharesAfterVal1.String()) - // transfer 1 stake from validator1 to validator2 - tx, err = stakingContract.TransferStake( + // move 1 stake from validator1 to validator2 + tx, err = stakingContract.MoveStake( r.ZEVMAuth, r.ZEVMAuth.From, validators[0].OperatorAddress, @@ -64,11 +69,11 @@ func TestPrecompilesStaking(r *runner.E2ERunner, args []string) { utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) // check shares for both validator1 and validator2 are 1 - sharesAfterVal1, err = stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[0].OperatorAddress) + sharesAfterVal1, err = stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[0].OperatorAddress) require.NoError(r, err) require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal1.String()) - sharesAfterVal2, err := stakingContract.GetShares(nil, r.ZEVMAuth.From, validators[1].OperatorAddress) + sharesAfterVal2, err := stakingContract.GetShares(&bind.CallOpts{}, r.ZEVMAuth.From, validators[1].OperatorAddress) require.NoError(r, err) require.Equal(r, big.NewInt(1e18).String(), sharesAfterVal2.String()) } diff --git a/precompiles/staking/IStaking.abi b/precompiles/staking/IStaking.abi index f6de61fb00..f8ee31e062 100644 --- a/precompiles/staking/IStaking.abi +++ b/precompiles/staking/IStaking.abi @@ -67,7 +67,12 @@ }, { "internalType": "string", - "name": "validator", + "name": "validatorSrc", + "type": "string" + }, + { + "internalType": "string", + "name": "validatorDst", "type": "string" }, { @@ -76,12 +81,12 @@ "type": "uint256" } ], - "name": "stake", + "name": "moveStake", "outputs": [ { - "internalType": "bool", - "name": "success", - "type": "bool" + "internalType": "int64", + "name": "completionTime", + "type": "int64" } ], "stateMutability": "nonpayable", @@ -96,12 +101,7 @@ }, { "internalType": "string", - "name": "validatorSrc", - "type": "string" - }, - { - "internalType": "string", - "name": "validatorDst", + "name": "validator", "type": "string" }, { @@ -110,12 +110,12 @@ "type": "uint256" } ], - "name": "transferStake", + "name": "stake", "outputs": [ { - "internalType": "int64", - "name": "completionTime", - "type": "int64" + "internalType": "bool", + "name": "success", + "type": "bool" } ], "stateMutability": "nonpayable", diff --git a/precompiles/staking/IStaking.go b/precompiles/staking/IStaking.go index 1a2226b98c..ec3143f41f 100644 --- a/precompiles/staking/IStaking.go +++ b/precompiles/staking/IStaking.go @@ -39,7 +39,7 @@ type Validator struct { // IStakingMetaData contains all meta data concerning the IStaking contract. var IStakingMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"consensusPubKey\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"jailed\",\"type\":\"bool\"},{\"internalType\":\"enumBondStatus\",\"name\":\"bondStatus\",\"type\":\"uint8\"}],\"internalType\":\"structValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"getAllValidators\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"operatorAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"consensusPubKey\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"jailed\",\"type\":\"bool\"},{\"internalType\":\"enumBondStatus\",\"name\":\"bondStatus\",\"type\":\"uint8\"}],\"internalType\":\"structValidator[]\",\"name\":\"validators\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"}],\"name\":\"getShares\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"shares\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validatorSrc\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"validatorDst\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"moveStake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"stake\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"staker\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"validator\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"unstake\",\"outputs\":[{\"internalType\":\"int64\",\"name\":\"completionTime\",\"type\":\"int64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // IStakingABI is the input ABI used to generate the binding from. @@ -250,6 +250,27 @@ func (_IStaking *IStakingCallerSession) GetShares(staker common.Address, validat return _IStaking.Contract.GetShares(&_IStaking.CallOpts, staker, validator) } +// MoveStake is a paid mutator transaction binding the contract method 0xd11a93d0. +// +// Solidity: function moveStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactor) MoveStake(opts *bind.TransactOpts, staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.contract.Transact(opts, "moveStake", staker, validatorSrc, validatorDst, amount) +} + +// MoveStake is a paid mutator transaction binding the contract method 0xd11a93d0. +// +// Solidity: function moveStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingSession) MoveStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.MoveStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) +} + +// MoveStake is a paid mutator transaction binding the contract method 0xd11a93d0. +// +// Solidity: function moveStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) +func (_IStaking *IStakingTransactorSession) MoveStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { + return _IStaking.Contract.MoveStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) +} + // Stake is a paid mutator transaction binding the contract method 0x90b8436f. // // Solidity: function stake(address staker, string validator, uint256 amount) returns(bool success) @@ -271,27 +292,6 @@ func (_IStaking *IStakingTransactorSession) Stake(staker common.Address, validat return _IStaking.Contract.Stake(&_IStaking.TransactOpts, staker, validator, amount) } -// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. -// -// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactor) TransferStake(opts *bind.TransactOpts, staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.contract.Transact(opts, "transferStake", staker, validatorSrc, validatorDst, amount) -} - -// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. -// -// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingSession) TransferStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.TransferStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) -} - -// TransferStake is a paid mutator transaction binding the contract method 0x26d67892. -// -// Solidity: function transferStake(address staker, string validatorSrc, string validatorDst, uint256 amount) returns(int64 completionTime) -func (_IStaking *IStakingTransactorSession) TransferStake(staker common.Address, validatorSrc string, validatorDst string, amount *big.Int) (*types.Transaction, error) { - return _IStaking.Contract.TransferStake(&_IStaking.TransactOpts, staker, validatorSrc, validatorDst, amount) -} - // Unstake is a paid mutator transaction binding the contract method 0x57c6ea3e. // // Solidity: function unstake(address staker, string validator, uint256 amount) returns(int64 completionTime) diff --git a/precompiles/staking/IStaking.json b/precompiles/staking/IStaking.json index 87e1599ce9..bd89798977 100644 --- a/precompiles/staking/IStaking.json +++ b/precompiles/staking/IStaking.json @@ -68,7 +68,12 @@ }, { "internalType": "string", - "name": "validator", + "name": "validatorSrc", + "type": "string" + }, + { + "internalType": "string", + "name": "validatorDst", "type": "string" }, { @@ -77,12 +82,12 @@ "type": "uint256" } ], - "name": "stake", + "name": "moveStake", "outputs": [ { - "internalType": "bool", - "name": "success", - "type": "bool" + "internalType": "int64", + "name": "completionTime", + "type": "int64" } ], "stateMutability": "nonpayable", @@ -97,12 +102,7 @@ }, { "internalType": "string", - "name": "validatorSrc", - "type": "string" - }, - { - "internalType": "string", - "name": "validatorDst", + "name": "validator", "type": "string" }, { @@ -111,12 +111,12 @@ "type": "uint256" } ], - "name": "transferStake", + "name": "stake", "outputs": [ { - "internalType": "int64", - "name": "completionTime", - "type": "int64" + "internalType": "bool", + "name": "success", + "type": "bool" } ], "stateMutability": "nonpayable", diff --git a/precompiles/staking/IStaking.sol b/precompiles/staking/IStaking.sol index 9cd26a9889..7924b296c0 100644 --- a/precompiles/staking/IStaking.sol +++ b/precompiles/staking/IStaking.sol @@ -47,13 +47,13 @@ interface IStaking { uint256 amount ) external returns (int64 completionTime); - /// @notice Transfer coins from validatorSrc to validatorDst + /// @notice Move coins from validatorSrc to validatorDst /// @param staker Staker address /// @param validatorSrc Validator from address /// @param validatorDst Validator to address /// @param amount Coins amount - /// @return completionTime Time when staket transfer is done - function transferStake( + /// @return completionTime Time when stake move is done + function moveStake( address staker, string memory validatorSrc, string memory validatorDst, diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index 6d72658e6c..c0e6649ae6 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -19,9 +19,9 @@ import ( const ( // write - StakeMethodName = "stake" - UnstakeMethodName = "unstake" - TransferStakeMethodName = "transferStake" + StakeMethodName = "stake" + UnstakeMethodName = "unstake" + MoveStakeMethodName = "moveStake" // read GetAllValidatorsMethodName = "getAllValidators" @@ -55,7 +55,7 @@ func initABI() { GasRequiredByMethod[methodID] = 10000 case UnstakeMethodName: GasRequiredByMethod[methodID] = 10000 - case TransferStakeMethodName: + case MoveStakeMethodName: GasRequiredByMethod[methodID] = 10000 case GetAllValidatorsMethodName: GasRequiredByMethod[methodID] = 0 @@ -126,7 +126,7 @@ func (c *Contract) GetAllValidators( ) ([]byte, error) { validators := c.stakingKeeper.GetAllValidators(ctx) - validatorsRes := []Validator{} + validatorsRes := make([]Validator, len(validators)) for _, v := range validators { validatorsRes = append(validatorsRes, Validator{ OperatorAddress: v.OperatorAddress, @@ -291,7 +291,7 @@ func (c *Contract) Unstake( return method.Outputs.Pack(res.GetCompletionTime().UTC().Unix()) } -func (c *Contract) TransferStake( +func (c *Contract) MoveStake( ctx sdk.Context, origin common.Address, method *abi.Method, @@ -409,10 +409,10 @@ func (c *Contract) Run(evm *vm.EVM, contract *vm.Contract, _ bool) ([]byte, erro return nil, err } return res, nil - case TransferStakeMethodName: + case MoveStakeMethodName: var res []byte execErr := stateDB.ExecuteNativeAction(contract.Address(), nil, func(ctx sdk.Context) error { - res, err = c.TransferStake(ctx, evm.Origin, method, args) + res, err = c.MoveStake(ctx, evm.Origin, method, args) return err }) if execErr != nil { diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 462e815774..6ae2f1fc3f 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -62,8 +62,8 @@ func Test_IStakingContract(t *testing.T) { require.NotNil(t, abi.Methods[UnstakeMethodName], "unstake method should be present in the ABI") require.NotNil( t, - abi.Methods[TransferStakeMethodName], - "transferStake method should be present in the ABI", + abi.Methods[MoveStakeMethodName], + "moveStake method should be present in the ABI", ) require.NotNil( @@ -78,7 +78,9 @@ func Test_IStakingContract(t *testing.T) { var method [4]byte t.Run("stake", func(t *testing.T) { + // ACT stake := contract.RequiredGas(abi.Methods[StakeMethodName].ID) + // ASSERT copy(method[:], abi.Methods[StakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( @@ -92,7 +94,9 @@ func Test_IStakingContract(t *testing.T) { }) t.Run("unstake", func(t *testing.T) { + // ACT unstake := contract.RequiredGas(abi.Methods[UnstakeMethodName].ID) + // ASSERT copy(method[:], abi.Methods[UnstakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( @@ -105,22 +109,26 @@ func Test_IStakingContract(t *testing.T) { ) }) - t.Run("transferStake", func(t *testing.T) { - transferStake := contract.RequiredGas(abi.Methods[TransferStakeMethodName].ID) - copy(method[:], abi.Methods[TransferStakeMethodName].ID[:4]) + t.Run("moveStake", func(t *testing.T) { + // ACT + moveStake := contract.RequiredGas(abi.Methods[MoveStakeMethodName].ID) + // ASSERT + copy(method[:], abi.Methods[MoveStakeMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.WriteCostPerByte require.Equal( t, GasRequiredByMethod[method]+baseCost, - transferStake, - "transferStake method should require %d gas, got %d", + moveStake, + "moveStake method should require %d gas, got %d", GasRequiredByMethod[method]+baseCost, - transferStake, + moveStake, ) }) t.Run("getAllValidators", func(t *testing.T) { + // ACT getAllValidators := contract.RequiredGas(abi.Methods[GetAllValidatorsMethodName].ID) + // ASSERT copy(method[:], abi.Methods[GetAllValidatorsMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.ReadCostPerByte require.Equal( @@ -134,7 +142,9 @@ func Test_IStakingContract(t *testing.T) { }) t.Run("getShares", func(t *testing.T) { + // ACT getShares := contract.RequiredGas(abi.Methods[GetSharesMethodName].ID) + // ASSERT copy(method[:], abi.Methods[GetSharesMethodName].ID[:4]) baseCost := uint64(len(method)) * gasConfig.ReadCostPerByte require.Equal( @@ -148,8 +158,11 @@ func Test_IStakingContract(t *testing.T) { }) t.Run("invalid method", func(t *testing.T) { + // ARRANGE invalidMethodBytes := []byte("invalidMethod") + // ACT gasInvalidMethod := contract.RequiredGas(invalidMethodBytes) + // ASSERT require.Equal( t, uint64(0), @@ -185,6 +198,7 @@ func Test_Stake(t *testing.T) { methodID := abi.Methods[StakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) @@ -200,11 +214,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should stake", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -221,11 +239,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.NoError(t, err) }) t.Run("should fail if origin is not staker", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -243,11 +265,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{originEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.ErrorContains(t, err, "origin is not staker address") }) t.Run("should fail if staking fails", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -260,11 +286,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, int64(42)} + // ACT _, err := contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if wrong args amount", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -281,11 +311,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if staker is not eth addr", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -301,11 +335,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if validator is not valid string", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -322,11 +360,15 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if amount is not int64", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -343,7 +385,10 @@ func Test_Stake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + // ACT _, err = contract.Stake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) } @@ -353,6 +398,7 @@ func Test_Unstake(t *testing.T) { methodID := abi.Methods[UnstakeMethodName] t.Run("should fail if validator doesn't exist", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) @@ -368,11 +414,15 @@ func Test_Unstake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should unstake", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -394,11 +444,15 @@ func Test_Unstake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, args) require.NoError(t, err) + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.NoError(t, err) }) t.Run("should fail if origin is not staker", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -422,11 +476,15 @@ func Test_Unstake(t *testing.T) { originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) + // ACT _, err = contract.Unstake(ctx, originEthAddr, &methodID, args) + + // ASSERT require.ErrorContains(t, err, "origin is not staker address") }) t.Run("should fail if no previous staking", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -443,11 +501,15 @@ func Test_Unstake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if wrong args amount", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -464,11 +526,15 @@ func Test_Unstake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if staker is not eth addr", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -484,11 +550,15 @@ func Test_Unstake(t *testing.T) { args := []interface{}{staker, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if validator is not valid string", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -505,11 +575,15 @@ func Test_Unstake(t *testing.T) { args := []interface{}{stakerEthAddr, 42, coins.AmountOf(config.BaseDenom).BigInt()} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if amount is not int64", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -526,16 +600,20 @@ func Test_Unstake(t *testing.T) { args := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64()} + // ACT _, err = contract.Unstake(ctx, stakerAddr, &methodID, args) + + // ASSERT require.Error(t, err) }) } -func Test_TransferStake(t *testing.T) { +func Test_MoveStake(t *testing.T) { ctx, contract, abi, sdkKeepers := setup(t) - methodID := abi.Methods[TransferStakeMethodName] + methodID := abi.Methods[MoveStakeMethodName] t.Run("should fail if validator dest doesn't exist", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -562,18 +640,22 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt(), } - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) - t.Run("should transfer stake", func(t *testing.T) { + t.Run("should move stake", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -601,19 +683,23 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt(), } - // transfer stake to validator dest - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + // move stake to validator dest + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.NoError(t, err) }) t.Run("should fail if staker is invalid arg", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -641,18 +727,22 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ 42, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt(), } - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) t.Run("should fail if validator src is invalid arg", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -680,18 +770,22 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, 42, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt(), } - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) t.Run("should fail if validator dest is invalid arg", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -719,18 +813,22 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, 42, coins.AmountOf(config.BaseDenom).BigInt(), } - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) t.Run("should fail if amount is invalid arg", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -758,18 +856,22 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, coins.AmountOf(config.BaseDenom).Uint64(), } - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) t.Run("should fail if wrong args amount", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -797,13 +899,17 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} + argsMoveStake := []interface{}{stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress} - _, err = contract.TransferStake(ctx, stakerAddr, &methodID, argsTransferStake) + // ACT + _, err = contract.MoveStake(ctx, stakerAddr, &methodID, argsMoveStake) + + // ASSERT require.Error(t, err) }) t.Run("should fail if origin is not staker", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validatorSrc := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validatorSrc) @@ -831,7 +937,7 @@ func Test_TransferStake(t *testing.T) { _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, argsStake) require.NoError(t, err) - argsTransferStake := []interface{}{ + argsMoveStake := []interface{}{ stakerEthAddr, validatorSrc.OperatorAddress, validatorDest.OperatorAddress, @@ -839,7 +945,11 @@ func Test_TransferStake(t *testing.T) { } originEthAddr := common.BytesToAddress(sample.Bech32AccAddress().Bytes()) - _, err = contract.TransferStake(ctx, originEthAddr, &methodID, argsTransferStake) + + // ACT + _, err = contract.MoveStake(ctx, originEthAddr, &methodID, argsMoveStake) + + // ASSERT require.ErrorContains(t, err, "origin is not staker") }) } @@ -849,7 +959,10 @@ func Test_GetAllValidators(t *testing.T) { methodID := abi.Methods[GetAllValidatorsMethodName] t.Run("should return empty array if validators not set", func(t *testing.T) { + // ACT validators, err := contract.GetAllValidators(ctx, &methodID) + + // ASSERT require.NoError(t, err) res, err := methodID.Outputs.Unpack(validators) @@ -859,11 +972,15 @@ func Test_GetAllValidators(t *testing.T) { }) t.Run("should return validators if set", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) + // ACT validators, err := contract.GetAllValidators(ctx, &methodID) + + // ASSERT require.NoError(t, err) res, err := methodID.Outputs.Unpack(validators) @@ -878,6 +995,7 @@ func Test_GetShares(t *testing.T) { methodID := abi.Methods[GetSharesMethodName] t.Run("should return stakes", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) sdkKeepers.StakingKeeper.SetValidator(ctx, validator) @@ -895,9 +1013,12 @@ func Test_GetShares(t *testing.T) { stakeArgs := []interface{}{stakerEthAddr, validator.OperatorAddress, coins.AmountOf(config.BaseDenom).BigInt()} stakeMethodID := abi.Methods[StakeMethodName] + + // ACT _, err = contract.Stake(ctx, stakerAddr, &stakeMethodID, stakeArgs) require.NoError(t, err) + // ASSERT args := []interface{}{stakerEthAddr, validator.OperatorAddress} stakes, err := contract.GetShares(ctx, &methodID, args) require.NoError(t, err) @@ -912,29 +1033,41 @@ func Test_GetShares(t *testing.T) { }) t.Run("should fail if wrong args amount", func(t *testing.T) { + // ARRANGE staker := sample.Bech32AccAddress() stakerEthAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr} + + // ACT _, err := contract.GetShares(ctx, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if invalid staker arg", func(t *testing.T) { + // ARRANGE r := rand.New(rand.NewSource(42)) validator := sample.Validator(t, r) - args := []interface{}{42, validator.OperatorAddress} + + // ACT _, err := contract.GetShares(ctx, &methodID, args) + + // ASSERT require.Error(t, err) }) t.Run("should fail if invalid val address", func(t *testing.T) { + // ARRANGE staker := sample.Bech32AccAddress() stakerEthAddr := common.BytesToAddress(staker.Bytes()) - args := []interface{}{stakerEthAddr, staker.String()} + + // ACT _, err := contract.GetShares(ctx, &methodID, args) + + // ASSERT require.Error(t, err) }) } From 09f35e8900590f4f8ad93808f52c208d7a962a5e Mon Sep 17 00:00:00 2001 From: skosito Date: Mon, 2 Sep 2024 15:51:10 +0200 Subject: [PATCH 26/27] add todo with issue --- precompiles/prototype/prototype.go | 1 + precompiles/staking/staking.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/precompiles/prototype/prototype.go b/precompiles/prototype/prototype.go index 730ee6008c..123885ccb4 100644 --- a/precompiles/prototype/prototype.go +++ b/precompiles/prototype/prototype.go @@ -41,6 +41,7 @@ func initABI() { var methodID [4]byte copy(methodID[:], ABI.Methods[methodName].ID[:4]) switch methodName { + // TODO: https://github.com/zeta-chain/node/issues/2812 case Bech32ToHexAddrMethodName: GasRequiredByMethod[methodID] = 500 case Bech32ifyMethodName: diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index c0e6649ae6..e85a7719e1 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -49,7 +49,8 @@ func initABI() { var methodID [4]byte copy(methodID[:], ABI.Methods[methodName].ID[:4]) switch methodName { - // TODO: just temporary flat values, double check these flat values + // TODO: https://github.com/zeta-chain/node/issues/2812 + // just temporary flat values, double check these flat values // can we just use WriteCostFlat/ReadCostFlat from gas config for flat values? case StakeMethodName: GasRequiredByMethod[methodID] = 10000 From f88bfa15427c8379ee1be1dd3063d2638b0db1a9 Mon Sep 17 00:00:00 2001 From: skosito Date: Wed, 4 Sep 2024 11:35:27 +0200 Subject: [PATCH 27/27] PR comment and bug fix with validators append --- precompiles/staking/staking.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/precompiles/staking/staking.go b/precompiles/staking/staking.go index e85a7719e1..308a1968f3 100644 --- a/precompiles/staking/staking.go +++ b/precompiles/staking/staking.go @@ -17,6 +17,7 @@ import ( ptypes "github.com/zeta-chain/node/precompiles/types" ) +// method names const ( // write StakeMethodName = "stake" @@ -128,13 +129,13 @@ func (c *Contract) GetAllValidators( validators := c.stakingKeeper.GetAllValidators(ctx) validatorsRes := make([]Validator, len(validators)) - for _, v := range validators { - validatorsRes = append(validatorsRes, Validator{ + for i, v := range validators { + validatorsRes[i] = Validator{ OperatorAddress: v.OperatorAddress, ConsensusPubKey: v.ConsensusPubkey.String(), BondStatus: uint8(v.Status), Jailed: v.Jailed, - }) + } } return method.Outputs.Pack(validatorsRes)