etht 0.1.4 → 0.1.5
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/.rubocop.yml +12 -0
- data/Gemfile.lock +2 -2
- data/README.md +6 -16
- data/etht.gemspec +13 -12
- data/lib/etht.rb +20 -20
- data/lib/etht/bigquery.rb +117 -67
- data/lib/etht/carteiras.rb +126 -0
- data/lib/etht/etherscan.rb +94 -142
- data/lib/etht/formatar.rb +90 -0
- data/lib/etht/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5a76fade4364cb9ecc118fcfae181f13d84fe3e39ba85a6078e8974ab403652
|
4
|
+
data.tar.gz: 269e68983b59463ec5aaeb3e0179b28f79f6a43135e2401269c467e0d90a74c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f446bfb04b77cf6d7cfc366f12b564a6385837484d388121341dbb4fa26d36e07f30d3883871d8e7bbbe1a353a3ed838fa95a2aa5b1d15026a8fc099f6e8389
|
7
|
+
data.tar.gz: 65936552f2176a0706e417579fa6747a8c7fcc9937a23d1f83fca413c2a964c6e37ee58cfd1dd086e964367d723e0ce828a130e182043129c4cc84117227ae75
|
data/.rubocop.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
etht (0.1.
|
4
|
+
etht (0.1.5)
|
5
5
|
faraday
|
6
6
|
google-cloud-bigquery
|
7
7
|
json
|
@@ -47,7 +47,7 @@ GEM
|
|
47
47
|
signet (~> 0.14)
|
48
48
|
httpclient (2.8.3)
|
49
49
|
json (2.3.1)
|
50
|
-
jwt (2.2.
|
50
|
+
jwt (2.2.2)
|
51
51
|
memoist (0.16.2)
|
52
52
|
mini_mime (1.0.2)
|
53
53
|
multi_json (1.15.0)
|
data/README.md
CHANGED
@@ -1,26 +1,16 @@
|
|
1
1
|
# Etht
|
2
2
|
|
3
|
-
Arquiva eth-transactions no bigquery. Pode
|
3
|
+
Arquiva eth-transactions no bigquery. Pode ajustar dias para reposicionamento temporal.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
Add this line to your application's Gemfile:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
gem 'etht'
|
11
|
-
```
|
12
|
-
|
13
|
-
And then execute:
|
14
|
-
|
15
|
-
$ bundle install
|
16
|
-
|
17
|
-
Or install it yourself as:
|
18
|
-
|
19
7
|
$ gem install etht
|
20
8
|
|
21
9
|
## Usage
|
22
10
|
|
23
|
-
|
11
|
+
$ etht help [COMMAND] # Describe available commands or one specific command
|
12
|
+
$ etht show # mostra carteiras e dados bigquery & etherscan
|
13
|
+
$ etht work # carrega dados novos do etherscan no bigquery
|
24
14
|
|
25
15
|
## Development
|
26
16
|
|
@@ -30,7 +20,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
30
20
|
|
31
21
|
## Contributing
|
32
22
|
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/etht. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/
|
23
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/etht. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/hernanirvaz/etht/blob/master/CODE_OF_CONDUCT.md).
|
34
24
|
|
35
25
|
|
36
26
|
## License
|
@@ -39,4 +29,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
39
29
|
|
40
30
|
## Code of Conduct
|
41
31
|
|
42
|
-
Everyone interacting in the Etht project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
32
|
+
Everyone interacting in the Etht project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/hernanirvaz/etht/blob/master/CODE_OF_CONDUCT.md).
|
data/etht.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative('lib/etht/version')
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'etht'
|
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ['hernanirvaz@gmail.com']
|
10
10
|
|
11
11
|
spec.summary = 'Arquiva eth-transactions no bigquery.'
|
12
|
-
spec.description = spec.summary
|
12
|
+
spec.description = "#{spec.summary} Pode ajustar dias para reposicionamento temporal."
|
13
13
|
|
14
14
|
spec.homepage = 'https://github.com/hernanirvaz/etht'
|
15
15
|
spec.license = 'MIT'
|
@@ -20,19 +20,20 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
# Specify which files should be added to the gem when it is released.
|
22
22
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
-
spec.files =
|
24
|
-
|
25
|
-
|
23
|
+
spec.files =
|
24
|
+
Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
end
|
26
27
|
spec.bindir = 'exe'
|
27
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
29
|
spec.require_paths = ['lib']
|
29
30
|
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
31
|
+
spec.add_development_dependency('bundler')
|
32
|
+
spec.add_development_dependency('rake')
|
32
33
|
|
33
|
-
spec.add_dependency
|
34
|
-
spec.add_dependency
|
35
|
-
spec.add_dependency
|
36
|
-
spec.add_dependency
|
37
|
-
spec.add_dependency
|
34
|
+
spec.add_dependency('faraday')
|
35
|
+
spec.add_dependency('google-cloud-bigquery')
|
36
|
+
spec.add_dependency('json')
|
37
|
+
spec.add_dependency('thor')
|
38
|
+
spec.add_dependency('yard')
|
38
39
|
end
|
data/lib/etht.rb
CHANGED
@@ -1,36 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require('thor')
|
4
|
+
require('etht/bigquery')
|
5
|
+
require('etht/carteiras')
|
6
|
+
require('etht/etherscan')
|
7
|
+
require('etht/formatar')
|
8
|
+
require('etht/version')
|
7
9
|
|
8
10
|
# @author Hernani Rodrigues Vaz
|
9
11
|
module Etht
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def initialize(message)
|
16
|
-
super(message)
|
12
|
+
# classe para erros desta gem
|
13
|
+
class Erro < StandardError
|
14
|
+
# @return [StandardError] personalizacao dos erros
|
15
|
+
def initialize(msg)
|
16
|
+
super(msg)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
#
|
20
|
+
# classe para carregar/mostrar dados comuns bigquery & etherscan
|
21
21
|
class CLI < Thor
|
22
|
-
desc 'work', 'carrega
|
23
|
-
option :
|
24
|
-
|
25
|
-
# processa etherscan
|
22
|
+
desc 'work', 'carrega transacoes novas no bigquery'
|
23
|
+
option :h, type: :hash, default: {}, desc: 'configuracao ajuste reposicionamento temporal'
|
24
|
+
# carrega transacoes novas no bigquery
|
26
25
|
def work
|
27
|
-
Bigquery.new(
|
26
|
+
Bigquery.new(options).processa
|
28
27
|
end
|
29
28
|
|
30
|
-
desc 'show', 'mostra dados
|
31
|
-
|
29
|
+
desc 'show', 'mostra carteiras e dados bigquery & etherscan'
|
30
|
+
option :v, type: :boolean, default: false, desc: 'mostra dados transacoes normais & token'
|
31
|
+
# mostra carteiras e dados bigquery & etherscan
|
32
32
|
def show
|
33
|
-
Bigquery.new.
|
33
|
+
Bigquery.new(options).carteiras.mostra
|
34
34
|
end
|
35
35
|
|
36
36
|
default_task :show
|
data/lib/etht/bigquery.rb
CHANGED
@@ -1,103 +1,153 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require('google/cloud/bigquery')
|
4
|
+
require('bigdecimal/util')
|
4
5
|
|
5
6
|
# @author Hernani Rodrigues Vaz
|
6
7
|
module Etht
|
7
|
-
|
8
|
-
DI = '%Y-%m-%d %H:%M:%S'
|
8
|
+
BD = 'hernanirvaz.coins'
|
9
9
|
|
10
|
-
#
|
10
|
+
# classe para processar etherscan & bigquery
|
11
11
|
class Bigquery
|
12
12
|
# @return [Google::Cloud::Bigquery] API bigquery
|
13
|
-
attr_reader :
|
14
|
-
# @return [Etherscan::Api] API etherscan
|
15
|
-
attr_reader :apies
|
16
|
-
|
17
|
-
# @return [Array] row folha calculo em processamento
|
18
|
-
attr_reader :row
|
13
|
+
attr_reader :api
|
19
14
|
# @return [Google::Cloud::Bigquery::QueryJob] job bigquery
|
20
15
|
attr_reader :job
|
21
|
-
# @return
|
22
|
-
attr_reader :
|
23
|
-
|
24
|
-
# @param [
|
25
|
-
# @
|
26
|
-
# @option
|
27
|
-
# @
|
28
|
-
|
29
|
-
# @return [Bigquery] acesso folhas calculo bloks.io & correspondente bigquery dataset
|
30
|
-
def initialize(ops = { e: false, m: false, i: false })
|
16
|
+
# @return [Thor::CoreExt::HashWithIndifferentAccess] opcoes trabalho
|
17
|
+
attr_reader :ops
|
18
|
+
|
19
|
+
# @param [Thor::CoreExt::HashWithIndifferentAccess] pop opcoes trabalho
|
20
|
+
# @option pop [Hash] :h ({}) configuracao ajuste reposicionamento temporal
|
21
|
+
# @option pop [Boolean] :v (false) mostra dados transacoes normais & token?
|
22
|
+
# @return [Bigquery] API bigquery & API etherscan
|
23
|
+
def initialize(pop)
|
31
24
|
# usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials
|
32
25
|
# @see https://cloud.google.com/bigquery/docs/authentication/getting-started
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@linha = ops
|
26
|
+
@api = Google::Cloud::Bigquery.new
|
27
|
+
@ops = pop
|
36
28
|
end
|
37
29
|
|
38
|
-
|
39
|
-
|
40
|
-
|
30
|
+
# @return [Carteiras] API etherscan - processar transacoes normais e tokens
|
31
|
+
def carteiras
|
32
|
+
@carteiras ||= Carteiras.new(
|
33
|
+
{
|
34
|
+
wb: sql("select * from #{BD}.walletEth order by 2")
|
35
|
+
.map { |e| { id: e[:id], ax: e[:address], sl: e[:saldo].to_d.round(10) } },
|
36
|
+
nt: sql("select blocknumber,iax from #{BD}.ethtx"),
|
37
|
+
nk: sql("select blocknumber,iax from #{BD}.ethkx")
|
38
|
+
},
|
39
|
+
ops
|
40
|
+
)
|
41
41
|
end
|
42
42
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def job_bigquery?(sql)
|
48
|
-
@job = apibq.query_job(sql)
|
49
|
-
@job.wait_until_done!
|
50
|
-
puts @job.error['message'] if @job.failed?
|
51
|
-
@job.failed?
|
43
|
+
# insere transacoes novas nas tabelas etht, ethk
|
44
|
+
def processa
|
45
|
+
puts(format("%<n>2i LINHAS INSERIDAS #{BD}.etht", n: carteiras.novaest.count.positive? ? dml(etht_ins) : 0))
|
46
|
+
puts(format("%<n>2i LINHAS INSERIDAS #{BD}.ethk", n: carteiras.novaesk.count.positive? ? dml(ethk_ins) : 0))
|
52
47
|
end
|
53
48
|
|
54
|
-
#
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
job_bigquery?(sql) ? 0 : job.num_dml_affected_rows
|
49
|
+
# @return [String] comando insert SQL formatado bigquery.etht
|
50
|
+
def etht_ins
|
51
|
+
"insert #{BD}.etht(blocknumber,timestamp,txhash,nonce,blockhash,transactionindex,axfrom,axto,value,gas," \
|
52
|
+
'gasprice,iserror,txreceipt_status,input,contractaddress,cumulativegasused,gasused,confirmations,dias' \
|
53
|
+
") VALUES(#{carteiras.novaest.map { |e| etht_val1(e) }.join(',')})"
|
60
54
|
end
|
61
55
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
56
|
+
# @return [String] valores formatados bigquery.etht parte1
|
57
|
+
def etht_val1(hes)
|
58
|
+
"#{Integer(hes['blockNumber'])}," \
|
59
|
+
"#{Integer(hes['timeStamp'])}," \
|
60
|
+
"'#{hes['hash']}'," \
|
61
|
+
"#{Integer(hes['nonce'])}," \
|
62
|
+
"'#{hes['blockHash']}'," \
|
63
|
+
"#{Integer(hes['transactionIndex'])}," \
|
64
|
+
"'#{hes['from']}'," \
|
65
|
+
"'#{hes['to']}'," \
|
66
|
+
"#{etht_val2(hes)}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String] valores formatados bigquery.etht parte2
|
70
|
+
def etht_val2(hes)
|
71
|
+
"cast('#{hes['value']}' as numeric)," \
|
72
|
+
"cast('#{hes['gas']}' as numeric)," \
|
73
|
+
"cast('#{hes['gasPrice']}' as numeric)," \
|
74
|
+
"#{Integer(hes['isError'])}," \
|
75
|
+
"#{hes['txreceipt_status'].length.zero? ? 'null' : hes['txreceipt_status']}," \
|
76
|
+
"#{hes['input'].length.zero? ? 'null' : "'#{hes['input']}'"}," \
|
77
|
+
"#{etht_val3(hes)}"
|
69
78
|
end
|
70
79
|
|
71
|
-
# @return [String]
|
72
|
-
def
|
73
|
-
"
|
80
|
+
# @return [String] valores formatados bigquery.etht parte3
|
81
|
+
def etht_val3(hes)
|
82
|
+
"#{hes['contractAddress'].length.zero? ? 'null' : "'#{hes['contractAddress']}'"}," \
|
83
|
+
"0,cast('#{hes['gasUsed']}' as numeric),0," \
|
84
|
+
"#{Integer(ops[:h][hes['blockNumber']] || 0)}"
|
74
85
|
end
|
75
86
|
|
76
|
-
# @return [
|
77
|
-
def
|
78
|
-
|
87
|
+
# @return [String] comando insert SQL formatado bigquery.ethk
|
88
|
+
def ethk_ins
|
89
|
+
"insert #{BD}.ethk(blocknumber,timestamp,txhash,nonce,blockhash,axfrom,contractaddress,axto,value,tokenname," \
|
90
|
+
'tokensymbol,tokendecimal,transactionindex,gas,gasprice,gasused,cumulativegasused,input,confirmations,dias' \
|
91
|
+
") VALUES(#{carteiras.novaesk.map { |e| ethk_val1(e) }.join(',')})"
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [String] valores formatados bigquery.ethk parte1
|
95
|
+
def ethk_val1(hes)
|
96
|
+
"#{Integer(hes['blockNumber'])}," \
|
97
|
+
"#{Integer(hes['timeStamp'])}," \
|
98
|
+
"'#{hes['hash']}'," \
|
99
|
+
"#{Integer(hes['nonce'])}," \
|
100
|
+
"'#{hes['blockHash']}'," \
|
101
|
+
"'#{hes['from']}'," \
|
102
|
+
"#{ethk_val2(hes)}"
|
103
|
+
end
|
79
104
|
|
80
|
-
|
105
|
+
# @return [String] valores formatados bigquery.ethk parte2
|
106
|
+
def ethk_val2(hes)
|
107
|
+
"#{hes['contractAddress'].length.zero? ? 'null' : "'#{hes['contractAddress']}'"}," \
|
108
|
+
"'#{hes['to']}'," \
|
109
|
+
"cast('#{hes['value']}' as numeric)," \
|
110
|
+
"'#{hes['tokenName']}'," \
|
111
|
+
"'#{hes['tokenSymbol']}'," \
|
112
|
+
"#{Integer(hes['tokenDecimal'])}," \
|
113
|
+
"#{Integer(hes['transactionIndex'])}," \
|
114
|
+
"#{ethk_val3(hes)}"
|
81
115
|
end
|
82
116
|
|
83
|
-
# @return [String]
|
84
|
-
def
|
85
|
-
'
|
117
|
+
# @return [String] valores formatados bigquery.ethk parte3
|
118
|
+
def ethk_val3(hes)
|
119
|
+
"cast('#{hes['gas']}' as numeric)," \
|
120
|
+
"cast('#{hes['gasPrice']}' as numeric)," \
|
121
|
+
"cast('#{hes['gasUsed']}' as numeric),0," \
|
122
|
+
"#{hes['input'].length.zero? ? 'null' : "'#{hes['input']}'"},0," \
|
123
|
+
"#{Integer(ops[:h][hes['blockNumber']] || 0)}"
|
86
124
|
end
|
87
125
|
|
88
|
-
#
|
89
|
-
|
90
|
-
|
126
|
+
# cria job bigquery & verifica execucao
|
127
|
+
#
|
128
|
+
# @param [String] sql a executar
|
129
|
+
# @return [Boolean] job ok?
|
130
|
+
def job?(sql)
|
131
|
+
@job = api.query_job(sql)
|
132
|
+
@job.wait_until_done!
|
133
|
+
puts(@job.error['message']) if @job.failed?
|
134
|
+
@job.failed?
|
91
135
|
end
|
92
136
|
|
93
|
-
#
|
94
|
-
|
95
|
-
|
137
|
+
# cria Data Manipulation Language (DML) job bigquery
|
138
|
+
#
|
139
|
+
# @param (see job?)
|
140
|
+
# @return [Integer] numero linhas afetadas
|
141
|
+
def dml(sql)
|
142
|
+
job?(sql) ? 0 : job.num_dml_affected_rows
|
96
143
|
end
|
97
144
|
|
98
|
-
#
|
99
|
-
|
100
|
-
|
145
|
+
# cria Structured Query Language (SQL) job bigquery
|
146
|
+
#
|
147
|
+
# @param (see job?)
|
148
|
+
# @return [Array<Hash>] resultados do SQL
|
149
|
+
def sql(sql)
|
150
|
+
job?(sql) ? [] : job.data
|
101
151
|
end
|
102
152
|
end
|
103
153
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('bigdecimal/util')
|
4
|
+
|
5
|
+
# @author Hernani Rodrigues Vaz
|
6
|
+
module Etht
|
7
|
+
# classe para processar carteiras & transacoes normais e tokens
|
8
|
+
class Carteiras
|
9
|
+
# @return [Etherscan] API etherscan
|
10
|
+
attr_reader :api
|
11
|
+
# @return [Array<Hash>] todos os dados bigquery
|
12
|
+
attr_reader :dbq
|
13
|
+
# @return [Thor::CoreExt::HashWithIndifferentAccess] opcoes trabalho
|
14
|
+
attr_reader :ops
|
15
|
+
|
16
|
+
# @param [Hash] dad todos os dados bigquery
|
17
|
+
# @param [Thor::CoreExt::HashWithIndifferentAccess] pop opcoes trabalho
|
18
|
+
# @option pop [Hash] :h ({}) configuracao dias ajuste reposicionamento temporal
|
19
|
+
# @option pop [Boolean] :v (false) mostra dados transacoes normais & tokens?
|
20
|
+
# @return [Carteiras] API etherscan - processar transacoes normais e tokens
|
21
|
+
def initialize(dad, pop)
|
22
|
+
@api = Etherscan.new
|
23
|
+
@dbq = dad
|
24
|
+
@ops = pop
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Array<Hash>] todos os dados etherscan - saldos & transacoes
|
28
|
+
def dadoses
|
29
|
+
@dadoses ||= api.multi_address_balance(dbq[:wb].map { |c| c[:ax] }).map { |e| base_etherscan(e) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<Integer>] lista blocknumbers novos de transacoes normais
|
33
|
+
def novobnt
|
34
|
+
@novobnt ||= (dadoses.map { |e| e[:tx].map { |n| Integer(n['blockNumber']) } }.flatten -
|
35
|
+
dbq[:nt].map { |t| t[:blocknumber] }).uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<Integer>] lista blocknumbers novos de transacoes token
|
39
|
+
def novobnk
|
40
|
+
@novobnk ||= (dadoses.map { |e| e[:kx].map { |n| Integer(n['blockNumber']) } }.flatten -
|
41
|
+
dbq[:nk].map { |t| t[:blocknumber] }).uniq
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Array<Hash>] lista transacoes normais novas
|
45
|
+
def novaest
|
46
|
+
@novaest ||= dadoses.map { |e| e[:tx].select { |s| novobnt.include?(Integer(s['blockNumber'])) } }.flatten.uniq
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Array<Hash>] lista transacoes tokan novas
|
50
|
+
def novaesk
|
51
|
+
@novaesk ||= dadoses.map { |e| e[:kx].select { |s| novobnk.include?(Integer(s['blockNumber'])) } }.flatten.uniq
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Array<Hash>] totdos dados juntos bigquery & etherscan
|
55
|
+
def dadosjn
|
56
|
+
@dadosjn ||= dbq[:wb].map { |b| bigquery_etherscan(b, dadoses.select { |s| b[:ax] == s[:ax] }.first) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Hash>] lista ordenada transacoes normais novas
|
60
|
+
def sortest
|
61
|
+
novaest.sort { |a, b| Integer(a['blockNumber']) <=> Integer(b['blockNumber']) }
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Hash>] lista ordenada transacoes tokan novas
|
65
|
+
def sortesk
|
66
|
+
novaesk.sort { |a, b| Integer(a['blockNumber']) <=> Integer(b['blockNumber']) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [Array<Hash>] lista ordenada transacoes normais & tokan novas
|
70
|
+
def totalha
|
71
|
+
(novaest + novaesk).sort { |a, b| Integer(a['blockNumber']) <=> Integer(b['blockNumber']) }
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [Hash] hes dados etherscan
|
75
|
+
# @return [Hash] dados etherscan - address, saldo & transacoes
|
76
|
+
def base_etherscan(hes)
|
77
|
+
{
|
78
|
+
ax: hes['account'],
|
79
|
+
sl: (hes['balance'].to_d / 10**18).round(10),
|
80
|
+
tx: etherscan_tx(hes['account']),
|
81
|
+
kx: etherscan_kx(hes['account'])
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [Hash] hbq dados bigquery
|
86
|
+
# @param [Hash] hes dados etherscan
|
87
|
+
# @return [Hash] dados juntos bigquery & etherscan
|
88
|
+
def bigquery_etherscan(hbq, hes)
|
89
|
+
{
|
90
|
+
id: hbq[:id],
|
91
|
+
ax: hbq[:ax],
|
92
|
+
bs: hbq[:sl],
|
93
|
+
bt: dbq[:nt].select { |t| t[:iax] == hbq[:ax] },
|
94
|
+
bk: dbq[:nk].select { |t| t[:iax] == hbq[:ax] },
|
95
|
+
es: hes[:sl],
|
96
|
+
et: hes[:tx],
|
97
|
+
ek: hes[:kx]
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
# @param [String] add endereco da carteira
|
102
|
+
# @return [Array<Hash>] lista transacoes normais ligadas a uma carteira - sem elementos irrelevantes
|
103
|
+
def etherscan_tx(add)
|
104
|
+
api.normal_tx(add).map do |e|
|
105
|
+
e.delete('cumulativeGasUsed')
|
106
|
+
e.delete('confirmations')
|
107
|
+
e
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param (see etherscan_tx)
|
112
|
+
# @return [Array<Hash>] lista transacoes token ligadas a uma carteira - sem elementos irrelevantes
|
113
|
+
def etherscan_kx(add)
|
114
|
+
api.token_tx(add).map do |e|
|
115
|
+
e.delete('cumulativeGasUsed')
|
116
|
+
e.delete('confirmations')
|
117
|
+
e
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Boolean] carteira tem transacoes novas(sim=NOK, nao=OK)?
|
122
|
+
def ok?(hjn)
|
123
|
+
hjn[:bs] == hjn[:es] && hjn[:bt].count == hjn[:et].count && hjn[:bk].count == hjn[:ek].count
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/etht/etherscan.rb
CHANGED
@@ -1,177 +1,129 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require('faraday')
|
4
|
+
require('json')
|
5
5
|
|
6
6
|
# @author Hernani Rodrigues Vaz
|
7
7
|
module Etht
|
8
|
-
#
|
9
|
-
class
|
10
|
-
|
11
|
-
attr_writer :logger
|
8
|
+
# classe para tratar pedidos etherscan com chave API
|
9
|
+
class Etherscan
|
10
|
+
attr_reader :key, :url
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
class Api
|
20
|
-
attr_reader :connection
|
21
|
-
|
22
|
-
def initialize(params = {})
|
23
|
-
@connection = Etht::Client.new(params)
|
12
|
+
# @param [String] :key apikey a juntar aos pedidos HTTP url:
|
13
|
+
# @param [String] :url base URL to use as a prefix for all requests
|
14
|
+
# @return [Etherscan] API etherscan base
|
15
|
+
def initialize(key: ENV['ETHERSCAN_API_KEY'], url: 'https://api.etherscan.io/')
|
16
|
+
@key = key
|
17
|
+
@url = url
|
24
18
|
end
|
25
19
|
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
# @return [<Symbol>] adapter for the connection - default :net_http
|
21
|
+
def adapter
|
22
|
+
@adapter ||= Faraday.default_adapter
|
29
23
|
end
|
30
|
-
end
|
31
24
|
|
32
|
-
|
33
|
-
|
34
|
-
URL
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@key = params.fetch(:key, Etht.config.key)
|
42
|
-
@url = params.fetch(:url, Etht.config.url || URL)
|
43
|
-
@adapter = params.fetch(:adapter, adapter)
|
44
|
-
@conn = params.fetch(:conn, conn)
|
45
|
-
@user_agent = params.fetch(:user_agent, "etherscan/#{Etht::VERSION};ruby")
|
46
|
-
@headers = HEADERS.merge('User-Agent' => @user_agent)
|
47
|
-
@raise_exceptions = params.fetch(:raise_exceptions, Etht.config.raise_exceptions || true)
|
48
|
-
yield self if block_given?
|
25
|
+
# manage the default properties and the middleware stack for fulfilling an HTTP request
|
26
|
+
#
|
27
|
+
# @return [<Faraday::Connection>] connection object with an URL & adapter
|
28
|
+
def conn
|
29
|
+
@conn ||=
|
30
|
+
Faraday.new(url: url) do |c|
|
31
|
+
c.request(:url_encoded)
|
32
|
+
c.adapter(adapter)
|
33
|
+
end
|
49
34
|
end
|
50
35
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
raise
|
36
|
+
# @return [<Hash>] resultado json do GET HTTP request
|
37
|
+
def get(**ars)
|
38
|
+
r =
|
39
|
+
conn.get('api') do |o|
|
40
|
+
o.headers = { content_type: 'application/json', accept: 'application/json', user_agent: 'etherscan;ruby' }
|
41
|
+
o.params = ars.merge({ apikey: key })
|
42
|
+
end
|
43
|
+
raise(Etht::Erro, r) if r.status != 200
|
59
44
|
|
60
|
-
JSON(
|
45
|
+
JSON(r.body)['result']
|
61
46
|
end
|
62
47
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
48
|
+
# get ether balance for a single address
|
49
|
+
#
|
50
|
+
# @param [String] add address for balance
|
51
|
+
# @return [<String>] devolve saldo
|
52
|
+
def address_balance(add)
|
53
|
+
raise(Etht::Erro, 'address must be defined') if add.nil?
|
69
54
|
|
70
|
-
|
71
|
-
@adapter ||= Faraday.default_adapter
|
55
|
+
get(module: 'account', action: 'balance', address: add, tag: 'latest')
|
72
56
|
end
|
73
57
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
58
|
+
# get ether balance for multiple addresses
|
59
|
+
#
|
60
|
+
# @param [String] ads lista de addresses (max 20)
|
61
|
+
# @return [Array<Hash>] devolve lista com contas & saldo
|
62
|
+
def multi_address_balance(ads)
|
63
|
+
raise(Etht::Erro, 'up to 20 accounts in a single batch') if ads.size > 20
|
78
64
|
|
79
|
-
|
80
|
-
class Accounts < Api
|
81
|
-
def address_balance(address)
|
82
|
-
params = {
|
83
|
-
module: 'account', action: 'balance',
|
84
|
-
address: address, tag: 'latest'
|
85
|
-
}
|
86
|
-
get(params)
|
65
|
+
get(module: 'account', action: 'balancemulti', address: ads.join(','), tag: 'latest')
|
87
66
|
end
|
88
67
|
|
89
|
-
|
90
|
-
|
68
|
+
# get ERC20-token account balance
|
69
|
+
#
|
70
|
+
# @param add (see address_balance)
|
71
|
+
# @param [String] cdd token contract address
|
72
|
+
# @return (see address_balance)
|
73
|
+
def token_balance(add, cdd)
|
74
|
+
raise(Etht::Erro, 'contract or address must be defined') if (cdd || add).nil?
|
91
75
|
|
92
|
-
|
93
|
-
module: 'account', action: 'balancemulti',
|
94
|
-
address: addresses.join(','), tag: 'latest'
|
95
|
-
}
|
96
|
-
get(params)
|
76
|
+
get(module: 'account', action: 'tokenbalance', address: add, contractaddress: cdd)
|
97
77
|
end
|
98
78
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
# @param
|
103
|
-
# @
|
104
|
-
# @
|
105
|
-
# @
|
106
|
-
# @
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
79
|
+
# get a list of normal transactions by address
|
80
|
+
#
|
81
|
+
# @param [String] add address for transactions
|
82
|
+
# @param [Hash] ars opcoes trabalho
|
83
|
+
# @option ars [String] :start_block starting blockNo to retrieve results
|
84
|
+
# @option ars [String] :end_block ending blockNo to retrieve results
|
85
|
+
# @option ars [String] :sort asc -> ascending order, desc -> descending order
|
86
|
+
# @option ars [String] :page to get paginated results
|
87
|
+
# @option ars [String] :offset max records to return
|
88
|
+
# @return [Array<Hash>] devolve ate max 10000 das ultimas transacoes
|
89
|
+
def normal_tx(add, **ars)
|
90
|
+
raise(Etht::Erro, 'address must be defined') if add.nil?
|
111
91
|
|
112
|
-
|
113
|
-
# Available args: start_block, end_block, sort, page, offset
|
114
|
-
def internal_transactions(address, args = {})
|
115
|
-
action = 'txlistinternal'
|
116
|
-
transcations(action, address, nil, args)
|
92
|
+
transcations('txlist', add, nil, **ars)
|
117
93
|
end
|
118
94
|
|
119
|
-
#
|
120
|
-
#
|
121
|
-
# @param
|
122
|
-
#
|
123
|
-
|
124
|
-
|
95
|
+
# get a list of ERC20 - token transfer events
|
96
|
+
#
|
97
|
+
# @param add (see normal_tx)
|
98
|
+
# @param [String] cdd token address (nil to get a list of all ERC20 transactions)
|
99
|
+
# @param ars (see normal_tx)
|
100
|
+
# @option ars (see normal_tx)
|
101
|
+
# @return (see normal_tx)
|
102
|
+
def token_tx(add, cdd = nil, **ars)
|
103
|
+
raise(Etht::Erro, 'contract or address must be defined') if (cdd || add).nil?
|
125
104
|
|
126
|
-
|
127
|
-
transcations(action, address, contract_address, args)
|
105
|
+
transcations('tokentx', add, cdd, **ars)
|
128
106
|
end
|
129
107
|
|
130
108
|
private
|
131
109
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
}
|
150
|
-
get(params)
|
110
|
+
# get a list of transactions
|
111
|
+
#
|
112
|
+
# @param [String] act accao a executar
|
113
|
+
# @param add (see normal_tx)
|
114
|
+
# @param cdd (see token_tx)
|
115
|
+
# @param ars (see normal_tx)
|
116
|
+
# @option ars (see normal_tx)
|
117
|
+
# @return (see normal_tx)
|
118
|
+
def transcations(act, add, cdd, **ars)
|
119
|
+
get(**{ module: 'account', action: act, address: add }.merge({
|
120
|
+
contractaddress: cdd,
|
121
|
+
startblock: ars[:start_block],
|
122
|
+
endblock: ars[:end_block],
|
123
|
+
page: ars[:page],
|
124
|
+
offset: ars[:offset],
|
125
|
+
sort: ars[:sort]
|
126
|
+
}.reject { |_, v| v.nil? }))
|
151
127
|
end
|
152
|
-
|
153
|
-
def balance(address, contract_address)
|
154
|
-
params = {
|
155
|
-
module: 'account', action: 'tokenbalance',
|
156
|
-
address: address, contractaddress: contract_address
|
157
|
-
}
|
158
|
-
get(params)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def self.configure
|
163
|
-
yield config
|
164
|
-
end
|
165
|
-
|
166
|
-
def self.config
|
167
|
-
@config ||= Config.new
|
168
|
-
end
|
169
|
-
|
170
|
-
def self.logger
|
171
|
-
config.logger
|
172
|
-
end
|
173
|
-
|
174
|
-
configure do |config|
|
175
|
-
config.key = ENV['ETHERSCAN_API_KEY']
|
176
128
|
end
|
177
129
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @author Hernani Rodrigues Vaz
|
4
|
+
module Etht
|
5
|
+
# (see Carteiras)
|
6
|
+
class Carteiras
|
7
|
+
# @return [String] texto formatado valores duma carteira
|
8
|
+
def valores(hjn)
|
9
|
+
format(
|
10
|
+
'%<v1>10.6f %<n1>2i %<n3>2i %<v2>11.6f %<n2>2i %<n4>2i %<ok>-3s',
|
11
|
+
v1: hjn[:bs],
|
12
|
+
n1: hjn[:bt].count,
|
13
|
+
n3: hjn[:bk].count,
|
14
|
+
v2: hjn[:es],
|
15
|
+
n2: hjn[:et].count,
|
16
|
+
n4: hjn[:ek].count,
|
17
|
+
ok: ok?(hjn) ? 'OK' : 'NOK'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String] texto formatado transacao normal
|
22
|
+
def formata_tx(htx)
|
23
|
+
format(
|
24
|
+
'%<bn>9i %<fr>-20.20s %<to>-20.20s %<dt>10.10s %<vl>17.6f',
|
25
|
+
bn: htx['blockNumber'],
|
26
|
+
fr: ftx(htx['from'], 20),
|
27
|
+
to: ftx(htx['to'], 20),
|
28
|
+
dt: Time.at(Integer(htx['timeStamp'])),
|
29
|
+
vl: (htx['value'].to_d / 10**18).round(10)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] texto formatado transacao token
|
34
|
+
def formata_kx(hkx)
|
35
|
+
format(
|
36
|
+
'%<bn>9i %<fr>-20.20s %<to>-20.20s %<dt>10.10s %<vl>11.3f %<sy>-5.5s',
|
37
|
+
bn: hkx['blockNumber'],
|
38
|
+
fr: ftx(hkx['from'], 20),
|
39
|
+
to: ftx(hkx['to'], 20),
|
40
|
+
dt: Time.at(Integer(hkx['timeStamp'])),
|
41
|
+
vl: (hkx['value'].to_d / 10**18).round(10),
|
42
|
+
sy: hkx['tokenSymbol']
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] texto lista carteiras & lista transacoes & ajuste dias
|
47
|
+
def mostra
|
48
|
+
return unless dadosjn.count.positive?
|
49
|
+
|
50
|
+
puts("\nid address ----bigquery---- ----etherscan----")
|
51
|
+
dadosjn.each { |e| puts format('%<s1>-6.6s %<s2>-34.34s ', s1: e[:id], s2: ftx(e[:ax], 34)) + valores(e) }
|
52
|
+
mostra_tx
|
53
|
+
mostra_kx
|
54
|
+
mostra_ha
|
55
|
+
end
|
56
|
+
|
57
|
+
# @example ether address inicio..fim
|
58
|
+
# 0x10f3a0cf0b534c..c033cf32e8a03586
|
59
|
+
# @param [String] add ether address
|
60
|
+
# @param [Integer] max chars a mostrar
|
61
|
+
# @return [String] ether address formatada
|
62
|
+
def ftx(add, max)
|
63
|
+
i = Integer((max - 2) / 2)
|
64
|
+
max < 7 ? 'erro' : "#{add[0, i]}..#{add[-i..]}"
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [String] lista transacoes normais
|
68
|
+
def mostra_tx
|
69
|
+
return unless novaest.count.positive? && ops[:v]
|
70
|
+
|
71
|
+
puts("\ntx normal address from address to ---data--- ------valor------")
|
72
|
+
sortest.each { |e| puts formata_tx(e) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [String] lista transacoes token
|
76
|
+
def mostra_kx
|
77
|
+
return unless novaesk.count.positive? && ops[:v]
|
78
|
+
|
79
|
+
puts("\ntx token address from address to ---data--- ---valor--- token")
|
80
|
+
sortesk.each { |e| puts formata_kx(e) }
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [String] texto configuracao ajuste dias
|
84
|
+
def mostra_ha
|
85
|
+
return unless (novaest.count + novaesk.count).positive? && ops[:v]
|
86
|
+
|
87
|
+
puts("\nstring ajuste dias\n-h=#{totalha.map { |e| "#{e['blockNumber']}:0" }.join(' ')}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/etht/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etht
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hernâni Rodrigues Vaz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -108,8 +108,8 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
-
description: Arquiva eth-transactions no bigquery. Pode
|
112
|
-
|
111
|
+
description: Arquiva eth-transactions no bigquery. Pode ajustar dias para reposicionamento
|
112
|
+
temporal.
|
113
113
|
email:
|
114
114
|
- hernanirvaz@gmail.com
|
115
115
|
executables:
|
@@ -118,6 +118,7 @@ extensions: []
|
|
118
118
|
extra_rdoc_files: []
|
119
119
|
files:
|
120
120
|
- ".gitignore"
|
121
|
+
- ".rubocop.yml"
|
121
122
|
- CODE_OF_CONDUCT.md
|
122
123
|
- Gemfile
|
123
124
|
- Gemfile.lock
|
@@ -130,7 +131,9 @@ files:
|
|
130
131
|
- exe/etht
|
131
132
|
- lib/etht.rb
|
132
133
|
- lib/etht/bigquery.rb
|
134
|
+
- lib/etht/carteiras.rb
|
133
135
|
- lib/etht/etherscan.rb
|
136
|
+
- lib/etht/formatar.rb
|
134
137
|
- lib/etht/version.rb
|
135
138
|
homepage: https://github.com/hernanirvaz/etht
|
136
139
|
licenses:
|