erc20 0.1.4 → 0.1.5
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/.rubocop.yml +0 -1
- data/Gemfile +2 -2
- data/Gemfile.lock +4 -7
- data/Rakefile +0 -1
- data/lib/erc20/erc20.rb +1 -1
- data/lib/erc20/wallet.rb +82 -68
- data/test/erc20/test_wallet.rb +55 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01ef1fcd61cd21540d932e28c65ed8afa08891e0cc20b498202eb146809d4993
|
4
|
+
data.tar.gz: c8e4593fb968dcfbad8e27d24aa974a37946b444eb42b4022139c48c51ffc5ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38e4e84ea00f176c34a9f3f15ff0e77473f31fb29196b72d7e7b70af38e7aa8877015ff3249822b55665dfadac2cecb909a46d4c62554bcf5c85d8e1a1d30744
|
7
|
+
data.tar.gz: ffc78c5dfe3817bb59481f6e27e5e5fe7539ffca1e931fe8752fa31e98fee9e4c1880a2d3ce40496549c16b764524c0fad70c147d6f8a7467d8f1ba9c990d6dc
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
@@ -9,12 +9,13 @@ gemspec
|
|
9
9
|
gem 'backtrace', '>0', require: false
|
10
10
|
gem 'concurrent-ruby', '~>1.2', require: false
|
11
11
|
gem 'cucumber', '~>9.2', require: false
|
12
|
-
gem 'donce', '
|
12
|
+
gem 'donce', '>=0.2.3', require: false
|
13
13
|
gem 'faraday', '>0', require: false
|
14
14
|
gem 'loog', '>0', require: false
|
15
15
|
gem 'minitest', '~>5.25', require: false
|
16
16
|
gem 'minitest-reporters', '~>1.7', require: false
|
17
17
|
gem 'minitest-retry', '~>0.2', require: false
|
18
|
+
gem 'os', '>0', require: false
|
18
19
|
gem 'qbash', '>0', require: false
|
19
20
|
gem 'rake', '~>13.2', require: false
|
20
21
|
gem 'random-port', '>0', require: false
|
@@ -22,7 +23,6 @@ gem 'rubocop', '~>1.75', require: false
|
|
22
23
|
gem 'rubocop-minitest', '>0', require: false
|
23
24
|
gem 'rubocop-performance', '>0', require: false
|
24
25
|
gem 'rubocop-rake', '>0', require: false
|
25
|
-
gem 'rubocop-rspec', '>0', require: false
|
26
26
|
gem 'simplecov', '~>0.22', require: false
|
27
27
|
gem 'simplecov-cobertura', '~>2.1', require: false
|
28
28
|
gem 'threads', '~>0.4', require: false
|
data/Gemfile.lock
CHANGED
@@ -51,7 +51,7 @@ GEM
|
|
51
51
|
cucumber-tag-expressions (6.1.2)
|
52
52
|
diff-lcs (1.6.1)
|
53
53
|
docile (1.4.1)
|
54
|
-
donce (0.2.
|
54
|
+
donce (0.2.3)
|
55
55
|
backtrace (~> 0.3)
|
56
56
|
os (~> 1.1)
|
57
57
|
qbash (~> 0.3)
|
@@ -136,7 +136,7 @@ GEM
|
|
136
136
|
rubyzip (~> 2.3)
|
137
137
|
regexp_parser (2.10.0)
|
138
138
|
rexml (3.4.1)
|
139
|
-
rubocop (1.75.
|
139
|
+
rubocop (1.75.3)
|
140
140
|
json (~> 2.3)
|
141
141
|
language_server-protocol (~> 3.17.0.2)
|
142
142
|
lint_roller (~> 1.1.0)
|
@@ -161,9 +161,6 @@ GEM
|
|
161
161
|
rubocop-rake (0.7.1)
|
162
162
|
lint_roller (~> 1.1)
|
163
163
|
rubocop (>= 1.72.1)
|
164
|
-
rubocop-rspec (3.6.0)
|
165
|
-
lint_roller (~> 1.1)
|
166
|
-
rubocop (~> 1.72, >= 1.72.1)
|
167
164
|
ruby-progressbar (1.13.0)
|
168
165
|
rubyzip (2.4.1)
|
169
166
|
scrypt (3.0.8)
|
@@ -214,13 +211,14 @@ DEPENDENCIES
|
|
214
211
|
backtrace (> 0)
|
215
212
|
concurrent-ruby (~> 1.2)
|
216
213
|
cucumber (~> 9.2)
|
217
|
-
donce (
|
214
|
+
donce (>= 0.2.3)
|
218
215
|
erc20!
|
219
216
|
faraday (> 0)
|
220
217
|
loog (> 0)
|
221
218
|
minitest (~> 5.25)
|
222
219
|
minitest-reporters (~> 1.7)
|
223
220
|
minitest-retry (~> 0.2)
|
221
|
+
os (> 0)
|
224
222
|
qbash (> 0)
|
225
223
|
rake (~> 13.2)
|
226
224
|
random-port (> 0)
|
@@ -228,7 +226,6 @@ DEPENDENCIES
|
|
228
226
|
rubocop-minitest (> 0)
|
229
227
|
rubocop-performance (> 0)
|
230
228
|
rubocop-rake (> 0)
|
231
|
-
rubocop-rspec (> 0)
|
232
229
|
simplecov (~> 0.22)
|
233
230
|
simplecov-cobertura (~> 2.1)
|
234
231
|
threads (~> 0.4)
|
data/Rakefile
CHANGED
data/lib/erc20/erc20.rb
CHANGED
data/lib/erc20/wallet.rb
CHANGED
@@ -308,7 +308,7 @@ class ERC20::Wallet
|
|
308
308
|
# @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
|
309
309
|
# @param [Integer] delay How many seconds to wait between +eth_subscribe+ calls
|
310
310
|
# @param [Integer] subscription_id Unique ID of the subscription
|
311
|
-
def accept(addresses, active = [], raw: false, delay: 1, subscription_id: rand(99_999))
|
311
|
+
def accept(addresses, active = [], raw: false, delay: 1, subscription_id: rand(99_999), &)
|
312
312
|
raise 'Addresses can\'t be nil' unless addresses
|
313
313
|
raise 'Addresses must respond to .to_a()' unless addresses.respond_to?(:to_a)
|
314
314
|
raise 'Active can\'t be nil' unless active
|
@@ -318,72 +318,27 @@ class ERC20::Wallet
|
|
318
318
|
raise 'Subscription ID must be an Integer' unless subscription_id.is_a?(Integer)
|
319
319
|
raise 'Subscription ID must be a positive Integer' unless subscription_id.positive?
|
320
320
|
EventMachine.run do
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
attempt.each do |a|
|
341
|
-
active.append(a) unless before.include?(a)
|
342
|
-
end
|
343
|
-
log_it(
|
344
|
-
:debug,
|
345
|
-
"Subscribed ##{subscription_id} to #{active.to_a.size} addresses at #{log_url}: " \
|
346
|
-
"#{active.to_a.map { |a| a[0..6] }.join(', ')}"
|
347
|
-
)
|
348
|
-
elsif data['method'] == 'eth_subscription' && data.dig('params', 'result')
|
349
|
-
event = data['params']['result']
|
350
|
-
if raw
|
351
|
-
log_it(:debug, "New event arrived from #{event['address']}")
|
352
|
-
else
|
353
|
-
event = {
|
354
|
-
amount: event['data'].to_i(16),
|
355
|
-
from: "0x#{event['topics'][1][26..].downcase}",
|
356
|
-
to: "0x#{event['topics'][2][26..].downcase}",
|
357
|
-
txn: event['transactionHash'].downcase
|
358
|
-
}
|
359
|
-
log_it(
|
360
|
-
:debug,
|
361
|
-
"Payment of #{event[:amount]} tokens arrived " \
|
362
|
-
"from #{event[:from]} to #{event[:to]} in #{event[:txn]}"
|
363
|
-
)
|
364
|
-
end
|
365
|
-
yield event
|
366
|
-
end
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
ws.on(:close) do
|
371
|
-
safe do
|
372
|
-
verbose do
|
373
|
-
log_it(:debug, "Disconnected from #{log_url}")
|
374
|
-
end
|
375
|
-
end
|
376
|
-
end
|
377
|
-
ws.on(:error) do |e|
|
378
|
-
safe do
|
379
|
-
verbose do
|
380
|
-
log_it(:debug, "Error at #{log_url}: #{e.message}")
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
321
|
+
reaccept(addresses, active, raw:, delay:, subscription_id:, &)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
private
|
326
|
+
|
327
|
+
# @param [Array<String>] addresses Addresses to monitor
|
328
|
+
# @param [Array] active List of addresses that we are actually listening to
|
329
|
+
# @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
|
330
|
+
# @param [Integer] delay How many seconds to wait between +eth_subscribe+ calls
|
331
|
+
# @param [Integer] subscription_id Unique ID of the subscription
|
332
|
+
# @return [Websocket]
|
333
|
+
def reaccept(addresses, active, raw:, delay:, subscription_id:, &)
|
334
|
+
u = url(http: false)
|
335
|
+
log_it(:debug, "Connecting ##{subscription_id} to #{u.hostname}:#{u.port}...")
|
336
|
+
contract = @contract
|
337
|
+
log_url = "ws#{@ssl ? 's' : ''}://#{u.hostname}:#{u.port}"
|
338
|
+
ws = Faye::WebSocket::Client.new(u.to_s, [], proxy: @proxy ? { origin: @proxy } : {}, ping: 60)
|
339
|
+
timer =
|
384
340
|
EventMachine.add_periodic_timer(delay) do
|
385
341
|
next if active.to_a.sort == addresses.to_a.sort
|
386
|
-
attempt = addresses.to_a
|
387
342
|
ws.send(
|
388
343
|
{
|
389
344
|
jsonrpc: '2.0',
|
@@ -404,15 +359,74 @@ class ERC20::Wallet
|
|
404
359
|
)
|
405
360
|
log_it(
|
406
361
|
:debug,
|
407
|
-
"Requested to subscribe ##{subscription_id} to #{addresses.to_a.size} addresses
|
362
|
+
"Requested to subscribe ##{subscription_id} to #{addresses.to_a.size} addresses: " \
|
408
363
|
"#{addresses.to_a.map { |a| a[0..6] }.join(', ')}"
|
409
364
|
)
|
410
365
|
end
|
366
|
+
ws.on(:open) do
|
367
|
+
safe do
|
368
|
+
verbose do
|
369
|
+
log_it(:debug, "Connected ##{subscription_id} to #{log_url}")
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
ws.on(:message) do |msg|
|
374
|
+
safe do
|
375
|
+
verbose do
|
376
|
+
data = to_json(msg)
|
377
|
+
if data['id']
|
378
|
+
before = active.to_a.uniq
|
379
|
+
addresses.to_a.each do |a|
|
380
|
+
next if before.include?(a)
|
381
|
+
active.append(a)
|
382
|
+
end
|
383
|
+
log_it(
|
384
|
+
:debug,
|
385
|
+
"Subscribed ##{subscription_id} to #{active.to_a.size} addresses at #{log_url}: " \
|
386
|
+
"#{active.to_a.map { |a| a[0..6] }.join(', ')}"
|
387
|
+
)
|
388
|
+
elsif data['method'] == 'eth_subscription' && data.dig('params', 'result')
|
389
|
+
event = data['params']['result']
|
390
|
+
if raw
|
391
|
+
log_it(:debug, "New event arrived from #{event['address']}")
|
392
|
+
else
|
393
|
+
event = {
|
394
|
+
amount: event['data'].to_i(16),
|
395
|
+
from: "0x#{event['topics'][1][26..].downcase}",
|
396
|
+
to: "0x#{event['topics'][2][26..].downcase}",
|
397
|
+
txn: event['transactionHash'].downcase
|
398
|
+
}
|
399
|
+
log_it(
|
400
|
+
:debug,
|
401
|
+
"Payment of #{event[:amount]} tokens arrived at ##{subscription_id} " \
|
402
|
+
"from #{event[:from]} to #{event[:to]} in #{event[:txn]}"
|
403
|
+
)
|
404
|
+
end
|
405
|
+
yield event
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
ws.on(:close) do
|
411
|
+
safe do
|
412
|
+
verbose do
|
413
|
+
log_it(:debug, "Disconnected ##{subscription_id} from #{log_url}")
|
414
|
+
sleep(delay)
|
415
|
+
active.clear
|
416
|
+
timer.cancel
|
417
|
+
reaccept(addresses, active, raw:, delay:, subscription_id: subscription_id + 1, &)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
ws.on(:error) do |e|
|
422
|
+
safe do
|
423
|
+
verbose do
|
424
|
+
log_it(:debug, "Failed ##{subscription_id} at #{log_url}: #{e.message}")
|
425
|
+
end
|
426
|
+
end
|
411
427
|
end
|
412
428
|
end
|
413
429
|
|
414
|
-
private
|
415
|
-
|
416
430
|
def to_json(msg)
|
417
431
|
JSON.parse(msg.data)
|
418
432
|
rescue StandardError
|
data/test/erc20/test_wallet.rb
CHANGED
@@ -7,7 +7,9 @@ require 'backtrace'
|
|
7
7
|
require 'donce'
|
8
8
|
require 'eth'
|
9
9
|
require 'faraday'
|
10
|
+
require 'fileutils'
|
10
11
|
require 'json'
|
12
|
+
require 'os'
|
11
13
|
require 'random-port'
|
12
14
|
require 'shellwords'
|
13
15
|
require 'threads'
|
@@ -231,6 +233,38 @@ class TestWallet < ERC20::Test
|
|
231
233
|
end
|
232
234
|
end
|
233
235
|
|
236
|
+
def test_accepts_payments_on_hardhat_after_disconnect
|
237
|
+
skip('Works only on macOS') unless OS.mac?
|
238
|
+
WebMock.enable_net_connect!
|
239
|
+
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
240
|
+
Dir.mktmpdir do |home|
|
241
|
+
die = File.join(home, 'die.txt')
|
242
|
+
on_hardhat(die:) do |wallet|
|
243
|
+
active = []
|
244
|
+
events = []
|
245
|
+
daemon =
|
246
|
+
Thread.new do
|
247
|
+
wallet.accept([walter], active, subscription_id: 42) do |e|
|
248
|
+
events.append(e)
|
249
|
+
end
|
250
|
+
rescue StandardError => e
|
251
|
+
fake_loog.error(Backtrace.new(e))
|
252
|
+
end
|
253
|
+
wait_for { !active.empty? }
|
254
|
+
wallet.pay(JEFF, walter, 4_567)
|
255
|
+
wait_for { events.size == 1 }
|
256
|
+
FileUtils.touch(die)
|
257
|
+
on_hardhat(port: wallet.port) do
|
258
|
+
wallet.pay(JEFF, walter, 3_456)
|
259
|
+
wait_for { events.size > 1 }
|
260
|
+
daemon.kill
|
261
|
+
daemon.join(30)
|
262
|
+
assert_equal(3, events.size)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
234
268
|
def test_accepts_many_payments_on_hardhat
|
235
269
|
WebMock.enable_net_connect!
|
236
270
|
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
@@ -474,12 +508,30 @@ class TestWallet < ERC20::Test
|
|
474
508
|
end
|
475
509
|
end
|
476
510
|
|
477
|
-
def on_hardhat
|
478
|
-
RandomPort::Pool::SINGLETON.acquire do |
|
511
|
+
def on_hardhat(port: nil, die: nil)
|
512
|
+
RandomPort::Pool::SINGLETON.acquire do |rnd|
|
513
|
+
port = rnd if port.nil?
|
514
|
+
if die
|
515
|
+
killer = [
|
516
|
+
'&',
|
517
|
+
'HARDHAT_PID=$!;',
|
518
|
+
'export HARDHAT_PID;',
|
519
|
+
'while true; do',
|
520
|
+
" if [ -e #{Shellwords.escape(File.join('/die', File.basename(die)))} ]; then",
|
521
|
+
' kill -9 "${HARDHAT_PID}";',
|
522
|
+
' break;',
|
523
|
+
' else',
|
524
|
+
' sleep 0.1;',
|
525
|
+
' fi;',
|
526
|
+
'done'
|
527
|
+
].join(' ')
|
528
|
+
end
|
529
|
+
cmd = "npx hardhat node #{killer if die}"
|
479
530
|
donce(
|
480
531
|
home: File.join(__dir__, '../../hardhat'),
|
481
532
|
ports: { port => 8545 },
|
482
|
-
|
533
|
+
volumes: die ? { File.dirname(die) => '/die' } : {},
|
534
|
+
command: "/bin/bash -c #{Shellwords.escape(cmd)}",
|
483
535
|
log: fake_loog
|
484
536
|
) do
|
485
537
|
wait_for_port(port)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erc20
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-23 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: eth
|