erc20 0.1.5 → 0.1.6

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: 01ef1fcd61cd21540d932e28c65ed8afa08891e0cc20b498202eb146809d4993
4
- data.tar.gz: c8e4593fb968dcfbad8e27d24aa974a37946b444eb42b4022139c48c51ffc5ca
3
+ metadata.gz: 7843a4cb824e3689fb7a457f8ca0744f1d1059349aa6ea8bfe8805cd597e8b24
4
+ data.tar.gz: d11d0eb794c22737519ef5ee2083763ba8244ce91f01f0f120ee8e6970af53c0
5
5
  SHA512:
6
- metadata.gz: 38e4e84ea00f176c34a9f3f15ff0e77473f31fb29196b72d7e7b70af38e7aa8877015ff3249822b55665dfadac2cecb909a46d4c62554bcf5c85d8e1a1d30744
7
- data.tar.gz: ffc78c5dfe3817bb59481f6e27e5e5fe7539ffca1e931fe8752fa31e98fee9e4c1880a2d3ce40496549c16b764524c0fad70c147d6f8a7467d8f1ba9c990d6dc
6
+ metadata.gz: 135c421a05cbc431e20113e8cd6d63294630588b6ba27a63249313455853b83266d697759764b6f82aae857131237fd5c8c85f4f1432fe74cb73a25935b5ca45
7
+ data.tar.gz: ecf62b97b0053bc123de22f600e61a90cbf2e0636c1afa199d96478df0186194a917d27f0234285deb14129715970e2b818be5f6cf7d3a692d131a685a1af41f
data/Gemfile CHANGED
@@ -9,13 +9,12 @@ 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.2.3', require: false
12
+ gem 'donce', '>0', 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
19
18
  gem 'qbash', '>0', require: false
20
19
  gem 'rake', '~>13.2', require: false
21
20
  gem 'random-port', '>0', require: false
@@ -23,6 +22,7 @@ gem 'rubocop', '~>1.75', require: false
23
22
  gem 'rubocop-minitest', '>0', require: false
24
23
  gem 'rubocop-performance', '>0', require: false
25
24
  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.3)
54
+ donce (0.2.4)
55
55
  backtrace (~> 0.3)
56
56
  os (~> 1.1)
57
57
  qbash (~> 0.3)
@@ -161,6 +161,9 @@ 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)
164
167
  ruby-progressbar (1.13.0)
165
168
  rubyzip (2.4.1)
166
169
  scrypt (3.0.8)
@@ -211,14 +214,13 @@ DEPENDENCIES
211
214
  backtrace (> 0)
212
215
  concurrent-ruby (~> 1.2)
213
216
  cucumber (~> 9.2)
214
- donce (>= 0.2.3)
217
+ donce (> 0)
215
218
  erc20!
216
219
  faraday (> 0)
217
220
  loog (> 0)
218
221
  minitest (~> 5.25)
219
222
  minitest-reporters (~> 1.7)
220
223
  minitest-retry (~> 0.2)
221
- os (> 0)
222
224
  qbash (> 0)
223
225
  rake (~> 13.2)
224
226
  random-port (> 0)
@@ -226,6 +228,7 @@ DEPENDENCIES
226
228
  rubocop-minitest (> 0)
227
229
  rubocop-performance (> 0)
228
230
  rubocop-rake (> 0)
231
+ rubocop-rspec (> 0)
229
232
  simplecov (~> 0.22)
230
233
  simplecov-cobertura (~> 2.1)
231
234
  threads (~> 0.4)
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.5'
28
+ VERSION = '0.1.6'
29
29
  end
data/lib/erc20/wallet.rb CHANGED
@@ -312,9 +312,11 @@ class ERC20::Wallet
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
315
+ raise 'Active must respond to .to_a()' unless active.respond_to?(:to_a)
315
316
  raise 'Active must respond to .append()' unless active.respond_to?(:append)
317
+ raise 'Active must respond to .clear()' unless active.respond_to?(:clear)
316
318
  raise 'Delay must be an Integer' unless delay.is_a?(Integer)
317
- raise 'Delay must be a positive Integer' unless delay.positive?
319
+ raise 'Delay must be a positive Integer or positive Float' unless delay.positive?
318
320
  raise 'Subscription ID must be an Integer' unless subscription_id.is_a?(Integer)
319
321
  raise 'Subscription ID must be a positive Integer' unless subscription_id.positive?
320
322
  EventMachine.run do
@@ -336,37 +338,38 @@ class ERC20::Wallet
336
338
  contract = @contract
337
339
  log_url = "ws#{@ssl ? 's' : ''}://#{u.hostname}:#{u.port}"
338
340
  ws = Faye::WebSocket::Client.new(u.to_s, [], proxy: @proxy ? { origin: @proxy } : {}, ping: 60)
339
- timer =
340
- EventMachine.add_periodic_timer(delay) do
341
- next if active.to_a.sort == addresses.to_a.sort
342
- ws.send(
343
- {
344
- jsonrpc: '2.0',
345
- id: subscription_id,
346
- method: 'eth_subscribe',
347
- params: [
348
- 'logs',
349
- {
350
- address: contract,
351
- topics: [
352
- '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
353
- nil,
354
- addresses.to_a.map { |a| "0x000000000000000000000000#{a[2..]}" }
355
- ]
356
- }
357
- ]
358
- }.to_json
359
- )
360
- log_it(
361
- :debug,
362
- "Requested to subscribe ##{subscription_id} to #{addresses.to_a.size} addresses: " \
363
- "#{addresses.to_a.map { |a| a[0..6] }.join(', ')}"
364
- )
365
- end
341
+ timer = nil
366
342
  ws.on(:open) do
367
343
  safe do
368
344
  verbose do
369
345
  log_it(:debug, "Connected ##{subscription_id} to #{log_url}")
346
+ timer =
347
+ EventMachine.add_periodic_timer(delay) do
348
+ next if active.to_a.sort == addresses.to_a.sort
349
+ ws.send(
350
+ {
351
+ jsonrpc: '2.0',
352
+ id: subscription_id,
353
+ method: 'eth_subscribe',
354
+ params: [
355
+ 'logs',
356
+ {
357
+ address: contract,
358
+ topics: [
359
+ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
360
+ nil,
361
+ addresses.to_a.map { |a| "0x000000000000000000000000#{a[2..]}" }
362
+ ]
363
+ }
364
+ ]
365
+ }.to_json
366
+ )
367
+ log_it(
368
+ :debug,
369
+ "Requested to subscribe ##{subscription_id} to #{addresses.to_a.size} addresses: " \
370
+ "#{addresses.to_a.map { |a| a[0..6] }.join(', ')}"
371
+ )
372
+ end
370
373
  end
371
374
  end
372
375
  end
@@ -411,9 +414,8 @@ class ERC20::Wallet
411
414
  safe do
412
415
  verbose do
413
416
  log_it(:debug, "Disconnected ##{subscription_id} from #{log_url}")
414
- sleep(delay)
415
417
  active.clear
416
- timer.cancel
418
+ timer&.cancel
417
419
  reaccept(addresses, active, raw:, delay:, subscription_id: subscription_id + 1, &)
418
420
  end
419
421
  end
@@ -22,58 +22,12 @@ require_relative '../../lib/erc20/wallet'
22
22
  # Copyright:: Copyright (c) 2025 Yegor Bugayenko
23
23
  # License:: MIT
24
24
  class TestWallet < ERC20::Test
25
- # At this address, in Ethereum mainnet, there are $8 USDT and 0.0042 ETH. I won't
26
- # move them anyway, that's why tests can use this address forever.
27
- STABLE = '0x7232148927F8a580053792f44D4d59d40Fd00ABD'
28
-
29
25
  # One guy private hex.
30
26
  JEFF = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
31
27
 
32
28
  # Another guy private hex.
33
29
  WALTER = '91f9111b1744d55361e632771a4e53839e9442a9fef45febc0a5c838c686a15b'
34
30
 
35
- def test_checks_balance_on_mainnet
36
- WebMock.enable_net_connect!
37
- b = mainnet.balance(STABLE)
38
- refute_nil(b)
39
- assert_equal(8_000_000, b) # this is $8 USDT
40
- end
41
-
42
- def test_checks_eth_balance_on_mainnet
43
- WebMock.enable_net_connect!
44
- b = mainnet.eth_balance(STABLE)
45
- refute_nil(b)
46
- assert_equal(4_200_000_000_000_000, b) # this is 0.0042 ETH
47
- end
48
-
49
- def test_checks_balance_of_absent_address
50
- WebMock.enable_net_connect!
51
- a = '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
52
- b = mainnet.balance(a)
53
- refute_nil(b)
54
- assert_equal(0, b)
55
- end
56
-
57
- def test_checks_gas_estimate_on_mainnet
58
- WebMock.enable_net_connect!
59
- b = mainnet.gas_estimate(STABLE, Eth::Key.new(priv: JEFF).address.to_s, 44_000)
60
- refute_nil(b)
61
- assert_predicate(b, :positive?)
62
- assert_operator(b, :>, 1000)
63
- end
64
-
65
- def test_fails_with_invalid_infura_key
66
- WebMock.enable_net_connect!
67
- skip('Apparently, even with invalid key, Infura returns balance')
68
- w = ERC20::Wallet.new(
69
- contract: ERC20::Wallet.USDT,
70
- host: 'mainnet.infura.io',
71
- http_path: '/v3/invalid-key-here',
72
- log: fake_loog
73
- )
74
- assert_raises(StandardError) { w.balance(STABLE) }
75
- end
76
-
77
31
  def test_logs_to_stdout
78
32
  WebMock.disable_net_connect!
79
33
  stub_request(:post, 'https://example.org/').to_return(
@@ -85,25 +39,12 @@ class TestWallet < ERC20::Test
85
39
  http_path: '/',
86
40
  log: $stdout
87
41
  )
88
- w.balance(STABLE)
42
+ w.balance(Eth::Key.new(priv: JEFF).address.to_s)
89
43
  end
90
44
 
91
45
  def test_checks_balance_on_testnet
92
46
  WebMock.enable_net_connect!
93
- b = testnet.balance(STABLE)
94
- refute_nil(b)
95
- assert_predicate(b, :zero?)
96
- end
97
-
98
- def test_checks_balance_on_polygon
99
- WebMock.enable_net_connect!
100
- w = ERC20::Wallet.new(
101
- contract: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
102
- host: 'polygon-mainnet.infura.io',
103
- http_path: "/v3/#{env('INFURA_KEY')}",
104
- log: fake_loog
105
- )
106
- b = w.balance(STABLE)
47
+ b = testnet.balance(Eth::Key.new(priv: JEFF).address.to_s)
107
48
  refute_nil(b)
108
49
  assert_predicate(b, :zero?)
109
50
  end
@@ -379,26 +320,6 @@ class TestWallet < ERC20::Test
379
320
  end
380
321
  end
381
322
 
382
- def test_accepts_payments_on_mainnet
383
- WebMock.enable_net_connect!
384
- active = []
385
- failed = false
386
- net = mainnet
387
- daemon =
388
- Thread.new do
389
- net.accept([STABLE], active) do |_|
390
- # ignore it
391
- end
392
- rescue StandardError => e
393
- failed = true
394
- fake_loog.error(Backtrace.new(e))
395
- end
396
- wait_for { !active.empty? }
397
- daemon.kill
398
- daemon.join(30)
399
- refute(failed)
400
- end
401
-
402
323
  def test_checks_balance_via_proxy
403
324
  WebMock.enable_net_connect!
404
325
  b = nil
@@ -410,151 +331,4 @@ class TestWallet < ERC20::Test
410
331
  end
411
332
  assert_equal(123_000_100_000, b)
412
333
  end
413
-
414
- def test_checks_balance_via_proxy_on_mainnet
415
- WebMock.enable_net_connect!
416
- via_proxy do |proxy|
417
- w = ERC20::Wallet.new(
418
- host: 'mainnet.infura.io',
419
- http_path: "/v3/#{env('INFURA_KEY')}",
420
- proxy:, log: fake_loog
421
- )
422
- assert_equal(8_000_000, w.balance(STABLE))
423
- end
424
- end
425
-
426
- def test_pays_on_mainnet
427
- WebMock.enable_net_connect!
428
- skip('This is live, must be run manually')
429
- w = mainnet
430
- print 'Enter Ethereum ERC20 private key (64 chars): '
431
- priv = gets.chomp
432
- to = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
433
- txn = w.pay(priv, to, 1_990_000)
434
- assert_equal(66, txn.length)
435
- end
436
-
437
- private
438
-
439
- def env(var)
440
- key = ENV.fetch(var, nil)
441
- skip("The #{var} environment variable is not set") if key.nil?
442
- skip("The #{var} environment variable is empty") if key.empty?
443
- key
444
- end
445
-
446
- def mainnet
447
- [
448
- {
449
- host: 'mainnet.infura.io',
450
- http_path: "/v3/#{env('INFURA_KEY')}",
451
- ws_path: "/ws/v3/#{env('INFURA_KEY')}"
452
- },
453
- {
454
- host: 'go.getblock.io',
455
- http_path: "/#{env('GETBLOCK_KEY')}",
456
- ws_path: "/#{env('GETBLOCK_WS_KEY')}"
457
- }
458
- ].map do |server|
459
- ERC20::Wallet.new(
460
- host: server[:host],
461
- http_path: server[:http_path],
462
- ws_path: server[:ws_path],
463
- log: fake_loog
464
- )
465
- end.sample
466
- end
467
-
468
- def testnet
469
- [
470
- {
471
- host: 'sepolia.infura.io',
472
- http_path: "/v3/#{env('INFURA_KEY')}",
473
- ws_path: "/ws/v3/#{env('INFURA_KEY')}"
474
- },
475
- {
476
- host: 'go.getblock.io',
477
- http_path: "/#{env('GETBLOCK_SEPOILA_KEY')}",
478
- ws_path: "/#{env('GETBLOCK_SEPOILA_KEY')}"
479
- }
480
- ].map do |server|
481
- ERC20::Wallet.new(
482
- host: server[:host],
483
- http_path: server[:http_path],
484
- ws_path: server[:ws_path],
485
- log: fake_loog
486
- )
487
- end.sample
488
- end
489
-
490
- def through_proxy(wallet, proxy)
491
- ERC20::Wallet.new(
492
- contract: wallet.contract, chain: wallet.chain,
493
- host: donce_host, port: wallet.port, http_path: wallet.http_path, ws_path: wallet.ws_path,
494
- ssl: wallet.ssl, proxy:, log: fake_loog
495
- )
496
- end
497
-
498
- def via_proxy
499
- RandomPort::Pool::SINGLETON.acquire do |port|
500
- donce(
501
- image: 'yegor256/squid-proxy:latest',
502
- ports: { port => 3128 },
503
- env: { 'USERNAME' => 'jeffrey', 'PASSWORD' => 'swordfish' },
504
- root: true, log: fake_loog
505
- ) do
506
- yield "http://jeffrey:swordfish@localhost:#{port}"
507
- end
508
- end
509
- end
510
-
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}"
530
- donce(
531
- home: File.join(__dir__, '../../hardhat'),
532
- ports: { port => 8545 },
533
- volumes: die ? { File.dirname(die) => '/die' } : {},
534
- command: "/bin/bash -c #{Shellwords.escape(cmd)}",
535
- log: fake_loog
536
- ) do
537
- wait_for_port(port)
538
- cmd = [
539
- '(cat hardhat.config.js)',
540
- '(ls -al)',
541
- '(echo y | npx hardhat ignition deploy ./ignition/modules/Foo.ts --network foo --deployment-id foo)',
542
- '(npx hardhat ignition status foo | tail -1 | cut -d" " -f3)'
543
- ].join(' && ')
544
- contract = donce(
545
- home: File.join(__dir__, '../../hardhat'),
546
- command: "/bin/bash -c #{Shellwords.escape(cmd)}",
547
- build_args: { 'HOST' => donce_host, 'PORT' => port },
548
- log: fake_loog,
549
- root: true
550
- ).split("\n").last
551
- wallet = ERC20::Wallet.new(
552
- contract:, chain: 4242,
553
- host: 'localhost', port:, http_path: '/', ws_path: '/', ssl: false,
554
- log: fake_loog
555
- )
556
- yield wallet
557
- end
558
- end
559
- end
560
334
  end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ # SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
4
+ # SPDX-License-Identifier: MIT
5
+
6
+ require 'backtrace'
7
+ require 'donce'
8
+ require 'eth'
9
+ require 'faraday'
10
+ require 'fileutils'
11
+ require 'json'
12
+ require 'os'
13
+ require 'random-port'
14
+ require 'shellwords'
15
+ require 'threads'
16
+ require 'typhoeus'
17
+ require_relative '../test__helper'
18
+ require_relative '../../lib/erc20/wallet'
19
+
20
+ # Test.
21
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
22
+ # Copyright:: Copyright (c) 2025 Yegor Bugayenko
23
+ # License:: MIT
24
+ class TestWalletLive < ERC20::Test
25
+ # At this address, in Ethereum mainnet, there are $8 USDT and 0.0042 ETH. I won't
26
+ # move them anyway, that's why tests can use this address forever.
27
+ STABLE = '0x7232148927F8a580053792f44D4d59d40Fd00ABD'
28
+
29
+ def test_checks_balance_on_mainnet
30
+ WebMock.enable_net_connect!
31
+ b = mainnet.balance(STABLE)
32
+ refute_nil(b)
33
+ assert_equal(8_000_000, b) # this is $8 USDT
34
+ end
35
+
36
+ def test_checks_eth_balance_on_mainnet
37
+ WebMock.enable_net_connect!
38
+ b = mainnet.eth_balance(STABLE)
39
+ refute_nil(b)
40
+ assert_equal(4_200_000_000_000_000, b) # this is 0.0042 ETH
41
+ end
42
+
43
+ def test_checks_balance_of_absent_address
44
+ WebMock.enable_net_connect!
45
+ a = '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
46
+ b = mainnet.balance(a)
47
+ refute_nil(b)
48
+ assert_equal(0, b)
49
+ end
50
+
51
+ def test_checks_gas_estimate_on_mainnet
52
+ WebMock.enable_net_connect!
53
+ b = mainnet.gas_estimate(STABLE, '0x7232148927F8a580053792f44D4d5FFFFFFFFFFF', 44_000)
54
+ refute_nil(b)
55
+ assert_predicate(b, :positive?)
56
+ assert_operator(b, :>, 1000)
57
+ end
58
+
59
+ def test_fails_with_invalid_infura_key
60
+ WebMock.enable_net_connect!
61
+ skip('Apparently, even with invalid key, Infura returns balance')
62
+ w = ERC20::Wallet.new(
63
+ contract: ERC20::Wallet.USDT,
64
+ host: 'mainnet.infura.io',
65
+ http_path: '/v3/invalid-key-here',
66
+ log: fake_loog
67
+ )
68
+ assert_raises(StandardError) { w.balance(STABLE) }
69
+ end
70
+
71
+ def test_checks_balance_on_polygon
72
+ WebMock.enable_net_connect!
73
+ w = ERC20::Wallet.new(
74
+ contract: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
75
+ host: 'polygon-mainnet.infura.io',
76
+ http_path: "/v3/#{env('INFURA_KEY')}",
77
+ log: fake_loog
78
+ )
79
+ b = w.balance(STABLE)
80
+ refute_nil(b)
81
+ assert_predicate(b, :zero?)
82
+ end
83
+
84
+ def test_accepts_payments_on_mainnet
85
+ WebMock.enable_net_connect!
86
+ active = []
87
+ failed = false
88
+ net = mainnet
89
+ daemon =
90
+ Thread.new do
91
+ net.accept([STABLE], active) do |_|
92
+ # ignore it
93
+ end
94
+ rescue StandardError => e
95
+ failed = true
96
+ fake_loog.error(Backtrace.new(e))
97
+ end
98
+ wait_for { !active.empty? }
99
+ daemon.kill
100
+ daemon.join(30)
101
+ refute(failed)
102
+ end
103
+
104
+ def test_checks_balance_via_proxy_on_mainnet
105
+ WebMock.enable_net_connect!
106
+ via_proxy do |proxy|
107
+ w = ERC20::Wallet.new(
108
+ host: 'mainnet.infura.io',
109
+ http_path: "/v3/#{env('INFURA_KEY')}",
110
+ proxy:, log: fake_loog
111
+ )
112
+ assert_equal(8_000_000, w.balance(STABLE))
113
+ end
114
+ end
115
+
116
+ def test_pays_on_mainnet
117
+ WebMock.enable_net_connect!
118
+ skip('This is live, must be run manually')
119
+ w = mainnet
120
+ print 'Enter Ethereum ERC20 private key (64 chars): '
121
+ priv = gets.chomp
122
+ to = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
123
+ txn = w.pay(priv, to, 1_990_000)
124
+ assert_equal(66, txn.length)
125
+ end
126
+ end
data/test/test__helper.rb CHANGED
@@ -42,6 +42,10 @@ class Primitivo
42
42
  @array = array
43
43
  end
44
44
 
45
+ def clear
46
+ @array.clear
47
+ end
48
+
45
49
  def to_a
46
50
  @array.to_a
47
51
  end
@@ -77,4 +81,126 @@ class ERC20::Test < Minitest::Test
77
81
  def wait_for_port(port)
78
82
  wait_for { Typhoeus::Request.get("http://localhost:#{port}").code == 200 }
79
83
  end
84
+
85
+ def env(var)
86
+ key = ENV.fetch(var, nil)
87
+ skip("The #{var} environment variable is not set") if key.nil?
88
+ skip("The #{var} environment variable is empty") if key.empty?
89
+ key
90
+ end
91
+
92
+ def mainnet
93
+ [
94
+ {
95
+ host: 'mainnet.infura.io',
96
+ http_path: "/v3/#{env('INFURA_KEY')}",
97
+ ws_path: "/ws/v3/#{env('INFURA_KEY')}"
98
+ },
99
+ {
100
+ host: 'go.getblock.io',
101
+ http_path: "/#{env('GETBLOCK_KEY')}",
102
+ ws_path: "/#{env('GETBLOCK_WS_KEY')}"
103
+ }
104
+ ].map do |server|
105
+ ERC20::Wallet.new(
106
+ host: server[:host],
107
+ http_path: server[:http_path],
108
+ ws_path: server[:ws_path],
109
+ log: fake_loog
110
+ )
111
+ end.sample
112
+ end
113
+
114
+ def testnet
115
+ [
116
+ {
117
+ host: 'sepolia.infura.io',
118
+ http_path: "/v3/#{env('INFURA_KEY')}",
119
+ ws_path: "/ws/v3/#{env('INFURA_KEY')}"
120
+ },
121
+ {
122
+ host: 'go.getblock.io',
123
+ http_path: "/#{env('GETBLOCK_SEPOILA_KEY')}",
124
+ ws_path: "/#{env('GETBLOCK_SEPOILA_KEY')}"
125
+ }
126
+ ].map do |server|
127
+ ERC20::Wallet.new(
128
+ host: server[:host],
129
+ http_path: server[:http_path],
130
+ ws_path: server[:ws_path],
131
+ log: fake_loog
132
+ )
133
+ end.sample
134
+ end
135
+
136
+ def through_proxy(wallet, proxy)
137
+ ERC20::Wallet.new(
138
+ contract: wallet.contract, chain: wallet.chain,
139
+ host: donce_host, port: wallet.port, http_path: wallet.http_path, ws_path: wallet.ws_path,
140
+ ssl: wallet.ssl, proxy:, log: fake_loog
141
+ )
142
+ end
143
+
144
+ def via_proxy
145
+ RandomPort::Pool::SINGLETON.acquire do |port|
146
+ donce(
147
+ image: 'yegor256/squid-proxy:latest',
148
+ ports: { port => 3128 },
149
+ env: { 'USERNAME' => 'jeffrey', 'PASSWORD' => 'swordfish' },
150
+ root: true, log: fake_loog
151
+ ) do
152
+ yield "http://jeffrey:swordfish@localhost:#{port}"
153
+ end
154
+ end
155
+ end
156
+
157
+ def on_hardhat(port: nil, die: nil)
158
+ RandomPort::Pool::SINGLETON.acquire do |rnd|
159
+ port = rnd if port.nil?
160
+ if die
161
+ killer = [
162
+ '&',
163
+ 'HARDHAT_PID=$!;',
164
+ 'export HARDHAT_PID;',
165
+ 'while true; do',
166
+ " if [ -e #{Shellwords.escape(File.join('/die', File.basename(die)))} ]; then",
167
+ ' kill -9 "${HARDHAT_PID}";',
168
+ ' break;',
169
+ ' else',
170
+ ' sleep 0.1;',
171
+ ' fi;',
172
+ 'done'
173
+ ].join(' ')
174
+ end
175
+ cmd = "npx hardhat node #{killer if die}"
176
+ donce(
177
+ home: File.join(__dir__, '../hardhat'),
178
+ ports: { port => 8545 },
179
+ volumes: die ? { File.dirname(die) => '/die' } : {},
180
+ command: "/bin/bash -c #{Shellwords.escape(cmd)}",
181
+ log: fake_loog
182
+ ) do
183
+ wait_for_port(port)
184
+ cmd = [
185
+ '(cat hardhat.config.js)',
186
+ '(ls -al)',
187
+ '(echo y | npx hardhat ignition deploy ./ignition/modules/Foo.ts --network foo --deployment-id foo)',
188
+ '(npx hardhat ignition status foo | tail -1 | cut -d" " -f3)'
189
+ ].join(' && ')
190
+ contract = donce(
191
+ home: File.join(__dir__, '../hardhat'),
192
+ command: "/bin/bash -c #{Shellwords.escape(cmd)}",
193
+ build_args: { 'HOST' => donce_host, 'PORT' => port },
194
+ log: fake_loog,
195
+ root: true
196
+ ).split("\n").last
197
+ wallet = ERC20::Wallet.new(
198
+ contract:, chain: 4242,
199
+ host: 'localhost', port:, http_path: '/', ws_path: '/', ssl: false,
200
+ log: fake_loog
201
+ )
202
+ yield wallet
203
+ end
204
+ end
205
+ end
80
206
  end
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.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-23 00:00:00.000000000 Z
10
+ date: 2025-04-24 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: eth
@@ -149,6 +149,7 @@ files:
149
149
  - renovate.json
150
150
  - test/erc20/test_fake_wallet.rb
151
151
  - test/erc20/test_wallet.rb
152
+ - test/erc20/test_wallet_live.rb
152
153
  - test/test__helper.rb
153
154
  homepage: http://github.com/yegor256/erc20.rb
154
155
  licenses: