Skip to content

Commit 016a98e

Browse files
fix: send entire balance not working as expected (#7871)
1 parent 5412619 commit 016a98e

File tree

4 files changed

+54
-25
lines changed

4 files changed

+54
-25
lines changed

e2e/tests/transfer/base_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,6 @@ func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() {
592592
})
593593

594594
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
595-
596595
t.Run("packets relayed", func(t *testing.T) {
597596
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
598597
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
@@ -619,7 +618,7 @@ func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() {
619618
chainAIBCToken := testsuite.GetIBCToken(chainB.Config().Denom, channelA.PortID, channelA.ChannelID)
620619
t.Run("packets relayed", func(t *testing.T) {
621620
// test that chainA has the entire balance back of its native token.
622-
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
621+
s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1)
623622
actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainADenom)
624623

625624
s.Require().NoError(err)

modules/apps/transfer/keeper/msg_server.go

+8
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ func (k Keeper) Transfer(ctx context.Context, msg *types.MsgTransfer) (*types.Ms
6161
tokens := make([]types.Token, len(coins))
6262

6363
for i, coin := range coins {
64+
// Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom.
65+
if coin.Amount.Equal(types.UnboundedSpendLimit()) {
66+
coin.Amount = k.BankKeeper.SpendableCoin(ctx, sender, coin.Denom).Amount
67+
if coin.Amount.IsZero() {
68+
return nil, errorsmod.Wrapf(types.ErrInvalidAmount, "empty spendable balance for %s", coin.Denom)
69+
}
70+
}
71+
6472
tokens[i], err = k.tokenFromCoin(ctx, coin)
6573
if err != nil {
6674
return nil, err

modules/apps/transfer/keeper/relay.go

-8
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ func (k Keeper) SendTransfer(
7373
return errorsmod.Wrap(types.ErrSendDisabled, err.Error())
7474
}
7575

76-
// Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom.
77-
if coin.Amount.Equal(types.UnboundedSpendLimit()) {
78-
coin.Amount = k.BankKeeper.SpendableCoin(ctx, sender, coin.Denom).Amount
79-
if coin.Amount.IsZero() {
80-
return errorsmod.Wrapf(types.ErrInvalidAmount, "empty spendable balance for %s", coin.Denom)
81-
}
82-
}
83-
8476
// NOTE: SendTransfer simply sends the denomination as it exists on its own
8577
// chain inside the packet data. The receiving chain will perform denom
8678
// prefixing as necessary.

modules/apps/transfer/transfer_test.go

+45-15
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,38 @@ func (suite *TransferTestSuite) SetupTest() {
3737
// 2 - from chainB to chainC
3838
// 3 - from chainC to chainB
3939
func (suite *TransferTestSuite) TestHandleMsgTransfer() {
40-
testCases := []struct {
41-
name string
40+
var (
4241
sourceDenomsToTransfer []string
42+
msgAmount sdkmath.Int
43+
)
44+
45+
testCases := []struct {
46+
name string
47+
malleate func()
4348
}{
4449
{
4550
"transfer single denom",
46-
[]string{sdk.DefaultBondDenom},
51+
func() {},
52+
},
53+
{
54+
"transfer amount larger than int64",
55+
func() {
56+
var ok bool
57+
msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64)
58+
suite.Require().True(ok)
59+
},
4760
},
4861
{
4962
"transfer multiple denoms",
50-
[]string{sdk.DefaultBondDenom, ibctesting.SecondaryDenom},
63+
func() {
64+
sourceDenomsToTransfer = []string{sdk.DefaultBondDenom, ibctesting.SecondaryDenom}
65+
},
66+
},
67+
{
68+
"transfer entire balance",
69+
func() {
70+
msgAmount = types.UnboundedSpendLimit()
71+
},
5172
},
5273
}
5374

@@ -63,19 +84,22 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
6384
pathAToB.Setup()
6485
traceAToB := types.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID)
6586

87+
sourceDenomsToTransfer = []string{sdk.DefaultBondDenom}
88+
msgAmount = ibctesting.DefaultCoinAmount
89+
90+
tc.malleate()
91+
6692
originalBalances := sdk.NewCoins()
67-
for _, denom := range tc.sourceDenomsToTransfer {
93+
for _, denom := range sourceDenomsToTransfer {
6894
originalBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), denom)
6995
originalBalances = originalBalances.Add(originalBalance)
7096
}
7197

7298
timeoutHeight := clienttypes.NewHeight(1, 110)
7399

74-
amount, ok := sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64)
75-
suite.Require().True(ok)
76100
originalCoins := sdk.NewCoins()
77-
for _, denom := range tc.sourceDenomsToTransfer {
78-
coinToSendToB := sdk.NewCoin(denom, amount)
101+
for _, denom := range sourceDenomsToTransfer {
102+
coinToSendToB := sdk.NewCoin(denom, msgAmount)
79103
originalCoins = originalCoins.Add(coinToSendToB)
80104
}
81105

@@ -87,6 +111,12 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
87111
packet, err := ibctesting.ParsePacketFromEvents(res.Events)
88112
suite.Require().NoError(err)
89113

114+
// Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance)
115+
packetData, err := types.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "")
116+
suite.Require().NoError(err)
117+
transferAmount, ok := sdkmath.NewIntFromString(packetData.Tokens[0].Amount)
118+
suite.Require().True(ok)
119+
90120
// relay send
91121
err = pathAToB.RelayPacket(packet)
92122
suite.Require().NoError(err) // relay committed
@@ -96,16 +126,16 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
96126
for _, coin := range originalCoins {
97127
// check that the balance for chainA is updated
98128
chainABalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), coin.Denom)
99-
suite.Require().Equal(originalBalances.AmountOf(coin.Denom).Sub(amount).Int64(), chainABalance.Amount.Int64())
129+
suite.Require().True(originalBalances.AmountOf(coin.Denom).Sub(transferAmount).Equal(chainABalance.Amount))
100130

101131
// check that module account escrow address has locked the tokens
102132
chainAEscrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, coin.Denom)
103-
suite.Require().Equal(coin, chainAEscrowBalance)
133+
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
104134

105135
// check that voucher exists on chain B
106136
chainBDenom := types.NewDenom(coin.Denom, traceAToB)
107137
chainBBalance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), chainBDenom.IBCDenom())
108-
coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), amount)
138+
coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount)
109139
suite.Require().Equal(coinSentFromAToB, chainBBalance)
110140

111141
coinsSentFromAToB = coinsSentFromAToB.Add(coinSentFromAToB)
@@ -137,7 +167,7 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
137167
chainCDenom := types.NewDenom(coin.Denom, traceBToC, traceAToB)
138168

139169
// check that the balance is updated on chainC
140-
coinSentFromBToC := sdk.NewCoin(chainCDenom.IBCDenom(), amount)
170+
coinSentFromBToC := sdk.NewCoin(chainCDenom.IBCDenom(), transferAmount)
141171
chainCBalance := suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coinSentFromBToC.Denom)
142172
suite.Require().Equal(coinSentFromBToC, chainCBalance)
143173

@@ -182,12 +212,12 @@ func (suite *TransferTestSuite) TestHandleMsgTransfer() {
182212
for _, coin := range originalCoins {
183213
// check that the balance is unchanged
184214
chainABalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), coin.Denom)
185-
suite.Require().Equal(originalBalances.AmountOf(coin.Denom).Sub(amount).Int64(), chainABalance.Amount.Int64())
215+
suite.Require().True(originalBalances.AmountOf(coin.Denom).Sub(transferAmount).Equal(chainABalance.Amount))
186216

187217
// check that module account escrow address is unchanged
188218
escrowAddress = types.GetEscrowAddress(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID)
189219
chainAEscrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, coin.Denom)
190-
suite.Require().Equal(coin, chainAEscrowBalance)
220+
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
191221
}
192222
})
193223
}

0 commit comments

Comments
 (0)