From d58dfa055b133fedf6865e68e0a746b0150ebed6 Mon Sep 17 00:00:00 2001 From: Carlos Rodriguez Date: Tue, 23 Jul 2024 22:45:29 +0200 Subject: [PATCH] fix conflicts --- CHANGELOG.md | 5 - e2e/tests/transfer/base_test.go | 644 ------------------ modules/apps/transfer/keeper/relay.go | 15 +- modules/apps/transfer/keeper/relay_test.go | 35 +- modules/apps/transfer/types/coin.go | 14 + modules/apps/transfer/types/token.go | 58 -- .../transfer/types/transfer_authorization.go | 33 - testing/chain.go | 7 +- 8 files changed, 23 insertions(+), 788 deletions(-) delete mode 100644 e2e/tests/transfer/base_test.go delete mode 100644 modules/apps/transfer/types/token.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 434f256364f..7b2f57f7b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,13 +46,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features -<<<<<<< HEAD -======= -* (apps/transfer) [\#6492](https://github.com/cosmos/ibc-go/pull/6492) Added new `Tokens` field to `MsgTransfer` to enable sending of multiple denoms, and deprecated the `Token` field. -* (apps/transfer) [\#6693](https://github.com/cosmos/ibc-go/pull/6693) Added new `Forwarding` field to `MsgTransfer` to enable forwarding tokens through multiple intermediary chains with a single transaction. This also enables automatic unwinding of tokens to their native chain. `x/authz` support for transfer allows granters to specify a set of possible forwarding hops that are allowed for grantees. * (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. ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) ### Bug Fixes ## [v7.6.0](https://github.com/cosmos/ibc-go/releases/tag/v7.6.0) - 2024-06-20 diff --git a/e2e/tests/transfer/base_test.go b/e2e/tests/transfer/base_test.go deleted file mode 100644 index 745a0d57f07..00000000000 --- a/e2e/tests/transfer/base_test.go +++ /dev/null @@ -1,644 +0,0 @@ -//go:build !test_e2e - -package transfer - -import ( - "context" - "testing" - "time" - - "github.com/strangelove-ventures/interchaintest/v8/ibc" - test "github.com/strangelove-ventures/interchaintest/v8/testutil" - testifysuite "github.com/stretchr/testify/suite" - - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ibc-go/e2e/testsuite" - "github.com/cosmos/ibc-go/e2e/testsuite/query" - "github.com/cosmos/ibc-go/e2e/testvalues" - transfertypes "github.com/cosmos/ibc-go/v9/modules/apps/transfer/types" -) - -func TestTransferTestSuite(t *testing.T) { - testifysuite.Run(t, new(TransferTestSuite)) -} - -type TransferTestSuite struct { - transferTester -} - -// transferTester defines some helper functions that can be used in various test suites -// that test transfer functionality. -type transferTester struct { - testsuite.E2ETestSuite -} - -// QueryTransferParams queries the on-chain send enabled param for the transfer module -func (s *transferTester) QueryTransferParams(ctx context.Context, chain ibc.Chain) transfertypes.Params { - res, err := query.GRPCQuery[transfertypes.QueryParamsResponse](ctx, chain, &transfertypes.QueryParamsRequest{}) - s.Require().NoError(err) - return *res.Params -} - -// CreateTransferPath sets up a path between chainA and chainB with a transfer channel and returns the relayer wired -// up to watch the channel and port IDs created. -func (s *transferTester) CreateTransferPath(testName string) (ibc.Relayer, ibc.ChannelOutput) { - relayer, channel := s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName) - s.T().Logf("test %s running on portID %s channelID %s", testName, channel.PortID, channel.ChannelID) - return relayer, channel -} - -// TestMsgTransfer_Succeeds_Nonincentivized will test sending successful IBC transfers from chainA to chainB. -// The transfer will occur over a basic transfer channel (non incentivized) and both native and non-native tokens -// will be sent forwards and backwards in the IBC transfer timeline (both chains will act as source and receiver chains). -func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - - // NOTE: t.Parallel() should be called before SetupPath in all tests. - // t.Name() must be stored in a variable before t.Parallel() otherwise t.Name() is not - // deterministic. - t.Parallel() - - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainBVersion := chainB.Config().Images[0].Version - chainADenom := chainA.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - chainBAddress := chainBWallet.FormattedAddress() - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - // TODO: https://github.com/cosmos/ibc-go/issues/6743 - // t.Run("ensure capability module BeginBlock is executed", func(t *testing.T) { - // // by restarting the chain we ensure that the capability module's BeginBlocker is executed. - // s.Require().NoError(chainA.(*cosmos.CosmosChain).StopAllNodes(ctx)) - // s.Require().NoError(chainA.(*cosmos.CosmosChain).StartAllNodes(ctx)) - // s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA), "failed to wait for blocks") - // }) - - t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - - // TODO: cannot query total escrow if tests in parallel are using the same denom. - // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) { - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - // s.Require().NoError(err) - // - // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) - // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) - // } - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - - if testvalues.TokenMetadataFeatureReleases.IsSupported(chainBVersion) { - t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { - s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) - }) - } - - t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferCoins(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - s.Require().Equal(sdkmath.ZeroInt(), actualBalance) - - // https://github.com/cosmos/ibc-go/issues/6742 - // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainBVersion) { - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBIBCToken.IBCDenom()) - // s.Require().NoError(err) - // s.Require().Equal(sdk.NewCoin(chainBIBCToken.IBCDenom(), sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because sending chain is not source for tokens - // } - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) - - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - s.Require().Equal(expected, actualBalance) - }) - - // https://github.com/cosmos/ibc-go/issues/6742 - // if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) { - // t.Run("tokens are un-escrowed", func(t *testing.T) { - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - // s.Require().NoError(err) - // s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back - // }) - // } -} - -// TestMsgTransfer_Succeeds_MultiDenom will test sending successful IBC transfers from chainA to chainB. -// A multidenom transfer with native chainB tokens and IBC tokens from chainA is executed from chainB to chainA. -func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized_MultiDenom() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainADenom := chainA.Config().Denom - chainBDenom := chainB.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - chainBAddress := chainBWallet.FormattedAddress() - - chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) - chainAIBCToken := testsuite.GetIBCToken(chainBDenom, channelA.PortID, channelA.ChannelID) - - t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - - t.Run("native chainA tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - - // https://github.com/cosmos/ibc-go/issues/6742 - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - // s.Require().NoError(err) - // - // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) - // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - - t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { - s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) - }) - - // send the native chainB denom and also the ibc token from chainA - transferCoins := []sdk.Coin{ - testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), - testvalues.DefaultTransferAmount(chainBDenom), - } - - t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) - - t.Run("chain A native denom", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("chain B IBC denom", func(t *testing.T) { - actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - }) - - // https://github.com/cosmos/ibc-go/issues/6742 - // t.Run("native chainA tokens are un-escrowed", func(t *testing.T) { - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - // s.Require().NoError(err) - // s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back - // }) -} - -// TestMsgTransfer_Fails_InvalidAddress_MultiDenom attempts to send a multidenom IBC transfer -// to an invalid address and ensures that the tokens on the sending chain are returned to the sender. -func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress_MultiDenom() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainADenom := chainA.Config().Denom - chainBDenom := chainB.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - chainBAddress := chainBWallet.FormattedAddress() - - chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) - - t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(testvalues.DefaultTransferAmount(chainADenom)), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - - t.Run("native chainA tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - - // https://github.com/cosmos/ibc-go/issues/6742 - // actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom) - // s.Require().NoError(err) - // - // expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount)) - // s.Require().Equal(expectedTotalEscrow, actualTotalEscrow) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - - t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) { - s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA) - }) - - // send the native chainB denom and also the ibc token from chainA - transferCoins := []sdk.Coin{ - testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), - testvalues.DefaultTransferAmount(chainBDenom), - } - - t.Run("stop relayer", func(t *testing.T) { - s.StopRelayer(ctx, relayer) - }) - - t.Run("native token from chain B and non-native IBC token from chainA, both to chainA", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") - - t.Run("tokens are sent from chain B", func(t *testing.T) { - t.Run("native chainB tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("non-native chainA IBC denom are burned", func(t *testing.T) { - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - s.Require().Equal(int64(0), actualBalance.Int64()) - }) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) - }) - - t.Run("tokens are returned to sender on chainB", func(t *testing.T) { - t.Run("native chainB denom", func(t *testing.T) { - actualBalance, err := s.GetChainBNativeBalance(ctx, chainBWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("non-native chainA IBC denom", func(t *testing.T) { - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - s.Require().NoError(err) - - expected := testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance.Int64()) - }) - }) -} - -// TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures -// that the tokens on the sending chain are unescrowed. -func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainADenom := chainA.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - - t.Run("native IBC token transfer from chainA to invalid address", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - t.Run("packets are relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - }) - - t.Run("token transfer amount unescrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - s.Require().Equal(expected, actualBalance) - }) -} - -func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, _ := s.GetChains() - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - - chainBWalletAmount := ibc.WalletAmount{ - Address: chainBWallet.FormattedAddress(), // destination address - Denom: chainA.Config().Denom, - Amount: sdkmath.NewInt(testvalues.IBCTransferAmount), - } - - t.Run("IBC transfer packet timesout", func(t *testing.T) { - tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()}) - s.Require().NoError(err) - s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid") - time.Sleep(time.Nanosecond * 1) // want it to timeout immediately - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - t.Run("ensure escrowed tokens have been refunded to sender due to timeout", func(t *testing.T) { - // ensure destination address did not receive any tokens - bal, err := s.GetChainBNativeBalance(ctx, chainBWallet) - s.Require().NoError(err) - s.Require().Equal(testvalues.StartingTokenAmount, bal) - - // ensure that the sender address has been successfully refunded the full amount - bal, err = s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - s.Require().Equal(testvalues.StartingTokenAmount, bal) - }) -} - -// This can be used to test sending with a transfer packet with a memo given different combinations of -// ibc-go versions. -// -// TestMsgTransfer_WithMemo will test sending IBC transfers from chainA to chainB -// If the chains contain a version of FungibleTokenPacketData with memo, both send and receive should succeed. -// If one of the chains contains a version of FungibleTokenPacketData without memo, then receiving a packet with -// memo should fail in that chain -func (s *TransferTestSuite) TestMsgTransfer_WithMemo() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainADenom := chainA.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - chainBAddress := chainBWallet.FormattedAddress() - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - - t.Run("IBC token transfer with memo from chainA to chainB", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferCoins(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo", nil) - s.AssertTxSuccess(transferTxResp) - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount - s.Require().Equal(expected, actualBalance) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) - - t.Run("packets relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - - s.Require().NoError(err) - s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64()) - }) -} - -// TestMsgTransfer_EntireBalance tests that it is possible to transfer the entire balance -// of a given denom by using types.UnboundedSpendLimit as the amount. -func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() { - t := s.T() - ctx := context.TODO() - - testName := t.Name() - t.Parallel() - relayer, channelA := s.CreateTransferPath(testName) - - chainA, chainB := s.GetChains() - - chainADenom := chainA.Config().Denom - - chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) - chainAAddress := chainAWallet.FormattedAddress() - - chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) - chainBAddress := chainBWallet.FormattedAddress() - - coinFromA := testvalues.DefaultTransferAmount(chainADenom) - - s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") - - t.Run("IBC token transfer from chainA to chainB", func(t *testing.T) { - transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, sdk.NewCoins(coinFromA), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainA), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - t.Run("tokens are escrowed", func(t *testing.T) { - actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) - s.Require().NoError(err) - - s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance) - }) - - t.Run("start relayer", func(t *testing.T) { - s.StartRelayer(relayer, testName) - }) - - chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) - - t.Run("packets relayed", func(t *testing.T) { - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - - s.Require().NoError(err) - s.Require().Equal(coinFromA.Amount.Int64(), actualBalance.Int64()) - - actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainADenom) - - s.Require().NoError(err) - s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance.Int64()) - }) - - t.Run("send entire balance from B to A", func(t *testing.T) { - transferCoins := sdk.NewCoins((sdk.NewCoin(chainBIBCToken.IBCDenom(), transfertypes.UnboundedSpendLimit())), sdk.NewCoin(chainB.Config().Denom, transfertypes.UnboundedSpendLimit())) - transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoins, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainB), 0, "", nil) - s.AssertTxSuccess(transferTxResp) - }) - - chainAIBCToken := testsuite.GetIBCToken(chainB.Config().Denom, channelA.PortID, channelA.ChannelID) - t.Run("packets relayed", func(t *testing.T) { - // test that chainA has the entire balance back of its native token. - s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) - actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainADenom) - - s.Require().NoError(err) - s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64()) - - // test that chainA has the entirety of chainB's token IBC denom. - actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainAIBCToken.IBCDenom()) - - s.Require().NoError(err) - s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64()) - - // Tests that chainB has a zero balance for both. - actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom()) - - s.Require().NoError(err) - s.Require().Zero(actualBalance.Int64()) - - actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainB.Config().Denom) - - s.Require().NoError(err) - s.Require().Zero(actualBalance.Int64()) - }) -} diff --git a/modules/apps/transfer/keeper/relay.go b/modules/apps/transfer/keeper/relay.go index 5a3f7f59c9d..3a36b6c6580 100644 --- a/modules/apps/transfer/keeper/relay.go +++ b/modules/apps/transfer/keeper/relay.go @@ -77,22 +77,12 @@ func (k Keeper) sendTransfer( // NOTE: denomination and hex hash correctness checked during msg.ValidateBasic fullDenomPath := token.Denom -<<<<<<< HEAD var err error // deconstruct the token denomination into the denomination trace info // to determine if the sender is the source chain if strings.HasPrefix(token.Denom, "ibc/") { fullDenomPath, err = k.DenomPathFromHash(ctx, token.Denom) -======= - for _, coin := range coins { - // Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom. - if coin.Amount.Equal(types.UnboundedSpendLimit()) { - coin.Amount = k.bankKeeper.GetBalance(ctx, sender, coin.Denom).Amount - } - - token, err := k.tokenFromCoin(ctx, coin) ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) if err != nil { return 0, err } @@ -103,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 64ac7347d58..48e268945e1 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -59,45 +59,16 @@ func (suite *KeeperTestSuite) TestSendTransfer() { }, true, }, { -<<<<<<< HEAD - "source channel not found", -======= - "successful transfer of native token with ics20-1", - func() { - coins = sdk.NewCoins(coins[0]) - - // Set version to isc20-1. - path.EndpointA.UpdateChannel(func(channel *channeltypes.Channel) { - channel.Version = types.V1 - }) - }, - nil, - }, - { - "successful transfer with empty forwarding hops and ics20-1", - func() { - coins = sdk.NewCoins(coins[0]) - - // Set version to isc20-1. - path.EndpointA.UpdateChannel(func(channel *channeltypes.Channel) { - channel.Version = types.V1 - }) - }, - nil, - }, - { "successful transfer of entire balance", func() { - coins = sdk.NewCoins(sdk.NewCoin(coins[0].Denom, types.UnboundedSpendLimit())) + coin.Amount = types.UnboundedSpendLimit() var ok bool - expEscrowAmounts[0], ok = sdkmath.NewIntFromString(ibctesting.DefaultGenesisAccBalance) + expEscrowAmount, ok = sdk.NewIntFromString(ibctesting.DefaultGenesisAccBalance) suite.Require().True(ok) - }, - nil, + }, true, }, { "failure: source channel not found", ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) func() { // channel references wrong ID path.EndpointA.ChannelID = ibctesting.InvalidID diff --git a/modules/apps/transfer/types/coin.go b/modules/apps/transfer/types/coin.go index 6abc5367cbe..8155f2b7e1d 100644 --- a/modules/apps/transfer/types/coin.go +++ b/modules/apps/transfer/types/coin.go @@ -2,12 +2,17 @@ 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 { @@ -46,3 +51,12 @@ func GetTransferCoin(portID, channelID, baseDenom string, amount math.Int) sdk.C 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/token.go b/modules/apps/transfer/types/token.go deleted file mode 100644 index d30a765f0f2..00000000000 --- a/modules/apps/transfer/types/token.go +++ /dev/null @@ -1,58 +0,0 @@ -package types - -import ( - "math/big" - - errorsmod "cosmossdk.io/errors" - sdkmath "cosmossdk.io/math" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Tokens is a slice of Tokens -type Tokens []Token - -// 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)) - -// Validate validates a token denomination and amount. -func (t Token) Validate() error { - if err := t.Denom.Validate(); err != nil { - return errorsmod.Wrap(err, "invalid token denom") - } - - amount, ok := sdkmath.NewIntFromString(t.Amount) - if !ok { - return errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", t.Amount) - } - - if !amount.IsPositive() { - return errorsmod.Wrapf(ErrInvalidAmount, "amount must be strictly positive: got %d", amount) - } - - return nil -} - -// ToCoin converts a Token to an sdk.Coin. -// -// The function parses the Amount field of the Token into an sdkmath.Int and returns a new sdk.Coin with -// the IBCDenom of the Token's Denom field and the parsed Amount. -// If the Amount cannot be parsed, an error is returned with a wrapped error message. -func (t Token) ToCoin() (sdk.Coin, error) { - transferAmount, ok := sdkmath.NewIntFromString(t.Amount) - if !ok { - return sdk.Coin{}, errorsmod.Wrapf(ErrInvalidAmount, "unable to parse transfer amount (%s) into math.Int", transferAmount) - } - - coin := sdk.NewCoin(t.Denom.IBCDenom(), transferAmount) - return coin, nil -} - -// 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 77bddece4fa..8676c6fb679 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -1,21 +1,8 @@ package types import ( -<<<<<<< HEAD - "math/big" "strings" - sdkmath "cosmossdk.io/math" -======= - "context" - "slices" - "strings" - - "github.com/cosmos/gogoproto/proto" - - errorsmod "cosmossdk.io/errors" - ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/authz" @@ -182,23 +169,3 @@ func validateMemo(ctx sdk.Context, memo string, allowedMemos []string) error { return sdkerrors.Wrapf(ErrInvalidAuthorization, "not allowed memo: %s", memo) } - -<<<<<<< HEAD -// 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) -======= -// getAllocationIndex ranges through a set of allocations, and returns the index of the allocation if found. If not, returns -1. -func getAllocationIndex(msg MsgTransfer, allocations []Allocation) int { - for index, allocation := range allocations { - if allocation.SourceChannel == msg.SourceChannel && allocation.SourcePort == msg.SourcePort { - return index - } - } - return allocationNotFound ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) -} diff --git a/testing/chain.go b/testing/chain.go index d26991ebe0a..7ad257b3a80 100644 --- a/testing/chain.go +++ b/testing/chain.go @@ -108,13 +108,8 @@ 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) -<<<<<<< HEAD - amount, ok := sdk.NewIntFromString("10000000000000000000") + amount, ok := sdk.NewIntFromString(DefaultGenesisAccBalance) require.True(t, ok) -======= - amount, ok := sdkmath.NewIntFromString(DefaultGenesisAccBalance) - require.True(tb, ok) ->>>>>>> 92e1f387 ((feat) Add possibility to transfer entire balance. (#6877)) // add sender account balance := banktypes.Balance{