bct 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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 [](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: []
|