2023-07-04 11:25:08 -07:00
|
|
|
package chains
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/txtracker/config"
|
|
|
|
sdk "github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrChainNotSupported = errors.New("chain id not supported")
|
|
|
|
ErrTransactionNotFound = errors.New("transaction not found")
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// rateLimitersByChain maps a chain ID to the request rate limiter for that chain.
|
|
|
|
rateLimitersByChain map[sdk.ChainID]*time.Ticker
|
|
|
|
// baseUrlsByChain maps a chain ID to the base URL of the RPC/API service for that chain.
|
|
|
|
baseUrlsByChain map[sdk.ChainID]string
|
|
|
|
)
|
|
|
|
|
2023-09-04 11:17:23 -07:00
|
|
|
// WARNING: The following chain IDs are not supported by the wormhole-sdk:
|
|
|
|
const ChainIDOsmosis sdk.ChainID = 20
|
2023-10-26 15:34:00 -07:00
|
|
|
const ChainIDEvmos sdk.ChainID = 4001
|
|
|
|
const ChainIDKujira sdk.ChainID = 4002
|
2023-09-04 11:17:23 -07:00
|
|
|
|
|
|
|
type WormchainTxDetail struct {
|
|
|
|
}
|
2023-07-04 11:25:08 -07:00
|
|
|
type TxDetail struct {
|
|
|
|
// From is the address that signed the transaction, encoded in the chain's native format.
|
|
|
|
From string
|
|
|
|
// NativeTxHash contains the transaction hash, encoded in the chain's native format.
|
|
|
|
NativeTxHash string
|
2023-09-04 11:17:23 -07:00
|
|
|
// Attribute contains the specific information of the transaction.
|
|
|
|
Attribute *AttributeTxDetail
|
|
|
|
}
|
|
|
|
|
|
|
|
type AttributeTxDetail struct {
|
|
|
|
Type string
|
|
|
|
Value any
|
2023-07-04 11:25:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func Initialize(cfg *config.RpcProviderSettings) {
|
|
|
|
|
|
|
|
// convertToRateLimiter converts "requests per minute" into the associated *time.Ticker
|
|
|
|
convertToRateLimiter := func(requestsPerMinute uint16) *time.Ticker {
|
|
|
|
|
|
|
|
division := float64(time.Minute) / float64(time.Duration(requestsPerMinute))
|
|
|
|
roundedUp := math.Ceil(division)
|
|
|
|
|
|
|
|
duration := time.Duration(roundedUp)
|
|
|
|
|
|
|
|
return time.NewTicker(duration)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize rate limiters for each chain
|
|
|
|
rateLimitersByChain = make(map[sdk.ChainID]*time.Ticker)
|
2023-07-05 10:53:00 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDAcala] = convertToRateLimiter(cfg.AcalaRequestsPerMinute)
|
2023-07-04 11:25:08 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDArbitrum] = convertToRateLimiter(cfg.ArbitrumRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDAlgorand] = convertToRateLimiter(cfg.AlgorandRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDAptos] = convertToRateLimiter(cfg.AptosRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDAvalanche] = convertToRateLimiter(cfg.AvalancheRequestsPerMinute)
|
2023-07-20 12:37:21 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDBase] = convertToRateLimiter(cfg.BaseRequestsPerMinute)
|
2023-07-04 11:25:08 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDBSC] = convertToRateLimiter(cfg.BscRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDCelo] = convertToRateLimiter(cfg.CeloRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDEthereum] = convertToRateLimiter(cfg.EthereumRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDFantom] = convertToRateLimiter(cfg.FantomRequestsPerMinute)
|
2023-07-05 10:53:00 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDInjective] = convertToRateLimiter(cfg.InjectiveRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDKarura] = convertToRateLimiter(cfg.KaruraRequestsPerMinute)
|
2023-07-04 11:25:08 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDKlaytn] = convertToRateLimiter(cfg.KlaytnRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDMoonbeam] = convertToRateLimiter(cfg.MoonbeamRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDOasis] = convertToRateLimiter(cfg.OasisRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDOptimism] = convertToRateLimiter(cfg.OptimismRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDPolygon] = convertToRateLimiter(cfg.PolygonRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDSolana] = convertToRateLimiter(cfg.SolanaRequestsPerMinute)
|
2023-07-05 10:53:00 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDTerra] = convertToRateLimiter(cfg.TerraRequestsPerMinute)
|
2023-07-04 11:25:08 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDTerra2] = convertToRateLimiter(cfg.Terra2RequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDSui] = convertToRateLimiter(cfg.SuiRequestsPerMinute)
|
|
|
|
rateLimitersByChain[sdk.ChainIDXpla] = convertToRateLimiter(cfg.XplaRequestsPerMinute)
|
2023-09-04 11:17:23 -07:00
|
|
|
rateLimitersByChain[sdk.ChainIDWormchain] = convertToRateLimiter(cfg.WormchainRequestsPerMinute)
|
|
|
|
rateLimitersByChain[ChainIDOsmosis] = convertToRateLimiter(cfg.OsmosisRequestsPerMinute)
|
2023-11-15 07:11:24 -08:00
|
|
|
rateLimitersByChain[sdk.ChainIDSei] = convertToRateLimiter(cfg.SeiRequestsPerMinute)
|
2023-07-04 11:25:08 -07:00
|
|
|
|
|
|
|
// Initialize the RPC base URLs for each chain
|
|
|
|
baseUrlsByChain = make(map[sdk.ChainID]string)
|
2023-07-05 10:53:00 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDAcala] = cfg.AcalaBaseUrl
|
2023-07-04 11:25:08 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDArbitrum] = cfg.ArbitrumBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDAlgorand] = cfg.AlgorandBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDAptos] = cfg.AptosBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDAvalanche] = cfg.AvalancheBaseUrl
|
2023-07-20 12:37:21 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDBase] = cfg.BaseBaseUrl
|
2023-07-04 11:25:08 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDBSC] = cfg.BscBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDCelo] = cfg.CeloBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDEthereum] = cfg.EthereumBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDFantom] = cfg.FantomBaseUrl
|
2023-07-05 10:53:00 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDInjective] = cfg.InjectiveBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDKarura] = cfg.KaruraBaseUrl
|
2023-07-04 11:25:08 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDKlaytn] = cfg.KlaytnBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDMoonbeam] = cfg.MoonbeamBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDOasis] = cfg.OasisBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDOptimism] = cfg.OptimismBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDPolygon] = cfg.PolygonBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDSolana] = cfg.SolanaBaseUrl
|
2023-07-05 10:53:00 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDTerra] = cfg.TerraBaseUrl
|
2023-07-04 11:25:08 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDTerra2] = cfg.Terra2BaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDSui] = cfg.SuiBaseUrl
|
|
|
|
baseUrlsByChain[sdk.ChainIDXpla] = cfg.XplaBaseUrl
|
2023-09-04 11:17:23 -07:00
|
|
|
baseUrlsByChain[sdk.ChainIDWormchain] = cfg.WormchainBaseUrl
|
2023-11-15 07:11:24 -08:00
|
|
|
baseUrlsByChain[sdk.ChainIDSei] = cfg.SeiBaseUrl
|
2023-07-04 11:25:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func FetchTx(
|
|
|
|
ctx context.Context,
|
|
|
|
cfg *config.RpcProviderSettings,
|
|
|
|
chainId sdk.ChainID,
|
|
|
|
txHash string,
|
2023-09-04 11:17:23 -07:00
|
|
|
p2pNetwork string,
|
2023-07-04 11:25:08 -07:00
|
|
|
) (*TxDetail, error) {
|
|
|
|
|
|
|
|
// Decide which RPC/API service to use based on chain ID
|
|
|
|
var fetchFunc func(ctx context.Context, rateLimiter *time.Ticker, baseUrl string, txHash string) (*TxDetail, error)
|
|
|
|
switch chainId {
|
|
|
|
case sdk.ChainIDSolana:
|
|
|
|
fetchFunc = fetchSolanaTx
|
|
|
|
case sdk.ChainIDAlgorand:
|
|
|
|
fetchFunc = fetchAlgorandTx
|
|
|
|
case sdk.ChainIDAptos:
|
|
|
|
fetchFunc = fetchAptosTx
|
|
|
|
case sdk.ChainIDSui:
|
|
|
|
fetchFunc = fetchSuiTx
|
2023-07-05 10:53:00 -07:00
|
|
|
case sdk.ChainIDInjective,
|
|
|
|
sdk.ChainIDTerra,
|
|
|
|
sdk.ChainIDTerra2,
|
2023-07-04 11:25:08 -07:00
|
|
|
sdk.ChainIDXpla:
|
|
|
|
fetchFunc = fetchCosmosTx
|
2023-07-05 10:53:00 -07:00
|
|
|
case sdk.ChainIDAcala,
|
|
|
|
sdk.ChainIDArbitrum,
|
2023-07-04 11:25:08 -07:00
|
|
|
sdk.ChainIDAvalanche,
|
2023-07-20 12:37:21 -07:00
|
|
|
sdk.ChainIDBase,
|
2023-07-04 11:25:08 -07:00
|
|
|
sdk.ChainIDBSC,
|
|
|
|
sdk.ChainIDCelo,
|
|
|
|
sdk.ChainIDEthereum,
|
|
|
|
sdk.ChainIDFantom,
|
2023-07-05 10:53:00 -07:00
|
|
|
sdk.ChainIDKarura,
|
2023-07-04 11:25:08 -07:00
|
|
|
sdk.ChainIDKlaytn,
|
|
|
|
sdk.ChainIDMoonbeam,
|
|
|
|
sdk.ChainIDOasis,
|
|
|
|
sdk.ChainIDOptimism,
|
|
|
|
sdk.ChainIDPolygon:
|
|
|
|
fetchFunc = fetchEthTx
|
2023-09-04 11:17:23 -07:00
|
|
|
case sdk.ChainIDWormchain:
|
|
|
|
rateLimiter, ok := rateLimitersByChain[ChainIDOsmosis]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("found no rate limiter for chain osmosis")
|
|
|
|
}
|
|
|
|
apiWormchain := &apiWormchain{
|
|
|
|
osmosisUrl: cfg.OsmosisBaseUrl,
|
|
|
|
osmosisRateLimiter: rateLimiter,
|
2023-10-26 15:34:00 -07:00
|
|
|
evmosUrl: cfg.EvmosBaseUrl,
|
|
|
|
evmosRateLimiter: rateLimiter,
|
|
|
|
kujiraUrl: cfg.KujiraBaseUrl,
|
|
|
|
kujiraRateLimiter: rateLimiter,
|
2023-09-04 11:17:23 -07:00
|
|
|
p2pNetwork: p2pNetwork,
|
|
|
|
}
|
|
|
|
fetchFunc = apiWormchain.fetchWormchainTx
|
2023-11-15 07:11:24 -08:00
|
|
|
case sdk.ChainIDSei:
|
|
|
|
rateLimiter, ok := rateLimitersByChain[sdk.ChainIDWormchain]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("found no rate limiter for chain osmosis")
|
|
|
|
}
|
|
|
|
apiSei := &apiSei{
|
|
|
|
wormchainRateLimiter: rateLimiter,
|
|
|
|
wormchainUrl: cfg.WormchainBaseUrl,
|
|
|
|
p2pNetwork: p2pNetwork,
|
|
|
|
}
|
|
|
|
fetchFunc = apiSei.fetchSeiTx
|
|
|
|
|
2023-07-04 11:25:08 -07:00
|
|
|
default:
|
|
|
|
return nil, ErrChainNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the rate limiter and base URL for the given chain ID
|
|
|
|
rateLimiter, ok := rateLimitersByChain[chainId]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("found no rate limiter for chain %s", chainId.String())
|
|
|
|
}
|
|
|
|
baseUrl, ok := baseUrlsByChain[chainId]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("found no base URL for chain %s", chainId.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get transaction details from the RPC/API service
|
|
|
|
txDetail, err := fetchFunc(ctx, rateLimiter, baseUrl, txHash)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to retrieve tx information: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return txDetail, nil
|
|
|
|
}
|