bct 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -3
- data/README.md +1 -1
- data/bct.gemspec +1 -1
- data/lib/bct.rb +18 -4
- data/lib/bct/apies.rb +161 -0
- data/lib/bct/apigm.rb +181 -0
- data/lib/bct/bigquery1.rb +102 -0
- data/lib/bct/bigquery2.rb +122 -0
- data/lib/bct/etherscan1.rb +118 -0
- data/lib/bct/etherscan2.rb +110 -0
- data/lib/bct/greymass1.rb +104 -0
- data/lib/bct/greymass2.rb +85 -0
- data/lib/bct/version.rb +1 -1
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31df00355fbff1327239b4a58cb208ef657ae0d2c260710557494d8b6273d9d0
|
4
|
+
data.tar.gz: 2d683c8e324fc18a78f31276447f40a39a386727f14b94528bcbcbda0a68a135
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b210ad6e12b94764f19f2e8f40d324696ae65fedda5bc3d996552607ac1086691c21cda59485b01875a21c76f268bfe5ea90e77c4741a2bf22ae219b133b1c1
|
7
|
+
data.tar.gz: 24cb2c0df27c3c49736d604106a4aaaac8f6dc9fbdbabad7ee232d40f51bc64e8ecc3cce02673d45c9d002413ff083cdc13ec7fd26b5f66614bbcf366abfc907
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bct (0.1.
|
4
|
+
bct (0.1.3)
|
5
5
|
faraday
|
6
6
|
google-cloud-bigquery
|
7
7
|
thor
|
@@ -17,15 +17,16 @@ GEM
|
|
17
17
|
declarative-option (0.1.0)
|
18
18
|
faraday (1.0.1)
|
19
19
|
multipart-post (>= 1.2, < 3)
|
20
|
-
google-api-client (0.44.
|
20
|
+
google-api-client (0.44.2)
|
21
21
|
addressable (~> 2.5, >= 2.5.1)
|
22
22
|
googleauth (~> 0.9)
|
23
23
|
httpclient (>= 2.8.1, < 3.0)
|
24
24
|
mini_mime (~> 1.0)
|
25
25
|
representable (~> 3.0)
|
26
26
|
retriable (>= 2.0, < 4.0)
|
27
|
+
rexml
|
27
28
|
signet (~> 0.12)
|
28
|
-
google-cloud-bigquery (1.
|
29
|
+
google-cloud-bigquery (1.22.0)
|
29
30
|
concurrent-ruby (~> 1.0)
|
30
31
|
google-api-client (~> 0.33)
|
31
32
|
google-cloud-core (~> 1.2)
|
@@ -58,6 +59,7 @@ GEM
|
|
58
59
|
declarative-option (< 0.2.0)
|
59
60
|
uber (< 0.2.0)
|
60
61
|
retriable (3.1.2)
|
62
|
+
rexml (3.2.4)
|
61
63
|
signet (0.14.0)
|
62
64
|
addressable (~> 2.3)
|
63
65
|
faraday (>= 0.17.3, < 2.0)
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Bct [![Build Status](https://travis-ci.com/hernanirvaz/bct.svg?branch=master)](https://travis-ci.com/hernanirvaz/bct)
|
2
2
|
|
3
|
-
Arquiva transactions etherscan/
|
3
|
+
Arquiva transactions etherscan/greymass no bigquery. Pode ajustar dias para reposicionamento temporal.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
data/bct.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ['Hernâni Rodrigues Vaz']
|
9
9
|
spec.email = ['hernanirvaz@gmail.com']
|
10
10
|
|
11
|
-
spec.summary = 'Arquiva transactions etherscan/
|
11
|
+
spec.summary = 'Arquiva transactions etherscan/greymass no bigquery.'
|
12
12
|
spec.description = "#{spec.summary} Pode ajustar dias para reposicionamento temporal."
|
13
13
|
spec.homepage = 'https://github.com/hernanirvaz/bct'
|
14
14
|
spec.license = 'MIT'
|
data/lib/bct.rb
CHANGED
@@ -1,18 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require('thor')
|
4
|
+
require('bct/apies')
|
5
|
+
require('bct/apigm')
|
6
|
+
require('bct/bigquery1')
|
7
|
+
require('bct/bigquery2')
|
8
|
+
require('bct/etherscan1')
|
9
|
+
require('bct/etherscan2')
|
10
|
+
require('bct/greymass1')
|
11
|
+
require('bct/greymass2')
|
4
12
|
require('bct/version')
|
5
13
|
|
6
14
|
module Bct
|
7
|
-
|
15
|
+
# classe para erros desta gem
|
16
|
+
class Erro < StandardError
|
17
|
+
# @return [StandardError] personalizacao dos erros
|
18
|
+
def initialize(msg)
|
19
|
+
super(msg)
|
20
|
+
end
|
21
|
+
end
|
8
22
|
|
9
|
-
# classe para carregar/mostrar dados transacoes
|
23
|
+
# classe para carregar/mostrar dados transacoes eth & eos no bigquery
|
10
24
|
class CLI < Thor
|
11
25
|
desc 'work', 'carrega transacoes novas no bigquery'
|
12
26
|
option :h, type: :hash, default: {}, desc: 'configuracao ajuste reposicionamento temporal'
|
13
27
|
# carrega transacoes novas no bigquery
|
14
28
|
def work
|
15
|
-
Bigquery.new(options).processa_tudo
|
29
|
+
Bct::Bigquery.new(options).processa_tudo
|
16
30
|
end
|
17
31
|
|
18
32
|
desc 'show', 'mostra resumo transacoes'
|
@@ -20,7 +34,7 @@ module Bct
|
|
20
34
|
option :t, type: :boolean, default: false, desc: 'mostra transacoes todas ou somente novas'
|
21
35
|
# mostra resumo transacoes
|
22
36
|
def show
|
23
|
-
Bigquery.new(options).mostra_tudo
|
37
|
+
Bct::Bigquery.new(options).mostra_tudo
|
24
38
|
end
|
25
39
|
|
26
40
|
default_task :show
|
data/lib/bct/apies.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('faraday')
|
4
|
+
require('json')
|
5
|
+
|
6
|
+
# @author Hernani Rodrigues Vaz
|
7
|
+
module Bct
|
8
|
+
# classe para acesso dados blockchain ETH
|
9
|
+
class Apies
|
10
|
+
# @return [String] apikey a juntar aos pedidos HTTP url:
|
11
|
+
attr_reader :key
|
12
|
+
# @return [String] base URL to use as a prefix for all requests
|
13
|
+
attr_reader :url
|
14
|
+
|
15
|
+
# @param [String] chv apikey a juntar aos pedidos HTTP url:
|
16
|
+
# @param [String] www base URL to use as a prefix for all requests
|
17
|
+
# @return [Apies] API etherscan base
|
18
|
+
def initialize(chv: ENV['ETHERSCAN_API_KEY'], www: 'https://api.etherscan.io/')
|
19
|
+
@key = chv
|
20
|
+
@url = www
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [<Symbol>] adapter for the connection - default :net_http
|
24
|
+
def adapter
|
25
|
+
@adapter ||= Faraday.default_adapter
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [<Faraday::Connection>] connection object with an URL & adapter
|
29
|
+
def conn
|
30
|
+
@conn ||=
|
31
|
+
Faraday.new(url: url) do |c|
|
32
|
+
c.request(:url_encoded)
|
33
|
+
c.adapter(adapter)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @example
|
38
|
+
# [
|
39
|
+
# { account: '0x...', balance: '4000000000000000000' },
|
40
|
+
# { account: '0x...', balance: '87000000000000000000' }
|
41
|
+
# ]
|
42
|
+
# @param [String] ads lista enderecos carteiras ETH (max 20)
|
43
|
+
# @return [Array<Hash>] devolve lista com dados & saldo de carteiras ETH
|
44
|
+
def account(ads)
|
45
|
+
raise(Erro, 'maximo de 20 enderecos') if ads.size > 20
|
46
|
+
|
47
|
+
get(action: 'balancemulti', address: ads.join(','), tag: 'latest')[:result]
|
48
|
+
end
|
49
|
+
|
50
|
+
# @example
|
51
|
+
# [
|
52
|
+
# {
|
53
|
+
# blockNumber: '4984535',
|
54
|
+
# timeStamp: '1517094794',
|
55
|
+
# hash: '0x...',
|
56
|
+
# nonce: '10',
|
57
|
+
# blockHash: '0x...',
|
58
|
+
# transactionIndex: '17',
|
59
|
+
# from: '0x...',
|
60
|
+
# to: '0x...',
|
61
|
+
# value: '52627271000000000000',
|
62
|
+
# gas: '21000',
|
63
|
+
# gasPrice: '19000000000',
|
64
|
+
# isError: '0',
|
65
|
+
# txreceipt_status: '1',
|
66
|
+
# input: '0x',
|
67
|
+
# contractAddress: '',
|
68
|
+
# gasUsed: '21000',
|
69
|
+
# cumulativeGasUsed: '566293',
|
70
|
+
# confirmations: '5848660'
|
71
|
+
# },
|
72
|
+
# {}
|
73
|
+
# ]
|
74
|
+
# @param [String] add endereco carteira ETH
|
75
|
+
# @param [Hash] arg argumentos trabalho
|
76
|
+
# @option arg [Integer] :start_block starting blockNo to retrieve results
|
77
|
+
# @option arg [Integer] :end_block ending blockNo to retrieve results
|
78
|
+
# @option arg [String] :sort asc -> ascending order, desc -> descending order
|
79
|
+
# @option arg [Integer] :page to get paginated results
|
80
|
+
# @option arg [Integer] :offset max records to return
|
81
|
+
# @return [Array<Hash>] lista de transacoes
|
82
|
+
def norml_tx(add, **arg)
|
83
|
+
raise(Erro, 'endereco tem de ser definido') if add.nil? || add.empty?
|
84
|
+
|
85
|
+
ledger(**arg.merge(action: 'txlist', address: add))
|
86
|
+
end
|
87
|
+
|
88
|
+
# @example registo duplicado
|
89
|
+
# [
|
90
|
+
# {
|
91
|
+
# blockNumber: '3967652',
|
92
|
+
# timeStamp: '1499081515',
|
93
|
+
# hash: '0x registo duplicado com todos os dados iguais',
|
94
|
+
# nonce: '3',
|
95
|
+
# blockHash: '0x00a49e999036dc13dc6c4244bb1d51d3146fe7f00bfb500a7624d82e299c7328',
|
96
|
+
# from: '0xd0a6e6c54dbc68db5db3a091b171a77407ff7ccf',
|
97
|
+
# contractAddress: '0x86fa049857e0209aa7d9e616f7eb3b3b78ecfdb0',
|
98
|
+
# to: '0x...',
|
99
|
+
# value: '0',
|
100
|
+
# tokenName: 'EOS',
|
101
|
+
# tokenSymbol: 'EOS',
|
102
|
+
# tokenDecimal: '18',
|
103
|
+
# transactionIndex: '83',
|
104
|
+
# gas: '173399',
|
105
|
+
# gasPrice: '21000000000',
|
106
|
+
# gasUsed: '173398',
|
107
|
+
# input: 'deprecated',
|
108
|
+
# cumulativeGasUsed: '7484878',
|
109
|
+
# confirmations: '3442641'
|
110
|
+
# },
|
111
|
+
# {}
|
112
|
+
# ]
|
113
|
+
# @param add (see norml_tx)
|
114
|
+
# @param [String] cdd token address (nil to get a list of all ERC20 transactions)
|
115
|
+
# @param arg (see norml_tx)
|
116
|
+
# @option arg (see norml_tx)
|
117
|
+
# @return [Array<Hash>] lista de token transfer events
|
118
|
+
def token_tx(add, cdd = nil, **arg)
|
119
|
+
raise(Erro, 'contrato ou endereco tem de estar definido') if (cdd || add).nil? || (cdd || add).empty?
|
120
|
+
|
121
|
+
# registos duplicados aparecem em token events (ver exemplo acima)
|
122
|
+
# -quando ha erros na blockchain (acho)
|
123
|
+
# -quando ha token events identicos no mesmo block (acho)
|
124
|
+
ledger(**arg.merge(action: 'tokentx', address: add, contractaddress: cdd))
|
125
|
+
end
|
126
|
+
|
127
|
+
# @param [Integer] pag pagina das transacoes a devolver
|
128
|
+
# @param [Array<Hash>] ary lista acumuladora das transacoes a devolver
|
129
|
+
# @param arg (see norml_tx)
|
130
|
+
# @option arg (see norml_tx)
|
131
|
+
# @return [Array<Hash>] devolve lista de transacoes/token transfer events
|
132
|
+
def ledger(pag = 0, ary = [], **arg)
|
133
|
+
r = get(**arg.merge(page: pag + 1, offset: 10_000))[:result]
|
134
|
+
ary += r
|
135
|
+
r.count < 10_000 ? ary : ledger(pag + 1, ary, **arg)
|
136
|
+
rescue StandardError
|
137
|
+
ary
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# @example
|
143
|
+
# {
|
144
|
+
# status: '1',
|
145
|
+
# message: 'OK',
|
146
|
+
# result: []
|
147
|
+
# }
|
148
|
+
# @return [Hash] resultado do HTTP request
|
149
|
+
def get(**arg)
|
150
|
+
JSON.parse(
|
151
|
+
(conn.get('api') do |o|
|
152
|
+
o.headers = { content_type: 'application/json', accept: 'application/json', user_agent: 'etherscan;ruby' }
|
153
|
+
o.params = arg.merge(module: 'account', apikey: key).reject { |_, v| v.nil? }
|
154
|
+
end).body,
|
155
|
+
symbolize_names: true
|
156
|
+
)
|
157
|
+
rescue StandardError
|
158
|
+
{ result: [] }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/bct/apigm.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('faraday')
|
4
|
+
require('json')
|
5
|
+
|
6
|
+
module Bct
|
7
|
+
# classe para acesso dados blockchain EOS
|
8
|
+
class Apigm
|
9
|
+
# @return [String] base URL to use as a prefix for all requests
|
10
|
+
attr_reader :url
|
11
|
+
|
12
|
+
# @param [String] www base URL to use as a prefix for all requests
|
13
|
+
# @return [Apigm] acesso dados blockchain EOS
|
14
|
+
def initialize(www: 'https://eos.greymass.com')
|
15
|
+
@url = www
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [<Symbol>] adapter for the connection - default :net_http
|
19
|
+
def adapter
|
20
|
+
@adapter ||= Faraday.default_adapter
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [<Faraday::Connection>] connection object with an URL & adapter
|
24
|
+
def conn
|
25
|
+
@conn ||=
|
26
|
+
Faraday.new(url: url) do |c|
|
27
|
+
c.request(:url_encoded)
|
28
|
+
c.adapter(adapter)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @example
|
33
|
+
# {
|
34
|
+
# account_name: '...',
|
35
|
+
# head_block_num: 141_391_122,
|
36
|
+
# head_block_time: '2020-09-11T16:05:51.000',
|
37
|
+
# privileged: false,
|
38
|
+
# last_code_update: '1970-01-01T00:00:00.000',
|
39
|
+
# created: '2018-06-09T13:14:37.000',
|
40
|
+
# core_liquid_balance: '1232.0228 EOS',
|
41
|
+
# ram_quota: 9548,
|
42
|
+
# net_weight: 10_001_142,
|
43
|
+
# cpu_weight: 10_001_144,
|
44
|
+
# net_limit: { used: 0, available: 1_066_648_346, max: 1_066_648_346 },
|
45
|
+
# cpu_limit: { used: 338, available: 88_498, max: 88_836 },
|
46
|
+
# ram_usage: 3574,
|
47
|
+
# permissions: [
|
48
|
+
# {
|
49
|
+
# perm_name: 'active',
|
50
|
+
# parent: 'owner',
|
51
|
+
# required_auth: {
|
52
|
+
# threshold: 1, keys: [{ key: 'EOS...', weight: 1 }], accounts: [], waits: []
|
53
|
+
# }
|
54
|
+
# },
|
55
|
+
# {
|
56
|
+
# perm_name: 'owner',
|
57
|
+
# parent: '',
|
58
|
+
# required_auth: {
|
59
|
+
# threshold: 1, keys: [{ key: 'EOS...', weight: 1 }], accounts: [], waits: []
|
60
|
+
# }
|
61
|
+
# }
|
62
|
+
# ],
|
63
|
+
# total_resources: { owner: '...', net_weight: '1000.1142 EOS', cpu_weight: '1000.1144 EOS', ram_bytes: 8148 },
|
64
|
+
# self_delegated_bandwidth: { from: '...', to: '...', net_weight: '1000.1142 EOS', cpu_weight: '1000.1144 EOS' },
|
65
|
+
# refund_request: nil,
|
66
|
+
# voter_info: {
|
67
|
+
# owner: '...',
|
68
|
+
# proxy: '...',
|
69
|
+
# producers: [],
|
70
|
+
# staked: 20_002_286,
|
71
|
+
# last_vote_weight: '17172913021904.12109375000000000',
|
72
|
+
# proxied_vote_weight: '0.00000000000000000',
|
73
|
+
# is_proxy: 0,
|
74
|
+
# flags1: 0,
|
75
|
+
# reserved2: 0,
|
76
|
+
# reserved3: '0.0000 EOS'
|
77
|
+
# },
|
78
|
+
# rex_info: nil
|
79
|
+
# }
|
80
|
+
# @param [Hash] arg argumentos trabalho
|
81
|
+
# @option arg [String] :account_name endereco carteira EOS
|
82
|
+
# @return [Hash] dados & saldo duma carteira EOS
|
83
|
+
def account(**arg)
|
84
|
+
raise(Erro, 'endereco tem de ser definido') if arg[:account_name].nil? || arg[:account_name].empty?
|
85
|
+
|
86
|
+
get('/v1/chain/get_account', **arg)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @example
|
90
|
+
# {
|
91
|
+
# actions: [
|
92
|
+
# {
|
93
|
+
# account_action_seq: 964,
|
94
|
+
# action_trace: {
|
95
|
+
# account_ram_deltas: [],
|
96
|
+
# act: {
|
97
|
+
# account: 'voicebestapp',
|
98
|
+
# authorization: [
|
99
|
+
# { actor: 'thetruevoice', permission: 'active' },
|
100
|
+
# { actor: 'voicebestapp', permission: 'active' }
|
101
|
+
# ],
|
102
|
+
# data: { from: 'voicebestapp', memo: '...', quantity: '1.0001 MESSAGE', to: '...' },
|
103
|
+
# hex_data: '...',
|
104
|
+
# name: 'transfer'
|
105
|
+
# },
|
106
|
+
# action_ordinal: 10,
|
107
|
+
# block_num: 141_345_345,
|
108
|
+
# block_time: '2020-09-11T09:44:04.500',
|
109
|
+
# closest_unnotified_ancestor_action_ordinal: 5,
|
110
|
+
# context_free: false,
|
111
|
+
# creator_action_ordinal: 5,
|
112
|
+
# elapsed: 6,
|
113
|
+
# producer_block_id: '...',
|
114
|
+
# receipt: {
|
115
|
+
# abi_sequence: 1,
|
116
|
+
# act_digest: '...',
|
117
|
+
# auth_sequence: [['thetruevoice', 6_778_215], ['voicebestapp', 435_346]],
|
118
|
+
# code_sequence: 1,
|
119
|
+
# global_sequence: 233_283_589_258,
|
120
|
+
# receiver: '...',
|
121
|
+
# recv_sequence: 927
|
122
|
+
# },
|
123
|
+
# receiver: '...',
|
124
|
+
# trx_id: '...'
|
125
|
+
# },
|
126
|
+
# block_num: 141_345_345,
|
127
|
+
# block_time: '2020-09-11T09:44:04.500',
|
128
|
+
# global_action_seq: 233_283_589_258,
|
129
|
+
# irreversible: true
|
130
|
+
# },
|
131
|
+
# {}
|
132
|
+
# ],
|
133
|
+
# head_block_num: 141_721_698,
|
134
|
+
# last_irreversible_block: 141_721_371
|
135
|
+
# }
|
136
|
+
# @param [String] add endereco carteira EOS
|
137
|
+
# @param [Hash] arg argumentos trabalho
|
138
|
+
# @option arg [String] :account_name endereco carteira EOS
|
139
|
+
# @option arg [Integer] :pos posicao da primeira transacao a devolver
|
140
|
+
# @option arg [Integer] :offset numero maximo transacoes a devolver
|
141
|
+
# @option arg [String] :filter filtro a aplicar na resposta
|
142
|
+
# @option arg [String] :sort ordenacao asc/desc
|
143
|
+
# @option arg [String] :after time inicio "2020-09-13T13:44:03.105Z"
|
144
|
+
# @option arg [String] :before time fim "2020-09-13T13:44:03.105Z"
|
145
|
+
# @option arg [Integer] :parent transacao pai
|
146
|
+
# @return [Array<Hash>] devolve lista de transacoes
|
147
|
+
def all_tx(add, **arg)
|
148
|
+
raise(Erro, 'endereco tem de ser definido') if add.nil? || add.empty?
|
149
|
+
|
150
|
+
ledger(**arg.merge(account_name: add))
|
151
|
+
end
|
152
|
+
|
153
|
+
# @param [Integer] pos posicao das transacoes a devolver
|
154
|
+
# @param [Array<Hash>] ary lista acumuladora das transacoes a devolver
|
155
|
+
# @param arg (see all_tx)
|
156
|
+
# @option arg (see all_tx)
|
157
|
+
# @return [Array<Hash>] lista das transacoes ligadas a uma carteira EOS
|
158
|
+
def ledger(pos = 0, ary = [], **arg)
|
159
|
+
r = get('/v1/history/get_actions', **arg.merge(pos: pos, offset: 100))[:actions]
|
160
|
+
ary += r
|
161
|
+
r.count < 100 ? ary : ledger(pos + r.count, ary, **arg)
|
162
|
+
rescue StandardError
|
163
|
+
ary
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
# @param [String] uri identificacao do recurso a questionar
|
169
|
+
# @param arg (see all_tx)
|
170
|
+
# @option arg (see all_tx)
|
171
|
+
# @return [Hash] resultado do HTTP request
|
172
|
+
def get(uri, **arg)
|
173
|
+
JSON.parse(
|
174
|
+
conn.post(uri, arg.to_json, content_type: 'application/json').body,
|
175
|
+
symbolize_names: true
|
176
|
+
)
|
177
|
+
rescue StandardError
|
178
|
+
{ actions: [] }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('google/cloud/bigquery')
|
4
|
+
require('bigdecimal/util')
|
5
|
+
|
6
|
+
# @author Hernani Rodrigues Vaz
|
7
|
+
module Bct
|
8
|
+
BD = 'hernanirvaz.coins'
|
9
|
+
|
10
|
+
# (see Bigquery)
|
11
|
+
class Bigquery
|
12
|
+
# @return [Google::Cloud::Bigquery] API bigquery
|
13
|
+
attr_reader :api
|
14
|
+
# @return [Google::Cloud::Bigquery::QueryJob] job bigquery
|
15
|
+
attr_reader :job
|
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)
|
27
|
+
# usa env GOOGLE_APPLICATION_CREDENTIALS para obter credentials
|
28
|
+
# @see https://cloud.google.com/bigquery/docs/authentication/getting-started
|
29
|
+
@api = Google::Cloud::Bigquery.new
|
30
|
+
@ops = pop
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Etherscan] API etherscan
|
34
|
+
def apies
|
35
|
+
@apies ||= Etherscan.new(
|
36
|
+
{
|
37
|
+
wb: sql("select * from #{BD}.walletEth order by 2"),
|
38
|
+
nt: sql("select itx,iax from #{BD}.ethtx"),
|
39
|
+
nk: sql("select itx,iax from #{BD}.ethkx")
|
40
|
+
},
|
41
|
+
ops
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Greymass] API greymass
|
46
|
+
def apigm
|
47
|
+
@apigm ||= Greymass.new(
|
48
|
+
{
|
49
|
+
wb: sql("select * from #{BD}.walletEos order by 2"),
|
50
|
+
nt: sql("select itx,iax from #{BD}.eostx")
|
51
|
+
},
|
52
|
+
ops
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# mostra resumos e transacoes etherscan & greymass
|
57
|
+
def mostra_tudo
|
58
|
+
apies.mostra_resumo
|
59
|
+
apigm.mostra_resumo
|
60
|
+
end
|
61
|
+
|
62
|
+
# insere transacoes novas nas tabelas todas etht, ethk, eos
|
63
|
+
def processa_tudo
|
64
|
+
processa_eth
|
65
|
+
puts(format("%<n>2i LINHAS INSERIDAS #{BD}.eos ", n: apigm.novax.count.positive? ? dml(eost_ins) : 0))
|
66
|
+
end
|
67
|
+
|
68
|
+
# insere transacoes novas nas tabelas etht (trx normais), ethk (trx token)
|
69
|
+
def processa_eth
|
70
|
+
puts(format("%<n>2i LINHAS INSERIDAS #{BD}.etht", n: apies.novtx.count.positive? ? dml(etht_ins) : 0))
|
71
|
+
puts(format("%<n>2i LINHAS INSERIDAS #{BD}.ethk", n: apies.novkx.count.positive? ? dml(ethk_ins) : 0))
|
72
|
+
end
|
73
|
+
|
74
|
+
# cria job bigquery & verifica execucao
|
75
|
+
#
|
76
|
+
# @param cmd (see sql)
|
77
|
+
# @return [Boolean] job ok?
|
78
|
+
def job?(cmd)
|
79
|
+
@job = api.query_job(cmd)
|
80
|
+
@job.wait_until_done!
|
81
|
+
puts(@job.error['message']) if @job.failed?
|
82
|
+
@job.failed?
|
83
|
+
end
|
84
|
+
|
85
|
+
# cria Structured Query Language (SQL) job bigquery
|
86
|
+
#
|
87
|
+
# @param [String] cmd comando SQL a executar
|
88
|
+
# @param [String] red resultado quando SQL tem erro
|
89
|
+
# @return [Google::Cloud::Bigquery::Data] resultado do SQL
|
90
|
+
def sql(cmd, red = [])
|
91
|
+
@sqr = job?(cmd) ? red : job.data
|
92
|
+
end
|
93
|
+
|
94
|
+
# cria Data Manipulation Language (DML) job bigquery
|
95
|
+
#
|
96
|
+
# @param cmd (see sql)
|
97
|
+
# @return [Integer] numero linhas afetadas
|
98
|
+
def dml(cmd)
|
99
|
+
job?(cmd) ? 0 : job.num_dml_affected_rows
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('google/cloud/bigquery')
|
4
|
+
require('bigdecimal/util')
|
5
|
+
|
6
|
+
# @author Hernani Rodrigues Vaz
|
7
|
+
module Bct
|
8
|
+
# classe para processar etherscan/greymass & bigquery
|
9
|
+
class Bigquery
|
10
|
+
# @return [String] comando insert SQL formatado etht (trx normais)
|
11
|
+
def etht_ins
|
12
|
+
"insert #{BD}.etht(blocknumber,timestamp,txhash,nonce,blockhash,transactionindex,axfrom,axto,iax," \
|
13
|
+
'value,gas,gasprice,gasused,iserror,txreceipt_status,input,contractaddress,dias' \
|
14
|
+
") VALUES#{apies.novtx.map { |e| etht_val1(e) }.join(',')}"
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String] valores formatados etht (trx normais parte1)
|
18
|
+
def etht_val1(htx)
|
19
|
+
"(#{Integer(htx[:blockNumber])}," \
|
20
|
+
"#{Integer(htx[:timeStamp])}," \
|
21
|
+
"'#{htx[:hash]}'," \
|
22
|
+
"#{Integer(htx[:nonce])}," \
|
23
|
+
"'#{htx[:blockHash]}'," \
|
24
|
+
"#{Integer(htx[:transactionIndex])}," \
|
25
|
+
"'#{htx[:from]}'," \
|
26
|
+
"'#{htx[:to]}'," \
|
27
|
+
"'#{htx[:iax]}'," \
|
28
|
+
"#{etht_val2(htx)}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String] valores formatados etht (trx normais parte2)
|
32
|
+
def etht_val2(htx)
|
33
|
+
"cast('#{htx[:value]}' as numeric)," \
|
34
|
+
"cast('#{htx[:gas]}' as numeric)," \
|
35
|
+
"cast('#{htx[:gasPrice]}' as numeric)," \
|
36
|
+
"cast('#{htx[:gasUsed]}' as numeric)," \
|
37
|
+
"#{Integer(htx[:isError])}," \
|
38
|
+
"#{htx[:txreceipt_status].length.zero? ? 'null' : htx[:txreceipt_status]}," \
|
39
|
+
"#{etht_val3(htx)}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [String] valores formatados etht (trx normais parte3)
|
43
|
+
def etht_val3(htx)
|
44
|
+
"#{htx[:input].length.zero? ? 'null' : "'#{htx[:input]}'"}," \
|
45
|
+
"#{htx[:contractAddress].length.zero? ? 'null' : "'#{htx[:contractAddress]}'"}," \
|
46
|
+
"#{Integer(ops[:h][htx[:blockNumber]] || 0)})"
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String] comando insert SQL formatado ethk (trx token)
|
50
|
+
def ethk_ins
|
51
|
+
"insert #{BD}.ethk(blocknumber,timestamp,txhash,nonce,blockhash,transactionindex,axfrom,axto,iax," \
|
52
|
+
'value,tokenname,tokensymbol,tokendecimal,gas,gasprice,gasused,input,contractaddress,dias' \
|
53
|
+
") VALUES#{apies.novkx.map { |e| ethk_val1(e) }.join(',')}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] valores formatados ethk (trx token parte1)
|
57
|
+
def ethk_val1(hkx)
|
58
|
+
"(#{Integer(hkx[:blockNumber])}," \
|
59
|
+
"#{Integer(hkx[:timeStamp])}," \
|
60
|
+
"'#{hkx[:hash]}'," \
|
61
|
+
"#{Integer(hkx[:nonce])}," \
|
62
|
+
"'#{hkx[:blockHash]}'," \
|
63
|
+
"#{Integer(hkx[:transactionIndex])}," \
|
64
|
+
"'#{hkx[:from]}'," \
|
65
|
+
"'#{hkx[:to]}'," \
|
66
|
+
"'#{hkx[:iax]}'," \
|
67
|
+
"#{ethk_val2(hkx)}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [String] valores formatados ethk (trx token parte2)
|
71
|
+
def ethk_val2(hkx)
|
72
|
+
"cast('#{hkx[:value]}' as numeric)," \
|
73
|
+
"'#{hkx[:tokenName]}'," \
|
74
|
+
"'#{hkx[:tokenSymbol]}'," \
|
75
|
+
"#{Integer(hkx[:tokenDecimal])}," \
|
76
|
+
"cast('#{hkx[:gas]}' as numeric)," \
|
77
|
+
"cast('#{hkx[:gasPrice]}' as numeric)," \
|
78
|
+
"cast('#{hkx[:gasUsed]}' as numeric)," \
|
79
|
+
"#{ethk_val3(hkx)}"
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [String] valores formatados ethk (trx token parte3)
|
83
|
+
def ethk_val3(hkx)
|
84
|
+
"#{hkx[:input].length.zero? ? 'null' : "'#{hkx[:input]}'"}," \
|
85
|
+
"#{hkx[:contractAddress].length.zero? ? 'null' : "'#{hkx[:contractAddress]}'"}," \
|
86
|
+
"#{Integer(ops[:h][hkx[:blockNumber]] || 0)})"
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [String] comando insert SQL formatado eos
|
90
|
+
def eost_ins
|
91
|
+
"insert #{BD}.eos(gseq,aseq,bnum,time,contract,action,acfrom,acto,iax,amount,moeda,memo,dias" \
|
92
|
+
") VALUES#{apigm.novax.map { |e| eost_val1(e) }.join(',')}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param [Hash] htx transacao ligadas a uma carteira - sem elementos irrelevantes
|
96
|
+
# @return [String] valores formatados para insert eos (parte1)
|
97
|
+
def eost_val1(htx)
|
98
|
+
a = htx[:action_trace][:act]
|
99
|
+
"(#{htx[:global_action_seq]}," \
|
100
|
+
"#{htx[:account_action_seq]}," \
|
101
|
+
"#{htx[:block_num]}," \
|
102
|
+
"DATETIME(TIMESTAMP('#{htx[:block_time]}'))," \
|
103
|
+
"'#{a[:account]}'," \
|
104
|
+
"'#{a[:name]}'," \
|
105
|
+
"#{eost_val2(htx, a)}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param [Hash] htx transacao ligadas a uma carteira - sem elementos irrelevantes
|
109
|
+
# @return [String] valores formatados para insert eos (parte2)
|
110
|
+
def eost_val2(htx, act)
|
111
|
+
d = act[:data]
|
112
|
+
q = d[:quantity].to_s
|
113
|
+
s = d[:memo].inspect
|
114
|
+
"'#{d[:from]}'," \
|
115
|
+
"'#{d[:to]}'," \
|
116
|
+
"'#{htx[:iax]}'," \
|
117
|
+
"#{q.to_d},'#{q[/[[:upper:]]+/]}'," \
|
118
|
+
"nullif('#{s.gsub(/['"]/, '')}','nil')," \
|
119
|
+
"#{ops[:h][String(htx[:itx])] || 0})"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('bigdecimal/util')
|
4
|
+
|
5
|
+
# @author Hernani Rodrigues Vaz
|
6
|
+
module Bct
|
7
|
+
# chaves a eliminar da API - resultado deve ser ignirado pois muda a cada pedido API feito
|
8
|
+
DL = %i[cumulativeGasUsed confirmations].freeze
|
9
|
+
|
10
|
+
# (see Etherscan)
|
11
|
+
class Etherscan
|
12
|
+
# @return [Apies] API etherscan
|
13
|
+
attr_reader :api
|
14
|
+
# @return [Array<Hash>] todos os dados bigquery
|
15
|
+
attr_reader :dbq
|
16
|
+
# @return [Thor::CoreExt::HashWithIndifferentAccess] opcoes trabalho
|
17
|
+
attr_reader :ops
|
18
|
+
|
19
|
+
# @param [Hash] dad todos os dados bigquery
|
20
|
+
# @param [Thor::CoreExt::HashWithIndifferentAccess] pop opcoes trabalho
|
21
|
+
# @option pop [Hash] :h ({}) configuracao dias ajuste reposicionamento temporal
|
22
|
+
# @option pop [Boolean] :v (false) mostra dados transacoes normais & tokens?
|
23
|
+
# @return [Etherscan] API etherscan - processar transacoes normais e tokens
|
24
|
+
def initialize(dad, pop)
|
25
|
+
@api = Apies.new
|
26
|
+
@dbq = dad
|
27
|
+
@ops = pop
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Array<String>] lista dos meus enderecos
|
31
|
+
def lax
|
32
|
+
@lax ||= dbq[:wb].map { |h| h[:ax] }
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<Hash>] todos os dados etherscan - saldos & transacoes
|
36
|
+
def dbc
|
37
|
+
@dbc ||= api.account(lax).map { |e| base_bc(e) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array<Hash>] todos os dados juntos bigquery & etherscan
|
41
|
+
def dados
|
42
|
+
@dados ||= dbq[:wb].map { |b| bq_bc(b, dbc.select { |s| b[:ax] == s[:ax] }.first) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Array<Integer>] lista indices transacoes normais novas
|
46
|
+
def bnt
|
47
|
+
@bnt ||= (dbc.map { |e| e[:tx].map { |n| n[:itx] } }.flatten - (ops[:t] ? [] : dbq[:nt].map { |t| t[:itx] }))
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Array<Integer>] lista indices transacoes token novas
|
51
|
+
def bnk
|
52
|
+
@bnk ||= (dbc.map { |e| e[:kx].map { |n| n[:itx] } }.flatten - (ops[:t] ? [] : dbq[:nk].map { |t| t[:itx] }))
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Array<Hash>] lista transacoes normais novas
|
56
|
+
def novtx
|
57
|
+
@novtx ||= dbc.map { |e| e[:tx].select { |n| bnt.include?(n[:itx]) } }.flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [Array<Hash>] lista transacoes token novas
|
61
|
+
def novkx
|
62
|
+
@novkx ||= dbc.map { |e| e[:kx].select { |n| bnk.include?(n[:itx]) } }.flatten
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [Hash] hbc dados etherscan
|
66
|
+
# @return [Hash] dados etherscan - address, saldo & transacoes
|
67
|
+
def base_bc(hbc)
|
68
|
+
a = hbc[:account]
|
69
|
+
{
|
70
|
+
ax: a,
|
71
|
+
sl: (hbc[:balance].to_d / 10**18).round(10),
|
72
|
+
tx: filtrar_tx(a, api.norml_tx(a)),
|
73
|
+
kx: filtrar_tx(a, api.token_tx(a))
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param [Hash] hbq dados bigquery
|
78
|
+
# @param hbc (see base_bc)
|
79
|
+
# @return [Hash] dados juntos bigquery & etherscan
|
80
|
+
def bq_bc(hbq, hbc)
|
81
|
+
{
|
82
|
+
id: hbq[:id],
|
83
|
+
ax: hbq[:ax],
|
84
|
+
bs: hbq[:sl],
|
85
|
+
bt: dbq[:nt].select { |t| t[:iax] == hbq[:ax] },
|
86
|
+
bk: dbq[:nk].select { |t| t[:iax] == hbq[:ax] },
|
87
|
+
es: hbc[:sl],
|
88
|
+
et: hbc[:tx],
|
89
|
+
ek: hbc[:kx]
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param [String] add endereco carteira ETH
|
94
|
+
# @param [Array<Hash>] ary lista das transacoes
|
95
|
+
# @return [Array<Hash>] devolve lista de transacoes/token transfer events filtrada
|
96
|
+
def filtrar_tx(add, ary)
|
97
|
+
# elimina transferencia from: (lax) to: (add) - esta transferencia aparece em from: (add) to: (lax)
|
98
|
+
# elimina chaves irrelevantes (DL) & adiciona chave indice itx & adiciona identificador da carteira iax
|
99
|
+
ary.delete_if { |h| h[:to] == add && lax.include?(h[:from]) }
|
100
|
+
.map { |h| h.delete_if { |k, _| DL.include?(k) }.merge(itx: Integer(h[:blockNumber]), iax: add) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [Array<Hash>] lista ordenada transacoes normais novas
|
104
|
+
def sortx
|
105
|
+
novtx.sort { |a, b| a[:itx] <=> b[:itx] }
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Array<Hash>] lista ordenada transacoes token novas
|
109
|
+
def sorkx
|
110
|
+
novkx.sort { |a, b| a[:itx] <=> b[:itx] }
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Array<Hash>] lista ordenada transacoes (normais & token) novas
|
114
|
+
def sorax
|
115
|
+
(novtx + novkx).sort { |a, b| a[:itx] <=> b[:itx] }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @author Hernani Rodrigues Vaz
|
4
|
+
module Bct
|
5
|
+
# classe para processar carteiras & transacoes normais e tokens
|
6
|
+
class Etherscan
|
7
|
+
# @param [Hash] hjn dados juntos bigquery & etherscan
|
8
|
+
# @return [String] texto formatado duma carteira
|
9
|
+
def formata_carteira(hjn)
|
10
|
+
format(
|
11
|
+
'%<s1>-6.6s %<s2>-32.32s ',
|
12
|
+
s1: hjn[:id],
|
13
|
+
s2: formata_endereco(hjn[:ax], 32)
|
14
|
+
) + formata_valores(hjn)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param (see formata_carteira)
|
18
|
+
# @return [String] texto formatado valores duma carteira
|
19
|
+
def formata_valores(hjn)
|
20
|
+
format(
|
21
|
+
'%<v1>11.6f %<n1>2i %<n3>2i %<v2>12.6f %<n2>2i %<n4>2i %<ok>-3s',
|
22
|
+
v1: hjn[:bs],
|
23
|
+
n1: hjn[:bt].count,
|
24
|
+
n3: hjn[:bk].count,
|
25
|
+
v2: hjn[:es],
|
26
|
+
n2: hjn[:et].count,
|
27
|
+
n4: hjn[:ek].count,
|
28
|
+
ok: ok?(hjn) ? 'OK' : 'NOK'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param (see formata_carteira)
|
33
|
+
# @return [Boolean] carteira tem transacoes novas(sim=NOK, nao=OK)?
|
34
|
+
def ok?(hjn)
|
35
|
+
hjn[:bs] == hjn[:es] && hjn[:bt].count == hjn[:et].count && hjn[:bk].count == hjn[:ek].count
|
36
|
+
end
|
37
|
+
|
38
|
+
# @example ether address inicio..fim
|
39
|
+
# 0x10f3a0cf0b534c..c033cf32e8a03586
|
40
|
+
# @param add (see filtrar_tx)
|
41
|
+
# @param [Integer] max chars a mostrar
|
42
|
+
# @return [String] endereco formatado
|
43
|
+
def formata_endereco(add, max)
|
44
|
+
i = Integer((max - 2) / 2)
|
45
|
+
e = (max <= 20 ? dbq[:wb].select { |s| s[:ax] == add }.first : nil) || { id: add }
|
46
|
+
max < 7 ? 'erro' : "#{e[:id][0, i - 3]}..#{add[-i - 3..]}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [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
|
+
# @param [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 %<vl>11.3f %<sy>-5.5s',
|
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 dados.count.positive?
|
79
|
+
|
80
|
+
puts("\nid address bigquery nm tk etherscan nm tk")
|
81
|
+
dados.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] && novtx.count.positive?
|
90
|
+
|
91
|
+
puts("\ntx normal from to data valor")
|
92
|
+
sortx.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] && novkx.count.positive?
|
98
|
+
|
99
|
+
puts("\ntx token from to data valor")
|
100
|
+
sorkx.each { |e| puts(formata_transacao_token(e)) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [String] texto configuracao ajuste dias das transacoes (normais & token)
|
104
|
+
def mostra_configuracao_ajuste_dias
|
105
|
+
return unless (novtx.count + novkx.count).positive?
|
106
|
+
|
107
|
+
puts("\nstring ajuste dias\n-h=#{sorax.map { |e| "#{e[:blockNumber]}:0" }.join(' ')}")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require('bigdecimal/util')
|
4
|
+
|
5
|
+
# @author Hernani Rodrigues Vaz
|
6
|
+
module Bct
|
7
|
+
# (see Greymass)
|
8
|
+
class Greymass
|
9
|
+
# @return [Apigm] API greymass
|
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?
|
20
|
+
# @option pop [Boolean] :t (false) mostra transacoes todas ou somente novas?
|
21
|
+
# @return [Greymass] API greymass - processar transacoes
|
22
|
+
def initialize(dad, pop)
|
23
|
+
@api = Apigm.new
|
24
|
+
@dbq = dad
|
25
|
+
@ops = pop
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Array<String>] lista dos meus enderecos
|
29
|
+
def lax
|
30
|
+
@lax ||= dbq[:wb].map { |h| h[:ax] }
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array<Hash>] todos os dados greymass - saldos & transacoes
|
34
|
+
def dbc
|
35
|
+
@dbc ||= dbq[:wb].map { |e| base_bc(e) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<Hash>] todos os dados juntos bigquery & greymass
|
39
|
+
def dados
|
40
|
+
@dados ||= dbq[:wb].map { |b| bq_bc(b, dbc.select { |s| b[:ax] == s[:ax] }.first) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Array<Integer>] lista indices transacoes novas
|
44
|
+
def bnt
|
45
|
+
@bnt ||= (dbc.map { |e| e[:tx].map { |n| n[:itx] } }.flatten - (ops[:t] ? [] : dbq[:nt].map { |t| t[:itx] }))
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Array<Hash>] lista transacoes novas
|
49
|
+
def novax
|
50
|
+
@novax ||= dbc.map { |e| e[:tx].select { |s| bnt.include?(s[:itx]) } }.flatten
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [Hash] hbq dados bigquery wallet
|
54
|
+
# @return [Hash] dados greymass - address, saldo & transacoes
|
55
|
+
def base_bc(hbq)
|
56
|
+
a = hbq[:ax]
|
57
|
+
{
|
58
|
+
ax: a,
|
59
|
+
sl: greymass_sl(a).inject(:+),
|
60
|
+
tx: filtrar_tx(a, api.all_tx(a))
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param hbq (see base_bc)
|
65
|
+
# @param [Hash] hbc dados greymass
|
66
|
+
# @return [Hash] dados juntos bigquery & greymass
|
67
|
+
def bq_bc(hbq, hbc)
|
68
|
+
{
|
69
|
+
id: hbq[:id],
|
70
|
+
ax: hbq[:ax],
|
71
|
+
bs: hbq[:sl],
|
72
|
+
bt: dbq[:nt].select { |t| t[:iax] == hbq[:ax] },
|
73
|
+
es: hbc[:sl],
|
74
|
+
et: hbc[:tx]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param (see filtrar_tx)
|
79
|
+
# @return [Array<BigDecimal>] lista recursos - liquido, net, spu
|
80
|
+
def greymass_sl(add)
|
81
|
+
v = api.account(account_name: add)
|
82
|
+
[
|
83
|
+
v[:core_liquid_balance].to_d,
|
84
|
+
v[:total_resources][:net_weight].to_d,
|
85
|
+
v[:total_resources][:cpu_weight].to_d
|
86
|
+
]
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param [String] add endereco carteira EOS
|
90
|
+
# @param [Array<Hash>] ary lista das transacoes
|
91
|
+
# @return [Array<Hash>] lista transacoes ligadas a uma carteira filtrada
|
92
|
+
def filtrar_tx(add, ary)
|
93
|
+
# elimina transferencia from: (lax) to: (add) - esta transferencia aparece em from: (add) to: (lax)
|
94
|
+
# adiciona chave indice itx & adiciona identificador da carteira iax
|
95
|
+
ary.delete_if { |h| act_data(h)[:to] == add && lax.include?(act_data(h)[:from]) }
|
96
|
+
.map { |h| h.merge(itx: h[:global_action_seq], iax: add) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [Array<Hash>] lista ordenada transacoes novas
|
100
|
+
def sorax
|
101
|
+
novax.sort { |a, b| b[:itx] <=> a[:itx] }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @author Hernani Rodrigues Vaz
|
4
|
+
module Bct
|
5
|
+
# classe para processar carteiras & transacoes
|
6
|
+
class Greymass
|
7
|
+
# @param [Hash] hjn dados juntos bigquery & greymass
|
8
|
+
# @return [String] texto formatado duma carteira
|
9
|
+
def formata_carteira(hjn)
|
10
|
+
format(
|
11
|
+
'%<s1>-12.12s %<v1>14.4f %<n1>4i %<v2>14.4f %<n2>4i %<ok>-3s',
|
12
|
+
s1: hjn[:ax],
|
13
|
+
v1: hjn[:bs],
|
14
|
+
n1: hjn[:bt].count,
|
15
|
+
v2: hjn[:es],
|
16
|
+
n2: hjn[:et].count,
|
17
|
+
ok: ok?(hjn) ? 'OK' : 'NOK'
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param (see formata_carteira)
|
22
|
+
# @return [Boolean] carteira tem transacoes novas(sim=NOK, nao=OK)?
|
23
|
+
def ok?(hjn)
|
24
|
+
hjn[:bs] == hjn[:es] && hjn[:bt].count == hjn[:et].count
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Hash] htx transacao
|
28
|
+
# @return [String] texto formatado transacao
|
29
|
+
def formata_transacao(htx)
|
30
|
+
format(
|
31
|
+
'%<bn>12i %<fr>-12.12s %<to>-12.12s %<ac>-10.10s %<dt>10.10s %<vl>12.4f %<sy>-6.6s',
|
32
|
+
bn: htx[:itx],
|
33
|
+
fr: act_data(htx)[:from],
|
34
|
+
to: act_data(htx)[:to],
|
35
|
+
ac: act(htx)[:name],
|
36
|
+
dt: Date.parse(htx[:block_time]),
|
37
|
+
vl: act_data_quantity(htx).to_d,
|
38
|
+
sy: act_data_quantity(htx)[/[[:upper:]]+/]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param (see formata_transacao)
|
43
|
+
# @return [Hash] dados da acao
|
44
|
+
def act(htx)
|
45
|
+
htx[:action_trace][:act]
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param (see formata_transacao)
|
49
|
+
# @return [Hash] dados da acao
|
50
|
+
def act_data(htx)
|
51
|
+
act(htx)[:data]
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param (see formata_transacao)
|
55
|
+
# @return [String] dados da quantidade
|
56
|
+
def act_data_quantity(htx)
|
57
|
+
act_data(htx)[:quantity].to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [String] texto carteiras & transacoes & ajuste dias
|
61
|
+
def mostra_resumo
|
62
|
+
return unless dados.count.positive?
|
63
|
+
|
64
|
+
puts("\naddress bigquery ntx greymass ntx")
|
65
|
+
dados.each { |e| puts(formata_carteira(e)) }
|
66
|
+
mostra_transacoes_novas
|
67
|
+
mostra_configuracao_ajuste_dias
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [String] texto transacoes
|
71
|
+
def mostra_transacoes_novas
|
72
|
+
return unless ops[:v] && novax.count.positive?
|
73
|
+
|
74
|
+
puts("\nsequence num from to accao data valor moeda")
|
75
|
+
sorax.each { |e| puts(formata_transacao(e)) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [String] texto configuracao ajuste dias das transacoes
|
79
|
+
def mostra_configuracao_ajuste_dias
|
80
|
+
return unless novax.count.positive?
|
81
|
+
|
82
|
+
puts("\nstring ajuste dias\n-h=#{sorax.map { |e| "#{e[:itx]}:0" }.join(' ')}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/bct/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
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-09-
|
11
|
+
date: 2020-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,8 +94,8 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
-
description: Arquiva transactions etherscan/
|
98
|
-
reposicionamento temporal.
|
97
|
+
description: Arquiva transactions etherscan/greymass no bigquery. Pode ajustar dias
|
98
|
+
para reposicionamento temporal.
|
99
99
|
email:
|
100
100
|
- hernanirvaz@gmail.com
|
101
101
|
executables:
|
@@ -117,6 +117,14 @@ files:
|
|
117
117
|
- bin/setup
|
118
118
|
- exe/bct
|
119
119
|
- lib/bct.rb
|
120
|
+
- lib/bct/apies.rb
|
121
|
+
- lib/bct/apigm.rb
|
122
|
+
- lib/bct/bigquery1.rb
|
123
|
+
- lib/bct/bigquery2.rb
|
124
|
+
- lib/bct/etherscan1.rb
|
125
|
+
- lib/bct/etherscan2.rb
|
126
|
+
- lib/bct/greymass1.rb
|
127
|
+
- lib/bct/greymass2.rb
|
120
128
|
- lib/bct/version.rb
|
121
129
|
homepage: https://github.com/hernanirvaz/bct
|
122
130
|
licenses:
|
@@ -142,5 +150,5 @@ requirements: []
|
|
142
150
|
rubygems_version: 3.1.2
|
143
151
|
signing_key:
|
144
152
|
specification_version: 4
|
145
|
-
summary: Arquiva transactions etherscan/
|
153
|
+
summary: Arquiva transactions etherscan/greymass no bigquery.
|
146
154
|
test_files: []
|