-
Notifications
You must be signed in to change notification settings - Fork 113
/
ibc_transfer.go
111 lines (89 loc) · 3.48 KB
/
ibc_transfer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package keeper
import (
"context"
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
ibctypes "github.com/cosmos/ibc-go/v4/modules/apps/transfer/types"
clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/axelarnetwork/axelar-core/x/axelarnet/types"
nexus "github.com/axelarnetwork/axelar-core/x/nexus/exported"
)
// IBCKeeper provides function to send IBC transfer
type IBCKeeper struct {
Keeper
ibcTransferK types.IBCTransferKeeper
}
// NewIBCKeeper returns a new IBCKeeper
func NewIBCKeeper(k Keeper, ibcTransferK types.IBCTransferKeeper) IBCKeeper {
return IBCKeeper{Keeper: k, ibcTransferK: ibcTransferK}
}
// SendIBCTransfer inits an IBC transfer
func (i IBCKeeper) SendIBCTransfer(ctx sdk.Context, transfer types.IBCTransfer) error {
// map the packet sequence to transfer id
err := i.SetSeqIDMapping(ctx, transfer)
if err != nil {
return err
}
height, err := i.getPacketTimeoutHeight(ctx, transfer.PortID, transfer.ChannelID)
if err != nil {
return err
}
return i.ibcTransferK.SendTransfer(ctx, transfer.PortID, transfer.ChannelID, transfer.Token, transfer.Sender, transfer.Receiver, height, 0)
}
// ParseIBCDenom retrieves the full identifiers trace and base denomination from the IBC transfer keeper store
func (i IBCKeeper) ParseIBCDenom(ctx sdk.Context, ibcDenom string) (ibctypes.DenomTrace, error) {
denomSplit := strings.Split(ibcDenom, "/")
hash, err := ibctypes.ParseHexHash(denomSplit[1])
if err != nil {
return ibctypes.DenomTrace{}, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid denom trace hash %s, %s", hash, err))
}
denomTrace, found := i.ibcTransferK.GetDenomTrace(ctx, hash)
if !found {
return ibctypes.DenomTrace{}, status.Error(
codes.NotFound,
sdkerrors.Wrap(ibctypes.ErrTraceNotFound, denomSplit[1]).Error(),
)
}
return denomTrace, nil
}
// SendMessage sends general message via ICS20 packet memo
func (i IBCKeeper) SendMessage(c context.Context, recipient nexus.CrossChainAddress, asset sdk.Coin, payload string, id string) error {
ctx := sdk.UnwrapSDKContext(c)
portID, channelID, err := i.getPortAndChannel(ctx, recipient.Chain.Name)
if err != nil {
return err
}
height, err := i.getPacketTimeoutHeight(ctx, portID, channelID)
if err != nil {
return err
}
msg := ibctypes.NewMsgTransfer(portID, channelID, asset, types.AxelarGMPAccount.String(), recipient.Address, height, 0)
msg.Memo = payload
res, err := i.ibcTransferK.Transfer(c, msg)
if err != nil {
return err
}
return i.SetSeqMessageIDMapping(ctx, portID, channelID, res.Sequence, id)
}
func (i IBCKeeper) getPacketTimeoutHeight(ctx sdk.Context, portID, channelID string) (clienttypes.Height, error) {
_, state, err := i.channelK.GetChannelClientState(ctx, portID, channelID)
if err != nil {
return clienttypes.Height{}, err
}
return clienttypes.NewHeight(state.GetLatestHeight().GetRevisionNumber(), state.GetLatestHeight().GetRevisionHeight()+i.GetRouteTimeoutWindow(ctx)), nil
}
func (i IBCKeeper) getPortAndChannel(ctx sdk.Context, chain nexus.ChainName) (string, string, error) {
path, ok := i.GetIBCPath(ctx, chain)
if !ok {
return "", "", fmt.Errorf("%s does not have a valid IBC path", chain.String())
}
pathSplit := strings.SplitN(path, "/", 2)
if len(pathSplit) != 2 {
return "", "", fmt.Errorf("invalid path %s for chain %s", path, chain.String())
}
return pathSplit[0], pathSplit[1], nil
}