erc20 0.1.1 → 0.1.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +17 -15
- data/README.md +20 -5
- data/lib/erc20/erc20.rb +1 -1
- data/lib/erc20/wallet.rb +15 -6
- data/test/erc20/test_fake_wallet.rb +1 -2
- data/test/erc20/test_wallet.rb +53 -2
- data/test/test__helper.rb +19 -6
- metadata +2 -3
- data/.simplecov +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96f0b876fa43c7c6ca87ebd998987fce27fa8a3c1923d906e71d674265083e7a
|
4
|
+
data.tar.gz: 9b43b005266bb003a086b4457a0d31f1dcd7eba08b82d6301ae5a93f9bfd32d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1d3e51d047763a3139c77286b41af1fdfeb40c2d10ed0c86dbfe1f1443d328957a1b37fc27a147ffcfcbb2c40f1071144a781292cf71e58dd5858045740cefd4
|
7
|
+
data.tar.gz: d4baf3bd795940d5daad717ec0b8b182bc849fc000fd402ae3906d93ad31d371c1ed5aaf82e02af2ccc4ff051bd24b83104c73316c34eeb917c094e747f1f9f0
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -46,14 +46,15 @@ GEM
|
|
46
46
|
cucumber-tag-expressions (6.1.2)
|
47
47
|
diff-lcs (1.6.1)
|
48
48
|
docile (1.4.1)
|
49
|
-
donce (0.
|
50
|
-
backtrace (
|
51
|
-
os (
|
52
|
-
qbash (
|
49
|
+
donce (0.2.0)
|
50
|
+
backtrace (~> 0.3)
|
51
|
+
os (~> 1.1)
|
52
|
+
qbash (~> 0.3)
|
53
53
|
elapsed (0.0.1)
|
54
54
|
loog (> 0)
|
55
55
|
tago (> 0)
|
56
|
-
eth (0.5.
|
56
|
+
eth (0.5.14)
|
57
|
+
bigdecimal (~> 3.1)
|
57
58
|
forwardable (~> 1.3)
|
58
59
|
keccak (~> 1.3)
|
59
60
|
konstructor (~> 1.0)
|
@@ -63,7 +64,7 @@ GEM
|
|
63
64
|
ethon (0.16.0)
|
64
65
|
ffi (>= 1.15.0)
|
65
66
|
eventmachine (1.2.7)
|
66
|
-
faraday (2.
|
67
|
+
faraday (2.13.0)
|
67
68
|
faraday-net_http (>= 2.0, < 3.5)
|
68
69
|
json
|
69
70
|
logger
|
@@ -72,10 +73,10 @@ GEM
|
|
72
73
|
faye-websocket (0.11.3)
|
73
74
|
eventmachine (>= 0.12.0)
|
74
75
|
websocket-driver (>= 0.5.1)
|
75
|
-
ffi (1.17.
|
76
|
-
ffi (1.17.
|
77
|
-
ffi (1.17.
|
78
|
-
ffi (1.17.
|
76
|
+
ffi (1.17.2-arm64-darwin)
|
77
|
+
ffi (1.17.2-x64-mingw-ucrt)
|
78
|
+
ffi (1.17.2-x86_64-darwin)
|
79
|
+
ffi (1.17.2-x86_64-linux-gnu)
|
79
80
|
ffi-compiler (1.3.2)
|
80
81
|
ffi (>= 1.15.5)
|
81
82
|
rake
|
@@ -106,11 +107,11 @@ GEM
|
|
106
107
|
uri
|
107
108
|
openssl (3.3.0)
|
108
109
|
os (1.1.4)
|
109
|
-
parallel (1.
|
110
|
-
parser (3.3.
|
110
|
+
parallel (1.27.0)
|
111
|
+
parser (3.3.8.0)
|
111
112
|
ast (~> 2.4.1)
|
112
113
|
racc
|
113
|
-
pkg-config (1.6.
|
114
|
+
pkg-config (1.6.1)
|
114
115
|
prism (1.4.0)
|
115
116
|
qbash (0.4.0)
|
116
117
|
backtrace (> 0)
|
@@ -139,7 +140,7 @@ GEM
|
|
139
140
|
rubocop-ast (>= 1.44.0, < 2.0)
|
140
141
|
ruby-progressbar (~> 1.7)
|
141
142
|
unicode-display_width (>= 2.4.0, < 4.0)
|
142
|
-
rubocop-ast (1.44.
|
143
|
+
rubocop-ast (1.44.1)
|
143
144
|
parser (>= 3.3.7.2)
|
144
145
|
prism (~> 1.4)
|
145
146
|
rubocop-minitest (0.38.0)
|
@@ -153,7 +154,7 @@ GEM
|
|
153
154
|
rubocop-rake (0.7.1)
|
154
155
|
lint_roller (~> 1.1)
|
155
156
|
rubocop (>= 1.72.1)
|
156
|
-
rubocop-rspec (3.
|
157
|
+
rubocop-rspec (3.6.0)
|
157
158
|
lint_roller (~> 1.1)
|
158
159
|
rubocop (~> 1.72, >= 1.72.1)
|
159
160
|
ruby-progressbar (1.13.0)
|
@@ -200,6 +201,7 @@ PLATFORMS
|
|
200
201
|
|
201
202
|
DEPENDENCIES
|
202
203
|
backtrace (> 0)
|
204
|
+
concurrent-ruby (~> 1.2)
|
203
205
|
cucumber (~> 9.2)
|
204
206
|
donce (> 0)
|
205
207
|
erc20!
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# Ethereum ERC20 Manipulations in Ruby
|
2
2
|
|
3
|
-
[](https://www.rultor.com/p/yegor256/erc20)
|
4
4
|
[](https://www.jetbrains.com/ruby/)
|
5
5
|
|
6
6
|
[](https://github.com/yegor256/erc20/actions/workflows/rake.yml)
|
7
|
-
[](
|
7
|
+
[](https://www.0pdd.com/p?name=yegor256/erc20)
|
8
|
+
[](https://badge.fury.io/rb/erc20)
|
9
9
|
[](https://codecov.io/github/yegor256/erc20?branch=master)
|
10
|
-
[](https://rubydoc.info/github/yegor256/erc20/master/frames)
|
11
11
|
[](https://hitsofcode.com/view/github/yegor256/erc20)
|
12
12
|
[](https://github.com/yegor256/erc20/blob/master/LICENSE.txt)
|
13
13
|
|
@@ -15,7 +15,22 @@ This small Ruby [gem](https://rubygems.org/gems/erc20)
|
|
15
15
|
makes manipulations with [Ethereum] [ERC20] tokens
|
16
16
|
as simple as possible, when you have a provider of
|
17
17
|
[JSON-RPC] and [WebSockets] Ethereum APIs, such as
|
18
|
-
[Infura], [GetBlock], or [Alchemy]
|
18
|
+
[Infura], [GetBlock], or [Alchemy].
|
19
|
+
|
20
|
+
Install it like this:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
gem install erc20
|
24
|
+
```
|
25
|
+
|
26
|
+
Or simply add this to your Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'erc20'
|
30
|
+
```
|
31
|
+
|
32
|
+
Then, make an instance of the main class and use to read
|
33
|
+
balances, send and receive payments:
|
19
34
|
|
20
35
|
```ruby
|
21
36
|
# Create a wallet:
|
data/lib/erc20/erc20.rb
CHANGED
data/lib/erc20/wallet.rb
CHANGED
@@ -121,7 +121,7 @@ class ERC20::Wallet
|
|
121
121
|
data = "0x#{func}000000000000000000000000#{address[2..].downcase}"
|
122
122
|
r = jsonrpc.eth_call({ to: @contract, data: data }, 'latest')
|
123
123
|
b = r[2..].to_i(16)
|
124
|
-
@log.debug("
|
124
|
+
@log.debug("The balance of #{address} is #{b} ERC20 tokens")
|
125
125
|
b
|
126
126
|
end
|
127
127
|
|
@@ -138,7 +138,7 @@ class ERC20::Wallet
|
|
138
138
|
raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(address)
|
139
139
|
r = jsonrpc.eth_getBalance(address, 'latest')
|
140
140
|
b = r[2..].to_i(16)
|
141
|
-
@log.debug("
|
141
|
+
@log.debug("The balance of #{address} is #{b} ETHs")
|
142
142
|
b
|
143
143
|
end
|
144
144
|
|
@@ -294,9 +294,14 @@ class ERC20::Wallet
|
|
294
294
|
# Once we actually start listening, the +active+ array will be updated
|
295
295
|
# with the list of addresses.
|
296
296
|
#
|
297
|
-
# The +addresses+ must have +to_a()+ implemented.
|
298
|
-
#
|
299
|
-
#
|
297
|
+
# The +addresses+ must have +to_a()+ implemented. This method will be
|
298
|
+
# called every +delay+ seconds. It is expected that it returns the list
|
299
|
+
# of Ethereum public addresses that must be monitored.
|
300
|
+
#
|
301
|
+
# The +active+ must have +append()+ and +to_a()+ implemented. This array
|
302
|
+
# maintains the list of addresses that were mentioned in incoming transactions.
|
303
|
+
# This array is used mostly for testing. It is suggested to always provide
|
304
|
+
# an empty array.
|
300
305
|
#
|
301
306
|
# @param [Array<String>] addresses Addresses to monitor
|
302
307
|
# @param [Array] active List of addresses that we are actually listening to
|
@@ -353,7 +358,11 @@ class ERC20::Wallet
|
|
353
358
|
"from #{event[:from]} to #{event[:to]} in #{event[:txn]}"
|
354
359
|
)
|
355
360
|
end
|
356
|
-
|
361
|
+
begin
|
362
|
+
yield event
|
363
|
+
rescue StandardError => e
|
364
|
+
@log.error(Backtrace.new(e).to_s)
|
365
|
+
end
|
357
366
|
end
|
358
367
|
end
|
359
368
|
end
|
@@ -8,7 +8,6 @@ require 'donce'
|
|
8
8
|
require 'eth'
|
9
9
|
require 'faraday'
|
10
10
|
require 'loog'
|
11
|
-
require 'minitest/autorun'
|
12
11
|
require 'random-port'
|
13
12
|
require 'shellwords'
|
14
13
|
require 'threads'
|
@@ -20,7 +19,7 @@ require_relative '../../lib/erc20/fake_wallet'
|
|
20
19
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
21
20
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
22
21
|
# License:: MIT
|
23
|
-
class TestFakeWallet <
|
22
|
+
class TestFakeWallet < ERC20::Test
|
24
23
|
def test_checks_gas_estimate
|
25
24
|
b = ERC20::FakeWallet.new.gas_estimate(
|
26
25
|
'0xEB2fE8872A6f1eDb70a2632Effffffffffffffff',
|
data/test/erc20/test_wallet.rb
CHANGED
@@ -8,7 +8,6 @@ require 'donce'
|
|
8
8
|
require 'eth'
|
9
9
|
require 'faraday'
|
10
10
|
require 'loog'
|
11
|
-
require 'minitest/autorun'
|
12
11
|
require 'random-port'
|
13
12
|
require 'shellwords'
|
14
13
|
require 'threads'
|
@@ -20,7 +19,7 @@ require_relative '../../lib/erc20/wallet'
|
|
20
19
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
21
20
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
22
21
|
# License:: MIT
|
23
|
-
class TestWallet <
|
22
|
+
class TestWallet < ERC20::Test
|
24
23
|
# At this address, in Ethereum mainnet, there are $8 USDT and 0.0042 ETH. I won't
|
25
24
|
# move them anyway, that's why tests can use this address forever.
|
26
25
|
STABLE = '0x7232148927F8a580053792f44D4d59d40Fd00ABD'
|
@@ -60,6 +59,7 @@ class TestWallet < Minitest::Test
|
|
60
59
|
def test_fails_with_invalid_infura_key
|
61
60
|
skip('Apparently, even with invalid key, Infura returns balance')
|
62
61
|
w = ERC20::Wallet.new(
|
62
|
+
contract: ERC20::Wallet.USDT,
|
63
63
|
host: 'mainnet.infura.io',
|
64
64
|
http_path: '/v3/invalid-key-here',
|
65
65
|
log: loog
|
@@ -201,6 +201,57 @@ class TestWallet < Minitest::Test
|
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
+
def test_accepts_many_payments_on_hardhat
|
205
|
+
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
206
|
+
on_hardhat do |wallet|
|
207
|
+
active = []
|
208
|
+
events = Concurrent::Set.new
|
209
|
+
total = 10
|
210
|
+
daemon =
|
211
|
+
Thread.new do
|
212
|
+
wallet.accept([walter], active) do |e|
|
213
|
+
events.add(e)
|
214
|
+
end
|
215
|
+
rescue StandardError => e
|
216
|
+
loog.error(Backtrace.new(e))
|
217
|
+
end
|
218
|
+
wait_for { !active.empty? }
|
219
|
+
sum = 1_234
|
220
|
+
Threads.new(total).assert do
|
221
|
+
wallet.pay(JEFF, walter, sum)
|
222
|
+
end
|
223
|
+
wait_for { events.size == total }
|
224
|
+
daemon.kill
|
225
|
+
daemon.join(30)
|
226
|
+
assert_equal(total, events.size)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_accepts_payments_with_failures_on_hardhat
|
231
|
+
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
232
|
+
on_hardhat do |wallet|
|
233
|
+
active = []
|
234
|
+
events = Concurrent::Set.new
|
235
|
+
total = 10
|
236
|
+
daemon =
|
237
|
+
Thread.new do
|
238
|
+
wallet.accept([walter], active) do |e|
|
239
|
+
events.add(e)
|
240
|
+
raise 'intentional'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
wait_for { !active.empty? }
|
244
|
+
sum = 1_234
|
245
|
+
Threads.new(total).assert do
|
246
|
+
wallet.pay(JEFF, walter, sum)
|
247
|
+
end
|
248
|
+
wait_for { events.size == total }
|
249
|
+
daemon.kill
|
250
|
+
daemon.join(30)
|
251
|
+
assert_equal(total, events.size)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
204
255
|
def test_accepts_payments_on_changing_addresses_on_hardhat
|
205
256
|
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
206
257
|
jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
|
data/test/test__helper.rb
CHANGED
@@ -6,14 +6,27 @@
|
|
6
6
|
$stdout.sync = true
|
7
7
|
|
8
8
|
require 'simplecov'
|
9
|
-
SimpleCov.external_at_exit = true
|
10
|
-
SimpleCov.start
|
11
|
-
|
12
9
|
require 'simplecov-cobertura'
|
13
|
-
SimpleCov.
|
10
|
+
unless SimpleCov.running
|
11
|
+
SimpleCov.command_name('test')
|
12
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
13
|
+
[
|
14
|
+
SimpleCov::Formatter::HTMLFormatter,
|
15
|
+
SimpleCov::Formatter::CoberturaFormatter
|
16
|
+
]
|
17
|
+
)
|
18
|
+
SimpleCov.minimum_coverage 90
|
19
|
+
SimpleCov.minimum_coverage_by_file 90
|
20
|
+
SimpleCov.start do
|
21
|
+
add_filter 'test/'
|
22
|
+
add_filter 'vendor/'
|
23
|
+
add_filter 'target/'
|
24
|
+
track_files 'lib/**/*.rb'
|
25
|
+
track_files '*.rb'
|
26
|
+
end
|
27
|
+
end
|
14
28
|
|
15
29
|
require 'minitest/autorun'
|
16
|
-
|
17
30
|
require 'minitest/reporters'
|
18
31
|
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
19
32
|
|
@@ -42,7 +55,7 @@ end
|
|
42
55
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
43
56
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
44
57
|
# License:: MIT
|
45
|
-
class Minitest::Test
|
58
|
+
class ERC20::Test < Minitest::Test
|
46
59
|
def loog
|
47
60
|
ENV['RAKE'] ? Loog::ERRORS : Loog::VERBOSE
|
48
61
|
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.
|
4
|
+
version: 0.1.2
|
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-20 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: eth
|
@@ -122,7 +122,6 @@ files:
|
|
122
122
|
- ".pdd"
|
123
123
|
- ".rubocop.yml"
|
124
124
|
- ".rultor.yml"
|
125
|
-
- ".simplecov"
|
126
125
|
- ".yamllint.yml"
|
127
126
|
- Gemfile
|
128
127
|
- Gemfile.lock
|
data/.simplecov
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
#
|
4
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
|
5
|
-
# SPDX-License-Identifier: MIT
|
6
|
-
|
7
|
-
if Gem.win_platform?
|
8
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
9
|
-
SimpleCov::Formatter::HTMLFormatter
|
10
|
-
]
|
11
|
-
SimpleCov.start do
|
12
|
-
add_filter '/test/'
|
13
|
-
end
|
14
|
-
else
|
15
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
|
16
|
-
[SimpleCov::Formatter::HTMLFormatter]
|
17
|
-
)
|
18
|
-
SimpleCov.start do
|
19
|
-
add_filter '/test/'
|
20
|
-
minimum_coverage 20
|
21
|
-
end
|
22
|
-
end
|