erc20 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/Gemfile +3 -3
- data/Gemfile.lock +19 -14
- data/lib/erc20/erc20.rb +1 -1
- data/lib/erc20/wallet.rb +53 -8
- data/test/erc20/test_fake_wallet.rb +4 -0
- data/test/erc20/test_wallet.rb +14 -4
- data/test/test__helper.rb +3 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 85e0628229d427776681b41f5937a4ac7259333c946866c6662ce6a6b9ac32d9
|
4
|
+
data.tar.gz: 217938bd8b36af8844f3f3fc7f4f4f44d06af596cef01e3de46f017349f633ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f9e60bab73c14a1ff6cacc2ef49b1ee7c615e0e36747b53540ef0988a7adbbb2482ce14eec32a393de56e853c4ae781b59a29df13fc4687fb4558e33f192d57
|
7
|
+
data.tar.gz: d472ce73882cf23a0a24f355961261cf03204c592eee0f8a439f2cb4e32ca76933e78cd563a4b2952c5050b1fe7eeef2ed2b9bceee7087ffda683a032abdeeaf
|
data/.rubocop.yml
CHANGED
@@ -27,10 +27,10 @@ AllCops:
|
|
27
27
|
TargetRubyVersion: 3.2
|
28
28
|
SuggestExtensions: false
|
29
29
|
NewCops: enable
|
30
|
-
|
31
|
-
- rubocop-minitest
|
30
|
+
plugins:
|
32
31
|
- rubocop-performance
|
33
32
|
- rubocop-rake
|
33
|
+
- rubocop-minitest
|
34
34
|
Minitest/EmptyLineBeforeAssertionMethods:
|
35
35
|
Enabled: false
|
36
36
|
Gemspec/RequiredRubyVersion:
|
data/Gemfile
CHANGED
@@ -34,9 +34,9 @@ gem 'qbash', '>0', require: false
|
|
34
34
|
gem 'rake', '13.2.1', require: false
|
35
35
|
gem 'random-port', '>0', require: false
|
36
36
|
gem 'rspec-rails', '7.1.1', require: false
|
37
|
-
gem 'rubocop', '1.
|
38
|
-
gem 'rubocop-minitest', '0.
|
39
|
-
gem 'rubocop-performance', '1.
|
37
|
+
gem 'rubocop', '1.72.1', require: false
|
38
|
+
gem 'rubocop-minitest', '0.37.1', require: false
|
39
|
+
gem 'rubocop-performance', '1.24.0', require: false
|
40
40
|
gem 'rubocop-rake', '>0', require: false
|
41
41
|
gem 'rubocop-rspec', '3.4.0', require: false
|
42
42
|
gem 'simplecov', '0.22.0', require: false
|
data/Gemfile.lock
CHANGED
@@ -103,6 +103,7 @@ GEM
|
|
103
103
|
keccak (1.3.2)
|
104
104
|
konstructor (1.0.2)
|
105
105
|
language_server-protocol (3.17.0.4)
|
106
|
+
lint_roller (1.1.0)
|
106
107
|
logger (1.6.6)
|
107
108
|
loofah (2.24.0)
|
108
109
|
crass (~> 1.0.2)
|
@@ -201,9 +202,10 @@ GEM
|
|
201
202
|
rspec-mocks (~> 3.13)
|
202
203
|
rspec-support (~> 3.13)
|
203
204
|
rspec-support (3.13.2)
|
204
|
-
rubocop (1.
|
205
|
+
rubocop (1.72.1)
|
205
206
|
json (~> 2.3)
|
206
|
-
language_server-protocol (
|
207
|
+
language_server-protocol (~> 3.17.0.2)
|
208
|
+
lint_roller (~> 1.1.0)
|
207
209
|
parallel (~> 1.10)
|
208
210
|
parser (>= 3.3.0.2)
|
209
211
|
rainbow (>= 2.2.2, < 4.0)
|
@@ -213,14 +215,17 @@ GEM
|
|
213
215
|
unicode-display_width (>= 2.4.0, < 4.0)
|
214
216
|
rubocop-ast (1.38.0)
|
215
217
|
parser (>= 3.3.1.0)
|
216
|
-
rubocop-minitest (0.
|
217
|
-
|
218
|
-
rubocop
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
rubocop (
|
218
|
+
rubocop-minitest (0.37.1)
|
219
|
+
lint_roller (~> 1.1)
|
220
|
+
rubocop (>= 1.72.1, < 2.0)
|
221
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
222
|
+
rubocop-performance (1.24.0)
|
223
|
+
lint_roller (~> 1.1)
|
224
|
+
rubocop (>= 1.72.1, < 2.0)
|
225
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
226
|
+
rubocop-rake (0.7.0)
|
227
|
+
lint_roller (~> 1.1)
|
228
|
+
rubocop (>= 1.72.1)
|
224
229
|
rubocop-rspec (3.4.0)
|
225
230
|
rubocop (~> 1.61)
|
226
231
|
ruby-progressbar (1.13.0)
|
@@ -238,7 +243,7 @@ GEM
|
|
238
243
|
simplecov (~> 0.19)
|
239
244
|
simplecov-html (0.13.1)
|
240
245
|
simplecov_json_formatter (0.1.4)
|
241
|
-
stringio (3.1.
|
246
|
+
stringio (3.1.3)
|
242
247
|
tago (0.0.2)
|
243
248
|
thor (1.3.2)
|
244
249
|
threads (0.4.1)
|
@@ -282,9 +287,9 @@ DEPENDENCIES
|
|
282
287
|
rake (= 13.2.1)
|
283
288
|
random-port (> 0)
|
284
289
|
rspec-rails (= 7.1.1)
|
285
|
-
rubocop (= 1.
|
286
|
-
rubocop-minitest (= 0.
|
287
|
-
rubocop-performance (= 1.
|
290
|
+
rubocop (= 1.72.1)
|
291
|
+
rubocop-minitest (= 0.37.1)
|
292
|
+
rubocop-performance (= 1.24.0)
|
288
293
|
rubocop-rake (> 0)
|
289
294
|
rubocop-rspec (= 3.4.0)
|
290
295
|
simplecov (= 0.22.0)
|
data/lib/erc20/erc20.rb
CHANGED
data/lib/erc20/wallet.rb
CHANGED
@@ -93,13 +93,30 @@ class ERC20::Wallet
|
|
93
93
|
def initialize(contract: USDT, chain: 1, log: $stdout,
|
94
94
|
host: nil, port: 443, http_path: '/', ws_path: '/',
|
95
95
|
ssl: true, proxy: nil)
|
96
|
+
raise 'Contract can\'t be nil' unless contract
|
97
|
+
raise 'Contract must be a String' unless contract.is_a?(String)
|
98
|
+
raise 'Invalid format of the contract' unless /^0x[0-9a-fA-F]{40}$/.match?(contract)
|
96
99
|
@contract = contract
|
100
|
+
raise 'Host can\'t be nil' unless host
|
101
|
+
raise 'Host must be a String' unless host.is_a?(String)
|
97
102
|
@host = host
|
103
|
+
raise 'Port can\'t be nil' unless port
|
104
|
+
raise 'Port must be an Integer' unless port.is_a?(Integer)
|
105
|
+
raise 'Port must be a positive Integer' unless port.positive?
|
98
106
|
@port = port
|
107
|
+
raise 'Ssl can\'t be nil' if ssl.nil?
|
99
108
|
@ssl = ssl
|
109
|
+
raise 'Http_path can\'t be nil' unless http_path
|
110
|
+
raise 'Http_path must be a String' unless http_path.is_a?(String)
|
100
111
|
@http_path = http_path
|
112
|
+
raise 'Ws_path can\'t be nil' unless ws_path
|
113
|
+
raise 'Ws_path must be a String' unless ws_path.is_a?(String)
|
101
114
|
@ws_path = ws_path
|
115
|
+
raise 'Log can\'t be nil' unless log
|
102
116
|
@log = log
|
117
|
+
raise 'Chain can\'t be nil' unless chain
|
118
|
+
raise 'Chain must be an Integer' unless chain.is_a?(Integer)
|
119
|
+
raise 'Chain must be a positive Integer' unless chain.positive?
|
103
120
|
@chain = chain
|
104
121
|
@proxy = proxy
|
105
122
|
@mutex = Mutex.new
|
@@ -110,6 +127,9 @@ class ERC20::Wallet
|
|
110
127
|
# @param [String] hex Public key, in hex, starting from '0x'
|
111
128
|
# @return [Integer] Balance, in tokens
|
112
129
|
def balance(hex)
|
130
|
+
raise 'Address can\'t be nil' unless hex
|
131
|
+
raise 'Address must be a String' unless hex.is_a?(String)
|
132
|
+
raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(hex)
|
113
133
|
func = '70a08231' # balanceOf
|
114
134
|
padded = "000000000000000000000000#{hex[2..].downcase}"
|
115
135
|
data = "0x#{func}#{padded}"
|
@@ -128,6 +148,23 @@ class ERC20::Wallet
|
|
128
148
|
# @param [Integer] gas_price How much gas you pay per computation unit
|
129
149
|
# @return [String] Transaction hash
|
130
150
|
def pay(priv, address, amount, gas_limit: nil, gas_price: nil)
|
151
|
+
raise 'Private key can\'t be nil' unless priv
|
152
|
+
raise 'Private key must be a String' unless priv.is_a?(String)
|
153
|
+
raise 'Invalid format of private key' unless /^[0-9a-fA-F]{64}$/.match?(priv)
|
154
|
+
raise 'Address can\'t be nil' unless address
|
155
|
+
raise 'Address must be a String' unless address.is_a?(String)
|
156
|
+
raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(address)
|
157
|
+
raise 'Amount can\'t be nil' unless amount
|
158
|
+
raise 'Amount must be an Integer' unless amount.is_a?(Integer)
|
159
|
+
raise 'Amount must be a positive Integer' unless amount.positive?
|
160
|
+
if gas_limit
|
161
|
+
raise 'Gas limit must be an Integer' unless gas_limit.is_a?(Integer)
|
162
|
+
raise 'Gas limit must be a positive Integer' unless gas_limit.positive?
|
163
|
+
end
|
164
|
+
if gas_price
|
165
|
+
raise 'Gas price must be an Integer' unless gas_price.is_a?(Integer)
|
166
|
+
raise 'Gas price must be a positive Integer' unless gas_price.positive?
|
167
|
+
end
|
131
168
|
func = 'a9059cbb' # transfer(address,uint256)
|
132
169
|
to_clean = address.downcase.sub(/^0x/, '')
|
133
170
|
to_padded = ('0' * (64 - to_clean.size)) + to_clean
|
@@ -169,14 +206,21 @@ class ERC20::Wallet
|
|
169
206
|
# Once we actually start listening, the +active+ array will be updated
|
170
207
|
# with the list of addresses.
|
171
208
|
#
|
172
|
-
#
|
173
|
-
#
|
209
|
+
# The +addresses+ must have +to_a()+ implemented.
|
210
|
+
# The +active+ must have +append()+ implemented.
|
211
|
+
# Only these methods will be called.
|
174
212
|
#
|
175
213
|
# @param [Array<String>] addresses Addresses to monitor
|
176
214
|
# @param [Array] active List of addresses that we are actually listening to
|
177
215
|
# @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
|
178
216
|
# @param [Integer] delay How many seconds to wait between +eth_subscribe+ calls
|
179
217
|
def accept(addresses, active = [], raw: false, delay: 1)
|
218
|
+
raise 'Addresses can\'t be nil' unless addresses
|
219
|
+
raise 'Addresses must respond to .to_a()' unless addresses.respond_to?(:to_a)
|
220
|
+
raise 'Active can\'t be nil' unless active
|
221
|
+
raise 'Active must respond to .append()' unless addresses.respond_to?(:append)
|
222
|
+
raise 'Amount must be an Integer' unless delay.is_a?(Integer)
|
223
|
+
raise 'Amount must be a positive Integer' unless delay.positive?
|
180
224
|
EventMachine.run do
|
181
225
|
u = url(http: false)
|
182
226
|
@log.debug("Connecting to #{u.hostname}:#{u.port}...")
|
@@ -192,12 +236,7 @@ class ERC20::Wallet
|
|
192
236
|
end
|
193
237
|
ws.on(:message) do |msg|
|
194
238
|
verbose do
|
195
|
-
data =
|
196
|
-
begin
|
197
|
-
JSON.parse(msg.data)
|
198
|
-
rescue StandardError
|
199
|
-
{}
|
200
|
-
end
|
239
|
+
data = to_json(msg)
|
201
240
|
if data['id']
|
202
241
|
before = active.to_a
|
203
242
|
attempt.each do |a|
|
@@ -265,6 +304,12 @@ class ERC20::Wallet
|
|
265
304
|
|
266
305
|
private
|
267
306
|
|
307
|
+
def to_json(msg)
|
308
|
+
JSON.parse(msg.data)
|
309
|
+
rescue StandardError
|
310
|
+
{}
|
311
|
+
end
|
312
|
+
|
268
313
|
def verbose
|
269
314
|
yield
|
270
315
|
rescue StandardError => e
|
@@ -33,6 +33,10 @@ require 'typhoeus'
|
|
33
33
|
require_relative '../../lib/erc20/fake_wallet'
|
34
34
|
require_relative '../test__helper'
|
35
35
|
|
36
|
+
k = Eth::Key.new
|
37
|
+
puts k.private_hex
|
38
|
+
puts k.address
|
39
|
+
|
36
40
|
# Test.
|
37
41
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
38
42
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
data/test/erc20/test_wallet.rb
CHANGED
@@ -38,9 +38,9 @@ require_relative '../test__helper'
|
|
38
38
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
39
39
|
# License:: MIT
|
40
40
|
class TestWallet < Minitest::Test
|
41
|
-
# At this address, in Etherium mainnet, there are
|
41
|
+
# At this address, in Etherium mainnet, there are $8 USDT. I won't
|
42
42
|
# move them anyway, that's why tests can use this address forever.
|
43
|
-
STABLE = '
|
43
|
+
STABLE = '0x7232148927F8a580053792f44D4d59d40Fd00ABD'
|
44
44
|
|
45
45
|
# One guy private hex.
|
46
46
|
JEFF = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
|
@@ -51,7 +51,7 @@ class TestWallet < Minitest::Test
|
|
51
51
|
def test_checks_balance_on_mainnet
|
52
52
|
b = mainnet.balance(STABLE)
|
53
53
|
refute_nil(b)
|
54
|
-
assert_equal(
|
54
|
+
assert_equal(8_000_000, b)
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_checks_balance_of_absent_address
|
@@ -253,11 +253,21 @@ class TestWallet < Minitest::Test
|
|
253
253
|
host: 'mainnet.infura.io', http_path: "/v3/#{env('INFURA_KEY')}",
|
254
254
|
proxy:, log: loog
|
255
255
|
)
|
256
|
-
assert_equal(
|
256
|
+
assert_equal(8_000_000, w.balance(STABLE))
|
257
257
|
end
|
258
258
|
end
|
259
259
|
end
|
260
260
|
|
261
|
+
def test_pays_on_mainnet
|
262
|
+
skip('This is live, must be run manually')
|
263
|
+
w = mainnet
|
264
|
+
print 'Enter Etherium ERC20 private key (64 chars): '
|
265
|
+
priv = gets.chomp
|
266
|
+
to = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
|
267
|
+
txn = w.pay(priv, to, 1_990_000)
|
268
|
+
assert_equal(66, txn.length)
|
269
|
+
end
|
270
|
+
|
261
271
|
private
|
262
272
|
|
263
273
|
def env(var)
|
data/test/test__helper.rb
CHANGED
@@ -64,12 +64,13 @@ class Minitest::Test
|
|
64
64
|
ENV['RAKE'] ? Loog::ERRORS : Loog::VERBOSE
|
65
65
|
end
|
66
66
|
|
67
|
-
def wait_for
|
67
|
+
def wait_for(seconds = 30)
|
68
68
|
start = Time.now
|
69
69
|
loop do
|
70
70
|
sleep(0.1)
|
71
71
|
break if yield
|
72
|
-
|
72
|
+
passed = Time.now - start
|
73
|
+
raise "Giving up after #{passed} seconds of waiting" if passed > seconds
|
73
74
|
rescue Errno::ECONNREFUSED
|
74
75
|
retry
|
75
76
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erc20
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eth
|