104 lines
2.5 KiB
Go
104 lines
2.5 KiB
Go
package chains
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
|
|
"github.com/wormhole-foundation/wormhole-explorer/txtracker/config"
|
|
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
)
|
|
|
|
const requestTimeout = 30 * time.Second
|
|
|
|
var (
|
|
ErrChainNotSupported = errors.New("chain id not supported")
|
|
ErrTransactionNotFound = errors.New("transaction not found")
|
|
)
|
|
|
|
type TxDetail struct {
|
|
// Signer is the address that signed the transaction, encoded in the chain's native format.
|
|
Signer string
|
|
// Timestamp indicates the time at which the transaction was confirmed.
|
|
Timestamp time.Time
|
|
// NativeTxHash contains the transaction hash, encoded in the chain's native format.
|
|
NativeTxHash string
|
|
}
|
|
|
|
var tickers = struct {
|
|
ankr *time.Ticker
|
|
solana *time.Ticker
|
|
terra *time.Ticker
|
|
}{}
|
|
|
|
func Initialize(cfg *config.Settings) {
|
|
|
|
// f converts "requests per minute" into the associated time.Duration
|
|
f := func(requestsPerMinute uint16) time.Duration {
|
|
|
|
division := float64(time.Minute) / float64(time.Duration(requestsPerMinute))
|
|
roundedUp := math.Ceil(division)
|
|
|
|
return time.Duration(roundedUp)
|
|
}
|
|
|
|
tickers.ankr = time.NewTicker(f(cfg.AnkrRequestsPerMinute))
|
|
tickers.terra = time.NewTicker(f(cfg.TerraRequestsPerMinute))
|
|
|
|
// the Solana adapter sends 2 requests per txHash
|
|
tickers.solana = time.NewTicker(f(cfg.SolanaRequestsPerMinute / 2))
|
|
}
|
|
|
|
func FetchTx(
|
|
ctx context.Context,
|
|
cfg *config.Settings,
|
|
chainId vaa.ChainID,
|
|
txHash string,
|
|
) (*TxDetail, error) {
|
|
|
|
var fetchFunc func(context.Context, *config.Settings, string) (*TxDetail, error)
|
|
var rateLimiter time.Ticker
|
|
|
|
// decide which RPC/API service to use based on chain ID
|
|
switch chainId {
|
|
case vaa.ChainIDSolana:
|
|
fetchFunc = fetchSolanaTx
|
|
rateLimiter = *tickers.solana
|
|
case vaa.ChainIDTerra:
|
|
fetchFunc = fetchTerraTx
|
|
rateLimiter = *tickers.terra
|
|
// most EVM-compatible chains use the same RPC service
|
|
case vaa.ChainIDEthereum,
|
|
vaa.ChainIDBSC,
|
|
vaa.ChainIDPolygon,
|
|
vaa.ChainIDAvalanche,
|
|
vaa.ChainIDFantom,
|
|
vaa.ChainIDArbitrum,
|
|
vaa.ChainIDOptimism:
|
|
|
|
fetchFunc = ankrFetchTx
|
|
rateLimiter = *tickers.ankr
|
|
default:
|
|
return nil, ErrChainNotSupported
|
|
}
|
|
|
|
// wait for rate limit - fail fast if context was cancelled
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
case <-rateLimiter.C:
|
|
}
|
|
|
|
// get transaction details from the RPC/API service
|
|
subContext, cancelFunc := context.WithTimeout(ctx, requestTimeout)
|
|
defer cancelFunc()
|
|
txDetail, err := fetchFunc(subContext, cfg, txHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve tx information: %w", err)
|
|
}
|
|
|
|
return txDetail, nil
|
|
}
|