etht 0.1.4 → 0.1.9

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: 9ca8ae2c29d35644a3d72e1ed819048020838736539e71023d5b81a0808875a5
4
- data.tar.gz: c40d028004ed756b4805eed8e89a71e0a7efc35de52bee891ebe7c00bc358abd
3
+ metadata.gz: dcc50ecfc4f0b90106f5ed792e2c870d6c6d00283f2f9b18005b27c28eeef15e
4
+ data.tar.gz: b20bbd45722191045ce9b3ade80590463e8f6529b1af9af4ed045b9a4ea4986a
5
5
  SHA512:
6
- metadata.gz: 005a56ce7e56e95fa9bcff843e9077a129f3492e5aa149a78d7f184307a88bc68e058004159fddeabd04c5b41eacec5eae405445a8d091b41dcdf9e5dd81f761
7
- data.tar.gz: 5707e919d084a23f8115b3516db8a3979555decb1b682245fba6249f31a46166ee8047687eeb3390eec0a9c59f8f8c4aa7e75d4ed09acff2d2f4a9b936b1bae0
6
+ metadata.gz: 917ffdc49fe414e5a28fcd92c1c5d8308bfb7833c8a4acfcbe648e4726be9be914319f7c99d6326177a0bab1880b7399e1cb8c0cf655f220cc12bf30576b877f
7
+ data.tar.gz: 6b69eb9c67c40d66d224094e99b478b71128f7b5c53c4ab2c3496136dc58b2a348d58a61d25aef8b93d24bad4ecd3e2952b51123df618342857ce90c46ff48aa
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ EnabledByDefault: true
4
+
5
+ Style/Copyright:
6
+ Enabled: false
7
+
8
+ Lint/ConstantResolution:
9
+ Enabled: false
10
+
11
+ Style/ConstantVisibility:
12
+ Enabled: false
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- etht (0.1.4)
4
+ etht (0.1.9)
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.1)
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 apagar movimentos existentes ja no bigquery.
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
- TODO: Write usage instructions here
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/[USERNAME]/etht/blob/master/CODE_OF_CONDUCT.md).
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/[USERNAME]/etht/blob/master/CODE_OF_CONDUCT.md).
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).
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'lib/etht/version'
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 + ' Pode apagar movimentos existentes ja no bigquery.'
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 = Dir.chdir(File.expand_path(__dir__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
- end
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 'bundler'
31
- spec.add_development_dependency 'rake'
31
+ spec.add_development_dependency('bundler')
32
+ spec.add_development_dependency('rake')
32
33
 
33
- spec.add_dependency 'faraday'
34
- spec.add_dependency 'google-cloud-bigquery'
35
- spec.add_dependency 'json'
36
- spec.add_dependency 'thor'
37
- spec.add_dependency 'yard'
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
@@ -1,36 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
4
- require 'etht/bigquery'
5
- require 'etht/etherscan'
6
- require 'etht/version'
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
- ID = `whoami`.chomp
11
-
12
- class Error < StandardError; end
13
- # class Exception < StandardError
14
- class Exception < StandardError
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
- # CLI para carregar etherscan comuns no bigquery
20
+ # classe para carregar/mostrar dados comuns bigquery & etherscan
21
21
  class CLI < Thor
22
- desc 'work', 'carrega/apaga dados do etherscan'
23
- option :e, type: :boolean, default: false, desc: 'apaga linha igual'
24
- option :m, type: :boolean, default: false, desc: 'apaga linhas existencia multipla'
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({ e: options[:e], m: options[:m], i: true }).processa_eth
26
+ Bigquery.new(options).processa
28
27
  end
29
28
 
30
- desc 'show', 'mostra dados do etherscan'
31
- # show etherscan
29
+ desc 'show', 'mostra reumo carteiras & transacoes'
30
+ option :v, type: :boolean, default: false, desc: 'mostra transacoes normais & token'
31
+ option :t, type: :boolean, default: false, desc: 'mostra transacoes todas ou somente novas'
32
+ # mostra reumo carteiras & transacoes
32
33
  def show
33
- Bigquery.new.processa_eth
34
+ Bigquery.new(options).carteiras.mostra_resumo
34
35
  end
35
36
 
36
37
  default_task :show
@@ -1,103 +1,162 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'google/cloud/bigquery'
3
+ require('google/cloud/bigquery')
4
+ require('bigdecimal/util')
4
5
 
5
6
  # @author Hernani Rodrigues Vaz
6
7
  module Etht
7
- DF = '%Y-%m-%d'
8
- DI = '%Y-%m-%d %H:%M:%S'
8
+ BD = 'hernanirvaz.coins'
9
9
 
10
- # (see Bigquery)
10
+ # classe para processar etherscan & bigquery
11
11
  class Bigquery
12
12
  # @return [Google::Cloud::Bigquery] API bigquery
13
- attr_reader :apibq
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 (see sql_select)
22
- attr_reader :sql
23
-
24
- # @param [String] csv folha calculo para processar
25
- # @param [Hash<Symbol, Boolean>] ops opcoes trabalho com linhas
26
- # @option ops [Boolean] :e (false) apaga linha igual?
27
- # @option ops [Boolean] :m (false) apaga linhas existencia multipla?
28
- # @option ops [Boolean] :i (false) insere linha nova?
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
+ # @return (see sql)
19
+ attr_reader :sqr
20
+
21
+ # @param [Thor::CoreExt::HashWithIndifferentAccess] pop opcoes trabalho
22
+ # @option pop [Hash] :h ({}) configuracao ajuste reposicionamento temporal
23
+ # @option pop [Boolean] :v (false) mostra transacoes normais & token?
24
+ # @option pop [Boolean] :t (false) mostra transacoes todas ou somente novas?
25
+ # @return [Bigquery] API bigquery & API etherscan
26
+ def initialize(pop)
31
27
  # usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials
32
28
  # @see https://cloud.google.com/bigquery/docs/authentication/getting-started
33
- @apibq = Google::Cloud::Bigquery.new
34
- @apies = Etht::Accounts.new
35
- @linha = ops
29
+ @api = Google::Cloud::Bigquery.new
30
+ @ops = pop
36
31
  end
37
32
 
38
- def processa_eth
39
- contract_address = '0xfc325129a11fab241287e42a9f04a74f14077b77'
40
- p apies.normal_transactions(contract_address)
33
+ # @return [Carteiras] API etherscan - processar transacoes normais e tokens
34
+ def transacoes
35
+ @transacoes ||= Carteiras.new(
36
+ {
37
+ wb: sql("select * from #{BD}.walletEth order by 2")
38
+ .map { |e| { id: e[:id], ax: e[:address], sl: e[:saldo].to_d.round(10) } },
39
+ nt: sql("select blocknumber,iax from #{BD}.ethtx"),
40
+ nk: sql("select blocknumber,iax from #{BD}.ethkx")
41
+ },
42
+ ops
43
+ )
41
44
  end
42
45
 
43
- # cria job bigquery & verifica execucao
44
- #
45
- # @param [String] sql a executar
46
- # @return [Boolean] job ok?
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?
46
+ # @return [Carteiras] API etherscan - processar carteiras & transacoes normais e tokens
47
+ def carteiras
48
+ transacoes
52
49
  end
53
50
 
54
- # cria Data Manipulation Language (DML) job bigquery
55
- #
56
- # @param (see job_bigquery?)
57
- # @return [Integer] numero linhas afetadas
58
- def dml(sql)
59
- job_bigquery?(sql) ? 0 : job.num_dml_affected_rows
51
+ # insere transacoes novas nas tabelas etht (trx normais), ethk (trx token)
52
+ def processa
53
+ puts(format("%<n>2i LINHAS INSERIDAS #{BD}.etht", n: transacoes.norml.count.positive? ? dml(etht_ins) : 0))
54
+ puts(format("%<n>2i LINHAS INSERIDAS #{BD}.ethk", n: transacoes.token.count.positive? ? dml(ethk_ins) : 0))
60
55
  end
61
56
 
62
- # pesquisa existencia linha folha calculo no bigquery
63
- #
64
- # @return [Google::Cloud::Bigquery::Data] resultado do sql num array<hash>
65
- def sql_select
66
- # array.count = 0 ==> pode carregar esta linha
67
- # array.count >= 1 ==> nao carregar esta linha
68
- @sql = job_bigquery?('select ' + eos_fields + ' ' + sql_where) ? [{}, {}] : job.data
57
+ # @return [String] comando insert SQL formatado etht (trx normais)
58
+ def etht_ins
59
+ "insert #{BD}.etht(blocknumber,timestamp,txhash,nonce,blockhash,transactionindex,axfrom,axto,value,gas," \
60
+ 'gasprice,iserror,txreceipt_status,input,contractaddress,cumulativegasused,gasused,confirmations,dias' \
61
+ ") VALUES#{transacoes.norml.map { |e| etht_val1(e) }.join(',')}"
62
+ end
63
+
64
+ # @return [String] comando insert SQL formatado ethk (trx token)
65
+ def ethk_ins
66
+ "insert #{BD}.ethk(blocknumber,timestamp,txhash,nonce,blockhash,axfrom,contractaddress,axto,value,tokenname," \
67
+ 'tokensymbol,tokendecimal,transactionindex,gas,gasprice,gasused,cumulativegasused,input,confirmations,dias' \
68
+ ") VALUES#{transacoes.token.map { |e| ethk_val1(e) }.join(',')}"
69
+ end
70
+
71
+ # @return [String] valores formatados etht (trx normais parte1)
72
+ def etht_val1(hes)
73
+ "(#{Integer(hes['blockNumber'])}," \
74
+ "#{Integer(hes['timeStamp'])}," \
75
+ "'#{hes['hash']}'," \
76
+ "#{Integer(hes['nonce'])}," \
77
+ "'#{hes['blockHash']}'," \
78
+ "#{Integer(hes['transactionIndex'])}," \
79
+ "'#{hes['from']}'," \
80
+ "'#{hes['to']}'," \
81
+ "#{etht_val2(hes)}"
69
82
  end
70
83
 
71
- # @return [String] parte sql para processamento linhas existentes
72
- def sql_where
73
- "from hernanirvaz.coins.eos where blocknumber=#{row[0]}"
84
+ # @return [String] valores formatados etht (trx normais parte2)
85
+ def etht_val2(hes)
86
+ "cast('#{hes['value']}' as numeric)," \
87
+ "cast('#{hes['gas']}' as numeric)," \
88
+ "cast('#{hes['gasPrice']}' as numeric)," \
89
+ "#{Integer(hes['isError'])}," \
90
+ "#{hes['txreceipt_status'].length.zero? ? 'null' : hes['txreceipt_status']}," \
91
+ "#{hes['input'].length.zero? ? 'null' : "'#{hes['input']}'"}," \
92
+ "#{etht_val3(hes)}"
74
93
  end
75
94
 
76
- # @return [Integer] numero linhas inseridas
77
- def sql_insert
78
- return 1 unless linha[:i]
95
+ # @return [String] valores formatados etht (trx normais parte3)
96
+ def etht_val3(hes)
97
+ "#{hes['contractAddress'].length.zero? ? 'null' : "'#{hes['contractAddress']}'"}," \
98
+ "0,cast('#{hes['gasUsed']}' as numeric),0," \
99
+ "#{Integer(ops[:h][hes['blockNumber']] || 0)})"
100
+ end
79
101
 
80
- dml('insert hernanirvaz.coins.eos(' + eos_fields + ') VALUES(' + str_insert1)
102
+ # @return [String] valores formatados ethk (trx token parte1)
103
+ def ethk_val1(hes)
104
+ "(#{Integer(hes['blockNumber'])}," \
105
+ "#{Integer(hes['timeStamp'])}," \
106
+ "'#{hes['hash']}'," \
107
+ "#{Integer(hes['nonce'])}," \
108
+ "'#{hes['blockHash']}'," \
109
+ "'#{hes['from']}'," \
110
+ "#{ethk_val2(hes)}"
81
111
  end
82
112
 
83
- # @return [String] campos da tabela no bigquery
84
- def eos_fields
85
- 'blocknumber,time,contract,action,acfrom,acto,amount,symbol,memo,data,dias'
113
+ # @return [String] valores formatados ethk (trx token parte2)
114
+ def ethk_val2(hes)
115
+ "#{hes['contractAddress'].length.zero? ? 'null' : "'#{hes['contractAddress']}'"}," \
116
+ "'#{hes['to']}'," \
117
+ "cast('#{hes['value']}' as numeric)," \
118
+ "'#{hes['tokenName']}'," \
119
+ "'#{hes['tokenSymbol']}'," \
120
+ "#{Integer(hes['tokenDecimal'])}," \
121
+ "#{Integer(hes['transactionIndex'])}," \
122
+ "#{ethk_val3(hes)}"
86
123
  end
87
124
 
88
- # @return [String] campos insert da linha bigquery
89
- def str_insert1
90
- "#{row[0]},'#{DateTime.parse(row[1]).strftime(DI)}','#{row[2]}'," + str_insert2
125
+ # @return [String] valores formatados ethk (trx token parte3)
126
+ def ethk_val3(hes)
127
+ "cast('#{hes['gas']}' as numeric)," \
128
+ "cast('#{hes['gasPrice']}' as numeric)," \
129
+ "cast('#{hes['gasUsed']}' as numeric),0," \
130
+ "#{hes['input'].length.zero? ? 'null' : "'#{hes['input']}'"},0," \
131
+ "#{Integer(ops[:h][hes['blockNumber']] || 0)})"
91
132
  end
92
133
 
93
- # @return [String] campos insert da linha bigquery
94
- def str_insert2
95
- "'#{row[3]}','#{row[4]}','#{row[5]}',#{row[6].to_f},'#{row[7]}','#{row[8]}','#{row[9]}',0)"
134
+ # cria job bigquery & verifica execucao
135
+ #
136
+ # @param cmd (see sql)
137
+ # @return [Boolean] job ok?
138
+ def job?(cmd)
139
+ @job = api.query_job(cmd)
140
+ @job.wait_until_done!
141
+ puts(@job.error['message']) if @job.failed?
142
+ @job.failed?
96
143
  end
97
144
 
98
- # @return [Integer] numero linhas apagadas
99
- def sql_delete
100
- dml('delete ' + sql_where)
145
+ # cria Structured Query Language (SQL) job bigquery
146
+ #
147
+ # @param [String] cmd comando SQL a executar
148
+ # @param [String] red resultado quando SQL tem erro
149
+ # @return [Google::Cloud::Bigquery::Data] resultado do SQL
150
+ def sql(cmd, red = [])
151
+ @sqr = job?(cmd) ? red : job.data
152
+ end
153
+
154
+ # cria Data Manipulation Language (DML) job bigquery
155
+ #
156
+ # @param cmd (see sql)
157
+ # @return [Integer] numero linhas afetadas
158
+ def dml(cmd)
159
+ job?(cmd) ? 0 : job.num_dml_affected_rows
101
160
  end
102
161
  end
103
162
  end
@@ -0,0 +1,121 @@
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 des
29
+ @des ||= api.multi_address_balance(dbq[:wb].map { |c| c[:ax] }).map { |e| base_etherscan(e) }
30
+ end
31
+
32
+ # @return [Array<Hash>] todos os dados juntos bigquery & etherscan
33
+ def djn
34
+ @djn ||= dbq[:wb].map { |b| bigquery_etherscan(b, des.select { |s| b[:ax] == s[:ax] }.first) }
35
+ end
36
+
37
+ # @return [Array<Integer>] lista blocknumbers de transacoes normais
38
+ def bnt
39
+ @bnt ||= (des.map { |e| e[:tx].map { |n| Integer(n['blockNumber']) } }.flatten -
40
+ (ops[:t] ? [] : dbq[:nt].map { |t| t[:blocknumber] })).uniq
41
+ end
42
+
43
+ # @return [Array<Integer>] lista blocknumbers de transacoes token
44
+ def bnk
45
+ @bnk ||= (des.map { |e| e[:kx].map { |n| Integer(n['blockNumber']) } }.flatten -
46
+ (ops[:t] ? [] : dbq[:nk].map { |t| t[:blocknumber] })).uniq
47
+ end
48
+
49
+ # @return [Array<Hash>] lista transacoes normais
50
+ def norml
51
+ @norml ||= des.map { |e| e[:tx].select { |s| bnt.include?(Integer(s['blockNumber'])) } }.flatten.uniq
52
+ end
53
+
54
+ # @return [Array<Hash>] lista transacoes tokan
55
+ def token
56
+ @token ||= des.map { |e| e[:kx].select { |s| bnk.include?(Integer(s['blockNumber'])) } }.flatten.uniq
57
+ end
58
+
59
+ # @return [Array<Hash>] lista ordenada transacoes normais
60
+ def norml_sort
61
+ norml.sort { |a, b| Integer(a['blockNumber']) <=> Integer(b['blockNumber']) }
62
+ end
63
+
64
+ # @return [Array<Hash>] lista ordenada transacoes tokan
65
+ def token_sort
66
+ token.sort { |a, b| Integer(a['blockNumber']) <=> Integer(b['blockNumber']) }
67
+ end
68
+
69
+ # @return [Array<Hash>] lista ordenada todas as transacoes (normais & tokan)
70
+ def todas_sort
71
+ (norml + token).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 add (see formata_endereco)
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 add (see formata_endereco)
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
+ end
121
+ end
@@ -1,177 +1,129 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday'
4
- require 'json'
3
+ require('faraday')
4
+ require('json')
5
5
 
6
6
  # @author Hernani Rodrigues Vaz
7
7
  module Etht
8
- # class Config
9
- class Config
10
- attr_accessor :key, :secret, :url, :raise_exceptions
11
- attr_writer :logger
8
+ # classe para tratar pedidos etherscan com chave API
9
+ class Etherscan
10
+ attr_reader :key, :url
12
11
 
13
- def logger
14
- @logger ||= Logger.new(STDERR)
15
- end
16
- end
17
-
18
- # class Api
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
- def get(params)
27
- response = connection.get(params)
28
- response['result']
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
- # class Client
33
- class Client
34
- URL = 'https://api.etherscan.io/'
35
- HEADERS = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }.freeze
36
-
37
- attr_reader :key, :url, :user_agent, :headers, :raise_exceptions
38
- attr_writer :adapter, :conn
39
-
40
- def initialize(params = {})
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
- def get(params = {})
52
- endpoint = 'api'
53
- merged_params = params.merge({ apikey: key })
54
- response = conn.get(endpoint) do |req|
55
- req.headers = headers
56
- req.params = merged_params
57
- end
58
- raise Etht::Exception, response if raise_exceptions? && response.status != 200
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(response.body)
45
+ JSON(r.body)['result']
61
46
  end
62
47
 
63
- def conn
64
- @conn ||= Faraday.new(url: @url) do |conn|
65
- conn.request :url_encoded
66
- conn.adapter adapter
67
- end
68
- end
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
- def adapter
71
- @adapter ||= Faraday.default_adapter
55
+ get(module: 'account', action: 'balance', address: add, tag: 'latest')
72
56
  end
73
57
 
74
- def raise_exceptions?
75
- @raise_exceptions
76
- end
77
- end
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
- # class Accounts < Etht::Api
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
- def multi_address_balance(addresses)
90
- raise Etht::Exception, 'up to 20 accounts in a single batch' if addresses.size > 20
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
- params = {
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
- # Get a list of 'Normal' Transactions By Address
100
- # if page is not defined Returns up to 10000 last transactions
101
- # Available args: start_block, end_block, sort, page, offset
102
- # @param sort 'asc' -> ascending order, 'des' -> descending order
103
- # @param start_block starting blockNo to retrieve results
104
- # @param end_block ending blockNo to retrieve results
105
- # @param page Paginated result <page number>
106
- # @param offset max records to return
107
- def normal_transactions(address, args = {})
108
- action = 'txlist'
109
- transcations(action, address, nil, args)
110
- end
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
- # Get a list of 'Internal' Transactions By Address
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
- # Get a list of "ERC20 - Token Transfer Events"
120
- # @param contract_address Token address (set nil to get a list of all ERC20 transactions)
121
- # @param address Address for ERC20 transactions (optional)
122
- # Available args: start_block, end_block, sort, page, offset
123
- def token_transactions(contract_address, address = nil, args = {})
124
- raise Etht::Exception, 'contract or address must be defined' if (contract_address || address).nil?
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
- action = 'tokentx'
127
- transcations(action, address, contract_address, args)
105
+ transcations('tokentx', add, cdd, **ars)
128
106
  end
129
107
 
130
108
  private
131
109
 
132
- def transcations(action, address, contract_address, args)
133
- params = {
134
- module: 'account', action: action,
135
- address: address, contractaddress: contract_address,
136
- startblock: args[:start_block], endblock: args[:end_block],
137
- page: args[:page], offset: args[:offset], sort: args[:sort]
138
- }.reject { |_, v| v.nil? }
139
- get(params)
140
- end
141
- end
142
-
143
- # class Tokens < Etht::Api
144
- class Tokens < Api
145
- def total_supply(contract_address)
146
- params = {
147
- module: 'stats', action: 'tokensupply',
148
- contractaddress: contract_address
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,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @author Hernani Rodrigues Vaz
4
+ module Etht
5
+ # (see Carteiras)
6
+ class Carteiras
7
+ # @param [String] add endereco carteira ether
8
+ # @param [Integer] max chars a mostrar
9
+ # @return [String] endereco ether formatado
10
+ # @example ether address inicio..fim
11
+ # 0x10f3a0cf0b534c..c033cf32e8a03586
12
+ def formata_endereco(add, max)
13
+ i = Integer((max - 2) / 2)
14
+ e = (max <= 20 ? dbq[:wb].select { |s| s[:ax] == add }.first : nil) || { id: add }
15
+ max < 7 ? 'erro' : "#{e[:id][0, i - 3]}..#{add[-i - 3..]}"
16
+ end
17
+
18
+ # @parm [Hash] hjn dados juntos bigquery & etherscan
19
+ # @return [String] texto formatado duma carteira
20
+ def formata_carteira(hjn)
21
+ format(
22
+ '%<s1>-6.6s %<s2>-34.34s ',
23
+ s1: hjn[:id],
24
+ s2: formata_endereco(hjn[:ax], 34)
25
+ ) + formata_valores(hjn)
26
+ end
27
+
28
+ # @parm (see formata_carteira)
29
+ # @return [Boolean] carteira tem transacoes novas(sim=NOK, nao=OK)?
30
+ def ok?(hjn)
31
+ hjn[:bs] == hjn[:es] && hjn[:bt].count == hjn[:et].count && hjn[:bk].count == hjn[:ek].count
32
+ end
33
+
34
+ # @parm (see formata_carteira)
35
+ # @return [String] texto formatado valores duma carteira
36
+ def formata_valores(hjn)
37
+ format(
38
+ '%<v1>10.6f %<n1>2i %<n3>2i %<v2>11.6f %<n2>2i %<n4>2i %<ok>-3s',
39
+ v1: hjn[:bs],
40
+ n1: hjn[:bt].count,
41
+ n3: hjn[:bk].count,
42
+ v2: hjn[:es],
43
+ n2: hjn[:et].count,
44
+ n4: hjn[:ek].count,
45
+ ok: ok?(hjn) ? 'OK' : 'NOK'
46
+ )
47
+ end
48
+
49
+ # @parm [Hash] htx transacao normal
50
+ # @return [String] texto formatado transacao normal
51
+ def formata_transacao_norml(htx)
52
+ format(
53
+ '%<bn>9i %<fr>-20.20s %<to>-20.20s %<dt>10.10s %<vl>17.6f',
54
+ bn: htx['blockNumber'],
55
+ fr: formata_endereco(htx['from'], 20),
56
+ to: formata_endereco(htx['to'], 20),
57
+ dt: Time.at(Integer(htx['timeStamp'])),
58
+ vl: (htx['value'].to_d / 10**18).round(10)
59
+ )
60
+ end
61
+
62
+ # @parm [Hash] hkx transacao token
63
+ # @return [String] texto formatado transacao token
64
+ def formata_transacao_token(hkx)
65
+ format(
66
+ '%<bn>9i %<fr>-20.20s %<to>-20.20s %<dt>10.10s %<sy>-5.5s %<vl>11.3f',
67
+ bn: hkx['blockNumber'],
68
+ fr: formata_endereco(hkx['from'], 20),
69
+ to: formata_endereco(hkx['to'], 20),
70
+ dt: Time.at(Integer(hkx['timeStamp'])),
71
+ vl: (hkx['value'].to_d / 10**18).round(10),
72
+ sy: hkx['tokenSymbol']
73
+ )
74
+ end
75
+
76
+ # @return [String] texto carteiras & transacoes & ajuste dias
77
+ def mostra_resumo
78
+ return unless djn.count.positive?
79
+
80
+ puts("\nid address ----bigquery---- ----etherscan----")
81
+ djn.each { |e| puts(formata_carteira(e)) }
82
+ mostra_transacao_norml
83
+ mostra_transacao_token
84
+ mostra_configuracao_ajuste_dias
85
+ end
86
+
87
+ # @return [String] texto transacoes normais
88
+ def mostra_transacao_norml
89
+ return unless ops[:v] && norml.count.positive?
90
+
91
+ puts("\ntx normal address from address to ---data--- ------valor------")
92
+ norml_sort.each { |e| puts(formata_transacao_norml(e)) }
93
+ end
94
+
95
+ # @return [String] texto transacoes token
96
+ def mostra_transacao_token
97
+ return unless ops[:v] && token.count.positive?
98
+
99
+ puts("\ntx token address from address to ---data--- token ---valor---")
100
+ token_sort.each { |e| puts(formata_transacao_token(e)) }
101
+ end
102
+
103
+ # @return [String] texto configuracao ajuste dias das transacoes (normais & tokan)
104
+ def mostra_configuracao_ajuste_dias
105
+ return unless (norml.count + token.count).positive?
106
+
107
+ puts("\nstring ajuste dias\n-h=#{todas_sort.map { |e| "#{e['blockNumber']}:0" }.join(' ')}")
108
+ end
109
+ end
110
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Etht
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.9'
5
5
  end
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
4
+ version: 0.1.9
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-09 00:00:00.000000000 Z
11
+ date: 2020-08-28 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 apagar movimentos existentes
112
- ja no bigquery.
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: