diff --git a/genchains_tron.go b/genchains_tron.go new file mode 100644 index 0000000..53f63b2 --- /dev/null +++ b/genchains_tron.go @@ -0,0 +1,119 @@ +//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_tron.go" + +type chain struct { + ChainID uint64 + Selector uint64 + Name string + VarName string +} + +var chainTemplate, _ = template.New("").Parse(`// Code generated by go generate please DO NOT EDIT +package chain_selectors + +type TronChain struct { + ChainID uint64 + Selector uint64 + Name string + VarName string +} + +var ( +{{ range . }} + {{.VarName}} = TronChain{ChainID: {{ .ChainID }}, Selector: {{ .Selector }}, Name: "{{ .Name }}"}{{ end }} +) + +var TronALL = []TronChain{ +{{ 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 { + if os.IsNotExist(err) { + fmt.Println("tron: no existing generations found") + existingContent = nil + } else { + panic(err) + } + } + + if string(existingContent) == string(formatted) { + fmt.Println("tron: no changes detected") + return + } + fmt.Println("tron: updating generations") + + 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.TronChainIdToChainSelector() { + name, err := chain_selectors.TronNameFromChainId(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_tron.go b/generated_chains_tron.go new file mode 100644 index 0000000..896ae3b --- /dev/null +++ b/generated_chains_tron.go @@ -0,0 +1,21 @@ +// Code generated by go generate please DO NOT EDIT +package chain_selectors + +type TronChain struct { + ChainID uint64 + Selector uint64 + Name string + VarName string +} + +var ( + TRON_MAINNET = TronChain{ChainID: 728126428, Selector: 1546563616611573946, Name: "tron-mainnet"} + TRON_TESTNET_NILE = TronChain{ChainID: 3448148188, Selector: 2052925811360307749, Name: "tron-testnet-nile"} + TRON_TESTNET_SHASTA = TronChain{ChainID: 2494104990, Selector: 13231703482326770598, Name: "tron-testnet-shasta"} +) + +var TronALL = []TronChain{ + TRON_MAINNET, + TRON_TESTNET_NILE, + TRON_TESTNET_SHASTA, +} diff --git a/selectors.go b/selectors.go index 4066128..b291e71 100644 --- a/selectors.go +++ b/selectors.go @@ -11,6 +11,7 @@ const ( FamilyStarknet = "starknet" FamilyCosmos = "cosmos" FamilyAptos = "aptos" + FamilyTron = "tron" ) type chainInfo struct { @@ -87,6 +88,28 @@ func getChainInfo(selector uint64) (chainInfo, error) { }, nil } + // check tron + _, exist = tronChainIdBySelector[selector] + if exist { + family := FamilyTron + + chainID, err := TronChainIdFromSelector(selector) + if err != nil { + return chainInfo{}, fmt.Errorf("failed to get %v chain ID from selector %d: %w", chainID, selector, err) + } + + details, exist := tronSelectorsMap[chainID] + if !exist { + return chainInfo{}, fmt.Errorf("invalid chain id %d for %s", chainID, family) + } + + return chainInfo{ + Family: family, + ChainID: fmt.Sprintf("%d", chainID), + ChainDetails: details, + }, nil + } + return chainInfo{}, fmt.Errorf("unknown chain selector %d", selector) } @@ -140,6 +163,18 @@ func GetChainDetailsByChainIDAndFamily(chainID string, family string) (ChainDeta return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) } + return details, nil + case FamilyTron: + tronChainId, err := strconv.ParseUint(chainID, 10, 64) + if err != nil { + return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) + } + + details, exist := tronSelectorsMap[tronChainId] + 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/selectors.yml b/selectors.yml index 095890d..81bfbbc 100644 --- a/selectors.yml +++ b/selectors.yml @@ -21,9 +21,9 @@ selectors: 97: selector: 13264668187771770619 name: "binance_smart_chain-testnet" - 133: + 133: selector: 4356164186791070119 - name: "ethereum-testnet-sepolia-hashkey-1" + name: "ethereum-testnet-sepolia-hashkey-1" 157: selector: 17833296867764334567 name: "shibarium-testnet-puppynet" @@ -32,7 +32,7 @@ selectors: name: "velas-testnet" 195: selector: 2066098519157881736 - name: "ethereum-testnet-sepolia-xlayer-1" + name: "ethereum-testnet-sepolia-xlayer-1" 280: selector: 6802309497652714138 name: "ethereum-testnet-goerli-zksync-1" @@ -66,8 +66,8 @@ selectors: 1112: selector: 9284632837123596123 name: "wemix-testnet" - 1114: - selector: 4264732132125536123 + 1114: + selector: 4264732132125536123 name: "core-testnet" 1123: selector: 1948510578179542068 @@ -126,8 +126,8 @@ selectors: 9559: selector: 1113014352258747600 name: "neonlink-testnet" - 10143: - selector: 2183018362218727504 + 10143: + selector: 2183018362218727504 name: "monad-testnet" 13473: selector: 4526165231216331901 @@ -266,7 +266,6 @@ selectors: 4801: selector: 5299555114858065850 name: "ethereum-testnet-sepolia-worldchain-1" - # Mainnets 1: @@ -299,18 +298,18 @@ selectors: 109: selector: 3993510008929295315 name: "shibarium-mainnet" - 130: - selector: 1923510103922296319 - name: "ethereum-mainnet-unichain-1" + 130: + selector: 1923510103922296319 + name: "ethereum-mainnet-unichain-1" 137: selector: 4051577828743386545 name: "polygon-mainnet" 146: selector: 1673871237479749969 name: "sonic-mainnet" - 177: + 177: selector: 7613811247471741961 - name: "ethereum-mainnet-hashkey-1" + name: "ethereum-mainnet-hashkey-1" 196: selector: 3016212468291539606 name: "ethereum-mainnet-xlayer-1" @@ -353,12 +352,12 @@ selectors: 1111: selector: 5142893604156789321 name: "wemix-mainnet" - 1116: - selector: 1224752112135636129 + 1116: + selector: 1224752112135636129 name: "core-mainnet" - 61166: - selector: 5214452172935136222 - name: "treasure-mainnet" + 61166: + selector: 5214452172935136222 + name: "treasure-mainnet" 12324: selector: 3162193654116181371 name: "ethereum-mainnet-arbitrum-1-l3x-1" @@ -410,9 +409,9 @@ selectors: 432204: selector: 5463201557265485081 name: "avalanche-subnet-dexalot-mainnet" - 57073: + 57073: selector: 3461204551265785888 - name: "ethereum-mainnet-ink-1" + name: "ethereum-mainnet-ink-1" 59144: selector: 4627098889531055414 name: "ethereum-mainnet-linea-1" @@ -449,7 +448,7 @@ selectors: 1329: selector: 9027416829622342829 name: "sei-mainnet" - 2020: + 2020: selector: 6916147374840168594 name: "ronin-mainnet" 204: @@ -463,7 +462,7 @@ selectors: name: "fantom-mainnet" 480: selector: 2049429975587534727 - name: "ethereum-mainnet-worldchain-1" + name: "ethereum-mainnet-worldchain-1" 21000000: selector: 9043146809313071210 - name: "corn-mainnet" \ No newline at end of file + name: "corn-mainnet" diff --git a/selectors_tron.yml b/selectors_tron.yml new file mode 100644 index 0000000..fec88c1 --- /dev/null +++ b/selectors_tron.yml @@ -0,0 +1,10 @@ +selectors: + 3448148188: + selector: 2052925811360307749 + name: "tron-testnet-nile" + 2494104990: + selector: 13231703482326770598 + name: "tron-testnet-shasta" + 728126428: + selector: 1546563616611573946 + name: "tron-mainnet" diff --git a/tron.go b/tron.go new file mode 100644 index 0000000..9363c60 --- /dev/null +++ b/tron.go @@ -0,0 +1,66 @@ +package chain_selectors + +import ( + _ "embed" + "fmt" + + "gopkg.in/yaml.v3" +) + +//go:generate go run genchains_tron.go + +//go:embed selectors_tron.yml +var tronSelectorsYml []byte + +var ( + tronSelectorsMap = parseTronYml(tronSelectorsYml) + tronChainIdBySelector = make(map[uint64]uint64) +) + +func init() { + for k, v := range tronSelectorsMap { + tronChainIdBySelector[v.ChainSelector] = k + } +} + +func parseTronYml(ymlFile []byte) map[uint64]ChainDetails { + type ymlData struct { + SelectorsByTronChainId map[uint64]ChainDetails `yaml:"selectors"` + } + + var data ymlData + err := yaml.Unmarshal(ymlFile, &data) + if err != nil { + panic(err) + } + + return data.SelectorsByTronChainId +} + +func TronChainIdToChainSelector() map[uint64]uint64 { + copyMap := make(map[uint64]uint64, len(tronSelectorsMap)) + for k, v := range tronSelectorsMap { + copyMap[k] = v.ChainSelector + } + return copyMap +} + +func TronNameFromChainId(chainId uint64) (string, error) { + details, exist := tronSelectorsMap[chainId] + if !exist { + return "", fmt.Errorf("chain name not found for chain %v", chainId) + } + if details.ChainName == "" { + return fmt.Sprint(chainId), nil + } + return details.ChainName, nil +} + +func TronChainIdFromSelector(selector uint64) (uint64, error) { + chainId, exist := tronChainIdBySelector[selector] + if !exist { + return 0, fmt.Errorf("chain id not found for selector %d", selector) + } + + return chainId, nil +} diff --git a/tron_test.go b/tron_test.go new file mode 100644 index 0000000..165c696 --- /dev/null +++ b/tron_test.go @@ -0,0 +1,85 @@ +package chain_selectors + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_TronYmlAreValid(t *testing.T) { + tests := []struct { + name string + chainSelector uint64 + chainsId uint64 + expectErr bool + }{ + { + name: "tron-mainnet", + chainSelector: 1546563616611573946, + chainsId: 728126428, + expectErr: false, + }, + { + name: "tron-testnet-nile", + chainSelector: 2052925811360307749, + chainsId: 3448148188, + expectErr: false, + }, + { + name: "tron-testnet-shasta", + chainSelector: 13231703482326770598, + chainsId: 2494104990, + expectErr: false, + }, + { + name: "non-existing", + chainSelector: 81923186267, + chainsId: 471, + expectErr: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + name, err1 := TronNameFromChainId(test.chainsId) + if test.expectErr { + require.Error(t, err1) + return + } + require.NoError(t, err1) + assert.Equal(t, test.name, name) + }) + } +} + +func Test_TronChainSelectors(t *testing.T) { + for selector, chainId := range tronChainIdBySelector { + family, err := GetSelectorFamily(selector) + require.NoError(t, err, + "selector %v should be returned as tron family, but received %v", + selector, err) + require.NotEmpty(t, family) + require.Equal(t, FamilyTron, family) + + id, err := TronChainIdFromSelector(selector) + require.Nil(t, err) + require.Equal(t, chainId, id) + } +} + +func Test_TronGetChainDetailsByChainIDAndFamily(t *testing.T) { + for k, v := range tronSelectorsMap { + details, err := GetChainDetailsByChainIDAndFamily(fmt.Sprint(k), FamilyTron) + assert.NoError(t, err) + assert.Equal(t, v, details) + } +} + +func Test_TronGetChainIDByChainSelector(t *testing.T) { + for k, v := range tronSelectorsMap { + chainID, err := GetChainIDFromSelector(v.ChainSelector) + assert.NoError(t, err) + assert.Equal(t, chainID, fmt.Sprintf("%v", k)) + } +}