From 9500e1e4a0f6e3082b409fc087d3408906d5de7b Mon Sep 17 00:00:00 2001 From: jlaveracll Date: Fri, 8 Nov 2024 18:01:44 -0300 Subject: [PATCH] [NONEVM-864] Add aptos chain selector (#68) * Add aptos chain selectors --- aptos.go | 71 ++++++++++++++++++++++++ aptos_test.go | 71 ++++++++++++++++++++++++ genchains_aptos.go | 114 ++++++++++++++++++++++++++++++++++++++ genchains_evm.go | 3 +- genchains_solana.go | 3 +- generated_chains_aptos.go | 19 +++++++ selectors.go | 18 ++++++ selectors_aptos.yml | 7 +++ 8 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 aptos.go create mode 100644 aptos_test.go create mode 100644 genchains_aptos.go create mode 100644 generated_chains_aptos.go create mode 100644 selectors_aptos.yml diff --git a/aptos.go b/aptos.go new file mode 100644 index 0000000..091caba --- /dev/null +++ b/aptos.go @@ -0,0 +1,71 @@ +package chain_selectors + +import ( + _ "embed" + "fmt" + + "gopkg.in/yaml.v3" +) + +//go:generate go run genchains_aptos.go + +//go:embed selectors_aptos.yml +var aptosSelectorsYml []byte + +var ( + aptosSelectorsMap = parseAptosYml(aptosSelectorsYml) + aptosChainIdBySelector = make(map[uint64]uint64) +) + +func init() { + for k, v := range aptosSelectorsMap { + aptosChainIdBySelector[v.ChainSelector] = k + } +} + +func parseAptosYml(ymlFile []byte) map[uint64]ChainDetails { + type ymlData struct { + SelectorsByAptosChainId map[uint64]ChainDetails `yaml:"selectors"` + } + + var data ymlData + err := yaml.Unmarshal(ymlFile, &data) + if err != nil { + panic(err) + } + + validateAptosChainID(data.SelectorsByAptosChainId) + return data.SelectorsByAptosChainId +} + +func validateAptosChainID(data map[uint64]ChainDetails) { + // TODO: https://smartcontract-it.atlassian.net/browse/NONEVM-890 +} + +func AptosChainIdToChainSelector() map[uint64]uint64 { + copyMap := make(map[uint64]uint64, len(aptosSelectorsMap)) + for k, v := range aptosSelectorsMap { + copyMap[k] = v.ChainSelector + } + return copyMap +} + +func AptosNameFromChainId(chainId uint64) (string, error) { + details, exist := aptosSelectorsMap[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 AptosChainIdFromSelector(selector uint64) (uint64, error) { + chainId, exist := aptosChainIdBySelector[selector] + if !exist { + return 0, fmt.Errorf("chain id not found for selector %d", selector) + } + + return chainId, nil +} diff --git a/aptos_test.go b/aptos_test.go new file mode 100644 index 0000000..9df5d80 --- /dev/null +++ b/aptos_test.go @@ -0,0 +1,71 @@ +package chain_selectors + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_AptosYmlAreValid(t *testing.T) { + tests := []struct { + name string + chainSelector uint64 + chainsId uint64 + expectErr bool + }{ + { + name: "aptos-mainnet", + chainSelector: 124615329519749607, + chainsId: 1, + expectErr: false, + }, + { + name: "aptos-testnet", + chainSelector: 6302590918974934319, + chainsId: 2, + 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 := AptosNameFromChainId(test.chainsId) + if test.expectErr { + require.Error(t, err1) + return + } + require.NoError(t, err1) + assert.Equal(t, test.name, name) + }) + } +} + +func Test_AptosChainSelectors(t *testing.T) { + for selector, chainId := range aptosChainIdBySelector { + family, err := GetSelectorFamily(selector) + require.NoError(t, err, + "selector %v should be returned as aptos family, but received %v", + selector, err) + require.NotEmpty(t, family) + require.Equal(t, FamilyAptos, family) + + id, err := AptosChainIdFromSelector(selector) + require.Nil(t, err) + require.Equal(t, chainId, id) + } +} + +func Test_AptosGetChainDetailsByChainIDAndFamily(t *testing.T) { + for k, v := range aptosSelectorsMap { + details, err := GetChainDetailsByChainIDAndFamily(fmt.Sprint(k), FamilyAptos) + assert.NoError(t, err) + assert.Equal(t, v, details) + } +} diff --git a/genchains_aptos.go b/genchains_aptos.go new file mode 100644 index 0000000..f05c31a --- /dev/null +++ b/genchains_aptos.go @@ -0,0 +1,114 @@ +//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_aptos.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 AptosChain struct { + ChainID uint64 + Selector uint64 + Name string + VarName string +} + +var ( +{{ range . }} + {{.VarName}} = AptosChain{ChainID: {{ .ChainID }}, Selector: {{ .Selector }}, Name: "{{ .Name }}"}{{ end }} +) + +var AptosALL = []AptosChain{ +{{ 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("aptos: no changes detected") + return + } + fmt.Println("aptos: 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.AptosChainIdToChainSelector() { + name, err := chain_selectors.AptosNameFromChainId(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/genchains_evm.go b/genchains_evm.go index 62dafdc..804068e 100644 --- a/genchains_evm.go +++ b/genchains_evm.go @@ -64,9 +64,10 @@ func main() { } if string(existingContent) == string(formatted) { - fmt.Println("no changes detected") + fmt.Println("evm: no changes detected") return } + fmt.Println("evm: updating generations") err = os.WriteFile(filename, formatted, 0644) if err != nil { diff --git a/genchains_solana.go b/genchains_solana.go index 85937ed..0f0a476 100644 --- a/genchains_solana.go +++ b/genchains_solana.go @@ -64,9 +64,10 @@ func main() { } if string(existingContent) == string(formatted) { - fmt.Println("no changes detected") + fmt.Println("solana: no changes detected") return } + fmt.Println("solana: updating generations") err = os.WriteFile(filename, formatted, 0644) if err != nil { diff --git a/generated_chains_aptos.go b/generated_chains_aptos.go new file mode 100644 index 0000000..ac53a9b --- /dev/null +++ b/generated_chains_aptos.go @@ -0,0 +1,19 @@ +// Code generated by go generate please DO NOT EDIT +package chain_selectors + +type AptosChain struct { + ChainID uint64 + Selector uint64 + Name string + VarName string +} + +var ( + APTOS_MAINNET = AptosChain{ChainID: 1, Selector: 4741433654826277614, Name: "aptos-mainnet"} + APTOS_TESTNET = AptosChain{ChainID: 2, Selector: 743186221051783445, Name: "aptos-testnet"} +) + +var AptosALL = []AptosChain{ + APTOS_MAINNET, + APTOS_TESTNET, +} diff --git a/selectors.go b/selectors.go index eb9ddb4..a163742 100644 --- a/selectors.go +++ b/selectors.go @@ -26,6 +26,12 @@ func GetSelectorFamily(selector uint64) (string, error) { return FamilySolana, nil } + // check aptos + _, exist = aptosChainIdBySelector[selector] + if exist { + return FamilyAptos, nil + } + return "", fmt.Errorf("unknown chain selector %d", selector) } @@ -49,6 +55,18 @@ func GetChainDetailsByChainIDAndFamily(chainID string, family string) (ChainDeta return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) } + return details, nil + case FamilyAptos: + aptosChainId, err := strconv.ParseUint(chainID, 10, 64) + if err != nil { + return ChainDetails{}, fmt.Errorf("invalid chain id %s for %s", chainID, family) + } + + details, exist := aptosSelectorsMap[aptosChainId] + 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_aptos.yml b/selectors_aptos.yml new file mode 100644 index 0000000..011e9ef --- /dev/null +++ b/selectors_aptos.yml @@ -0,0 +1,7 @@ +selectors: + 1: + name: aptos-mainnet + selector: 4741433654826277614 + 2: + name: aptos-testnet + selector: 743186221051783445