eost 0.1.6 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('bigdecimal/util')
4
+
5
+ # @author Hernani Rodrigues Vaz
6
+ module Eost
7
+ # classe para processar carteiras & transacoes
8
+ class Carteiras
9
+ # @return [Eosscan] API eosscan
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
+ # @return [Array<Integer>] lista blocknumbers transacoes no bigquery - condicionados por opcoes iniciais
16
+ attr_reader :abn
17
+
18
+ # @param [Hash] dad todos os dados bigquery
19
+ # @param [Thor::CoreExt::HashWithIndifferentAccess] pop opcoes trabalho
20
+ # @option pop [Hash] :h ({}) configuracao dias ajuste reposicionamento temporal
21
+ # @option pop [Boolean] :v (false) mostra dados transacoes?
22
+ # @option pop [Boolean] :t (false) mostra transacoes todas ou somente novas?
23
+ # @return [Carteiras] API eosscan - processar transacoes
24
+ def initialize(dad, pop)
25
+ @api = Eosscan.new
26
+ @dbq = dad
27
+ @ops = pop
28
+ @abn = (ops[:t] ? [] : dbq[:nt].map { |t| t[:blocknumber] })
29
+ end
30
+
31
+ # @return [Array<Hash>] todos os dados eosscan - saldos & transacoes
32
+ def des
33
+ @des ||= dbq[:wb].map { |e| base_eosscan(e) }
34
+ end
35
+
36
+ # @return [Array<Hash>] todos os dados juntos bigquery & eosscan
37
+ def djn
38
+ @djn ||= dbq[:wb].map { |b| bigquery_eosscan(b, des.select { |s| b[:ax] == s[:ax] }.first) }
39
+ end
40
+
41
+ # @return [Array<Integer>] lista blocknumbers de transacoes novas
42
+ def bnn
43
+ @bnn ||= (des.map { |e| e[:tx].map { |n| Integer(n['block_num']) } }.flatten - abn)
44
+ end
45
+
46
+ # @return [Array<Hash>] lista transacoes novas
47
+ def novas
48
+ @novas ||= des.map { |e| e[:tx].select { |s| bnn.include?(Integer(s['block_num'])) } }.flatten.uniq
49
+ end
50
+
51
+ # @return [Array<Hash>] lista ordenada transacoes novas
52
+ def novas_sort
53
+ novas.sort { |a, b| Integer(a['block_num']) <=> Integer(b['block_num']) }
54
+ end
55
+
56
+ # @param [Hash] hwb wallet bigquery eos
57
+ # @return [Hash] dados eosscan - address, saldo & transacoes
58
+ def base_eosscan(hwb)
59
+ {
60
+ ax: hwb[:ax],
61
+ sl: eosscan_sl(hwb[:ax]).inject(:+),
62
+ tx: eosscan_tx(hwb[:ax])
63
+ }
64
+ end
65
+
66
+ # @param (see bigquery_eosscan)
67
+ # @return [Hash<Array, Boolean>] lista blocknumbers novos & carteira ok?
68
+ def novas_ok(hwb, hes)
69
+ # quando todas as transacoes obtidas da api vao ser inseridas,
70
+ # entao podem ficar transacoes por inserir - api consegue apenas um maximo de 100 transacoes
71
+ n = hes[:tx].map { |v| Integer(v['block_num']) } - abn
72
+ { nn: n, ok: hwb[:sl] == hes[:sl] && n.count < hes[:tx].count }
73
+ end
74
+
75
+ # @param hwb (see base_eosscan)
76
+ # @param [Hash] hes dados eosscan
77
+ # @return [Hash] dados juntos bigquery & eosscan
78
+ def bigquery_eosscan(hwb, hes)
79
+ {
80
+ id: hwb[:id],
81
+ ax: hwb[:ax],
82
+ bs: hwb[:sl],
83
+ bt: dbq[:nt].select { |t| t[:iax] == hwb[:ax] },
84
+ es: hes[:sl],
85
+ et: hes[:tx]
86
+ }.merge(novas_ok(hwb, hes))
87
+ end
88
+
89
+ # @param [String] add endereco carteira EOS
90
+ # @return [Array<BigDecimal>] lista recursos - liquido, net, spu
91
+ def eosscan_sl(add)
92
+ v = api.chain_get_account(account_name: add)
93
+ [
94
+ v['core_liquid_balance'].to_d,
95
+ v['total_resources']['net_weight'].to_d,
96
+ v['total_resources']['cpu_weight'].to_d
97
+ ]
98
+ end
99
+
100
+ # @param (see eosscan_sl)
101
+ # @return [Array<Hash>] lista ultimas 100 transacoes ligadas a uma carteira - sem elementos irrelevantes
102
+ def eosscan_tx(add)
103
+ api.history_get_actions(account_name: add, offset: -100)['actions'].map do |e|
104
+ e.delete('global_action_seq')
105
+ e
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('faraday')
4
+ require('json')
5
+
6
+ module Eost
7
+ # classe para acesso dados blockchain EOS
8
+ class Eosscan
9
+ # @return [String] endereco da API blockchain EOS
10
+ attr_reader :url
11
+
12
+ # attr_reader :spec, :api, :edp
13
+
14
+ # @return [Eosscan] acesso dados blockchain EOS
15
+ def initialize(www: 'https://eos.greymass.com')
16
+ @url = www
17
+ # load_specs
18
+ end
19
+
20
+ # @return [<Symbol>] adapter for the connection - default :net_http
21
+ def adapter
22
+ @adapter ||= Faraday.default_adapter
23
+ end
24
+
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
34
+ end
35
+
36
+ # @example chain_get_account
37
+ # {
38
+ # 'account_name': '...',
39
+ # 'head_block_num': 138_586_631,
40
+ # 'head_block_time': '2020-08-26T10:16:05.500',
41
+ # 'privileged': false,
42
+ # 'last_code_update': '1970-01-01T00:00:00.000',
43
+ # 'created': '2018-06-09T13:14:37.000',
44
+ # # DEVOLVIDO 'core_liquid_balance': '1232.0226 EOS',
45
+ # 'ram_quota': 9548,
46
+ # 'net_weight': 10_001_142,
47
+ # 'cpu_weight': 10_001_144,
48
+ # 'total_resources': {
49
+ # 'owner': '...',
50
+ # # DEVOLVIDO 'net_weight': '1000.1142 EOS',
51
+ # # DEVOLVIDO 'cpu_weight': '1000.1144 EOS',
52
+ # 'ram_bytes': 8148
53
+ # },
54
+ # 'net_limit': { 'used': 0, 'available': 1_068_152_841, 'max': 1_068_152_841 },
55
+ # 'cpu_limit': { 'used': 338, 'available': 90_856, 'max': 91_194 },
56
+ # 'ram_usage': 3574,
57
+ # 'permissions': [
58
+ # {
59
+ # 'perm_name': 'active',
60
+ # 'parent': 'owner',
61
+ # 'required_auth': {
62
+ # 'threshold': 1,
63
+ # 'keys': [{ 'key': '...', 'weight': 1 }],
64
+ # 'accounts': [],
65
+ # 'waits': []
66
+ # }
67
+ # },
68
+ # {
69
+ # 'perm_name': 'owner',
70
+ # 'parent': '',
71
+ # 'required_auth': {
72
+ # 'threshold': 1,
73
+ # 'keys': [{ 'key': '...', 'weight': 1 }],
74
+ # 'accounts': [],
75
+ # 'waits': []
76
+ # }
77
+ # }
78
+ # ],
79
+ # 'self_delegated_bandwidth': {
80
+ # 'from': '...', 'to': '...', 'net_weight': '1000.1142 EOS', 'cpu_weight': '1000.1144 EOS'
81
+ # },
82
+ # 'refund_request': nil,
83
+ # 'voter_info': {
84
+ # 'owner': '...',
85
+ # 'proxy': '...',
86
+ # 'producers': [],
87
+ # 'staked': 20_002_286,
88
+ # 'last_vote_weight': '17172913021904.12109375000000000',
89
+ # 'proxied_vote_weight': '0.00000000000000000',
90
+ # 'is_proxy': 0,
91
+ # 'flags1': 0,
92
+ # 'reserved2': 0,
93
+ # 'reserved3': '0.0000 EOS'
94
+ # },
95
+ # 'rex_info': nil
96
+ # }
97
+ # @return [Hash] dados numa carteira EOS
98
+ def chain_get_account(**args)
99
+ JSON.parse(conn.post('/v1/chain/get_account', args.to_json, content_type: 'application/json').body)
100
+ end
101
+
102
+ # @example history_get_actions
103
+ # {
104
+ # 'actions' => [
105
+ # {
106
+ # 'account_action_seq': 937,
107
+ # 'action_trace': {
108
+ # 'account_ram_deltas': [],
109
+ # 'act': {
110
+ # 'account': 'newsblockone',
111
+ # 'authorization': [
112
+ # { 'actor': 'blockonenews', 'permission': 'active' },
113
+ # { 'actor': 'newsblockone', 'permission': 'active' }
114
+ # ],
115
+ # 'data': {
116
+ # 'from': 'newsblockone',
117
+ # 'memo': '100 million EOS tokens released on the network - EOS Reallocation Program @ get-eos.io',
118
+ # 'quantity': '1.0000 NEWS',
119
+ # 'to': '...'
120
+ # },
121
+ # 'hex_data': 'a02685',
122
+ # 'name': 'transfer'
123
+ # },
124
+ # 'action_ordinal': 20,
125
+ # 'block_num': 135_581_543,
126
+ # 'block_time': '2020-08-09T00:45:41.000',
127
+ # 'closest_unnotified_ancestor_action_ordinal': 10,
128
+ # 'context_free': false,
129
+ # 'creator_action_ordinal': 10,
130
+ # 'elapsed': 17,
131
+ # 'producer_block_id': '0814cf67c5dfe81e8647be1ddec70a8c84c45e1d65779132ace3561be044a12c',
132
+ # 'receipt': {
133
+ # 'abi_sequence': 2,
134
+ # 'act_digest': '8b2a534341229734f1532430ffcd40c30c7b82da30c3f23446f248c2e1209a68',
135
+ # 'auth_sequence': [['blockonenews', 485_788], ['newsblockone', 368_458]],
136
+ # 'code_sequence': 2,
137
+ # 'global_sequence': 204_352_530_651,
138
+ # 'receiver': '...',
139
+ # 'recv_sequence': 900
140
+ # },
141
+ # 'receiver': '...',
142
+ # 'trx_id': 'de327b9ba02f2fbca9eb2ee3a4e26f8ead6198248b52d184e1f480c578705ba9'
143
+ # },
144
+ # 'block_num': 135_581_543,
145
+ # 'block_time': '2020-08-09T00:45:41.000',
146
+ # # DELETED 'global_action_seq': 204_352_530_651,
147
+ # 'irreversible': true
148
+ # }
149
+ # ],
150
+ # 'head_block_num' => 138_936_528,
151
+ # 'last_irreversible_block' => 138_936_194
152
+ # }
153
+ # @return [Hash] dados das transacoes ligadas a uma carteira EOS
154
+ def history_get_actions(**args)
155
+ JSON.parse(conn.post('/v1/history/get_actions', args.to_json, content_type: 'application/json').body)
156
+ end
157
+
158
+ # private
159
+ # Load API specification from spec files
160
+ # def load_specs
161
+ # @spec = {}
162
+ # Dir["#{spec_path}*"].map { |f| File.basename(f, '.json') }.compact.each { |n| @spec[n] = read_spec(n) }
163
+ # end
164
+ #
165
+ # def spec_path
166
+ # "#{File.dirname(__dir__)}/../specs/"
167
+ # end
168
+ #
169
+ # def read_spec(name)
170
+ # JSON.parse(File.read("#{spec_path}#{name}.json"))
171
+ # end
172
+ #
173
+ # Add API methods to class for seamless usage - this used for undefined methods
174
+ # def method_missing(method_name, *args)
175
+ # return super(method_name, *args) unless respond_to_missing?(method_name)
176
+ #
177
+ # api_call(args.first)
178
+ # end
179
+ #
180
+ # def respond_to_missing?(method_name)
181
+ # @api, @edp = extract_endpoint(method_name)
182
+ # return super(endpoint, *args) unless rtm?
183
+ #
184
+ # rtm?
185
+ # end
186
+ #
187
+ # def rtm?
188
+ # spec.key?(api) && spec[api].key?(edp)
189
+ # end
190
+ #
191
+ # def extract_endpoint(name)
192
+ # name.to_s.split('_', 2)
193
+ # end
194
+ #
195
+ # def known_params
196
+ # # api, endpoint = extract_endpoint(method_name)
197
+ # return {} unless rtm?
198
+ #
199
+ # spec[api][edp]['params'] || {}
200
+ # end
201
+ #
202
+ # # The actual http call
203
+ # def api_call(args)
204
+ # args ||= {}
205
+ # known = known_params
206
+ # r = conn.post(
207
+ # "/v1/#{api}/#{edp}",
208
+ # args.select { |k, _| known.include?(k.to_s) }.to_json,
209
+ # content_type: 'application/json'
210
+ # )
211
+ # JSON.parse(r.body)
212
+ # end
213
+ end
214
+ end
@@ -1,19 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eost
4
- HT = %w[block_num block_time contract action
5
- from to amount symbol memo data].freeze
6
- R1 = '%<v3>-12.12s %<v4>-8.8s %<v5>-12.12s %<v6>-12.12s'
4
+ HT = %w[block_num block_time contract action from to amount symbol memo data].freeze
5
+ R1 = '%<v5>-12.12s %<v6>-12.12s'
7
6
  R2 = '%<v7>10.5f %<v8>-8.8s'
8
7
 
9
8
  # trabalhar com folhas calculo bloks.io & dados no bigquery
10
9
  class Bigquery
11
- # processa linhas folha calculo
12
- def processa
10
+ # processa folha calculo
11
+ def processa_csv
13
12
  n = 0
14
13
  folha.sheet(0).parse(header_search: HT) do |r|
15
14
  n += 1
16
- puts n == 1 ? "\n" + folha.info : processa_row(r)
15
+ puts n == 1 ? "\n#{folha.info}" : processa_row(r)
17
16
  end
18
17
  end
19
18
 
@@ -23,8 +22,12 @@ module Eost
23
22
  # @return [String] texto informativo do processamento
24
23
  def processa_row(has)
25
24
  @row = has.values
26
- sql_select
27
- if row_naoexiste? then row_str + (sql_insert == 1 ? ' NOVA' : ' ERRO')
25
+
26
+ # array.count = 0 ==> pode carregar esta linha
27
+ # array.count >= 1 ==> nao carregar esta linha
28
+ sql("select #{eos_fields} #{sql_where}", [{}, {}])
29
+
30
+ if row_naoexiste? then row_str + (eos_insert_csv == 1 ? ' NOVA' : ' ERRO')
28
31
  elsif row_existe? then row_existente
29
32
  else row_multiplas
30
33
  end
@@ -32,45 +35,45 @@ module Eost
32
35
 
33
36
  # @return [String] linha folha calculo formatada
34
37
  def row_str
35
- "#{row[0]} #{DateTime.parse(row[1]).strftime(DF)} " + row_r1 + row_r2
38
+ "#{row[0]} #{Time.parse(row[1]).strftime(DF)} " + row_r1 + row_r2
36
39
  end
37
40
 
38
41
  # @return [String] linha folha calculo formatada
39
42
  def row_r1
40
- format(R1, v3: row[2], v4: row[3], v5: row[4], v6: row[5])
43
+ format(R1, v5: row[4], v6: row[5])
41
44
  end
42
45
 
43
46
  # @return [String] linha folha calculo formatada
44
47
  def row_r2
45
- format(R2, v7: row[6].to_f, v8: row[7])
48
+ format(R2, v7: Float(row[6]), v8: row[7])
46
49
  end
47
50
 
48
51
  # @return [String] linha folha calculo existente
49
52
  def row_existente
50
- d = linha[:e] ? sql_delete : 0
51
- row_str + ' EXISTENTE' + str_apagadas(d)
53
+ d = linha[:e] ? dml("delete #{sql_where}") : 0
54
+ "#{row_str} EXISTENTE#{str_apagadas(d)}"
52
55
  end
53
56
 
54
57
  # @return [String] linha folha calculo existencia multipla
55
58
  def row_multiplas
56
- d = linha[:m] ? sql_delete : 0
57
- row_str + ' MULTIPLAS ' + sql.count.to_s + str_apagadas(d)
59
+ d = linha[:m] ? dml("delete #{sql_where}") : 0
60
+ "#{row_str} MULTIPLAS #{sql.count}#{str_apagadas(d)}"
58
61
  end
59
62
 
60
- # @param [Integer] numero linhas apagadas
63
+ # @param [Integer] num numero linhas apagadas
61
64
  # @return [String] texto formatado linhas apagadas
62
65
  def str_apagadas(num)
63
- num.positive? ? ' & ' + num.to_s + ' APAGADA(S) ' : ' '
66
+ num.positive? ? " & #{num} APAGADA(S) " : ' '
64
67
  end
65
68
 
66
69
  # @return [Boolean] linha folha calculo nao existe no bigquery?
67
70
  def row_naoexiste?
68
- sql.count.zero?
71
+ sqr.count.zero?
69
72
  end
70
73
 
71
74
  # @return [Boolean] linha folha calculo existe no bigquery?
72
75
  def row_existe?
73
- sql.count == 1
76
+ sqr.count == 1
74
77
  end
75
78
  end
76
79
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @author Hernani Rodrigues Vaz
4
+ module Eost
5
+ # (see Carteiras)
6
+ class Carteiras
7
+ # @param [Hash] hjn dados juntos bigquery & eosscan
8
+ # @return [String] texto formatado duma carteira
9
+ def formata_carteira(hjn)
10
+ format(
11
+ '%<s1>-12.12s %<v1>12.4f %<v2>12.4f %<nn>3i %<ok>-3s',
12
+ s1: hjn[:ax],
13
+ v1: hjn[:bs],
14
+ v2: hjn[:es],
15
+ nn: hjn[:nn].count,
16
+ ok: hjn[:ok] ? 'OK' : 'NOK'
17
+ )
18
+ end
19
+
20
+ # @param [Hash] htx transacao
21
+ # @return [String] texto formatado transacao
22
+ def formata_transacao(htx)
23
+ format(
24
+ '%<bn>9i %<fr>-12.12s %<to>-12.12s %<ac>-12.12s %<dt>10.10s %<vl>13.4f %<sy>-6.6s',
25
+ bn: htx['block_num'],
26
+ fr: act_data(htx)['from'],
27
+ to: act_data(htx)['to'],
28
+ ac: htx['action_trace']['act']['name'],
29
+ dt: Date.parse(htx['block_time']),
30
+ vl: act_data(htx)['quantity'].to_d,
31
+ sy: act_data(htx)['quantity'][/[[:upper:]]+/]
32
+ )
33
+ end
34
+
35
+ # @param (see formata_transacao)
36
+ # @return [Hash] dados da acao
37
+ def act_data(htx)
38
+ htx['action_trace']['act']['data']
39
+ end
40
+
41
+ # @return [String] texto carteiras & transacoes & ajuste dias
42
+ def mostra_resumo
43
+ return unless djn.count.positive?
44
+
45
+ puts("\naddress --bigquery-- --eosscans-- new")
46
+ djn.each { |e| puts(formata_carteira(e)) }
47
+ mostra_transacoes_novas
48
+ mostra_configuracao_ajuste_dias
49
+ end
50
+
51
+ # @return [String] texto transacoes
52
+ def mostra_transacoes_novas
53
+ return unless ops[:v] && novas.count.positive?
54
+
55
+ puts("\nblock num add from add to accao ---data--- ----valor----")
56
+ novas_sort.each { |e| puts(formata_transacao(e)) }
57
+ end
58
+
59
+ # @return [String] texto configuracao ajuste dias das transacoes
60
+ def mostra_configuracao_ajuste_dias
61
+ return unless novas.count.positive?
62
+
63
+ puts("\nstring ajuste dias\n-h=#{novas_sort.map { |e| "#{e['block_num']}:0" }.join(' ')}")
64
+ end
65
+ end
66
+ end