Skip to content

Commit

Permalink
feat: add multichain tokens support for IBC MsgTransfer
Browse files Browse the repository at this point in the history
  • Loading branch information
freak12techno committed Feb 24, 2024
1 parent fac1ad1 commit 51155f0
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 20 deletions.
59 changes: 48 additions & 11 deletions pkg/data_fetcher/data_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"main/pkg/metrics"
amountPkg "main/pkg/types/amount"
"strconv"
"strings"

transferTypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

"main/pkg/alias_manager"
"main/pkg/cache"
Expand Down Expand Up @@ -378,7 +381,7 @@ func (f *DataFetcher) GetIbcRemoteChainID(

for _, node := range f.TendermintApiClients[chain.Name] {
ibcChannelClientStateResponse, err, queryInfo := node.GetIbcConnectionClientState(ibcChannel.ConnectionHops[0])
f.MetricsManager.LogTendermintQuery(chain.Name, queryInfo, QueryInfo.QueryTypeIbcChannel)
f.MetricsManager.LogTendermintQuery(chain.Name, queryInfo, QueryInfo.QueryTypeIbcConnectionClientState)
if err != nil {
f.Logger.Error().Err(err).Msg("Error fetching IBC client state")
continue
Expand All @@ -397,23 +400,57 @@ func (f *DataFetcher) GetIbcRemoteChainID(
return ibcClientState.ClientState.ChainId, true
}

func (f *DataFetcher) GetDenomTrace(
chain *configTypes.Chain,
denom string,
) (*transferTypes.DenomTrace, bool) {
denomSplit := strings.Split(denom, "/")
if len(denomSplit) != 2 || denomSplit[0] != transferTypes.DenomPrefix {
f.Logger.Error().Msg("Invalid IBC prefix provided")
return nil, false
}

denomHash := denomSplit[1]

keyName := chain.Name + "_denom_trace_" + denom

if cachedEntry, cachedEntryPresent := f.Cache.Get(keyName); cachedEntryPresent {
if cachedEntryParsed, ok := cachedEntry.(*transferTypes.DenomTrace); ok {
return cachedEntryParsed, true
}

f.Logger.Error().Msg("Could not convert cached staking params to *types.DenomTrace")
return nil, false
}

for _, node := range f.TendermintApiClients[chain.Name] {
notCachedEntry, err, queryInfo := node.GetIbcDenomTrace(denomHash)
f.MetricsManager.LogTendermintQuery(chain.Name, queryInfo, QueryInfo.QueryTypeIbcDenomTrace)

if err != nil {
f.Logger.Error().Err(err).Msg("Error fetching IBC denom trace")
continue
}

f.Cache.Set(keyName, notCachedEntry)
return notCachedEntry, true
}

f.Logger.Error().Msg("Could not connect to any nodes to get IBC denom trace")
return nil, false
}

func (f *DataFetcher) GetAliasManager() *alias_manager.AliasManager {
return f.AliasManager
}

func (f *DataFetcher) FindMultichainDenom(
func (f *DataFetcher) FindChainById(
chainID string,
baseDenom string,
) (*configTypes.Chain, *configTypes.DenomInfo, bool) {
) (*configTypes.Chain, bool) {
chain := f.Config.Chains.FindByChainID(chainID)
if chain == nil {
return nil, nil, false
}

denom := chain.Denoms.Find(baseDenom)
if denom == nil {
return nil, nil, false
return nil, false
}

return chain, denom, true
return chain, true
}
66 changes: 62 additions & 4 deletions pkg/messages/msg_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type MsgTransfer struct {
Sender configTypes.Link
Receiver configTypes.Link

SrcChannel string
SrcPort string

Chain *configTypes.Chain
}

Expand All @@ -28,10 +31,12 @@ func ParseMsgTransfer(data []byte, chain *configTypes.Chain, height int64) (type
}

return &MsgTransfer{
Token: amount.AmountFrom(parsedMessage.Token),
Sender: chain.GetWalletLink(parsedMessage.Sender),
Receiver: configTypes.Link{Value: parsedMessage.Receiver},
Chain: chain,
Token: amount.AmountFrom(parsedMessage.Token),
Sender: chain.GetWalletLink(parsedMessage.Sender),
Receiver: configTypes.Link{Value: parsedMessage.Receiver},
SrcChannel: parsedMessage.SourceChannel,
SrcPort: parsedMessage.SourcePort,
Chain: chain,
}, nil
}

Expand All @@ -40,13 +45,66 @@ func (m MsgTransfer) Type() string {
}

func (m *MsgTransfer) GetAdditionalData(fetcher types.DataFetcher) {
m.FetchRemoteChainData(fetcher)

fetcher.PopulateAmount(m.Chain, m.Token)

if alias := fetcher.GetAliasManager().Get(m.Chain.Name, m.Sender.Value); alias != "" {
m.Sender.Title = alias
}
}

func (m *MsgTransfer) FetchRemoteChainData(fetcher types.DataFetcher) {
// p.Receiver is always someone from the remote chain, so we need to fetch the data
// from cross-chain.
// p.Sender is on native chain, so we can use p.Chain to generate links
// and denoms and prices.

// If it's an IBC token (like, withdraw on Osmosis) - we need to figure out what
// the original denom is, to convert it, and also take the remote chain for links
// generation.
// If it's a native token - just take the denom from the current chain, but also fetch
// the remote chain for links generation.
var trace ibcTypes.DenomTrace
if m.Token.IsIbcToken() {
externalTrace, found := fetcher.GetDenomTrace(m.Chain, m.Token.Denom)
if !found {
return
}
trace = *externalTrace
} else {
trace = ibcTypes.ParseDenomTrace(m.Token.Denom)
}

m.Token.Denom = trace.BaseDenom
m.Token.BaseDenom = trace.BaseDenom

if trace.IsNativeDenom() {
fetcher.PopulateAmount(m.Chain, m.Token)
}

originalChainID, fetched := fetcher.GetIbcRemoteChainID(m.Chain, m.SrcChannel, m.SrcPort)

if !fetched {
return
}

chain, found := fetcher.FindChainById(originalChainID)
if !found {
return
}

if !trace.IsNativeDenom() {
fetcher.PopulateAmount(chain, m.Token)
}

m.Receiver = chain.GetWalletLink(m.Receiver.Value)

if alias := fetcher.GetAliasManager().Get(chain.Name, m.Receiver.Value); alias != "" {
m.Receiver.Title = alias
}
}

func (m *MsgTransfer) GetValues() event.EventValues {
return []event.EventValue{
event.From(cosmosTypes.EventTypeMessage, cosmosTypes.AttributeKeyAction, m.Type()),
Expand Down
2 changes: 1 addition & 1 deletion pkg/messages/packet/fungible_token_packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (p *FungibleTokenPacket) FetchRemoteChainData(fetcher types.DataFetcher) {
return
}

if chain, _, found := fetcher.FindMultichainDenom(originalChainID, trace.BaseDenom); found {
if chain, found := fetcher.FindChainById(originalChainID); found {
fetcher.PopulateAmount(chain, p.Token)
p.Sender = chain.GetWalletLink(p.Sender.Value)

Expand Down
16 changes: 16 additions & 0 deletions pkg/tendermint/api/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strconv"
"time"

"github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

configTypes "main/pkg/config/types"
"main/pkg/types/responses"

Expand Down Expand Up @@ -143,6 +145,20 @@ func (c *TendermintApiClient) GetIbcConnectionClientState(
return &response.IdentifiedClientState, nil, queryInfo
}

func (c *TendermintApiClient) GetIbcDenomTrace(
hash string,
) (*types.DenomTrace, error, query_info.QueryInfo) {
url := fmt.Sprintf("/ibc/apps/transfer/v1/denom_traces/%s", hash)

var response *responses.IbcDenomTraceResponse
err, queryInfo := c.Get(url, &response)
if err != nil {
return nil, err, queryInfo
}

return &response.DenomTrace, nil, queryInfo
}

func (c *TendermintApiClient) Get(url string, target interface{}) (error, query_info.QueryInfo) {
return c.GetWithHeaders(url, target, map[string]string{})
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/types/amount/amount.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"math/big"
"strings"

transferTypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

cosmosTypes "github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -56,6 +58,11 @@ func (a Amount) String() string {
return fmt.Sprintf("%d%s", value, a.Denom)
}

func (a Amount) IsIbcToken() bool {
denomSplit := strings.Split(a.Denom, "/")
return len(denomSplit) == 2 && denomSplit[0] == transferTypes.DenomPrefix
}

type Amounts []*Amount

func (a Amounts) String() string {
Expand Down
11 changes: 7 additions & 4 deletions pkg/types/data_fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
priceFetchers "main/pkg/price_fetchers"
"main/pkg/types/amount"
"main/pkg/types/responses"

transferTypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
)

// This interface is only here to avoid a cyclic dependency
Expand All @@ -32,8 +34,9 @@ type DataFetcher interface {
GetStakingParams(chain *configTypes.Chain) (*responses.StakingParams, bool)
GetAliasManager() *alias_manager.AliasManager
GetIbcRemoteChainID(chain *configTypes.Chain, channel, port string) (string, bool)
FindMultichainDenom(
chainID string,
baseDenom string,
) (*configTypes.Chain, *configTypes.DenomInfo, bool)
FindChainById(chainID string) (*configTypes.Chain, bool)
GetDenomTrace(
chain *configTypes.Chain,
denom string,
) (*transferTypes.DenomTrace, bool)
}
1 change: 1 addition & 0 deletions pkg/types/query_info/query_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const (
QueryTypeValidator QueryType = "validator"
QueryTypeIbcChannel QueryType = "ibc_channel"
QueryTypeIbcConnectionClientState QueryType = "ibc_connection_client_state"
QueryTypeIbcDenomTrace QueryType = "ibc_denom_trace"
)

func GetQueryTypes() []QueryType {
Expand Down
7 changes: 7 additions & 0 deletions pkg/types/responses/ibc_denom_trace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package responses

import "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"

type IbcDenomTraceResponse struct {
DenomTrace types.DenomTrace `json:"denom_trace"`
}

0 comments on commit 51155f0

Please sign in to comment.