118 lines
2.9 KiB
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
|
|
}
|