etherscan 0.2.2 → 0.2.4

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: 7e554929b14bae7f3a3f2c5da47aa57775a4e1088dd8f108cfe84f2c857ae088
4
+ data.tar.gz: 833280f97f3d0649c0f12be779b93d0da8ee347681d22f96942b7b040486fb9d
5
5
  SHA512:
6
- metadata.gz: 9acb8d8e72c16132e98d2c7e4b5444c4cfd5dd3badefd28adab1d1e533010be7607641e3ad10dd99c8770a950b660652eb2e86768db7d26bfda1f6dc9cb5b43a
7
- data.tar.gz: f69f8e8ae09066a8cab2b7a5f0d06ba8b090803cec69f1d0d8c280c20f996088547c28ad0b032780dd29e9760f29378900886b3258f5520c5cbf0919e3d4cead
6
+ metadata.gz: 7cb3a7981cf01a151b2e2ea262810f81096dc9ee3a7634b806118bbc947f628ddbab97446d474a7c1093b4f43f3b940f9958a847cd42178d85317c9ec80ffff4
7
+ data.tar.gz: 755c15c09278bd308ecd07c7359bed0677eea3d333b817fa2666e41788d9ce6e88c58446abeb0d4ffc81f478eafedfa1cbcf1f07f84563aba83515ffa8d5d33d
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.3'
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.4'
5
+ end
data/lib/etherscan.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'json'
2
+ require 'logger'
3
+ require 'net/http'
4
+ require 'etherscan/api'
5
+ require 'tronscan/api'
6
+
7
+ require 'active_support'
8
+ require 'active_support/core_ext/string'
9
+
10
+ module Etherscan
11
+ # https://chainid.network/chains.json
12
+ # key is the underscore(short name) of the chain in chains.json
13
+ # https://blockscan.com/
14
+ CHAINS = {
15
+ 'eth' => 'http://api.etherscan.io/api',
16
+ 'gor' => 'https://api-goerli.etherscan.io/api',
17
+ 'sep' => 'https://api-sepolia.etherscan.io/api',
18
+ 'arb1' => 'https://api.arbiscan.io/api',
19
+ 'arb_goerli' => 'https://api-goerli.arbiscan.io/api',
20
+ 'arb_sep' => 'https://api-sepolia.arbiscan.io/api',
21
+ 'bnb' => 'https://api.bscscan.com/api',
22
+ 'bnbt' => 'https://api-testnet.bscscan.com/api',
23
+ 'matic' => 'https://api.polygonscan.com/api',
24
+ 'maticmum' => 'https://api-testnet.polygonscan.com/api',
25
+ 'zkevm' => 'https://api-zkevm.polygonscan.com/api',
26
+ 'testnet_zk_evm_mango' => 'https://api-testnet-zkevm.polygonscan.com/api',
27
+ 'ftm' => 'https://api.ftmscan.com/api',
28
+ 'tftm' => 'https://api-testnet.ftmscan.com/api',
29
+ 'oeth' => 'https://api-optimistic.etherscan.io/api',
30
+ 'mbeam' => 'https://api-moonbeam.moonscan.io/api',
31
+ 'mriver' => 'https://api-moonriver.moonscan.io/api',
32
+ 'mbase' => 'https://api-moonbase.moonscan.io/api',
33
+ 'cronos' => 'https://api.cronoscan.com/api',
34
+ 'btt' => 'https://api.bttcscan.com/api',
35
+ 'tbtt' => 'https://api-testnet.bttcscan.com/api',
36
+ 'gnosis' => 'https://api.gnosisscan.io/api',
37
+ 'arb_nova' => 'https://api-nova.arbiscan.io/api',
38
+ # 'celo' => 'https://api.celoscan.io',
39
+ # 'alfa' => 'https://api-alfajores.celoscan.io',
40
+ 'linea' => 'https://api.lineascan.build/api',
41
+ 'linea_testnet' => 'https://api-testnet.lineascan.build/api',
42
+ 'base' => 'https://api.basescan.org/api',
43
+ 'basegor' => 'https://api-goerli.basescan.org/api',
44
+ 'wemix' => 'https://api.wemixscan.com/api',
45
+ 'scr' => 'https://api.scrollscan.com/api',
46
+ 'scr_sepolia' => 'https://api-sepolia.scrollscan.com/api',
47
+ 'obnb' => 'https://api-opbnb.bscscan.com/api'
48
+ }
49
+
50
+ # https://tronscan.org/#/developer/api
51
+ TRON_CHAINS = {
52
+ 'tron' => 'https://apilist.tronscanapi.com/api',
53
+ }
54
+
55
+ class << self
56
+ attr_accessor :logger
57
+
58
+ def api(chain_short_name, api_key = nil)
59
+ url = CHAINS[chain_short_name]
60
+ url = CHAINS[chain_short_name.underscore] if url.nil?
61
+
62
+ tron_url = TRON_CHAINS[chain_short_name]
63
+ tron_url = TRON_CHAINS[chain_short_name.underscore] if url.nil?
64
+
65
+ raise "Chain `#{chain_short_name}` is not supported. Only Etherscan [#{CHAINS.keys}] & Tronscan [#{TRON_CHAINS.keys}] are supported." if url.nil? && tron_url.nil?
66
+
67
+ return Etherscan::Api.new(url, api_key) if url
68
+ return Tronscan::Api.new(tron_url, api_key) if tron_url
69
+ end
70
+
71
+ # for example: Etherscan.eth('your_api_key')
72
+ # just a wrap of Etherscan.api('eth', 'your_api_key')
73
+ def method_missing(method, *args)
74
+ chain_short_name = method.to_s.downcase
75
+ api_key = args[0]
76
+
77
+ api(chain_short_name, api_key)
78
+ end
79
+
80
+ def respond_to_missing?(*_args)
81
+ true
82
+ end
83
+
84
+ def test_all
85
+ CHAINS.reverse_each do |chain_short_name, url|
86
+ api = Etherscan::Api.new(url)
87
+ address = '0x0000000000000000000000000000000000000000'
88
+ balance = api.account_balance(address: address)
89
+ puts "#{chain_short_name} 0x0 balance: #{balance}"
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ Etherscan.logger = Logger.new($stdout)
96
+ Etherscan.logger.level = Logger::INFO
@@ -0,0 +1,56 @@
1
+ module Tronscan
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 = nil, params = {})
9
+ action_path = action.nil? ? '' : "/#{action}"
10
+
11
+ params = params.reject { |_k, v| v.nil? } # filter out nil values
12
+ params_query = params.keys.map { |key| "#{key}=#{params[key]}" }.join('&').strip
13
+ params_query = '' if params_query.empty?
14
+
15
+ uri = URI "#{@url}/#{module_name}#{action_path}?#{params_query}"
16
+ Etherscan.logger.debug "Req: #{uri}"
17
+
18
+ http = Net::HTTP.new(uri.host, uri.port)
19
+ http.use_ssl = (uri.scheme == 'https')
20
+
21
+ request = Net::HTTP::Get.new(uri.request_uri)
22
+ request['TRON-PRO-API-KEY'] = @api_key unless @api_key.nil?
23
+
24
+ resp = http.request(request).body
25
+ # Etherscan.logger.debug "Rsp: #{resp}"
26
+ resp = JSON.parse(resp)
27
+
28
+ # raise resp['result'] if resp['status'] == '0'
29
+
30
+ resp
31
+ rescue StandardError => e
32
+ puts e.message
33
+ puts e.backtrace.inspect
34
+ end
35
+
36
+ def respond_to_missing?(*_args)
37
+ true
38
+ end
39
+
40
+ def method_missing(method, *args)
41
+ module_name, action = method.to_s.split('_')
42
+ request(module_name, action, args[0] || {})
43
+ end
44
+
45
+ # def contract_getcontractcreation(contractaddresses:)
46
+ # if contractaddresses.is_a? Array
47
+ # request('contract', 'getcontractcreation', contractaddresses: contractaddresses.join(','))
48
+ # elsif contractaddresses.is_a? String
49
+ # request('contract', 'getcontractcreation', contractaddresses: contractaddresses)
50
+ # else
51
+ # raise 'contractaddresses must be an array or a string'
52
+ # end
53
+ # end
54
+
55
+ end
56
+ end
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.4
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,19 @@ 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
40
+ - lib/tronscan/api.rb
34
41
  homepage: https://github.com/wuminzhe/etherscan
35
42
  licenses:
36
43
  - MIT
37
- metadata: {}
44
+ metadata:
45
+ homepage_uri: https://github.com/wuminzhe/etherscan
38
46
  post_install_message:
39
47
  rdoc_options: []
40
48
  require_paths:
@@ -43,7 +51,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
43
51
  requirements:
44
52
  - - ">="
45
53
  - !ruby/object:Gem::Version
46
- version: '0'
54
+ version: 2.6.0
47
55
  required_rubygems_version: !ruby/object:Gem::Requirement
48
56
  requirements:
49
57
  - - ">="