From 28a3bb58ea5cc2dee14db741b6fb2bf1c7b47e05 Mon Sep 17 00:00:00 2001 From: connorwstein Date: Wed, 6 Nov 2024 11:46:16 -0500 Subject: [PATCH 1/3] Sketch --- evm.go | 139 ++++ selectors_e2e_test.go => evm_e2e_test.go | 0 selectors_test.go => evm_test.go | 8 +- genchains.go => genchains_evm.go | 0 ...rated_chains.go => generated_chains_evm.go | 0 selector_families.yml | 612 ------------------ selectors.go | 175 +---- selectors.yml | 2 + selectors_solana.yml | 11 + solana.go | 34 + solana_test.go | 9 + test_selectors.yml | 2 + 12 files changed, 207 insertions(+), 785 deletions(-) create mode 100644 evm.go rename selectors_e2e_test.go => evm_e2e_test.go (100%) rename selectors_test.go => evm_test.go (95%) rename genchains.go => genchains_evm.go (100%) rename generated_chains.go => generated_chains_evm.go (100%) delete mode 100644 selector_families.yml create mode 100644 selectors_solana.yml create mode 100644 solana.go create mode 100644 solana_test.go diff --git a/evm.go b/evm.go new file mode 100644 index 0000000..2cfdac3 --- /dev/null +++ b/evm.go @@ -0,0 +1,139 @@ +package chain_selectors + +import ( + _ "embed" + "fmt" + "strconv" + + "gopkg.in/yaml.v3" +) + +//go:generate go run genchains.go + +//go:embed selectors.yml +var selectorsYml []byte + +//go:embed test_selectors.yml +var testSelectorsYml []byte + +type chainDetails struct { + ChainSelector uint64 `yaml:"selector"` + ChainName string `yaml:"name"` +} + +var ( + evmSelectorsMap = parseYml(selectorsYml) + evmTestSelectorsMap = parseYml(testSelectorsYml) + evmChainIdToChainSelector = loadAllEVMSelectors() + evmChainsBySelector = make(map[uint64]Chain) + evmChainsByEvmChainID = make(map[uint64]Chain) +) + +func init() { + for _, ch := range ALL { + evmChainsBySelector[ch.Selector] = ch + evmChainsByEvmChainID[ch.EvmChainID] = ch + } +} + +func loadAllEVMSelectors() map[uint64]chainDetails { + output := make(map[uint64]chainDetails, len(evmSelectorsMap)+len(evmTestSelectorsMap)) + for k, v := range evmSelectorsMap { + output[k] = v + } + for k, v := range evmTestSelectorsMap { + output[k] = v + } + return output +} + +func parseYml(ymlFile []byte) map[uint64]chainDetails { + type ymlData struct { + SelectorsByEvmChainId map[uint64]chainDetails `yaml:"selectors"` + } + + var data ymlData + err := yaml.Unmarshal(ymlFile, &data) + if err != nil { + panic(err) + } + + return data.SelectorsByEvmChainId +} + +func EvmChainIdToChainSelector() map[uint64]uint64 { + copyMap := make(map[uint64]uint64, len(evmChainIdToChainSelector)) + for k, v := range evmChainIdToChainSelector { + copyMap[k] = v.ChainSelector + } + return copyMap +} + +func ChainIdFromSelector(chainSelectorId uint64) (uint64, error) { + for k, v := range evmChainIdToChainSelector { + if v.ChainSelector == chainSelectorId { + return k, nil + } + } + return 0, fmt.Errorf("chain not found for chain selector %d", chainSelectorId) +} + +func SelectorFromChainId(chainId uint64) (uint64, error) { + if chainSelectorId, exist := evmChainIdToChainSelector[chainId]; exist { + return chainSelectorId.ChainSelector, nil + } + return 0, fmt.Errorf("chain selector not found for chain %d", chainId) +} + +func NameFromChainId(chainId uint64) (string, error) { + details, exist := evmChainIdToChainSelector[chainId] + if !exist { + return "", fmt.Errorf("chain name not found for chain %d", chainId) + } + if details.ChainName == "" { + return strconv.FormatUint(chainId, 10), nil + } + return details.ChainName, nil +} + +func ChainIdFromName(name string) (uint64, error) { + for k, v := range evmChainIdToChainSelector { + if v.ChainName == name { + return k, nil + } + } + chainId, err := strconv.ParseUint(name, 10, 64) + if err == nil { + if details, exist := evmChainIdToChainSelector[chainId]; exist && details.ChainName == "" { + return chainId, nil + } + } + return 0, fmt.Errorf("chain not found for name %s", name) +} + +func TestChainIds() []uint64 { + chainIds := make([]uint64, 0, len(evmTestSelectorsMap)) + for k := range evmTestSelectorsMap { + chainIds = append(chainIds, k) + } + return chainIds +} + +func ChainBySelector(sel uint64) (Chain, bool) { + ch, exists := evmChainsBySelector[sel] + return ch, exists +} + +func ChainByEvmChainID(evmChainID uint64) (Chain, bool) { + ch, exists := evmChainsByEvmChainID[evmChainID] + return ch, exists +} + +func IsEvm(chainSel uint64) (bool, error) { + _, exists := ChainBySelector(chainSel) + if !exists { + return false, fmt.Errorf("chain %d not found", chainSel) + } + // We always return true since only evm chains are supported atm. + return true, nil +} diff --git a/selectors_e2e_test.go b/evm_e2e_test.go similarity index 100% rename from selectors_e2e_test.go rename to evm_e2e_test.go diff --git a/selectors_test.go b/evm_test.go similarity index 95% rename from selectors_test.go rename to evm_test.go index 7ae78e6..5125cd8 100644 --- a/selectors_test.go +++ b/evm_test.go @@ -20,8 +20,8 @@ func TestNoSameChainSelectorsAreGenerated(t *testing.T) { } func TestNoOverlapBetweenRealAndTestChains(t *testing.T) { - for k, _ := range selectorsMap { - _, exist := testSelectorsMap[k] + for k, _ := range evmSelectorsMap { + _, exist := evmTestSelectorsMap[k] assert.False(t, exist, "Chain %d is duplicated between real and test chains", k) } } @@ -113,10 +113,10 @@ func Test_ChainSelectors(t *testing.T) { func Test_TestChainIds(t *testing.T) { chainIds := TestChainIds() - assert.Equal(t, len(chainIds), len(testSelectorsMap), "Should return correct number of test chain ids") + assert.Equal(t, len(chainIds), len(evmTestSelectorsMap), "Should return correct number of test chain ids") for _, chainId := range chainIds { - _, exist := testSelectorsMap[chainId] + _, exist := evmTestSelectorsMap[chainId] assert.True(t, exist) } } diff --git a/genchains.go b/genchains_evm.go similarity index 100% rename from genchains.go rename to genchains_evm.go diff --git a/generated_chains.go b/generated_chains_evm.go similarity index 100% rename from generated_chains.go rename to generated_chains_evm.go diff --git a/selector_families.yml b/selector_families.yml deleted file mode 100644 index e52b8cc..0000000 --- a/selector_families.yml +++ /dev/null @@ -1,612 +0,0 @@ -selector_families: - 176199025415897437: - family: evm - 222782988166878823: - family: evm - name: hedera-testnet - 241851231317828981: - family: evm - name: bitcoin-merlin-mainnet - 328334718812072308: - family: evm - 374210358663784372: - family: evm - name: velas-mainnet - 465200170687744372: - family: evm - name: gnosis_chain-mainnet - 572210378683744374: - family: evm - name: velas-testnet - 665284410079532457: - family: evm - 729797994450396300: - family: evm - name: telos-evm-testnet - 781901677223027175: - family: evm - 789068866484373046: - family: evm - 829525985033418733: - family: evm - name: ethereum-testnet-sepolia-mode-1 - 909606746561742123: - family: evm - 928756709184343973: - family: evm - 964127714438319834: - family: evm - 973671184102733124: - family: evm - 1113014352258747600: - family: evm - name: neonlink-testnet - 1237925231416731909: - family: evm - name: ethereum-mainnet-immutable-zkevm-1 - 1252863800116739621: - family: evm - name: polkadot-mainnet-moonbeam - 1273605685587320666: - family: evm - 1346049177634351622: - family: evm - name: celo-mainnet - 1355020143337428062: - family: evm - name: kusama-mainnet-moonriver - 1355246678561316402: - family: evm - name: ethereum-testnet-goerli-linea-1 - 1456215246176062136: - family: evm - name: cronos-mainnet - 1458281248224512906: - family: evm - name: avalanche-subnet-dexalot-testnet - 1462016016387883143: - family: evm - name: fraxtal-mainnet - 1467223411771711614: - family: evm - name: bitcoin-testnet-botanix - 1467427327723633929: - family: evm - name: ethereum-testnet-sepolia-corn-1 - 1477345371608778000: - family: evm - name: telos-evm-mainnet - 1488785539820432596: - family: evm - 1540201334317828111: - family: evm - name: ethereum-mainnet-astar-zkevm-1 - 1556008542357238666: - family: evm - name: ethereum-mainnet-mantle-1 - 1562403441176082196: - family: evm - name: ethereum-mainnet-zksync-1 - 1654667687261492630: - family: evm - name: ethereum-testnet-sepolia-polygon-zkevm-1 - 1761333065194157300: - family: evm - name: coinex_smart_chain-mainnet - 1939936305787790600: - family: evm - name: areon-mainnet - 1974710175227680991: - family: evm - 2027362563942762617: - family: evm - name: ethereum-testnet-sepolia-blast-1 - 2039744413822257700: - family: evm - name: near-mainnet - 2066098519157881736: - family: evm - name: ethereum-testnet-sepolia-xlayer-1 - 2110537777356199208: - family: evm - name: kava-testnet - 2279865765895943307: - family: evm - name: ethereum-testnet-sepolia-scroll-1 - 2333097300889804761: - family: evm - name: polkadot-testnet-centrifuge-altair - 2509173735760116798: - family: evm - 2664363617261496610: - family: evm - name: ethereum-testnet-goerli-optimism-1 - 2783890746839497525: - family: evm - 2953028829530698683: - family: evm - 2995292832068775165: - family: evm - name: cronos-testnet - 3016212468291539606: - family: evm - name: ethereum-mainnet-xlayer-1 - 3162193654116181371: - family: evm - name: ethereum-mainnet-arbitrum-1-l3x-1 - 3229138320728879060: - family: evm - name: hedera-mainnet - 3330151784927722907: - family: evm - 3379446385462418246: - family: evm - name: geth-testnet - 3478487238524512106: - family: evm - name: ethereum-testnet-sepolia-arbitrum-1 - 3486622437121596122: - family: evm - name: ethereum-testnet-sepolia-arbitrum-1-l3x-1 - 3552045678561919002: - family: evm - name: celo-testnet-alfajores - 3574539439524578558: - family: evm - 3632230855428784129: - family: evm - 3719320017875267166: - family: evm - name: ethereum-mainnet-kroma-1 - 3734403246176062136: - family: evm - name: ethereum-mainnet-optimism-1 - 3740583887329090549: - family: evm - 3776006016387883143: - family: evm - name: bittorrent_chain-mainnet - 3777822886988675105: - family: evm - name: ethereum-testnet-sepolia-metis-1 - 3842103497652714138: - family: evm - name: cronos-testnet-zkevm-1 - 4051577828743386545: - family: evm - name: polygon-mainnet - 4066443121807923198: - family: evm - 4168263376276232250: - family: evm - name: ethereum-testnet-goerli-mantle-1 - 4174149892778961910: - family: evm - 4340886533089894000: - family: evm - name: polkadot-testnet-darwinia-pangoro - 4348158687435793198: - family: evm - name: ethereum-mainnet-polygon-zkevm-1 - 4350319965322101699: - family: evm - name: zklink_nova-mainnet - 4411394078118774322: - family: evm - name: ethereum-mainnet-blast-1 - 4418231248214522936: - family: evm - name: ethereum-testnet-sepolia-polygon-validium-1 - 4459371029167934217: - family: evm - name: bittorrent_chain-testnet - 4526165231216331901: - family: evm - name: ethereum-testnet-sepolia-immutable-zkevm-1 - 4543928599863227519: - family: evm - 4560701533377838164: - family: evm - name: bitcoin-mainnet-botanix - 4561443241176882990: - family: evm - name: filecoin-mainnet - 4562743618362911021: - family: evm - name: ethereum-testnet-sepolia-zircuit-1 - 4627098889531055414: - family: evm - name: ethereum-mainnet-linea-1 - 4716670523656754658: - family: evm - 4793464827907405086: - family: evm - name: geth-devnet-3 - 4874388048629246000: - family: evm - name: bitcichain-mainnet - 4888058894222120000: - family: evm - name: bitcichain-testnet - 4905564228793744293: - family: evm - name: fantom-testnet - 4949039107694359620: - family: evm - name: ethereum-mainnet-arbitrum-1 - 5009297550715157269: - family: evm - name: ethereum-mainnet - 5061593697262339000: - family: evm - name: near-testnet - 5142893604156789321: - family: evm - name: wemix-mainnet - 5224473277236331295: - family: evm - name: ethereum-testnet-sepolia-optimism-1 - 5269261765892944301: - family: evm - name: bitcoin-testnet-merlin - 5298399861320400553: - family: evm - name: ethereum-testnet-sepolia-lisk-1 - 5361632739113536121: - family: evm - name: polkadot-testnet-moonbeam-moonbase - 5463201557265485081: - family: evm - name: avalanche-subnet-dexalot-mainnet - 5548718428018410741: - family: evm - 5614341928911841614: - family: evm - 5719461335882077547: - family: evm - name: ethereum-testnet-sepolia-linea-1 - 5721565186521185178: - family: evm - 5790810961207155433: - family: evm - name: ethereum-testnet-goerli-base-1 - 5837261596322416298: - family: evm - name: zklink_nova-testnet - 5990477251245693094: - family: evm - name: ethereum-testnet-sepolia-kroma-1 - 6059917085984771915: - family: evm - 6101244977088475029: - family: evm - name: ethereum-testnet-goerli-arbitrum-1 - 6422105447186081193: - family: evm - name: polkadot-mainnet-astar - 6433500567565415381: - family: evm - name: avalanche-mainnet - 6443235356619661032: - family: evm - 6448403805635971860: - family: evm - 6676710761873615962: - family: evm - 6690738652320128159: - family: evm - 6742472197519042017: - family: evm - 6747736380229414777: - family: evm - 6751512843227450641: - family: evm - 6802309497652714138: - family: evm - name: ethereum-testnet-goerli-zksync-1 - 6875898693582952601: - family: evm - 6898391096552792247: - family: evm - name: ethereum-testnet-sepolia-zksync-1 - 6955638871347136141: - family: evm - name: polkadot-testnet-astar-shibuya - 7005880874640146484: - family: evm - 7032045258883126022: - family: evm - 7060342227814389000: - family: evm - name: filecoin-testnet - 7264351850409363825: - family: evm - name: ethereum-mainnet-mode-1 - 7317911323415911000: - family: evm - name: areon-testnet - 7353384334508842175: - family: evm - 7404045285477377670: - family: evm - 7431973150957944526: - family: evm - 7550000543357438061: - family: evm - name: kava-mainnet - 7585715102059681757: - family: evm - 7715160997071429212: - family: evm - 7777066535355430289: - family: evm - 7823363553221722351: - family: evm - 7837562506228496256: - family: evm - name: avalanche-testnet-nexon - 7961714422080771198: - family: evm - 8015762103567576333: - family: evm - 8175830712062617656: - family: evm - name: polkadot-mainnet-centrifuge - 8211981504472319767: - family: evm - 8236463271206331221: - family: evm - name: ethereum-testnet-sepolia-mantle-1 - 8239338020728974000: - family: evm - name: neonlink-mainnet - 8304510386741731151: - family: evm - name: ethereum-testnet-holesky-morph-1 - 8354317460459584308: - family: evm - 8412806778050735057: - family: evm - 8446413392851542429: - family: evm - name: private-testnet-opala - 8694984074292254623: - family: evm - 8698844633699288298: - family: evm - 8794884152664322911: - family: evm - 8805746078405598895: - family: evm - name: ethereum-mainnet-metis-1 - 8866418665544333000: - family: evm - name: polkadot-mainnet-darwinia - 8871595565390010547: - family: evm - name: gnosis_chain-testnet-chiado - 8901520481741771655: - family: evm - name: ethereum-testnet-holesky-fraxtal-1 - 8953668971247136127: - family: evm - name: bitcoin-testnet-rootstock - 8955032871639343000: - family: evm - name: coinex_smart_chain-testnet - 8966794841936584464: - family: evm - 9156614022853705708: - family: evm - 9248511054298050610: - family: evm - 9264503539336248559: - family: evm - 9284632837123596123: - family: evm - name: wemix-testnet - 9574369650680012313: - family: evm - 9675086780529785020: - family: evm - 9932483170498916221: - family: evm - 10089241509396411113: - family: evm - 10106333385848939617: - family: evm - 10199579733509604193: - family: evm - 10344971235874465080: - family: evm - name: ethereum-testnet-sepolia-base-1 - 10497629267361915835: - family: evm - 10537986502862404866: - family: evm - 10547673735879567911: - family: evm - 11059667695644972511: - family: evm - name: ethereum-testnet-goerli-polygon-zkevm-1 - 11335955773964346155: - family: evm - 11344663589394136015: - family: evm - name: binance_smart_chain-mainnet - 11754399446572002459: - family: evm - 11787463284727550157: - family: evm - 11985232338641871056: - family: evm - 12027427861168955422: - family: evm - 12226902941055802385: - family: evm - 12336603543561911511: - family: evm - name: berachain-testnet-artio - 8999465244383784164: - family: evm - name: berachain-testnet-bartio - 12470167056735102403: - family: evm - 12499149790922928210: - family: evm - 12513826466599144030: - family: evm - 12532609583862916517: - family: evm - name: polygon-testnet-mumbai - 12922642891491394802: - family: evm - name: geth-devnet-2 - 12965905455277595820: - family: evm - 13087962012083037329: - family: evm - 13204309965629103672: - family: evm - name: ethereum-mainnet-scroll-1 - 13264668187771770619: - family: evm - name: binance_smart_chain-testnet - 13443138560923813712: - family: evm - 13648736134397881410: - family: evm - 13781595843667691007: - family: evm - 13819071330241498802: - family: evm - 13936493323944617843: - family: evm - 13973515790491921010: - family: evm - 14506622911400094011: - family: evm - 14767482510784806043: - family: evm - name: avalanche-testnet-fuji - 14943531413383612703: - family: evm - 15168140751097121912: - family: evm - 15210860601736105873: - family: evm - 15447447865219782832: - family: evm - 15733873364998401606: - family: evm - 15767478222558315144: - family: evm - 15804983202763665802: - family: evm - 15896959195233368219: - family: evm - 15945074456050759193: - family: evm - 15971525489660198786: - family: evm - name: ethereum-mainnet-base-1 - 15998314635132476942: - family: evm - 16015286601757825753: - family: evm - name: ethereum-testnet-sepolia - 16281711391670634445: - family: evm - name: polygon-testnet-amoy - 16449698933146693970: - family: evm - 16591966440843528322: - family: evm - 16702426279731183946: - family: evm - 17251043223284625647: - family: evm - 17514102371649734225: - family: evm - 17580537314894454709: - family: evm - 17759418850483131633: - family: evm - 17810359353458878177: - family: evm - 18316006852148771137: - family: evm - 10443705513486043421: - family: evm - name: ethereum-testnet-sepolia-arbitrum-1-treasure-1 - 1010349088906777999: - family: evm - name: ethereum-mainnet-arbitrum-1-treasure-1 - 4489326297382772450: - family: evm - 686603546605904534: - family: evm - 17198166215261833993: - family: evm - 13116810400804392105: - family: evm - name: ronin-testnet-saigon - 1948510578179542068: - family: evm - name: "bitcoin-testnet-bsquared-1" - 3789623672476206327: - family: evm - name: "bitcoin-testnet-bitlayer-1" - 5535534526963509396: - family: evm - name: "bitcoin-testnet-sepolia-bob-1" - 6827576821754315911: - family: evm - name: "ethereum-testnet-sepolia-lens-1" - 1216300075444106652: - family: evm - name: "sei-testnet-atlantic" - 3676871237479449268: - family: evm - name: "sonic-testnet" - 4286062357653186312: - family: evm - name: "hyperliquid-testnet" - 4237030917318060427: - family: evm - name: "story-testnet" - 5406759801798337480: - family: evm - name: "bitcoin-mainnet-bsquared-1" - 7937294810946806131: - family: evm - name: "bitcoin-mainnet-bitlayer-1" - 3849287863852499584: - family: evm - name: "bitcoin-mainnet-bob-1" - 9027416829622342829: - family: evm - name: "sei-mainnet" - 13274425992935471758: - family: evm - 7717148896336251131: - family: evm - 14135854469784514356: - family: evm - 7248756420937879088: - family: evm - 14684575664602284776: - family: evm - 465944652040885897: - family: evm - 16468599424800719238: - family: evm - 3768048213127883732: - family: evm - 6916147374840168594: - family: evm - name: "ronin-mainnet" - 5299555114858065850: - family: evm - 2049429975587534727: - family: evm \ No newline at end of file diff --git a/selectors.go b/selectors.go index 4d4d484..5c1374e 100644 --- a/selectors.go +++ b/selectors.go @@ -1,28 +1,6 @@ package chain_selectors -import ( - _ "embed" - "fmt" - "strconv" - - "gopkg.in/yaml.v3" -) - -//go:generate go run genchains.go - -//go:embed selectors.yml -var selectorsYml []byte - -//go:embed test_selectors.yml -var testSelectorsYml []byte - -//go:embed selector_families.yml -var selectorFamiliesYml []byte - -type chainDetails struct { - ChainSelector uint64 `yaml:"selector"` - ChainName string `yaml:"name"` -} +import "fmt" const ( FamilyEVM = "evm" @@ -32,153 +10,12 @@ const ( FamilyAptos = "aptos" ) -var selectorsMap = parseYml(selectorsYml) -var testSelectorsMap = parseYml(testSelectorsYml) - -var evmChainIdToChainSelector = loadAllSelectors() -var selectorToChainFamily = loadSelectorToFamilyMap() - -func loadAllSelectors() map[uint64]chainDetails { - output := make(map[uint64]chainDetails, len(selectorsMap)+len(testSelectorsMap)) - for k, v := range selectorsMap { - output[k] = v - } - for k, v := range testSelectorsMap { - output[k] = v - } - return output -} - -func loadSelectorToFamilyMap() map[uint64]string { - type familyDetails struct { - Family string `yaml:"family"` - Name string `yaml:"name"` - } - - type yamlData struct { - SelectorFamilies map[uint64]familyDetails `yaml:"selector_families"` - } - - var data yamlData - err := yaml.Unmarshal(selectorFamiliesYml, &data) - if err != nil { - panic(err) - } - - var selectorFamilies = make(map[uint64]string, len(data.SelectorFamilies)) - for k, v := range data.SelectorFamilies { - selectorFamilies[k] = v.Family - } - - return selectorFamilies -} - -func parseYml(ymlFile []byte) map[uint64]chainDetails { - type ymlData struct { - Selectors map[uint64]chainDetails `yaml:"selectors"` - } - - var data ymlData - err := yaml.Unmarshal(ymlFile, &data) - if err != nil { - panic(err) - } - - return data.Selectors -} - func GetSelectorFamily(selector uint64) (string, error) { - family, exist := selectorToChainFamily[selector] - if !exist { - return "", fmt.Errorf("family not found for selector %d", selector) + if _, exist := evmChainIdToChainSelector[selector]; exist { + return FamilyEVM, nil } - - return family, nil -} - -func EvmChainIdToChainSelector() map[uint64]uint64 { - copyMap := make(map[uint64]uint64, len(evmChainIdToChainSelector)) - for k, v := range evmChainIdToChainSelector { - copyMap[k] = v.ChainSelector - } - return copyMap -} - -func ChainIdFromSelector(chainSelectorId uint64) (uint64, error) { - for k, v := range evmChainIdToChainSelector { - if v.ChainSelector == chainSelectorId { - return k, nil - } - } - return 0, fmt.Errorf("chain not found for chain selector %d", chainSelectorId) -} - -func SelectorFromChainId(chainId uint64) (uint64, error) { - if chainSelectorId, exist := evmChainIdToChainSelector[chainId]; exist { - return chainSelectorId.ChainSelector, nil - } - return 0, fmt.Errorf("chain selector not found for chain %d", chainId) -} - -func NameFromChainId(chainId uint64) (string, error) { - details, exist := evmChainIdToChainSelector[chainId] - if !exist { - return "", fmt.Errorf("chain name not found for chain %d", chainId) - } - if details.ChainName == "" { - return strconv.FormatUint(chainId, 10), nil - } - return details.ChainName, nil -} - -func ChainIdFromName(name string) (uint64, error) { - for k, v := range evmChainIdToChainSelector { - if v.ChainName == name { - return k, nil - } - } - chainId, err := strconv.ParseUint(name, 10, 64) - if err == nil { - if details, exist := evmChainIdToChainSelector[chainId]; exist && details.ChainName == "" { - return chainId, nil - } - } - return 0, fmt.Errorf("chain not found for name %s", name) -} - -func TestChainIds() []uint64 { - chainIds := make([]uint64, 0, len(testSelectorsMap)) - for k := range testSelectorsMap { - chainIds = append(chainIds, k) - } - return chainIds -} - -var chainsBySelector = make(map[uint64]Chain) -var chainsByEvmChainID = make(map[uint64]Chain) - -func init() { - for _, ch := range ALL { - chainsBySelector[ch.Selector] = ch - chainsByEvmChainID[ch.EvmChainID] = ch - } -} - -func ChainBySelector(sel uint64) (Chain, bool) { - ch, exists := chainsBySelector[sel] - return ch, exists -} - -func ChainByEvmChainID(evmChainID uint64) (Chain, bool) { - ch, exists := chainsByEvmChainID[evmChainID] - return ch, exists -} - -func IsEvm(chainSel uint64) (bool, error) { - _, exists := ChainBySelector(chainSel) - if !exists { - return false, fmt.Errorf("chain %d not found", chainSel) + if _, exist := solanaChainIdBySelector[selector]; exist { + return FamilySolana, nil } - // We always return true since only evm chains are supported atm. - return true, nil + return "", fmt.Errorf("unknown chain selector %d", selector) } diff --git a/selectors.yml b/selectors.yml index ee17d82..8b3d762 100644 --- a/selectors.yml +++ b/selectors.yml @@ -1,3 +1,5 @@ +# EVM selectors. +# File doesn't have a family suffix for backwards compatibility. --- selectors: # Testnets diff --git a/selectors_solana.yml b/selectors_solana.yml new file mode 100644 index 0000000..f02b167 --- /dev/null +++ b/selectors_solana.yml @@ -0,0 +1,11 @@ +selectors: + "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d": + name: solana-mainnet + selector: 124615329519749607 + "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY": + name: solana-testnet + selector: 6302590918974934319 + "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG": + name: solana-devnet + selector: 16423721717087811551 + diff --git a/solana.go b/solana.go new file mode 100644 index 0000000..e6c1f17 --- /dev/null +++ b/solana.go @@ -0,0 +1,34 @@ +package chain_selectors + +import ( + _ "embed" + + "gopkg.in/yaml.v3" +) + +//go:embed selectors_solana.yml +var solanaSelectorsYml []byte + +var ( + solanaSelectorsMap = parseSolanaYml(solanaSelectorsYml) + solanaChainIdBySelector = make(map[uint64]string) +) + +func init() { + for k, v := range solanaSelectorsMap { + solanaChainIdBySelector[v.ChainSelector] = k + } +} + +func parseSolanaYml(ymlFile []byte) map[string]chainDetails { + type ymlData struct { + SelectorsBySolanaChainId map[string]chainDetails `yaml:"selectors"` + } + + var data ymlData + err := yaml.Unmarshal(ymlFile, &data) + if err != nil { + panic(err) + } + return data.SelectorsBySolanaChainId +} diff --git a/solana_test.go b/solana_test.go new file mode 100644 index 0000000..fefe432 --- /dev/null +++ b/solana_test.go @@ -0,0 +1,9 @@ +package chain_selectors + +import "testing" + +func TestSolana(t *testing.T) { + for k, v := range solanaSelectorsMap { + t.Logf("k: %s, v: %v", k, v) + } +} diff --git a/test_selectors.yml b/test_selectors.yml index 1cd3fd4..835508d 100644 --- a/test_selectors.yml +++ b/test_selectors.yml @@ -1,3 +1,5 @@ +# EVM test selectors. +# File doesn't have a family suffix for backwards compatibility. selectors: # For testing purposes 1000: From 392f8fba8b35598a58096ab5a74746754b46b8a6 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 6 Nov 2024 15:51:56 -0600 Subject: [PATCH 2/3] add solana and backwards compatibility --- evm.go | 12 ++-- evm_test.go | 13 ++++- genchains_evm.go | 2 +- genchains_solana.go | 113 +++++++++++++++++++++++++++++++++++++ generated_chains_solana.go | 21 +++++++ selectors.go | 41 +++++++++++++- solana.go | 26 ++++++++- solana_test.go | 70 ++++++++++++++++++++++- 8 files changed, 282 insertions(+), 16 deletions(-) create mode 100644 genchains_solana.go create mode 100644 generated_chains_solana.go diff --git a/evm.go b/evm.go index 2cfdac3..877caf1 100644 --- a/evm.go +++ b/evm.go @@ -8,7 +8,7 @@ import ( "gopkg.in/yaml.v3" ) -//go:generate go run genchains.go +//go:generate go run genchains_evm.go //go:embed selectors.yml var selectorsYml []byte @@ -16,7 +16,7 @@ var selectorsYml []byte //go:embed test_selectors.yml var testSelectorsYml []byte -type chainDetails struct { +type ChainDetails struct { ChainSelector uint64 `yaml:"selector"` ChainName string `yaml:"name"` } @@ -36,8 +36,8 @@ func init() { } } -func loadAllEVMSelectors() map[uint64]chainDetails { - output := make(map[uint64]chainDetails, len(evmSelectorsMap)+len(evmTestSelectorsMap)) +func loadAllEVMSelectors() map[uint64]ChainDetails { + output := make(map[uint64]ChainDetails, len(evmSelectorsMap)+len(evmTestSelectorsMap)) for k, v := range evmSelectorsMap { output[k] = v } @@ -47,9 +47,9 @@ func loadAllEVMSelectors() map[uint64]chainDetails { return output } -func parseYml(ymlFile []byte) map[uint64]chainDetails { +func parseYml(ymlFile []byte) map[uint64]ChainDetails { type ymlData struct { - SelectorsByEvmChainId map[uint64]chainDetails `yaml:"selectors"` + SelectorsByEvmChainId map[uint64]ChainDetails `yaml:"selectors"` } var data ymlData diff --git a/evm_test.go b/evm_test.go index 5125cd8..6691f74 100644 --- a/evm_test.go +++ b/evm_test.go @@ -2,6 +2,7 @@ package chain_selectors import ( "math/rand" + "strconv" "testing" "github.com/stretchr/testify/assert" @@ -51,9 +52,10 @@ func TestAllChainSelectorsHaveFamilies(t *testing.T) { for _, ch := range ALL { family, err := GetSelectorFamily(ch.Selector) require.NoError(t, err, - "Family not found for selector %d (chain id %d, name %s), please update selector_families.yml with the appropriate chain family for this chain", + "Family not found for selector %d (chain id %d, name %s), please update selector.yml with the appropriate chain family for this chain", ch.Selector, ch.EvmChainID, ch.Name) require.NotEmpty(t, family) + require.Equal(t, FamilyEVM, family) } } @@ -224,3 +226,12 @@ func Test_IsEvm(t *testing.T) { assert.False(t, isEvm) }) } + +func Test_EVMGetChainDetailsByChainIDAndFamily(t *testing.T) { + for k, v := range evmChainIdToChainSelector { + strChainID := strconv.FormatUint(k, 10) + details, err := GetChainDetailsByChainIDAndFamily(strChainID, FamilyEVM) + assert.NoError(t, err) + assert.Equal(t, v, details) + } +} diff --git a/genchains_evm.go b/genchains_evm.go index 09742c3..62dafdc 100644 --- a/genchains_evm.go +++ b/genchains_evm.go @@ -16,7 +16,7 @@ import ( chain_selectors "github.com/smartcontractkit/chain-selectors" ) -const filename = "generated_chains.go" +const filename = "generated_chains_evm.go" type chain struct { EvmChainID uint64 diff --git a/genchains_solana.go b/genchains_solana.go new file mode 100644 index 0000000..85937ed --- /dev/null +++ b/genchains_solana.go @@ -0,0 +1,113 @@ +//go:build ignore + +package main + +import ( + "bytes" + "fmt" + "go/format" + "html/template" + "os" + "sort" + "strconv" + "strings" + "unicode" + + chain_selectors "github.com/smartcontractkit/chain-selectors" +) + +const filename = "generated_chains_solana.go" + +type chain struct { + ChainID string + Selector uint64 + Name string + VarName string +} + +var chainTemplate, _ = template.New("").Parse(`// Code generated by go generate please DO NOT EDIT +package chain_selectors + +type SolanaChain struct { + ChainID string + Selector uint64 + Name string + VarName string +} + +var ( +{{ range . }} + {{.VarName}} = SolanaChain{ChainID: "{{ .ChainID }}", Selector: {{ .Selector }}, Name: "{{ .Name }}"}{{ end }} +) + +var SolanaALL = []SolanaChain{ +{{ range . }}{{ .VarName }}, +{{ end }} +} + +`) + +func main() { + src, err := genChainsSourceCode() + if err != nil { + panic(err) + } + + formatted, err := format.Source([]byte(src)) + if err != nil { + panic(err) + } + + existingContent, err := os.ReadFile(filename) + if err != nil { + panic(err) + } + + if string(existingContent) == string(formatted) { + fmt.Println("no changes detected") + return + } + + err = os.WriteFile(filename, formatted, 0644) + if err != nil { + panic(err) + } +} + +func genChainsSourceCode() (string, error) { + var wr = new(bytes.Buffer) + chains := make([]chain, 0) + + for ChainID, chainSel := range chain_selectors.SolanaChainIdToChainSelector() { + name, err := chain_selectors.SolanaNameFromChainId(ChainID) + if err != nil { + return "", err + } + + chains = append(chains, chain{ + ChainID: ChainID, + Selector: chainSel, + Name: name, + VarName: toVarName(name, chainSel), + }) + } + + sort.Slice(chains, func(i, j int) bool { return chains[i].VarName < chains[j].VarName }) + if err := chainTemplate.ExecuteTemplate(wr, "", chains); err != nil { + return "", err + } + return wr.String(), nil +} + +func toVarName(name string, chainSel uint64) string { + const unnamed = "TEST" + x := strings.ReplaceAll(name, "-", "_") + x = strings.ToUpper(x) + if len(x) > 0 && unicode.IsDigit(rune(x[0])) { + x = unnamed + "_" + x + } + if len(x) == 0 { + x = unnamed + "_" + strconv.FormatUint(chainSel, 10) + } + return x +} diff --git a/generated_chains_solana.go b/generated_chains_solana.go new file mode 100644 index 0000000..ba261dd --- /dev/null +++ b/generated_chains_solana.go @@ -0,0 +1,21 @@ +// Code generated by go generate please DO NOT EDIT +package chain_selectors + +type SolanaChain struct { + ChainID string + Selector uint64 + Name string + VarName string +} + +var ( + SOLANA_DEVNET = SolanaChain{ChainID: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG", Selector: 16423721717087811551, Name: "solana-devnet"} + SOLANA_MAINNET = SolanaChain{ChainID: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d", Selector: 124615329519749607, Name: "solana-mainnet"} + SOLANA_TESTNET = SolanaChain{ChainID: "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY", Selector: 6302590918974934319, Name: "solana-testnet"} +) + +var SolanaALL = []SolanaChain{ + SOLANA_DEVNET, + SOLANA_MAINNET, + SOLANA_TESTNET, +} diff --git a/selectors.go b/selectors.go index 5c1374e..eb9ddb4 100644 --- a/selectors.go +++ b/selectors.go @@ -1,6 +1,9 @@ package chain_selectors -import "fmt" +import ( + "fmt" + "strconv" +) const ( FamilyEVM = "evm" @@ -11,11 +14,43 @@ const ( ) func GetSelectorFamily(selector uint64) (string, error) { - if _, exist := evmChainIdToChainSelector[selector]; exist { + // check EVM + _, exist := evmChainsBySelector[selector] + if exist { return FamilyEVM, nil } - if _, exist := solanaChainIdBySelector[selector]; exist { + + // check solana + _, exist = solanaChainIdBySelector[selector] + if exist { return FamilySolana, nil } + return "", fmt.Errorf("unknown chain selector %d", selector) } + +func GetChainDetailsByChainIDAndFamily(chainID string, family string) (ChainDetails, error) { + switch family { + case FamilyEVM: + evmChainId, err := strconv.ParseUint(chainID, 10, 64) + if err != nil { + return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) + } + + details, exist := evmChainIdToChainSelector[evmChainId] + if !exist { + return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) + } + + return details, nil + case FamilySolana: + details, exist := solanaSelectorsMap[chainID] + if !exist { + return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) + } + + return details, nil + default: + return ChainDetails{}, fmt.Errorf("family %s is not yet support", family) + } +} diff --git a/solana.go b/solana.go index e6c1f17..5bf4767 100644 --- a/solana.go +++ b/solana.go @@ -2,10 +2,13 @@ package chain_selectors import ( _ "embed" + "fmt" "gopkg.in/yaml.v3" ) +//go:generate go run genchains_solana.go + //go:embed selectors_solana.yml var solanaSelectorsYml []byte @@ -20,9 +23,9 @@ func init() { } } -func parseSolanaYml(ymlFile []byte) map[string]chainDetails { +func parseSolanaYml(ymlFile []byte) map[string]ChainDetails { type ymlData struct { - SelectorsBySolanaChainId map[string]chainDetails `yaml:"selectors"` + SelectorsBySolanaChainId map[string]ChainDetails `yaml:"selectors"` } var data ymlData @@ -32,3 +35,22 @@ func parseSolanaYml(ymlFile []byte) map[string]chainDetails { } return data.SelectorsBySolanaChainId } + +func SolanaChainIdToChainSelector() map[string]uint64 { + copyMap := make(map[string]uint64, len(solanaSelectorsMap)) + for k, v := range solanaSelectorsMap { + copyMap[k] = v.ChainSelector + } + return copyMap +} + +func SolanaNameFromChainId(chainId string) (string, error) { + details, exist := solanaSelectorsMap[chainId] + if !exist { + return "", fmt.Errorf("chain name not found for chain %v", chainId) + } + if details.ChainName == "" { + return chainId, nil + } + return details.ChainName, nil +} diff --git a/solana_test.go b/solana_test.go index fefe432..1f229e1 100644 --- a/solana_test.go +++ b/solana_test.go @@ -1,9 +1,73 @@ package chain_selectors -import "testing" +import ( + "math/rand" + "testing" -func TestSolana(t *testing.T) { + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_YmlAreValid(t *testing.T) { + tests := []struct { + name string + chainSelector uint64 + chainsId string + expectErr bool + }{ + { + name: "solana-mainnet", + chainSelector: 124615329519749607, + chainsId: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d", + expectErr: false, + }, + { + name: "solana-testnet", + chainSelector: 6302590918974934319, + chainsId: "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY", + expectErr: false, + }, + { + name: "solana-devnet", + chainSelector: 16423721717087811551, + chainsId: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG", + expectErr: false, + }, + { + name: "non-existing", + chainSelector: rand.Uint64(), + chainsId: "non-existing", + expectErr: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + name, err1 := SolanaNameFromChainId(test.chainsId) + if test.expectErr { + require.Error(t, err1) + return + } + require.NoError(t, err1) + assert.Equal(t, test.name, name) + }) + } +} + +func Test_SolanaChainSelectors(t *testing.T) { + for selector := range solanaChainIdBySelector { + family, err := GetSelectorFamily(selector) + require.NoError(t, err, + "selector %v should be returned as solana family, but received %v", + selector, err) + require.NotEmpty(t, family) + require.Equal(t, FamilySolana, family) + } +} + +func Test_SolanaGetChainDetailsByChainIDAndFamily(t *testing.T) { for k, v := range solanaSelectorsMap { - t.Logf("k: %s, v: %v", k, v) + details, err := GetChainDetailsByChainIDAndFamily(k, FamilySolana) + assert.NoError(t, err) + assert.Equal(t, v, details) } } From d0bc28a4fda0a77f62c767df60c1e883b654a1f2 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Thu, 7 Nov 2024 11:23:04 -0600 Subject: [PATCH 3/3] add hash validation and update readme --- README.md | 25 +++++++++++++++++++++++-- go.mod | 1 + go.sum | 2 ++ selectors_solana.yml | 2 +- solana.go | 24 ++++++++++++++++++++++++ solana_test.go | 6 +++++- 6 files changed, 56 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 96753a0..8a8a047 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ CCIP uses its own set of chain selectors represented by uint64 to identify blockchains. This repository contains a mapping between the custom chain identifiers (`chainSelectorId`) chain names and the chain identifiers -used by the blockchains themselves (`chainId`). +used by the blockchains themselves (`chainId`). For solana we use the base58 encoded genesis hash as the chain id. Please refer to the [official documentation](https://docs.chain.link/ccip/supported-networks) to learn more about supported networks and their selectors. @@ -19,6 +19,13 @@ import ( ) func main() { + // -------------------Chains agnostic --------------------: + + // Getting chain family based on selector + family, err := GetSelectorFamily(2664363617261496610) + + // -------------------For EVM chains-------------------- + // Getting selector based on ChainId selector, err := chainselectors.SelectorFromChainId(420) @@ -34,8 +41,22 @@ func main() { // Accessing mapping directly lookupChainId := uint64(1337) if chainSelector, exists := chainselectors.EvmChainIdToChainSelector()[lookupChainId]; exists { - fmt.Println("Found chain selector for chain", lookupChainId, ":", chainSelector) + fmt.Println("Found evm chain selector for chain", lookupChainId, ":", chainSelector) } + + // -------------------Solana Chain --------------------: + + // Getting chain family based on selector + family, err := SolanaNameFromChainId("5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d") + + // Getting chain id from chain selector + chainId, err := chainselectors.SolanaChainIdFromSelector(124615329519749607) + + // Accessing mapping directly + lookupChainId := "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d" + if chainSelector, exists:= chainselectors.SolanaChainIdToChainSelector()[lookupChainId]; exists { + fmt.Println("Found solana chain selector for chain", lookupChainId, ":", chainSelector) + } } ``` diff --git a/go.mod b/go.mod index 14e1c73..3361493 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/smartcontractkit/chain-selectors go 1.20 require ( + github.com/mr-tron/base58 v1.2.0 github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index fa4b6e6..33edea2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/selectors_solana.yml b/selectors_solana.yml index f02b167..b9fcb8b 100644 --- a/selectors_solana.yml +++ b/selectors_solana.yml @@ -1,5 +1,5 @@ selectors: - "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d": + "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d": # bash58 encoded genesis hash, https://solana.com/docs/rpc/http/getgenesishash name: solana-mainnet selector: 124615329519749607 "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY": diff --git a/solana.go b/solana.go index 5bf4767..2246976 100644 --- a/solana.go +++ b/solana.go @@ -4,6 +4,7 @@ import ( _ "embed" "fmt" + "github.com/mr-tron/base58" "gopkg.in/yaml.v3" ) @@ -33,9 +34,23 @@ func parseSolanaYml(ymlFile []byte) map[string]ChainDetails { if err != nil { panic(err) } + + validateSolanaChainID(data.SelectorsBySolanaChainId) return data.SelectorsBySolanaChainId } +func validateSolanaChainID(data map[string]ChainDetails) { + for genesisHash := range data { + b, err := base58.Decode(genesisHash) + if err != nil { + panic(fmt.Errorf("failed to decode base58 genesis hash %s: %w", genesisHash, err)) + } + if len(b) != 32 { + panic(fmt.Errorf("decoded genesis hash %s is not 32 bytes long", genesisHash)) + } + } +} + func SolanaChainIdToChainSelector() map[string]uint64 { copyMap := make(map[string]uint64, len(solanaSelectorsMap)) for k, v := range solanaSelectorsMap { @@ -54,3 +69,12 @@ func SolanaNameFromChainId(chainId string) (string, error) { } return details.ChainName, nil } + +func SolanaChainIdFromSelector(selector uint64) (string, error) { + chainId, exist := solanaChainIdBySelector[selector] + if !exist { + return "", fmt.Errorf("chain id not found for selector %d", selector) + } + + return chainId, nil +} diff --git a/solana_test.go b/solana_test.go index 1f229e1..2efc164 100644 --- a/solana_test.go +++ b/solana_test.go @@ -54,13 +54,17 @@ func Test_YmlAreValid(t *testing.T) { } func Test_SolanaChainSelectors(t *testing.T) { - for selector := range solanaChainIdBySelector { + for selector, chainId := range solanaChainIdBySelector { family, err := GetSelectorFamily(selector) require.NoError(t, err, "selector %v should be returned as solana family, but received %v", selector, err) require.NotEmpty(t, family) require.Equal(t, FamilySolana, family) + + id, err := SolanaChainIdFromSelector(selector) + require.Nil(t, err) + require.Equal(t, chainId, id) } }