diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fc72752..c697d73 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,7 +6,7 @@ on: pull_request: branches: [ master ] schedule: - - cron: "42 6 * * 0" + - cron: "41 6 * * 0" jobs: build: @@ -23,10 +23,11 @@ jobs: - name: Test env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} + NETWORKS: ${{ secrets.NETWORKS }} run: go test -v -coverprofile=coverage.txt -covermode=count ./... - name: Codecov uses: codecov/codecov-action@v2.1.0 - name: golangci-lint - uses: golangci/golangci-lint-action@v2 \ No newline at end of file + uses: golangci/golangci-lint-action@v2 diff --git a/README.md b/README.md index 56acd1f..17163a8 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -**English** | [中文](https://github.com/nanmu42/etherscan-api/blob/master/README_ZH.md) +**English** | [中文](https://github.com/uded/etherscan-api/blob/master/README_ZH.md) # etherscan-api -[![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[![CI status](https://github.com/nanmu42/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/nanmu42/etherscan-api/actions) -[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) -[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) +[![GoDoc](https://godoc.org/github.com/uded/etherscan-api?status.svg)](https://godoc.org/github.com/uded/etherscan-api) +[![CI status](https://github.com/uded/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/uded/etherscan-api/actions) +[![codecov](https://codecov.io/gh/uded/etherscan-api/branch/master/graph/badge.svg?token=2OFgDXhVM0)](https://codecov.io/gh/uded/etherscan-api) +[![Go Report Card](https://goreportcard.com/badge/github.com/uded/etherscan-api)](https://goreportcard.com/report/github.com/uded/etherscan-api) Golang client for the Etherscan.io API(and its families like BscScan), with nearly full implementation(accounts, transactions, tokens, contracts, blocks, stats), full network support(Mainnet, Ropsten, Kovan, Rinkby, Goerli, Tobalaba), and only depending on standard library. :wink: # Usage ```bash -go get github.com/nanmu42/etherscan-api +go get github.com/uded/etherscan-api ``` Create an API instance and off you go. :rocket: ```go import ( - "github.com/nanmu42/etherscan-api" + "github.com/uded/etherscan-api" "fmt" ) @@ -61,7 +61,7 @@ func main() { } ``` -You may find full method list at [GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api). +You may find full method list at [GoDoc](https://godoc.org/github.com/uded/etherscan-api). # Etherscan API Key diff --git a/README_ZH.md b/README_ZH.md index 905a770..0a9fd0b 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -2,10 +2,10 @@ # etherscan-api -[![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[![CI status](https://github.com/nanmu42/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/nanmu42/etherscan-api/actions) -[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) -[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) +[![GoDoc](https://godoc.org/github.com/uded/etherscan-api?status.svg)](https://godoc.org/github.com/uded/etherscan-api) +[![CI status](https://github.com/uded/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/uded/etherscan-api/actions) +[![codecov](https://codecov.io/gh/uded/etherscan-api/branch/master/graph/badge.svg?token=2OFgDXhVM0)](https://codecov.io/gh/uded/etherscan-api) +[![Go Report Card](https://goreportcard.com/badge/github.com/uded/etherscan-api)](https://goreportcard.com/report/github.com/uded/etherscan-api) Etherscan API的Golang客户端, 支持几乎所有功能(accounts, transactions, tokens, contracts, blocks, stats), @@ -15,14 +15,14 @@ Etherscan API的Golang客户端, # 使用方法 ```bash -go get github.com/nanmu42/etherscan-api +go get github.com/uded/etherscan-api ``` 填入网络选项和API Key即可开始使用。 :rocket: ```go import ( - "github.com/nanmu42/etherscan-api" + "github.com/uded/etherscan-api" "fmt" ) @@ -63,7 +63,7 @@ func main() { } ``` -客户端方法列表可在[GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api)查询。 +客户端方法列表可在[GoDoc](https://godoc.org/github.com/uded/etherscan-api)查询。 # Etherscan API Key @@ -80,4 +80,4 @@ API的调用速率不能高于5次/秒,否则会遭到封禁。 MIT -请自由享受开源,欢迎贡献开源。 \ No newline at end of file +请自由享受开源,欢迎贡献开源。 diff --git a/block_e2e_test.go b/block_e2e_test.go index c49a8ac..f907748 100644 --- a/block_e2e_test.go +++ b/block_e2e_test.go @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 LI Zhennan + * Copyright (c) 2022 Łukasz Rżanek * * Use of this work is governed by a MIT License. * You may find a license copy in project root. @@ -10,37 +11,79 @@ package etherscan import ( "encoding/json" "testing" + + "github.com/stretchr/testify/assert" ) func TestClient_BlockReward(t *testing.T) { - const ans = `{"blockNumber":"2165403","timeStamp":"1472533979","blockMiner":"0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3","blockReward":"5314181600000000000","uncles":[{"miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","unclePosition":"0","blockreward":"3750000000000000000"},{"miner":"0x0d0c9855c722ff0c78f21e43aa275a5b8ea60dce","unclePosition":"1","blockreward":"3750000000000000000"}],"uncleInclusionReward":"312500000000000000"}` + const ( + ethAns = `{"blockNumber":"2165403","timeStamp":"1472533979","blockMiner":"0x13a06d3dfe21e0db5c016c03ea7d2509f7f8d1e3","blockReward":"5314181600000000000","uncles":[{"miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","unclePosition":"0","blockreward":"3750000000000000000"},{"miner":"0x0d0c9855c722ff0c78f21e43aa275a5b8ea60dce","unclePosition":"1","blockreward":"3750000000000000000"}],"uncleInclusionReward":"312500000000000000"}` + maticAns = `{"blockNumber":"2165403","timeStamp":"1595322344","blockMiner":"0x0375b2fc7140977c9c76d45421564e354ed42277","blockReward":"0","uncles":[],"uncleInclusionReward":"0"}` + bscAns = `{"blockNumber":"2165403","timeStamp":"1605169045","blockMiner":"0x78f3adfc719c99674c072166708589033e2d9afe","blockReward":"0","uncles":[],"uncleInclusionReward":"0"}` + avaxAns = `{"blockNumber":"2165403","timeStamp":"1622557183","blockMiner":"0x0100000000000000000000000000000000000000","blockReward":"7963290000000000","uncles":[],"uncleInclusionReward":"0"}` + ftmAns = `{"blockNumber":"2165403","timeStamp":"1612983193","blockMiner":"0x0000000000000000000000000000000000000000","blockReward":"5225066000000000","uncles":[],"uncleInclusionReward":"0"}` + cronosAns = `{"blockNumber":"2165403","timeStamp":"1648843458","blockMiner":"0x4f87a3f99bd1e58d01de1c38b7f83cb967e816c2","blockReward":"49532402000000000000","uncles":[],"uncleInclusionReward":"0"}` + arbitrumAns = `{"blockNumber":"2165403","timeStamp":"1634069963","blockMiner":"0x0000000000000000000000000000000000000000","blockReward":"2065103660121552","uncles":[],"uncleInclusionReward":"0"}` + ) - reward, err := api.BlockReward(2165403) - noError(t, err, "api.BlockReward") + type Data struct { + BlockNumber int + Ans string + } - j, err := json.Marshal(reward) - noError(t, err, "json.Marshal") - if string(j) != ans { - t.Errorf("api.BlockReward not working, got %s, want %s", j, ans) + testData := map[string]Data{ + EthMainnet.CommonName: {2165403, ethAns}, + MaticMainnet.CommonName: {2165403, maticAns}, + BscMainnet.CommonName: {2165403, bscAns}, + AvaxMainnet.CommonName: {2165403, avaxAns}, + FantomMainnet.CommonName: {2165403, ftmAns}, + CronosMainnet.CommonName: {2165403, cronosAns}, + ArbitrumMainnet.CommonName: {2165403, arbitrumAns}, + } + + for _, network := range TestNetworks { + if td, ok := testData[network.Network.CommonName]; ok { + t.Run(network.Network.Name, func(t *testing.T) { + reward, err := network.client.BlockReward(td.BlockNumber) + assert.NoError(t, err) + + j, err := json.Marshal(reward) + assert.NoError(t, err) + assert.Equalf(t, td.Ans, string(j), "api.BlockReward not working, got %s, want %s", j, ethAns) + }) + } } } func TestClient_BlockNumber(t *testing.T) { - // Note: All values taken from docs.etherscan.io/api-endpoints/blocks - const ansBefore = 9251482 - const ansAfter = 9251483 - - blockNumber, err := api.BlockNumber(1578638524, "before") - noError(t, err, "api.BlockNumber") + type Data struct { + Timestamp int64 + AnsBefore int + AnsAfter int + } - if blockNumber != ansBefore { - t.Errorf(`api.BlockNumber(1578638524, "before") not working, got %d, want %d`, blockNumber, ansBefore) + testData := map[string]Data{ + // Note: All values taken from docs.etherscan.io/api-endpoints/blocks + EthMainnet.CommonName: {1578638524, 9251482, 9251483}, + MaticMainnet.CommonName: {1601510400, 5164199, 5164200}, + BscMainnet.CommonName: {1601510400, 946206, 946207}, + AvaxMainnet.CommonName: {1609455600, 18960, 18961}, + FantomMainnet.CommonName: {1609455600, 1632921, 1632921}, // it is the same! + CronosMainnet.CommonName: {1654034400, 3023556, 3023557}, + ArbitrumMainnet.CommonName: {1656626400, 16655953, 16655954}, } - blockNumber, err = api.BlockNumber(1578638524, "after") - noError(t, err, "api.BlockNumber") + for _, network := range TestNetworks { + if td, ok := testData[network.Network.CommonName]; ok { + t.Run(network.Network.Name, func(t *testing.T) { + blockNumber, err := network.client.BlockNumber(td.Timestamp, "before") + assert.NoError(t, err) + assert.Equalf(t, td.AnsBefore, blockNumber, `api.BlockNumber(%d, "before") not working, got %d, want %d`, td.Timestamp, blockNumber, td.AnsBefore) - if blockNumber != ansAfter { - t.Errorf(`api.BlockNumber(1578638524,"after") not working, got %d, want %d`, blockNumber, ansAfter) + blockNumber, err = network.client.BlockNumber(td.Timestamp, "after") + assert.NoError(t, err) + assert.Equalf(t, td.AnsAfter, blockNumber, `api.BlockNumber(%d, "after") not working, got %d, want %d`, td.Timestamp, blockNumber, td.AnsAfter) + }) + } } } diff --git a/client.go b/client.go index e69679a..5e95a68 100644 --- a/client.go +++ b/client.go @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 LI Zhennan + * Copyright (c) 2022 Łukasz Rżanek * * Use of this work is governed by a MIT License. * You may find a license copy in project root. @@ -23,6 +24,7 @@ import ( type Client struct { coon *http.Client key string + network Network baseURL string // Verbose when true, talks a lot @@ -37,13 +39,17 @@ type Client struct { AfterRequest func(module, action string, param map[string]interface{}, outcome interface{}, requestErr error) } +func (c *Client) GetNetwork() Network { + return c.network +} + // New initialize a new etherscan API client // please use pre-defined network value func New(network Network, APIKey string) *Client { return NewCustomized(Customization{ Timeout: 30 * time.Second, Key: APIKey, - BaseURL: fmt.Sprintf(`https://%s.etherscan.io/api?`, network.SubDomain()), + Network: &network, }) } @@ -51,10 +57,12 @@ func New(network Network, APIKey string) *Client { type Customization struct { // Timeout for API call Timeout time.Duration - // API key applied from Etherscan + // API key applied from scanner Key string // Base URL like `https://api.etherscan.io/api?` BaseURL string + // Network + Network *Network // When true, talks a lot Verbose bool // HTTP Client to be used. Specifying this value will ignore the Timeout value set @@ -81,8 +89,12 @@ func NewCustomized(config Customization) *Client { Timeout: config.Timeout, } } + if config.Network != nil { + config.BaseURL = config.Network.baseURL + } return &Client{ coon: httpClient, + network: *config.Network, key: config.Key, baseURL: config.BaseURL, Verbose: config.Verbose, diff --git a/client_test.go b/client_test.go index a8d045c..612580c 100644 --- a/client_test.go +++ b/client_test.go @@ -12,7 +12,7 @@ import ( ) func TestClient_craftURL(t *testing.T) { - c := New(Ropsten, "abc123") + c := New(EthRopsten, "abc123") const expected = `https://api-ropsten.etherscan.io/api?action=craftURL&apikey=abc123&four=d&four=e&four=f&module=testing&one=1&three=1&three=2&three=3&two=2` output := c.craftURL("testing", "craftURL", M{ diff --git a/doc.go b/doc.go index d345cab..7e5c952 100644 --- a/doc.go +++ b/doc.go @@ -2,7 +2,7 @@ // // This work is a nearly Full implementation // (accounts, transactions, tokens, contracts, blocks, stats), -// with full network support(Mainnet, Ropsten, Kovan, Rinkby, Tobalaba), +// with full network support(EthMainnet, EthRopsten, EthKovan, EthRinkby, EthTobalaba), // and only depending on standard library. // // Example can be found at https://github.com/nanmu42/etherscan-api diff --git a/go.mod b/go.mod index 3e16f01..8defbc1 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,14 @@ -module github.com/nanmu42/etherscan-api +module github.com/uded/etherscan-api -go 1.13 +go 1.18 -require github.com/google/go-cmp v0.5.7 // indirect +require ( + github.com/google/go-cmp v0.5.7 + github.com/stretchr/testify v1.8.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index a365b08..e51298d 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,19 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/network.go b/network.go index 32372ca..b90c91a 100644 --- a/network.go +++ b/network.go @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 LI Zhennan + * Copyright (c) 2022 Łukasz Rżanek * * Use of this work is governed by a MIT License. * You may find a license copy in project root. @@ -7,28 +8,169 @@ package etherscan -const ( - //// Ethereum public networks - - // Mainnet Ethereum mainnet for production - Mainnet Network = "api" - // Ropsten Testnet(POW) - Ropsten Network = "api-ropsten" - // Kovan Testnet(POA) - Kovan Network = "api-kovan" - // Rinkby Testnet(CLIQUE) - Rinkby Network = "api-rinkeby" - // Goerli Testnet(CLIQUE) - Goerli Network = "api-goerli" - // Tobalaba Testnet - Tobalaba Network = "api-tobalaba" +import ( + "fmt" + "strings" ) +var ( + // EthMainnet Ethereum mainnet for production + EthMainnet Network = Network{"Ethereum", "eth_main", "ETH", "https://api.etherscan.io/api?", "0x1", 1} + // EthRopsten Testnet(POW) + EthRopsten Network = Network{"Ethereum Ropsten", "eth_ropsten", "ETH", "https://api-ropsten.etherscan.io/api?", "0x3", 3} + // EthKovan Testnet(POA) + EthKovan Network = Network{"Ethereum Kovan", "eth_kovan", "ETH", "https://api-kovan.etherscan.io/api?", "0x2a", 42} + // EthRinkby Testnet(CLIQUE) + EthRinkby Network = Network{"Ethereum Rinkby", "eth_rinkeby", "ETH", "https://api-rinkeby.etherscan.io/api?", "0x4", 4} + // EthGoerli Testnet(CLIQUE) + EthGoerli Network = Network{"Ethereum Goerli", "eth_goerli", "ETH", "https://api-goerli.etherscan.io/api?", "0x5", 5} + // EthTobalaba Testnet + EthTobalaba Network = Network{"Ethereum Tobalaba", "eth_tobalaba", "ETH", "https://api-tobalaba.etherscan.io/api?", "0x0", 0} + // MaticMainnet Matic mainnet for production + MaticMainnet Network = Network{"Polygon", "polygon", "MATIC", "https://api.polygonscan.com/api?", "0x89", 137} + // MaticTestnet Matic testnet for development + MaticTestnet Network = Network{"Polygon Mumbai", "polygon_mumbai", "MATIC", "https://api-testnet.polygonscan.com/api?", "0x13881", 80001} + // BscMainnet Bsc mainnet for production + BscMainnet Network = Network{"Binance", "bsc", "BNB", "https://api.bscscan.com/api?", "0x38", 56} + // BscTestnet Bsc testnet for development + BscTestnet Network = Network{"Binance test", "bsc_test", "BNB", "https://api-testnet.bscscan.com/api?", "0x61", 97} + // AvaxMainnet Avalanche mainnet for production + AvaxMainnet Network = Network{"Avax", "avax", "AVAX", "https://api.snowtrace.io/api?", "0xa86a", 43114} + // AvaxTestnet Avalanche testnet for development + AvaxTestnet Network = Network{"Avax test", "avax_test", "AVAX", "https://api-testnet.snowtrace.io/api?", "0xa869", 43113} + // FantomMainnet for production + FantomMainnet Network = Network{"Fantom", "fantom", "FTM", "htstps://api.ftmscan.com/api?", "0xfa", 250} + // FantomTestnet + FantomTestnet Network = Network{"Fantom test", "fantom_test", "FTM", "https://api-testnet.ftmscan.com/api?", "0x0", 0} + // Cronos mainnet for production + CronosMainnet Network = Network{"Cronos", "cronos", "CRO", "https://api.cronoscan.com/api?", "0x19", 25} + // Cronos test net + CronosTestnet Network = Network{"Cronos test", "cronos_test", "CRO", "https://api-testnet.cronoscan.com/api?", "0x152", 338} + // Arbitrum mainnet for production + ArbitrumMainnet Network = Network{"Arbitrum", "arbitrum", "ETH", "https://api.arbiscan.io/api?", "0x0", 0} + // Arbitrum test net + ArbitrumTestnet Network = Network{"Arbitrum test", "arbitrum_test", "ETH", "https://api-testnet.arbiscan.io/", "0x0", 0} + // Optimism mainnet for production + OptimismMainnet Network = Network{"Optimsm", "optimism", "ETH", "https://api-optimistic.etherscan.io/", "0xa", 10} + // Optimism test net + OptimismTestnet Network = Network{"Optimism Goerli", "optimism_test", "ETH", "https://api-goerli-optimistic.etherscan.io/", "", 420} + + networks = map[string]*Network{ + EthMainnet.Name: &EthMainnet, + EthMainnet.CommonName: &EthMainnet, + "ethmainnet": &EthMainnet, + "ethereum": &EthMainnet, + "eth": &EthMainnet, + EthRopsten.Name: &EthRopsten, + EthRopsten.CommonName: &EthRopsten, + "ethropsten": &EthRopsten, + "ropsten": &EthRopsten, + EthKovan.Name: &EthKovan, + EthKovan.CommonName: &EthKovan, + "ethkovan": &EthKovan, + EthRinkby.Name: &EthRinkby, + EthRinkby.CommonName: &EthRinkby, + "ethrinkby": &EthRinkby, + EthGoerli.Name: &EthGoerli, + EthGoerli.CommonName: &EthGoerli, + "ethgoerli": &EthGoerli, + EthTobalaba.Name: &EthTobalaba, + EthTobalaba.CommonName: &EthTobalaba, + "ethtobalaba": &EthTobalaba, + MaticMainnet.Name: &MaticMainnet, + MaticMainnet.CommonName: &MaticMainnet, + "maticmainnet": &MaticMainnet, + "polygon": &MaticMainnet, + "polygon-pos": &MaticMainnet, + "matic": &MaticMainnet, + MaticTestnet.Name: &MaticTestnet, + MaticTestnet.CommonName: &MaticTestnet, + "matictestnet": &MaticTestnet, + "mumbai": &MaticTestnet, + BscMainnet.Name: &BscMainnet, + BscMainnet.CommonName: &BscMainnet, + "bscmainnet": &BscMainnet, + "binance": &BscMainnet, + "binance-smart-chain": &BscMainnet, + BscTestnet.Name: &BscTestnet, + BscTestnet.CommonName: &BscTestnet, + "bsctestnet": &BscTestnet, + AvaxMainnet.Name: &AvaxMainnet, + AvaxMainnet.CommonName: &AvaxMainnet, + "avalanche": &AvaxMainnet, + "avax": &AvaxMainnet, + "avaxmainnet": &AvaxMainnet, + "avalanchemainnet": &AvaxMainnet, + AvaxTestnet.Name: &AvaxTestnet, + AvaxTestnet.CommonName: &AvaxTestnet, + "avaxtestnet": &AvaxTestnet, + "avalanchetestnet": &AvaxTestnet, + "avaxfuji": &AvaxTestnet, + "avalanchefuji": &AvaxTestnet, + FantomMainnet.Name: &FantomMainnet, + FantomMainnet.CommonName: &FantomMainnet, + "fantommainnet": &FantomMainnet, + FantomTestnet.Name: &FantomTestnet, + FantomTestnet.CommonName: &FantomTestnet, + "fantomtest": &FantomTestnet, + "fantomtestnet": &FantomTestnet, + CronosMainnet.Name: &CronosMainnet, + CronosMainnet.CommonName: &CronosMainnet, + "cronosmainnet": &CronosMainnet, + CronosTestnet.Name: &CronosTestnet, + CronosTestnet.CommonName: &CronosTestnet, + "cronostest": &CronosTestnet, + "cronostestnet": &CronosTestnet, + ArbitrumMainnet.Name: &ArbitrumMainnet, + ArbitrumMainnet.CommonName: &ArbitrumMainnet, + "arbitrummainnet": &ArbitrumMainnet, + ArbitrumTestnet.Name: &ArbitrumTestnet, + ArbitrumTestnet.CommonName: &ArbitrumTestnet, + "arbitrumtest": &ArbitrumTestnet, + "arbitrumtestnet": &ArbitrumTestnet, + "arbitrum_rinkeby": &ArbitrumTestnet, + OptimismMainnet.Name: &OptimismMainnet, + OptimismMainnet.CommonName: &OptimismMainnet, + OptimismTestnet.Name: &OptimismTestnet, + OptimismTestnet.CommonName: &OptimismTestnet, + "optimismtest": &OptimismTestnet, + "optimismtestnet": &OptimismTestnet, + "optimism_goerli": &OptimismTestnet, + "optimismgoerli": &OptimismTestnet, + } + + networkNames []string +) + +func init() { + for name := range networks { + networkNames = append(networkNames, name) + } +} + // Network is ethereum network type (mainnet, ropsten, etc) -type Network string +type Network struct { + Name string // Name of the network or chain + CommonName string // CommonName of the network or chain + TokenName string // TokenName of the network + baseURL string // baseURL for the API client + ChainIDHex string // ChainIDHex for identifing the chain + ChainID int // ChainID for identyfing the chain +} + +// Domain returns the subdomain of etherscan API via n provided. +func (n Network) Domain() (sub string) { + return n.baseURL +} + +func ParseNetworkName(network string) (Network, error) { + if x, ok := networks[network]; ok { + return *x, nil + } + // Case-insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. + if x, ok := networks[strings.ToLower(network)]; ok { + return *x, nil + } + return Network{}, fmt.Errorf("%s is not a valid ETHNetworkType, try one of [%s]", network, strings.Join(networkNames, ", ")) -// SubDomain returns the subdomain of etherscan API -// via n provided. -func (n Network) SubDomain() (sub string) { - return string(n) } diff --git a/network_test.go b/network_test.go new file mode 100644 index 0000000..d3a0d40 --- /dev/null +++ b/network_test.go @@ -0,0 +1,69 @@ +package etherscan + +import ( + "reflect" + "testing" +) + +func TestParseNetworkName(t *testing.T) { + tests := []struct { + name string + want Network + wantErr bool + }{ + {"ethmainnet", EthMainnet, false}, + {"eth main net", Network{}, true}, + {"ethereum", EthMainnet, false}, + {"eth", EthMainnet, false}, + {"ethropsten", EthRopsten, false}, + {"ropsten", EthRopsten, false}, + {"ethkovan", EthKovan, false}, + {"ethrinkby", EthRinkby, false}, + {"ethgoerli", EthGoerli, false}, + {"ethtobalaba", EthTobalaba, false}, + {"maticmainnet", MaticMainnet, false}, + {"polygon", MaticMainnet, false}, + {"matic", MaticMainnet, false}, + {"matictestnet", MaticTestnet, false}, + {"mumbai", MaticTestnet, false}, + {"bscmainnet", BscMainnet, false}, + {"binance", BscMainnet, false}, + {"bsctestnet", BscTestnet, false}, + {EthMainnet.Name, EthMainnet, false}, + {EthMainnet.CommonName, EthMainnet, false}, + {EthRopsten.Name, EthRopsten, false}, + {EthRopsten.CommonName, EthRopsten, false}, + {EthKovan.Name, EthKovan, false}, + {EthKovan.CommonName, EthKovan, false}, + {EthRinkby.Name, EthRinkby, false}, + {EthRinkby.CommonName, EthRinkby, false}, + {EthGoerli.Name, EthGoerli, false}, + {EthGoerli.CommonName, EthGoerli, false}, + {EthTobalaba.Name, EthTobalaba, false}, + {EthTobalaba.CommonName, EthTobalaba, false}, + {MaticMainnet.Name, MaticMainnet, false}, + {MaticMainnet.CommonName, MaticMainnet, false}, + {MaticTestnet.Name, MaticTestnet, false}, + {MaticTestnet.CommonName, MaticTestnet, false}, + {BscMainnet.Name, BscMainnet, false}, + {BscMainnet.CommonName, BscMainnet, false}, + {BscTestnet.Name, BscTestnet, false}, + {BscTestnet.CommonName, BscTestnet, false}, + {AvaxMainnet.Name, AvaxMainnet, false}, + {AvaxMainnet.CommonName, AvaxMainnet, false}, + {AvaxTestnet.Name, AvaxTestnet, false}, + {AvaxTestnet.CommonName, AvaxTestnet, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseNetworkName(tt.name) + if (err != nil) != tt.wantErr { + t.Errorf("ParseNetworkName() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ParseNetworkName() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/setup_e2e_test.go b/setup_e2e_test.go index 64f2702..7650394 100644 --- a/setup_e2e_test.go +++ b/setup_e2e_test.go @@ -32,7 +32,7 @@ func init() { } bucket = NewBucket(500 * time.Millisecond) - api = New(Mainnet, apiKey) + api = New(EthMainnet, apiKey) api.Verbose = true api.BeforeRequest = func(module string, action string, param map[string]interface{}) error { bucket.Take()