From b71e6e892f69b33c2bb489d762957de422404edc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 20:30:00 +0200 Subject: [PATCH] (feat) Add possibility to transfer entire balance. (backport #6877) (#6929) * (feat) Add possibility to transfer entire balance. (#6877) * Add possibility to transfer entire balance. * Added entry to the Changelog. * Added e2e test * Added forwarding * Update modules/apps/transfer/keeper/relay.go Co-authored-by: DimitrisJim * Move UnboundedSpendLimit to token.go * add documentation * add test to compatibility matrices * PR Feedback. --------- Co-authored-by: DimitrisJim Co-authored-by: Carlos Rodriguez (cherry picked from commit 92e1f387b0c222dd2cc44c7df7981b0ca96a27f5) # Conflicts: # .github/compatibility-test-matrices/main/transfer-v2-multidenom-chain-a.json # .github/compatibility-test-matrices/release-v9.0.x/transfer-v2-multidenom-chain-a.json # .github/compatibility-test-matrices/unreleased/transfer-v2-multidenom.json # CHANGELOG.md # docs/docs/02-apps/01-transfer/04-messages.md # docs/docs/02-apps/01-transfer/10-ICS20-v1/04-messages.md # e2e/tests/transfer/base_test.go # modules/apps/transfer/keeper/relay.go # modules/apps/transfer/keeper/relay_test.go # modules/apps/transfer/types/token.go # modules/apps/transfer/types/transfer_authorization.go # testing/chain.go * fix conflicts * lint * delete docs --------- Co-authored-by: Nikolas De Giorgis Co-authored-by: Carlos Rodriguez --- .../main/transfer-v2-multidenom-chain-a.json | 19 +++++++++++++++++++ .../transfer-v2-multidenom-chain-a.json | 19 +++++++++++++++++++ .../unreleased/transfer-v2-multidenom.json | 19 +++++++++++++++++++ CHANGELOG.md | 2 ++ modules/apps/transfer/keeper/relay.go | 5 +++++ modules/apps/transfer/keeper/relay_test.go | 9 +++++++++ modules/apps/transfer/types/coin.go | 17 +++++++++++++++-- .../transfer/types/transfer_authorization.go | 14 -------------- testing/chain.go | 6 +++++- 9 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 .github/compatibility-test-matrices/main/transfer-v2-multidenom-chain-a.json create mode 100644 .github/compatibility-test-matrices/release-v9.0.x/transfer-v2-multidenom-chain-a.json create mode 100644 .github/compatibility-test-matrices/unreleased/transfer-v2-multidenom.json diff --git a/.github/compatibility-test-matrices/main/transfer-v2-multidenom-chain-a.json b/.github/compatibility-test-matrices/main/transfer-v2-multidenom-chain-a.json new file mode 100644 index 00000000000..fb04ec8ba4d --- /dev/null +++ b/.github/compatibility-test-matrices/main/transfer-v2-multidenom-chain-a.json @@ -0,0 +1,19 @@ +{ + "chain-a": [ + "main" + ], + "chain-b": [ + "main" + ], + "entrypoint": [ + "TestTransferTestSuite" + ], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom", + "TestMsgTransfer_EntireBalance", + "TestMsgTransfer_Fails_InvalidAddress_MultiDenom" + ], + "relayer-type": [ + "hermes" + ] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/release-v9.0.x/transfer-v2-multidenom-chain-a.json b/.github/compatibility-test-matrices/release-v9.0.x/transfer-v2-multidenom-chain-a.json new file mode 100644 index 00000000000..e75a7b6c73e --- /dev/null +++ b/.github/compatibility-test-matrices/release-v9.0.x/transfer-v2-multidenom-chain-a.json @@ -0,0 +1,19 @@ +{ + "chain-a": [ + "release-v9.0.x" + ], + "chain-b": [ + "release-v9.0.x" + ], + "entrypoint": [ + "TestTransferTestSuite" + ], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom", + "TestMsgTransfer_EntireBalance", + "TestMsgTransfer_Fails_InvalidAddress_MultiDenom" + ], + "relayer-type": [ + "hermes" + ] +} \ No newline at end of file diff --git a/.github/compatibility-test-matrices/unreleased/transfer-v2-multidenom.json b/.github/compatibility-test-matrices/unreleased/transfer-v2-multidenom.json new file mode 100644 index 00000000000..da1134e3fa6 --- /dev/null +++ b/.github/compatibility-test-matrices/unreleased/transfer-v2-multidenom.json @@ -0,0 +1,19 @@ +{ + "chain-a": [ + "release-v9.0.x" + ], + "chain-b": [ + "release-v9.0.x" + ], + "entrypoint": [ + "TestTransferTestSuite" + ], + "test": [ + "TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom", + "TestMsgTransfer_EntireBalance", + "TestMsgTransfer_Fails_InvalidAddress_MultiDenom" + ], + "relayer-type": [ + "hermes" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 48fa8a167a8..99d5c70987a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (apps/transfer) [\#6877](https://github.com/cosmos/ibc-go/pull/6877) Added the possibility to transfer the entire user balance of a particular denomination by using [`UnboundedSpendLimit`](https://github.com/cosmos/ibc-go/blob/715f00eef8727da41db25fdd4763b709bdbba07e/modules/apps/transfer/types/transfer_authorization.go#L253-L255) as the token amount. + ### Bug Fixes ## [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0) - 2024-06-20 diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 29ea46a6b57..3a36b6c6580 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -93,6 +93,11 @@ func (k Keeper) sendTransfer( telemetry.NewLabel(coretypes.LabelDestinationChannel, destinationChannel), } + // Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom. + if token.Amount.Equal(types.UnboundedSpendLimit()) { + token.Amount = k.bankKeeper.GetBalance(ctx, sender, token.Denom).Amount + } + // NOTE: SendTransfer simply sends the denomination as it exists on its own // chain inside the packet data. The receiving chain will perform denom // prefixing as necessary. diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index e5eeb9a8ef6..c1ea80166ef 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -58,6 +58,15 @@ func (suite *KeeperTestSuite) TestSendTransfer() { memo = "memo" }, true, }, + { + "successful transfer of entire balance", + func() { + coin.Amount = types.UnboundedSpendLimit() + var ok bool + expEscrowAmount, ok = sdk.NewIntFromString(ibctesting.DefaultGenesisAccBalance) + suite.Require().True(ok) + }, true, + }, { "source channel not found", func() { diff --git a/modules/apps/transfer/types/coin.go b/modules/apps/transfer/types/coin.go index 6abc5367cbe..a8cebdb62c2 100644 --- a/modules/apps/transfer/types/coin.go +++ b/modules/apps/transfer/types/coin.go @@ -2,12 +2,16 @@ package types import ( "fmt" + "math/big" "strings" - "cosmossdk.io/math" + sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" ) +// maxUint256 is the maximum value for a 256 bit unsigned integer. +var maxUint256 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) + // SenderChainIsSource returns false if the denomination originally came // from the receiving chain and true otherwise. func SenderChainIsSource(sourcePort, sourceChannel, denom string) bool { @@ -42,7 +46,16 @@ func GetPrefixedDenom(portID, channelID, baseDenom string) string { // GetTransferCoin creates a transfer coin with the port ID and channel ID // prefixed to the base denom. -func GetTransferCoin(portID, channelID, baseDenom string, amount math.Int) sdk.Coin { +func GetTransferCoin(portID, channelID, baseDenom string, amount sdkmath.Int) sdk.Coin { denomTrace := ParseDenomTrace(GetPrefixedDenom(portID, channelID, baseDenom)) return sdk.NewCoin(denomTrace.IBCDenom(), amount) } + +// UnboundedSpendLimit returns the sentinel value that can be used +// as the amount for a denomination's spend limit for which spend limit updating +// should be disabled. Please note that using this sentinel value means that a grantee +// will be granted the privilege to do ICS20 token transfers for the total amount +// of the denomination available at the granter's account. +func UnboundedSpendLimit() sdkmath.Int { + return sdkmath.NewIntFromBigInt(maxUint256) +} diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index 1af6f9c9c43..8676c6fb679 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -1,10 +1,8 @@ package types import ( - "math/big" "strings" - sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/authz" @@ -15,9 +13,6 @@ import ( var _ authz.Authorization = &TransferAuthorization{} -// maxUint256 is the maximum value for a 256 bit unsigned integer. -var maxUint256 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) - // NewTransferAuthorization creates a new TransferAuthorization object. func NewTransferAuthorization(allocations ...Allocation) *TransferAuthorization { return &TransferAuthorization{ @@ -174,12 +169,3 @@ func validateMemo(ctx sdk.Context, memo string, allowedMemos []string) error { return sdkerrors.Wrapf(ErrInvalidAuthorization, "not allowed memo: %s", memo) } - -// UnboundedSpendLimit returns the sentinel value that can be used -// as the amount for a denomination's spend limit for which spend limit updating -// should be disabled. Please note that using this sentinel value means that a grantee -// will be granted the privilege to do ICS20 token transfers for the total amount -// of the denomination available at the granter's account. -func UnboundedSpendLimit() sdkmath.Int { - return sdk.NewIntFromBigInt(maxUint256) -} diff --git a/testing/chain.go b/testing/chain.go index b34003adc2d..7ad257b3a80 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -42,6 +42,10 @@ type SenderAccount struct { SenderAccount authtypes.AccountI } +const ( + DefaultGenesisAccBalance = "10000000000000000000" +) + // TestChain is a testing struct that wraps a simapp with the last TM Header, the current ABCI // header and the validators of the TestChain. It also contains a field called ChainID. This // is the clientID that *other* chains use to refer to this TestChain. The SenderAccount @@ -104,7 +108,7 @@ func NewTestChainWithValSet(t *testing.T, coord *Coordinator, chainID string, va for i := 0; i < MaxAccounts; i++ { senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), uint64(i), 0) - amount, ok := sdk.NewIntFromString("10000000000000000000") + amount, ok := sdk.NewIntFromString(DefaultGenesisAccBalance) require.True(t, ok) // add sender account