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") + }) +}