diff --git a/x/axelarnet/module.go b/x/axelarnet/module.go index 4eaf299e6..3c01ec2bc 100644 --- a/x/axelarnet/module.go +++ b/x/axelarnet/module.go @@ -262,7 +262,7 @@ func (m AxelarnetIBCModule) OnAcknowledgementPacket( return err } - return m.setRoutedPacketFailed(ctx, packet) + return m.setRoutedPacketFailed(ctx, packet, m.bank) } } @@ -287,7 +287,7 @@ func (m AxelarnetIBCModule) OnTimeoutPacket( return err } - return m.setRoutedPacketFailed(ctx, packet) + return m.setRoutedPacketFailed(ctx, packet, m.bank) } // returns the transfer id and delete the existing mapping @@ -334,7 +334,7 @@ func setRoutedPacketCompleted(ctx sdk.Context, k keeper.Keeper, n types.Nexus, p return nil } -func (m AxelarnetIBCModule) setRoutedPacketFailed(ctx sdk.Context, packet channeltypes.Packet) error { +func (m AxelarnetIBCModule) setRoutedPacketFailed(ctx sdk.Context, packet channeltypes.Packet, bank types.BankKeeper) error { // IBC ack/timeout packets, by convention, use the source port/channel to represent native chain -> counterparty chain channel id // https://github.com/cosmos/ibc/tree/main/spec/core/ics-004-channel-and-packet-semantics#definitions port, channel, sequence := packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence() @@ -347,6 +347,11 @@ func (m AxelarnetIBCModule) setRoutedPacketFailed(ctx sdk.Context, packet channe return err } + err = refund_from_asset_escrow_address_to_ibc_account(ctx, packet, bank) + if err != nil { + return err + } + err = lockableAsset.LockFrom(ctx, types.AxelarIBCAccount) if err != nil { return err @@ -395,3 +400,25 @@ func extractTokenFromAckOrTimeoutPacket(packet channeltypes.Packet) sdk.Coin { return sdk.NewCoin(trace.IBCDenom(), amount) } + +// Temporary logic to handle in-transit IBC transfers during upgrade. Previously IBC transfers sent from asset escrow address; now sent from IBC account. +// Deprecated: Remove this function after the v1.1 upgrade +func refund_from_asset_escrow_address_to_ibc_account(ctx sdk.Context, packet channeltypes.Packet, bank types.BankKeeper) error { + // Packet is validated by the IBC module, so we can safely assume it's a valid ICS20 packet + data := funcs.Must(types.ToICS20Packet(packet)) + + // decode the sender address + originalSender := funcs.Must(sdk.AccAddressFromBech32(data.Sender)) + if originalSender.Equals(types.AxelarIBCAccount) { + return nil + } + + // parse the denomination from the full denom path + trace := ibctransfertypes.ParseDenomTrace(data.Denom) + + // parse the transfer amount + transferAmount := funcs.MustOk(sdk.NewIntFromString(data.Amount)) + + token := sdk.NewCoin(trace.IBCDenom(), transferAmount) + return bank.SendCoins(ctx, originalSender, types.AxelarIBCAccount, sdk.NewCoins(token)) +} diff --git a/x/axelarnet/module_test.go b/x/axelarnet/module_test.go index 280d66bfe..c547bb1f0 100644 --- a/x/axelarnet/module_test.go +++ b/x/axelarnet/module_test.go @@ -32,7 +32,7 @@ import ( "github.com/axelarnetwork/utils/funcs" "github.com/axelarnetwork/utils/slices" . "github.com/axelarnetwork/utils/test" -) + func TestIBCModule(t *testing.T) { var ( @@ -197,6 +197,7 @@ func TestIBCModule(t *testing.T) { transfer := funcs.MustOk(k.GetTransfer(ctx, transfer.ID)) assert.Equal(t, types.TransferFailed, transfer.Status) assert.Len(t, lockableAsset.LockFromCalls(), 1) + assert.Len(t, bankK.SendCoinsCalls(), 2) }), whenPendingTransfersExist. @@ -237,5 +238,20 @@ func TestIBCModule(t *testing.T) { assert.Equal(t, nexus.Failed, message.Status) assert.Len(t, lockableAsset.LockFromCalls(), 1) }), + + seqMapsToID. + When2(whenChainIsActivated). + When("lock coin succeeds", lockCoin(true)). + When("packet sender is from IBC account", func() { + fungibleTokenPacket.Sender = types.AxelarIBCAccount.String() + packet = channeltypes.NewPacket(fungibleTokenPacket.GetBytes(), packetSeq, ibctransfertypes.PortID, channelID, ibctransfertypes.PortID, channelID, clienttypes.NewHeight(0, 110), 0) + }). + When2(whenOnTimeout). + Then("should not trigger refund from asset escrow to IBC account", func(t *testing.T) { + transfer := funcs.MustOk(k.GetTransfer(ctx, transfer.ID)) + assert.Equal(t, types.TransferFailed, transfer.Status) + assert.Len(t, lockableAsset.LockFromCalls(), 1) + assert.Len(t, bankK.SendCoinsCalls(), 1) + }), ).Run(t) }