erc20 0.0.4 → 0.0.5
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 +13 -12
- data/README.md +29 -6
- data/Rakefile +2 -0
- data/erc20.gemspec +8 -6
- data/lib/erc20/wallet.rb +63 -43
- data/lib/erc20.rb +1 -1
- data/test/erc20/test_wallet.rb +145 -24
- data/test/test__helper.rb +4 -2
- metadata +27 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 14d84ab9ee2ac821f5f404dcea22437f622fe82bf5565a04c695f718974ba129
|
|
4
|
+
data.tar.gz: d2ec55f58ef13c62abfb0b870d541d3cc5bd8a32f5c685eda64fb484558af332
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ef86114757f0b841b022a8cef64330610d68425d13e900487e4d99589065ed19640f393986f3ea62003fe2d24ae93513406611d4725d58dcc3165e7e70a5ffa
|
|
7
|
+
data.tar.gz: 92855745194e5cf9b6ff1902f0f7e4f44606aa4e306313668e7fb01849ab91e10fccf99e6f7ac6dfa0a6e176abc6d7aaa30f279b88e7c0f94b1133380f61d643
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -2,11 +2,11 @@ PATH
|
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
4
|
erc20 (0.0.0)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
eth (>= 0.5.13)
|
|
6
|
+
faye-websocket (>= 0.11.3)
|
|
7
|
+
json (>= 2.10.1)
|
|
8
|
+
jsonrpc-client (>= 0.1.4)
|
|
8
9
|
loog (> 0)
|
|
9
|
-
websocket-client-simple (> 0)
|
|
10
10
|
|
|
11
11
|
GEM
|
|
12
12
|
remote: https://rubygems.org/
|
|
@@ -71,13 +71,16 @@ GEM
|
|
|
71
71
|
scrypt (~> 3.0)
|
|
72
72
|
ethon (0.16.0)
|
|
73
73
|
ffi (>= 1.15.0)
|
|
74
|
-
|
|
74
|
+
eventmachine (1.2.7)
|
|
75
75
|
faraday (2.12.2)
|
|
76
76
|
faraday-net_http (>= 2.0, < 3.5)
|
|
77
77
|
json
|
|
78
78
|
logger
|
|
79
79
|
faraday-net_http (3.4.0)
|
|
80
80
|
net-http (>= 0.5.0)
|
|
81
|
+
faye-websocket (0.11.3)
|
|
82
|
+
eventmachine (>= 0.12.0)
|
|
83
|
+
websocket-driver (>= 0.5.1)
|
|
81
84
|
ffi (1.17.1-arm64-darwin)
|
|
82
85
|
ffi (1.17.1-x64-mingw-ucrt)
|
|
83
86
|
ffi (1.17.1-x86_64-darwin)
|
|
@@ -93,7 +96,7 @@ GEM
|
|
|
93
96
|
pp (>= 0.6.0)
|
|
94
97
|
rdoc (>= 4.0.0)
|
|
95
98
|
reline (>= 0.4.2)
|
|
96
|
-
json (2.10.
|
|
99
|
+
json (2.10.1)
|
|
97
100
|
jsonrpc-client (0.1.4)
|
|
98
101
|
faraday
|
|
99
102
|
multi_json (>= 1.1.0)
|
|
@@ -115,7 +118,6 @@ GEM
|
|
|
115
118
|
minitest-retry (0.2.5)
|
|
116
119
|
minitest (>= 5.0)
|
|
117
120
|
multi_json (1.15.0)
|
|
118
|
-
mutex_m (0.3.0)
|
|
119
121
|
net-http (0.6.0)
|
|
120
122
|
uri
|
|
121
123
|
nokogiri (1.18.2-arm64-darwin)
|
|
@@ -251,12 +253,10 @@ GEM
|
|
|
251
253
|
unicode-emoji (4.0.4)
|
|
252
254
|
uri (1.0.2)
|
|
253
255
|
useragent (0.16.11)
|
|
254
|
-
websocket (
|
|
255
|
-
websocket-client-simple (0.9.0)
|
|
256
|
+
websocket-driver (0.7.7)
|
|
256
257
|
base64
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
websocket
|
|
258
|
+
websocket-extensions (>= 0.1.0)
|
|
259
|
+
websocket-extensions (0.1.5)
|
|
260
260
|
yard (0.9.37)
|
|
261
261
|
zeitwerk (2.7.1)
|
|
262
262
|
|
|
@@ -273,6 +273,7 @@ DEPENDENCIES
|
|
|
273
273
|
backtrace (> 0)
|
|
274
274
|
donce (> 0)
|
|
275
275
|
erc20!
|
|
276
|
+
faraday (> 0)
|
|
276
277
|
loog (> 0)
|
|
277
278
|
minitest (= 5.25.4)
|
|
278
279
|
minitest-reporters (= 1.7.1)
|
data/README.md
CHANGED
|
@@ -23,18 +23,19 @@ require 'erc20'
|
|
|
23
23
|
w = ERC20::Wallet.new(
|
|
24
24
|
contract: ERC20::Wallet.USDT, # hex of it
|
|
25
25
|
host: 'mainnet.infura.io',
|
|
26
|
-
|
|
26
|
+
http_path: '/v3/<your-infura-key>',
|
|
27
|
+
ws_path: '/ws/v3/<your-infura-key>',
|
|
27
28
|
log: $stdout
|
|
28
29
|
)
|
|
29
30
|
|
|
30
|
-
# Check
|
|
31
|
+
# Check how many ERC20 tokens are on the given address:
|
|
31
32
|
usdt = w.balance(address)
|
|
32
33
|
|
|
33
|
-
# Send a few tokens to someone and get transaction hash:
|
|
34
|
-
|
|
34
|
+
# Send a few ERC20 tokens to someone and get transaction hash:
|
|
35
|
+
hex = w.pay(private_key, to_address, amount)
|
|
35
36
|
|
|
36
|
-
# Stay waiting, and trigger the block when
|
|
37
|
-
addresses = ['0x...', '0x...']
|
|
37
|
+
# Stay waiting, and trigger the block when new ERC20 payments show up:
|
|
38
|
+
addresses = ['0x...', '0x...'] # only wait for payments to these addresses
|
|
38
39
|
w.accept(addresses) do |event|
|
|
39
40
|
puts event[:amount] # how much
|
|
40
41
|
puts event[:from] # who sent the payment
|
|
@@ -55,6 +56,22 @@ To get address from private one:
|
|
|
55
56
|
public_hex = Eth::Key.new(priv: key).address
|
|
56
57
|
```
|
|
57
58
|
|
|
59
|
+
To connect to the server via [HTTP proxy] with [basic authentication]:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
w = ERC20::Wallet.new(
|
|
63
|
+
host: 'go.getblock.io',
|
|
64
|
+
http_path: '/<your-rpc-getblock-key>',
|
|
65
|
+
ws_path: '/<your-ws-getblock-key>',
|
|
66
|
+
proxy: 'http://jeffrey:swordfish@example.com:3128' # here!
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
You can use [squid-proxy] [Docker] image to set up your own [HTTP proxy] server.
|
|
71
|
+
|
|
72
|
+
Of course, this library works with [Polygon], [Optimism],
|
|
73
|
+
and other forks of [Etherium].
|
|
74
|
+
|
|
58
75
|
## How to contribute
|
|
59
76
|
|
|
60
77
|
Read
|
|
@@ -78,3 +95,9 @@ If it's clean and you don't see any error messages, submit your pull request.
|
|
|
78
95
|
[Infura]: https://infura.io/
|
|
79
96
|
[Alchemy]: https://alchemy.com/
|
|
80
97
|
[GetBlock]: https://getblock.io/
|
|
98
|
+
[basic authentication]: https://en.wikipedia.org/wiki/Basic_access_authentication
|
|
99
|
+
[HTTP proxy]: https://en.wikipedia.org/wiki/Proxy_server
|
|
100
|
+
[squid-proxy]: https://github.com/yegor256/squid-proxy
|
|
101
|
+
[Docker]: https://www.docker.com/
|
|
102
|
+
[Polygon]: https://polygon.technology/
|
|
103
|
+
[Optimism]: https://www.optimism.io/
|
data/Rakefile
CHANGED
data/erc20.gemspec
CHANGED
|
@@ -31,18 +31,20 @@ Gem::Specification.new do |s|
|
|
|
31
31
|
s.license = 'MIT'
|
|
32
32
|
s.summary = 'Sending and receiving ERC20 tokens in Etherium network'
|
|
33
33
|
s.description =
|
|
34
|
-
'A simple library for making ERC20 manipulations as easy as they' \
|
|
35
|
-
'can be for cryptocurrency newbies'
|
|
34
|
+
'A simple library for making ERC20 manipulations as easy as they ' \
|
|
35
|
+
'can be for cryptocurrency newbies: checking balance, sending payments, ' \
|
|
36
|
+
'and monitoring addresses for incoming payments. The library expects ' \
|
|
37
|
+
'Etherium node to provide JSON RPC and Websockets API.'
|
|
36
38
|
s.authors = ['Yegor Bugayenko']
|
|
37
39
|
s.email = 'yegor256@gmail.com'
|
|
38
40
|
s.homepage = 'http://github.com/yegor256/erc20.rb'
|
|
39
41
|
s.files = `git ls-files`.split($RS)
|
|
40
42
|
s.rdoc_options = ['--charset=UTF-8']
|
|
41
43
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
42
|
-
s.add_dependency '
|
|
43
|
-
s.add_dependency '
|
|
44
|
-
s.add_dependency '
|
|
44
|
+
s.add_dependency 'eth', '>=0.5.13'
|
|
45
|
+
s.add_dependency 'faye-websocket', '>=0.11.3'
|
|
46
|
+
s.add_dependency 'json', '>=2.10.1'
|
|
47
|
+
s.add_dependency 'jsonrpc-client', '>=0.1.4'
|
|
45
48
|
s.add_dependency 'loog', '>0'
|
|
46
|
-
s.add_dependency 'websocket-client-simple', '>0'
|
|
47
49
|
s.metadata['rubygems_mfa_required'] = 'true'
|
|
48
50
|
end
|
data/lib/erc20/wallet.rb
CHANGED
|
@@ -21,9 +21,12 @@
|
|
|
21
21
|
# SOFTWARE.
|
|
22
22
|
|
|
23
23
|
require 'eth'
|
|
24
|
+
require 'eventmachine'
|
|
25
|
+
require 'faye/websocket'
|
|
26
|
+
require 'json'
|
|
24
27
|
require 'jsonrpc/client'
|
|
25
28
|
require 'loog'
|
|
26
|
-
require '
|
|
29
|
+
require 'uri'
|
|
27
30
|
require_relative '../erc20'
|
|
28
31
|
|
|
29
32
|
# A wallet.
|
|
@@ -37,34 +40,31 @@ class ERC20::Wallet
|
|
|
37
40
|
# Address of USDT contract.
|
|
38
41
|
USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'
|
|
39
42
|
|
|
43
|
+
# These properties are read-only:
|
|
44
|
+
attr_reader :host, :port, :ssl, :chain, :contract, :ws_path, :http_path
|
|
45
|
+
|
|
40
46
|
# Constructor.
|
|
41
47
|
# @param [String] contract Hex of the contract in Etherium
|
|
42
|
-
# @param [String] rpc The URL of Etherium JSON-RPC provider
|
|
43
48
|
# @param [Integer] chain The ID of the chain (1 for mainnet)
|
|
44
49
|
# @param [String] host The host to connect to
|
|
45
50
|
# @param [Integer] port TCP port to use
|
|
46
|
-
# @param [String]
|
|
51
|
+
# @param [String] http_path The path in the connection URL, for HTTP RPC
|
|
52
|
+
# @param [String] ws_path The path in the connection URL, for Websockets
|
|
47
53
|
# @param [Boolean] ssl Should we use SSL (for https and wss)
|
|
54
|
+
# @param [String] proxy The URL of the proxy to use
|
|
48
55
|
# @param [Object] log The destination for logs
|
|
49
|
-
def initialize(contract: USDT,
|
|
50
|
-
host: nil, port: 443,
|
|
56
|
+
def initialize(contract: USDT, chain: 1, log: $stdout,
|
|
57
|
+
host: nil, port: 443, http_path: '/', ws_path: '/',
|
|
58
|
+
ssl: true, proxy: nil)
|
|
51
59
|
@contract = contract
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raise 'Either rpc or host+port+path are required' unless host && port && path
|
|
58
|
-
@rpc = "http#{ssl ? 's' : ''}://#{host}:#{port}#{path}"
|
|
59
|
-
end
|
|
60
|
-
if wss
|
|
61
|
-
@wss = wss
|
|
62
|
-
else
|
|
63
|
-
raise 'Either wss or host+port+path are required' unless host && port && path
|
|
64
|
-
@wss = "http#{ssl ? 's' : ''}://#{host}:#{port}#{path}"
|
|
65
|
-
end
|
|
60
|
+
@host = host
|
|
61
|
+
@port = port
|
|
62
|
+
@ssl = ssl
|
|
63
|
+
@http_path = http_path
|
|
64
|
+
@ws_path = ws_path
|
|
66
65
|
@log = log
|
|
67
66
|
@chain = chain
|
|
67
|
+
@proxy = proxy
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# Get balance of a public address.
|
|
@@ -119,35 +119,39 @@ class ERC20::Wallet
|
|
|
119
119
|
|
|
120
120
|
# Wait for incoming transactions and let the block know when they
|
|
121
121
|
# arrive. It's a blocking call, it's better to run it in a separate
|
|
122
|
-
# thread.
|
|
122
|
+
# thread. It will never finish. In order to stop it, you should do
|
|
123
|
+
# +Thread.kill+.
|
|
123
124
|
#
|
|
124
125
|
# @param [Array<String>] addresses Addresses to monitor
|
|
125
126
|
# @param [Array] ready When connected, TRUE will be added to this array
|
|
126
127
|
# @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
|
|
127
128
|
def accept(addresses, connected: [], raw: false)
|
|
128
|
-
|
|
129
|
+
EM.run do
|
|
130
|
+
u = url(http: false)
|
|
131
|
+
@log.debug("Connecting to #{u}...")
|
|
132
|
+
ws = Faye::WebSocket::Client.new(u, [], proxy: @proxy ? { origin: @proxy } : {})
|
|
129
133
|
log = @log
|
|
130
134
|
contract = @contract
|
|
131
|
-
wss = @wss
|
|
132
135
|
ws.on(:open) do
|
|
133
|
-
log.debug("Connected to #{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
136
|
+
log.debug("Connected to #{@host}")
|
|
137
|
+
ws.send(
|
|
138
|
+
{
|
|
139
|
+
jsonrpc: '2.0',
|
|
140
|
+
id: 1,
|
|
141
|
+
method: 'eth_subscribe',
|
|
142
|
+
params: [
|
|
143
|
+
'logs',
|
|
144
|
+
{
|
|
145
|
+
address: contract,
|
|
146
|
+
topics: [
|
|
147
|
+
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
|
148
|
+
nil,
|
|
149
|
+
addresses.map { |a| "0x000000000000000000000000#{a[2..]}" }
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
}.to_json
|
|
154
|
+
)
|
|
151
155
|
connected.append(1)
|
|
152
156
|
log.debug("Subscribed to #{addresses.count} addresses")
|
|
153
157
|
end
|
|
@@ -172,19 +176,35 @@ class ERC20::Wallet
|
|
|
172
176
|
end
|
|
173
177
|
end
|
|
174
178
|
ws.on(:close) do |_e|
|
|
175
|
-
log.debug("Disconnected from #{
|
|
179
|
+
log.debug("Disconnected from #{@host}")
|
|
176
180
|
end
|
|
177
181
|
ws.on(:error) do |e|
|
|
178
|
-
log.debug("Error at #{
|
|
182
|
+
log.debug("Error at #{@host}: #{e.message}")
|
|
179
183
|
end
|
|
180
184
|
end
|
|
181
185
|
end
|
|
182
186
|
|
|
183
187
|
private
|
|
184
188
|
|
|
189
|
+
def url(http: true)
|
|
190
|
+
"#{http ? 'http' : 'ws'}#{@ssl ? 's' : ''}://#{@host}:#{@port}#{http ? @http_path : @ws_path}"
|
|
191
|
+
end
|
|
192
|
+
|
|
185
193
|
def jsonrpc
|
|
186
194
|
JSONRPC.logger = Loog::NULL
|
|
187
|
-
|
|
195
|
+
connection =
|
|
196
|
+
if @proxy
|
|
197
|
+
uri = URI.parse(@proxy)
|
|
198
|
+
Faraday.new do |f|
|
|
199
|
+
f.adapter(Faraday.default_adapter)
|
|
200
|
+
f.proxy = {
|
|
201
|
+
uri: "#{uri.scheme}://#{uri.hostname}:#{uri.port}",
|
|
202
|
+
user: uri.user,
|
|
203
|
+
password: uri.password
|
|
204
|
+
}
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
JSONRPC::Client.new(url, connection:)
|
|
188
208
|
end
|
|
189
209
|
|
|
190
210
|
def gas_estimate(from, data)
|
data/lib/erc20.rb
CHANGED
data/test/erc20/test_wallet.rb
CHANGED
|
@@ -23,22 +23,24 @@
|
|
|
23
23
|
require 'backtrace'
|
|
24
24
|
require 'donce'
|
|
25
25
|
require 'eth'
|
|
26
|
+
require 'faraday'
|
|
26
27
|
require 'loog'
|
|
28
|
+
require 'minitest/autorun'
|
|
27
29
|
require 'random-port'
|
|
28
30
|
require 'shellwords'
|
|
29
31
|
require 'typhoeus'
|
|
30
|
-
require 'minitest/autorun'
|
|
31
32
|
require_relative '../../lib/erc20'
|
|
32
33
|
require_relative '../../lib/erc20/wallet'
|
|
34
|
+
require_relative '../test__helper'
|
|
33
35
|
|
|
34
36
|
# Test.
|
|
35
37
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
36
38
|
# Copyright:: Copyright (c) 2025 Yegor Bugayenko
|
|
37
39
|
# License:: MIT
|
|
38
40
|
class TestWallet < Minitest::Test
|
|
39
|
-
# At this address, in
|
|
41
|
+
# At this address, in Etherium mainnet, there are a few USDT tokens. I won't
|
|
40
42
|
# move them anyway, that's why tests can use this address forever.
|
|
41
|
-
|
|
43
|
+
STABLE = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
|
|
42
44
|
|
|
43
45
|
# One guy private hex.
|
|
44
46
|
JEFF = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
|
|
@@ -47,7 +49,7 @@ class TestWallet < Minitest::Test
|
|
|
47
49
|
WALTER = '91f9111b1744d55361e632771a4e53839e9442a9fef45febc0a5c838c686a15b'
|
|
48
50
|
|
|
49
51
|
def test_checks_balance_on_mainnet
|
|
50
|
-
b = mainnet.balance(
|
|
52
|
+
b = mainnet.balance(STABLE)
|
|
51
53
|
refute_nil(b)
|
|
52
54
|
assert_equal(27_258_889, b)
|
|
53
55
|
end
|
|
@@ -61,18 +63,28 @@ class TestWallet < Minitest::Test
|
|
|
61
63
|
|
|
62
64
|
def test_fails_with_invalid_infura_key
|
|
63
65
|
w = ERC20::Wallet.new(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
log:
|
|
66
|
+
host: 'mainnet.infura.io',
|
|
67
|
+
http_path: '/v3/invalid-key-here',
|
|
68
|
+
log: loog
|
|
67
69
|
)
|
|
68
|
-
assert_raises(StandardError) { w.balance(
|
|
70
|
+
assert_raises(StandardError) { w.balance(STABLE) }
|
|
69
71
|
end
|
|
70
72
|
|
|
71
73
|
def test_checks_balance_on_testnet
|
|
72
|
-
|
|
73
|
-
b
|
|
74
|
+
b = testnet.balance(STABLE)
|
|
75
|
+
refute_nil(b)
|
|
76
|
+
assert_predicate(b, :zero?)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_checks_balance_on_polygon
|
|
80
|
+
w = ERC20::Wallet.new(
|
|
81
|
+
contract: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
|
|
82
|
+
host: 'polygon-mainnet.infura.io', http_path: "/v3/#{env('INFURA_KEY')}",
|
|
83
|
+
log: loog
|
|
84
|
+
)
|
|
85
|
+
b = w.balance(STABLE)
|
|
74
86
|
refute_nil(b)
|
|
75
|
-
assert_predicate(b, :
|
|
87
|
+
assert_predicate(b, :zero?)
|
|
76
88
|
end
|
|
77
89
|
|
|
78
90
|
def test_checks_balance_on_hardhat
|
|
@@ -106,7 +118,7 @@ class TestWallet < Minitest::Test
|
|
|
106
118
|
event = e
|
|
107
119
|
end
|
|
108
120
|
rescue StandardError => e
|
|
109
|
-
|
|
121
|
+
loog.error(Backtrace.new(e))
|
|
110
122
|
end
|
|
111
123
|
wait_for { !connected.empty? }
|
|
112
124
|
sum = 77_000
|
|
@@ -120,8 +132,80 @@ class TestWallet < Minitest::Test
|
|
|
120
132
|
end
|
|
121
133
|
end
|
|
122
134
|
|
|
135
|
+
def test_accepts_payments_on_hardhat_via_proxy
|
|
136
|
+
via_proxy do |proxy|
|
|
137
|
+
walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
|
|
138
|
+
jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
|
|
139
|
+
on_hardhat do |w|
|
|
140
|
+
wallet = through_proxy(w, proxy)
|
|
141
|
+
connected = []
|
|
142
|
+
event = nil
|
|
143
|
+
daemon =
|
|
144
|
+
Thread.new do
|
|
145
|
+
wallet.accept([walter, jeff], connected:) do |e|
|
|
146
|
+
event = e
|
|
147
|
+
end
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
loog.error(Backtrace.new(e))
|
|
150
|
+
end
|
|
151
|
+
wait_for { !connected.empty? }
|
|
152
|
+
sum = 55_000
|
|
153
|
+
wallet.pay(JEFF, walter, sum)
|
|
154
|
+
wait_for { !event.nil? }
|
|
155
|
+
daemon.kill
|
|
156
|
+
daemon.join(30)
|
|
157
|
+
assert_equal(sum, event[:amount])
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_accepts_payments_on_mainnet
|
|
163
|
+
connected = []
|
|
164
|
+
failed = false
|
|
165
|
+
net = mainnet
|
|
166
|
+
daemon =
|
|
167
|
+
Thread.new do
|
|
168
|
+
net.accept([STABLE], connected:) do |_|
|
|
169
|
+
# ignore it
|
|
170
|
+
end
|
|
171
|
+
rescue StandardError => e
|
|
172
|
+
failed = true
|
|
173
|
+
loog.error(Backtrace.new(e))
|
|
174
|
+
end
|
|
175
|
+
wait_for { !connected.empty? }
|
|
176
|
+
daemon.kill
|
|
177
|
+
daemon.join(30)
|
|
178
|
+
refute(failed)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_checks_balance_via_proxy
|
|
182
|
+
via_proxy do |proxy|
|
|
183
|
+
on_hardhat do |w|
|
|
184
|
+
wallet = through_proxy(w, proxy)
|
|
185
|
+
b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
|
|
186
|
+
assert_equal(123_000_100_000, b)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_checks_balance_via_proxy_on_mainnet
|
|
192
|
+
via_proxy do |proxy|
|
|
193
|
+
on_hardhat do
|
|
194
|
+
w = ERC20::Wallet.new(
|
|
195
|
+
host: 'mainnet.infura.io', http_path: "/v3/#{env('INFURA_KEY')}",
|
|
196
|
+
proxy:, log: loog
|
|
197
|
+
)
|
|
198
|
+
assert_equal(27_258_889, w.balance(STABLE))
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
123
203
|
private
|
|
124
204
|
|
|
205
|
+
def loog
|
|
206
|
+
ENV['RAKE'] ? Loog::NULL : Loog::VERBOSE
|
|
207
|
+
end
|
|
208
|
+
|
|
125
209
|
def wait_for
|
|
126
210
|
start = Time.now
|
|
127
211
|
loop do
|
|
@@ -145,29 +229,66 @@ class TestWallet < Minitest::Test
|
|
|
145
229
|
|
|
146
230
|
def mainnet
|
|
147
231
|
[
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
232
|
+
{
|
|
233
|
+
host: 'mainnet.infura.io',
|
|
234
|
+
http_path: "/v3/#{env('INFURA_KEY')}",
|
|
235
|
+
ws_path: "/ws/v3/#{env('INFURA_KEY')}"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
host: 'go.getblock.io',
|
|
239
|
+
http_path: "/#{env('GETBLOCK_KEY')}",
|
|
240
|
+
ws_path: "/#{env('GETBLOCK_WS_KEY')}"
|
|
241
|
+
}
|
|
242
|
+
].map do |server|
|
|
243
|
+
ERC20::Wallet.new(host: server[:host], http_path: server[:http_path], ws_path: server[:ws_path], log: loog)
|
|
152
244
|
end.sample
|
|
153
245
|
end
|
|
154
246
|
|
|
155
247
|
def testnet
|
|
156
248
|
[
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
249
|
+
{
|
|
250
|
+
host: 'sepolia.infura.io',
|
|
251
|
+
http_path: "/v3/#{env('INFURA_KEY')}",
|
|
252
|
+
ws_path: "/ws/v3/#{env('INFURA_KEY')}"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
host: 'go.getblock.io',
|
|
256
|
+
http_path: "/#{env('GETBLOCK_SEPOILA_KEY')}",
|
|
257
|
+
ws_path: "/#{env('GETBLOCK_SEPOILA_KEY')}"
|
|
258
|
+
}
|
|
259
|
+
].map do |server|
|
|
260
|
+
ERC20::Wallet.new(host: server[:host], http_path: server[:http_path], ws_path: server[:ws_path], log: loog)
|
|
161
261
|
end.sample
|
|
162
262
|
end
|
|
163
263
|
|
|
264
|
+
def through_proxy(wallet, proxy)
|
|
265
|
+
ERC20::Wallet.new(
|
|
266
|
+
contract: wallet.contract, chain: wallet.chain,
|
|
267
|
+
host: donce_host, port: wallet.port, http_path: wallet.http_path, ws_path: wallet.ws_path,
|
|
268
|
+
ssl: wallet.ssl, proxy:, log: loog
|
|
269
|
+
)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def via_proxy
|
|
273
|
+
RandomPort::Pool::SINGLETON.acquire do |port|
|
|
274
|
+
donce(
|
|
275
|
+
image: 'yegor256/squid-proxy:latest',
|
|
276
|
+
ports: { port => 3128 },
|
|
277
|
+
env: { 'USERNAME' => 'jeffrey', 'PASSWORD' => 'swordfish' },
|
|
278
|
+
root: true, log: loog
|
|
279
|
+
) do
|
|
280
|
+
yield "http://jeffrey:swordfish@localhost:#{port}"
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
164
285
|
def on_hardhat
|
|
165
286
|
RandomPort::Pool::SINGLETON.acquire do |port|
|
|
166
287
|
donce(
|
|
167
288
|
home: File.join(__dir__, '../../hardhat'),
|
|
168
289
|
ports: { port => 8545 },
|
|
169
290
|
command: 'npx hardhat node',
|
|
170
|
-
log:
|
|
291
|
+
log: loog
|
|
171
292
|
) do
|
|
172
293
|
wait_for_port(port)
|
|
173
294
|
cmd = [
|
|
@@ -180,13 +301,13 @@ class TestWallet < Minitest::Test
|
|
|
180
301
|
home: File.join(__dir__, '../../hardhat'),
|
|
181
302
|
command: "/bin/bash -c #{Shellwords.escape(cmd)}",
|
|
182
303
|
build_args: { 'HOST' => donce_host, 'PORT' => port },
|
|
183
|
-
log:
|
|
304
|
+
log: loog,
|
|
184
305
|
root: true
|
|
185
306
|
).split("\n").last
|
|
186
307
|
wallet = ERC20::Wallet.new(
|
|
187
308
|
contract:, chain: 4242,
|
|
188
|
-
host: 'localhost', port:,
|
|
189
|
-
log:
|
|
309
|
+
host: 'localhost', port:, http_path: '/', ws_path: '/', ssl: false,
|
|
310
|
+
log: loog
|
|
190
311
|
)
|
|
191
312
|
yield wallet
|
|
192
313
|
end
|
data/test/test__helper.rb
CHANGED
|
@@ -35,5 +35,7 @@ require 'minitest/reporters'
|
|
|
35
35
|
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
|
36
36
|
|
|
37
37
|
# To make tests retry on failure:
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if ENV['RAKE']
|
|
39
|
+
require 'minitest/retry'
|
|
40
|
+
Minitest::Retry.use!(methods_to_skip: [])
|
|
41
|
+
end
|
metadata
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
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.5
|
|
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-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: eth
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
19
|
+
version: 0.5.13
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- - "
|
|
24
|
+
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 0.5.13
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
28
|
+
name: faye-websocket
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 0.11.3
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 0.11.3
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
42
|
+
name: json
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: 2.10.1
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: 2.10.1
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: jsonrpc-client
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: 0.1.4
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: 0.1.4
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
70
|
+
name: loog
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - ">"
|
|
@@ -80,8 +80,10 @@ dependencies:
|
|
|
80
80
|
- - ">"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
|
-
description: A simple library for making ERC20 manipulations as easy as
|
|
84
|
-
for cryptocurrency newbies
|
|
83
|
+
description: 'A simple library for making ERC20 manipulations as easy as they can
|
|
84
|
+
be for cryptocurrency newbies: checking balance, sending payments, and monitoring
|
|
85
|
+
addresses for incoming payments. The library expects Etherium node to provide JSON
|
|
86
|
+
RPC and Websockets API.'
|
|
85
87
|
email: yegor256@gmail.com
|
|
86
88
|
executables: []
|
|
87
89
|
extensions: []
|