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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71eb4c1f0b7e6778be6d2b121792c20aa190cdfdfc62948ce39e1c4ea1cb288f
4
- data.tar.gz: 9dbb23249587fe95bf6681ab9b87a131b4f946343c515df036c1fa1c7f45b200
3
+ metadata.gz: 01ef1fcd61cd21540d932e28c65ed8afa08891e0cc20b498202eb146809d4993
4
+ data.tar.gz: c8e4593fb968dcfbad8e27d24aa974a37946b444eb42b4022139c48c51ffc5ca
5
5
  SHA512:
6
- metadata.gz: aeb83a009f276a4ead1a30e1d1cb052816067fb1571cca6cb20cc39a7840422342cbf246b498932be850c2330663d7e08356c2b3f35f2365acc933511462a4ab
7
- data.tar.gz: '08b280c073e3d8bd1cc7b335b8d520e1808e190b50aaa227ee5fed17ae78ebc622637cd28c037e5991ea2dd8356cddb4f794ba240313f441cde0e79c3e7163d3'
6
+ metadata.gz: 38e4e84ea00f176c34a9f3f15ff0e77473f31fb29196b72d7e7b70af38e7aa8877015ff3249822b55665dfadac2cecb909a46d4c62554bcf5c85d8e1a1d30744
7
+ data.tar.gz: ffc78c5dfe3817bb59481f6e27e5e5fe7539ffca1e931fe8752fa31e98fee9e4c1880a2d3ce40496549c16b764524c0fad70c147d6f8a7467d8f1ba9c990d6dc
data/.rubocop.yml CHANGED
@@ -11,7 +11,6 @@ AllCops:
11
11
  SuggestExtensions: false
12
12
  NewCops: enable
13
13
  plugins:
14
- - rubocop-rspec
15
14
  - rubocop-performance
16
15
  - rubocop-rake
17
16
  - rubocop-minitest
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', '>0', require: false
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.0)
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.2)
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 (> 0)
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
@@ -39,7 +39,6 @@ require 'rubocop/rake_task'
39
39
  desc 'Run RuboCop on all directories'
40
40
  RuboCop::RakeTask.new(:rubocop) do |task|
41
41
  task.fail_on_error = true
42
- task.requires << 'rubocop-rspec'
43
42
  end
44
43
 
45
44
  require 'cucumber/rake/task'
data/lib/erc20/erc20.rb CHANGED
@@ -25,5 +25,5 @@
25
25
  # License:: MIT
26
26
  module ERC20
27
27
  # Current version of the gem (changed by the +.rultor.yml+ on every release)
28
- VERSION = '0.1.4'
28
+ VERSION = '0.1.5'
29
29
  end
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
- u = url(http: false)
322
- log_it(:debug, "Connecting to #{u.hostname}:#{u.port}...")
323
- ws = Faye::WebSocket::Client.new(u.to_s, [], proxy: @proxy ? { origin: @proxy } : {})
324
- contract = @contract
325
- attempt = []
326
- log_url = "ws#{@ssl ? 's' : ''}://#{u.hostname}:#{u.port}"
327
- ws.on(:open) do
328
- safe do
329
- verbose do
330
- log_it(:debug, "Connected to #{log_url}")
331
- end
332
- end
333
- end
334
- ws.on(:message) do |msg|
335
- safe do
336
- verbose do
337
- data = to_json(msg)
338
- if data['id']
339
- before = active.to_a
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 at #{log_url}: " \
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
@@ -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 |port|
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
- command: 'npx hardhat node',
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
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-21 00:00:00.000000000 Z
10
+ date: 2025-04-23 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: eth