erc20 0.2.8 → 0.3.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 +1 -0
- data/Gemfile.lock +60 -48
- data/README.md +8 -5
- data/Rakefile +2 -2
- data/bin/erc20 +144 -139
- data/erc20.gemspec +8 -8
- data/features/cli.feature +3 -15
- data/features/live.feature +17 -0
- data/features/step_definitions/steps.rb +5 -10
- data/features/support/env.rb +1 -1
- data/lib/erc20/erc20.rb +1 -2
- data/lib/erc20/fake_wallet.rb +4 -9
- data/lib/erc20/wallet.rb +153 -133
- data/lib/erc20.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9719ca99febb05bab13326b5a1de126b73aa5d923ad7f091ef3c4493a6d4030f
|
|
4
|
+
data.tar.gz: b75de1bc31c3e1e1ac2b3167d92ac9d924841048035e190ec961dfebde81d9c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6c306141b126499fe21d589809a596567439b7ec70ada449a0b7b4feee656fddb08d94f21943b10c6ea8c73526327a337cc056adc861a720fdee0bdce5e3a2f4
|
|
7
|
+
data.tar.gz: adc65e660ef7394c02f0d37979a7568c91b032a07620d25a40d3874e866468b3531e617e353d8bcd48b9e453d10891aa4977f67e21cbd41912123ce470ea3d79
|
data/Gemfile
CHANGED
|
@@ -20,6 +20,7 @@ gem 'rake', '~>13.2', require: false
|
|
|
20
20
|
gem 'random-port', '~>0.7', require: false
|
|
21
21
|
gem 'rdoc', '~>7.0', require: false
|
|
22
22
|
gem 'rubocop', '~>1.75', require: false
|
|
23
|
+
gem 'rubocop-elegant', '~>0.5', require: false
|
|
23
24
|
gem 'rubocop-minitest', '~>0.38', require: false
|
|
24
25
|
gem 'rubocop-performance', '~>1.25', require: false
|
|
25
26
|
gem 'rubocop-rake', '~>0.7', require: false
|
data/Gemfile.lock
CHANGED
|
@@ -8,18 +8,18 @@ PATH
|
|
|
8
8
|
json (~> 2.10)
|
|
9
9
|
jsonrpc-client (~> 0.1)
|
|
10
10
|
loog (~> 0.4)
|
|
11
|
-
|
|
11
|
+
thor (~> 1.3)
|
|
12
12
|
|
|
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
24
|
concurrent-ruby (1.3.6)
|
|
25
25
|
crack (1.0.1)
|
|
@@ -38,30 +38,31 @@ GEM
|
|
|
38
38
|
multi_test (~> 1.1)
|
|
39
39
|
sys-uname (~> 1.3)
|
|
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
49
|
cucumber-html-formatter (22.3.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
53
|
date (3.5.1)
|
|
54
54
|
diff-lcs (1.6.2)
|
|
55
55
|
docile (1.4.1)
|
|
56
|
-
donce (0.
|
|
56
|
+
donce (0.7.0)
|
|
57
57
|
backtrace (~> 0.3)
|
|
58
58
|
os (~> 1.1)
|
|
59
59
|
qbash (~> 0.3)
|
|
60
|
-
|
|
60
|
+
drb (2.2.3)
|
|
61
|
+
elapsed (0.3.1)
|
|
61
62
|
loog (~> 0.6)
|
|
62
63
|
tago (~> 0.1)
|
|
63
64
|
ellipsized (0.3.0)
|
|
64
|
-
erb (6.0.
|
|
65
|
+
erb (6.0.4)
|
|
65
66
|
eth (0.5.13)
|
|
66
67
|
forwardable (~> 1.3)
|
|
67
68
|
keccak (~> 1.3)
|
|
@@ -69,102 +70,107 @@ GEM
|
|
|
69
70
|
openssl (>= 2.2, < 4.0)
|
|
70
71
|
rbsecp256k1 (~> 6.0)
|
|
71
72
|
scrypt (~> 3.0)
|
|
72
|
-
ethon (0.
|
|
73
|
+
ethon (0.18.0)
|
|
73
74
|
ffi (>= 1.15.0)
|
|
75
|
+
logger
|
|
74
76
|
eventmachine (1.2.7)
|
|
75
|
-
faraday (2.14.
|
|
77
|
+
faraday (2.14.2)
|
|
76
78
|
faraday-net_http (>= 2.0, < 3.5)
|
|
77
79
|
json
|
|
78
80
|
logger
|
|
79
|
-
faraday-net_http (3.4.
|
|
81
|
+
faraday-net_http (3.4.3)
|
|
80
82
|
net-http (~> 0.5)
|
|
81
83
|
faye-websocket (0.12.0)
|
|
82
84
|
eventmachine (>= 0.12.0)
|
|
83
85
|
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.
|
|
86
|
+
ffi (1.17.4-arm64-darwin)
|
|
87
|
+
ffi (1.17.4-x64-mingw-ucrt)
|
|
88
|
+
ffi (1.17.4-x86_64-darwin)
|
|
89
|
+
ffi (1.17.4-x86_64-linux-gnu)
|
|
90
|
+
ffi-compiler (1.4.2)
|
|
89
91
|
ffi (>= 1.15.5)
|
|
90
92
|
rake
|
|
91
93
|
forwardable (1.4.0)
|
|
92
94
|
hashdiff (1.2.1)
|
|
93
|
-
json (2.
|
|
95
|
+
json (2.19.5)
|
|
94
96
|
jsonrpc-client (0.1.4)
|
|
95
97
|
faraday
|
|
96
98
|
multi_json (>= 1.1.0)
|
|
97
|
-
keccak (1.3.
|
|
99
|
+
keccak (1.3.3)
|
|
98
100
|
konstructor (1.0.2)
|
|
99
101
|
language_server-protocol (3.17.0.5)
|
|
100
102
|
lint_roller (1.1.0)
|
|
101
103
|
logger (1.7.0)
|
|
102
|
-
loog (0.
|
|
104
|
+
loog (0.8.0)
|
|
103
105
|
ellipsized
|
|
104
106
|
logger (~> 1.0)
|
|
105
107
|
memoist3 (1.0.0)
|
|
106
108
|
mini_mime (1.1.5)
|
|
107
109
|
mini_portile2 (2.8.9)
|
|
108
|
-
minitest (6.0.
|
|
110
|
+
minitest (6.0.6)
|
|
111
|
+
drb (~> 2.0)
|
|
109
112
|
prism (~> 1.5)
|
|
110
|
-
minitest-reporters (1.
|
|
113
|
+
minitest-reporters (1.8.0)
|
|
111
114
|
ansi
|
|
112
115
|
builder
|
|
113
|
-
minitest (>= 5.0)
|
|
116
|
+
minitest (>= 5.0, < 7)
|
|
114
117
|
ruby-progressbar
|
|
115
118
|
minitest-retry (0.3.1)
|
|
116
119
|
minitest (>= 5.0)
|
|
117
|
-
multi_json (1.
|
|
120
|
+
multi_json (1.21.1)
|
|
118
121
|
multi_test (1.1.0)
|
|
119
122
|
net-http (0.9.1)
|
|
120
123
|
uri (>= 0.11.1)
|
|
121
|
-
openssl (3.3.
|
|
124
|
+
openssl (3.3.3)
|
|
122
125
|
os (1.1.4)
|
|
123
|
-
parallel (1.
|
|
124
|
-
parser (3.3.
|
|
126
|
+
parallel (2.1.0)
|
|
127
|
+
parser (3.3.11.1)
|
|
125
128
|
ast (~> 2.4.1)
|
|
126
129
|
racc
|
|
127
130
|
pkg-config (1.6.5)
|
|
128
|
-
prism (1.
|
|
131
|
+
prism (1.9.0)
|
|
129
132
|
psych (5.3.1)
|
|
130
133
|
date
|
|
131
134
|
stringio
|
|
132
|
-
public_suffix (7.0.
|
|
133
|
-
qbash (0.
|
|
135
|
+
public_suffix (7.0.5)
|
|
136
|
+
qbash (0.8.4)
|
|
134
137
|
backtrace (> 0)
|
|
135
138
|
elapsed (> 0)
|
|
136
139
|
loog (> 0)
|
|
137
140
|
tago (> 0)
|
|
138
141
|
racc (1.8.1)
|
|
139
142
|
rainbow (3.1.1)
|
|
140
|
-
rake (13.
|
|
141
|
-
random-port (0.
|
|
143
|
+
rake (13.4.2)
|
|
144
|
+
random-port (0.8.2)
|
|
142
145
|
tago (~> 0.0)
|
|
143
146
|
rbsecp256k1 (6.0.0)
|
|
144
147
|
mini_portile2 (~> 2.8)
|
|
145
148
|
pkg-config (~> 1.5)
|
|
146
149
|
rubyzip (~> 2.3)
|
|
147
|
-
rdoc (7.
|
|
150
|
+
rdoc (7.2.0)
|
|
148
151
|
erb
|
|
149
152
|
psych (>= 4.0.0)
|
|
150
153
|
tsort
|
|
151
|
-
regexp_parser (2.
|
|
154
|
+
regexp_parser (2.12.0)
|
|
152
155
|
rexml (3.4.4)
|
|
153
|
-
rubocop (1.
|
|
156
|
+
rubocop (1.86.2)
|
|
154
157
|
json (~> 2.3)
|
|
155
158
|
language_server-protocol (~> 3.17.0.2)
|
|
156
159
|
lint_roller (~> 1.1.0)
|
|
157
|
-
parallel (
|
|
160
|
+
parallel (>= 1.10)
|
|
158
161
|
parser (>= 3.3.0.2)
|
|
159
162
|
rainbow (>= 2.2.2, < 4.0)
|
|
160
163
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
161
|
-
rubocop-ast (>= 1.
|
|
164
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
162
165
|
ruby-progressbar (~> 1.7)
|
|
163
166
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
164
|
-
rubocop-ast (1.49.
|
|
167
|
+
rubocop-ast (1.49.1)
|
|
165
168
|
parser (>= 3.3.7.2)
|
|
166
169
|
prism (~> 1.7)
|
|
167
|
-
rubocop-
|
|
170
|
+
rubocop-elegant (0.5.1)
|
|
171
|
+
lint_roller (~> 1.1)
|
|
172
|
+
rubocop (~> 1.75)
|
|
173
|
+
rubocop-minitest (0.39.1)
|
|
168
174
|
lint_roller (~> 1.1)
|
|
169
175
|
rubocop (>= 1.75.0, < 2.0)
|
|
170
176
|
rubocop-ast (>= 1.38.0, < 2.0)
|
|
@@ -189,23 +195,27 @@ GEM
|
|
|
189
195
|
simplecov (~> 0.19)
|
|
190
196
|
simplecov-html (0.13.2)
|
|
191
197
|
simplecov_json_formatter (0.1.4)
|
|
192
|
-
slop (4.10.1)
|
|
193
198
|
stringio (3.2.0)
|
|
194
|
-
sys-uname (1.
|
|
199
|
+
sys-uname (1.5.1)
|
|
200
|
+
ffi (~> 1.1)
|
|
201
|
+
memoist3 (~> 1.0.0)
|
|
202
|
+
sys-uname (1.5.1-universal-mingw32)
|
|
195
203
|
ffi (~> 1.1)
|
|
196
204
|
memoist3 (~> 1.0.0)
|
|
197
|
-
|
|
205
|
+
win32ole
|
|
206
|
+
tago (0.7.0)
|
|
207
|
+
thor (1.5.0)
|
|
198
208
|
threads (0.5.0)
|
|
199
209
|
backtrace (~> 0)
|
|
200
210
|
concurrent-ruby (~> 1.0)
|
|
201
211
|
tsort (0.2.0)
|
|
202
|
-
typhoeus (1.
|
|
203
|
-
ethon (>= 0.
|
|
212
|
+
typhoeus (1.6.0)
|
|
213
|
+
ethon (>= 0.18.0)
|
|
204
214
|
unicode-display_width (3.2.0)
|
|
205
215
|
unicode-emoji (~> 4.1)
|
|
206
216
|
unicode-emoji (4.2.0)
|
|
207
217
|
uri (1.1.1)
|
|
208
|
-
webmock (3.26.
|
|
218
|
+
webmock (3.26.2)
|
|
209
219
|
addressable (>= 2.8.0)
|
|
210
220
|
crack (>= 0.3.2)
|
|
211
221
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -213,7 +223,8 @@ GEM
|
|
|
213
223
|
base64
|
|
214
224
|
websocket-extensions (>= 0.1.0)
|
|
215
225
|
websocket-extensions (0.1.5)
|
|
216
|
-
|
|
226
|
+
win32ole (1.9.3)
|
|
227
|
+
yard (0.9.44)
|
|
217
228
|
|
|
218
229
|
PLATFORMS
|
|
219
230
|
arm64-darwin-22
|
|
@@ -240,6 +251,7 @@ DEPENDENCIES
|
|
|
240
251
|
random-port (~> 0.7)
|
|
241
252
|
rdoc (~> 7.0)
|
|
242
253
|
rubocop (~> 1.75)
|
|
254
|
+
rubocop-elegant (~> 0.5)
|
|
243
255
|
rubocop-minitest (~> 0.38)
|
|
244
256
|
rubocop-performance (~> 1.25)
|
|
245
257
|
rubocop-rake (~> 0.7)
|
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, # retry failed HTTP RPC calls 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
|
|
|
@@ -75,8 +77,8 @@ To check the price of a gas unit and the expected cost of a payment:
|
|
|
75
77
|
# How many gas units required to send this payment:
|
|
76
78
|
units = w.gas_estimate(from, to, amount)
|
|
77
79
|
|
|
78
|
-
# What is the price of a gas unit, in
|
|
79
|
-
|
|
80
|
+
# What is the price of a gas unit, in wei:
|
|
81
|
+
price = w.gas_price
|
|
80
82
|
```
|
|
81
83
|
|
|
82
84
|
To generate a new private key, use [eth](https://rubygems.org/gems/eth):
|
|
@@ -106,7 +108,7 @@ w = ERC20::Wallet.new(
|
|
|
106
108
|
You can use [squid-proxy] [Docker] image to set up your own [HTTP proxy] server.
|
|
107
109
|
|
|
108
110
|
Of course, this library works with [Polygon], [Optimism],
|
|
109
|
-
and other EVM compatible blockchains.
|
|
111
|
+
and other [EVM] compatible blockchains.
|
|
110
112
|
|
|
111
113
|
## How to use in command line
|
|
112
114
|
|
|
@@ -122,7 +124,7 @@ gem install erc20
|
|
|
122
124
|
Then, run it:
|
|
123
125
|
|
|
124
126
|
```bash
|
|
125
|
-
erc20
|
|
127
|
+
erc20 help
|
|
126
128
|
```
|
|
127
129
|
|
|
128
130
|
Usage should be straightforward. If you have questions, please submit an issue.
|
|
@@ -170,3 +172,4 @@ If it's clean and you don't see any error messages, submit your pull request.
|
|
|
170
172
|
[Docker]: https://www.docker.com/
|
|
171
173
|
[Polygon]: https://polygon.technology/
|
|
172
174
|
[Optimism]: https://www.optimism.io/
|
|
175
|
+
[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
|
@@ -8,126 +8,145 @@ $stdout.sync = true
|
|
|
8
8
|
|
|
9
9
|
require 'backtrace'
|
|
10
10
|
require 'loog'
|
|
11
|
-
require '
|
|
11
|
+
require 'thor'
|
|
12
12
|
require_relative '../lib/erc20'
|
|
13
13
|
require_relative '../lib/erc20/erc20'
|
|
14
14
|
require_relative '../lib/erc20/wallet'
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
eth_balance: Get ETH balance
|
|
28
|
-
Options are:"
|
|
29
|
-
o.string(
|
|
30
|
-
'--contract',
|
|
31
|
-
'Public address of ERC20 contract',
|
|
32
|
-
default: ERC20::Wallet::USDT
|
|
33
|
-
)
|
|
34
|
-
o.integer(
|
|
35
|
-
'--chain',
|
|
36
|
-
'Ethereum chain ID',
|
|
37
|
-
default: 1
|
|
38
|
-
)
|
|
39
|
-
o.bool(
|
|
40
|
-
'--ssl',
|
|
41
|
-
'Use SSL for HTTP connections',
|
|
42
|
-
default: true
|
|
43
|
-
)
|
|
44
|
-
o.string(
|
|
45
|
-
'--host',
|
|
46
|
-
'Host name of the provider',
|
|
47
|
-
default: 'eth.llamarpc.com'
|
|
48
|
-
)
|
|
49
|
-
o.string(
|
|
50
|
-
'--port',
|
|
51
|
-
'TCP port of the provider',
|
|
52
|
-
default: 443
|
|
53
|
-
)
|
|
54
|
-
o.string(
|
|
55
|
-
'--http_path',
|
|
56
|
-
'URL path for the HTTP RPC entry point of the provider',
|
|
57
|
-
default: '/'
|
|
58
|
-
)
|
|
59
|
-
o.string(
|
|
60
|
-
'--ws_path',
|
|
61
|
-
'URL path for the Websockets entry point of the provider',
|
|
62
|
-
default: '/'
|
|
63
|
-
)
|
|
64
|
-
o.string(
|
|
65
|
-
'--proxy',
|
|
66
|
-
'HTTP/S proxy for all requests, e.g. "localhost:3128"'
|
|
67
|
-
)
|
|
68
|
-
o.integer(
|
|
69
|
-
'--attempts',
|
|
70
|
-
'How many times should we try before failing',
|
|
71
|
-
default: 1
|
|
72
|
-
)
|
|
73
|
-
o.bool(
|
|
74
|
-
'--dry',
|
|
75
|
-
'Don\'t send a real payment, run in a read-only mode'
|
|
76
|
-
)
|
|
77
|
-
o.bool('--help', 'Read this: https://github.com/yegor256/erc20') do
|
|
78
|
-
puts o
|
|
79
|
-
exit
|
|
80
|
-
end
|
|
81
|
-
o.bool('--verbose', 'Print all possible debug messages')
|
|
82
|
-
end
|
|
83
|
-
rescue Slop::Error => e
|
|
84
|
-
raise e.message
|
|
16
|
+
# Command line interface for ERC20 operations.
|
|
17
|
+
#
|
|
18
|
+
# This CLI provides commands for managing ERC20 tokens and ETH on Ethereum:
|
|
19
|
+
# generating keys, checking balances, and sending payments.
|
|
20
|
+
#
|
|
21
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
22
|
+
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
|
23
|
+
# License:: MIT
|
|
24
|
+
class Bin < Thor
|
|
25
|
+
def self.exit_on_failure?
|
|
26
|
+
true
|
|
85
27
|
end
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
28
|
+
|
|
29
|
+
class_option :contract, type: :string, default: ERC20::Wallet::USDT,
|
|
30
|
+
desc: 'Public address of ERC20 contract'
|
|
31
|
+
class_option :chain, type: :numeric, default: 1,
|
|
32
|
+
desc: 'Ethereum chain ID'
|
|
33
|
+
class_option :ssl, type: :boolean, default: true,
|
|
34
|
+
desc: 'Use SSL for HTTP connections'
|
|
35
|
+
class_option :host, type: :string, default: 'eth.llamarpc.com',
|
|
36
|
+
desc: 'Host name of the provider'
|
|
37
|
+
class_option :port, type: :numeric, default: 443,
|
|
38
|
+
desc: 'TCP port of the provider'
|
|
39
|
+
class_option :http_path, type: :string, default: '/',
|
|
40
|
+
desc: 'URL path for the HTTP RPC entry point of the provider'
|
|
41
|
+
class_option :ws_path, type: :string, default: '/',
|
|
42
|
+
desc: 'URL path for the Websockets entry point of the provider'
|
|
43
|
+
class_option :proxy, type: :string,
|
|
44
|
+
desc: 'HTTP/S proxy for all requests, e.g. "localhost:3128"'
|
|
45
|
+
class_option :attempts, type: :numeric, default: 1,
|
|
46
|
+
desc: 'How many times should we try 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'
|
|
50
|
+
class_option :dry, type: :boolean, default: false,
|
|
51
|
+
desc: "Don't send a real payment, run in a read-only mode"
|
|
52
|
+
class_option :verbose, type: :boolean, default: false,
|
|
53
|
+
desc: 'Print all possible debug messages'
|
|
54
|
+
|
|
55
|
+
desc 'version', 'Print version of the tool'
|
|
56
|
+
def version
|
|
57
|
+
log.info(ERC20::VERSION)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
desc 'key', 'Generate a new Ethereum private key (64 hex symbols)'
|
|
61
|
+
def key
|
|
62
|
+
log.info(Eth::Key.new.private_hex.downcase)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
desc 'address KEY', 'Turn private key into a public address (44 hex symbols)'
|
|
66
|
+
def address(pvt)
|
|
67
|
+
log.info(Eth::Key.new(priv: pvt).address.to_s.downcase)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
desc 'price', 'Get current price of one gas unit, in gwei'
|
|
71
|
+
def price
|
|
72
|
+
log.info(wallet.gas_price)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
desc 'balance ADDRESS', 'Get ERC20 balance'
|
|
76
|
+
def balance(address)
|
|
110
77
|
log.debug("Checking ERC20 balance of #{address}")
|
|
111
78
|
tokens = wallet.balance(address)
|
|
112
79
|
log.debug("The balance is #{tokens} ERC20 tokens (#{tokens.to_f / 1_000_000} USDT)")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
80
|
+
log.info(tokens)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
desc 'eth_balance ADDRESS', 'Get ETH balance'
|
|
84
|
+
def eth_balance(address)
|
|
117
85
|
log.debug("Checking ETH balance of #{address}")
|
|
118
86
|
wei = wallet.eth_balance(address)
|
|
119
87
|
log.debug("The balance of #{address} is #{wei} wei (#{format('%0.4f', wei.to_f / 1_000_000_000_000_000_000)} ETH)")
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
88
|
+
log.info(wei)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
desc 'pay KEY ADDRESS AMOUNT', 'Send ERC20 payment'
|
|
92
|
+
def pay(pkey, address, amount)
|
|
124
93
|
priv = Eth::Key.new(priv: pkey)
|
|
125
|
-
log.debug("Sending ERC20 tokens from #{priv.address
|
|
126
|
-
address = opts.arguments[2]
|
|
127
|
-
raise 'Address is required' if address.nil?
|
|
94
|
+
log.debug("Sending ERC20 tokens from #{priv.address}")
|
|
128
95
|
log.debug("Sending ERC20 tokens to #{address}")
|
|
129
|
-
amount =
|
|
130
|
-
|
|
96
|
+
amount = tokens(amount)
|
|
97
|
+
log.debug("Sending #{amount} ERC20 tokens")
|
|
98
|
+
if wallet.eth_balance(priv.address.to_s).zero?
|
|
99
|
+
log.debug("ETH balance of #{priv.address} is zero, the txn most definitely will be rejected by the server!")
|
|
100
|
+
end
|
|
101
|
+
log.info(wallet.pay(priv.private_hex, address, amount))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
desc 'eth_pay KEY ADDRESS AMOUNT', 'Send ETH payment'
|
|
105
|
+
def eth_pay(pkey, address, amount)
|
|
106
|
+
priv = Eth::Key.new(priv: pkey)
|
|
107
|
+
log.debug("Sending ETH from #{priv.address}")
|
|
108
|
+
log.debug("Sending ETH to #{address}")
|
|
109
|
+
raise "Amount #{amount.inspect} is not valid" unless /^[0-9]+(\.[0-9]+)?(eth|wei|gwei)?$/.match?(amount)
|
|
110
|
+
amount = wei(amount)
|
|
111
|
+
log.debug("Sending #{amount} wei...")
|
|
112
|
+
log.info(wallet.eth_pay(priv.private_hex, address, amount))
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.banner(task, namespace = false, subcommand = false)
|
|
116
|
+
"#{basename} #{task.usage}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def self.help(shell, subcommand = false)
|
|
120
|
+
shell.say "Usage (#{ERC20::VERSION}): erc20 [options] command [args]"
|
|
121
|
+
shell.say 'Commands are:'
|
|
122
|
+
super
|
|
123
|
+
shell.say "\nRead this: https://github.com/yegor256/erc20"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
def log
|
|
129
|
+
@log ||= options[:verbose] ? Loog::VERBOSE : Loog::REGULAR
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def wallet
|
|
133
|
+
@wallet ||=
|
|
134
|
+
if options[:dry]
|
|
135
|
+
ERC20::FakeWallet.new
|
|
136
|
+
else
|
|
137
|
+
ERC20::Wallet.new(
|
|
138
|
+
contract: options[:contract],
|
|
139
|
+
host: options[:host], port: options[:port].to_i,
|
|
140
|
+
http_path: options[:http_path], ws_path: options[:ws_path],
|
|
141
|
+
ssl: options[:ssl],
|
|
142
|
+
attempts: options[:attempts].to_i,
|
|
143
|
+
fallbacks: options[:fallbacks],
|
|
144
|
+
log: log
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def tokens(amount)
|
|
131
150
|
if /^[0-9]+$/.match?(amount)
|
|
132
151
|
amount = amount.to_i
|
|
133
152
|
log.debug("The token amount equals to #{amount} ERC20 tokens (as provided)")
|
|
@@ -140,43 +159,29 @@ Options are:"
|
|
|
140
159
|
else
|
|
141
160
|
raise "Can't understand amount: #{amount.inspect}"
|
|
142
161
|
end
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
puts wallet.pay(priv.private_hex, address, amount)
|
|
148
|
-
when 'eth_pay'
|
|
149
|
-
pkey = opts.arguments[1]
|
|
150
|
-
raise 'Private key is required' if pkey.nil?
|
|
151
|
-
priv = Eth::Key.new(priv: pkey)
|
|
152
|
-
log.debug("Sending ETH from #{priv.address.to_s}")
|
|
153
|
-
address = opts.arguments[2]
|
|
154
|
-
raise 'Address is required' if address.nil?
|
|
155
|
-
log.debug("Sending ETH to #{address}")
|
|
156
|
-
amount = opts.arguments[3]
|
|
157
|
-
raise 'Amount argument is required' if amount.nil?
|
|
158
|
-
raise "Amount #{amount.inspect} is not valid" unless /^[0-9]+(\.[0-9]+)?(eth|wei|gwei)?$/.match?(amount)
|
|
162
|
+
amount
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def wei(amount)
|
|
159
166
|
if /^[0-9]+$/.match?(amount)
|
|
160
|
-
amount
|
|
161
|
-
elsif /[0-9]wei
|
|
162
|
-
amount
|
|
163
|
-
elsif /[0-9]gwei
|
|
164
|
-
|
|
165
|
-
elsif /[0-9]eth
|
|
166
|
-
|
|
167
|
+
amount.to_i
|
|
168
|
+
elsif /[0-9]wei$/.match?(amount)
|
|
169
|
+
amount.gsub(/wei$/, '').to_i
|
|
170
|
+
elsif /[0-9]gwei$/.match?(amount)
|
|
171
|
+
(amount.gsub(/gwei$/, '').to_f * 1_000_000_000).to_i
|
|
172
|
+
elsif /[0-9]eth$/.match?(amount)
|
|
173
|
+
(amount.gsub(/eth$/, '').to_f * 1_000_000_000_000_000_000).to_i
|
|
167
174
|
else
|
|
168
175
|
raise "Can't understand amount: #{amount.inspect}"
|
|
169
176
|
end
|
|
170
|
-
log.debug("Sending #{amount} wei...")
|
|
171
|
-
puts wallet.eth_pay(priv.private_hex, address, amount)
|
|
172
|
-
else
|
|
173
|
-
raise "Command #{opts.arguments[0]} is not supported"
|
|
174
177
|
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
begin
|
|
181
|
+
Bin.start(ARGV)
|
|
175
182
|
rescue StandardError => e
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
puts "ERROR: #{e.message}"
|
|
180
|
-
end
|
|
183
|
+
log = ARGV.include?('--verbose') ? Loog::VERBOSE : Loog::REGULAR
|
|
184
|
+
log.debug(Backtrace.new(e))
|
|
185
|
+
log.error(e.message)
|
|
181
186
|
exit(255)
|
|
182
187
|
end
|