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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd1daab223f53155af170d28c0762aca7bfa166c863a04f9bc63ce4aaa238f36
4
- data.tar.gz: 0be29dadd9daa63af08cf6aa31aad30964062cfd2f70dc93e817588eff72a4a6
3
+ metadata.gz: 0e12314597d854f9d264641830082a829f3f99f40a49cbfa1283cac848527416
4
+ data.tar.gz: 825813db2b4c6debd2cf9c96a59ebbe8f5aacf13cdb84f5cbd9ecd85f44fd814
5
5
  SHA512:
6
- metadata.gz: 9acb8d8e72c16132e98d2c7e4b5444c4cfd5dd3badefd28adab1d1e533010be7607641e3ad10dd99c8770a950b660652eb2e86768db7d26bfda1f6dc9cb5b43a
7
- data.tar.gz: f69f8e8ae09066a8cab2b7a5f0d06ba8b090803cec69f1d0d8c280c20f996088547c28ad0b032780dd29e9760f29378900886b3258f5520c5cbf0919e3d4cead
6
+ metadata.gz: 3ddbff16ed0c4d471b68bdccd621b1bb6c0f7a70209e93aa5d340f131f0f2e194cb58410b8cb2e50106f91f5a8b68ea75e104f1981a4ef49f53ec1d39a627acc
7
+ data.tar.gz: 368ab98f97fff3c59808fe6433029bc41b2dd59e60fb0776cfc8d8b863c5461bbec194040371ca9b4f584fcdb249b755903bf48163dc1527c5eb74ab6dda5e0d
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+
2
+ source 'https://rubygems.org'
3
+
4
+ # Specify your gem's dependencies in testgem.gemspec
5
+ gemspec
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # etherscan
2
+
3
+ Access to the API of etherscan.io and the compatible APIs.
4
+
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ gem 'etherscan', '~> 0.2.0'
10
+ ```
11
+
12
+ ```bash
13
+ gem install etherscan
14
+ ```
15
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Etherscan
4
+ VERSION = "0.2.3"
5
+ 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.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-03 00:00:00.000000000 Z
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: '0'
53
+ version: 2.6.0
47
54
  required_rubygems_version: !ruby/object:Gem::Requirement
48
55
  requirements:
49
56
  - - ">="