wormhole-explorer/tx-tracker/chains/api_cosmos.go

118 lines
2.9 KiB
Go

package chains
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/wormhole-foundation/wormhole-explorer/common/pool"
"github.com/wormhole-foundation/wormhole-explorer/txtracker/internal/metrics"
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
"go.uber.org/zap"
)
const (
cosmosMsgExecuteContract = "/cosmwasm.wasm.v1.MsgExecuteContract"
injectiveMsgExecuteContract = "/injective.wasmx.v1.MsgExecuteContractCompat"
)
// cosmosTxsResponse models the response body from `GET /cosmos/tx/v1beta1/txs/{hash}`
type cosmosTxsResponse struct {
TxResponse struct {
Tx struct {
Body struct {
Messages []struct {
Type_ string `json:"@type"`
Sender string `json:"sender"`
} `json:"messages"`
} `json:"body"`
} `json:"tx"`
Timestamp string `json:"timestamp"`
TxHash string `json:"txhash"`
} `json:"tx_response"`
}
type apiCosmos struct {
chainId sdk.ChainID
}
func (c *apiCosmos) FetchCosmosTx(
ctx context.Context,
pool *pool.Pool,
txHash string,
metrics metrics.Metrics,
logger *zap.Logger,
) (*TxDetail, error) {
// get rpc sorted by score and priority.
rpcs := pool.GetItems()
if len(rpcs) == 0 {
return nil, ErrChainNotSupported
}
var txDetail *TxDetail
var err error
for _, rpc := range rpcs {
// Wait for the RPC rate limiter
rpc.Wait(ctx)
txDetail, err = c.fetchCosmosTx(ctx, rpc.Id, txHash)
if err != nil {
metrics.IncCallRpcError(uint16(c.chainId), rpc.Description)
logger.Debug("Failed to fetch transaction from cosmos node", zap.String("url", rpc.Id), zap.Error(err))
continue
}
metrics.IncCallRpcSuccess(uint16(c.chainId), rpc.Description)
break
}
return txDetail, err
}
func (c *apiCosmos) fetchCosmosTx(
ctx context.Context,
baseUrl string,
txHash string,
) (*TxDetail, error) {
// Call the transaction endpoint of the cosmos REST API
var response cosmosTxsResponse
{
// Perform the HTTP request
uri := fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", baseUrl, txHash)
body, err := httpGet(ctx, uri)
if err != nil {
if strings.Contains(err.Error(), "404") {
return nil, ErrTransactionNotFound
}
return nil, fmt.Errorf("failed to query cosmos tx endpoint: %w", err)
}
// Deserialize response body
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("failed to deserialize cosmos tx response: %w", err)
}
}
// Find the sender address
var sender string
for i := range response.TxResponse.Tx.Body.Messages {
msg := &response.TxResponse.Tx.Body.Messages[i]
if msg.Type_ == cosmosMsgExecuteContract || msg.Type_ == injectiveMsgExecuteContract {
sender = msg.Sender
break
}
}
if sender == "" {
return nil, fmt.Errorf("failed to find sender address in cosmos tx response")
}
// Build the result object and return
TxDetail := &TxDetail{
From: sender,
NativeTxHash: response.TxResponse.TxHash,
}
return TxDetail, nil
}