@@ -10,7 +10,8 @@ import (
1010 "github.com/cosmos/ibc-go/v10/modules/apps/transfer/internal/events"
1111 "github.com/cosmos/ibc-go/v10/modules/apps/transfer/internal/telemetry"
1212 "github.com/cosmos/ibc-go/v10/modules/apps/transfer/types"
13- channeltypes "github.com/cosmos/ibc-go/v10/modules/core/04-channel/types"
13+ clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
14+ channeltypesv2 "github.com/cosmos/ibc-go/v10/modules/core/04-channel/v2/types"
1415 ibcerrors "github.com/cosmos/ibc-go/v10/modules/core/errors"
1516)
1617
@@ -29,16 +30,6 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
2930 return nil , err
3031 }
3132
32- channel , found := k .channelKeeper .GetChannel (ctx , msg .SourcePort , msg .SourceChannel )
33- if ! found {
34- return nil , errorsmod .Wrapf (channeltypes .ErrChannelNotFound , "port ID (%s) channel ID (%s)" , msg .SourcePort , msg .SourceChannel )
35- }
36-
37- appVersion , found := k .ics4Wrapper .GetAppVersion (ctx , msg .SourcePort , msg .SourceChannel )
38- if ! found {
39- return nil , errorsmod .Wrapf (ibcerrors .ErrInvalidRequest , "application version not found for source port: %s and source channel: %s" , msg .SourcePort , msg .SourceChannel )
40- }
41-
4233 coin := msg .Token
4334
4435 // Using types.UnboundedSpendLimit allows us to send the entire balance of a given denom.
@@ -54,31 +45,76 @@ func (k Keeper) Transfer(goCtx context.Context, msg *types.MsgTransfer) (*types.
5445 return nil , err
5546 }
5647
57- if err := k .SendTransfer (ctx , msg .SourcePort , msg .SourceChannel , token , sender ); err != nil {
58- return nil , err
48+ packetData := types .NewFungibleTokenPacketData (token .Denom .Path (), token .Amount , sender .String (), msg .Receiver , msg .Memo )
49+
50+ if err := packetData .ValidateBasic (); err != nil {
51+ return nil , errorsmod .Wrapf (err , "failed to validate %s packet data" , types .V1 )
5952 }
6053
61- packetDataBytes , err := createPacketDataBytesFromVersion (
62- appVersion , sender .String (), msg .Receiver , msg .Memo , token ,
63- )
54+ // if a channel exists with source channel, then use IBC V1 protocol
55+ // otherwise use IBC V2 protocol
56+ channel , isIBCV1 := k .channelKeeper .GetChannel (ctx , msg .SourcePort , msg .SourceChannel )
57+
58+ var sequence uint64
59+ if isIBCV1 {
60+ // if a V1 channel exists for the source channel, then use IBC V1 protocol
61+ sequence , err = k .transferV1Packet (ctx , msg .SourceChannel , token , msg .TimeoutHeight , msg .TimeoutTimestamp , packetData )
62+ // telemetry for transfer occurs here, in IBC V2 this is done in the onSendPacket callback
63+ telemetry .ReportTransfer (msg .SourcePort , msg .SourceChannel , channel .Counterparty .PortId , channel .Counterparty .ChannelId , token )
64+ } else {
65+ // otherwise try to send an IBC V2 packet, if the sourceChannel is not a IBC V2 client
66+ // then core IBC will return a CounterpartyNotFound error
67+ sequence , err = k .transferV2Packet (ctx , msg .Encoding , msg .SourceChannel , msg .TimeoutTimestamp , packetData )
68+ }
6469 if err != nil {
6570 return nil , err
6671 }
6772
68- sequence , err := k .ics4Wrapper .SendPacket (ctx , msg .SourcePort , msg .SourceChannel , msg .TimeoutHeight , msg .TimeoutTimestamp , packetDataBytes )
73+ k .Logger (ctx ).Info ("IBC fungible token transfer" , "token" , coin , "sender" , msg .Sender , "receiver" , msg .Receiver )
74+
75+ return & types.MsgTransferResponse {Sequence : sequence }, nil
76+ }
77+
78+ func (k Keeper ) transferV1Packet (ctx sdk.Context , sourceChannel string , token types.Token , timeoutHeight clienttypes.Height , timeoutTimestamp uint64 , packetData types.FungibleTokenPacketData ) (uint64 , error ) {
79+ if err := k .SendTransfer (ctx , types .PortID , sourceChannel , token , sdk .MustAccAddressFromBech32 (packetData .Sender )); err != nil {
80+ return 0 , err
81+ }
82+
83+ packetDataBytes := packetData .GetBytes ()
84+ sequence , err := k .ics4Wrapper .SendPacket (ctx , types .PortID , sourceChannel , timeoutHeight , timeoutTimestamp , packetDataBytes )
6985 if err != nil {
70- return nil , err
86+ return 0 , err
7187 }
7288
73- events .EmitTransferEvent (ctx , sender . String (), msg .Receiver , token , msg .Memo )
89+ events .EmitTransferEvent (ctx , packetData . Sender , packetData .Receiver , token , packetData .Memo )
7490
75- destinationPort := channel .Counterparty .PortId
76- destinationChannel := channel .Counterparty .ChannelId
77- telemetry .ReportTransfer (msg .SourcePort , msg .SourceChannel , destinationPort , destinationChannel , token )
91+ return sequence , nil
92+ }
7893
79- k .Logger (ctx ).Info ("IBC fungible token transfer" , "token" , coin , "sender" , msg .Sender , "receiver" , msg .Receiver )
94+ func (k Keeper ) transferV2Packet (ctx sdk.Context , encoding , sourceChannel string , timeoutTimestamp uint64 , packetData types.FungibleTokenPacketData ) (uint64 , error ) {
95+ if encoding == "" {
96+ encoding = types .EncodingJSON
97+ }
8098
81- return & types.MsgTransferResponse {Sequence : sequence }, nil
99+ data , err := types .MarshalPacketData (packetData , types .V1 , encoding )
100+ if err != nil {
101+ return 0 , err
102+ }
103+
104+ payload := channeltypesv2 .NewPayload (
105+ types .PortID , types .PortID ,
106+ types .V1 , encoding , data ,
107+ )
108+ msg := channeltypesv2 .NewMsgSendPacket (
109+ sourceChannel , timeoutTimestamp ,
110+ packetData .Sender , payload ,
111+ )
112+
113+ res , err := k .channelKeeperV2 .SendPacket (ctx , msg )
114+ if err != nil {
115+ return 0 , err
116+ }
117+ return res .Sequence , nil
82118}
83119
84120// UpdateParams defines an rpc handler method for MsgUpdateParams. Updates the ibc-transfer module's parameters.
0 commit comments