node: pythnet testnet support (#1380)
This commit is contained in:
parent
d010f0d430
commit
e0fd3e788f
|
@ -128,6 +128,10 @@ var (
|
|||
solanaWsRPC *string
|
||||
solanaRPC *string
|
||||
|
||||
pythnetContract *string
|
||||
pythnetWsRPC *string
|
||||
pythnetRPC *string
|
||||
|
||||
logLevel *string
|
||||
|
||||
unsafeDevMode *bool
|
||||
|
@ -238,6 +242,10 @@ func init() {
|
|||
solanaWsRPC = NodeCmd.Flags().String("solanaWS", "", "Solana Websocket URL (required")
|
||||
solanaRPC = NodeCmd.Flags().String("solanaRPC", "", "Solana RPC URL (required")
|
||||
|
||||
pythnetContract = NodeCmd.Flags().String("pythnetContract", "", "Address of the PythNet program (required)")
|
||||
pythnetWsRPC = NodeCmd.Flags().String("pythnetWS", "", "PythNet Websocket URL (required")
|
||||
pythnetRPC = NodeCmd.Flags().String("pythnetRPC", "", "PythNet RPC URL (required")
|
||||
|
||||
logLevel = NodeCmd.Flags().String("logLevel", "info", "Logging level (debug, info, warn, error, dpanic, panic, fatal)")
|
||||
|
||||
unsafeDevMode = NodeCmd.Flags().Bool("unsafeDevMode", false, "Launch node in unsafe, deterministic devnet mode")
|
||||
|
@ -358,6 +366,9 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
if *solanaWsRPC != "" {
|
||||
readiness.RegisterComponent(common.ReadinessSolanaSyncing)
|
||||
}
|
||||
if *pythnetWsRPC != "" {
|
||||
readiness.RegisterComponent(common.ReadinessPythNetSyncing)
|
||||
}
|
||||
if *terraWS != "" {
|
||||
readiness.RegisterComponent(common.ReadinessTerraSyncing)
|
||||
}
|
||||
|
@ -621,6 +632,16 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
if *algorandAppID == 0 {
|
||||
logger.Fatal("Please specify --algorandAppID")
|
||||
}
|
||||
|
||||
if *pythnetContract == "" {
|
||||
logger.Fatal("Please specify --pythnetContract")
|
||||
}
|
||||
if *pythnetWsRPC == "" {
|
||||
logger.Fatal("Please specify --pythnetWsUrl")
|
||||
}
|
||||
if *pythnetRPC == "" {
|
||||
logger.Fatal("Please specify --pythnetUrl")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -684,6 +705,13 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
logger.Fatal("invalid Solana contract address", zap.Error(err))
|
||||
}
|
||||
var pythnetAddress solana_types.PublicKey
|
||||
if *testnetMode {
|
||||
pythnetAddress, err = solana_types.PublicKeyFromBase58(*pythnetContract)
|
||||
if err != nil {
|
||||
logger.Fatal("invalid PythNet contract address", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// In devnet mode, we generate a deterministic guardian key and write it to disk.
|
||||
if *unsafeDevMode {
|
||||
|
@ -778,6 +806,7 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
chainObsvReqC[vaa.ChainIDNeon] = make(chan *gossipv1.ObservationRequest)
|
||||
chainObsvReqC[vaa.ChainIDEthereumRopsten] = make(chan *gossipv1.ObservationRequest)
|
||||
chainObsvReqC[vaa.ChainIDInjective] = make(chan *gossipv1.ObservationRequest)
|
||||
chainObsvReqC[vaa.ChainIDPythNet] = make(chan *gossipv1.ObservationRequest)
|
||||
}
|
||||
|
||||
// Multiplex observation requests to the appropriate chain
|
||||
|
@ -1007,12 +1036,24 @@ func runNode(cmd *cobra.Command, args []string) {
|
|||
|
||||
if *solanaWsRPC != "" {
|
||||
if err := supervisor.Run(ctx, "solwatch-confirmed",
|
||||
solana.NewSolanaWatcher(*solanaWsRPC, *solanaRPC, solAddress, lockC, nil, rpc.CommitmentConfirmed).Run); err != nil {
|
||||
solana.NewSolanaWatcher(*solanaWsRPC, *solanaRPC, solAddress, lockC, nil, rpc.CommitmentConfirmed, common.ReadinessSolanaSyncing, vaa.ChainIDSolana).Run); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := supervisor.Run(ctx, "solwatch-finalized",
|
||||
solana.NewSolanaWatcher(*solanaWsRPC, *solanaRPC, solAddress, lockC, chainObsvReqC[vaa.ChainIDSolana], rpc.CommitmentFinalized).Run); err != nil {
|
||||
solana.NewSolanaWatcher(*solanaWsRPC, *solanaRPC, solAddress, lockC, chainObsvReqC[vaa.ChainIDSolana], rpc.CommitmentFinalized, common.ReadinessSolanaSyncing, vaa.ChainIDSolana).Run); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if *pythnetWsRPC != "" {
|
||||
if err := supervisor.Run(ctx, "pythwatch-confirmed",
|
||||
solana.NewSolanaWatcher(*pythnetWsRPC, *pythnetRPC, pythnetAddress, lockC, nil, rpc.CommitmentConfirmed, common.ReadinessPythNetSyncing, vaa.ChainIDPythNet).Run); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := supervisor.Run(ctx, "pythwatch-finalized",
|
||||
solana.NewSolanaWatcher(*pythnetWsRPC, *pythnetRPC, pythnetAddress, lockC, chainObsvReqC[vaa.ChainIDPythNet], rpc.CommitmentFinalized, common.ReadinessPythNetSyncing, vaa.ChainIDPythNet).Run); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,4 +22,5 @@ const (
|
|||
ReadinessNeonSyncing readiness.Component = "neonSyncing"
|
||||
ReadinessTerra2Syncing readiness.Component = "terra2Syncing"
|
||||
ReadinessInjectiveSyncing readiness.Component = "injectiveSyncing"
|
||||
ReadinessPythNetSyncing readiness.Component = "pythnetSyncing"
|
||||
)
|
||||
|
|
|
@ -31,6 +31,12 @@ type SolanaWatcher struct {
|
|||
messageEvent chan *common.MessagePublication
|
||||
obsvReqC chan *gossipv1.ObservationRequest
|
||||
rpcClient *rpc.Client
|
||||
// Readiness component
|
||||
readiness readiness.Component
|
||||
// VAA ChainID of the network we're connecting to.
|
||||
chainID vaa.ChainID
|
||||
// Human readable name of network
|
||||
networkName string
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -38,27 +44,27 @@ var (
|
|||
prometheus.CounterOpts{
|
||||
Name: "wormhole_solana_connection_errors_total",
|
||||
Help: "Total number of Solana connection errors",
|
||||
}, []string{"commitment", "reason"})
|
||||
}, []string{"solana_network", "commitment", "reason"})
|
||||
solanaAccountSkips = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "wormhole_solana_account_updates_skipped_total",
|
||||
Help: "Total number of account updates skipped due to invalid data",
|
||||
}, []string{"reason"})
|
||||
solanaMessagesConfirmed = promauto.NewCounter(
|
||||
}, []string{"solana_network", "reason"})
|
||||
solanaMessagesConfirmed = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "wormhole_solana_observations_confirmed_total",
|
||||
Help: "Total number of verified Solana observations found",
|
||||
})
|
||||
}, []string{"solana_network"})
|
||||
currentSolanaHeight = promauto.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "wormhole_solana_current_height",
|
||||
Help: "Current Solana slot height",
|
||||
}, []string{"commitment"})
|
||||
}, []string{"solana_network", "commitment"})
|
||||
queryLatency = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "wormhole_solana_query_latency",
|
||||
Help: "Latency histogram for Solana RPC calls",
|
||||
}, []string{"operation", "commitment"})
|
||||
}, []string{"solana_network", "operation", "commitment"})
|
||||
)
|
||||
|
||||
const rpcTimeout = time.Second * 5
|
||||
|
@ -105,7 +111,9 @@ func NewSolanaWatcher(
|
|||
contractAddress solana.PublicKey,
|
||||
messageEvents chan *common.MessagePublication,
|
||||
obsvReqC chan *gossipv1.ObservationRequest,
|
||||
commitment rpc.CommitmentType) *SolanaWatcher {
|
||||
commitment rpc.CommitmentType,
|
||||
readiness readiness.Component,
|
||||
chainID vaa.ChainID) *SolanaWatcher {
|
||||
return &SolanaWatcher{
|
||||
contract: contractAddress,
|
||||
wsUrl: wsUrl, rpcUrl: rpcUrl,
|
||||
|
@ -113,13 +121,16 @@ func NewSolanaWatcher(
|
|||
obsvReqC: obsvReqC,
|
||||
commitment: commitment,
|
||||
rpcClient: rpc.New(rpcUrl),
|
||||
readiness: readiness,
|
||||
chainID: chainID,
|
||||
networkName: vaa.ChainID(chainID).String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SolanaWatcher) Run(ctx context.Context) error {
|
||||
// Initialize gossip metrics (we want to broadcast the address even if we're not yet syncing)
|
||||
contractAddr := base58.Encode(s.contract[:])
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDSolana, &gossipv1.Heartbeat_Network{
|
||||
p2p.DefaultRegistry.SetNetworkStats(s.chainID, &gossipv1.Heartbeat_Network{
|
||||
ContractAddress: contractAddr,
|
||||
})
|
||||
|
||||
|
@ -136,7 +147,7 @@ func (s *SolanaWatcher) Run(ctx context.Context) error {
|
|||
case <-ctx.Done():
|
||||
return
|
||||
case m := <-s.obsvReqC:
|
||||
if m.ChainId != uint32(vaa.ChainIDSolana) {
|
||||
if m.ChainId != uint32(s.chainID) {
|
||||
panic("unexpected chain id")
|
||||
}
|
||||
|
||||
|
@ -152,19 +163,19 @@ func (s *SolanaWatcher) Run(ctx context.Context) error {
|
|||
start := time.Now()
|
||||
slot, err := s.rpcClient.GetSlot(rCtx, s.commitment)
|
||||
cancel()
|
||||
queryLatency.WithLabelValues("get_slot", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
queryLatency.WithLabelValues(s.networkName, "get_slot", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
if err != nil {
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "get_slot_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "get_slot_error").Inc()
|
||||
errC <- err
|
||||
return
|
||||
}
|
||||
if lastSlot == 0 {
|
||||
lastSlot = slot - 1
|
||||
}
|
||||
currentSolanaHeight.WithLabelValues(string(s.commitment)).Set(float64(slot))
|
||||
readiness.SetReady(common.ReadinessSolanaSyncing)
|
||||
p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDSolana, &gossipv1.Heartbeat_Network{
|
||||
currentSolanaHeight.WithLabelValues(s.networkName, string(s.commitment)).Set(float64(slot))
|
||||
readiness.SetReady(s.readiness)
|
||||
p2p.DefaultRegistry.SetNetworkStats(s.chainID, &gossipv1.Heartbeat_Network{
|
||||
Height: int64(slot),
|
||||
ContractAddress: contractAddr,
|
||||
})
|
||||
|
@ -240,7 +251,7 @@ func (s *SolanaWatcher) fetchBlock(ctx context.Context, logger *zap.Logger, slot
|
|||
Commitment: s.commitment,
|
||||
})
|
||||
|
||||
queryLatency.WithLabelValues("get_confirmed_block", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
queryLatency.WithLabelValues(s.networkName, "get_confirmed_block", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
if err != nil {
|
||||
var rpcErr *jsonrpc.RPCError
|
||||
if errors.As(err, &rpcErr) && (rpcErr.Code == -32007 /* SLOT_SKIPPED */ || rpcErr.Code == -32004 /* BLOCK_NOT_AVAILABLE */) {
|
||||
|
@ -268,17 +279,17 @@ func (s *SolanaWatcher) fetchBlock(ctx context.Context, logger *zap.Logger, slot
|
|||
} else {
|
||||
logger.Error("failed to request block", zap.Error(err), zap.Uint64("slot", slot),
|
||||
zap.String("commitment", string(s.commitment)))
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "get_confirmed_block_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "get_confirmed_block_error").Inc()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if out == nil {
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "get_confirmed_block_error").Inc()
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "get_confirmed_block_error").Inc()
|
||||
logger.Error("nil response when requesting block", zap.Error(err), zap.Uint64("slot", slot),
|
||||
zap.String("commitment", string(s.commitment)))
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -340,10 +351,10 @@ OUTER:
|
|||
Commitment: s.commitment,
|
||||
})
|
||||
cancel()
|
||||
queryLatency.WithLabelValues("get_confirmed_transaction", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
queryLatency.WithLabelValues(s.networkName, "get_confirmed_transaction", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
if err != nil {
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "get_confirmed_transaction_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "get_confirmed_transaction_error").Inc()
|
||||
logger.Error("failed to request transaction",
|
||||
zap.Error(err),
|
||||
zap.Uint64("slot", slot),
|
||||
|
@ -464,10 +475,10 @@ func (s *SolanaWatcher) fetchMessageAccount(ctx context.Context, logger *zap.Log
|
|||
Encoding: solana.EncodingBase64,
|
||||
Commitment: s.commitment,
|
||||
})
|
||||
queryLatency.WithLabelValues("get_account_info", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
queryLatency.WithLabelValues(s.networkName, "get_account_info", string(s.commitment)).Observe(time.Since(start).Seconds())
|
||||
if err != nil {
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "get_account_info_error").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "get_account_info_error").Inc()
|
||||
logger.Error("failed to request account",
|
||||
zap.Error(err),
|
||||
zap.Uint64("slot", slot),
|
||||
|
@ -477,8 +488,8 @@ func (s *SolanaWatcher) fetchMessageAccount(ctx context.Context, logger *zap.Log
|
|||
}
|
||||
|
||||
if !info.Value.Owner.Equals(s.contract) {
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "account_owner_mismatch").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "account_owner_mismatch").Inc()
|
||||
logger.Error("account has invalid owner",
|
||||
zap.Uint64("slot", slot),
|
||||
zap.String("commitment", string(s.commitment)),
|
||||
|
@ -489,8 +500,8 @@ func (s *SolanaWatcher) fetchMessageAccount(ctx context.Context, logger *zap.Log
|
|||
|
||||
data := info.Value.Data.GetBinary()
|
||||
if string(data[:3]) != "msg" && string(data[:3]) != "msu" {
|
||||
p2p.DefaultRegistry.AddErrorCount(vaa.ChainIDSolana, 1)
|
||||
solanaConnectionErrors.WithLabelValues(string(s.commitment), "bad_account_data").Inc()
|
||||
p2p.DefaultRegistry.AddErrorCount(s.chainID, 1)
|
||||
solanaConnectionErrors.WithLabelValues(s.networkName, string(s.commitment), "bad_account_data").Inc()
|
||||
logger.Error("account is not a message account",
|
||||
zap.Uint64("slot", slot),
|
||||
zap.String("commitment", string(s.commitment)),
|
||||
|
@ -511,7 +522,7 @@ func (s *SolanaWatcher) fetchMessageAccount(ctx context.Context, logger *zap.Log
|
|||
func (s *SolanaWatcher) processMessageAccount(logger *zap.Logger, data []byte, acc solana.PublicKey) {
|
||||
proposal, err := ParseMessagePublicationAccount(data)
|
||||
if err != nil {
|
||||
solanaAccountSkips.WithLabelValues("parse_transfer_out").Inc()
|
||||
solanaAccountSkips.WithLabelValues(s.networkName, "parse_transfer_out").Inc()
|
||||
logger.Error(
|
||||
"failed to parse transfer proposal",
|
||||
zap.Stringer("account", acc),
|
||||
|
@ -528,13 +539,13 @@ func (s *SolanaWatcher) processMessageAccount(logger *zap.Logger, data []byte, a
|
|||
Timestamp: time.Unix(int64(proposal.SubmissionTime), 0),
|
||||
Nonce: proposal.Nonce,
|
||||
Sequence: proposal.Sequence,
|
||||
EmitterChain: vaa.ChainIDSolana,
|
||||
EmitterChain: s.chainID,
|
||||
EmitterAddress: proposal.EmitterAddress,
|
||||
Payload: proposal.Payload,
|
||||
ConsistencyLevel: proposal.ConsistencyLevel,
|
||||
}
|
||||
|
||||
solanaMessagesConfirmed.Inc()
|
||||
solanaMessagesConfirmed.WithLabelValues(s.networkName).Inc()
|
||||
|
||||
logger.Info("message observed",
|
||||
zap.Stringer("account", acc),
|
||||
|
|
|
@ -132,6 +132,8 @@ func (c ChainID) String() string {
|
|||
return "terra2"
|
||||
case ChainIDInjective:
|
||||
return "injective"
|
||||
case ChainIDPythNet:
|
||||
return "pythnet"
|
||||
default:
|
||||
return fmt.Sprintf("unknown chain ID: %d", c)
|
||||
}
|
||||
|
@ -179,6 +181,8 @@ func ChainIDFromString(s string) (ChainID, error) {
|
|||
return ChainIDTerra2, nil
|
||||
case "injective":
|
||||
return ChainIDInjective, nil
|
||||
case "pythnet":
|
||||
return ChainIDPythNet, nil
|
||||
default:
|
||||
return ChainIDUnset, fmt.Errorf("unknown chain ID: %s", s)
|
||||
}
|
||||
|
@ -222,6 +226,8 @@ const (
|
|||
ChainIDTerra2 ChainID = 18
|
||||
// ChainIDInjective is the ChainID of Injective
|
||||
ChainIDInjective ChainID = 19
|
||||
// ChainIDPythNet is the ChainID of PythNet
|
||||
ChainIDPythNet ChainID = 26
|
||||
|
||||
// ChainIDEthereumRopsten is the ChainID of Ethereum Ropsten
|
||||
ChainIDEthereumRopsten ChainID = 10001
|
||||
|
|
|
@ -34,6 +34,7 @@ enum ChainID {
|
|||
CHAIN_ID_ARBITRUM = 23;
|
||||
CHAIN_ID_OPTIMISM = 24;
|
||||
CHAIN_ID_GNOSIS = 25;
|
||||
CHAIN_ID_PYTHNET = 26;
|
||||
// Special case - Eth has two testnets. CHAIN_ID_ETHEREUM is Goerli,
|
||||
// but we also want to connect to Ropsten, so we add a separate chain.
|
||||
CHAIN_ID_ETHEREUM_ROPSTEN = 10001;
|
||||
|
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
## 0.5.2
|
||||
|
||||
### Changed
|
||||
### Added
|
||||
|
||||
Added chain ids for Arbitrum, Optimism, and Gnosis
|
||||
Support for PythNet
|
||||
Chain ids for Arbitrum, Optimism, and Gnosis
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Changed
|
||||
### Added
|
||||
|
||||
Chain ids for Injective, Osmosis, Sui, and Aptos
|
||||
|
||||
Added chain ids for Injective, Osmosis, Sui, and Aptos
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
coalesceChainId,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
CHAIN_ID_PYTHNET,
|
||||
} from "./consts";
|
||||
|
||||
/**
|
||||
|
@ -72,7 +73,7 @@ export const tryUint8ArrayToNative = (
|
|||
const chainId = coalesceChainId(chain);
|
||||
if (isEVMChain(chainId)) {
|
||||
return hexZeroPad(hexValue(a), 20);
|
||||
} else if (chainId === CHAIN_ID_SOLANA) {
|
||||
} else if (chainId === CHAIN_ID_SOLANA || chainId === CHAIN_ID_PYTHNET) {
|
||||
return new PublicKey(a).toString();
|
||||
} else if (isTerraChain(chainId)) {
|
||||
const h = uint8ArrayToHex(a);
|
||||
|
@ -188,7 +189,7 @@ export const tryNativeToHexString = (
|
|||
const chainId = coalesceChainId(chain);
|
||||
if (isEVMChain(chainId)) {
|
||||
return uint8ArrayToHex(zeroPad(arrayify(address), 32));
|
||||
} else if (chainId === CHAIN_ID_SOLANA) {
|
||||
} else if (chainId === CHAIN_ID_SOLANA || chainId === CHAIN_ID_PYTHNET) {
|
||||
return uint8ArrayToHex(zeroPad(new PublicKey(address).toBytes(), 32));
|
||||
} else if (chainId === CHAIN_ID_TERRA) {
|
||||
if (isNativeDenom(address)) {
|
||||
|
|
|
@ -25,6 +25,7 @@ export const CHAINS = {
|
|||
arbitrum: 23,
|
||||
optimism: 24,
|
||||
gnosis: 25,
|
||||
pythnet: 26,
|
||||
ropsten: 10001,
|
||||
} as const;
|
||||
|
||||
|
@ -198,6 +199,11 @@ const MAINNET = {
|
|||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
pythnet: {
|
||||
core: undefined,
|
||||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
ropsten: {
|
||||
core: undefined,
|
||||
token_bridge: undefined,
|
||||
|
@ -337,6 +343,11 @@ const TESTNET = {
|
|||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
pythnet: {
|
||||
core: undefined,
|
||||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
ropsten: {
|
||||
core: "0x210c5F5e2AF958B4defFe715Dc621b7a3BA888c5",
|
||||
token_bridge: "0xF174F9A837536C449321df1Ca093Bb96948D5386",
|
||||
|
@ -476,6 +487,11 @@ const DEVNET = {
|
|||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
pythnet: {
|
||||
core: undefined,
|
||||
token_bridge: undefined,
|
||||
nft_bridge: undefined,
|
||||
},
|
||||
ropsten: {
|
||||
core: undefined,
|
||||
token_bridge: undefined,
|
||||
|
@ -547,6 +563,7 @@ export const CHAIN_ID_APTOS = CHAINS["aptos"];
|
|||
export const CHAIN_ID_ARBITRUM = CHAINS["arbitrum"];
|
||||
export const CHAIN_ID_OPTIMISM = CHAINS["optimism"];
|
||||
export const CHAIN_ID_GNOSIS = CHAINS["gnosis"];
|
||||
export const CHAIN_ID_PYTHNET = CHAINS["pythnet"];
|
||||
export const CHAIN_ID_ETHEREUM_ROPSTEN = CHAINS["ropsten"];
|
||||
|
||||
// This inverts the [[CHAINS]] object so that we can look up a chain by id
|
||||
|
|
Loading…
Reference in New Issue