Skip to content

Commit e325d2c

Browse files
committed
refactor(network): add support for v2 url scheme
* change `Network` to `Chain` * add support for new url scheme - api.etherscan.com/v2/api? instead of <chain>scan.com/api? * update tests
1 parent d3c2f46 commit e325d2c

File tree

4 files changed

+183
-49
lines changed

4 files changed

+183
-49
lines changed

internal/chain/chain.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2018 LI Zhennan
3+
*
4+
* Use of this work is governed by a MIT License.
5+
* You may find a license copy in project root.
6+
*/
7+
8+
package chain
9+
10+
type Chain int
11+
12+
// ChainIDs supported as of 2025/01/02
13+
const (
14+
EthereumMainnet Chain = 1
15+
OpMainnet Chain = 10
16+
SepoliaTestnet Chain = 11155111
17+
HoleskyTestnet Chain = 17000
18+
CronosMainnet Chain = 25
19+
ApeChainCurtisTestnet Chain = 33111
20+
ApeChainMainnet Chain = 33139
21+
ArbitrumOneMainnet Chain = 42161
22+
ArbitrumNovaMainnet Chain = 42170
23+
CeloMainnet Chain = 42220
24+
AvalancheCChain Chain = 43114
25+
AvalancheFujiTestnet Chain = 43113
26+
XDCMainnet Chain = 50
27+
XDCApothemTestnet Chain = 51
28+
BNBSmartChainMainnet Chain = 56
29+
BNBSmartChainTestnet Chain = 97
30+
Gnosis Chain = 100
31+
PolygonMainnet Chain = 137
32+
SonicMainnet Chain = 146
33+
BitTorrentChainMainnet Chain = 199
34+
FantomOperaMainnet Chain = 250
35+
FraxtalMainnet Chain = 252
36+
KromaMainnet Chain = 255
37+
zkSyncMainnet Chain = 324
38+
zkSyncSepoliaTestnet Chain = 300
39+
MoonbeamMainnet Chain = 1284
40+
MoonbaseAlphaTestnet Chain = 1287
41+
MoonriverMainnet Chain = 1285
42+
BitTorrentChainTestnet Chain = 1028
43+
PolygonZkEVMMainnet Chain = 1101
44+
WEMIX30Mainnet Chain = 1111
45+
WEMIX30Testnet Chain = 1112
46+
FantomTestnet Chain = 4002
47+
WorldMainnet Chain = 480
48+
WorldSepoliaTestnet Chain = 4801
49+
MantleMainnet Chain = 5000
50+
MantleSepoliaTestnet Chain = 5003
51+
BaseMainnet Chain = 8453
52+
BaseSepoliaTestnet Chain = 84532
53+
BlastMainnet Chain = 81457
54+
CeloAlfajoresTestnet Chain = 44787
55+
PolygonAmoyTestnet Chain = 80002
56+
PolygonZkEVMCardona Chain = 2442
57+
FraxtalTestnet Chain = 2522
58+
KromaSepolia Chain = 2358
59+
ScrollMainnet Chain = 534352
60+
ScrollSepoliaTestnet Chain = 534351
61+
SonicBlazeTestnet Chain = 57054
62+
LineaMainnet Chain = 59144
63+
LineaSepoliaTestnet Chain = 59141
64+
SophonMainnet Chain = 50104
65+
SophonSepoliaTestnet Chain = 531050104
66+
TaikoMainnet Chain = 167000
67+
TaikoHeklaTestnet Chain = 167009
68+
XaiMainnet Chain = 660279
69+
XaiSepoliaTestnet Chain = 37714555429
70+
BlastSepoliaTestnet Chain = 168587773
71+
OpSepoliaTestnet Chain = 11155420
72+
ArbitrumSepoliaTestnet Chain = 421614
73+
)
74+
75+
var chainNames = map[Chain]string{
76+
EthereumMainnet: "Ethereum Mainnet",
77+
OpMainnet: "OP Mainnet",
78+
SepoliaTestnet: "Sepolia Testnet",
79+
HoleskyTestnet: "Holesky Testnet",
80+
CronosMainnet: "Cronos Mainnet",
81+
ApeChainCurtisTestnet: "ApeChain Curtis Testnet",
82+
ApeChainMainnet: "ApeChain Mainnet",
83+
ArbitrumOneMainnet: "Arbitrum One Mainnet",
84+
ArbitrumNovaMainnet: "Arbitrum Nova Mainnet",
85+
CeloMainnet: "Celo Mainnet",
86+
AvalancheCChain: "Avalanche C-Chain",
87+
AvalancheFujiTestnet: "Avalanche Fuji Testnet",
88+
XDCMainnet: "XDC Mainnet",
89+
XDCApothemTestnet: "XDC Apothem Testnet",
90+
BNBSmartChainMainnet: "BNB Smart Chain Mainnet",
91+
BNBSmartChainTestnet: "BNB Smart Chain Testnet",
92+
Gnosis: "Gnosis",
93+
PolygonMainnet: "Polygon Mainnet",
94+
SonicMainnet: "Sonic Mainnet",
95+
BitTorrentChainMainnet: "BitTorrent Chain Mainnet",
96+
FantomOperaMainnet: "Fantom Opera Mainnet",
97+
FraxtalMainnet: "Fraxtal Mainnet",
98+
KromaMainnet: "Kroma Mainnet",
99+
zkSyncMainnet: "zkSync Mainnet",
100+
zkSyncSepoliaTestnet: "zkSync Sepolia Testnet",
101+
MoonbeamMainnet: "Moonbeam Mainnet",
102+
MoonbaseAlphaTestnet: "Moonbase Alpha Testnet",
103+
MoonriverMainnet: "Moonriver Mainnet",
104+
BitTorrentChainTestnet: "BitTorrent Chain Testnet",
105+
PolygonZkEVMMainnet: "Polygon zkEVM Mainnet",
106+
WEMIX30Mainnet: "WEMIX3.0 Mainnet",
107+
WEMIX30Testnet: "WEMIX3.0 Testnet",
108+
FantomTestnet: "Fantom Testnet",
109+
WorldMainnet: "World Mainnet",
110+
WorldSepoliaTestnet: "World Sepolia Testnet",
111+
MantleMainnet: "Mantle Mainnet",
112+
MantleSepoliaTestnet: "Mantle Sepolia Testnet",
113+
BaseMainnet: "Base Mainnet",
114+
BaseSepoliaTestnet: "Base Sepolia Testnet",
115+
BlastMainnet: "Blast Mainnet",
116+
CeloAlfajoresTestnet: "Celo Alfajores Testnet",
117+
PolygonAmoyTestnet: "Polygon Amoy Testnet",
118+
PolygonZkEVMCardona: "Polygon zkEVM Cardona Testnet",
119+
FraxtalTestnet: "Fraxtal Testnet",
120+
KromaSepolia: "Kroma Sepolia Testnet",
121+
ScrollMainnet: "Scroll Mainnet",
122+
ScrollSepoliaTestnet: "Scroll Sepolia Testnet",
123+
SonicBlazeTestnet: "Sonic Blaze Testnet",
124+
LineaMainnet: "Linea Mainnet",
125+
LineaSepoliaTestnet: "Linea Sepolia Testnet",
126+
SophonMainnet: "Sophon Mainnet",
127+
SophonSepoliaTestnet: "Sophon Sepolia Testnet",
128+
TaikoMainnet: "Taiko Mainnet",
129+
TaikoHeklaTestnet: "Taiko Hekla L2 Testnet",
130+
XaiMainnet: "Xai Mainnet",
131+
XaiSepoliaTestnet: "Xai Sepolia Testnet",
132+
BlastSepoliaTestnet: "Blast Sepolia Testnet",
133+
OpSepoliaTestnet: "OP Sepolia Testnet",
134+
ArbitrumSepoliaTestnet: "Arbitrum Sepolia Testnet",
135+
}
136+
137+
// String returns the name of the network
138+
func (n Chain) String() string {
139+
if name, ok := chainNames[n]; ok {
140+
return name
141+
}
142+
return "Unknown Network"
143+
}
144+
145+
// ChainID returns the chain ID as a uint64
146+
func (n Chain) ChainID() int {
147+
return int(n)
148+
}
149+
150+
// NewNetwork creates a Network from a name and chain ID
151+
func NewNetwork(name string, chainID int) Chain {
152+
network := Chain(chainID)
153+
154+
return network
155+
}
156+
157+
// GetByChainID returns a Network by its chain ID
158+
func GetByChainID(chainID int) (Chain, bool) {
159+
network := Chain(chainID)
160+
_, exists := chainNames[network]
161+
return network, exists
162+
}

internal/network/network.go

-34
This file was deleted.

pkg/client/client.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ import (
1414
"net/http"
1515
"net/http/httputil"
1616
"net/url"
17+
"strconv"
1718
"time"
1819

1920
"github.com/pkg/errors"
20-
"github.com/timcki/etherscan-api/internal/network"
21+
"github.com/timcki/etherscan-api/internal/chain"
2122
)
2223

2324
type (
@@ -27,7 +28,7 @@ type (
2728
conn *http.Client
2829
key string
2930
baseURL string
30-
chainID string
31+
chain chain.Chain
3132

3233
// Verbose when true, talks a lot
3334
Verbose bool
@@ -51,6 +52,8 @@ type (
5152
BaseURL string
5253
// When true, talks a lot
5354
Verbose bool
55+
// ChainID to be used
56+
Chain chain.Chain
5457
// HTTP Client to be used. Specifying this value will ignore the Timeout value set
5558
// Set your own timeout.
5659
Client *http.Client
@@ -67,11 +70,12 @@ type (
6770

6871
// NewClient initialize a new etherscan API client
6972
// please use pre-defined network value
70-
func NewClient(network network.Network, APIKey string) *Client {
73+
func NewClient(chain chain.Chain, APIKey string) *Client {
7174
return NewCustomized(Customization{
7275
Timeout: 30 * time.Second,
7376
Key: APIKey,
74-
BaseURL: fmt.Sprintf(`https://%s.etherscan.io/api`, network.SubDomain()),
77+
Chain: chain,
78+
BaseURL: `https://api.etherscan.io/v2/api`,
7579
})
7680
}
7781

@@ -87,6 +91,7 @@ func NewCustomized(config Customization) *Client {
8791
return &Client{
8892
conn: httpClient,
8993
key: config.Key,
94+
chain: config.Chain,
9095
baseURL: config.BaseURL,
9196
Verbose: config.Verbose,
9297
BeforeRequest: config.BeforeRequest,
@@ -237,7 +242,7 @@ func (c *Client) craftURL(module, action string, values url.Values) string {
237242
values.Add("module", module)
238243
values.Add("action", action)
239244
values.Add("apikey", c.key)
240-
values.Add("chainid", c.chainID)
245+
values.Add("chainid", strconv.Itoa(c.chain.ChainID()))
241246

242247
return fmt.Sprintf("%s?%s", c.baseURL, values.Encode())
243248
}

pkg/client/client_test.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,24 @@
88
package client
99

1010
import (
11+
"net/url"
1112
"testing"
1213

13-
"github.com/timcki/etherscan-api/internal/types"
14+
"github.com/stretchr/testify/assert"
15+
"github.com/timcki/etherscan-api/internal/chain"
1416
)
1517

1618
func TestClient_craftURL(t *testing.T) {
17-
c := NewClient(types.Ropsten, "abc123")
19+
c := NewClient(chain.EthereumMainnet, "abc123")
1820

19-
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`
20-
output := c.craftURL("testing", "craftURL", M{
21-
"one": 1,
22-
"two": "2",
23-
"three": []int{1, 2, 3},
21+
const expected = `https://api.etherscan.io/v2/api?action=craftURL&apikey=abc123&chainid=1&four=d&four=e&four=f&module=testing&one=1&three=1&three=2&three=3&two=2`
22+
23+
output := c.craftURL("testing", "craftURL", url.Values{
24+
"one": []string{"1"},
25+
"two": []string{"2"},
26+
"three": []string{"1", "2", "3"},
2427
"four": []string{"d", "e", "f"},
2528
})
2629

27-
if output != expected {
28-
t.Fatalf("output != expected, got %s, want %s", output, expected)
29-
}
30+
assert.Equal(t, expected, output)
3031
}

0 commit comments

Comments
 (0)