erc20 0.1.0 → 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/.gitignore +5 -5
- data/.rultor.yml +1 -1
- data/Gemfile +11 -11
- data/Gemfile.lock +58 -165
- data/README.md +32 -17
- data/REUSE.toml +11 -1
- data/bin/erc20 +2 -2
- data/erc20.gemspec +2 -2
- data/lib/erc20/erc20.rb +3 -3
- data/lib/erc20/fake_wallet.rb +6 -3
- data/lib/erc20/wallet.rb +49 -23
- data/test/erc20/test_fake_wallet.rb +2 -3
- data/test/erc20/test_wallet.rb +65 -13
- data/test/test__helper.rb +20 -7
- metadata +5 -9
- 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/.gitignore
CHANGED
data/.rultor.yml
CHANGED
data/Gemfile
CHANGED
@@ -7,24 +7,24 @@ source 'https://rubygems.org'
|
|
7
7
|
gemspec
|
8
8
|
|
9
9
|
gem 'backtrace', '>0', require: false
|
10
|
-
gem '
|
10
|
+
gem 'concurrent-ruby', '~>1.2', require: false
|
11
|
+
gem 'cucumber', '~>9.2', require: false
|
11
12
|
gem 'donce', '>0', require: false
|
12
13
|
gem 'faraday', '>0', require: false
|
13
14
|
gem 'loog', '>0', require: false
|
14
|
-
gem 'minitest', '5.25
|
15
|
-
gem 'minitest-reporters', '1.7
|
16
|
-
gem 'minitest-retry', '0.2
|
15
|
+
gem 'minitest', '~>5.25', require: false
|
16
|
+
gem 'minitest-reporters', '~>1.7', require: false
|
17
|
+
gem 'minitest-retry', '~>0.2', require: false
|
17
18
|
gem 'qbash', '>0', require: false
|
18
|
-
gem 'rake', '13.2
|
19
|
+
gem 'rake', '~>13.2', require: false
|
19
20
|
gem 'random-port', '>0', require: false
|
20
|
-
gem '
|
21
|
-
gem 'rubocop', '1.72.2', require: false
|
21
|
+
gem 'rubocop', '~>1.75', require: false
|
22
22
|
gem 'rubocop-minitest', '>0', require: false
|
23
23
|
gem 'rubocop-performance', '>0', require: false
|
24
24
|
gem 'rubocop-rake', '>0', require: false
|
25
25
|
gem 'rubocop-rspec', '>0', require: false
|
26
|
-
gem 'simplecov', '0.22
|
27
|
-
gem 'simplecov-cobertura', '2.1
|
28
|
-
gem 'threads', '0.4
|
26
|
+
gem 'simplecov', '~>0.22', require: false
|
27
|
+
gem 'simplecov-cobertura', '~>2.1', require: false
|
28
|
+
gem 'threads', '~>0.4', require: false
|
29
29
|
gem 'typhoeus', '>0', require: false
|
30
|
-
gem 'yard', '0.9
|
30
|
+
gem 'yard', '~>0.9', require: false
|
data/Gemfile.lock
CHANGED
@@ -12,83 +12,49 @@ PATH
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
|
-
actionpack (8.0.1)
|
16
|
-
actionview (= 8.0.1)
|
17
|
-
activesupport (= 8.0.1)
|
18
|
-
nokogiri (>= 1.8.5)
|
19
|
-
rack (>= 2.2.4)
|
20
|
-
rack-session (>= 1.0.1)
|
21
|
-
rack-test (>= 0.6.3)
|
22
|
-
rails-dom-testing (~> 2.2)
|
23
|
-
rails-html-sanitizer (~> 1.6)
|
24
|
-
useragent (~> 0.16)
|
25
|
-
actionview (8.0.1)
|
26
|
-
activesupport (= 8.0.1)
|
27
|
-
builder (~> 3.1)
|
28
|
-
erubi (~> 1.11)
|
29
|
-
rails-dom-testing (~> 2.2)
|
30
|
-
rails-html-sanitizer (~> 1.6)
|
31
|
-
activesupport (8.0.1)
|
32
|
-
base64
|
33
|
-
benchmark (>= 0.3)
|
34
|
-
bigdecimal
|
35
|
-
concurrent-ruby (~> 1.0, >= 1.3.1)
|
36
|
-
connection_pool (>= 2.2.5)
|
37
|
-
drb
|
38
|
-
i18n (>= 1.6, < 2)
|
39
|
-
logger (>= 1.4.2)
|
40
|
-
minitest (>= 5.1)
|
41
|
-
securerandom (>= 0.3)
|
42
|
-
tzinfo (~> 2.0, >= 2.0.5)
|
43
|
-
uri (>= 0.13.1)
|
44
15
|
ansi (1.5.0)
|
45
|
-
ast (2.4.
|
16
|
+
ast (2.4.3)
|
46
17
|
backtrace (0.4.0)
|
47
18
|
base64 (0.2.0)
|
48
|
-
benchmark (0.4.0)
|
49
19
|
bigdecimal (3.1.9)
|
50
20
|
builder (3.3.0)
|
51
21
|
concurrent-ruby (1.3.5)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
cucumber-ci-environment (~> 9.2, >= 9.2.0)
|
57
|
-
cucumber-core (~> 12.0)
|
22
|
+
cucumber (9.2.1)
|
23
|
+
builder (~> 3.2)
|
24
|
+
cucumber-ci-environment (> 9, < 11)
|
25
|
+
cucumber-core (> 13, < 14)
|
58
26
|
cucumber-cucumber-expressions (~> 17.0)
|
59
|
-
cucumber-gherkin (> 24, <
|
27
|
+
cucumber-gherkin (> 24, < 28)
|
60
28
|
cucumber-html-formatter (> 20.3, < 22)
|
61
29
|
cucumber-messages (> 19, < 25)
|
62
30
|
diff-lcs (~> 1.5)
|
63
|
-
mini_mime (~> 1.1
|
64
|
-
multi_test (~> 1.1
|
65
|
-
sys-uname (~> 1.2
|
66
|
-
cucumber-ci-environment (
|
67
|
-
cucumber-core (
|
68
|
-
cucumber-gherkin (>=
|
31
|
+
mini_mime (~> 1.1)
|
32
|
+
multi_test (~> 1.1)
|
33
|
+
sys-uname (~> 1.2)
|
34
|
+
cucumber-ci-environment (10.0.1)
|
35
|
+
cucumber-core (13.0.3)
|
36
|
+
cucumber-gherkin (>= 27, < 28)
|
69
37
|
cucumber-messages (>= 20, < 23)
|
70
|
-
cucumber-tag-expressions (
|
38
|
+
cucumber-tag-expressions (> 5, < 7)
|
71
39
|
cucumber-cucumber-expressions (17.1.0)
|
72
40
|
bigdecimal
|
73
|
-
cucumber-gherkin (
|
74
|
-
cucumber-messages (>= 19.1.4, <
|
41
|
+
cucumber-gherkin (27.0.0)
|
42
|
+
cucumber-messages (>= 19.1.4, < 23)
|
75
43
|
cucumber-html-formatter (21.9.0)
|
76
44
|
cucumber-messages (> 19, < 28)
|
77
45
|
cucumber-messages (22.0.0)
|
78
|
-
cucumber-tag-expressions (
|
79
|
-
|
80
|
-
diff-lcs (1.6.0)
|
46
|
+
cucumber-tag-expressions (6.1.2)
|
47
|
+
diff-lcs (1.6.1)
|
81
48
|
docile (1.4.1)
|
82
|
-
donce (0.
|
83
|
-
backtrace (
|
84
|
-
os (
|
85
|
-
qbash (
|
86
|
-
drb (2.2.1)
|
49
|
+
donce (0.2.0)
|
50
|
+
backtrace (~> 0.3)
|
51
|
+
os (~> 1.1)
|
52
|
+
qbash (~> 0.3)
|
87
53
|
elapsed (0.0.1)
|
88
54
|
loog (> 0)
|
89
55
|
tago (> 0)
|
90
|
-
|
91
|
-
|
56
|
+
eth (0.5.14)
|
57
|
+
bigdecimal (~> 3.1)
|
92
58
|
forwardable (~> 1.3)
|
93
59
|
keccak (~> 1.3)
|
94
60
|
konstructor (~> 1.0)
|
@@ -98,7 +64,7 @@ GEM
|
|
98
64
|
ethon (0.16.0)
|
99
65
|
ffi (>= 1.15.0)
|
100
66
|
eventmachine (1.2.7)
|
101
|
-
faraday (2.
|
67
|
+
faraday (2.13.0)
|
102
68
|
faraday-net_http (>= 2.0, < 3.5)
|
103
69
|
json
|
104
70
|
logger
|
@@ -107,22 +73,15 @@ GEM
|
|
107
73
|
faye-websocket (0.11.3)
|
108
74
|
eventmachine (>= 0.12.0)
|
109
75
|
websocket-driver (>= 0.5.1)
|
110
|
-
ffi (1.17.
|
111
|
-
ffi (1.17.
|
112
|
-
ffi (1.17.
|
113
|
-
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)
|
114
80
|
ffi-compiler (1.3.2)
|
115
81
|
ffi (>= 1.15.5)
|
116
82
|
rake
|
117
83
|
forwardable (1.3.3)
|
118
|
-
|
119
|
-
concurrent-ruby (~> 1.0)
|
120
|
-
io-console (0.8.0)
|
121
|
-
irb (1.15.1)
|
122
|
-
pp (>= 0.6.0)
|
123
|
-
rdoc (>= 4.0.0)
|
124
|
-
reline (>= 0.4.2)
|
125
|
-
json (2.10.1)
|
84
|
+
json (2.10.2)
|
126
85
|
jsonrpc-client (0.1.4)
|
127
86
|
faraday
|
128
87
|
multi_json (>= 1.1.0)
|
@@ -130,14 +89,11 @@ GEM
|
|
130
89
|
konstructor (1.0.2)
|
131
90
|
language_server-protocol (3.17.0.4)
|
132
91
|
lint_roller (1.1.0)
|
133
|
-
logger (1.
|
134
|
-
loofah (2.24.0)
|
135
|
-
crass (~> 1.0.2)
|
136
|
-
nokogiri (>= 1.12.0)
|
92
|
+
logger (1.7.0)
|
137
93
|
loog (0.6.0)
|
138
94
|
mini_mime (1.1.5)
|
139
95
|
mini_portile2 (2.8.8)
|
140
|
-
minitest (5.25.
|
96
|
+
minitest (5.25.5)
|
141
97
|
minitest-reporters (1.7.1)
|
142
98
|
ansi
|
143
99
|
builder
|
@@ -149,56 +105,20 @@ GEM
|
|
149
105
|
multi_test (1.1.0)
|
150
106
|
net-http (0.6.0)
|
151
107
|
uri
|
152
|
-
nokogiri (1.18.3-arm64-darwin)
|
153
|
-
racc (~> 1.4)
|
154
|
-
nokogiri (1.18.3-x64-mingw-ucrt)
|
155
|
-
racc (~> 1.4)
|
156
|
-
nokogiri (1.18.3-x86_64-darwin)
|
157
|
-
racc (~> 1.4)
|
158
|
-
nokogiri (1.18.3-x86_64-linux-gnu)
|
159
|
-
racc (~> 1.4)
|
160
108
|
openssl (3.3.0)
|
161
109
|
os (1.1.4)
|
162
|
-
parallel (1.
|
163
|
-
parser (3.3.
|
110
|
+
parallel (1.27.0)
|
111
|
+
parser (3.3.8.0)
|
164
112
|
ast (~> 2.4.1)
|
165
113
|
racc
|
166
|
-
pkg-config (1.
|
167
|
-
|
168
|
-
prettyprint
|
169
|
-
prettyprint (0.2.0)
|
170
|
-
psych (5.2.3)
|
171
|
-
date
|
172
|
-
stringio
|
114
|
+
pkg-config (1.6.1)
|
115
|
+
prism (1.4.0)
|
173
116
|
qbash (0.4.0)
|
174
117
|
backtrace (> 0)
|
175
118
|
elapsed (> 0)
|
176
119
|
loog (> 0)
|
177
120
|
tago (> 0)
|
178
121
|
racc (1.8.1)
|
179
|
-
rack (3.1.10)
|
180
|
-
rack-session (2.1.0)
|
181
|
-
base64 (>= 0.1.0)
|
182
|
-
rack (>= 3.0.0)
|
183
|
-
rack-test (2.2.0)
|
184
|
-
rack (>= 1.3)
|
185
|
-
rackup (2.2.1)
|
186
|
-
rack (>= 3)
|
187
|
-
rails-dom-testing (2.2.0)
|
188
|
-
activesupport (>= 5.0.0)
|
189
|
-
minitest
|
190
|
-
nokogiri (>= 1.6)
|
191
|
-
rails-html-sanitizer (1.6.2)
|
192
|
-
loofah (~> 2.21)
|
193
|
-
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
194
|
-
railties (8.0.1)
|
195
|
-
actionpack (= 8.0.1)
|
196
|
-
activesupport (= 8.0.1)
|
197
|
-
irb (~> 1.13)
|
198
|
-
rackup (>= 1.0.0)
|
199
|
-
rake (>= 12.2)
|
200
|
-
thor (~> 1.0, >= 1.2.2)
|
201
|
-
zeitwerk (~> 2.6)
|
202
122
|
rainbow (3.1.1)
|
203
123
|
rake (13.2.1)
|
204
124
|
random-port (0.7.5)
|
@@ -207,30 +127,9 @@ GEM
|
|
207
127
|
mini_portile2 (~> 2.8)
|
208
128
|
pkg-config (~> 1.5)
|
209
129
|
rubyzip (~> 2.3)
|
210
|
-
rdoc (6.12.0)
|
211
|
-
psych (>= 4.0.0)
|
212
130
|
regexp_parser (2.10.0)
|
213
|
-
reline (0.6.0)
|
214
|
-
io-console (~> 0.5)
|
215
131
|
rexml (3.4.1)
|
216
|
-
|
217
|
-
rspec-support (~> 3.13.0)
|
218
|
-
rspec-expectations (3.13.3)
|
219
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
220
|
-
rspec-support (~> 3.13.0)
|
221
|
-
rspec-mocks (3.13.2)
|
222
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
223
|
-
rspec-support (~> 3.13.0)
|
224
|
-
rspec-rails (7.1.1)
|
225
|
-
actionpack (>= 7.0)
|
226
|
-
activesupport (>= 7.0)
|
227
|
-
railties (>= 7.0)
|
228
|
-
rspec-core (~> 3.13)
|
229
|
-
rspec-expectations (~> 3.13)
|
230
|
-
rspec-mocks (~> 3.13)
|
231
|
-
rspec-support (~> 3.13)
|
232
|
-
rspec-support (3.13.2)
|
233
|
-
rubocop (1.72.2)
|
132
|
+
rubocop (1.75.2)
|
234
133
|
json (~> 2.3)
|
235
134
|
language_server-protocol (~> 3.17.0.2)
|
236
135
|
lint_roller (~> 1.1.0)
|
@@ -238,23 +137,24 @@ GEM
|
|
238
137
|
parser (>= 3.3.0.2)
|
239
138
|
rainbow (>= 2.2.2, < 4.0)
|
240
139
|
regexp_parser (>= 2.9.3, < 3.0)
|
241
|
-
rubocop-ast (>= 1.
|
140
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
242
141
|
ruby-progressbar (~> 1.7)
|
243
142
|
unicode-display_width (>= 2.4.0, < 4.0)
|
244
|
-
rubocop-ast (1.
|
245
|
-
parser (>= 3.3.
|
246
|
-
|
143
|
+
rubocop-ast (1.44.1)
|
144
|
+
parser (>= 3.3.7.2)
|
145
|
+
prism (~> 1.4)
|
146
|
+
rubocop-minitest (0.38.0)
|
247
147
|
lint_roller (~> 1.1)
|
248
|
-
rubocop (>= 1.
|
148
|
+
rubocop (>= 1.75.0, < 2.0)
|
249
149
|
rubocop-ast (>= 1.38.0, < 2.0)
|
250
|
-
rubocop-performance (1.
|
150
|
+
rubocop-performance (1.25.0)
|
251
151
|
lint_roller (~> 1.1)
|
252
|
-
rubocop (>= 1.
|
152
|
+
rubocop (>= 1.75.0, < 2.0)
|
253
153
|
rubocop-ast (>= 1.38.0, < 2.0)
|
254
154
|
rubocop-rake (0.7.1)
|
255
155
|
lint_roller (~> 1.1)
|
256
156
|
rubocop (>= 1.72.1)
|
257
|
-
rubocop-rspec (3.
|
157
|
+
rubocop-rspec (3.6.0)
|
258
158
|
lint_roller (~> 1.1)
|
259
159
|
rubocop (~> 1.72, >= 1.72.1)
|
260
160
|
ruby-progressbar (1.13.0)
|
@@ -262,7 +162,6 @@ GEM
|
|
262
162
|
scrypt (3.0.8)
|
263
163
|
ffi-compiler (>= 1.0, < 2.0)
|
264
164
|
rake (>= 9, < 14)
|
265
|
-
securerandom (0.4.1)
|
266
165
|
simplecov (0.22.0)
|
267
166
|
docile (~> 1.1)
|
268
167
|
simplecov-html (~> 0.11)
|
@@ -273,29 +172,23 @@ GEM
|
|
273
172
|
simplecov-html (0.13.1)
|
274
173
|
simplecov_json_formatter (0.1.4)
|
275
174
|
slop (4.10.1)
|
276
|
-
stringio (3.1.5)
|
277
175
|
sys-uname (1.3.1)
|
278
176
|
ffi (~> 1.1)
|
279
|
-
tago (0.0
|
280
|
-
thor (1.3.2)
|
177
|
+
tago (0.1.0)
|
281
178
|
threads (0.4.1)
|
282
179
|
backtrace (~> 0)
|
283
180
|
concurrent-ruby (~> 1.0)
|
284
181
|
typhoeus (1.4.1)
|
285
182
|
ethon (>= 0.9.0)
|
286
|
-
tzinfo (2.0.6)
|
287
|
-
concurrent-ruby (~> 1.0)
|
288
183
|
unicode-display_width (3.1.4)
|
289
184
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
290
185
|
unicode-emoji (4.0.4)
|
291
|
-
uri (1.0.
|
292
|
-
useragent (0.16.11)
|
186
|
+
uri (1.0.3)
|
293
187
|
websocket-driver (0.7.7)
|
294
188
|
base64
|
295
189
|
websocket-extensions (>= 0.1.0)
|
296
190
|
websocket-extensions (0.1.5)
|
297
191
|
yard (0.9.37)
|
298
|
-
zeitwerk (2.7.2)
|
299
192
|
|
300
193
|
PLATFORMS
|
301
194
|
arm64-darwin-22
|
@@ -308,28 +201,28 @@ PLATFORMS
|
|
308
201
|
|
309
202
|
DEPENDENCIES
|
310
203
|
backtrace (> 0)
|
311
|
-
|
204
|
+
concurrent-ruby (~> 1.2)
|
205
|
+
cucumber (~> 9.2)
|
312
206
|
donce (> 0)
|
313
207
|
erc20!
|
314
208
|
faraday (> 0)
|
315
209
|
loog (> 0)
|
316
|
-
minitest (
|
317
|
-
minitest-reporters (
|
318
|
-
minitest-retry (
|
210
|
+
minitest (~> 5.25)
|
211
|
+
minitest-reporters (~> 1.7)
|
212
|
+
minitest-retry (~> 0.2)
|
319
213
|
qbash (> 0)
|
320
|
-
rake (
|
214
|
+
rake (~> 13.2)
|
321
215
|
random-port (> 0)
|
322
|
-
|
323
|
-
rubocop (= 1.72.2)
|
216
|
+
rubocop (~> 1.75)
|
324
217
|
rubocop-minitest (> 0)
|
325
218
|
rubocop-performance (> 0)
|
326
219
|
rubocop-rake (> 0)
|
327
220
|
rubocop-rspec (> 0)
|
328
|
-
simplecov (
|
329
|
-
simplecov-cobertura (
|
330
|
-
threads (
|
221
|
+
simplecov (~> 0.22)
|
222
|
+
simplecov-cobertura (~> 2.1)
|
223
|
+
threads (~> 0.4)
|
331
224
|
typhoeus (> 0)
|
332
|
-
yard (
|
225
|
+
yard (~> 0.9)
|
333
226
|
|
334
227
|
BUNDLED WITH
|
335
228
|
2.5.16
|
data/README.md
CHANGED
@@ -1,21 +1,36 @@
|
|
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
|
|
14
14
|
This small Ruby [gem](https://rubygems.org/gems/erc20)
|
15
|
-
makes manipulations with [
|
16
|
-
as simple as
|
17
|
-
[JSON-RPC] and [WebSockets]
|
18
|
-
[Infura], [GetBlock], or [Alchemy]
|
15
|
+
makes manipulations with [Ethereum] [ERC20] tokens
|
16
|
+
as simple as possible, when you have a provider of
|
17
|
+
[JSON-RPC] and [WebSockets] Ethereum APIs, such as
|
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:
|
@@ -44,10 +59,10 @@ w.accept(addresses) do |event|
|
|
44
59
|
end
|
45
60
|
```
|
46
61
|
|
47
|
-
|
62
|
+
You can also check ETH balance and send ETH transactions:
|
48
63
|
|
49
64
|
```ruby
|
50
|
-
# Check how many ETHs are
|
65
|
+
# Check how many ETHs are on the given address:
|
51
66
|
eth = w.eth_balance(address)
|
52
67
|
|
53
68
|
# Send a few ETHs to someone and get transaction hash:
|
@@ -91,7 +106,7 @@ w = ERC20::Wallet.new(
|
|
91
106
|
You can use [squid-proxy] [Docker] image to set up your own [HTTP proxy] server.
|
92
107
|
|
93
108
|
Of course, this library works with [Polygon], [Optimism],
|
94
|
-
and other
|
109
|
+
and other EVM compatible blockchains.
|
95
110
|
|
96
111
|
## How to use in command line
|
97
112
|
|
@@ -110,13 +125,13 @@ Then, run it:
|
|
110
125
|
erc20 --help
|
111
126
|
```
|
112
127
|
|
113
|
-
|
128
|
+
Usage should be straightforward. If you have questions, please submit an issue.
|
114
129
|
|
115
130
|
## How to use in tests
|
116
131
|
|
117
|
-
You can use `ERC20::FakeWallet` class that behaves exactly like
|
132
|
+
You can use the `ERC20::FakeWallet` class that behaves exactly like
|
118
133
|
`ERC20::Wallet`, but doesn't make any network connections to the provider.
|
119
|
-
|
134
|
+
Additionally, it records all requests sent to it:
|
120
135
|
|
121
136
|
```ruby
|
122
137
|
require 'erc20'
|
@@ -129,7 +144,7 @@ assert w.history.include?({ method: :pay, priv:, address:, amount: 42_000 })
|
|
129
144
|
|
130
145
|
Read
|
131
146
|
[these guidelines](https://www.yegor256.com/2014/04/15/github-guidelines.html).
|
132
|
-
Make sure
|
147
|
+
Make sure your build is green before you contribute
|
133
148
|
your pull request. You will need to have
|
134
149
|
[Ruby](https://www.ruby-lang.org/en/) 3.2+ and
|
135
150
|
[Bundler](https://bundler.io/) installed. Then:
|
@@ -142,7 +157,7 @@ bundle exec rake
|
|
142
157
|
If it's clean and you don't see any error messages, submit your pull request.
|
143
158
|
|
144
159
|
[gem]: https://github.com/rubygems/rubygems
|
145
|
-
[
|
160
|
+
[Ethereum]: https://en.wikipedia.org/wiki/Ethereum
|
146
161
|
[ERC20]: https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
|
147
162
|
[JSON-RPC]: https://ethereum.org/en/developers/docs/apis/json-rpc/
|
148
163
|
[Websockets]: https://ethereum.org/en/developers/tutorials/using-websockets/
|
data/REUSE.toml
CHANGED
@@ -4,6 +4,10 @@
|
|
4
4
|
version = 1
|
5
5
|
[[annotations]]
|
6
6
|
path = [
|
7
|
+
"**.json",
|
8
|
+
"**.md",
|
9
|
+
"**.png",
|
10
|
+
"**.txt",
|
7
11
|
"**/*.csv",
|
8
12
|
"**/*.jpg",
|
9
13
|
"**/*.json",
|
@@ -13,13 +17,19 @@ path = [
|
|
13
17
|
"**/*.svg",
|
14
18
|
"**/*.txt",
|
15
19
|
"**/*.vm",
|
20
|
+
"**/.DS_Store",
|
16
21
|
"**/.gitignore",
|
22
|
+
"**/.gitleaksignore",
|
23
|
+
"**/.pdd",
|
17
24
|
"**/CNAME",
|
25
|
+
"**/Cargo.toml",
|
26
|
+
"**/Gemfile.lock",
|
27
|
+
".DS_Store",
|
18
28
|
".gitattributes",
|
19
29
|
".gitignore",
|
20
30
|
".gitleaksignore",
|
21
31
|
".pdd",
|
22
|
-
".
|
32
|
+
"Cargo.toml",
|
23
33
|
"Gemfile.lock",
|
24
34
|
"README.md",
|
25
35
|
"hardhat/.gitignore",
|
data/bin/erc20
CHANGED
@@ -18,7 +18,7 @@ begin
|
|
18
18
|
opts = Slop.parse(ARGV, strict: true, help: true) do |o|
|
19
19
|
o.banner = "Usage (#{ERC20::VERSION}): erc20 [options] command [args]
|
20
20
|
Commands are:
|
21
|
-
key: Generate a new
|
21
|
+
key: Generate a new Ethereum private key (64 symbols)
|
22
22
|
address: Turn private key into a public address (44 symbols)
|
23
23
|
price: Get current price of one gas unit, in gwei
|
24
24
|
pay: Send ERC20 payment
|
@@ -33,7 +33,7 @@ Options are:"
|
|
33
33
|
)
|
34
34
|
o.integer(
|
35
35
|
'--chain',
|
36
|
-
'
|
36
|
+
'Ethereum chain ID',
|
37
37
|
default: 1
|
38
38
|
)
|
39
39
|
o.string(
|
data/erc20.gemspec
CHANGED
@@ -12,12 +12,12 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.name = 'erc20'
|
13
13
|
s.version = ERC20::VERSION
|
14
14
|
s.license = 'MIT'
|
15
|
-
s.summary = 'Sending and receiving ERC20 tokens in
|
15
|
+
s.summary = 'Sending and receiving ERC20 tokens in Ethereum network'
|
16
16
|
s.description =
|
17
17
|
'A simple library for making ERC20 manipulations as easy as they ' \
|
18
18
|
'can be for cryptocurrency newbies: checking balance, sending payments, ' \
|
19
19
|
'and monitoring addresses for incoming payments. The library expects ' \
|
20
|
-
'
|
20
|
+
'Ethereum node to provide JSON RPC and Websockets API.'
|
21
21
|
s.authors = ['Yegor Bugayenko']
|
22
22
|
s.email = 'yegor256@gmail.com'
|
23
23
|
s.homepage = 'http://github.com/yegor256/erc20.rb'
|
data/lib/erc20/erc20.rb
CHANGED
@@ -3,9 +3,9 @@
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
|
4
4
|
# SPDX-License-Identifier: MIT
|
5
5
|
|
6
|
-
# This module makes manipulations with
|
6
|
+
# This module makes manipulations with Ethereum ERC20 tokens
|
7
7
|
# as simple as they can be, if you have a provider of
|
8
|
-
# JSON-RPC and WebSockets
|
8
|
+
# JSON-RPC and WebSockets Ethereum APIs, for example
|
9
9
|
# Infura, GetBlock, or Alchemy.
|
10
10
|
#
|
11
11
|
# Start like this:
|
@@ -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.
|
28
|
+
VERSION = '0.1.2'
|
29
29
|
end
|
data/lib/erc20/fake_wallet.rb
CHANGED
@@ -91,9 +91,11 @@ class ERC20::FakeWallet
|
|
91
91
|
|
92
92
|
# Send a single ERC20 payment from a private address to a public one.
|
93
93
|
#
|
94
|
-
# @param [String]
|
95
|
-
# @param [String]
|
96
|
-
# @param [Integer]
|
94
|
+
# @param [String] priv Private key, in hex
|
95
|
+
# @param [String] address Public key, in hex
|
96
|
+
# @param [Integer] amount The amount of ERC20 tokens to send
|
97
|
+
# @param [Integer] gas_limit Optional gas limit
|
98
|
+
# @param [Integer] gas_price Optional gas price in gwei
|
97
99
|
# @return [String] Transaction hash
|
98
100
|
def pay(priv, address, amount, gas_limit: nil, gas_price: nil)
|
99
101
|
hex = TXN_HASH
|
@@ -106,6 +108,7 @@ class ERC20::FakeWallet
|
|
106
108
|
# @param [String] priv Private key, in hex
|
107
109
|
# @param [String] address Public key, in hex
|
108
110
|
# @param [Integer] amount The amount of ETHs to send
|
111
|
+
# @param [Integer] gas_price Optional gas price in gwei
|
109
112
|
# @return [String] Transaction hash
|
110
113
|
def eth_pay(priv, address, amount, gas_price: nil)
|
111
114
|
hex = TXN_HASH
|
data/lib/erc20/wallet.rb
CHANGED
@@ -12,7 +12,7 @@ require 'loog'
|
|
12
12
|
require 'uri'
|
13
13
|
require_relative 'erc20'
|
14
14
|
|
15
|
-
# A wallet with ERC20 tokens on
|
15
|
+
# A wallet with ERC20 tokens on Ethereum.
|
16
16
|
#
|
17
17
|
# Objects of this class are thread-safe.
|
18
18
|
#
|
@@ -64,7 +64,7 @@ class ERC20::Wallet
|
|
64
64
|
attr_reader :host, :port, :ssl, :chain, :contract, :ws_path, :http_path
|
65
65
|
|
66
66
|
# Constructor.
|
67
|
-
# @param [String] contract Hex of the contract in
|
67
|
+
# @param [String] contract Hex of the contract in Ethereum
|
68
68
|
# @param [Integer] chain The ID of the chain (1 for mainnet)
|
69
69
|
# @param [String] host The host to connect to
|
70
70
|
# @param [Integer] port TCP port to use
|
@@ -107,9 +107,9 @@ class ERC20::Wallet
|
|
107
107
|
|
108
108
|
# Get ERC20 balance of a public address (it's not the same as ETH balance!).
|
109
109
|
#
|
110
|
-
# An address in
|
111
|
-
# balance in ETH crypto. Another balance is the one kept by ERC20 contract
|
112
|
-
# in its own
|
110
|
+
# An address in Ethereum may have many balances. One of them is the main
|
111
|
+
# balance in ETH crypto. Another balance is the one kept by the ERC20 contract
|
112
|
+
# in its own ledger in root storage. This balance is checked by this method.
|
113
113
|
#
|
114
114
|
# @param [String] address Public key, in hex, starting from '0x'
|
115
115
|
# @return [Integer] Balance, in tokens
|
@@ -121,13 +121,13 @@ 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
|
|
128
128
|
# Get ETH balance of a public address.
|
129
129
|
#
|
130
|
-
# An address in
|
130
|
+
# An address in Ethereum may have many balances. One of them is the main
|
131
131
|
# balance in ETH crypto. This balance is checked by this method.
|
132
132
|
#
|
133
133
|
# @param [String] hex Public key, in hex, starting from '0x'
|
@@ -138,16 +138,16 @@ 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
|
|
145
|
-
# How
|
145
|
+
# How many gas units are required to send an ERC20 transaction.
|
146
146
|
#
|
147
|
-
# @param [String] from The
|
148
|
-
# @param [String] to
|
147
|
+
# @param [String] from The sending address, in hex
|
148
|
+
# @param [String] to The receiving address, in hex
|
149
149
|
# @param [Integer] amount How many ERC20 tokens to send
|
150
|
-
# @return [Integer]
|
150
|
+
# @return [Integer] Number of gas units required
|
151
151
|
def gas_estimate(from, to, amount)
|
152
152
|
raise 'Address can\'t be nil' unless from
|
153
153
|
raise 'Address must be a String' unless from.is_a?(String)
|
@@ -164,15 +164,33 @@ class ERC20::Wallet
|
|
164
164
|
end
|
165
165
|
|
166
166
|
# What is the price of gas unit in gwei?
|
167
|
+
#
|
168
|
+
# In Ethereum, gas is a unit that measures the computational work required to
|
169
|
+
# execute operations on the network. Every transaction and smart contract
|
170
|
+
# interaction consumes gas. Gas price is the amount of ETH you're willing to pay
|
171
|
+
# for each unit of gas, denominated in gwei (1 gwei = 0.000000001 ETH). Higher
|
172
|
+
# gas prices incentivize miners to include your transaction sooner, while lower
|
173
|
+
# prices may result in longer confirmation times.
|
174
|
+
#
|
167
175
|
# @return [Integer] Price of gas unit, in gwei (0.000000001 ETH)
|
168
176
|
def gas_price
|
169
|
-
|
177
|
+
block = jsonrpc.eth_getBlockByNumber('latest', false)
|
178
|
+
raise "Can't get gas price, try again later" if block.nil?
|
179
|
+
gwei = block['baseFeePerGas'].to_i(16)
|
170
180
|
@log.debug("The cost of one gas unit is #{gwei} gwei")
|
171
181
|
gwei
|
172
182
|
end
|
173
183
|
|
174
184
|
# Send a single ERC20 payment from a private address to a public one.
|
175
185
|
#
|
186
|
+
# ERC20 payments differ fundamentally from native ETH transfers. While ETH transfers
|
187
|
+
# simply move the cryptocurrency directly between addresses, ERC20 token transfers
|
188
|
+
# are actually interactions with a smart contract. When you transfer ERC20 tokens,
|
189
|
+
# you're not sending anything directly to another user - instead, you're calling
|
190
|
+
# the token contract's transfer function, which updates its internal ledger to
|
191
|
+
# decrease your balance and increase the recipient's balance. This requires more
|
192
|
+
# gas than ETH transfers since it involves executing contract code.
|
193
|
+
#
|
176
194
|
# @param [String] priv Private key, in hex
|
177
195
|
# @param [String] address Public key, in hex
|
178
196
|
# @param [Integer] amount The amount of ERC20 tokens to send
|
@@ -225,8 +243,7 @@ class ERC20::Wallet
|
|
225
243
|
#
|
226
244
|
# @param [String] priv Private key, in hex
|
227
245
|
# @param [String] address Public key, in hex
|
228
|
-
# @param [Integer] amount The amount of
|
229
|
-
# @param [Integer] limit How much gas you're ready to spend
|
246
|
+
# @param [Integer] amount The amount of ETH to send
|
230
247
|
# @param [Integer] price How much gas you pay per computation unit
|
231
248
|
# @return [String] Transaction hash
|
232
249
|
def eth_pay(priv, address, amount, price: gas_price)
|
@@ -272,14 +289,19 @@ class ERC20::Wallet
|
|
272
289
|
# +Thread.kill+.
|
273
290
|
#
|
274
291
|
# The array with the list of addresses (+addresses+) may change its
|
275
|
-
# content on-fly. The +accept()+ method will +
|
292
|
+
# content on-the-fly. The +accept()+ method will +eth_subscribe+ to the addresses
|
276
293
|
# that are added and will +eth_unsubscribe+ from those that are removed.
|
277
294
|
# Once we actually start listening, the +active+ array will be updated
|
278
295
|
# with the list of addresses.
|
279
296
|
#
|
280
|
-
# The +addresses+ must have +to_a()+ implemented.
|
281
|
-
#
|
282
|
-
#
|
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.
|
283
305
|
#
|
284
306
|
# @param [Array<String>] addresses Addresses to monitor
|
285
307
|
# @param [Array] active List of addresses that we are actually listening to
|
@@ -291,8 +313,8 @@ class ERC20::Wallet
|
|
291
313
|
raise 'Addresses must respond to .to_a()' unless addresses.respond_to?(:to_a)
|
292
314
|
raise 'Active can\'t be nil' unless active
|
293
315
|
raise 'Active must respond to .append()' unless active.respond_to?(:append)
|
294
|
-
raise '
|
295
|
-
raise '
|
316
|
+
raise 'Delay must be an Integer' unless delay.is_a?(Integer)
|
317
|
+
raise 'Delay must be a positive Integer' unless delay.positive?
|
296
318
|
raise 'Subscription ID must be an Integer' unless subscription_id.is_a?(Integer)
|
297
319
|
raise 'Subscription ID must be a positive Integer' unless subscription_id.positive?
|
298
320
|
EventMachine.run do
|
@@ -332,11 +354,15 @@ class ERC20::Wallet
|
|
332
354
|
txn: event['transactionHash'].downcase
|
333
355
|
}
|
334
356
|
log.debug(
|
335
|
-
"Payment of #{event[:amount]} tokens arrived" \
|
357
|
+
"Payment of #{event[:amount]} tokens arrived " \
|
336
358
|
"from #{event[:from]} to #{event[:to]} in #{event[:txn]}"
|
337
359
|
)
|
338
360
|
end
|
339
|
-
|
361
|
+
begin
|
362
|
+
yield event
|
363
|
+
rescue StandardError => e
|
364
|
+
@log.error(Backtrace.new(e).to_s)
|
365
|
+
end
|
340
366
|
end
|
341
367
|
end
|
342
368
|
end
|
@@ -8,19 +8,18 @@ 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'
|
15
14
|
require 'typhoeus'
|
16
|
-
require_relative '../../lib/erc20/fake_wallet'
|
17
15
|
require_relative '../test__helper'
|
16
|
+
require_relative '../../lib/erc20/fake_wallet'
|
18
17
|
|
19
18
|
# Test.
|
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,20 +8,19 @@ 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'
|
15
14
|
require 'typhoeus'
|
16
|
-
require_relative '../../lib/erc20/wallet'
|
17
15
|
require_relative '../test__helper'
|
16
|
+
require_relative '../../lib/erc20/wallet'
|
18
17
|
|
19
18
|
# Test.
|
20
19
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
21
20
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
22
21
|
# License:: MIT
|
23
|
-
class TestWallet <
|
24
|
-
# At this address, in
|
22
|
+
class TestWallet < ERC20::Test
|
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'
|
27
26
|
|
@@ -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
|
@@ -76,7 +76,8 @@ class TestWallet < Minitest::Test
|
|
76
76
|
def test_checks_balance_on_polygon
|
77
77
|
w = ERC20::Wallet.new(
|
78
78
|
contract: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
|
79
|
-
host: 'polygon-mainnet.infura.io',
|
79
|
+
host: 'polygon-mainnet.infura.io',
|
80
|
+
http_path: "/v3/#{env('INFURA_KEY')}",
|
80
81
|
log: loog
|
81
82
|
)
|
82
83
|
b = w.balance(STABLE)
|
@@ -200,6 +201,57 @@ class TestWallet < Minitest::Test
|
|
200
201
|
end
|
201
202
|
end
|
202
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
|
+
|
203
255
|
def test_accepts_payments_on_changing_addresses_on_hardhat
|
204
256
|
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
205
257
|
jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
|
@@ -290,20 +342,19 @@ class TestWallet < Minitest::Test
|
|
290
342
|
|
291
343
|
def test_checks_balance_via_proxy_on_mainnet
|
292
344
|
via_proxy do |proxy|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
end
|
345
|
+
w = ERC20::Wallet.new(
|
346
|
+
host: 'mainnet.infura.io',
|
347
|
+
http_path: "/v3/#{env('INFURA_KEY')}",
|
348
|
+
proxy:, log: loog
|
349
|
+
)
|
350
|
+
assert_equal(8_000_000, w.balance(STABLE))
|
300
351
|
end
|
301
352
|
end
|
302
353
|
|
303
354
|
def test_pays_on_mainnet
|
304
355
|
skip('This is live, must be run manually')
|
305
356
|
w = mainnet
|
306
|
-
print 'Enter
|
357
|
+
print 'Enter Ethereum ERC20 private key (64 chars): '
|
307
358
|
priv = gets.chomp
|
308
359
|
to = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
|
309
360
|
txn = w.pay(priv, to, 1_990_000)
|
@@ -315,6 +366,7 @@ class TestWallet < Minitest::Test
|
|
315
366
|
def env(var)
|
316
367
|
key = ENV.fetch(var, nil)
|
317
368
|
skip("The #{var} environment variable is not set") if key.nil?
|
369
|
+
skip("The #{var} environment variable is empty") if key.empty?
|
318
370
|
key
|
319
371
|
end
|
320
372
|
|
data/test/test__helper.rb
CHANGED
@@ -6,21 +6,34 @@
|
|
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
|
|
20
33
|
# To make tests retry on failure:
|
21
34
|
if ENV['RAKE']
|
22
35
|
require 'minitest/retry'
|
23
|
-
Minitest::Retry.use!
|
36
|
+
Minitest::Retry.use!
|
24
37
|
end
|
25
38
|
|
26
39
|
# Primitive array.
|
@@ -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,14 +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
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-04-20 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: eth
|
@@ -96,7 +95,7 @@ dependencies:
|
|
96
95
|
version: '4'
|
97
96
|
description: 'A simple library for making ERC20 manipulations as easy as they can
|
98
97
|
be for cryptocurrency newbies: checking balance, sending payments, and monitoring
|
99
|
-
addresses for incoming payments. The library expects
|
98
|
+
addresses for incoming payments. The library expects Ethereum node to provide JSON
|
100
99
|
RPC and Websockets API.'
|
101
100
|
email: yegor256@gmail.com
|
102
101
|
executables:
|
@@ -123,7 +122,6 @@ files:
|
|
123
122
|
- ".pdd"
|
124
123
|
- ".rubocop.yml"
|
125
124
|
- ".rultor.yml"
|
126
|
-
- ".simplecov"
|
127
125
|
- ".yamllint.yml"
|
128
126
|
- Gemfile
|
129
127
|
- Gemfile.lock
|
@@ -157,7 +155,6 @@ licenses:
|
|
157
155
|
- MIT
|
158
156
|
metadata:
|
159
157
|
rubygems_mfa_required: 'true'
|
160
|
-
post_install_message:
|
161
158
|
rdoc_options:
|
162
159
|
- "--charset=UTF-8"
|
163
160
|
require_paths:
|
@@ -173,8 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
170
|
- !ruby/object:Gem::Version
|
174
171
|
version: '0'
|
175
172
|
requirements: []
|
176
|
-
rubygems_version: 3.
|
177
|
-
signing_key:
|
173
|
+
rubygems_version: 3.6.2
|
178
174
|
specification_version: 4
|
179
|
-
summary: Sending and receiving ERC20 tokens in
|
175
|
+
summary: Sending and receiving ERC20 tokens in Ethereum network
|
180
176
|
test_files: []
|
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
|