etherscan 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02fe5c2f28f3931d545116485b98ad1464be2aafba56ef96b400e4006e140dc1
4
- data.tar.gz: a7acfe273d5b76d31de3dcfe7766e538f90043d24906035ae525d33e89bd0a35
3
+ metadata.gz: 0e12314597d854f9d264641830082a829f3f99f40a49cbfa1283cac848527416
4
+ data.tar.gz: 825813db2b4c6debd2cf9c96a59ebbe8f5aacf13cdb84f5cbd9ecd85f44fd814
5
5
  SHA512:
6
- metadata.gz: b04843c6de3e52d3e2ba7de86a18ef192fae439c7cee5797b0ae41ccda5240c7114b13b7df2f3c6bc456f59003d0a945ce0170abe51efd876231bcffa9d44574
7
- data.tar.gz: e7773c1740d4e85ca3ba881cae36c190fc9b29afdb3f2a5d4ecf60ed8fa1ae318008fbfbb4264f470bcc33cb5e22e4101e0caecf27594c3afb473bbb77b47166
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.1
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
  - - ">="