erc20 0.0.2 → 0.0.4
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 +3 -1
- data/Gemfile.lock +0 -1
- data/README.md +8 -6
- data/erc20.gemspec +0 -1
- data/lib/erc20/wallet.rb +38 -7
- data/lib/erc20.rb +1 -1
- data/test/erc20/test_wallet.rb +10 -12
- metadata +1 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b5bccf0ea259136bad39ab36f01240da8a029958b627ed337cd20477a7a13c9
|
4
|
+
data.tar.gz: 4c5d605e086d38755d25d8153c0bf6b220460f5b984607fd304706fe0b3d942f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0adbab22a3b752f63e1e001eac554ba8bcc8a40a2fea7234b9d674608eb281c912fb2e21931497006509b8cf2ae80e82d9de8dd49b7727f48759796da375d43b
|
7
|
+
data.tar.gz: ae3387a6c71c5ba367c8bd5814d2eebced21d6de6087dc214b083659811e2ed60efb5d0d981b7ba57b10030435566645ddd8328ac99ef4f9574b534192385383
|
data/.rubocop.yml
CHANGED
@@ -43,8 +43,10 @@ Layout/MultilineMethodCallIndentation:
|
|
43
43
|
Enabled: false
|
44
44
|
Metrics/AbcSize:
|
45
45
|
Enabled: false
|
46
|
+
Metrics/ParameterLists:
|
47
|
+
Enabled: false
|
46
48
|
Metrics/BlockLength:
|
47
|
-
Max:
|
49
|
+
Max: 100
|
48
50
|
Metrics/CyclomaticComplexity:
|
49
51
|
Max: 25
|
50
52
|
Metrics/PerceivedComplexity:
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -11,17 +11,19 @@
|
|
11
11
|
[](https://hitsofcode.com/view/github/yegor256/erc20)
|
12
12
|
[](https://github.com/yegor256/erc20/blob/master/LICENSE.txt)
|
13
13
|
|
14
|
-
This small Ruby
|
14
|
+
This small Ruby [gem](https://rubygems.org/gems/erc20)
|
15
|
+
makes manipulations with [Etherium] [ERC20] tokens
|
15
16
|
as simple as they can be, if you have a provider of
|
16
17
|
[JSON-RPC] and [WebSocket] Etherium APIs, for example
|
17
18
|
[Infura], [GetBlock], or [Alchemy]:
|
18
19
|
|
19
20
|
```ruby
|
20
21
|
# Create a wallet:
|
22
|
+
require 'erc20'
|
21
23
|
w = ERC20::Wallet.new(
|
22
24
|
contract: ERC20::Wallet.USDT, # hex of it
|
23
|
-
|
24
|
-
|
25
|
+
host: 'mainnet.infura.io',
|
26
|
+
path: '/v3/<your-key>',
|
25
27
|
log: $stdout
|
26
28
|
)
|
27
29
|
|
@@ -34,9 +36,9 @@ txn = w.pay(private_key, to_address, amount)
|
|
34
36
|
# Stay waiting, and trigger the block when transactions arrive:
|
35
37
|
addresses = ['0x...', '0x...']
|
36
38
|
w.accept(addresses) do |event|
|
37
|
-
puts event[
|
38
|
-
puts
|
39
|
-
puts
|
39
|
+
puts event[:amount] # how much
|
40
|
+
puts event[:from] # who sent the payment
|
41
|
+
puts event[:to] # who was the receiver
|
40
42
|
end
|
41
43
|
```
|
42
44
|
|
data/erc20.gemspec
CHANGED
@@ -43,7 +43,6 @@ Gem::Specification.new do |s|
|
|
43
43
|
s.add_dependency 'eth', '~>0.4'
|
44
44
|
s.add_dependency 'jsonrpc-client', '>0'
|
45
45
|
s.add_dependency 'loog', '>0'
|
46
|
-
s.add_dependency 'tago', '>0'
|
47
46
|
s.add_dependency 'websocket-client-simple', '>0'
|
48
47
|
s.metadata['rubygems_mfa_required'] = 'true'
|
49
48
|
end
|
data/lib/erc20/wallet.rb
CHANGED
@@ -41,11 +41,28 @@ class ERC20::Wallet
|
|
41
41
|
# @param [String] contract Hex of the contract in Etherium
|
42
42
|
# @param [String] rpc The URL of Etherium JSON-RPC provider
|
43
43
|
# @param [Integer] chain The ID of the chain (1 for mainnet)
|
44
|
+
# @param [String] host The host to connect to
|
45
|
+
# @param [Integer] port TCP port to use
|
46
|
+
# @param [String] path The path in the connection URL
|
47
|
+
# @param [Boolean] ssl Should we use SSL (for https and wss)
|
44
48
|
# @param [Object] log The destination for logs
|
45
|
-
def initialize(contract: USDT, rpc: nil, wss: nil, chain: 1, log: $stdout
|
49
|
+
def initialize(contract: USDT, rpc: nil, wss: nil, chain: 1, log: $stdout,
|
50
|
+
host: nil, port: 443, path: '/', ssl: true)
|
46
51
|
@contract = contract
|
47
|
-
|
48
|
-
|
52
|
+
raise 'Use either host or rpc' if rpc && host
|
53
|
+
raise 'Use either host or wss' if wss && host
|
54
|
+
if rpc
|
55
|
+
@rpc = rpc
|
56
|
+
else
|
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
|
49
66
|
@log = log
|
50
67
|
@chain = chain
|
51
68
|
end
|
@@ -59,7 +76,9 @@ class ERC20::Wallet
|
|
59
76
|
padded = "000000000000000000000000#{hex[2..].downcase}"
|
60
77
|
data = "0x#{func}#{padded}"
|
61
78
|
r = jsonrpc.eth_call({ to: @contract, data: data }, 'latest')
|
62
|
-
r[2..].to_i(16)
|
79
|
+
b = r[2..].to_i(16)
|
80
|
+
@log.debug("Balance of #{hex} is #{b}")
|
81
|
+
b
|
63
82
|
end
|
64
83
|
|
65
84
|
# Send a single payment from a private address to a public one.
|
@@ -92,7 +111,10 @@ class ERC20::Wallet
|
|
92
111
|
}
|
93
112
|
)
|
94
113
|
tx.sign(key)
|
95
|
-
|
114
|
+
hex = "0x#{tx.hex}"
|
115
|
+
jsonrpc.eth_sendRawTransaction(hex)
|
116
|
+
@log.debug("Sent #{amount} from #{from} to #{address}: #{hex}")
|
117
|
+
hex
|
96
118
|
end
|
97
119
|
|
98
120
|
# Wait for incoming transactions and let the block know when they
|
@@ -101,7 +123,8 @@ class ERC20::Wallet
|
|
101
123
|
#
|
102
124
|
# @param [Array<String>] addresses Addresses to monitor
|
103
125
|
# @param [Array] ready When connected, TRUE will be added to this array
|
104
|
-
|
126
|
+
# @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
|
127
|
+
def accept(addresses, connected: [], raw: false)
|
105
128
|
WebSocket::Client::Simple.connect(@wss) do |ws|
|
106
129
|
log = @log
|
107
130
|
contract = @contract
|
@@ -126,6 +149,7 @@ class ERC20::Wallet
|
|
126
149
|
}
|
127
150
|
ws.send(msg.to_json)
|
128
151
|
connected.append(1)
|
152
|
+
log.debug("Subscribed to #{addresses.count} addresses")
|
129
153
|
end
|
130
154
|
ws.on(:message) do |msg|
|
131
155
|
data =
|
@@ -136,7 +160,14 @@ class ERC20::Wallet
|
|
136
160
|
end
|
137
161
|
if data['method'] == 'eth_subscription' && data.dig('params', 'result')
|
138
162
|
event = data['params']['result']
|
139
|
-
|
163
|
+
unless raw
|
164
|
+
event = {
|
165
|
+
amount: event['data'].to_i(16),
|
166
|
+
from: "0x#{event['topics'][1][26..].downcase}",
|
167
|
+
to: "0x#{event['topics'][2][26..].downcase}"
|
168
|
+
}
|
169
|
+
end
|
170
|
+
log.debug("New event arrived from #{event['address']}")
|
140
171
|
yield event
|
141
172
|
end
|
142
173
|
end
|
data/lib/erc20.rb
CHANGED
data/test/erc20/test_wallet.rb
CHANGED
@@ -62,6 +62,7 @@ class TestWallet < Minitest::Test
|
|
62
62
|
def test_fails_with_invalid_infura_key
|
63
63
|
w = ERC20::Wallet.new(
|
64
64
|
rpc: 'https://mainnet.infura.io/v3/invalid-key-here',
|
65
|
+
wss: 'https://mainnet.infura.io/v3/another-invalid-key-here',
|
65
66
|
log: Loog::NULL
|
66
67
|
)
|
67
68
|
assert_raises(StandardError) { w.balance(STABLE_ADDRESS) }
|
@@ -113,11 +114,9 @@ class TestWallet < Minitest::Test
|
|
113
114
|
wait_for { !event.nil? }
|
114
115
|
daemon.kill
|
115
116
|
daemon.join(30)
|
116
|
-
assert_equal(sum, event[
|
117
|
-
|
118
|
-
assert_equal(
|
119
|
-
to = "0x#{event['topics'][2][26..].downcase}"
|
120
|
-
assert_equal(walter, to)
|
117
|
+
assert_equal(sum, event[:amount])
|
118
|
+
assert_equal(jeff, event[:from])
|
119
|
+
assert_equal(walter, event[:to])
|
121
120
|
end
|
122
121
|
end
|
123
122
|
|
@@ -128,7 +127,7 @@ class TestWallet < Minitest::Test
|
|
128
127
|
loop do
|
129
128
|
sleep(0.1)
|
130
129
|
break if yield
|
131
|
-
raise 'timeout' if Time.now - start >
|
130
|
+
raise 'timeout' if Time.now - start > 60
|
132
131
|
rescue Errno::ECONNREFUSED
|
133
132
|
retry
|
134
133
|
end
|
@@ -148,8 +147,8 @@ class TestWallet < Minitest::Test
|
|
148
147
|
[
|
149
148
|
"https://mainnet.infura.io/v3/#{env('INFURA_KEY')}",
|
150
149
|
"https://go.getblock.io/#{env('GETBLOCK_KEY')}"
|
151
|
-
].map do |
|
152
|
-
ERC20::Wallet.new(rpc
|
150
|
+
].map do |url|
|
151
|
+
ERC20::Wallet.new(rpc: url, wss: url, log: Loog::NULL)
|
153
152
|
end.sample
|
154
153
|
end
|
155
154
|
|
@@ -157,8 +156,8 @@ class TestWallet < Minitest::Test
|
|
157
156
|
[
|
158
157
|
"https://sepolia.infura.io/v3/#{env('INFURA_KEY')}",
|
159
158
|
"https://go.getblock.io/#{env('GETBLOCK_SEPOILA_KEY')}"
|
160
|
-
].map do |
|
161
|
-
ERC20::Wallet.new(rpc
|
159
|
+
].map do |url|
|
160
|
+
ERC20::Wallet.new(rpc: url, wss: url, log: Loog::NULL)
|
162
161
|
end.sample
|
163
162
|
end
|
164
163
|
|
@@ -186,8 +185,7 @@ class TestWallet < Minitest::Test
|
|
186
185
|
).split("\n").last
|
187
186
|
wallet = ERC20::Wallet.new(
|
188
187
|
contract:, chain: 4242,
|
189
|
-
|
190
|
-
wss: "ws://localhost:#{port}",
|
188
|
+
host: 'localhost', port:, path: '/', ssl: false,
|
191
189
|
log: Loog::NULL
|
192
190
|
)
|
193
191
|
yield wallet
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yegor Bugayenko
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - ">"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: tago
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: websocket-client-simple
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|