chain_config.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. package evm
  2. // This file defines the set of EVM chains supported by the guardian watcher, including their EVM chain IDs and whether they support finalized and / or safe blocks.
  3. // There is data for both Mainnet and Testnet. A chain should only be populated in the tables if the watcher should be allowed in that environment.
  4. // Wherever possible, a public RPC endpoint is included so the verification tool can confirm that the EVM chain IDs specified here match the values of a known public node.
  5. // NOTE: Whenever changes are made to the config data in this file, you should do the following:
  6. // node/pkg/watcher/evm$ go test
  7. // node/pkg/watcher/evm/verify_chain_config$ go run verify.go
  8. import (
  9. "context"
  10. "errors"
  11. "fmt"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/certusone/wormhole/node/pkg/common"
  16. ethCommon "github.com/ethereum/go-ethereum/common"
  17. "github.com/ethereum/go-ethereum/rpc"
  18. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  19. "go.uber.org/zap"
  20. )
  21. type (
  22. // EnvEntry specifies the config data for a given chain / environment.
  23. EnvEntry struct {
  24. // InstantFinality indicates that the chain has instant finality, meaning finalized and safe blocks can be generated along with the latest block.
  25. // Note that if InstantFinality is set to true, Finalized and Safe are for documentation purposes only.
  26. InstantFinality bool
  27. // Finalized indicates if the chain supports querying for finalized blocks.
  28. Finalized bool
  29. // Safe indicates if the chain supports querying for safe blocks.
  30. Safe bool
  31. // EvmChainID is the expected EVM chain ID (what is returned by the `eth_chainId` RPC call).
  32. EvmChainID uint64
  33. // PublicRPC is not actually used by the watcher. It's used to verify that the EvmChainID specified here is correct.
  34. PublicRPC string
  35. // ContractAddr specifies the Wormhole core contract address for this chain (starting with 0x).
  36. // SECURITY: This is for documentation and validation only. Allowing it as a default would provide a single point attack vector.
  37. ContractAddr string
  38. // CCLContractAddr specifies the address of the custom consistency level contract for this chain (starting with 0x).
  39. // SECURITY: This is for documentation and validation only. Allowing it as a default would provide a single point attack vector.
  40. CCLContractAddr string
  41. }
  42. // EnvMap defines the config data for a given environment (mainet or testnet).
  43. EnvMap map[vaa.ChainID]EnvEntry
  44. )
  45. var (
  46. ErrInvalidEnv = errors.New("invalid environment")
  47. ErrNotFound = errors.New("not found")
  48. // mainnetChainConfig specifies the configuration for all chains enabled in Mainnet.
  49. // NOTE: Only add a chain here if the watcher should allow it in Mainnet!
  50. // NOTE: If you change this data, be sure and run the tests described at the top of this file!
  51. mainnetChainConfig = EnvMap{
  52. vaa.ChainIDEthereum: {Finalized: true, Safe: true, EvmChainID: 1, PublicRPC: "https://ethereum-rpc.publicnode.com", ContractAddr: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"},
  53. vaa.ChainIDBSC: {Finalized: true, Safe: true, EvmChainID: 56, PublicRPC: "https://bsc-rpc.publicnode.com", ContractAddr: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B"},
  54. // Polygon supports polling for finalized but not safe: https://forum.polygon.technology/t/optimizing-decentralized-apps-ux-with-milestones-a-significantly-accelerated-finality-solution/13154
  55. vaa.ChainIDPolygon: {Finalized: true, Safe: false, EvmChainID: 137, PublicRPC: "https://polygon-bor-rpc.publicnode.com", ContractAddr: "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7"},
  56. vaa.ChainIDAvalanche: {InstantFinality: true, Finalized: true, Safe: true, EvmChainID: 43114, PublicRPC: "https://avalanche-c-chain-rpc.publicnode.com", ContractAddr: "0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c"},
  57. vaa.ChainIDFantom: {InstantFinality: true, Finalized: false, Safe: false, EvmChainID: 250, PublicRPC: "https://fantom-rpc.publicnode.com", ContractAddr: "0x126783A6Cb203a3E35344528B26ca3a0489a1485"},
  58. vaa.ChainIDKlaytn: {InstantFinality: true, Finalized: false, Safe: false, EvmChainID: 8217, PublicRPC: "https://public-en.node.kaia.io", ContractAddr: "0x0C21603c4f3a6387e241c0091A7EA39E43E90bb7"},
  59. vaa.ChainIDCelo: {Finalized: true, Safe: false, EvmChainID: 42220, PublicRPC: "https://celo-rpc.publicnode.com", ContractAddr: "0xa321448d90d4e5b0A732867c18eA198e75CAC48E"},
  60. vaa.ChainIDMoonbeam: {Finalized: true, Safe: true, EvmChainID: 1284, PublicRPC: "https://moonbeam-rpc.publicnode.com", ContractAddr: "0xC8e2b0cD52Cf01b0Ce87d389Daa3d414d4cE29f3"},
  61. vaa.ChainIDArbitrum: {Finalized: true, Safe: true, EvmChainID: 42161, PublicRPC: "https://arbitrum-one-rpc.publicnode.com", ContractAddr: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46"},
  62. vaa.ChainIDOptimism: {Finalized: true, Safe: true, EvmChainID: 10, PublicRPC: "https://optimism-rpc.publicnode.com", ContractAddr: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722"},
  63. // vaa.ChainIDGnosis: Not supported in the guardian.
  64. // vaa.ChainIDBtc: Not supported in the guardian.
  65. vaa.ChainIDBase: {Finalized: true, Safe: true, EvmChainID: 8453, PublicRPC: "https://base-rpc.publicnode.com", ContractAddr: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6"},
  66. // vaa.ChainIDFileCoin: Not supported in the guardian.
  67. // vaa.ChainIDRootstock: Not supported in the guardian.
  68. // As of 11/10/2023 Scroll supports polling for finalized but not safe.
  69. vaa.ChainIDScroll: {Finalized: true, Safe: false, EvmChainID: 534352, PublicRPC: "https://scroll-rpc.publicnode.com", ContractAddr: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6"},
  70. vaa.ChainIDMantle: {Finalized: true, Safe: true, EvmChainID: 5000, PublicRPC: "https://mantle-rpc.publicnode.com", ContractAddr: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6"},
  71. vaa.ChainIDXLayer: {Finalized: true, Safe: true, EvmChainID: 196, PublicRPC: "https://xlayerrpc.okx.com", ContractAddr: "0x194B123c5E96B9b2E49763619985790Dc241CAC0"},
  72. // As of 9/06/2024 Linea supports polling for finalized but not safe.
  73. vaa.ChainIDLinea: {Finalized: true, Safe: false, EvmChainID: 59144, PublicRPC: "https://rpc.linea.build", ContractAddr: "0x0C56aebD76E6D9e4a1Ec5e94F4162B4CBbf77b32"},
  74. vaa.ChainIDBerachain: {Finalized: true, Safe: true, EvmChainID: 80094, PublicRPC: "https://berachain-rpc.publicnode.com", ContractAddr: "0xCa1D5a146B03f6303baF59e5AD5615ae0b9d146D"},
  75. vaa.ChainIDSeiEVM: {Finalized: true, Safe: true, EvmChainID: 1329, PublicRPC: "https://evm-rpc.sei-apis.com", ContractAddr: "0xCa1D5a146B03f6303baF59e5AD5615ae0b9d146D"},
  76. // vaa.ChainIDEclipse: Not supported in the guardian.
  77. // vaa.ChainIDBOB: Not supported in the guardian.
  78. vaa.ChainIDUnichain: {Finalized: true, Safe: true, EvmChainID: 130, PublicRPC: "https://unichain-rpc.publicnode.com", ContractAddr: "0xCa1D5a146B03f6303baF59e5AD5615ae0b9d146D"},
  79. vaa.ChainIDWorldchain: {Finalized: true, Safe: true, EvmChainID: 480, PublicRPC: "https://worldchain-mainnet.g.alchemy.com/public", ContractAddr: "0xcbcEe4e081464A15d8Ad5f58BB493954421eB506"},
  80. vaa.ChainIDInk: {Finalized: true, Safe: true, EvmChainID: 57073, PublicRPC: "https://rpc-qnd.inkonchain.com", ContractAddr: "0xCa1D5a146B03f6303baF59e5AD5615ae0b9d146D"},
  81. vaa.ChainIDHyperEVM: {Finalized: true, Safe: true, EvmChainID: 999, PublicRPC: "https://rpc.hyperliquid.xyz/evm", ContractAddr: "0x7C0faFc4384551f063e05aee704ab943b8B53aB3"},
  82. vaa.ChainIDMonad: {Finalized: true, Safe: true, EvmChainID: 143, PublicRPC: "https://rpc.monad.xyz", ContractAddr: "0x194B123c5E96B9b2E49763619985790Dc241CAC0"},
  83. vaa.ChainIDMezo: {Finalized: true, Safe: true, EvmChainID: 31612, PublicRPC: "https://jsonrpc-mezo.boar.network/", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  84. // vaa.ChainIDConverge: Not in Mainnet yet
  85. vaa.ChainIDPlume: {Finalized: true, Safe: true, EvmChainID: 98866, PublicRPC: "https://rpc.plume.org", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  86. vaa.ChainIDXRPLEVM: {Finalized: true, Safe: true, EvmChainID: 1440000, PublicRPC: "https://rpc.xrplevm.org/", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  87. // vaa.ChainIDPlasma: {Finalized: true, Safe: true, EvmChainID: _, PublicRPC: "", ContractAddr: ""},
  88. vaa.ChainIDCreditCoin: {Finalized: true, Safe: true, EvmChainID: 102030, PublicRPC: "https://mainnet3.creditcoin.network", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  89. // vaa.ChainIDMoca: Not in Mainnet yet
  90. }
  91. // testnetChainConfig specifies the configuration for all chains enabled in Testnet.
  92. // NOTE: Only add a chain here if the watcher should allow it in Testnet.
  93. // NOTE: If you change this data, be sure and run the tests described at the top of this file!
  94. testnetChainConfig = EnvMap{
  95. // As of 2025 September we use Sepolia as the default Ethereum testnet, given that Holesky is being deprecated.
  96. vaa.ChainIDEthereum: {Finalized: true, Safe: true, EvmChainID: 11155111, PublicRPC: "https://ethereum-sepolia-rpc.publicnode.com", ContractAddr: "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78"},
  97. vaa.ChainIDBSC: {Finalized: true, Safe: true, EvmChainID: 97, PublicRPC: "https://bsc-testnet-rpc.publicnode.com", ContractAddr: "0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D"},
  98. // Polygon supports polling for finalized but not safe: https://forum.polygon.technology/t/optimizing-decentralized-apps-ux-with-milestones-a-significantly-accelerated-finality-solution/13154
  99. vaa.ChainIDPolygon: {Finalized: true, Safe: false, EvmChainID: 80001, ContractAddr: "0x0CBE91CF822c73C2315FB05100C2F714765d5c20"}, // Polygon Mumbai is deprecated.
  100. vaa.ChainIDAvalanche: {InstantFinality: true, Finalized: true, Safe: true, EvmChainID: 43113, PublicRPC: "https://avalanche-fuji-c-chain-rpc.publicnode.com", ContractAddr: "0x7bbcE28e64B3F8b84d876Ab298393c38ad7aac4C"},
  101. vaa.ChainIDFantom: {InstantFinality: true, Finalized: false, Safe: false, EvmChainID: 4002, PublicRPC: "https://fantom-testnet-rpc.publicnode.com", ContractAddr: "0x1BB3B4119b7BA9dfad76B0545fb3F531383c3bB7"},
  102. vaa.ChainIDKlaytn: {InstantFinality: true, Finalized: false, Safe: false, EvmChainID: 1001, PublicRPC: "https://public-en-kairos.node.kaia.io", ContractAddr: "0x1830CC6eE66c84D2F177B94D544967c774E624cA"},
  103. vaa.ChainIDCelo: {Finalized: true, Safe: true, EvmChainID: 44787, PublicRPC: "https://alfajores-forno.celo-testnet.org", ContractAddr: "0x88505117CA88e7dd2eC6EA1E13f0948db2D50D56"},
  104. vaa.ChainIDMoonbeam: {Finalized: true, Safe: true, EvmChainID: 1287, PublicRPC: "https://rpc.api.moonbase.moonbeam.network", ContractAddr: "0xa5B7D85a8f27dd7907dc8FdC21FA5657D5E2F901"},
  105. vaa.ChainIDArbitrum: {Finalized: true, Safe: true, EvmChainID: 421613, ContractAddr: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"}, // Arbitrum Goerli is deprecated.
  106. vaa.ChainIDOptimism: {Finalized: true, Safe: true, EvmChainID: 420, ContractAddr: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35"}, // Optimism Goerli is deprecated.
  107. // vaa.ChainIDGnosis: Not supported in the guardian.
  108. // vaa.ChainIDBtc: Not supported in the guardian.
  109. vaa.ChainIDBase: {Finalized: true, Safe: true, EvmChainID: 84531, ContractAddr: "0x23908A62110e21C04F3A4e011d24F901F911744A"}, // Base Goerli is deprecated.
  110. // vaa.ChainIDFileCoin: Not supported in the guardian.
  111. // vaa.ChainIDRootstock: Not supported in the guardian.
  112. // As of 11/10/2023 Scroll supports polling for finalized but not safe.
  113. vaa.ChainIDScroll: {Finalized: true, Safe: false, EvmChainID: 534351, PublicRPC: "https://scroll-sepolia-rpc.publicnode.com", ContractAddr: "0x055F47F1250012C6B20c436570a76e52c17Af2D5"},
  114. vaa.ChainIDMantle: {Finalized: true, Safe: true, EvmChainID: 5003, PublicRPC: "https://rpc.sepolia.mantle.xyz", ContractAddr: "0x376428e7f26D5867e69201b275553C45B09EE090"},
  115. vaa.ChainIDXLayer: {Finalized: true, Safe: true, EvmChainID: 195, PublicRPC: "https://xlayertestrpc.okx.com", ContractAddr: "0xA31aa3FDb7aF7Db93d18DDA4e19F811342EDF780"},
  116. // As of 9/06/2024 Linea supports polling for finalized but not safe.
  117. vaa.ChainIDLinea: {Finalized: true, Safe: false, EvmChainID: 59141, PublicRPC: "https://rpc.sepolia.linea.build", ContractAddr: "0x79A1027a6A159502049F10906D333EC57E95F083"},
  118. vaa.ChainIDBerachain: {Finalized: true, Safe: true, EvmChainID: 80069, PublicRPC: "https://bepolia.rpc.berachain.com/", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  119. vaa.ChainIDSeiEVM: {Finalized: true, Safe: true, EvmChainID: 1328, PublicRPC: "https://evm-rpc-testnet.sei-apis.com/", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  120. // vaa.ChainIDEclipse: Not supported in the guardian.
  121. // vaa.ChainIDBOB: Not supported in the guardian.
  122. vaa.ChainIDUnichain: {Finalized: true, Safe: true, EvmChainID: 1301, PublicRPC: "https://unichain-sepolia-rpc.publicnode.com", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  123. vaa.ChainIDWorldchain: {Finalized: true, Safe: true, EvmChainID: 4801, PublicRPC: "https://worldchain-sepolia.g.alchemy.com/public", ContractAddr: "0xe5E02cD12B6FcA153b0d7fF4bF55730AE7B3C93A"},
  124. vaa.ChainIDInk: {Finalized: true, Safe: true, EvmChainID: 763373, PublicRPC: "https://rpc-qnd-sepolia.inkonchain.com", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  125. vaa.ChainIDHyperEVM: {Finalized: true, Safe: true, EvmChainID: 998, PublicRPC: "https://rpc.hyperliquid-testnet.xyz/evm", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  126. vaa.ChainIDMonad: {Finalized: true, Safe: true, EvmChainID: 10143, PublicRPC: "https://testnet-rpc.monad.xyz", ContractAddr: "0xBB73cB66C26740F31d1FabDC6b7A46a038A300dd"},
  127. vaa.ChainIDMezo: {Finalized: true, Safe: true, EvmChainID: 31611, PublicRPC: "https://rpc.test.mezo.org", ContractAddr: "0x268557122Ffd64c85750d630b716471118F323c8"},
  128. vaa.ChainIDConverge: {Finalized: true, Safe: true, EvmChainID: 52085145, PublicRPC: "https://rpc-converge-testnet-1.t.conduit.xyz", ContractAddr: "0x556B259cFaCd9896B2773310080c7c3bcE90Ff01"},
  129. vaa.ChainIDPlume: {Finalized: true, Safe: true, EvmChainID: 98867, PublicRPC: "https://testnet-rpc.plume.org", ContractAddr: "0x81705b969cDcc6FbFde91a0C6777bE0EF3A75855"},
  130. vaa.ChainIDXRPLEVM: {Finalized: true, Safe: true, EvmChainID: 1449000, PublicRPC: "https://rpc.testnet.xrplevm.org/", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  131. vaa.ChainIDPlasma: {Finalized: true, Safe: true, EvmChainID: 9746, PublicRPC: "https://testnet-rpc.plasma.to", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  132. vaa.ChainIDCreditCoin: {Finalized: true, Safe: true, EvmChainID: 102031, PublicRPC: "https://rpc.cc3-testnet.creditcoin.network", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  133. vaa.ChainIDMoca: {Finalized: true, Safe: true, EvmChainID: 222888, PublicRPC: "https://testnet-rpc.mocachain.org/", ContractAddr: "0xaBf89de706B583424328B54dD05a8fC986750Da8"},
  134. vaa.ChainIDSepolia: {Finalized: true, Safe: true, EvmChainID: 11155111, PublicRPC: "https://ethereum-sepolia-rpc.publicnode.com", ContractAddr: "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78"},
  135. vaa.ChainIDArbitrumSepolia: {Finalized: true, Safe: true, EvmChainID: 421614, PublicRPC: "https://arbitrum-sepolia-rpc.publicnode.com", ContractAddr: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35"},
  136. vaa.ChainIDBaseSepolia: {Finalized: true, Safe: true, EvmChainID: 84532, PublicRPC: "https://base-sepolia-rpc.publicnode.com", ContractAddr: "0x79A1027a6A159502049F10906D333EC57E95F083"},
  137. vaa.ChainIDOptimismSepolia: {Finalized: true, Safe: true, EvmChainID: 11155420, PublicRPC: "https://optimism-sepolia-rpc.publicnode.com", ContractAddr: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe"},
  138. vaa.ChainIDHolesky: {Finalized: true, Safe: true, EvmChainID: 17000, PublicRPC: "https://1rpc.io/holesky", ContractAddr: "0xa10f2eF61dE1f19f586ab8B6F2EbA89bACE63F7a"},
  139. vaa.ChainIDPolygonSepolia: {Finalized: true, Safe: false, EvmChainID: 80002, PublicRPC: "https://polygon-amoy-bor-rpc.publicnode.com", ContractAddr: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35"},
  140. }
  141. )
  142. // SupportedInMainnet returns true if the chain is configured in Mainnet.
  143. func SupportedInMainnet(chainID vaa.ChainID) bool {
  144. _, exists := mainnetChainConfig[chainID]
  145. return exists
  146. }
  147. // GetFinality returns the finalized and safe flags for the specified environment / chain. These are used to configure the watcher at run time.
  148. func GetFinality(env common.Environment, chainID vaa.ChainID) (finalized bool, safe bool, err error) {
  149. // Tilt supports polling for both finalized and safe.
  150. if env == common.UnsafeDevNet {
  151. return true, true, nil
  152. }
  153. m, err := GetChainConfigMap(env)
  154. if err != nil {
  155. return false, false, err
  156. }
  157. entry, exists := m[chainID]
  158. if !exists {
  159. return false, false, ErrNotFound
  160. }
  161. if entry.InstantFinality {
  162. return false, false, nil
  163. }
  164. return entry.Finalized, entry.Safe, nil
  165. }
  166. // GetEvmChainID returns the configured EVM chain ID for the specified environment / chain.
  167. func GetEvmChainID(env common.Environment, chainID vaa.ChainID) (uint64, error) {
  168. m, err := GetChainConfigMap(env)
  169. if err != nil {
  170. return 0, err
  171. }
  172. entry, exists := m[chainID]
  173. if !exists {
  174. return 0, ErrNotFound
  175. }
  176. return entry.EvmChainID, nil
  177. }
  178. // GetChainConfigMap is a helper that returns the configuration for the specified environment.
  179. // This is public so the chain verify utility can use it.
  180. func GetChainConfigMap(env common.Environment) (EnvMap, error) {
  181. if env == common.MainNet {
  182. return mainnetChainConfig, nil
  183. }
  184. if env == common.TestNet {
  185. return testnetChainConfig, nil
  186. }
  187. return EnvMap{}, ErrInvalidEnv
  188. }
  189. // QueryEvmChainID queries the specified RPC for the EVM chain ID.
  190. func QueryEvmChainID(ctx context.Context, url string) (uint64, error) {
  191. timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
  192. defer cancel()
  193. c, err := rpc.DialContext(timeout, url)
  194. if err != nil {
  195. return 0, fmt.Errorf("failed to connect to endpoint: %w", err)
  196. }
  197. var str string
  198. err = c.CallContext(ctx, &str, "eth_chainId")
  199. if err != nil {
  200. return 0, fmt.Errorf("failed to read evm chain id: %w", err)
  201. }
  202. evmChainID, err := strconv.ParseUint(strings.TrimPrefix(str, "0x"), 16, 64)
  203. if err != nil {
  204. return 0, fmt.Errorf(`eth_chainId returned an invalid int: "%s"`, str)
  205. }
  206. return evmChainID, nil
  207. }
  208. // verifyEvmChainID reads the EVM chain ID from the node and verifies that it matches the expected value (making sure we aren't connected to the wrong chain).
  209. func (w *Watcher) verifyEvmChainID(ctx context.Context, logger *zap.Logger, url string) error {
  210. // Don't bother to check in tilt.
  211. if w.env == common.UnsafeDevNet {
  212. return nil
  213. }
  214. timeout, cancel := context.WithTimeout(ctx, 15*time.Second)
  215. defer cancel()
  216. c, err := rpc.DialContext(timeout, url)
  217. if err != nil {
  218. return fmt.Errorf("failed to connect to endpoint: %w", err)
  219. }
  220. var str string
  221. err = c.CallContext(ctx, &str, "eth_chainId")
  222. if err != nil {
  223. return fmt.Errorf("failed to read evm chain id: %w", err)
  224. }
  225. evmChainID, err := strconv.ParseUint(strings.TrimPrefix(str, "0x"), 16, 64)
  226. if err != nil {
  227. return fmt.Errorf(`eth_chainId returned an invalid int: "%s"`, str)
  228. }
  229. expectedEvmChainID, err := GetEvmChainID(w.env, w.chainID)
  230. if err != nil {
  231. return fmt.Errorf("failed to look up evm chain id: %w", err)
  232. }
  233. logger.Info("queried evm chain id", zap.Uint64("expected", expectedEvmChainID), zap.Uint64("actual", evmChainID))
  234. if evmChainID != uint64(expectedEvmChainID) {
  235. return fmt.Errorf("evm chain ID miss match, expected %d, received %d", expectedEvmChainID, evmChainID)
  236. }
  237. return nil
  238. }
  239. // GetContractAddr returns the configured contract address for the specified environment / chain.
  240. func GetContractAddrString(env common.Environment, chainID vaa.ChainID) (string, error) {
  241. m, err := GetChainConfigMap(env)
  242. if err != nil {
  243. return "", err
  244. }
  245. entry, exists := m[chainID]
  246. if !exists {
  247. return "", ErrNotFound
  248. }
  249. return entry.ContractAddr, nil
  250. }
  251. // GetContractAddr returns the configured contract address for the specified environment / chain.
  252. func GetContractAddr(env common.Environment, chainID vaa.ChainID) (ethCommon.Address, error) {
  253. str, err := GetContractAddrString(env, chainID)
  254. if err != nil {
  255. return ethCommon.Address{}, err
  256. }
  257. return ethCommon.HexToAddress(str), nil
  258. }