erc20 0.2.0 → 0.2.2

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.
@@ -1,118 +0,0 @@
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 'loog'
11
- require 'random-port'
12
- require 'shellwords'
13
- require 'threads'
14
- require 'typhoeus'
15
- require_relative '../test__helper'
16
- require_relative '../../lib/erc20/fake_wallet'
17
-
18
- # Test.
19
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
20
- # Copyright:: Copyright (c) 2025 Yegor Bugayenko
21
- # License:: MIT
22
- class TestFakeWallet < ERC20::Test
23
- def test_checks_gas_estimate
24
- b = ERC20::FakeWallet.new.gas_estimate(
25
- '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff',
26
- '0xfadef8ba4a5d709a2bf55b7a8798c9b438c640c1',
27
- 44_000
28
- )
29
- refute_nil(b)
30
- end
31
-
32
- def test_checks_gas_price
33
- gwei = ERC20::FakeWallet.new.gas_price
34
- refute_nil(gwei)
35
- end
36
-
37
- def test_checks_fake_balance
38
- w = ERC20::FakeWallet.new
39
- a = '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
40
- b = w.balance(a)
41
- refute_nil(b)
42
- assert_equal(42_000_000, b)
43
- assert_includes(w.history, { method: :balance, result: b, address: a })
44
- end
45
-
46
- def test_checks_preset_fake_balance
47
- w = ERC20::FakeWallet.new
48
- a = '0xEB2fE8872A6f1eDb70a2632Effffffffeefbbfaa'
49
- b = 55_555
50
- w.set_balance(a, b)
51
- assert_equal(b, w.balance(a))
52
- end
53
-
54
- def test_checks_fake_eth_balance
55
- a = '0xEB2fE8872A6f1eDb70a2632Ebbffffff66fff77f'
56
- w = ERC20::FakeWallet.new
57
- b = w.eth_balance(a)
58
- refute_nil(b)
59
- assert_equal(77_000_000_000_000_000, b)
60
- assert_includes(w.history, { method: :eth_balance, result: b, address: a })
61
- end
62
-
63
- def test_checks_preset_fake_eth_balance
64
- a = '0xEB2fE8872A6f1eDb70a2632Eff88fff99fff33ff'
65
- w = ERC20::FakeWallet.new
66
- b = 33_333
67
- w.set_eth_balance(a, b)
68
- assert_equal(b, w.eth_balance(a))
69
- end
70
-
71
- def test_reads_sum_of_payment
72
- txn = '0xcf0598d640d4bea3367e6af28a08c54342a39156afd292a31453778e4755945d'
73
- assert_predicate(ERC20::FakeWallet.new.sum_of(txn), :positive?)
74
- end
75
-
76
- def test_returns_host
77
- assert_equal('example.com', ERC20::FakeWallet.new.host)
78
- end
79
-
80
- def test_pays_fake_money
81
- priv = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
82
- address = '0xfadef8ba4a5d709a2bf55b7a8798c9b438c640c1'
83
- w = ERC20::FakeWallet.new
84
- amount = 555_000
85
- txn = w.pay(priv, address, amount)
86
- assert_equal(66, txn.length)
87
- assert_match(/^0x[a-f0-9]{64}$/, txn)
88
- assert_equal(ERC20::FakeWallet::TXN_HASH, txn)
89
- assert_includes(w.history, { method: :pay, result: txn, priv:, address:, amount:, gas_limit: nil, gas_price: nil })
90
- end
91
-
92
- def test_pays_fake_eths
93
- priv = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
94
- to = '0xfadef8ba4a5d709a2bf55b7a8798c9b438c640c1'
95
- txn = ERC20::FakeWallet.new.eth_pay(Eth::Key.new(priv:), to, 555)
96
- assert_equal(66, txn.length)
97
- assert_match(/^0x[a-f0-9]{64}$/, txn)
98
- end
99
-
100
- def test_accepts_payments_on_hardhat
101
- active = Primitivo.new([])
102
- addresses = Primitivo.new(['0xfadef8ba4a5d709a2bf55b7a8798c9b438c640c1'])
103
- event = nil
104
- daemon =
105
- Thread.new do
106
- ERC20::FakeWallet.new.accept(addresses, active, delay: 0.1) do |e|
107
- event = e
108
- end
109
- rescue StandardError => e
110
- fake_loog.error(Backtrace.new(e))
111
- end
112
- wait_for { !active.to_a.empty? }
113
- wait_for { !event.nil? }
114
- daemon.kill
115
- daemon.join(30)
116
- refute_nil(event)
117
- end
118
- end
@@ -1,343 +0,0 @@
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 TestWallet < ERC20::Test
25
- # One guy private hex.
26
- JEFF = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
27
-
28
- # Another guy private hex.
29
- WALTER = '91f9111b1744d55361e632771a4e53839e9442a9fef45febc0a5c838c686a15b'
30
-
31
- def test_logs_to_stdout
32
- WebMock.disable_net_connect!
33
- stub_request(:post, 'https://example.org/').to_return(
34
- body: { jsonrpc: '2.0', id: 42, result: '0x1F1F1F' }.to_json,
35
- headers: { 'Content-Type' => 'application/json' }
36
- )
37
- w = ERC20::Wallet.new(
38
- host: 'example.org',
39
- http_path: '/',
40
- log: $stdout
41
- )
42
- w.balance(Eth::Key.new(priv: JEFF).address.to_s)
43
- end
44
-
45
- def test_checks_balance_on_testnet
46
- WebMock.enable_net_connect!
47
- b = testnet.balance(Eth::Key.new(priv: JEFF).address.to_s)
48
- refute_nil(b)
49
- assert_predicate(b, :zero?)
50
- end
51
-
52
- def test_checks_gas_estimate_on_hardhat
53
- WebMock.enable_net_connect!
54
- sum = 100_000
55
- on_hardhat do |wallet|
56
- b1 = wallet.gas_estimate(
57
- Eth::Key.new(priv: JEFF).address.to_s,
58
- Eth::Key.new(priv: WALTER).address.to_s,
59
- sum
60
- )
61
- assert_operator(b1, :>, 21_000)
62
- end
63
- end
64
-
65
- def test_checks_balance_on_hardhat
66
- WebMock.enable_net_connect!
67
- on_hardhat do |wallet|
68
- b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
69
- assert_equal(123_000_100_000, b)
70
- end
71
- end
72
-
73
- def test_checks_eth_balance_on_hardhat
74
- WebMock.enable_net_connect!
75
- on_hardhat do |wallet|
76
- b = wallet.balance(Eth::Key.new(priv: WALTER).address.to_s)
77
- assert_equal(456_000_000_000, b)
78
- end
79
- end
80
-
81
- def test_checks_balance_on_hardhat_in_threads
82
- WebMock.enable_net_connect!
83
- on_hardhat do |wallet|
84
- Threads.new.assert do
85
- b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
86
- assert_equal(123_000_100_000, b)
87
- end
88
- end
89
- end
90
-
91
- def test_pays_on_hardhat
92
- WebMock.enable_net_connect!
93
- on_hardhat do |wallet|
94
- to = Eth::Key.new(priv: WALTER).address.to_s
95
- before = wallet.balance(to)
96
- sum = 42_000
97
- from = Eth::Key.new(priv: JEFF).address.to_s
98
- assert_operator(wallet.balance(from), :>, sum * 2)
99
- txn = wallet.pay(JEFF, to, sum)
100
- assert_equal(66, txn.length)
101
- assert_match(/^0x[a-f0-9]{64}$/, txn)
102
- assert_equal(before + sum, wallet.balance(to))
103
- end
104
- end
105
-
106
- def test_reads_payment_amount_on_hardhat
107
- WebMock.enable_net_connect!
108
- on_hardhat do |wallet|
109
- sum = 33_330
110
- txn = wallet.pay(JEFF, Eth::Key.new(priv: WALTER).address.to_s, sum)
111
- assert_equal(sum, wallet.sum_of(txn))
112
- end
113
- end
114
-
115
- def test_eth_pays_on_hardhat
116
- WebMock.enable_net_connect!
117
- on_hardhat do |wallet|
118
- to = Eth::Key.new(priv: WALTER).address.to_s
119
- before = wallet.eth_balance(to)
120
- sum = 42_000
121
- from = Eth::Key.new(priv: JEFF).address.to_s
122
- assert_operator(wallet.eth_balance(from), :>, sum * 2)
123
- txn = wallet.eth_pay(JEFF, to, sum)
124
- assert_equal(66, txn.length)
125
- assert_match(/^0x[a-f0-9]{64}$/, txn)
126
- assert_equal(before + sum, wallet.eth_balance(to))
127
- end
128
- end
129
-
130
- def test_pays_on_hardhat_in_threads
131
- WebMock.enable_net_connect!
132
- on_hardhat do |wallet|
133
- to = Eth::Key.new(priv: WALTER).address.to_s
134
- before = wallet.balance(to)
135
- sum = 42_000
136
- mul = 10
137
- Threads.new(mul).assert do
138
- wallet.pay(JEFF, to, sum)
139
- end
140
- assert_equal(before + (sum * mul), wallet.balance(to))
141
- end
142
- end
143
-
144
- def test_pays_eth_on_hardhat_in_threads
145
- WebMock.enable_net_connect!
146
- on_hardhat do |wallet|
147
- to = Eth::Key.new(priv: WALTER).address.to_s
148
- before = wallet.eth_balance(to)
149
- sum = 42_000
150
- mul = 10
151
- Threads.new(mul).assert do
152
- wallet.eth_pay(JEFF, to, sum)
153
- end
154
- assert_equal(before + (sum * mul), wallet.eth_balance(to))
155
- end
156
- end
157
-
158
- def test_accepts_payments_on_hardhat
159
- WebMock.enable_net_connect!
160
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
161
- jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
162
- on_hardhat do |wallet|
163
- active = []
164
- event = nil
165
- daemon =
166
- Thread.new do
167
- wallet.accept([walter, jeff], active) do |e|
168
- event = e
169
- end
170
- rescue StandardError => e
171
- fake_loog.error(Backtrace.new(e))
172
- end
173
- wait_for { !active.empty? }
174
- sum = 77_000
175
- wallet.pay(JEFF, walter, sum)
176
- wait_for { !event.nil? }
177
- daemon.kill
178
- daemon.join(30)
179
- assert_equal(sum, event[:amount])
180
- assert_equal(jeff, event[:from])
181
- assert_equal(walter, event[:to])
182
- assert_equal(66, event[:txn].length)
183
- end
184
- end
185
-
186
- def test_accepts_payments_on_hardhat_after_disconnect
187
- skip('Works only on macOS') unless OS.mac?
188
- WebMock.enable_net_connect!
189
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
190
- Dir.mktmpdir do |home|
191
- die = File.join(home, 'die.txt')
192
- on_hardhat(die:) do |wallet|
193
- active = []
194
- events = []
195
- daemon =
196
- Thread.new do
197
- wallet.accept([walter], active, subscription_id: 42) do |e|
198
- events.append(e)
199
- end
200
- rescue StandardError => e
201
- fake_loog.error(Backtrace.new(e))
202
- end
203
- wait_for { !active.empty? }
204
- wallet.pay(JEFF, walter, 4_567)
205
- wait_for { events.size == 1 }
206
- FileUtils.touch(die)
207
- on_hardhat(port: wallet.port) do
208
- wallet.pay(JEFF, walter, 3_456)
209
- wait_for { events.size > 1 }
210
- daemon.kill
211
- daemon.join(30)
212
- assert_equal(3, events.size)
213
- end
214
- end
215
- end
216
- end
217
-
218
- def test_accepts_many_payments_on_hardhat
219
- WebMock.enable_net_connect!
220
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
221
- on_hardhat do |wallet|
222
- active = []
223
- events = Concurrent::Set.new
224
- total = 10
225
- daemon =
226
- Thread.new do
227
- wallet.accept([walter], active) do |e|
228
- events.add(e)
229
- end
230
- rescue StandardError => e
231
- fake_loog.error(Backtrace.new(e))
232
- end
233
- wait_for { !active.empty? }
234
- sum = 1_234
235
- Threads.new(total).assert do
236
- wallet.pay(JEFF, walter, sum)
237
- end
238
- wait_for { events.size == total }
239
- daemon.kill
240
- daemon.join(30)
241
- assert_equal(total, events.size)
242
- end
243
- end
244
-
245
- def test_accepts_payments_with_failures_on_hardhat
246
- WebMock.enable_net_connect!
247
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
248
- on_hardhat do |wallet|
249
- active = []
250
- events = Concurrent::Set.new
251
- total = 10
252
- daemon =
253
- Thread.new do
254
- wallet.accept([walter], active) do |e|
255
- events.add(e)
256
- raise 'intentional'
257
- end
258
- end
259
- wait_for { !active.empty? }
260
- sum = 1_234
261
- Threads.new(total).assert do
262
- wallet.pay(JEFF, walter, sum)
263
- end
264
- wait_for { events.size == total }
265
- daemon.kill
266
- daemon.join(30)
267
- assert_equal(total, events.size)
268
- end
269
- end
270
-
271
- def test_accepts_payments_on_changing_addresses_on_hardhat
272
- WebMock.enable_net_connect!
273
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
274
- jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
275
- addresses = Primitivo.new([walter])
276
- on_hardhat do |wallet|
277
- active = Primitivo.new([])
278
- event = nil
279
- daemon =
280
- Thread.new do
281
- wallet.accept(addresses, active) do |e|
282
- event = e
283
- end
284
- rescue StandardError => e
285
- fake_loog.error(Backtrace.new(e))
286
- end
287
- wait_for { active.to_a.include?(walter) }
288
- sum1 = 453_000
289
- wallet.pay(JEFF, walter, sum1)
290
- wait_for { !event.nil? }
291
- assert_equal(sum1, event[:amount])
292
- sum2 = 22_000
293
- event = nil
294
- addresses.append(jeff)
295
- wait_for { active.to_a.include?(jeff) }
296
- wallet.pay(WALTER, jeff, sum2)
297
- wait_for { !event.nil? }
298
- assert_equal(sum2, event[:amount])
299
- daemon.kill
300
- daemon.join(30)
301
- end
302
- end
303
-
304
- def test_accepts_payments_on_hardhat_via_proxy
305
- WebMock.enable_net_connect!
306
- via_proxy do |proxy|
307
- walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
308
- jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
309
- on_hardhat do |w|
310
- wallet = through_proxy(w, proxy)
311
- active = []
312
- event = nil
313
- daemon =
314
- Thread.new do
315
- wallet.accept([walter, jeff], active) do |e|
316
- event = e
317
- end
318
- rescue StandardError => e
319
- fake_loog.error(Backtrace.new(e))
320
- end
321
- wait_for { !active.empty? }
322
- sum = 55_000
323
- wallet.pay(JEFF, walter, sum)
324
- wait_for { !event.nil? }
325
- daemon.kill
326
- daemon.join(30)
327
- assert_equal(sum, event[:amount])
328
- end
329
- end
330
- end
331
-
332
- def test_checks_balance_via_proxy
333
- WebMock.enable_net_connect!
334
- b = nil
335
- via_proxy do |proxy|
336
- on_hardhat do |w|
337
- wallet = through_proxy(w, proxy)
338
- b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
339
- end
340
- end
341
- assert_equal(123_000_100_000, b)
342
- end
343
- end
@@ -1,126 +0,0 @@
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