Add support for cosmos chains in `tx-tracker` (#426)
### Description Tracking issue: https://github.com/wormhole-foundation/wormhole-explorer/issues/418 This pull request modifies the `tx-tracker` service to support `ChainIDTerra2` and `ChainIDXpla`. In particular, this will make sender addresses from these blockchains available for the Wormhole Scan UI. Support for `ChainIDTerra` was left out due to lack of working public RPC nodes (`https://lcd.terra.dev` doesn't seem to be functioning correctly at the moment).
This commit is contained in:
parent
2c476dd696
commit
4b4b6f61ca
|
@ -6,5 +6,48 @@ RESOURCES_LIMITS_MEMORY=256Mi
|
|||
RESOURCES_LIMITS_CPU=500m
|
||||
RESOURCES_REQUESTS_MEMORY=128Mi
|
||||
RESOURCES_REQUESTS_CPU=250m
|
||||
SOLANA_BASE_URL=https://api.mainnet-beta.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=2
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=2
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=2
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=2
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=2
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=2
|
|
@ -7,9 +7,51 @@ RESOURCES_LIMITS_CPU=500m
|
|||
RESOURCES_REQUESTS_MEMORY=64Mi
|
||||
RESOURCES_REQUESTS_CPU=250m
|
||||
|
||||
SOLANA_BASE_URL=https://api.mainnet-beta.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
|
||||
STRATEGY_NAME=time_range
|
||||
STRATEGY_TIMESTAMP_AFTER=2023-01-01T00:00:00.000Z
|
||||
STRATEGY_TIMESTAMP_BEFORE=2023-04-01T00:00:00.000Z
|
||||
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=1
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=1
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=1
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=1
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=1
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=1
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=1
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=1
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=1
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=1
|
|
@ -6,5 +6,48 @@ RESOURCES_LIMITS_MEMORY=128Mi
|
|||
RESOURCES_LIMITS_CPU=200m
|
||||
RESOURCES_REQUESTS_MEMORY=64Mi
|
||||
RESOURCES_REQUESTS_CPU=100m
|
||||
SOLANA_BASE_URL=https://api.mainnet-beta.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=1
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=1
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=1
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=1
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=1
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=1
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=1
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=1
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=1
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=1
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=1
|
||||
|
|
|
@ -83,6 +83,14 @@ spec:
|
|||
value: {{ .SUI_BASE_URL }}
|
||||
- name: SUI_REQUESTS_PER_MINUTE
|
||||
value: "{{ .SUI_REQUESTS_PER_MINUTE }}"
|
||||
- name: TERRA2_BASE_URL
|
||||
value: {{ .TERRA2_BASE_URL }}
|
||||
- name: TERRA2_REQUESTS_PER_MINUTE
|
||||
value: "{{ .TERRA2_REQUESTS_PER_MINUTE }}"
|
||||
- name: XPLA_BASE_URL
|
||||
value: {{ .XPLA_BASE_URL }}
|
||||
- name: XPLA_REQUESTS_PER_MINUTE
|
||||
value: "{{ .XPLA_REQUESTS_PER_MINUTE }}"
|
||||
- name: NUM_WORKERS
|
||||
value: "100"
|
||||
- name: BULK_SIZE
|
||||
|
|
|
@ -9,6 +9,49 @@ RESOURCES_REQUESTS_MEMORY=128Mi
|
|||
RESOURCES_REQUESTS_CPU=250m
|
||||
SQS_URL=
|
||||
SQS_AWS_REGION=
|
||||
SOLANA_BASE_URL=https://api.mainnet-beta.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
AWS_IAM_ROLE=
|
||||
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=8
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=8
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=4
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=8
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=8
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=8
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=8
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=8
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=8
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=8
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=8
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=4
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=4
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=4
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=4
|
|
@ -9,7 +9,49 @@ RESOURCES_REQUESTS_MEMORY=15Mi
|
|||
RESOURCES_REQUESTS_CPU=40m
|
||||
SQS_URL=
|
||||
SQS_AWS_REGION=
|
||||
AWS_IAM_ROLE=
|
||||
|
||||
SOLANA_BASE_URL=https://api.mainnet-beta.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
AWS_IAM_ROLE=
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=2
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=2
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=2
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=2
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=2
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=2
|
|
@ -9,6 +9,49 @@ RESOURCES_REQUESTS_MEMORY=15Mi
|
|||
RESOURCES_REQUESTS_CPU=10m
|
||||
SQS_URL=
|
||||
SQS_AWS_REGION=
|
||||
SOLANA_BASE_URL=https://api.devnet.solana.com
|
||||
SOLANA_REQUESTS_PER_MINUTE=6
|
||||
AWS_IAM_ROLE=
|
||||
AWS_IAM_ROLE=
|
||||
|
||||
APTOS_BASE_URL=
|
||||
APTOS_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ARBITRUM_BASE_URL=
|
||||
ARBITRUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
AVALANCHE_BASE_URL=
|
||||
AVALANCHE_REQUESTS_PER_MINUTE=2
|
||||
|
||||
BSC_BASE_URL=
|
||||
BSC_REQUESTS_PER_MINUTE=2
|
||||
|
||||
CELO_BASE_URL=
|
||||
CELO_REQUESTS_PER_MINUTE=2
|
||||
|
||||
ETHEREUM_BASE_URL=
|
||||
ETHEREUM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
FANTOM_BASE_URL=
|
||||
FANTOM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
KLAYTN_BASE_URL=
|
||||
KLAYTN_REQUESTS_PER_MINUTE=2
|
||||
|
||||
MOONBEAM_BASE_URL=
|
||||
MOONBEAM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
OPTIMISM_BASE_URL=
|
||||
OPTIMISM_REQUESTS_PER_MINUTE=2
|
||||
|
||||
POLYGON_BASE_URL=
|
||||
POLYGON_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SOLANA_BASE_URL=
|
||||
SOLANA_REQUESTS_PER_MINUTE=2
|
||||
|
||||
SUI_BASE_URL=
|
||||
SUI_REQUESTS_PER_MINUTE=2
|
||||
|
||||
TERRA2_BASE_URL=
|
||||
TERRA2_REQUESTS_PER_MINUTE=2
|
||||
|
||||
XPLA_BASE_URL=
|
||||
XPLA_REQUESTS_PER_MINUTE=2
|
|
@ -109,6 +109,14 @@ spec:
|
|||
value: {{ .SUI_BASE_URL }}
|
||||
- name: SUI_REQUESTS_PER_MINUTE
|
||||
value: "{{ .SUI_REQUESTS_PER_MINUTE }}"
|
||||
- name: TERRA2_BASE_URL
|
||||
value: {{ .TERRA2_BASE_URL }}
|
||||
- name: TERRA2_REQUESTS_PER_MINUTE
|
||||
value: "{{ .TERRA2_REQUESTS_PER_MINUTE }}"
|
||||
- name: XPLA_BASE_URL
|
||||
value: {{ .XPLA_BASE_URL }}
|
||||
- name: XPLA_REQUESTS_PER_MINUTE
|
||||
value: "{{ .XPLA_REQUESTS_PER_MINUTE }}"
|
||||
resources:
|
||||
limits:
|
||||
memory: {{ .RESOURCES_LIMITS_MEMORY }}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package chains
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
cosmosMsgExecuteContract = "/cosmwasm.wasm.v1.MsgExecuteContract"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func fetchCosmosTx(
|
||||
ctx context.Context,
|
||||
baseUri string,
|
||||
txHash string,
|
||||
) (*TxDetail, error) {
|
||||
|
||||
// Query the Cosmos transaction endpoint
|
||||
uri := fmt.Sprintf("%s/cosmos/tx/v1beta1/txs/%s", baseUri, txHash)
|
||||
body, err := httpGet(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query cosmos tx endpoint: %w", err)
|
||||
}
|
||||
|
||||
// Deserialize response body
|
||||
var response cosmosTxsResponse
|
||||
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 {
|
||||
sender = msg.Sender
|
||||
break
|
||||
}
|
||||
}
|
||||
if sender == "" {
|
||||
return nil, fmt.Errorf("failed to find sender address in cosmos tx response")
|
||||
}
|
||||
|
||||
// Parse the timestamp
|
||||
timestamp, err := time.Parse("2006-01-02T15:04:05Z", response.TxResponse.Timestamp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse tx timestamp from cosmos tx response: %w", err)
|
||||
}
|
||||
|
||||
// Build the result object and return
|
||||
TxDetail := &TxDetail{
|
||||
From: sender,
|
||||
Timestamp: timestamp,
|
||||
NativeTxHash: response.TxResponse.TxHash,
|
||||
}
|
||||
return TxDetail, nil
|
||||
}
|
|
@ -45,6 +45,8 @@ var tickers = struct {
|
|||
polygon *time.Ticker
|
||||
solana *time.Ticker
|
||||
sui *time.Ticker
|
||||
terra2 *time.Ticker
|
||||
xpla *time.Ticker
|
||||
}{}
|
||||
|
||||
func Initialize(cfg *config.RpcProviderSettings) {
|
||||
|
@ -58,8 +60,10 @@ func Initialize(cfg *config.RpcProviderSettings) {
|
|||
return time.Duration(roundedUp)
|
||||
}
|
||||
|
||||
// this adapter sends 1 request per txHash
|
||||
// these adapters send 1 request per txHash
|
||||
tickers.sui = time.NewTicker(f(cfg.SuiRequestsPerMinute))
|
||||
tickers.terra2 = time.NewTicker(f(cfg.Terra2RequestsPerMinute))
|
||||
tickers.xpla = time.NewTicker(f(cfg.XplaRequestsPerMinute))
|
||||
|
||||
// these adapters send 2 requests per txHash
|
||||
tickers.aptos = time.NewTicker(f(cfg.AptosRequestsPerMinute / 2))
|
||||
|
@ -147,6 +151,16 @@ func FetchTx(
|
|||
case vaa.ChainIDSui:
|
||||
fetchFunc = fetchSuiTx
|
||||
rateLimiter = *tickers.sui
|
||||
case vaa.ChainIDTerra2:
|
||||
fetchFunc = func(ctx context.Context, cfg *config.RpcProviderSettings, txHash string) (*TxDetail, error) {
|
||||
return fetchCosmosTx(ctx, cfg.Terra2BaseUrl, txHash)
|
||||
}
|
||||
rateLimiter = *tickers.terra2
|
||||
case vaa.ChainIDXpla:
|
||||
fetchFunc = func(ctx context.Context, cfg *config.RpcProviderSettings, txHash string) (*TxDetail, error) {
|
||||
return fetchCosmosTx(ctx, cfg.XplaBaseUrl, txHash)
|
||||
}
|
||||
rateLimiter = *tickers.xpla
|
||||
default:
|
||||
return nil, ErrChainNotSupported
|
||||
}
|
||||
|
@ -203,6 +217,9 @@ func httpGet(ctx context.Context, url string) ([]byte, error) {
|
|||
return nil, fmt.Errorf("failed to query url: %w", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("unexpected HTTP status code: %d", response.StatusCode)
|
||||
}
|
||||
|
||||
// Read the response body and return
|
||||
body, err := ioutil.ReadAll(response.Body)
|
||||
|
|
|
@ -85,6 +85,10 @@ type RpcProviderSettings struct {
|
|||
SolanaRequestsPerMinute uint16 `split_words:"true" required:"true"`
|
||||
SuiBaseUrl string `split_words:"true" required:"true"`
|
||||
SuiRequestsPerMinute uint16 `split_words:"true" required:"true"`
|
||||
Terra2BaseUrl string `split_words:"true" required:"true"`
|
||||
Terra2RequestsPerMinute uint16 `split_words:"true" required:"true"`
|
||||
XplaBaseUrl string `split_words:"true" required:"true"`
|
||||
XplaRequestsPerMinute uint16 `split_words:"true" required:"true"`
|
||||
}
|
||||
|
||||
func LoadFromEnv[T any]() (*T, error) {
|
||||
|
|
Loading…
Reference in New Issue