etherscan 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +15 -0
- data/Rakefile +12 -0
- data/lib/etherscan/api.rb +191 -0
- data/lib/etherscan/version.rb +5 -0
- data/lib/etherscan.rb +85 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e12314597d854f9d264641830082a829f3f99f40a49cbfa1283cac848527416
|
4
|
+
data.tar.gz: 825813db2b4c6debd2cf9c96a59ebbe8f5aacf13cdb84f5cbd9ecd85f44fd814
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ddbff16ed0c4d471b68bdccd621b1bb6c0f7a70209e93aa5d340f131f0f2e194cb58410b8cb2e50106f91f5a8b68ea75e104f1981a4ef49f53ec1d39a627acc
|
7
|
+
data.tar.gz: 368ab98f97fff3c59808fe6433029bc41b2dd59e60fb0776cfc8d8b863c5461bbec194040371ca9b4f584fcdb249b755903bf48163dc1527c5eb74ab6dda5e0d
|
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
module Etherscan
|
2
|
+
class Api
|
3
|
+
def initialize(url, api_key = nil)
|
4
|
+
@url = url
|
5
|
+
@api_key = api_key
|
6
|
+
end
|
7
|
+
|
8
|
+
def request(module_name, action, params = {})
|
9
|
+
params = params.reject { |_k, v| v.nil? } # filter out nil values
|
10
|
+
params_query = params.keys.map { |key| "#{key}=#{params[key]}" }.join('&').strip
|
11
|
+
params_query = if params_query.empty?
|
12
|
+
''
|
13
|
+
else
|
14
|
+
"&#{params_query}"
|
15
|
+
end
|
16
|
+
|
17
|
+
api_key_query = @api_key.nil? ? '' : "&apikey=#{@api_key}"
|
18
|
+
|
19
|
+
uri = URI "#{@url}?module=#{module_name}&action=#{action}#{params_query}#{api_key_query}"
|
20
|
+
Etherscan.logger.debug "Req: #{uri}"
|
21
|
+
resp = Net::HTTP.get(uri)
|
22
|
+
Etherscan.logger.debug "Rsp: #{resp}"
|
23
|
+
resp = JSON.parse(resp)
|
24
|
+
|
25
|
+
raise resp['result'] if resp['status'] == '0'
|
26
|
+
|
27
|
+
resp['result']
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to_missing?(*_args)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method, *args)
|
35
|
+
module_name, action = method.to_s.split('_')
|
36
|
+
request(module_name, action, args[0])
|
37
|
+
end
|
38
|
+
|
39
|
+
#########################################
|
40
|
+
# Accounts
|
41
|
+
#########################################
|
42
|
+
def account_balance(address:, tag: 'latest')
|
43
|
+
request('account', 'balance', address: address, tag: tag)
|
44
|
+
end
|
45
|
+
|
46
|
+
def account_balancemulti(addresses:, tag: 'latest')
|
47
|
+
if addresses.is_a? Array
|
48
|
+
request('account', 'balancemulti', address: addresses.join(','), tag: tag)
|
49
|
+
elsif addresses.is_a? String
|
50
|
+
request('account', 'balancemulti', address: addresses, tag: tag)
|
51
|
+
else
|
52
|
+
raise 'addresses must be an array or a string'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def account_txlist(address:, startblock: 0, endblock: 999_999_999, sort: 'asc', page: 1, offset: 1000)
|
57
|
+
request('account', 'txlist', address: address,
|
58
|
+
startblock: startblock,
|
59
|
+
endblock: endblock,
|
60
|
+
sort: sort,
|
61
|
+
page: page,
|
62
|
+
offset: offset)
|
63
|
+
end
|
64
|
+
|
65
|
+
def account_txlistinternal(address: nil,
|
66
|
+
startblock: 0, endblock: 999_999_999, sort: 'asc', page: 1, offset: 1000)
|
67
|
+
params = {
|
68
|
+
address: address,
|
69
|
+
startblock: startblock, endblock: endblock, sort: sort, page: page, offset: offset
|
70
|
+
}
|
71
|
+
request('account', 'txlistinternal', params)
|
72
|
+
end
|
73
|
+
|
74
|
+
def account_txlistinternal_by_txhash(txhash:)
|
75
|
+
# https://docs.etherscan.io/api-endpoints/accounts#get-internal-transactions-by-transaction-hash
|
76
|
+
request('account', 'txlistinternal', txhash: txhash)
|
77
|
+
end
|
78
|
+
|
79
|
+
def account_tokentx(address:, contractaddress:,
|
80
|
+
startblock: 0, endblock: 999_999_999, sort: 'asc', page: 1, offset: 1000)
|
81
|
+
params = {
|
82
|
+
address: address, contractaddress: contractaddress,
|
83
|
+
startblock: startblock, endblock: endblock, sort: sort, page: page, offset: offset
|
84
|
+
}
|
85
|
+
request('account', 'tokentx', params)
|
86
|
+
end
|
87
|
+
|
88
|
+
def account_tokennfttx(address:, contractaddress:,
|
89
|
+
startblock: 0, endblock: 999_999_999, sort: 'asc', page: 1, offset: 1000)
|
90
|
+
params = {
|
91
|
+
address: address, contractaddress: contractaddress,
|
92
|
+
startblock: startblock, endblock: endblock, sort: sort, page: page, offset: offset
|
93
|
+
}
|
94
|
+
request('account', 'tokennfttx', params)
|
95
|
+
end
|
96
|
+
|
97
|
+
#########################################
|
98
|
+
# Contracts
|
99
|
+
#########################################
|
100
|
+
def contract_getabi(address:)
|
101
|
+
request('contract', 'getabi', address: address)
|
102
|
+
end
|
103
|
+
|
104
|
+
def contract_getsourcecode(address:)
|
105
|
+
request('contract', 'getsourcecode', address: address)
|
106
|
+
end
|
107
|
+
|
108
|
+
def contract_getcontractcreation(contractaddresses:)
|
109
|
+
if contractaddresses.is_a? Array
|
110
|
+
request('contract', 'getcontractcreation', contractaddresses: contractaddresses.join(','))
|
111
|
+
elsif contractaddresses.is_a? String
|
112
|
+
request('contract', 'getcontractcreation', contractaddresses: contractaddresses)
|
113
|
+
else
|
114
|
+
raise 'contractaddresses must be an array or a string'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#########################################
|
119
|
+
# Transactions
|
120
|
+
#########################################
|
121
|
+
def transaction_getstatus(txhash:)
|
122
|
+
request('transaction', 'getstatus', txhash: txhash)
|
123
|
+
end
|
124
|
+
|
125
|
+
def transaction_gettxreceiptstatus(txhash:)
|
126
|
+
request('transaction', 'gettxreceiptstatus', txhash: txhash)
|
127
|
+
end
|
128
|
+
|
129
|
+
#########################################
|
130
|
+
# Blocks
|
131
|
+
#########################################
|
132
|
+
def block_getblockreward(blockno:)
|
133
|
+
request('block', 'getblockreward', blockno: blockno)
|
134
|
+
end
|
135
|
+
|
136
|
+
def block_getblockcount(blockno:)
|
137
|
+
request('block', 'getblockcount', blockno: blockno)
|
138
|
+
end
|
139
|
+
|
140
|
+
def block_getblocknobytime(timestamp:, closest: 'before')
|
141
|
+
request('block', 'getblocknobytime', timestamp: timestamp, closest: closest)
|
142
|
+
end
|
143
|
+
|
144
|
+
#########################################
|
145
|
+
# Logs
|
146
|
+
#########################################
|
147
|
+
# fromBlock, toBlock, address
|
148
|
+
# topic0, topic1, topic2, topic3 (32 Bytes per topic)
|
149
|
+
# topic0_1_opr (and|or between topic0 & topic1),
|
150
|
+
# topic1_2_opr (and|or between topic1 & topic2),
|
151
|
+
# topic2_3_opr (and|or between topic2 & topic3),
|
152
|
+
# topic0_2_opr (and|or between topic0 & topic2),
|
153
|
+
# topic0_3_opr (and|or between topic0 & topic3),
|
154
|
+
# topic1_3_opr (and|or between topic1 & topic3)
|
155
|
+
def logs_getLogs(from_block: 0, to_block: 'latest', address: nil,
|
156
|
+
topic0: nil, topic1: nil, topic2: nil, topic3: nil,
|
157
|
+
topic0_1_opr: nil, topic1_2_opr: nil, topic2_3_opr: nil,
|
158
|
+
topic0_2_opr: nil, topic0_3_opr: nil, topic1_3_opr: nil)
|
159
|
+
params = {
|
160
|
+
fromBlock: from_block, toBlock: to_block, address: address,
|
161
|
+
topic0: topic0, topic1: topic1, topic2: topic2, topic3: topic3,
|
162
|
+
topic0_1_opr: topic0_1_opr, topic1_2_opr: topic1_2_opr, topic2_3_opr: topic2_3_opr,
|
163
|
+
topic0_2_opr: topic0_2_opr, topic0_3_opr: topic0_3_opr, topic1_3_opr: topic1_3_opr
|
164
|
+
}
|
165
|
+
|
166
|
+
request('logs', 'getLogs', params)
|
167
|
+
end
|
168
|
+
|
169
|
+
#########################################
|
170
|
+
# Tokens
|
171
|
+
#########################################
|
172
|
+
def stats_tokensupply(contractaddress:)
|
173
|
+
request('stats', 'tokensupply', contractaddress: contractaddress)
|
174
|
+
end
|
175
|
+
|
176
|
+
def account_tokenbalance(contractaddress:, address:, tag: 'latest')
|
177
|
+
request('account', 'tokenbalance', contractaddress: contractaddress, address: address, tag: tag)
|
178
|
+
end
|
179
|
+
|
180
|
+
#########################################
|
181
|
+
# Stats
|
182
|
+
#########################################
|
183
|
+
def stats_ethsupply
|
184
|
+
request('stats', 'ethsupply')
|
185
|
+
end
|
186
|
+
|
187
|
+
def stats_ethprice
|
188
|
+
request('stats', 'ethprice')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
data/lib/etherscan.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'logger'
|
3
|
+
require 'net/http'
|
4
|
+
require 'etherscan/api'
|
5
|
+
|
6
|
+
require 'active_support'
|
7
|
+
require 'active_support/core_ext/string'
|
8
|
+
|
9
|
+
module Etherscan
|
10
|
+
# https://chainid.network/chains.json
|
11
|
+
# key is the underscore(short name) of the chain in chains.json
|
12
|
+
# https://blockscan.com/
|
13
|
+
CHAINS = {
|
14
|
+
'eth' => 'http://api.etherscan.io/api',
|
15
|
+
'gor' => 'https://api-goerli.etherscan.io/api',
|
16
|
+
'sep' => 'https://api-sepolia.etherscan.io/api',
|
17
|
+
'arb1' => 'https://api.arbiscan.io/api',
|
18
|
+
'arb_goerli' => 'https://api-goerli.arbiscan.io/api',
|
19
|
+
'arb_sep' => 'https://api-sepolia.arbiscan.io/api',
|
20
|
+
'bnb' => 'https://api.bscscan.com/api',
|
21
|
+
'bnbt' => 'https://api-testnet.bscscan.com/api',
|
22
|
+
'matic' => 'https://api.polygonscan.com/api',
|
23
|
+
'maticmum' => 'https://api-testnet.polygonscan.com/api',
|
24
|
+
'zkevm' => 'https://api-zkevm.polygonscan.com/api',
|
25
|
+
'testnet_zk_evm_mango' => 'https://api-testnet-zkevm.polygonscan.com/api',
|
26
|
+
'ftm' => 'https://api.ftmscan.com/api',
|
27
|
+
'tftm' => 'https://api-testnet.ftmscan.com/api',
|
28
|
+
'oeth' => 'https://api-optimistic.etherscan.io/api',
|
29
|
+
'mbeam' => 'https://api-moonbeam.moonscan.io/api',
|
30
|
+
'mriver' => 'https://api-moonriver.moonscan.io/api',
|
31
|
+
'mbase' => 'https://api-moonbase.moonscan.io/api',
|
32
|
+
'cronos' => 'https://api.cronoscan.com/api',
|
33
|
+
'btt' => 'https://api.bttcscan.com/api',
|
34
|
+
'tbtt' => 'https://api-testnet.bttcscan.com/api',
|
35
|
+
'gnosis' => 'https://api.gnosisscan.io/api',
|
36
|
+
'arb_nova' => 'https://api-nova.arbiscan.io/api',
|
37
|
+
# 'celo' => 'https://api.celoscan.io',
|
38
|
+
# 'alfa' => 'https://api-alfajores.celoscan.io',
|
39
|
+
'linea' => 'https://api.lineascan.build/api',
|
40
|
+
'linea_testnet' => 'https://api-testnet.lineascan.build/api',
|
41
|
+
'base' => 'https://api.basescan.org/api',
|
42
|
+
'basegor' => 'https://api-goerli.basescan.org/api',
|
43
|
+
'wemix' => 'https://api.wemixscan.com/api',
|
44
|
+
'scr' => 'https://api.scrollscan.com/api',
|
45
|
+
'scr_sepolia' => 'https://api-sepolia.scrollscan.com/api',
|
46
|
+
'obnb' => 'https://api-opbnb.bscscan.com/api'
|
47
|
+
}
|
48
|
+
|
49
|
+
class << self
|
50
|
+
attr_accessor :logger
|
51
|
+
|
52
|
+
def api(chain_short_name, api_key = nil)
|
53
|
+
url = CHAINS[chain_short_name]
|
54
|
+
url = CHAINS[chain_short_name.underscore] if url.nil?
|
55
|
+
raise "Chain `#{chain_short_name}` is not supported. Only #{CHAINS.keys} are supported." if url.nil?
|
56
|
+
|
57
|
+
Etherscan::Api.new(url, api_key)
|
58
|
+
end
|
59
|
+
|
60
|
+
# for example: Etherscan.eth('your_api_key')
|
61
|
+
# just a wrap of Etherscan.api('eth', 'your_api_key')
|
62
|
+
def method_missing(method, *args)
|
63
|
+
chain_short_name = method.to_s.downcase
|
64
|
+
api_key = args[0]
|
65
|
+
|
66
|
+
api(chain_short_name, api_key)
|
67
|
+
end
|
68
|
+
|
69
|
+
def respond_to_missing?(*_args)
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_all
|
74
|
+
CHAINS.reverse_each do |chain_short_name, url|
|
75
|
+
api = Etherscan::Api.new(url)
|
76
|
+
address = '0x0000000000000000000000000000000000000000'
|
77
|
+
balance = api.account_balance(address: address)
|
78
|
+
puts "#{chain_short_name} 0x0 balance: #{balance}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Etherscan.logger = Logger.new($stdout)
|
85
|
+
Etherscan.logger.level = Logger::INFO
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etherscan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aki Wu
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,11 +30,18 @@ email:
|
|
30
30
|
executables: []
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
|
-
files:
|
33
|
+
files:
|
34
|
+
- Gemfile
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/etherscan.rb
|
38
|
+
- lib/etherscan/api.rb
|
39
|
+
- lib/etherscan/version.rb
|
34
40
|
homepage: https://github.com/wuminzhe/etherscan
|
35
41
|
licenses:
|
36
42
|
- MIT
|
37
|
-
metadata:
|
43
|
+
metadata:
|
44
|
+
homepage_uri: https://github.com/wuminzhe/etherscan
|
38
45
|
post_install_message:
|
39
46
|
rdoc_options: []
|
40
47
|
require_paths:
|
@@ -43,7 +50,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
50
|
requirements:
|
44
51
|
- - ">="
|
45
52
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
53
|
+
version: 2.6.0
|
47
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
55
|
requirements:
|
49
56
|
- - ">="
|