From 034f47210dca7bfe4a74ba9af6675cb901a64095 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 9 Apr 2024 12:52:03 +0100 Subject: [PATCH] Update MsgTransfer to accept sdk.Coins instead of sdk.Coin (#6113) --- e2e/tests/transfer/authz_test.go | 88 ++++++----- e2e/tests/transfer/incentivized_test.go | 4 +- e2e/tests/transfer/upgrades_test.go | 4 +- e2e/tests/upgrades/upgrade_test.go | 2 +- e2e/testsuite/tx.go | 2 +- e2e/testvalues/values.go | 4 + .../host/keeper/relay_test.go | 38 ++--- modules/apps/29-fee/keeper/events_test.go | 2 +- modules/apps/29-fee/transfer_test.go | 4 +- modules/apps/callbacks/ibc_middleware_test.go | 2 +- modules/apps/callbacks/replay_test.go | 2 +- modules/apps/callbacks/transfer_test.go | 4 +- modules/apps/transfer/client/cli/tx.go | 2 +- .../apps/transfer/keeper/invariants_test.go | 2 +- .../apps/transfer/keeper/mbt_relay_test.go | 2 +- modules/apps/transfer/keeper/msg_server.go | 15 +- .../apps/transfer/keeper/msg_server_test.go | 2 +- modules/apps/transfer/keeper/relay_test.go | 12 +- modules/apps/transfer/transfer_test.go | 6 +- modules/apps/transfer/types/msgs.go | 59 +++++++- modules/apps/transfer/types/msgs_test.go | 48 +++--- .../transfer/types/transfer_authorization.go | 7 +- .../types/transfer_authorization_test.go | 22 +-- modules/apps/transfer/types/tx.pb.go | 137 +++++++++++++----- proto/ibc/applications/transfer/v1/tx.proto | 2 + 25 files changed, 302 insertions(+), 170 deletions(-) diff --git a/e2e/tests/transfer/authz_test.go b/e2e/tests/transfer/authz_test.go index dd390ad3da5..33297e0766d 100644 --- a/e2e/tests/transfer/authz_test.go +++ b/e2e/tests/transfer/authz_test.go @@ -104,16 +104,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { t.Run("broadcast MsgGrant", createMsgGrantFn) t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: testvalues.DefaultTransferAmount(chainADenom), - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -161,16 +163,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_MsgTransfer_Succeeds() { }) t.Run("exec unauthorized MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: testvalues.DefaultTransferAmount(chainADenom), - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + testvalues.DefaultTransferCoins(chainADenom), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -241,16 +245,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { const invalidSpendAmount = spendLimit + 1 t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)}, - Sender: granterAddress, - Receiver: receiverWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(invalidSpendAmount)}), + granterAddress, + receiverWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ @@ -298,16 +304,18 @@ func (suite *AuthzTransferTestSuite) TestAuthz_InvalidTransferAuthorizations() { invalidWalletAddress := invalidWallet.FormattedAddress() t.Run("broadcast MsgExec for ibc MsgTransfer", func(t *testing.T) { - transferMsg := transfertypes.MsgTransfer{ - SourcePort: channelA.PortID, - SourceChannel: channelA.ChannelID, - Token: sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)}, - Sender: granterAddress, - Receiver: invalidWalletAddress, - TimeoutHeight: suite.GetTimeoutHeight(ctx, chainB), - } - - protoAny, err := codectypes.NewAnyWithValue(&transferMsg) + transferMsg := transfertypes.NewMsgTransfer( + channelA.PortID, + channelA.ChannelID, + sdk.NewCoins(sdk.Coin{Denom: chainADenom, Amount: sdkmath.NewInt(spendLimit)}), + granterAddress, + invalidWalletAddress, + suite.GetTimeoutHeight(ctx, chainB), + 0, + "", + ) + + protoAny, err := codectypes.NewAnyWithValue(transferMsg) suite.Require().NoError(err) msgExec := &authz.MsgExec{ diff --git a/e2e/tests/transfer/incentivized_test.go b/e2e/tests/transfer/incentivized_test.go index 1902c9e0c66..b8818a00002 100644 --- a/e2e/tests/transfer/incentivized_test.go +++ b/e2e/tests/transfer/incentivized_test.go @@ -197,7 +197,7 @@ func (s *IncentivizedTransferTestSuite) TestMsgPayPacketFee_InvalidReceiverAccou transferAmount := testvalues.DefaultTransferAmount(chainADenom) t.Run("send IBC transfer", func(t *testing.T) { - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "") txResp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgTransfer) // this message should be successful, as receiver account is not validated on the sending chain. s.AssertTxSuccess(txResp) @@ -322,7 +322,7 @@ func (s *IncentivizedTransferTestSuite) TestMultiMsg_MsgPayPacketFeeSingleSender }) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) diff --git a/e2e/tests/transfer/upgrades_test.go b/e2e/tests/transfer/upgrades_test.go index c1bd43898dd..74f5593d611 100644 --- a/e2e/tests/transfer/upgrades_test.go +++ b/e2e/tests/transfer/upgrades_test.go @@ -13,6 +13,8 @@ import ( sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/e2e/testsuite" "github.com/cosmos/ibc-go/e2e/testvalues" feetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" @@ -195,7 +197,7 @@ func (s *TransferChannelUpgradesTestSuite) TestChannelUpgrade_WithFeeMiddleware_ transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) }) diff --git a/e2e/tests/upgrades/upgrade_test.go b/e2e/tests/upgrades/upgrade_test.go index c3d56fc4f76..494a73a4511 100644 --- a/e2e/tests/upgrades/upgrade_test.go +++ b/e2e/tests/upgrades/upgrade_test.go @@ -960,7 +960,7 @@ func (s *UpgradeTestSuite) TestV8ToV8_1ChainUpgrade_ChannelUpgrades() { transferAmount := testvalues.DefaultTransferAmount(chainA.Config().Denom) msgPayPacketFee := feetypes.NewMsgPayPacketFee(testFee, channelA.PortID, channelA.ChannelID, chainAWallet.FormattedAddress(), nil) - msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, transferAmount, chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") + msgTransfer := transfertypes.NewMsgTransfer(channelA.PortID, channelA.ChannelID, sdk.NewCoins(transferAmount), chainAWallet.FormattedAddress(), chainBWallet.FormattedAddress(), s.GetTimeoutHeight(ctx, chainB), 0, "") resp := s.BroadcastMessages(ctx, chainA, chainAWallet, msgPayPacketFee, msgTransfer) s.AssertTxSuccess(resp) }) diff --git a/e2e/testsuite/tx.go b/e2e/testsuite/tx.go index ecbde19b3ad..b0ee8ab763b 100644 --- a/e2e/testsuite/tx.go +++ b/e2e/testsuite/tx.go @@ -265,7 +265,7 @@ func (s *E2ETestSuite) ExecuteGovV1Beta1Proposal(ctx context.Context, chain ibc. func (s *E2ETestSuite) Transfer(ctx context.Context, chain ibc.Chain, user ibc.Wallet, portID, channelID string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, ) sdk.TxResponse { - msg := transfertypes.NewMsgTransfer(portID, channelID, token, sender, receiver, timeoutHeight, timeoutTimestamp, memo) + msg := transfertypes.NewMsgTransfer(portID, channelID, sdk.NewCoins(token), sender, receiver, timeoutHeight, timeoutTimestamp, memo) return s.BroadcastMessages(ctx, chain, user, msg) } diff --git a/e2e/testvalues/values.go b/e2e/testvalues/values.go index b6f750ef7f7..07f262296f6 100644 --- a/e2e/testvalues/values.go +++ b/e2e/testvalues/values.go @@ -43,6 +43,10 @@ func DefaultTransferAmount(denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(IBCTransferAmount)} } +func DefaultTransferCoins(denom string) sdk.Coins { + return sdk.NewCoins(DefaultTransferAmount(denom)) +} + func TransferAmount(amount int64, denom string) sdk.Coin { return sdk.Coin{Denom: denom, Amount: sdkmath.NewInt(amount)} } diff --git a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go index ac5d41378e3..f49e623e939 100644 --- a/modules/apps/27-interchain-accounts/host/keeper/relay_test.go +++ b/modules/apps/27-interchain-accounts/host/keeper/relay_test.go @@ -342,15 +342,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - msg := &transfertypes.MsgTransfer{ - SourcePort: transferPath.EndpointA.ChannelConfig.PortID, - SourceChannel: transferPath.EndpointA.ChannelID, - Token: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), - Sender: interchainAccountAddr, - Receiver: suite.chainA.SenderAccount.GetAddress().String(), - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: uint64(0), - } + msg := transfertypes.NewMsgTransfer( + transferPath.EndpointA.ChannelConfig.PortID, + transferPath.EndpointA.ChannelID, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), + interchainAccountAddr, + suite.chainA.SenderAccount.GetAddress().String(), + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding) suite.Require().NoError(err) @@ -376,15 +377,16 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { interchainAccountAddr, found := suite.chainB.GetSimApp().ICAHostKeeper.GetInterchainAccountAddress(suite.chainB.GetContext(), ibctesting.FirstConnectionID, path.EndpointA.ChannelConfig.PortID) suite.Require().True(found) - msg := &transfertypes.MsgTransfer{ - SourcePort: transferPath.EndpointA.ChannelConfig.PortID, - SourceChannel: transferPath.EndpointA.ChannelID, - Token: sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), - Sender: interchainAccountAddr, - Receiver: "", - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - TimeoutTimestamp: uint64(0), - } + msg := transfertypes.NewMsgTransfer( + transferPath.EndpointA.ChannelConfig.PortID, + transferPath.EndpointA.ChannelID, + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), + interchainAccountAddr, + "", + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) data, err := icatypes.SerializeCosmosTx(suite.chainA.GetSimApp().AppCodec(), []proto.Message{msg}, encoding) suite.Require().NoError(err) diff --git a/modules/apps/29-fee/keeper/events_test.go b/modules/apps/29-fee/keeper/events_test.go index f4f49abcf0d..d70b8bfd229 100644 --- a/modules/apps/29-fee/keeper/events_test.go +++ b/modules/apps/29-fee/keeper/events_test.go @@ -113,7 +113,7 @@ func (suite *KeeperTestSuite) TestDistributeFeeEvent() { msgTransfer := transfertypes.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100))), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, "", ) diff --git a/modules/apps/29-fee/transfer_test.go b/modules/apps/29-fee/transfer_test.go index 79a0405fc0d..f76393bfcf8 100644 --- a/modules/apps/29-fee/transfer_test.go +++ b/modules/apps/29-fee/transfer_test.go @@ -30,7 +30,7 @@ func (suite *FeeTestSuite) TestFeeTransfer() { msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), } res, err := suite.chainA.SendMsgs(msgs...) suite.Require().NoError(err) // message committed @@ -138,7 +138,7 @@ func (suite *FeeTestSuite) TestTransferFeeUpgrade() { fee := types.NewFee(defaultRecvFee, defaultAckFee, defaultTimeoutFee) msgs := []sdk.Msg{ types.NewMsgPayPacketFee(fee, path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, suite.chainA.SenderAccount.GetAddress().String(), nil), - transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, ibctesting.TestCoin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), + transfertypes.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(ibctesting.TestCoin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, ""), } res, err := suite.chainA.SendMsgs(msgs...) diff --git a/modules/apps/callbacks/ibc_middleware_test.go b/modules/apps/callbacks/ibc_middleware_test.go index d041ea61b2c..180ca3eb056 100644 --- a/modules/apps/callbacks/ibc_middleware_test.go +++ b/modules/apps/callbacks/ibc_middleware_test.go @@ -463,7 +463,7 @@ func (s *CallbacksTestSuite) TestOnTimeoutPacket() { timeoutTimestamp := uint64(s.chainB.GetContext().BlockTime().UnixNano()) msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - ibctesting.TestCoin, s.chainA.SenderAccount.GetAddress().String(), + sdk.NewCoins(ibctesting.TestCoin), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.ZeroHeight(), timeoutTimestamp, fmt.Sprintf(`{"src_callback": {"address":"%s", "gas_limit":"%d"}}`, ibctesting.TestAccAddress, userGasLimit), // set user gas limit above panic level in mock contract keeper ) diff --git a/modules/apps/callbacks/replay_test.go b/modules/apps/callbacks/replay_test.go index d23de002613..04a8e5900fb 100644 --- a/modules/apps/callbacks/replay_test.go +++ b/modules/apps/callbacks/replay_test.go @@ -326,7 +326,7 @@ func (s *CallbacksTestSuite) ExecuteFailedTransfer(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, memo, diff --git a/modules/apps/callbacks/transfer_test.go b/modules/apps/callbacks/transfer_test.go index 4d288e96bbc..16698f074e6 100644 --- a/modules/apps/callbacks/transfer_test.go +++ b/modules/apps/callbacks/transfer_test.go @@ -189,7 +189,7 @@ func (s *CallbacksTestSuite) ExecuteTransfer(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 100), 0, memo, @@ -223,7 +223,7 @@ func (s *CallbacksTestSuite) ExecuteTransferTimeout(memo string) { msg := transfertypes.NewMsgTransfer( s.path.EndpointA.ChannelConfig.PortID, s.path.EndpointA.ChannelID, - amount, + sdk.NewCoins(amount), s.chainA.SenderAccount.GetAddress().String(), s.chainB.SenderAccount.GetAddress().String(), timeoutHeight, timeoutTimestamp, memo, diff --git a/modules/apps/transfer/client/cli/tx.go b/modules/apps/transfer/client/cli/tx.go index 497e56370d5..ccdd08af588 100644 --- a/modules/apps/transfer/client/cli/tx.go +++ b/modules/apps/transfer/client/cli/tx.go @@ -107,7 +107,7 @@ Relative timeout timestamp is added to the value of the user's local system cloc } msg := types.NewMsgTransfer( - srcPort, srcChannel, coin, sender, receiver, timeoutHeight, timeoutTimestamp, memo, + srcPort, srcChannel, sdk.NewCoins(coin), sender, receiver, timeoutHeight, timeoutTimestamp, memo, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, diff --git a/modules/apps/transfer/keeper/invariants_test.go b/modules/apps/transfer/keeper/invariants_test.go index ef1b6ae90ca..e150f9ef7c9 100644 --- a/modules/apps/transfer/keeper/invariants_test.go +++ b/modules/apps/transfer/keeper/invariants_test.go @@ -47,7 +47,7 @@ func (suite *KeeperTestSuite) TestTotalEscrowPerDenomInvariant() { msg := types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "", diff --git a/modules/apps/transfer/keeper/mbt_relay_test.go b/modules/apps/transfer/keeper/mbt_relay_test.go index fcb466dd468..72c5f0b1f41 100644 --- a/modules/apps/transfer/keeper/mbt_relay_test.go +++ b/modules/apps/transfer/keeper/mbt_relay_test.go @@ -348,7 +348,7 @@ func (suite *KeeperTestSuite) TestModelBasedRelay() { msg := types.NewMsgTransfer( tc.packet.SourcePort, tc.packet.SourceChannel, - sdk.NewCoin(denom, amount), + sdk.NewCoins(sdk.NewCoin(denom, amount)), sender.String(), tc.packet.Data.Receiver, suite.chainA.GetTimeoutHeight(), 0, // only use timeout height diff --git a/modules/apps/transfer/keeper/msg_server.go b/modules/apps/transfer/keeper/msg_server.go index c5171573226..94536cba937 100644 --- a/modules/apps/transfer/keeper/msg_server.go +++ b/modules/apps/transfer/keeper/msg_server.go @@ -26,8 +26,11 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. return nil, err } - if !k.bankKeeper.IsSendEnabledCoin(ctx, msg.Token) { - return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", msg.Token.Denom) + // TODO: replace with correct usage. + token := msg.GetTokens()[0] + + if !k.bankKeeper.IsSendEnabledCoin(ctx, token) { + return nil, errorsmod.Wrapf(types.ErrSendDisabled, "%s transfers are currently disabled", token.Denom) } if k.bankKeeper.BlockedAddr(sender) { @@ -35,21 +38,21 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types. } sequence, err := k.sendTransfer( - ctx, msg.SourcePort, msg.SourceChannel, msg.Token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, + ctx, msg.SourcePort, msg.SourceChannel, token, sender, msg.Receiver, msg.TimeoutHeight, msg.TimeoutTimestamp, msg.Memo) if err != nil { return nil, err } - k.Logger(ctx).Info("IBC fungible token transfer", "token", msg.Token.Denom, "amount", msg.Token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) + k.Logger(ctx).Info("IBC fungible token transfer", "token", token.Denom, "amount", token.Amount.String(), "sender", msg.Sender, "receiver", msg.Receiver) ctx.EventManager().EmitEvents(sdk.Events{ sdk.NewEvent( types.EventTypeTransfer, sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), sdk.NewAttribute(types.AttributeKeyReceiver, msg.Receiver), - sdk.NewAttribute(types.AttributeKeyAmount, msg.Token.Amount.String()), - sdk.NewAttribute(types.AttributeKeyDenom, msg.Token.Denom), + sdk.NewAttribute(types.AttributeKeyAmount, token.Amount.String()), + sdk.NewAttribute(types.AttributeKeyDenom, token.Denom), sdk.NewAttribute(types.AttributeKeyMemo, msg.Memo), ), sdk.NewEvent( diff --git a/modules/apps/transfer/keeper/msg_server_test.go b/modules/apps/transfer/keeper/msg_server_test.go index 55bc27a8a89..957ccf82c94 100644 --- a/modules/apps/transfer/keeper/msg_server_test.go +++ b/modules/apps/transfer/keeper/msg_server_test.go @@ -95,7 +95,7 @@ func (suite *KeeperTestSuite) TestMsgTransfer() { msg = types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, // only use timeout height "memo", ) diff --git a/modules/apps/transfer/keeper/relay_test.go b/modules/apps/transfer/keeper/relay_test.go index 1eb02bba9bd..24ab6f9eb69 100644 --- a/modules/apps/transfer/keeper/relay_test.go +++ b/modules/apps/transfer/keeper/relay_test.go @@ -126,7 +126,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { expEscrowAmount = sdkmath.ZeroInt() // create IBC token on chainA - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coin, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "") + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.NewCoins(coin), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "") result, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -141,7 +141,7 @@ func (suite *KeeperTestSuite) TestSendTransfer() { msg := types.NewMsgTransfer( path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, - coin, sender.String(), suite.chainB.SenderAccount.GetAddress().String(), + sdk.NewCoins(coin), sender.String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, // only use timeout height memo, ) @@ -206,7 +206,7 @@ func (suite *KeeperTestSuite) TestSendTransferSetsTotalEscrowAmountForSourceIBCT transferMsg := types.NewMsgTransfer( path1.EndpointA.ChannelConfig.PortID, path1.EndpointA.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), suite.chainB.GetTimeoutHeight(), 0, "", @@ -226,7 +226,7 @@ func (suite *KeeperTestSuite) TestSendTransferSetsTotalEscrowAmountForSourceIBCT msg := types.NewMsgTransfer( path2.EndpointB.ChannelConfig.PortID, path2.EndpointB.ChannelID, - coin, + sdk.NewCoins(coin), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), suite.chainA.GetTimeoutHeight(), 0, "", @@ -383,7 +383,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { if tc.recvIsSource { // send coin from chainB to chainA, receive them, acknowledge them, and send back to chainB coinFromBToA := sdk.NewCoin(sdk.DefaultBondDenom, sdkmath.NewInt(100)) - transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, coinFromBToA, suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, memo) + transferMsg := types.NewMsgTransfer(path.EndpointB.ChannelConfig.PortID, path.EndpointB.ChannelID, sdk.NewCoins(coinFromBToA), suite.chainB.SenderAccount.GetAddress().String(), suite.chainA.SenderAccount.GetAddress().String(), clienttypes.NewHeight(1, 110), 0, memo) res, err := suite.chainB.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed @@ -403,7 +403,7 @@ func (suite *KeeperTestSuite) TestOnRecvPacket() { // send coin from chainA to chainB coin := sdk.NewCoin(trace.IBCDenom(), amount) - transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, coin, suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, memo) + transferMsg := types.NewMsgTransfer(path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, sdk.NewCoins(coin), suite.chainA.SenderAccount.GetAddress().String(), receiver, clienttypes.NewHeight(1, 110), 0, memo) _, err := suite.chainA.SendMsgs(transferMsg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/transfer_test.go b/modules/apps/transfer/transfer_test.go index ef8dea295c6..1cb1261c0e3 100644 --- a/modules/apps/transfer/transfer_test.go +++ b/modules/apps/transfer/transfer_test.go @@ -52,7 +52,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { coinToSendToB := sdk.NewCoin(sdk.DefaultBondDenom, amount) // send from chainA to chainB - msg := types.NewMsgTransfer(pathAtoB.EndpointA.ChannelConfig.PortID, pathAtoB.EndpointA.ChannelID, coinToSendToB, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg := types.NewMsgTransfer(pathAtoB.EndpointA.ChannelConfig.PortID, pathAtoB.EndpointA.ChannelID, sdk.NewCoins(coinToSendToB), suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err := suite.chainA.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -82,7 +82,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { pathBtoC.Setup() // send from chainB to chainC - msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg = types.NewMsgTransfer(pathBtoC.EndpointA.ChannelConfig.PortID, pathBtoC.EndpointA.ChannelID, sdk.NewCoins(coinSentFromAToB), suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainB.SendMsgs(msg) suite.Require().NoError(err) // message committed @@ -105,7 +105,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() { suite.Require().Zero(balance.Amount.Int64()) // send from chainC back to chainB - msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") + msg = types.NewMsgTransfer(pathBtoC.EndpointB.ChannelConfig.PortID, pathBtoC.EndpointB.ChannelID, sdk.NewCoins(coinSentFromBToC), suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "") res, err = suite.chainC.SendMsgs(msg) suite.Require().NoError(err) // message committed diff --git a/modules/apps/transfer/types/msgs.go b/modules/apps/transfer/types/msgs.go index 2b045fee47e..2994e841951 100644 --- a/modules/apps/transfer/types/msgs.go +++ b/modules/apps/transfer/types/msgs.go @@ -45,19 +45,19 @@ func (msg MsgUpdateParams) ValidateBasic() error { // NewMsgTransfer creates a new MsgTransfer instance func NewMsgTransfer( sourcePort, sourceChannel string, - token sdk.Coin, sender, receiver string, + tokens sdk.Coins, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, memo string, ) *MsgTransfer { return &MsgTransfer{ SourcePort: sourcePort, SourceChannel: sourceChannel, - Token: token, Sender: sender, Receiver: receiver, TimeoutHeight: timeoutHeight, TimeoutTimestamp: timeoutTimestamp, Memo: memo, + Tokens: tokens, } } @@ -72,11 +72,13 @@ func (msg MsgTransfer) ValidateBasic() error { if err := host.ChannelIdentifierValidator(msg.SourceChannel); err != nil { return errorsmod.Wrap(err, "invalid source channel ID") } - if !msg.Token.IsValid() { - return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, msg.Token.String()) + + if len(msg.Tokens) == 0 && !isValidToken(msg.Token) { + return errorsmod.Wrap(ErrInvalidAmount, "either token or token array must be filled") } - if !msg.Token.IsPositive() { - return errorsmod.Wrap(ibcerrors.ErrInsufficientFunds, msg.Token.String()) + + if len(msg.Tokens) != 0 && isValidToken(msg.Token) { + return errorsmod.Wrap(ErrInvalidAmount, "cannot fill both token and token array") } _, err := sdk.AccAddressFromBech32(msg.Sender) @@ -92,5 +94,48 @@ func (msg MsgTransfer) ValidateBasic() error { if len(msg.Memo) > MaximumMemoLength { return errorsmod.Wrapf(ErrInvalidMemo, "memo must not exceed %d bytes", MaximumMemoLength) } - return ValidateIBCDenom(msg.Token.Denom) + + for _, token := range msg.GetTokens() { + if !isValidToken(token) { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.String()) + } + if err := ValidateIBCDenom(token.GetDenom()); err != nil { + return errorsmod.Wrap(ibcerrors.ErrInvalidCoins, token.Denom) + } + } + + return nil +} + +// GetTokens returns the tokens which will be transferred. +func (msg MsgTransfer) GetTokens() []sdk.Coin { + tokens := msg.Tokens + if isValidToken(msg.Token) { + tokens = []sdk.Coin{msg.Token} + } + return tokens +} + +// isValidToken returns true if the token provided is valid, +// and should be used to transfer tokens. +// this function is used in case the user constructs a sdk.Coin literal +// instead of using the construction function. +func isValidToken(coin sdk.Coin) bool { + if coin.IsNil() { + return false + } + + if strings.TrimSpace(coin.Denom) == "" { + return false + } + + if coin.Amount.IsZero() { + return false + } + + if coin.Amount.IsNegative() { + return false + } + + return true } diff --git a/modules/apps/transfer/types/msgs_test.go b/modules/apps/transfer/types/msgs_test.go index e9bb11cac54..e98400bfa45 100644 --- a/modules/apps/transfer/types/msgs_test.go +++ b/modules/apps/transfer/types/msgs_test.go @@ -38,11 +38,11 @@ var ( receiver = sdk.AccAddress("testaddr2").String() emptyAddr string - coin = sdk.NewCoin("atom", sdkmath.NewInt(100)) - ibcCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdkmath.NewInt(100)) - invalidIBCCoin = sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdkmath.NewInt(100)) - invalidDenomCoin = sdk.Coin{Denom: "0atom", Amount: sdkmath.NewInt(100)} - zeroCoin = sdk.Coin{Denom: "atoms", Amount: sdkmath.NewInt(0)} + coins = sdk.NewCoins(sdk.NewCoin("atom", sdkmath.NewInt(100))) + ibcCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdkmath.NewInt(100))) + invalidIBCCoins = sdk.NewCoins(sdk.NewCoin("ibc/7F1D3FCF4AE79E1554", sdkmath.NewInt(100))) + invalidDenomCoins = []sdk.Coin{{Denom: "0atom", Amount: sdkmath.NewInt(100)}} + zeroCoins = sdk.NewCoins(sdk.Coin{Denom: "atoms", Amount: sdkmath.NewInt(0)}) timeoutHeight = clienttypes.NewHeight(0, 10) ) @@ -54,22 +54,26 @@ func TestMsgTransferValidation(t *testing.T) { msg *types.MsgTransfer expPass bool }{ - {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), true}, - {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoin, sender, receiver, timeoutHeight, 0, ""), true}, - {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"too long memo", types.NewMsgTransfer(validPort, validChannel, coin, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), false}, - {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coin, sender, receiver, timeoutHeight, 0, ""), false}, - {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"zero coin", types.NewMsgTransfer(validPort, validChannel, zeroCoin, sender, receiver, timeoutHeight, 0, ""), false}, - {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coin, emptyAddr, receiver, timeoutHeight, 0, ""), false}, - {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coin, sender, "", timeoutHeight, 0, ""), false}, - {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coin, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), false}, - {"empty coin", types.NewMsgTransfer(validPort, validChannel, sdk.Coin{}, sender, receiver, timeoutHeight, 0, ""), false}, + {"valid msg with base denom", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), true}, + {"valid msg with trace hash", types.NewMsgTransfer(validPort, validChannel, ibcCoins, sender, receiver, timeoutHeight, 0, ""), true}, + {"multidenom", types.NewMsgTransfer(validPort, validChannel, coins.Add(ibcCoins...), sender, receiver, timeoutHeight, 0, ""), true}, + {"invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, invalidIBCCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too short port id", types.NewMsgTransfer(invalidShortPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long port id", types.NewMsgTransfer(invalidLongPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"port id contains non-alpha", types.NewMsgTransfer(invalidPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too short channel id", types.NewMsgTransfer(validPort, invalidShortChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long channel id", types.NewMsgTransfer(validPort, invalidLongChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"too long memo", types.NewMsgTransfer(validPort, validChannel, coins, sender, receiver, timeoutHeight, 0, ibctesting.GenerateString(types.MaximumMemoLength+1)), false}, + {"channel id contains non-alpha", types.NewMsgTransfer(validPort, invalidChannel, coins, sender, receiver, timeoutHeight, 0, ""), false}, + {"invalid denom", types.NewMsgTransfer(validPort, validChannel, invalidDenomCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, + {"missing sender address", types.NewMsgTransfer(validPort, validChannel, coins, emptyAddr, receiver, timeoutHeight, 0, ""), false}, + {"missing recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, "", timeoutHeight, 0, ""), false}, + {"too long recipient address", types.NewMsgTransfer(validPort, validChannel, coins, sender, ibctesting.GenerateString(types.MaximumReceiverLength+1), timeoutHeight, 0, ""), false}, + {"empty coins", types.NewMsgTransfer(validPort, validChannel, sdk.NewCoins(), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: invalid denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidDenomCoins...), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: invalid ibc denom", types.NewMsgTransfer(validPort, validChannel, coins.Add(invalidIBCCoins...), sender, receiver, timeoutHeight, 0, ""), false}, + {"multidenom: zero coins", types.NewMsgTransfer(validPort, validChannel, zeroCoins, sender, receiver, timeoutHeight, 0, ""), false}, } for i, tc := range testCases { @@ -87,7 +91,7 @@ func TestMsgTransferValidation(t *testing.T) { // TestMsgTransferGetSigners tests GetSigners for MsgTransfer func TestMsgTransferGetSigners(t *testing.T) { addr := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) - msg := types.NewMsgTransfer(validPort, validChannel, coin, addr.String(), receiver, timeoutHeight, 0, "") + msg := types.NewMsgTransfer(validPort, validChannel, coins, addr.String(), receiver, timeoutHeight, 0, "") encodingCfg := moduletestutil.MakeTestEncodingConfig(transfer.AppModuleBasic{}) signers, _, err := encodingCfg.Codec.GetMsgV1Signers(msg) diff --git a/modules/apps/transfer/types/transfer_authorization.go b/modules/apps/transfer/types/transfer_authorization.go index 242bc86e801..b38702f980a 100644 --- a/modules/apps/transfer/types/transfer_authorization.go +++ b/modules/apps/transfer/types/transfer_authorization.go @@ -44,6 +44,9 @@ func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (a return authz.AcceptResponse{}, errorsmod.Wrap(ibcerrors.ErrInvalidType, "type mismatch") } + // TODO: replace with correct usage in https://github.com/cosmos/ibc-go/issues/5802 + token := msgTransfer.GetTokens()[0] + for index, allocation := range a.Allocations { if !(allocation.SourceChannel == msgTransfer.SourceChannel && allocation.SourcePort == msgTransfer.SourcePort) { continue @@ -59,11 +62,11 @@ func (a TransferAuthorization) Accept(ctx context.Context, msg proto.Message) (a } // If the spend limit is set to the MaxUint256 sentinel value, do not subtract the amount from the spend limit. - if allocation.SpendLimit.AmountOf(msgTransfer.Token.Denom).Equal(UnboundedSpendLimit()) { + if allocation.SpendLimit.AmountOf(token.Denom).Equal(UnboundedSpendLimit()) { return authz.AcceptResponse{Accept: true, Delete: false, Updated: nil}, nil } - limitLeft, isNegative := allocation.SpendLimit.SafeSub(msgTransfer.Token) + limitLeft, isNegative := allocation.SpendLimit.SafeSub(token) if isNegative { return authz.AcceptResponse{}, errorsmod.Wrapf(ibcerrors.ErrInsufficientFunds, "requested amount is more than spend limit") } diff --git a/modules/apps/transfer/types/transfer_authorization_test.go b/modules/apps/transfer/types/transfer_authorization_test.go index 781e5d68e3d..d86c396d335 100644 --- a/modules/apps/transfer/types/transfer_authorization_test.go +++ b/modules/apps/transfer/types/transfer_authorization_test.go @@ -15,7 +15,7 @@ const testMemo = `{"wasm":{"contract":"osmo1c3ljch9dfw5kf52nfwpxd2zmj2ese7agnx0p func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { var ( - msgTransfer types.MsgTransfer + msgTransfer *types.MsgTransfer transferAuthz types.TransferAuthorization ) @@ -247,18 +247,20 @@ func (suite *TypesTestSuite) TestTransferAuthorizationAccept() { }, } - msgTransfer = types.MsgTransfer{ - SourcePort: path.EndpointA.ChannelConfig.PortID, - SourceChannel: path.EndpointA.ChannelID, - Token: ibctesting.TestCoin, - Sender: suite.chainA.SenderAccount.GetAddress().String(), - Receiver: ibctesting.TestAccAddress, - TimeoutHeight: suite.chainB.GetTimeoutHeight(), - } + msgTransfer = types.NewMsgTransfer( + path.EndpointA.ChannelConfig.PortID, + path.EndpointA.ChannelID, + sdk.NewCoins(ibctesting.TestCoin), + suite.chainA.SenderAccount.GetAddress().String(), + ibctesting.TestAccAddress, + suite.chainB.GetTimeoutHeight(), + 0, + "", + ) tc.malleate() - res, err := transferAuthz.Accept(suite.chainA.GetContext(), &msgTransfer) + res, err := transferAuthz.Accept(suite.chainA.GetContext(), msgTransfer) tc.assertResult(res, err) }) } diff --git a/modules/apps/transfer/types/tx.pb.go b/modules/apps/transfer/types/tx.pb.go index 6dbba5651c9..860b32b2549 100644 --- a/modules/apps/transfer/types/tx.pb.go +++ b/modules/apps/transfer/types/tx.pb.go @@ -54,6 +54,8 @@ type MsgTransfer struct { TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty"` // optional memo Memo string `protobuf:"bytes,8,opt,name=memo,proto3" json:"memo,omitempty"` + // tokens to be transferred + Tokens []types.Coin `protobuf:"bytes,9,rep,name=tokens,proto3" json:"tokens"` } func (m *MsgTransfer) Reset() { *m = MsgTransfer{} } @@ -221,46 +223,47 @@ func init() { } var fileDescriptor_7401ed9bed2f8e09 = []byte{ - // 613 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xcf, 0x4f, 0x13, 0x4f, - 0x14, 0xef, 0x7e, 0x29, 0xfd, 0xc2, 0x54, 0x40, 0x56, 0x03, 0xcb, 0xc6, 0x6c, 0x49, 0x23, 0x09, - 0x96, 0x30, 0x93, 0x62, 0x0c, 0xa6, 0xc7, 0x72, 0xf1, 0x20, 0x09, 0x36, 0x78, 0xf1, 0x42, 0x76, - 0xa7, 0xcf, 0xed, 0x84, 0xee, 0xcc, 0x3a, 0x33, 0x6d, 0xf4, 0x62, 0x88, 0x27, 0xe3, 0xc9, 0x3f, - 0xc1, 0xa3, 0x47, 0xfe, 0x0c, 0x8e, 0x1c, 0x3d, 0x19, 0x03, 0x07, 0x2e, 0xfe, 0x11, 0x66, 0x66, - 0xa7, 0x75, 0xf5, 0x50, 0xf5, 0xb2, 0xfb, 0x7e, 0x7c, 0xde, 0xaf, 0xcf, 0x9b, 0x87, 0xb6, 0x58, - 0x42, 0x49, 0x9c, 0xe7, 0x43, 0x46, 0x63, 0xcd, 0x04, 0x57, 0x44, 0xcb, 0x98, 0xab, 0x97, 0x20, - 0xc9, 0xb8, 0x4d, 0xf4, 0x6b, 0x9c, 0x4b, 0xa1, 0x85, 0x7f, 0x8f, 0x25, 0x14, 0x97, 0x61, 0x78, - 0x02, 0xc3, 0xe3, 0x76, 0xb8, 0x1a, 0x67, 0x8c, 0x0b, 0x62, 0xbf, 0x45, 0x40, 0x78, 0x37, 0x15, - 0xa9, 0xb0, 0x22, 0x31, 0x92, 0xb3, 0xae, 0x53, 0xa1, 0x32, 0xa1, 0x48, 0xa6, 0x52, 0x93, 0x3e, - 0x53, 0xa9, 0x73, 0x44, 0xce, 0x91, 0xc4, 0x0a, 0xc8, 0xb8, 0x9d, 0x80, 0x8e, 0xdb, 0x84, 0x0a, - 0xc6, 0x9d, 0xbf, 0x61, 0xda, 0xa4, 0x42, 0x02, 0xa1, 0x43, 0x06, 0x5c, 0x9b, 0xe8, 0x42, 0x72, - 0x80, 0x9d, 0xd9, 0x73, 0x4c, 0x9a, 0xb5, 0xe0, 0xe6, 0xd9, 0x1c, 0xaa, 0x1f, 0xaa, 0xf4, 0xd8, - 0x59, 0xfd, 0x06, 0xaa, 0x2b, 0x31, 0x92, 0x14, 0x4e, 0x72, 0x21, 0x75, 0xe0, 0x6d, 0x7a, 0xdb, - 0x8b, 0x3d, 0x54, 0x98, 0x8e, 0x84, 0xd4, 0xfe, 0x16, 0x5a, 0x76, 0x00, 0x3a, 0x88, 0x39, 0x87, - 0x61, 0xf0, 0x9f, 0xc5, 0x2c, 0x15, 0xd6, 0x83, 0xc2, 0xe8, 0x77, 0xd0, 0xbc, 0x16, 0xa7, 0xc0, - 0x83, 0xb9, 0x4d, 0x6f, 0xbb, 0xbe, 0xb7, 0x81, 0x8b, 0xa9, 0xb0, 0x99, 0x0a, 0xbb, 0xa9, 0xf0, - 0x81, 0x60, 0xbc, 0xbb, 0x78, 0xf1, 0xb5, 0x51, 0xf9, 0x7c, 0x73, 0xde, 0xf2, 0x7a, 0x45, 0x88, - 0xbf, 0x86, 0x6a, 0x0a, 0x78, 0x1f, 0x64, 0x50, 0xb5, 0xa9, 0x9d, 0xe6, 0x87, 0x68, 0x41, 0x02, - 0x05, 0x36, 0x06, 0x19, 0xcc, 0x5b, 0xcf, 0x54, 0xf7, 0x9f, 0xa2, 0x65, 0xcd, 0x32, 0x10, 0x23, - 0x7d, 0x32, 0x00, 0x96, 0x0e, 0x74, 0x50, 0xb3, 0x85, 0x43, 0x6c, 0xd6, 0x65, 0xe8, 0xc2, 0x8e, - 0xa4, 0x71, 0x1b, 0x3f, 0xb1, 0x88, 0x72, 0xe5, 0x25, 0x17, 0x5c, 0x78, 0xfc, 0x1d, 0xb4, 0x3a, - 0xc9, 0x66, 0xfe, 0x4a, 0xc7, 0x59, 0x1e, 0xfc, 0xbf, 0xe9, 0x6d, 0x57, 0x7b, 0xb7, 0x9d, 0xe3, - 0x78, 0x62, 0xf7, 0x7d, 0x54, 0xcd, 0x20, 0x13, 0xc1, 0x82, 0x6d, 0xc9, 0xca, 0x9d, 0xd6, 0xfb, - 0x4f, 0x8d, 0xca, 0xbb, 0x9b, 0xf3, 0x96, 0xeb, 0xfd, 0xc3, 0xcd, 0x79, 0x6b, 0xad, 0xa0, 0x60, - 0x57, 0xf5, 0x4f, 0x49, 0x89, 0xf2, 0xe6, 0x3e, 0xba, 0x53, 0x52, 0x7b, 0xa0, 0x72, 0xc1, 0x15, - 0x98, 0x69, 0x15, 0xbc, 0x1a, 0x01, 0xa7, 0x60, 0xd7, 0x50, 0xed, 0x4d, 0xf5, 0x4e, 0xd5, 0xa4, - 0x6f, 0xbe, 0x45, 0x2b, 0x87, 0x2a, 0x7d, 0x9e, 0xf7, 0x63, 0x0d, 0x47, 0xb1, 0x8c, 0x33, 0x65, - 0xa9, 0x63, 0x29, 0x07, 0xe9, 0x36, 0xe7, 0x34, 0xbf, 0x8b, 0x6a, 0xb9, 0x45, 0xd8, 0x6d, 0xd5, - 0xf7, 0xee, 0xe3, 0x59, 0xaf, 0x18, 0x17, 0xd9, 0xba, 0x55, 0x43, 0x50, 0xcf, 0x45, 0x76, 0x56, - 0x7e, 0xce, 0x64, 0x93, 0x36, 0x37, 0xd0, 0xfa, 0x6f, 0xf5, 0x27, 0xcd, 0xef, 0x7d, 0xf7, 0xd0, - 0xdc, 0xa1, 0x4a, 0xfd, 0x01, 0x5a, 0x98, 0x3e, 0xad, 0x07, 0xb3, 0x6b, 0x96, 0x38, 0x08, 0xdb, - 0x7f, 0x0d, 0x9d, 0xd2, 0xa5, 0xd1, 0xad, 0x5f, 0x98, 0xd8, 0xfd, 0x63, 0x8a, 0x32, 0x3c, 0x7c, - 0xf4, 0x4f, 0xf0, 0x49, 0xd5, 0x70, 0xfe, 0xcc, 0x3c, 0x9f, 0xee, 0xb3, 0x8b, 0xab, 0xc8, 0xbb, - 0xbc, 0x8a, 0xbc, 0x6f, 0x57, 0x91, 0xf7, 0xf1, 0x3a, 0xaa, 0x5c, 0x5e, 0x47, 0x95, 0x2f, 0xd7, - 0x51, 0xe5, 0xc5, 0x7e, 0xca, 0xf4, 0x60, 0x94, 0x60, 0x2a, 0x32, 0xe2, 0x0e, 0x9b, 0x25, 0x74, - 0x37, 0x15, 0x64, 0xfc, 0x98, 0x64, 0xa2, 0x3f, 0x1a, 0x82, 0x32, 0xc7, 0x5a, 0x3a, 0x52, 0xfd, - 0x26, 0x07, 0x95, 0xd4, 0xec, 0x7d, 0x3e, 0xfc, 0x11, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xda, 0xd1, - 0xc3, 0x96, 0x04, 0x00, 0x00, + // 630 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xb1, 0x6e, 0x13, 0x4d, + 0x10, 0xf6, 0xfd, 0x76, 0xfc, 0x27, 0x6b, 0x92, 0x90, 0x05, 0x25, 0x17, 0x0b, 0x9d, 0x2d, 0x8b, + 0x48, 0xc1, 0x51, 0x76, 0xe5, 0x20, 0x14, 0x64, 0x51, 0x39, 0x0d, 0x05, 0x91, 0x82, 0x15, 0x1a, + 0x9a, 0xe8, 0x6e, 0x3d, 0x9c, 0x57, 0xf1, 0xed, 0x1e, 0xb7, 0x6b, 0x0b, 0x1a, 0x84, 0xa8, 0x10, + 0x15, 0x8f, 0x40, 0x49, 0x99, 0x27, 0xa0, 0x4e, 0x99, 0x92, 0x0a, 0xa1, 0xa4, 0x48, 0xc3, 0x43, + 0xa0, 0xdd, 0x5b, 0x9b, 0x83, 0x22, 0x84, 0xc6, 0xde, 0x99, 0xf9, 0xe6, 0x9b, 0xf9, 0x66, 0x3c, + 0x46, 0x1b, 0x3c, 0x62, 0x34, 0x4c, 0xd3, 0x11, 0x67, 0xa1, 0xe6, 0x52, 0x28, 0xaa, 0xb3, 0x50, + 0xa8, 0x17, 0x90, 0xd1, 0x49, 0x87, 0xea, 0x57, 0x24, 0xcd, 0xa4, 0x96, 0xf8, 0x0e, 0x8f, 0x18, + 0x29, 0xc2, 0xc8, 0x14, 0x46, 0x26, 0x9d, 0xfa, 0x4a, 0x98, 0x70, 0x21, 0xa9, 0xfd, 0xcc, 0x13, + 0xea, 0xb7, 0x63, 0x19, 0x4b, 0xfb, 0xa4, 0xe6, 0xe5, 0xbc, 0x6b, 0x4c, 0xaa, 0x44, 0x2a, 0x9a, + 0xa8, 0xd8, 0xd0, 0x27, 0x2a, 0x76, 0x81, 0xc0, 0x05, 0xa2, 0x50, 0x01, 0x9d, 0x74, 0x22, 0xd0, + 0x61, 0x87, 0x32, 0xc9, 0x85, 0x8b, 0x37, 0x4c, 0x9b, 0x4c, 0x66, 0x40, 0xd9, 0x88, 0x83, 0xd0, + 0x26, 0x3b, 0x7f, 0x39, 0xc0, 0xd6, 0xd5, 0x3a, 0xa6, 0xcd, 0x5a, 0x70, 0xeb, 0x4b, 0x19, 0xd5, + 0xf6, 0x55, 0x7c, 0xe8, 0xbc, 0xb8, 0x81, 0x6a, 0x4a, 0x8e, 0x33, 0x06, 0x47, 0xa9, 0xcc, 0xb4, + 0xef, 0x35, 0xbd, 0xcd, 0x85, 0x3e, 0xca, 0x5d, 0x07, 0x32, 0xd3, 0x78, 0x03, 0x2d, 0x39, 0x00, + 0x1b, 0x86, 0x42, 0xc0, 0xc8, 0xff, 0xcf, 0x62, 0x16, 0x73, 0xef, 0x5e, 0xee, 0xc4, 0x5d, 0x34, + 0xa7, 0xe5, 0x31, 0x08, 0xbf, 0xdc, 0xf4, 0x36, 0x6b, 0x3b, 0xeb, 0x24, 0x57, 0x45, 0x8c, 0x2a, + 0xe2, 0x54, 0x91, 0x3d, 0xc9, 0x45, 0x6f, 0xe1, 0xf4, 0x5b, 0xa3, 0xf4, 0xf9, 0xf2, 0xa4, 0xed, + 0xf5, 0xf3, 0x14, 0xbc, 0x8a, 0xaa, 0x0a, 0xc4, 0x00, 0x32, 0xbf, 0x62, 0xa9, 0x9d, 0x85, 0xeb, + 0x68, 0x3e, 0x03, 0x06, 0x7c, 0x02, 0x99, 0x3f, 0x67, 0x23, 0x33, 0x1b, 0x3f, 0x41, 0x4b, 0x9a, + 0x27, 0x20, 0xc7, 0xfa, 0x68, 0x08, 0x3c, 0x1e, 0x6a, 0xbf, 0x6a, 0x0b, 0xd7, 0x89, 0x59, 0x97, + 0x19, 0x17, 0x71, 0x43, 0x9a, 0x74, 0xc8, 0x63, 0x8b, 0x28, 0x56, 0x5e, 0x74, 0xc9, 0x79, 0x04, + 0x6f, 0xa1, 0x95, 0x29, 0x9b, 0xf9, 0x56, 0x3a, 0x4c, 0x52, 0xff, 0xff, 0xa6, 0xb7, 0x59, 0xe9, + 0xdf, 0x74, 0x81, 0xc3, 0xa9, 0x1f, 0x63, 0x54, 0x49, 0x20, 0x91, 0xfe, 0xbc, 0x6d, 0xc9, 0xbe, + 0xf1, 0x23, 0x54, 0xb5, 0x5a, 0x94, 0xbf, 0xd0, 0x2c, 0x5f, 0x5b, 0xbf, 0xcb, 0xe9, 0xb6, 0xdf, + 0x7f, 0x6a, 0x94, 0xde, 0x5d, 0x9e, 0xb4, 0x9d, 0xf2, 0x0f, 0x97, 0x27, 0xed, 0xd5, 0x9c, 0x60, + 0x5b, 0x0d, 0x8e, 0x69, 0x61, 0x61, 0xad, 0x5d, 0x74, 0xab, 0x60, 0xf6, 0x41, 0xa5, 0x52, 0x28, + 0x30, 0xb3, 0x52, 0xf0, 0x72, 0x0c, 0x82, 0x81, 0x5d, 0x62, 0xa5, 0x3f, 0xb3, 0xbb, 0x15, 0x43, + 0xdf, 0x7a, 0x83, 0x96, 0xf7, 0x55, 0xfc, 0x2c, 0x1d, 0x84, 0x1a, 0x0e, 0xc2, 0x2c, 0x4c, 0x94, + 0x1d, 0x3c, 0x8f, 0x05, 0x64, 0x6e, 0xef, 0xce, 0xc2, 0x3d, 0x54, 0x4d, 0x2d, 0xc2, 0xee, 0xba, + 0xb6, 0x73, 0x97, 0x5c, 0x75, 0x03, 0x24, 0x67, 0xeb, 0x55, 0x8c, 0xb0, 0xbe, 0xcb, 0xec, 0x2e, + 0xff, 0xd2, 0x64, 0x49, 0x5b, 0xeb, 0x68, 0xed, 0x8f, 0xfa, 0xd3, 0xe6, 0x77, 0x7e, 0x78, 0xa8, + 0xbc, 0xaf, 0x62, 0x3c, 0x44, 0xf3, 0xb3, 0x1f, 0xe6, 0xbd, 0xab, 0x6b, 0x16, 0x66, 0x50, 0xef, + 0x5c, 0x1b, 0x3a, 0x1b, 0x97, 0x46, 0x37, 0x7e, 0x9b, 0xc4, 0xf6, 0x5f, 0x29, 0x8a, 0xf0, 0xfa, + 0x83, 0x7f, 0x82, 0x4f, 0xab, 0xd6, 0xe7, 0xde, 0x9a, 0xb5, 0xf7, 0x9e, 0x9e, 0x9e, 0x07, 0xde, + 0xd9, 0x79, 0xe0, 0x7d, 0x3f, 0x0f, 0xbc, 0x8f, 0x17, 0x41, 0xe9, 0xec, 0x22, 0x28, 0x7d, 0xbd, + 0x08, 0x4a, 0xcf, 0x77, 0x63, 0xae, 0x87, 0xe3, 0x88, 0x30, 0x99, 0x50, 0xf7, 0xb7, 0xc0, 0x23, + 0xb6, 0x1d, 0x4b, 0x3a, 0x79, 0x48, 0x13, 0x39, 0x18, 0x8f, 0x40, 0x99, 0x53, 0x2f, 0x9c, 0xb8, + 0x7e, 0x9d, 0x82, 0x8a, 0xaa, 0xf6, 0xba, 0xef, 0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x29, 0xee, + 0x20, 0x91, 0xd4, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -403,6 +406,20 @@ func (m *MsgTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Tokens) > 0 { + for iNdEx := len(m.Tokens) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Tokens[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + } if len(m.Memo) > 0 { i -= len(m.Memo) copy(dAtA[i:], m.Memo) @@ -601,6 +618,12 @@ func (m *MsgTransfer) Size() (n int) { if l > 0 { n += 1 + l + sovTx(uint64(l)) } + if len(m.Tokens) > 0 { + for _, e := range m.Tokens { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } return n } @@ -920,6 +943,40 @@ func (m *MsgTransfer) Unmarshal(dAtA []byte) error { } m.Memo = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Tokens", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Tokens = append(m.Tokens, types.Coin{}) + if err := m.Tokens[len(m.Tokens)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/proto/ibc/applications/transfer/v1/tx.proto b/proto/ibc/applications/transfer/v1/tx.proto index 42c70d3bedc..52e5d29b7e1 100644 --- a/proto/ibc/applications/transfer/v1/tx.proto +++ b/proto/ibc/applications/transfer/v1/tx.proto @@ -49,6 +49,8 @@ message MsgTransfer { uint64 timeout_timestamp = 7; // optional memo string memo = 8; + // tokens to be transferred + repeated cosmos.base.v1beta1.Coin tokens = 9 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; } // MsgTransferResponse defines the Msg/Transfer response type.