erc20 0.0.11 → 0.0.13
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/.rubocop.yml +2 -2
- data/Gemfile +3 -3
- data/Gemfile.lock +19 -14
- data/lib/erc20/erc20.rb +1 -1
- data/lib/erc20/fake_wallet.rb +4 -2
- data/lib/erc20/wallet.rb +53 -8
- data/test/erc20/test_fake_wallet.rb +6 -1
- 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/fake_wallet.rb
CHANGED
@@ -70,6 +70,9 @@ class ERC20::FakeWallet
|
|
70
70
|
def accept(addresses, active = [], raw: false, delay: 1)
|
71
71
|
addresses.to_a.each { |a| active.append(a) }
|
72
72
|
loop do
|
73
|
+
sleep(delay)
|
74
|
+
a = addresses.to_a.sample
|
75
|
+
next if a.nil?
|
73
76
|
event =
|
74
77
|
if raw
|
75
78
|
{}
|
@@ -77,12 +80,11 @@ class ERC20::FakeWallet
|
|
77
80
|
{
|
78
81
|
amount: 424_242,
|
79
82
|
from: '0xd5ff1bfcde7a03da61ad229d962c74f1ea2f16a5',
|
80
|
-
to:
|
83
|
+
to: a,
|
81
84
|
txn: '0x172de9cda30537eae68ab4a96163ebbb8f8a85293b8737dd2e5deb4714b14623'
|
82
85
|
}
|
83
86
|
end
|
84
87
|
yield event
|
85
|
-
sleep(delay)
|
86
88
|
end
|
87
89
|
end
|
88
90
|
end
|
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
|
@@ -61,13 +65,14 @@ class TestFakeWallet < Minitest::Test
|
|
61
65
|
event = nil
|
62
66
|
daemon =
|
63
67
|
Thread.new do
|
64
|
-
ERC20::FakeWallet.new.accept(addresses, active) do |e|
|
68
|
+
ERC20::FakeWallet.new.accept(addresses, active, delay: 0.1) do |e|
|
65
69
|
event = e
|
66
70
|
end
|
67
71
|
rescue StandardError => e
|
68
72
|
loog.error(Backtrace.new(e))
|
69
73
|
end
|
70
74
|
wait_for { !active.to_a.empty? }
|
75
|
+
wait_for { !event.nil? }
|
71
76
|
daemon.kill
|
72
77
|
daemon.join(30)
|
73
78
|
refute_nil(event)
|
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
|