erc20 0.2.9 → 0.4.0
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 +6 -4
- data/Gemfile.lock +83 -72
- data/README.md +15 -4
- data/Rakefile +2 -2
- data/bin/erc20 +21 -8
- data/erc20.gemspec +8 -8
- data/features/dry.feature +9 -1
- data/features/step_definitions/steps.rb +5 -5
- data/features/support/env.rb +1 -1
- data/lib/erc20/checks.rb +102 -0
- data/lib/erc20/erc20.rb +1 -2
- data/lib/erc20/fake_wallet.rb +59 -25
- data/lib/erc20/wallet.rb +321 -198
- data/lib/erc20.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aae427d3d4d3c935aa7ea204031b9bdd85c67ce26fd182cb3a3d4ac8c2ed3cd4
|
|
4
|
+
data.tar.gz: 54fce4882a54f7de725bcc40ca234538900dfa67bcae210b23544ac0806d31d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d49f09b1f3210391496b670aa467455584ba1cce21a19b2247bd6874438291803aef1e367e78c8027e2b6e1eb174ff7950eb002ae46ef272dcc59381948c7c0
|
|
7
|
+
data.tar.gz: ea308190d652a2f7747e24f93910e8d4a2c593acbc01edd53d60594e56f96652591a49281d6f784873f46c2474e99385292554eea66eb3cb6a856efd24c6796e
|
data/Gemfile
CHANGED
|
@@ -8,8 +8,9 @@ gemspec
|
|
|
8
8
|
|
|
9
9
|
gem 'backtrace', '~>0.4', require: false
|
|
10
10
|
gem 'concurrent-ruby', '~>1.2', require: false
|
|
11
|
-
gem 'cucumber', '~>
|
|
11
|
+
gem 'cucumber', '~>11.0', require: false
|
|
12
12
|
gem 'donce', '~>0.2', require: false
|
|
13
|
+
gem 'em-websocket', '~>0.5', require: false
|
|
13
14
|
gem 'faraday', '~>2.14', require: false
|
|
14
15
|
gem 'loog', '~>0.6', require: false
|
|
15
16
|
gem 'minitest', '~>6.0', require: false
|
|
@@ -18,13 +19,14 @@ gem 'minitest-retry', '~>0.2', require: false
|
|
|
18
19
|
gem 'qbash', '~>0.4', require: false
|
|
19
20
|
gem 'rake', '~>13.2', require: false
|
|
20
21
|
gem 'random-port', '~>0.7', require: false
|
|
21
|
-
gem 'rdoc', '~>
|
|
22
|
+
gem 'rdoc', '~>8.0', require: false
|
|
22
23
|
gem 'rubocop', '~>1.75', require: false
|
|
24
|
+
gem 'rubocop-elegant', '~>0.5', require: false
|
|
23
25
|
gem 'rubocop-minitest', '~>0.38', require: false
|
|
24
26
|
gem 'rubocop-performance', '~>1.25', require: false
|
|
25
27
|
gem 'rubocop-rake', '~>0.7', require: false
|
|
26
|
-
gem 'simplecov', '~>0
|
|
27
|
-
gem 'simplecov-cobertura', '~>
|
|
28
|
+
gem 'simplecov', '~>1.0', require: false
|
|
29
|
+
gem 'simplecov-cobertura', '~>4.0', require: false
|
|
28
30
|
gem 'threads', '~>0.4', require: false
|
|
29
31
|
gem 'typhoeus', '~>1.5', require: false
|
|
30
32
|
gem 'webmock', '~>3.23', require: false
|
data/Gemfile.lock
CHANGED
|
@@ -13,55 +13,57 @@ PATH
|
|
|
13
13
|
GEM
|
|
14
14
|
remote: https://rubygems.org/
|
|
15
15
|
specs:
|
|
16
|
-
addressable (2.
|
|
16
|
+
addressable (2.9.0)
|
|
17
17
|
public_suffix (>= 2.0.2, < 8.0)
|
|
18
|
-
ansi (1.
|
|
18
|
+
ansi (1.6.0)
|
|
19
19
|
ast (2.4.3)
|
|
20
20
|
backtrace (0.4.1)
|
|
21
21
|
base64 (0.3.0)
|
|
22
|
-
bigdecimal (4.
|
|
22
|
+
bigdecimal (4.1.2)
|
|
23
23
|
builder (3.3.0)
|
|
24
|
-
concurrent-ruby (1.3.
|
|
24
|
+
concurrent-ruby (1.3.8)
|
|
25
25
|
crack (1.0.1)
|
|
26
26
|
bigdecimal
|
|
27
27
|
rexml
|
|
28
|
-
cucumber (
|
|
28
|
+
cucumber (11.1.1)
|
|
29
29
|
base64 (~> 0.2)
|
|
30
30
|
builder (~> 3.2)
|
|
31
31
|
cucumber-ci-environment (> 9, < 12)
|
|
32
|
-
cucumber-core (
|
|
32
|
+
cucumber-core (>= 16.2.0, < 17)
|
|
33
33
|
cucumber-cucumber-expressions (> 17, < 20)
|
|
34
|
-
cucumber-html-formatter (> 21, <
|
|
34
|
+
cucumber-html-formatter (> 21, < 24)
|
|
35
35
|
diff-lcs (~> 1.5)
|
|
36
36
|
logger (~> 1.6)
|
|
37
37
|
mini_mime (~> 1.1)
|
|
38
38
|
multi_test (~> 1.1)
|
|
39
|
-
sys-uname (~> 1.
|
|
39
|
+
sys-uname (~> 1.5)
|
|
40
40
|
cucumber-ci-environment (11.0.0)
|
|
41
|
-
cucumber-core (16.
|
|
41
|
+
cucumber-core (16.2.0)
|
|
42
42
|
cucumber-gherkin (> 36, < 40)
|
|
43
43
|
cucumber-messages (> 31, < 33)
|
|
44
44
|
cucumber-tag-expressions (> 6, < 9)
|
|
45
|
-
cucumber-cucumber-expressions (19.0.
|
|
45
|
+
cucumber-cucumber-expressions (19.0.1)
|
|
46
46
|
bigdecimal
|
|
47
|
-
cucumber-gherkin (
|
|
47
|
+
cucumber-gherkin (39.1.0)
|
|
48
48
|
cucumber-messages (>= 31, < 33)
|
|
49
|
-
cucumber-html-formatter (
|
|
49
|
+
cucumber-html-formatter (23.1.0)
|
|
50
50
|
cucumber-messages (> 23, < 33)
|
|
51
|
-
cucumber-messages (32.
|
|
51
|
+
cucumber-messages (32.3.1)
|
|
52
52
|
cucumber-tag-expressions (8.1.0)
|
|
53
|
-
date (3.5.1)
|
|
54
53
|
diff-lcs (1.6.2)
|
|
55
|
-
|
|
56
|
-
donce (0.4.0)
|
|
54
|
+
donce (0.7.0)
|
|
57
55
|
backtrace (~> 0.3)
|
|
58
56
|
os (~> 1.1)
|
|
59
57
|
qbash (~> 0.3)
|
|
60
|
-
|
|
58
|
+
drb (2.2.3)
|
|
59
|
+
elapsed (0.3.1)
|
|
61
60
|
loog (~> 0.6)
|
|
62
61
|
tago (~> 0.1)
|
|
63
62
|
ellipsized (0.3.0)
|
|
64
|
-
|
|
63
|
+
em-websocket (0.5.3)
|
|
64
|
+
eventmachine (>= 0.12.9)
|
|
65
|
+
http_parser.rb (~> 0)
|
|
66
|
+
erb (6.0.6)
|
|
65
67
|
eth (0.5.13)
|
|
66
68
|
forwardable (~> 1.3)
|
|
67
69
|
keccak (~> 1.3)
|
|
@@ -69,102 +71,110 @@ GEM
|
|
|
69
71
|
openssl (>= 2.2, < 4.0)
|
|
70
72
|
rbsecp256k1 (~> 6.0)
|
|
71
73
|
scrypt (~> 3.0)
|
|
72
|
-
ethon (0.
|
|
74
|
+
ethon (0.18.0)
|
|
73
75
|
ffi (>= 1.15.0)
|
|
76
|
+
logger
|
|
74
77
|
eventmachine (1.2.7)
|
|
75
|
-
faraday (2.14.
|
|
78
|
+
faraday (2.14.3)
|
|
76
79
|
faraday-net_http (>= 2.0, < 3.5)
|
|
77
80
|
json
|
|
78
81
|
logger
|
|
79
|
-
faraday-net_http (3.4.
|
|
82
|
+
faraday-net_http (3.4.4)
|
|
80
83
|
net-http (~> 0.5)
|
|
81
84
|
faye-websocket (0.12.0)
|
|
82
85
|
eventmachine (>= 0.12.0)
|
|
83
86
|
websocket-driver (>= 0.8.0)
|
|
84
|
-
ffi (1.17.
|
|
85
|
-
ffi (1.17.
|
|
86
|
-
ffi (1.17.
|
|
87
|
-
ffi (1.17.
|
|
88
|
-
ffi-compiler (1.
|
|
87
|
+
ffi (1.17.4-arm64-darwin)
|
|
88
|
+
ffi (1.17.4-x64-mingw-ucrt)
|
|
89
|
+
ffi (1.17.4-x86_64-darwin)
|
|
90
|
+
ffi (1.17.4-x86_64-linux-gnu)
|
|
91
|
+
ffi-compiler (1.4.2)
|
|
89
92
|
ffi (>= 1.15.5)
|
|
90
93
|
rake
|
|
91
94
|
forwardable (1.4.0)
|
|
92
95
|
hashdiff (1.2.1)
|
|
93
|
-
|
|
96
|
+
http_parser.rb (0.8.1)
|
|
97
|
+
json (2.21.1)
|
|
94
98
|
jsonrpc-client (0.1.4)
|
|
95
99
|
faraday
|
|
96
100
|
multi_json (>= 1.1.0)
|
|
97
|
-
keccak (1.3.
|
|
101
|
+
keccak (1.3.3)
|
|
98
102
|
konstructor (1.0.2)
|
|
99
|
-
language_server-protocol (3.17.0.
|
|
103
|
+
language_server-protocol (3.17.0.6)
|
|
100
104
|
lint_roller (1.1.0)
|
|
101
105
|
logger (1.7.0)
|
|
102
|
-
loog (0.
|
|
106
|
+
loog (0.8.0)
|
|
103
107
|
ellipsized
|
|
104
108
|
logger (~> 1.0)
|
|
105
109
|
memoist3 (1.0.0)
|
|
106
110
|
mini_mime (1.1.5)
|
|
107
111
|
mini_portile2 (2.8.9)
|
|
108
|
-
minitest (6.0.
|
|
112
|
+
minitest (6.0.6)
|
|
113
|
+
drb (~> 2.0)
|
|
109
114
|
prism (~> 1.5)
|
|
110
|
-
minitest-reporters (1.
|
|
115
|
+
minitest-reporters (1.8.0)
|
|
111
116
|
ansi
|
|
112
117
|
builder
|
|
113
|
-
minitest (>= 5.0)
|
|
118
|
+
minitest (>= 5.0, < 7)
|
|
114
119
|
ruby-progressbar
|
|
115
120
|
minitest-retry (0.3.1)
|
|
116
121
|
minitest (>= 5.0)
|
|
117
|
-
multi_json (1.
|
|
122
|
+
multi_json (1.21.1)
|
|
118
123
|
multi_test (1.1.0)
|
|
119
124
|
net-http (0.9.1)
|
|
120
125
|
uri (>= 0.11.1)
|
|
121
|
-
openssl (3.3.
|
|
126
|
+
openssl (3.3.3)
|
|
122
127
|
os (1.1.4)
|
|
123
|
-
parallel (1.
|
|
124
|
-
parser (3.3.
|
|
128
|
+
parallel (2.1.0)
|
|
129
|
+
parser (3.3.12.0)
|
|
125
130
|
ast (~> 2.4.1)
|
|
126
131
|
racc
|
|
127
132
|
pkg-config (1.6.5)
|
|
128
|
-
prism (1.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
stringio
|
|
132
|
-
public_suffix (7.0.2)
|
|
133
|
-
qbash (0.6.0)
|
|
133
|
+
prism (1.9.0)
|
|
134
|
+
public_suffix (7.0.5)
|
|
135
|
+
qbash (0.8.4)
|
|
134
136
|
backtrace (> 0)
|
|
135
137
|
elapsed (> 0)
|
|
136
138
|
loog (> 0)
|
|
137
139
|
tago (> 0)
|
|
138
140
|
racc (1.8.1)
|
|
139
141
|
rainbow (3.1.1)
|
|
140
|
-
rake (13.
|
|
141
|
-
random-port (0.
|
|
142
|
+
rake (13.4.2)
|
|
143
|
+
random-port (0.8.2)
|
|
142
144
|
tago (~> 0.0)
|
|
145
|
+
rbs (4.1.0)
|
|
146
|
+
logger
|
|
147
|
+
prism (>= 1.6.0)
|
|
148
|
+
tsort
|
|
143
149
|
rbsecp256k1 (6.0.0)
|
|
144
150
|
mini_portile2 (~> 2.8)
|
|
145
151
|
pkg-config (~> 1.5)
|
|
146
152
|
rubyzip (~> 2.3)
|
|
147
|
-
rdoc (
|
|
153
|
+
rdoc (8.0.0)
|
|
148
154
|
erb
|
|
149
|
-
|
|
155
|
+
prism (>= 1.6.0)
|
|
156
|
+
rbs (>= 4.0.0)
|
|
150
157
|
tsort
|
|
151
|
-
regexp_parser (2.
|
|
158
|
+
regexp_parser (2.12.0)
|
|
152
159
|
rexml (3.4.4)
|
|
153
|
-
rubocop (1.
|
|
160
|
+
rubocop (1.88.2)
|
|
154
161
|
json (~> 2.3)
|
|
155
162
|
language_server-protocol (~> 3.17.0.2)
|
|
156
163
|
lint_roller (~> 1.1.0)
|
|
157
|
-
parallel (
|
|
164
|
+
parallel (>= 1.10)
|
|
158
165
|
parser (>= 3.3.0.2)
|
|
159
166
|
rainbow (>= 2.2.2, < 4.0)
|
|
160
167
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
161
|
-
rubocop-ast (>= 1.
|
|
168
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
162
169
|
ruby-progressbar (~> 1.7)
|
|
163
170
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
164
|
-
rubocop-ast (1.
|
|
171
|
+
rubocop-ast (1.50.0)
|
|
165
172
|
parser (>= 3.3.7.2)
|
|
166
173
|
prism (~> 1.7)
|
|
167
|
-
rubocop-
|
|
174
|
+
rubocop-elegant (0.7.1)
|
|
175
|
+
lint_roller (~> 1.1)
|
|
176
|
+
rubocop (~> 1.75)
|
|
177
|
+
rubocop-minitest (0.40.0)
|
|
168
178
|
lint_roller (~> 1.1)
|
|
169
179
|
rubocop (>= 1.75.0, < 2.0)
|
|
170
180
|
rubocop-ast (>= 1.38.0, < 2.0)
|
|
@@ -180,40 +190,39 @@ GEM
|
|
|
180
190
|
scrypt (3.1.0)
|
|
181
191
|
ffi-compiler (>= 1.0, < 2.0)
|
|
182
192
|
rake (~> 13)
|
|
183
|
-
simplecov (0.
|
|
184
|
-
|
|
185
|
-
simplecov-html (~> 0.11)
|
|
186
|
-
simplecov_json_formatter (~> 0.1)
|
|
187
|
-
simplecov-cobertura (3.1.0)
|
|
193
|
+
simplecov (1.0.3)
|
|
194
|
+
simplecov-cobertura (4.0.0)
|
|
188
195
|
rexml
|
|
189
|
-
simplecov (~> 0
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
sys-uname (1.
|
|
196
|
+
simplecov (~> 1.0)
|
|
197
|
+
sys-uname (1.5.1)
|
|
198
|
+
ffi (~> 1.1)
|
|
199
|
+
memoist3 (~> 1.0.0)
|
|
200
|
+
sys-uname (1.5.1-universal-mingw32)
|
|
194
201
|
ffi (~> 1.1)
|
|
195
202
|
memoist3 (~> 1.0.0)
|
|
203
|
+
win32ole
|
|
196
204
|
tago (0.7.0)
|
|
197
205
|
thor (1.5.0)
|
|
198
206
|
threads (0.5.0)
|
|
199
207
|
backtrace (~> 0)
|
|
200
208
|
concurrent-ruby (~> 1.0)
|
|
201
209
|
tsort (0.2.0)
|
|
202
|
-
typhoeus (1.
|
|
203
|
-
ethon (>= 0.
|
|
210
|
+
typhoeus (1.6.0)
|
|
211
|
+
ethon (>= 0.18.0)
|
|
204
212
|
unicode-display_width (3.2.0)
|
|
205
213
|
unicode-emoji (~> 4.1)
|
|
206
214
|
unicode-emoji (4.2.0)
|
|
207
215
|
uri (1.1.1)
|
|
208
|
-
webmock (3.26.
|
|
216
|
+
webmock (3.26.2)
|
|
209
217
|
addressable (>= 2.8.0)
|
|
210
218
|
crack (>= 0.3.2)
|
|
211
219
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
212
|
-
websocket-driver (0.8.
|
|
220
|
+
websocket-driver (0.8.2)
|
|
213
221
|
base64
|
|
214
222
|
websocket-extensions (>= 0.1.0)
|
|
215
223
|
websocket-extensions (0.1.5)
|
|
216
|
-
|
|
224
|
+
win32ole (1.9.3)
|
|
225
|
+
yard (0.9.45)
|
|
217
226
|
|
|
218
227
|
PLATFORMS
|
|
219
228
|
arm64-darwin-22
|
|
@@ -227,8 +236,9 @@ PLATFORMS
|
|
|
227
236
|
DEPENDENCIES
|
|
228
237
|
backtrace (~> 0.4)
|
|
229
238
|
concurrent-ruby (~> 1.2)
|
|
230
|
-
cucumber (~>
|
|
239
|
+
cucumber (~> 11.0)
|
|
231
240
|
donce (~> 0.2)
|
|
241
|
+
em-websocket (~> 0.5)
|
|
232
242
|
erc20!
|
|
233
243
|
faraday (~> 2.14)
|
|
234
244
|
loog (~> 0.6)
|
|
@@ -238,13 +248,14 @@ DEPENDENCIES
|
|
|
238
248
|
qbash (~> 0.4)
|
|
239
249
|
rake (~> 13.2)
|
|
240
250
|
random-port (~> 0.7)
|
|
241
|
-
rdoc (~>
|
|
251
|
+
rdoc (~> 8.0)
|
|
242
252
|
rubocop (~> 1.75)
|
|
253
|
+
rubocop-elegant (~> 0.5)
|
|
243
254
|
rubocop-minitest (~> 0.38)
|
|
244
255
|
rubocop-performance (~> 1.25)
|
|
245
256
|
rubocop-rake (~> 0.7)
|
|
246
|
-
simplecov (~> 0
|
|
247
|
-
simplecov-cobertura (~>
|
|
257
|
+
simplecov (~> 1.0)
|
|
258
|
+
simplecov-cobertura (~> 4.0)
|
|
248
259
|
threads (~> 0.4)
|
|
249
260
|
typhoeus (~> 1.5)
|
|
250
261
|
webmock (~> 3.23)
|
data/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Or simply add this to your Gemfile:
|
|
|
29
29
|
gem 'erc20'
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
Then, make an instance of the main class and use to read
|
|
32
|
+
Then, make an instance of the main class and use it to read
|
|
33
33
|
balances, send and receive payments:
|
|
34
34
|
|
|
35
35
|
```ruby
|
|
@@ -40,6 +40,8 @@ w = ERC20::Wallet.new(
|
|
|
40
40
|
host: 'mainnet.infura.io',
|
|
41
41
|
http_path: '/v3/<your-infura-key>',
|
|
42
42
|
ws_path: '/ws/v3/<your-infura-key>',
|
|
43
|
+
attempts: 3, # try every endpoint up to 3 times, with backoff
|
|
44
|
+
fallbacks: ['https://eth.drpc.org'], # alternative RPC endpoints to try
|
|
43
45
|
log: $stdout
|
|
44
46
|
)
|
|
45
47
|
|
|
@@ -56,9 +58,16 @@ w.accept(addresses) do |event|
|
|
|
56
58
|
puts event[:amount] # how much, in tokens (1000000 = $1 USDT)
|
|
57
59
|
puts event[:from] # who sent the payment
|
|
58
60
|
puts event[:to] # who was the receiver
|
|
61
|
+
puts event[:block] # number of the block it was mined in
|
|
62
|
+
puts event[:index] # position of the log in the block
|
|
59
63
|
end
|
|
60
64
|
```
|
|
61
65
|
|
|
66
|
+
When the connection to the provider breaks, `accept` reconnects and fetches the
|
|
67
|
+
logs of the blocks that were mined while it was away. A payment may reach the
|
|
68
|
+
block twice because of that, so use `txn` and `index` together as the key of
|
|
69
|
+
the payment and credit it only once.
|
|
70
|
+
|
|
62
71
|
You can also check ETH balance and send ETH transactions:
|
|
63
72
|
|
|
64
73
|
```ruby
|
|
@@ -75,8 +84,9 @@ To check the price of a gas unit and the expected cost of a payment:
|
|
|
75
84
|
# How many gas units required to send this payment:
|
|
76
85
|
units = w.gas_estimate(from, to, amount)
|
|
77
86
|
|
|
78
|
-
# What is the
|
|
79
|
-
|
|
87
|
+
# What is the most a gas unit may cost, in wei (payments are type-2,
|
|
88
|
+
# thus the network charges only the base fee plus the tip):
|
|
89
|
+
price = w.gas_price
|
|
80
90
|
```
|
|
81
91
|
|
|
82
92
|
To generate a new private key, use [eth](https://rubygems.org/gems/eth):
|
|
@@ -106,7 +116,7 @@ w = ERC20::Wallet.new(
|
|
|
106
116
|
You can use [squid-proxy] [Docker] image to set up your own [HTTP proxy] server.
|
|
107
117
|
|
|
108
118
|
Of course, this library works with [Polygon], [Optimism],
|
|
109
|
-
and other EVM compatible blockchains.
|
|
119
|
+
and other [EVM] compatible blockchains.
|
|
110
120
|
|
|
111
121
|
## How to use in command line
|
|
112
122
|
|
|
@@ -170,3 +180,4 @@ If it's clean and you don't see any error messages, submit your pull request.
|
|
|
170
180
|
[Docker]: https://www.docker.com/
|
|
171
181
|
[Polygon]: https://polygon.technology/
|
|
172
182
|
[Optimism]: https://www.optimism.io/
|
|
183
|
+
[EVM]: https://ethereum.org/developers/docs/evm/
|
data/Rakefile
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'rake'
|
|
4
|
+
require 'rake/clean'
|
|
3
5
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
|
|
4
6
|
# SPDX-License-Identifier: MIT
|
|
5
7
|
|
|
6
8
|
require 'rubygems'
|
|
7
|
-
require 'rake'
|
|
8
|
-
require 'rake/clean'
|
|
9
9
|
|
|
10
10
|
ENV['RAKE'] = 'true'
|
|
11
11
|
|
data/bin/erc20
CHANGED
|
@@ -43,7 +43,10 @@ class Bin < Thor
|
|
|
43
43
|
class_option :proxy, type: :string,
|
|
44
44
|
desc: 'HTTP/S proxy for all requests, e.g. "localhost:3128"'
|
|
45
45
|
class_option :attempts, type: :numeric, default: 1,
|
|
46
|
-
desc: 'How many times should we try before failing'
|
|
46
|
+
desc: 'How many times should we try every endpoint before failing'
|
|
47
|
+
class_option :fallbacks, type: :array,
|
|
48
|
+
default: ['https://ethereum.publicnode.com', 'https://eth.drpc.org', 'https://rpc.ankr.com/eth'],
|
|
49
|
+
desc: 'Fallback HTTP RPC endpoint URLs to try when the primary host fails'
|
|
47
50
|
class_option :dry, type: :boolean, default: false,
|
|
48
51
|
desc: "Don't send a real payment, run in a read-only mode"
|
|
49
52
|
class_option :verbose, type: :boolean, default: false,
|
|
@@ -136,6 +139,8 @@ class Bin < Thor
|
|
|
136
139
|
host: options[:host], port: options[:port].to_i,
|
|
137
140
|
http_path: options[:http_path], ws_path: options[:ws_path],
|
|
138
141
|
ssl: options[:ssl],
|
|
142
|
+
attempts: options[:attempts].to_i,
|
|
143
|
+
fallbacks: options[:fallbacks],
|
|
139
144
|
log: log
|
|
140
145
|
)
|
|
141
146
|
end
|
|
@@ -146,10 +151,10 @@ class Bin < Thor
|
|
|
146
151
|
amount = amount.to_i
|
|
147
152
|
log.debug("The token amount equals to #{amount} ERC20 tokens (as provided)")
|
|
148
153
|
elsif /^[0-9]+(\.[0-9]+)?usdt$/.match?(amount)
|
|
149
|
-
amount = (amount.gsub(/usdt$/, '')
|
|
154
|
+
amount = units(amount.gsub(/usdt$/, ''), 1_000_000)
|
|
150
155
|
log.debug("The USDT amount equals to #{amount} ERC20 tokens")
|
|
151
156
|
elsif /^\$[0-9]+(\.[0-9]+)?$/.match?(amount)
|
|
152
|
-
amount = (amount.gsub(/^\$/, '')
|
|
157
|
+
amount = units(amount.gsub(/^\$/, ''), 1_000_000)
|
|
153
158
|
log.debug("The dollar amount equals to #{amount} ERC20 tokens")
|
|
154
159
|
else
|
|
155
160
|
raise "Can't understand amount: #{amount.inspect}"
|
|
@@ -160,16 +165,24 @@ class Bin < Thor
|
|
|
160
165
|
def wei(amount)
|
|
161
166
|
if /^[0-9]+$/.match?(amount)
|
|
162
167
|
amount.to_i
|
|
163
|
-
elsif
|
|
168
|
+
elsif /^[0-9]+wei$/.match?(amount)
|
|
164
169
|
amount.gsub(/wei$/, '').to_i
|
|
165
|
-
elsif
|
|
166
|
-
(amount.gsub(/gwei$/, '')
|
|
167
|
-
elsif
|
|
168
|
-
(amount.gsub(/eth$/, '')
|
|
170
|
+
elsif /^[0-9]+(\.[0-9]+)?gwei$/.match?(amount)
|
|
171
|
+
units(amount.gsub(/gwei$/, ''), 1_000_000_000)
|
|
172
|
+
elsif /^[0-9]+(\.[0-9]+)?eth$/.match?(amount)
|
|
173
|
+
units(amount.gsub(/eth$/, ''), 1_000_000_000_000_000_000)
|
|
169
174
|
else
|
|
170
175
|
raise "Can't understand amount: #{amount.inspect}"
|
|
171
176
|
end
|
|
172
177
|
end
|
|
178
|
+
|
|
179
|
+
def units(amount, factor)
|
|
180
|
+
exact = Rational(amount) * factor
|
|
181
|
+
unless exact.denominator == 1
|
|
182
|
+
raise "Amount #{amount.inspect} is too precise, while the smallest unit is 1/#{factor}"
|
|
183
|
+
end
|
|
184
|
+
exact.numerator
|
|
185
|
+
end
|
|
173
186
|
end
|
|
174
187
|
|
|
175
188
|
begin
|
data/erc20.gemspec
CHANGED
|
@@ -7,7 +7,7 @@ require 'English'
|
|
|
7
7
|
require_relative 'lib/erc20/erc20'
|
|
8
8
|
|
|
9
9
|
Gem::Specification.new do |s|
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to?(:required_rubygems_version=)
|
|
11
11
|
s.required_ruby_version = '~>3.0'
|
|
12
12
|
s.name = 'erc20'
|
|
13
13
|
s.version = ERC20::VERSION
|
|
@@ -25,12 +25,12 @@ Gem::Specification.new do |s|
|
|
|
25
25
|
s.rdoc_options = ['--charset=UTF-8']
|
|
26
26
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
27
27
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
28
|
-
s.add_dependency
|
|
29
|
-
s.add_dependency
|
|
30
|
-
s.add_dependency
|
|
31
|
-
s.add_dependency
|
|
32
|
-
s.add_dependency
|
|
33
|
-
s.add_dependency
|
|
34
|
-
s.add_dependency
|
|
28
|
+
s.add_dependency('elapsed', '~>0.2')
|
|
29
|
+
s.add_dependency('eth', '~>0.5')
|
|
30
|
+
s.add_dependency('faye-websocket', '~>0.11')
|
|
31
|
+
s.add_dependency('json', '~>2.10')
|
|
32
|
+
s.add_dependency('jsonrpc-client', '~>0.1')
|
|
33
|
+
s.add_dependency('loog', '~>0.4')
|
|
34
|
+
s.add_dependency('thor', '~>1.3')
|
|
35
35
|
s.metadata['rubygems_mfa_required'] = 'true'
|
|
36
36
|
end
|
data/features/dry.feature
CHANGED
|
@@ -21,13 +21,17 @@ Feature: Command Line Processing, in Dry Mode
|
|
|
21
21
|
Then Exit code is zero
|
|
22
22
|
|
|
23
23
|
Scenario: ERC20 payment can be sent in dollars
|
|
24
|
-
When I run bin/erc20 with "pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD
|
|
24
|
+
When I run bin/erc20 with "pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD \$10"
|
|
25
25
|
Then Exit code is zero
|
|
26
26
|
|
|
27
27
|
Scenario: ERC20 payment can be sent in USDT
|
|
28
28
|
When I run bin/erc20 with "pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD 10usdt"
|
|
29
29
|
Then Exit code is zero
|
|
30
30
|
|
|
31
|
+
Scenario: ERC20 payment in USDT keeps every token unit
|
|
32
|
+
When I run bin/erc20 with "pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD 8.2usdt"
|
|
33
|
+
Then Stdout contains "Sending 8200000 ERC20 tokens"
|
|
34
|
+
|
|
31
35
|
Scenario: ETH payment can be sent in wei
|
|
32
36
|
When I run bin/erc20 with "eth_pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD 10000000"
|
|
33
37
|
Then Exit code is zero
|
|
@@ -35,3 +39,7 @@ Feature: Command Line Processing, in Dry Mode
|
|
|
35
39
|
Scenario: ETH payment can be sent in ETH
|
|
36
40
|
When I run bin/erc20 with "eth_pay --dry --verbose 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD 1eth"
|
|
37
41
|
Then Exit code is zero
|
|
42
|
+
|
|
43
|
+
Scenario: ETH payment smaller than one wei is rejected
|
|
44
|
+
When I run bin/erc20 with "eth_pay --dry 46feba063e9b59a8ae0dba68abd39a3cb8f52089e776576d6eb1bb5bfec123d1 0x7232148927F8a580053792f44D4d59d40Fd00ABD 0.0000000000000000001eth"
|
|
45
|
+
Then Exit code is not zero
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'English'
|
|
3
4
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
|
|
4
5
|
# SPDX-License-Identifier: MIT
|
|
5
6
|
|
|
6
7
|
require 'tmpdir'
|
|
7
|
-
require 'English'
|
|
8
8
|
require_relative '../../lib/erc20'
|
|
9
9
|
|
|
10
10
|
Before do
|
|
@@ -31,19 +31,19 @@ When(%r{^I run bin/erc20 with "([^"]*)"$}) do |arg|
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
Then(/^Stdout contains "([^"]*)"$/) do |txt|
|
|
34
|
-
raise "STDOUT doesn't contain '#{txt}':\n#{@stdout}" unless @stdout.include?(txt)
|
|
34
|
+
raise(StandardError, "STDOUT doesn't contain '#{txt}':\n#{@stdout}") unless @stdout.include?(txt)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
Then(/^Stdout is empty$/) do
|
|
38
|
-
raise "STDOUT is not empty:\n#{@stdout}" unless @stdout == ''
|
|
38
|
+
raise(StandardError, "STDOUT is not empty:\n#{@stdout}") unless @stdout == ''
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
Then(/^Exit code is zero$/) do
|
|
42
|
-
raise "Non-zero exit #{@exitstatus}:\n#{@stdout}" unless @exitstatus.zero?
|
|
42
|
+
raise(StandardError, "Non-zero exit #{@exitstatus}:\n#{@stdout}") unless @exitstatus.zero?
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
Then(/^Exit code is not zero$/) do
|
|
46
|
-
raise 'Zero exit code' if @exitstatus.zero?
|
|
46
|
+
raise(StandardError, 'Zero exit code') if @exitstatus.zero?
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
When(/^I run bash with "([^"]*)"$/) do |text|
|
data/features/support/env.rb
CHANGED