From d6867e648319ad6f82f430bfbf683e25163857d1 Mon Sep 17 00:00:00 2001 From: abel Date: Thu, 11 Apr 2024 11:01:58 -0300 Subject: [PATCH] (feat) Added support and examples for IBC Transfer module queries and messages --- client/chain/chain.go | 78 ++++++++++++++--- client/chain/chain_test_support.go | 23 +++++ .../ibc/transfer/1_MsgTransfer/example.go | 87 +++++++++++++++++++ .../transfer/query/1_DenomTrace/example.go | 76 ++++++++++++++++ .../transfer/query/2_DenomTraces/example.go | 72 +++++++++++++++ .../ibc/transfer/query/3_DenomHash/example.go | 73 ++++++++++++++++ .../transfer/query/4_EscrowAddress/example.go | 72 +++++++++++++++ .../query/5_TotalEscrowForDenom/example.go | 71 +++++++++++++++ 8 files changed, 542 insertions(+), 10 deletions(-) create mode 100644 examples/chain/ibc/transfer/1_MsgTransfer/example.go create mode 100644 examples/chain/ibc/transfer/query/1_DenomTrace/example.go create mode 100644 examples/chain/ibc/transfer/query/2_DenomTraces/example.go create mode 100644 examples/chain/ibc/transfer/query/3_DenomHash/example.go create mode 100644 examples/chain/ibc/transfer/query/4_EscrowAddress/example.go create mode 100644 examples/chain/ibc/transfer/query/5_TotalEscrowForDenom/example.go diff --git a/client/chain/chain.go b/client/chain/chain.go index 1ab1e0ac..bcd5443d 100644 --- a/client/chain/chain.go +++ b/client/chain/chain.go @@ -13,6 +13,8 @@ import ( "sync/atomic" "time" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -243,6 +245,13 @@ type ChainClient interface { FetchValidatorSetByHeight(ctx context.Context, height int64, pagination *query.PageRequest) (*tmservice.GetValidatorSetByHeightResponse, error) ABCIQuery(ctx context.Context, path string, data []byte, height int64, prove bool) (*tmservice.ABCIQueryResponse, error) + // IBC Transfer module + FetchDenomTrace(ctx context.Context, hash string) (*ibctransfertypes.QueryDenomTraceResponse, error) + FetchDenomTraces(ctx context.Context, pagination *query.PageRequest) (*ibctransfertypes.QueryDenomTracesResponse, error) + FetchDenomHash(ctx context.Context, trace string) (*ibctransfertypes.QueryDenomHashResponse, error) + FetchEscrowAddress(ctx context.Context, portId string, channelId string) (*ibctransfertypes.QueryEscrowAddressResponse, error) + FetchTotalEscrowForDenom(ctx context.Context, denom string) (*ibctransfertypes.QueryTotalEscrowForDenomResponse, error) + Close() } @@ -269,16 +278,17 @@ type chainClient struct { sessionEnabled bool - txClient txtypes.ServiceClient authQueryClient authtypes.QueryClient - exchangeQueryClient exchangetypes.QueryClient - bankQueryClient banktypes.QueryClient authzQueryClient authztypes.QueryClient - wasmQueryClient wasmtypes.QueryClient + bankQueryClient banktypes.QueryClient chainStreamClient chainstreamtypes.StreamClient - tokenfactoryQueryClient tokenfactorytypes.QueryClient distributionQueryClient distributiontypes.QueryClient + exchangeQueryClient exchangetypes.QueryClient + ibcTransferQueryClient ibctransfertypes.QueryClient tendermintQueryClient tmservice.ServiceClient + tokenfactoryQueryClient tokenfactorytypes.QueryClient + txClient txtypes.ServiceClient + wasmQueryClient wasmtypes.QueryClient subaccountToNonce map[ethcommon.Hash]uint32 closed int64 @@ -365,16 +375,17 @@ func NewChainClient( sessionEnabled: stickySessionEnabled, - txClient: txtypes.NewServiceClient(conn), authQueryClient: authtypes.NewQueryClient(conn), - exchangeQueryClient: exchangetypes.NewQueryClient(conn), - bankQueryClient: banktypes.NewQueryClient(conn), authzQueryClient: authztypes.NewQueryClient(conn), - wasmQueryClient: wasmtypes.NewQueryClient(conn), + bankQueryClient: banktypes.NewQueryClient(conn), chainStreamClient: chainstreamtypes.NewStreamClient(chainStreamConn), - tokenfactoryQueryClient: tokenfactorytypes.NewQueryClient(conn), distributionQueryClient: distributiontypes.NewQueryClient(conn), + exchangeQueryClient: exchangetypes.NewQueryClient(conn), + ibcTransferQueryClient: ibctransfertypes.NewQueryClient(conn), tendermintQueryClient: tmservice.NewServiceClient(conn), + tokenfactoryQueryClient: tokenfactorytypes.NewQueryClient(conn), + txClient: txtypes.NewServiceClient(conn), + wasmQueryClient: wasmtypes.NewQueryClient(conn), subaccountToNonce: make(map[ethcommon.Hash]uint32), } @@ -2255,3 +2266,50 @@ func (c *chainClient) ABCIQuery(ctx context.Context, path string, data []byte, h return res, err } + +// IBC Transfer module +func (c *chainClient) FetchDenomTrace(ctx context.Context, hash string) (*ibctransfertypes.QueryDenomTraceResponse, error) { + req := &ibctransfertypes.QueryDenomTraceRequest{ + Hash: hash, + } + res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.ibcTransferQueryClient.DenomTrace, req) + + return res, err +} + +func (c *chainClient) FetchDenomTraces(ctx context.Context, pagination *query.PageRequest) (*ibctransfertypes.QueryDenomTracesResponse, error) { + req := &ibctransfertypes.QueryDenomTracesRequest{ + Pagination: pagination, + } + res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.ibcTransferQueryClient.DenomTraces, req) + + return res, err +} + +func (c *chainClient) FetchDenomHash(ctx context.Context, trace string) (*ibctransfertypes.QueryDenomHashResponse, error) { + req := &ibctransfertypes.QueryDenomHashRequest{ + Trace: trace, + } + res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.ibcTransferQueryClient.DenomHash, req) + + return res, err +} + +func (c *chainClient) FetchEscrowAddress(ctx context.Context, portId string, channelId string) (*ibctransfertypes.QueryEscrowAddressResponse, error) { + req := &ibctransfertypes.QueryEscrowAddressRequest{ + PortId: portId, + ChannelId: channelId, + } + res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.ibcTransferQueryClient.EscrowAddress, req) + + return res, err +} + +func (c *chainClient) FetchTotalEscrowForDenom(ctx context.Context, denom string) (*ibctransfertypes.QueryTotalEscrowForDenomResponse, error) { + req := &ibctransfertypes.QueryTotalEscrowForDenomRequest{ + Denom: denom, + } + res, err := common.ExecuteCall(ctx, c.network.ChainCookieAssistant, c.ibcTransferQueryClient.TotalEscrowForDenom, req) + + return res, err +} diff --git a/client/chain/chain_test_support.go b/client/chain/chain_test_support.go index a22fe440..cc643ecc 100644 --- a/client/chain/chain_test_support.go +++ b/client/chain/chain_test_support.go @@ -5,6 +5,8 @@ import ( "errors" "time" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + "github.com/cosmos/cosmos-sdk/client/grpc/tmservice" distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" @@ -578,3 +580,24 @@ func (c *MockChainClient) FetchValidatorSetByHeight(ctx context.Context, height func (c *MockChainClient) ABCIQuery(ctx context.Context, path string, data []byte, height int64, prove bool) (*tmservice.ABCIQueryResponse, error) { return &tmservice.ABCIQueryResponse{}, nil } + +// IBC Transfer module +func (c *MockChainClient) FetchDenomTrace(ctx context.Context, hash string) (*ibctransfertypes.QueryDenomTraceResponse, error) { + return &ibctransfertypes.QueryDenomTraceResponse{}, nil +} + +func (c *MockChainClient) FetchDenomTraces(ctx context.Context, pagination *query.PageRequest) (*ibctransfertypes.QueryDenomTracesResponse, error) { + return &ibctransfertypes.QueryDenomTracesResponse{}, nil +} + +func (c *MockChainClient) FetchDenomHash(ctx context.Context, trace string) (*ibctransfertypes.QueryDenomHashResponse, error) { + return &ibctransfertypes.QueryDenomHashResponse{}, nil +} + +func (c *MockChainClient) FetchEscrowAddress(ctx context.Context, portId string, channelId string) (*ibctransfertypes.QueryEscrowAddressResponse, error) { + return &ibctransfertypes.QueryEscrowAddressResponse{}, nil +} + +func (c *MockChainClient) FetchTotalEscrowForDenom(ctx context.Context, denom string) (*ibctransfertypes.QueryTotalEscrowForDenomResponse, error) { + return &ibctransfertypes.QueryTotalEscrowForDenomResponse{}, nil +} diff --git a/examples/chain/ibc/transfer/1_MsgTransfer/example.go b/examples/chain/ibc/transfer/1_MsgTransfer/example.go new file mode 100644 index 00000000..49d137ca --- /dev/null +++ b/examples/chain/ibc/transfer/1_MsgTransfer/example.go @@ -0,0 +1,87 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/InjectiveLabs/sdk-go/client" + "github.com/InjectiveLabs/sdk-go/client/common" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + sdktypes "github.com/cosmos/cosmos-sdk/types" + ibctransfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + ibccoretypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + // initialize grpc client + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + if err != nil { + panic(err) + } + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + sourcePort := "transfer" + sourceChannel := "channel-126" + coin := sdktypes.Coin{ + Denom: "inj", Amount: sdktypes.NewInt(1000000000000000000), // 1 INJ + } + sender := senderAddress.String() + receiver := "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r" + timeoutHeight := ibccoretypes.Height{RevisionNumber: 10, RevisionHeight: 10} + + msg := &ibctransfertypes.MsgTransfer{ + SourcePort: sourcePort, + SourceChannel: sourceChannel, + Token: coin, + Sender: sender, + Receiver: receiver, + TimeoutHeight: timeoutHeight, + } + + //AsyncBroadcastMsg, SyncBroadcastMsg, QueueBroadcastMsg + response, err := chainClient.AsyncBroadcastMsg(msg) + + if err != nil { + panic(err) + } + + str, _ := json.MarshalIndent(response, "", " ") + fmt.Print(string(str)) +} diff --git a/examples/chain/ibc/transfer/query/1_DenomTrace/example.go b/examples/chain/ibc/transfer/query/1_DenomTrace/example.go new file mode 100644 index 00000000..7653f19a --- /dev/null +++ b/examples/chain/ibc/transfer/query/1_DenomTrace/example.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + + "github.com/InjectiveLabs/sdk-go/client" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "os" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + panic(err) + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + denomTrace := types.DenomTrace{ + Path: "transfer/channel-126", + BaseDenom: "uluna", + } + ctx := context.Background() + + res, err := chainClient.FetchDenomTrace(ctx, denomTrace.Hash().String()) + if err != nil { + fmt.Println(err) + } + + str, _ := json.MarshalIndent(res, "", " ") + fmt.Print(string(str)) + +} diff --git a/examples/chain/ibc/transfer/query/2_DenomTraces/example.go b/examples/chain/ibc/transfer/query/2_DenomTraces/example.go new file mode 100644 index 00000000..1bf3191c --- /dev/null +++ b/examples/chain/ibc/transfer/query/2_DenomTraces/example.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/InjectiveLabs/sdk-go/client" + "github.com/cosmos/cosmos-sdk/types/query" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "os" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + panic(err) + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + pagination := query.PageRequest{Offset: 2, Limit: 4} + ctx := context.Background() + + res, err := chainClient.FetchDenomTraces(ctx, &pagination) + if err != nil { + fmt.Println(err) + } + + str, _ := json.MarshalIndent(res, "", " ") + fmt.Print(string(str)) + +} diff --git a/examples/chain/ibc/transfer/query/3_DenomHash/example.go b/examples/chain/ibc/transfer/query/3_DenomHash/example.go new file mode 100644 index 00000000..1eaef69b --- /dev/null +++ b/examples/chain/ibc/transfer/query/3_DenomHash/example.go @@ -0,0 +1,73 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/InjectiveLabs/sdk-go/client" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "os" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + panic(err) + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + path := "transfer/channel-126" + baseDenom := "uluna" + fullPath := fmt.Sprintf("%s/%s", path, baseDenom) + ctx := context.Background() + + res, err := chainClient.FetchDenomHash(ctx, fullPath) + if err != nil { + fmt.Println(err) + } + + str, _ := json.MarshalIndent(res, "", " ") + fmt.Print(string(str)) + +} diff --git a/examples/chain/ibc/transfer/query/4_EscrowAddress/example.go b/examples/chain/ibc/transfer/query/4_EscrowAddress/example.go new file mode 100644 index 00000000..36922820 --- /dev/null +++ b/examples/chain/ibc/transfer/query/4_EscrowAddress/example.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/InjectiveLabs/sdk-go/client" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "os" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + panic(err) + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + portId := "transfer" + channelId := "channel-126" + ctx := context.Background() + + res, err := chainClient.FetchEscrowAddress(ctx, portId, channelId) + if err != nil { + fmt.Println(err) + } + + str, _ := json.MarshalIndent(res, "", " ") + fmt.Print(string(str)) + +} diff --git a/examples/chain/ibc/transfer/query/5_TotalEscrowForDenom/example.go b/examples/chain/ibc/transfer/query/5_TotalEscrowForDenom/example.go new file mode 100644 index 00000000..f64c0f1c --- /dev/null +++ b/examples/chain/ibc/transfer/query/5_TotalEscrowForDenom/example.go @@ -0,0 +1,71 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/InjectiveLabs/sdk-go/client" + + chainclient "github.com/InjectiveLabs/sdk-go/client/chain" + "github.com/InjectiveLabs/sdk-go/client/common" + rpchttp "github.com/cometbft/cometbft/rpc/client/http" + + "os" +) + +func main() { + network := common.LoadNetwork("testnet", "lb") + tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket") + if err != nil { + panic(err) + } + + senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring( + os.Getenv("HOME")+"/.injectived", + "injectived", + "file", + "inj-user", + "12345678", + "5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided + false, + ) + + if err != nil { + panic(err) + } + + clientCtx, err := chainclient.NewClientContext( + network.ChainId, + senderAddress.String(), + cosmosKeyring, + ) + + if err != nil { + panic(err) + } + + clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient) + + chainClient, err := chainclient.NewChainClient( + clientCtx, + network, + common.OptionGasPrices(client.DefaultGasPriceWithDenom), + ) + + if err != nil { + panic(err) + } + + baseDenom := "uluna" + ctx := context.Background() + + res, err := chainClient.FetchTotalEscrowForDenom(ctx, baseDenom) + if err != nil { + fmt.Println(err) + } + + str, _ := json.MarshalIndent(res, "", " ") + fmt.Print(string(str)) + +}