eost 0.1.3 → 0.1.8

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.
@@ -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,95 +1,79 @@
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
- RF = '%<v3>-50.50s %<v4>8.2f'
4
+ HT = %w[block_num block_time contract action from to amount symbol memo data].freeze
5
+ R1 = '%<v5>-12.12s %<v6>-12.12s'
6
+ R2 = '%<v7>10.5f %<v8>-8.8s'
7
7
 
8
- # folhas calculo comuns no bigquery
8
+ # trabalhar com folhas calculo bloks.io & dados no bigquery
9
9
  class Bigquery
10
- # prepara linha folha calculo para processamento
11
- #
12
- # @param [Hash] has da linha em processamento
13
- def corrige_hash(has)
14
- @row = has.values
15
- @row[2] = row[2].strip
16
- @row[3] = -1 * row[3] if num > 1
17
- end
18
-
19
- # processa linhas folha calculo
20
- def processa
10
+ # processa folha calculo
11
+ def processa_csv
21
12
  n = 0
22
- # usada somente a primeira sheet
23
- book.sheet(0).parse(header_search: HT) do |r|
13
+ folha.sheet(0).parse(header_search: HT) do |r|
24
14
  n += 1
25
- puts n == 1 ? "\n" + book.info : processa_row(r)
26
- end
27
- sql_update
28
- end
29
-
30
- # mostra linhas folha calculo
31
- def show
32
- n = 0
33
- # usada somente a primeira sheet
34
- book.sheet(0).parse(header_search: HT) do |r|
35
- n += 1
36
- puts n == 1 ? "\n" + book.info : show_row(r)
15
+ puts n == 1 ? "\n#{folha.info}" : processa_row(r)
37
16
  end
38
17
  end
39
18
 
40
19
  # processa linha folha calculo para arquivo
41
20
  #
42
- # @param (see corrige_hash)
43
- # @return [String] linha folha calculo processada
21
+ # @param [Hash] has da linha em processamento
22
+ # @return [String] texto informativo do processamento
44
23
  def processa_row(has)
45
- corrige_hash(has)
46
- sql_select
47
- if rnaoexiste? then row_str + (sql_insert == 1 ? ' NOVA' : ' ERRO')
48
- elsif rsimila? then row_similar
49
- elsif rexiste? then row_existente
24
+ @row = has.values
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')
31
+ elsif row_existe? then row_existente
32
+ else row_multiplas
50
33
  end
51
34
  end
52
35
 
53
- # obtem linha folha calculo para apresentacao
54
- #
55
- # @param (see corrige_hash)
56
- # @return (see row_str)
57
- def show_row(has)
58
- corrige_hash(has)
59
- row_str
36
+ # @return [String] linha folha calculo formatada
37
+ def row_str
38
+ "#{row[0]} #{Time.parse(row[1]).strftime(DF)} " + row_r1 + row_r2
60
39
  end
61
40
 
62
41
  # @return [String] linha folha calculo formatada
63
- def row_str
64
- "#{row[0].strftime(DF)} #{row[1].strftime(DF)} " \
65
- "#{format(RF, v3: row[2], v4: row[3])}"
42
+ def row_r1
43
+ format(R1, v5: row[4], v6: row[5])
66
44
  end
67
45
 
68
- # @return [String] linha folha calculo similar
69
- def row_similar
70
- d = apaga['s'] ? sql_delete : 0
71
- row_str + " SIMILAR#{d.zero? ? ' ' : ' APAGADA '}#{sql.first[:ds].strip}"
46
+ # @return [String] linha folha calculo formatada
47
+ def row_r2
48
+ format(R2, v7: Float(row[6]), v8: row[7])
72
49
  end
73
50
 
74
51
  # @return [String] linha folha calculo existente
75
52
  def row_existente
76
- d = apaga['e'] ? sql_delete : 0
77
- row_str + " EXISTENTE#{d.zero? ? '' : ' APAGADA'}"
53
+ d = linha[:e] ? dml("delete #{sql_where}") : 0
54
+ "#{row_str} EXISTENTE#{str_apagadas(d)}"
78
55
  end
79
56
 
80
- # @return [Boolean] linha folha calculo nao existe no bigquery?
81
- def rnaoexiste?
82
- sql.count.zero?
57
+ # @return [String] linha folha calculo existencia multipla
58
+ def row_multiplas
59
+ d = linha[:m] ? dml("delete #{sql_where}") : 0
60
+ "#{row_str} MULTIPLAS #{sql.count}#{str_apagadas(d)}"
83
61
  end
84
62
 
85
- # @return [Boolean] linha folha calculo existe no bigquery?
86
- def rexiste?
87
- sql.count == 1 && sql.first[:ds].strip == row[2]
63
+ # @param [Integer] num numero linhas apagadas
64
+ # @return [String] texto formatado linhas apagadas
65
+ def str_apagadas(num)
66
+ num.positive? ? " & #{num} APAGADA(S) " : ' '
67
+ end
68
+
69
+ # @return [Boolean] linha folha calculo nao existe no bigquery?
70
+ def row_naoexiste?
71
+ sqr.count.zero?
88
72
  end
89
73
 
90
- # @return [Boolean] linha folha calculo parecida no bigquery?
91
- def rsimila?
92
- sql.count == 1 && sql.first[:ds].strip != row[2]
74
+ # @return [Boolean] linha folha calculo existe no bigquery?
75
+ def row_existe?
76
+ sqr.count == 1
93
77
  end
94
78
  end
95
79
  end