11package types
22
33import (
4+ "encoding/binary"
5+ "fmt"
6+ "strconv"
7+ "strings"
8+
49 "github.com/btcsuite/btcd/txscript"
510 "github.com/btcsuite/btcd/wire"
11+
12+ errorsmod "cosmossdk.io/errors"
13+ channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
614)
715
816const (
@@ -21,22 +29,29 @@ const (
2129
2230// BuildIBCTransferScript builds the script for IBC transfer with the given channel and recipient address
2331func BuildIBCTransferScript (channelId string , recipient string ) ([]byte , error ) {
32+ // unprefix channel id
33+ unprefixedChannelId , err := GetUnprefixedChannelId (channelId )
34+ if err != nil {
35+ return nil , err
36+ }
37+
38+ // build OP_RETURN script
2439 scriptBuilder := txscript .NewScriptBuilder ()
2540 scriptBuilder .AddOp (txscript .OP_RETURN )
2641
2742 // add magic number
2843 scriptBuilder .AddOp (IBCTransferMagicNumber )
2944
3045 // add payload
31- scriptBuilder .AddData ([] byte ( channelId ) ).AddData ([]byte (recipient ))
46+ scriptBuilder .AddData (unprefixedChannelId ).AddData ([]byte (recipient ))
3247
3348 return scriptBuilder .Script ()
3449}
3550
3651// GetIBCTransferScript gets the IBC transfer script from the given deposit tx
3752func GetIBCTransferScript (depositTx * wire.MsgTx ) []byte {
3853 for _ , out := range depositTx .TxOut {
39- if IsOpReturnOutput (out ) && out .PkScript [1 ] == IBCTransferMagicNumber {
54+ if IsOpReturnOutput (out ) && len ( out . PkScript ) > 1 && out .PkScript [1 ] == IBCTransferMagicNumber {
4055 return out .PkScript
4156 }
4257 }
@@ -48,21 +63,25 @@ func GetIBCTransferScript(depositTx *wire.MsgTx) []byte {
4863func ParseIBCTransferScript (script []byte ) (channelId string , recipient string , err error ) {
4964 tokenizer := txscript .MakeScriptTokenizer (0 , script )
5065 if ! tokenizer .Next () || tokenizer .Err () != nil || tokenizer .Opcode () != txscript .OP_RETURN {
51- return "" , "" , ErrInvalidIBCTransferScript
66+ return "" , "" , errorsmod . Wrap ( ErrInvalidIBCTransferScript , "non OP_RETURN script" )
5267 }
5368
5469 if ! tokenizer .Next () || tokenizer .Err () != nil || tokenizer .Opcode () != IBCTransferMagicNumber {
55- return "" , "" , ErrInvalidIBCTransferScript
70+ return "" , "" , errorsmod . Wrap ( ErrInvalidIBCTransferScript , "failed to parse magic number" )
5671 }
5772
5873 if ! tokenizer .Next () || tokenizer .Err () != nil {
59- return "" , "" , ErrInvalidIBCTransferScript
74+ return "" , "" , errorsmod .Wrap (ErrInvalidIBCTransferScript , "failed to parse channel id" )
75+ }
76+
77+ if len (tokenizer .Data ()) != 4 {
78+ return "" , "" , errorsmod .Wrap (ErrInvalidIBCTransferScript , "invalid channel id" )
6079 }
6180
62- channelId = string (tokenizer .Data ())
81+ channelId = NormalizeChannelId (tokenizer .Data ())
6382
6483 if ! tokenizer .Next () || tokenizer .Err () != nil {
65- return "" , "" , ErrInvalidIBCTransferScript
84+ return "" , "" , errorsmod . Wrap ( ErrInvalidIBCTransferScript , "failed to parse recipient address" )
6685 }
6786
6887 recipient = string (tokenizer .Data ())
@@ -73,3 +92,30 @@ func ParseIBCTransferScript(script []byte) (channelId string, recipient string,
7392
7493 return
7594}
95+
96+ // GetUnprefixedChannelId gets the channel id without prefix
97+ func GetUnprefixedChannelId (channelId string ) ([]byte , error ) {
98+ unprefixedChannelId , found := strings .CutPrefix (channelId , channeltypes .ChannelPrefix )
99+ if ! found {
100+ return nil , channeltypes .ErrInvalidChannelIdentifier
101+ }
102+
103+ id , err := strconv .ParseUint (unprefixedChannelId , 10 , 32 )
104+ if err != nil {
105+ return nil , err
106+ }
107+
108+ bz := make ([]byte , 4 )
109+ binary .BigEndian .PutUint32 (bz , uint32 (id ))
110+
111+ return bz , nil
112+ }
113+
114+ // NormalizeChannelId normalizes the given channel id
115+ func NormalizeChannelId (unprefixedChannelId []byte ) string {
116+ if len (unprefixedChannelId ) != 4 {
117+ return ""
118+ }
119+
120+ return fmt .Sprintf ("%s%d" , channeltypes .ChannelPrefix , binary .BigEndian .Uint32 (unprefixedChannelId ))
121+ }
0 commit comments