erc20 0.0.1 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b960e80558aca8b654d178d60629c83673a67c419a41be4e224be99b12bebbf
4
- data.tar.gz: 706ad9b5c8187dd6db93821f0aefe2d84ddadfe6f77eac35d5e416e052653f57
3
+ metadata.gz: 9b1ea9500a3c12a4b5f84d2aa2101e16c3cfcf1fef9f0d8ccb95433aba9798cb
4
+ data.tar.gz: dcd57712fa1fd1adb99511cbc42cb1c2315e7178b9d90862bddfb632ec83b0fc
5
5
  SHA512:
6
- metadata.gz: 935689bf9f9e0bc69a8d5415bf30d5f24f0c64c3057a5e5496229f73c1a4a0f65c51f2d243959f6cf0be39d44e35f19eaa4d0df1e62320d1861b9d28196de33c
7
- data.tar.gz: 875542239945570f71fa515d478d70ba705b5c85a66d1109b1e138e71b318cdba01044c6a51a791912b2551c0f5d5cc02ff7022e7f1e560dfa45faa86d875c03
6
+ metadata.gz: dada085699673561fff510ba8d2071d983c36634b7a7bdf0232536ee4319469a4cd19d7f6b5b48ec6572ec974eeaa5fe192052210b985fb3a094bfbf5b97c8be
7
+ data.tar.gz: 306828e368f07f87274eeaf1439075bc11a7b858260997419df4620b94682156b3d77000d9d5245aba5397507a0de107339f4d13b354cf3291cf592437884a69
@@ -28,6 +28,7 @@ name: actionlint
28
28
  - master
29
29
  jobs:
30
30
  actionlint:
31
+ timeout-minutes: 15
31
32
  runs-on: ubuntu-24.04
32
33
  steps:
33
34
  - uses: actions/checkout@v4
@@ -25,6 +25,7 @@ name: codecov
25
25
  - master
26
26
  jobs:
27
27
  codecov:
28
+ timeout-minutes: 15
28
29
  runs-on: ubuntu-24.04
29
30
  steps:
30
31
  - uses: actions/checkout@v4
@@ -33,7 +34,7 @@ jobs:
33
34
  ruby-version: 3.3
34
35
  bundler-cache: true
35
36
  - run: bundle config set --global path "$(pwd)/vendor/bundle"
36
- - run: bundle install
37
+ - run: bundle install --no-color
37
38
  - run: bundle exec rake
38
39
  - uses: codecov/codecov-action@v5
39
40
  with:
@@ -28,6 +28,7 @@ name: copyrights
28
28
  - master
29
29
  jobs:
30
30
  copyrights:
31
+ timeout-minutes: 15
31
32
  runs-on: ubuntu-24.04
32
33
  steps:
33
34
  - uses: actions/checkout@v4
@@ -32,6 +32,7 @@ concurrency:
32
32
  cancel-in-progress: true
33
33
  jobs:
34
34
  markdown-lint:
35
+ timeout-minutes: 15
35
36
  runs-on: ubuntu-24.04
36
37
  steps:
37
38
  - uses: actions/checkout@v4
@@ -28,6 +28,7 @@ name: pdd
28
28
  - master
29
29
  jobs:
30
30
  pdd:
31
+ timeout-minutes: 15
31
32
  runs-on: ubuntu-24.04
32
33
  steps:
33
34
  - uses: actions/checkout@v4
@@ -28,18 +28,27 @@ name: rake
28
28
  - master
29
29
  jobs:
30
30
  rake:
31
- name: test
32
31
  strategy:
33
32
  matrix:
34
- os: [ubuntu-24.04, macos-15, windows-2022]
33
+ os: [ubuntu-24.04]
35
34
  ruby: [3.3]
36
35
  runs-on: ${{ matrix.os }}
36
+ env:
37
+ INFURA_KEY: ${{ secrets.INFURA_KEY }}
38
+ GETBLOCK_KEY: ${{ secrets.GETBLOCK_KEY }}
39
+ GETBLOCK_SEPOILA_KEY: ${{ secrets.GETBLOCK_SEPOILA_KEY }}
37
40
  steps:
38
41
  - uses: actions/checkout@v4
42
+ - run: |
43
+ sudo apt-get update --yes --fix-missing
44
+ sudo apt-get install --yes autoconf
45
+ if: matrix.os == 'ubuntu-24.04'
46
+ - run: brew install automake
47
+ if: matrix.os == 'macos-15'
39
48
  - uses: ruby/setup-ruby@v1
40
49
  with:
41
50
  ruby-version: ${{ matrix.ruby }}
42
51
  bundler-cache: true
43
52
  - run: bundle config set --global path "$(pwd)/vendor/bundle"
44
- - run: bundle install
53
+ - run: bundle install --no-color
45
54
  - run: bundle exec rake
@@ -0,0 +1,37 @@
1
+ # (The MIT License)
2
+ #
3
+ # Copyright (c) 2025 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+ ---
23
+ name: shellcheck
24
+ 'on':
25
+ push:
26
+ branches:
27
+ - master
28
+ pull_request:
29
+ branches:
30
+ - master
31
+ jobs:
32
+ shellcheck:
33
+ timeout-minutes: 15
34
+ runs-on: ubuntu-24.04
35
+ steps:
36
+ - uses: actions/checkout@v4
37
+ - uses: ludeeus/action-shellcheck@master
@@ -28,6 +28,7 @@ name: xcop
28
28
  - master
29
29
  jobs:
30
30
  xcop:
31
+ timeout-minutes: 15
31
32
  runs-on: ubuntu-24.04
32
33
  steps:
33
34
  - uses: actions/checkout@v4
@@ -28,6 +28,7 @@ name: yamllint
28
28
  - master
29
29
  jobs:
30
30
  yamllint:
31
+ timeout-minutes: 15
31
32
  runs-on: ubuntu-24.04
32
33
  steps:
33
34
  - uses: actions/checkout@v4
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ coverage/
6
6
  rdoc/
7
7
  doc/
8
8
  .yardoc/
9
+ node_modules/
data/.gitleaksignore ADDED
@@ -0,0 +1,2 @@
1
+ hardhat/hardhat.config.js:generic-api-key:38
2
+ hardhat/hardhat.config.js:generic-api-key:42
data/.rubocop.yml CHANGED
@@ -30,7 +30,7 @@ AllCops:
30
30
  require:
31
31
  - rubocop-minitest
32
32
  - rubocop-performance
33
-
33
+ - rubocop-rake
34
34
  Minitest/EmptyLineBeforeAssertionMethods:
35
35
  Enabled: false
36
36
  Gemspec/RequiredRubyVersion:
@@ -44,7 +44,7 @@ Layout/MultilineMethodCallIndentation:
44
44
  Metrics/AbcSize:
45
45
  Enabled: false
46
46
  Metrics/BlockLength:
47
- Max: 30
47
+ Max: 100
48
48
  Metrics/CyclomaticComplexity:
49
49
  Max: 25
50
50
  Metrics/PerceivedComplexity:
data/.rultor.yml CHANGED
@@ -29,7 +29,7 @@ release:
29
29
  pre: false
30
30
  script: |-
31
31
  [[ "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit -1
32
- bundle exec rake
32
+ DONCE_SUDO=true bundle exec rake
33
33
  rm -rf *.gem
34
34
  sed -i "s/0\.0\.0/${tag}/g" lib/erc20.rb
35
35
  git add lib/erc20.rb
@@ -39,4 +39,4 @@ release:
39
39
  gem push *.gem --config-file ../rubygems.yml
40
40
  merge:
41
41
  script: |-
42
- bundle exec rake
42
+ DONCE_SUDO=true bundle exec rake
data/Gemfile CHANGED
@@ -23,16 +23,23 @@
23
23
  source 'https://rubygems.org'
24
24
  gemspec
25
25
 
26
+ gem 'backtrace', '>0', require: false
27
+ gem 'donce', '>0', require: false
26
28
  gem 'loog', '>0', require: false
27
29
  gem 'minitest', '5.25.4', require: false
28
30
  gem 'minitest-reporters', '1.7.1', require: false
31
+ gem 'minitest-retry', '0.2.5', require: false
32
+ gem 'qbash', '>0', require: false
29
33
  gem 'rake', '13.2.1', require: false
30
- gem 'rspec-rails', '7.1.0', require: false
31
- gem 'rubocop', '1.71.1', require: false
34
+ gem 'random-port', '>0', require: false
35
+ gem 'rspec-rails', '7.1.1', require: false
36
+ gem 'rubocop', '1.71.2', require: false
32
37
  gem 'rubocop-minitest', '0.36.0', require: false
33
38
  gem 'rubocop-performance', '1.23.1', require: false
39
+ gem 'rubocop-rake', '>0', require: false
34
40
  gem 'rubocop-rspec', '3.4.0', require: false
35
41
  gem 'simplecov', '0.22.0', require: false
36
42
  gem 'simplecov-cobertura', '2.1.0', require: false
37
43
  gem 'threads', '0.4.1', require: false
44
+ gem 'typhoeus', '>0', require: false
38
45
  gem 'yard', '0.9.37', require: false
data/Gemfile.lock CHANGED
@@ -3,7 +3,10 @@ PATH
3
3
  specs:
4
4
  erc20 (0.0.0)
5
5
  backtrace (> 0)
6
- tago (> 0)
6
+ eth (~> 0.4)
7
+ jsonrpc-client (> 0)
8
+ loog (> 0)
9
+ websocket-client-simple (> 0)
7
10
 
8
11
  GEM
9
12
  remote: https://rubygems.org/
@@ -50,8 +53,39 @@ GEM
50
53
  date (3.4.1)
51
54
  diff-lcs (1.5.1)
52
55
  docile (1.4.1)
56
+ donce (0.1.0)
57
+ backtrace (> 0)
58
+ os (> 0)
59
+ qbash (> 0)
53
60
  drb (2.2.1)
61
+ elapsed (0.0.1)
62
+ loog (> 0)
63
+ tago (> 0)
54
64
  erubi (1.13.1)
65
+ eth (0.5.13)
66
+ forwardable (~> 1.3)
67
+ keccak (~> 1.3)
68
+ konstructor (~> 1.0)
69
+ openssl (>= 2.2, < 4.0)
70
+ rbsecp256k1 (~> 6.0)
71
+ scrypt (~> 3.0)
72
+ ethon (0.16.0)
73
+ ffi (>= 1.15.0)
74
+ event_emitter (0.2.6)
75
+ faraday (2.12.2)
76
+ faraday-net_http (>= 2.0, < 3.5)
77
+ json
78
+ logger
79
+ faraday-net_http (3.4.0)
80
+ net-http (>= 0.5.0)
81
+ ffi (1.17.1-arm64-darwin)
82
+ ffi (1.17.1-x64-mingw-ucrt)
83
+ ffi (1.17.1-x86_64-darwin)
84
+ ffi (1.17.1-x86_64-linux-gnu)
85
+ ffi-compiler (1.3.2)
86
+ ffi (>= 1.15.5)
87
+ rake
88
+ forwardable (1.3.3)
55
89
  i18n (1.14.7)
56
90
  concurrent-ruby (~> 1.0)
57
91
  io-console (0.8.0)
@@ -59,19 +93,31 @@ GEM
59
93
  pp (>= 0.6.0)
60
94
  rdoc (>= 4.0.0)
61
95
  reline (>= 0.4.2)
62
- json (2.9.1)
96
+ json (2.10.0)
97
+ jsonrpc-client (0.1.4)
98
+ faraday
99
+ multi_json (>= 1.1.0)
100
+ keccak (1.3.2)
101
+ konstructor (1.0.2)
63
102
  language_server-protocol (3.17.0.4)
64
103
  logger (1.6.5)
65
104
  loofah (2.24.0)
66
105
  crass (~> 1.0.2)
67
106
  nokogiri (>= 1.12.0)
68
107
  loog (0.6.0)
108
+ mini_portile2 (2.8.8)
69
109
  minitest (5.25.4)
70
110
  minitest-reporters (1.7.1)
71
111
  ansi
72
112
  builder
73
113
  minitest (>= 5.0)
74
114
  ruby-progressbar
115
+ minitest-retry (0.2.5)
116
+ minitest (>= 5.0)
117
+ multi_json (1.15.0)
118
+ mutex_m (0.3.0)
119
+ net-http (0.6.0)
120
+ uri
75
121
  nokogiri (1.18.2-arm64-darwin)
76
122
  racc (~> 1.4)
77
123
  nokogiri (1.18.2-x64-mingw-ucrt)
@@ -80,16 +126,24 @@ GEM
80
126
  racc (~> 1.4)
81
127
  nokogiri (1.18.2-x86_64-linux-gnu)
82
128
  racc (~> 1.4)
129
+ openssl (3.3.0)
130
+ os (1.1.4)
83
131
  parallel (1.26.3)
84
- parser (3.3.7.0)
132
+ parser (3.3.7.1)
85
133
  ast (~> 2.4.1)
86
134
  racc
135
+ pkg-config (1.5.9)
87
136
  pp (0.6.2)
88
137
  prettyprint
89
138
  prettyprint (0.2.0)
90
139
  psych (5.2.3)
91
140
  date
92
141
  stringio
142
+ qbash (0.4.0)
143
+ backtrace (> 0)
144
+ elapsed (> 0)
145
+ loog (> 0)
146
+ tago (> 0)
93
147
  racc (1.8.1)
94
148
  rack (3.1.9)
95
149
  rack-session (2.1.0)
@@ -116,13 +170,19 @@ GEM
116
170
  zeitwerk (~> 2.6)
117
171
  rainbow (3.1.1)
118
172
  rake (13.2.1)
119
- rdoc (6.11.0)
173
+ random-port (0.7.5)
174
+ tago (> 0)
175
+ rbsecp256k1 (6.0.0)
176
+ mini_portile2 (~> 2.8)
177
+ pkg-config (~> 1.5)
178
+ rubyzip (~> 2.3)
179
+ rdoc (6.12.0)
120
180
  psych (>= 4.0.0)
121
181
  regexp_parser (2.10.0)
122
182
  reline (0.6.0)
123
183
  io-console (~> 0.5)
124
184
  rexml (3.4.0)
125
- rspec-core (3.13.2)
185
+ rspec-core (3.13.3)
126
186
  rspec-support (~> 3.13.0)
127
187
  rspec-expectations (3.13.3)
128
188
  diff-lcs (>= 1.2.0, < 2.0)
@@ -130,7 +190,7 @@ GEM
130
190
  rspec-mocks (3.13.2)
131
191
  diff-lcs (>= 1.2.0, < 2.0)
132
192
  rspec-support (~> 3.13.0)
133
- rspec-rails (7.1.0)
193
+ rspec-rails (7.1.1)
134
194
  actionpack (>= 7.0)
135
195
  activesupport (>= 7.0)
136
196
  railties (>= 7.0)
@@ -139,7 +199,7 @@ GEM
139
199
  rspec-mocks (~> 3.13)
140
200
  rspec-support (~> 3.13)
141
201
  rspec-support (3.13.2)
142
- rubocop (1.71.1)
202
+ rubocop (1.71.2)
143
203
  json (~> 2.3)
144
204
  language_server-protocol (>= 3.17.0)
145
205
  parallel (~> 1.10)
@@ -157,9 +217,15 @@ GEM
157
217
  rubocop-performance (1.23.1)
158
218
  rubocop (>= 1.48.1, < 2.0)
159
219
  rubocop-ast (>= 1.31.1, < 2.0)
220
+ rubocop-rake (0.6.0)
221
+ rubocop (~> 1.0)
160
222
  rubocop-rspec (3.4.0)
161
223
  rubocop (~> 1.61)
162
224
  ruby-progressbar (1.13.0)
225
+ rubyzip (2.4.1)
226
+ scrypt (3.0.8)
227
+ ffi-compiler (>= 1.0, < 2.0)
228
+ rake (>= 9, < 14)
163
229
  securerandom (0.4.1)
164
230
  simplecov (0.22.0)
165
231
  docile (~> 1.1)
@@ -176,6 +242,8 @@ GEM
176
242
  threads (0.4.1)
177
243
  backtrace (~> 0)
178
244
  concurrent-ruby (~> 1.0)
245
+ typhoeus (1.4.1)
246
+ ethon (>= 0.9.0)
179
247
  tzinfo (2.0.6)
180
248
  concurrent-ruby (~> 1.0)
181
249
  unicode-display_width (3.1.4)
@@ -183,6 +251,12 @@ GEM
183
251
  unicode-emoji (4.0.4)
184
252
  uri (1.0.2)
185
253
  useragent (0.16.11)
254
+ websocket (1.2.11)
255
+ websocket-client-simple (0.9.0)
256
+ base64
257
+ event_emitter
258
+ mutex_m
259
+ websocket
186
260
  yard (0.9.37)
187
261
  zeitwerk (2.7.1)
188
262
 
@@ -196,19 +270,26 @@ PLATFORMS
196
270
  x86_64-linux
197
271
 
198
272
  DEPENDENCIES
273
+ backtrace (> 0)
274
+ donce (> 0)
199
275
  erc20!
200
276
  loog (> 0)
201
277
  minitest (= 5.25.4)
202
278
  minitest-reporters (= 1.7.1)
279
+ minitest-retry (= 0.2.5)
280
+ qbash (> 0)
203
281
  rake (= 13.2.1)
204
- rspec-rails (= 7.1.0)
205
- rubocop (= 1.71.1)
282
+ random-port (> 0)
283
+ rspec-rails (= 7.1.1)
284
+ rubocop (= 1.71.2)
206
285
  rubocop-minitest (= 0.36.0)
207
286
  rubocop-performance (= 1.23.1)
287
+ rubocop-rake (> 0)
208
288
  rubocop-rspec (= 3.4.0)
209
289
  simplecov (= 0.22.0)
210
290
  simplecov-cobertura (= 2.1.0)
211
291
  threads (= 0.4.1)
292
+ typhoeus (> 0)
212
293
  yard (= 0.9.37)
213
294
 
214
295
  BUNDLED WITH
data/README.md CHANGED
@@ -11,27 +11,49 @@
11
11
  [![Hits-of-Code](https://hitsofcode.com/github/yegor256/erc20)](https://hitsofcode.com/view/github/yegor256/erc20)
12
12
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/erc20/blob/master/LICENSE.txt)
13
13
 
14
- This small Ruby library makes Etherium ERC20 manipulations
15
- as simple as they can be, for a cryptocurrency newbie:
14
+ This small Ruby [gem](https://rubygems.org/gems/erc20)
15
+ makes manipulations with [Etherium] [ERC20] tokens
16
+ as simple as they can be, if you have a provider of
17
+ [JSON-RPC] and [WebSocket] Etherium APIs, for example
18
+ [Infura], [GetBlock], or [Alchemy]:
16
19
 
17
20
  ```ruby
18
- # List of private keys:
19
- keys = ['...', '...']
20
-
21
21
  # Create a wallet:
22
- w = ERC20::Wallet.new(keys)
22
+ require 'erc20'
23
+ w = ERC20::Wallet.new(
24
+ contract: ERC20::Wallet.USDT, # hex of it
25
+ rpc: 'https://mainnet.infura.io/v3/<your-key>',
26
+ wss: 'wss://mainnet.infura.io/v3/<your-key>',
27
+ log: $stdout
28
+ )
23
29
 
24
- # Add new key and return its public address:
25
- address = w.create
30
+ # Check balance on the address:
31
+ usdt = w.balance(address)
26
32
 
27
- # Send a few tokens to someone:
28
- w.send(to_address, amount)
33
+ # Send a few tokens to someone and get transaction hash:
34
+ txn = w.pay(private_key, to_address, amount)
29
35
 
30
36
  # Stay waiting, and trigger the block when transactions arrive:
31
- w.accept(&block)
37
+ addresses = ['0x...', '0x...']
38
+ w.accept(addresses) do |event|
39
+ puts event[:amount] # how much
40
+ puts event[:from] # who sent the payment
41
+ puts event[:to] # who was the receiver
42
+ end
43
+ ```
44
+
45
+ To generate a new private key, use [eth](https://rubygems.org/gems/eth):
46
+
47
+ ```ruby
48
+ require 'eth'
49
+ key = Eth::Key.new.private_hex
32
50
  ```
33
51
 
34
- That's it.
52
+ To get address from private one:
53
+
54
+ ```ruby
55
+ public_hex = Eth::Key.new(priv: key).address
56
+ ```
35
57
 
36
58
  ## How to contribute
37
59
 
@@ -48,3 +70,11 @@ bundle exec rake
48
70
  ```
49
71
 
50
72
  If it's clean and you don't see any error messages, submit your pull request.
73
+
74
+ [Etherium]: https://en.wikipedia.org/wiki/Ethereum
75
+ [ERC20]: https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
76
+ [JSON-RPC]: https://ethereum.org/en/developers/docs/apis/json-rpc/
77
+ [Websocket]: https://ethereum.org/en/developers/tutorials/using-websockets/
78
+ [Infura]: https://infura.io/
79
+ [Alchemy]: https://alchemy.com/
80
+ [GetBlock]: https://getblock.io/
data/Rakefile CHANGED
@@ -32,7 +32,7 @@ def version
32
32
  Gem::Specification.load(Dir['*.gemspec'].first).version
33
33
  end
34
34
 
35
- task default: %i[clean test rubocop yard copyright]
35
+ task default: %i[clean test rubocop yard]
36
36
 
37
37
  require 'rake/testtask'
38
38
  desc 'Run all unit tests'
@@ -61,11 +61,3 @@ desc 'Run benchmark script'
61
61
  task :benchmark do
62
62
  ruby 'benchmarks/simple.rb'
63
63
  end
64
-
65
- task :copyright do
66
- sh "grep -q -r '#{Date.today.strftime('%Y')}' \
67
- --include '*.rb' \
68
- --include '*.txt' \
69
- --include 'Rakefile' \
70
- ."
71
- end
data/erc20.gemspec CHANGED
@@ -40,6 +40,9 @@ Gem::Specification.new do |s|
40
40
  s.rdoc_options = ['--charset=UTF-8']
41
41
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
42
42
  s.add_dependency 'backtrace', '>0'
43
- s.add_dependency 'tago', '>0'
43
+ s.add_dependency 'eth', '~>0.4'
44
+ s.add_dependency 'jsonrpc-client', '>0'
45
+ s.add_dependency 'loog', '>0'
46
+ s.add_dependency 'websocket-client-simple', '>0'
44
47
  s.metadata['rubygems_mfa_required'] = 'true'
45
48
  end
@@ -0,0 +1,5 @@
1
+ artifacts/
2
+ cache/
3
+ node_modules/
4
+ ignition/deployments/
5
+ package-lock.json
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2025 Yegor Bugayenko
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the 'Software'), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in all
11
+ # copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+ FROM node:22
22
+
23
+ ENV HOME=/hh
24
+ WORKDIR /app
25
+ EXPOSE 8545
26
+
27
+ COPY package.json .
28
+ RUN npm install
29
+
30
+ # These two must be provided via '--build-arg':
31
+ ARG PORT=8080
32
+ ARG HOST=localhost
33
+ COPY hardhat.config.js .
34
+ RUN sed -i "s/PORT/$PORT/g" hardhat.config.js
35
+ RUN sed -i "s/HOST/$HOST/g" hardhat.config.js
36
+
37
+ COPY contracts contracts
38
+ COPY ignition ignition
39
+
40
+ RUN rm -rf ignition/deployments
41
+
42
+ RUN npx hardhat compile
43
+
44
+ RUN rm -rf cache
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ contract Foo {
5
+ string public name = "Foo";
6
+ string public symbol = "FOO";
7
+ uint8 public decimals = 18;
8
+
9
+ // Tracks the total token supply
10
+ uint256 public totalSupply;
11
+
12
+ // Owner of the token contract (for minting, etc., if desired)
13
+ address public owner;
14
+
15
+ // Balances for each account
16
+ mapping(address => uint256) private _balances;
17
+
18
+ // Allowances for each account, where allowances[from][spender] = amount
19
+ mapping(address => mapping(address => uint256)) private _allowances;
20
+
21
+ // Events
22
+ event Transfer(address indexed from, address indexed to, uint256 value);
23
+ event Approval(address indexed owner, address indexed spender, uint256 value);
24
+
25
+ // Constructor: Mint an initial supply to the deployer
26
+ constructor() {
27
+ owner = msg.sender;
28
+ _mint(owner, 100000);
29
+ _mint(0xd5fF1bFCDE7A03Da61ad229d962c74F1eA2f16A5, 123000000000); // Jeff
30
+ _mint(0xd7a63Ac9DD3d7878bc042A4bADA214EE4aff8c85, 456000000000); // Walter
31
+ }
32
+
33
+ function balanceOf(address account) external view returns (uint256) {
34
+ return _balances[account];
35
+ }
36
+
37
+ function transfer(address recipient, uint256 amount) external returns (bool) {
38
+ _transfer(msg.sender, recipient, amount);
39
+ return true;
40
+ }
41
+
42
+ function allowance(address _owner, address _spender) external view returns (uint256) {
43
+ return _allowances[_owner][_spender];
44
+ }
45
+
46
+ function approve(address spender, uint256 amount) external returns (bool) {
47
+ _approve(msg.sender, spender, amount);
48
+ return true;
49
+ }
50
+
51
+ function transferFrom(
52
+ address sender,
53
+ address recipient,
54
+ uint256 amount
55
+ ) external returns (bool) {
56
+ uint256 currentAllowance = _allowances[sender][msg.sender];
57
+ require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
58
+ _approve(sender, msg.sender, currentAllowance - amount);
59
+ _transfer(sender, recipient, amount);
60
+
61
+ return true;
62
+ }
63
+
64
+ function mint(address account, uint256 amount) public {
65
+ require(msg.sender == owner, "ERC20: only owner can mint");
66
+ _mint(account, amount);
67
+ }
68
+
69
+ function _transfer(address sender, address recipient, uint256 amount) internal {
70
+ require(sender != address(0), "ERC20: transfer from the zero address");
71
+ require(recipient != address(0), "ERC20: transfer to the zero address");
72
+ require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance");
73
+ _balances[sender] -= amount;
74
+ _balances[recipient] += amount;
75
+ emit Transfer(sender, recipient, amount);
76
+ }
77
+
78
+ function _approve(
79
+ address tokenOwner,
80
+ address spender,
81
+ uint256 amount
82
+ ) internal {
83
+ require(tokenOwner != address(0), "ERC20: approve from the zero address");
84
+ require(spender != address(0), "ERC20: approve to the zero address");
85
+ _allowances[tokenOwner][spender] = amount;
86
+ emit Approval(tokenOwner, spender, amount);
87
+ }
88
+
89
+ function _mint(address account, uint256 amount) internal {
90
+ require(account != address(0), "ERC20: mint to the zero address");
91
+ totalSupply += amount;
92
+ _balances[account] += amount;
93
+ emit Transfer(address(0), account, amount);
94
+ }
95
+ }
@@ -0,0 +1,52 @@
1
+ // Copyright (c) 2025 Yegor Bugayenko
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+
21
+ require("@nomicfoundation/hardhat-toolbox");
22
+
23
+ /** @type import("hardhat/config").HardhatUserConfig */
24
+
25
+ module.exports = {
26
+ solidity: "0.8.28",
27
+ defaultNetwork: "foo",
28
+ networks: {
29
+ hardhat: {
30
+ chainId: 4242,
31
+ gas: 10,
32
+ gasPrice: 4,
33
+ maxFeePerGas: 100,
34
+ maxPriorityFeePerGas: 100,
35
+ initialBaseFeePerGas: 100,
36
+ accounts: [
37
+ {
38
+ privateKey: "81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a",
39
+ balance: "55555555555555555555555"
40
+ },
41
+ {
42
+ privateKey: "91f9111b1744d55361e632771a4e53839e9442a9fef45febc0a5c838c686a15b",
43
+ balance: "66666666666666666666666"
44
+ }
45
+ ]
46
+ },
47
+ foo: {
48
+ chainId: 4242,
49
+ url: "http://HOST:PORT"
50
+ }
51
+ }
52
+ };
@@ -0,0 +1,28 @@
1
+ // Copyright (c) 2025 Yegor Bugayenko
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the 'Software'), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+
21
+ import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
22
+
23
+ const FooModule = buildModule("FooModule", (m) => {
24
+ const foo = m.contract("Foo");
25
+ return { foo };
26
+ });
27
+
28
+ export default FooModule;
@@ -0,0 +1,7 @@
1
+ {
2
+ "devDependencies": {
3
+ "@nomicfoundation/hardhat-ignition-ethers": "^0.15.9",
4
+ "@nomicfoundation/hardhat-toolbox": "^5.0.0",
5
+ "hardhat": "^2.22.18"
6
+ }
7
+ }
data/lib/erc20/wallet.rb CHANGED
@@ -20,6 +20,10 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
+ require 'eth'
24
+ require 'jsonrpc/client'
25
+ require 'loog'
26
+ require 'websocket-client-simple'
23
27
  require_relative '../erc20'
24
28
 
25
29
  # A wallet.
@@ -30,18 +34,147 @@ require_relative '../erc20'
30
34
  # Copyright:: Copyright (c) 2025 Yegor Bugayenko
31
35
  # License:: MIT
32
36
  class ERC20::Wallet
37
+ # Address of USDT contract.
38
+ USDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'
39
+
33
40
  # Constructor.
34
- # @param [Array<String>] keys Private keys
35
- # @param [String] host The host of the server
41
+ # @param [String] contract Hex of the contract in Etherium
42
+ # @param [String] rpc The URL of Etherium JSON-RPC provider
43
+ # @param [Integer] chain The ID of the chain (1 for mainnet)
36
44
  # @param [Object] log The destination for logs
37
- def initialize(keys, host: '', log: $stdout)
38
- @keys = keys
39
- @host = host
45
+ def initialize(contract: USDT, rpc: nil, wss: nil, chain: 1, log: $stdout)
46
+ @contract = contract
47
+ @rpc = rpc
48
+ @wss = wss
40
49
  @log = log
50
+ @chain = chain
51
+ end
52
+
53
+ # Get balance of a public address.
54
+ #
55
+ # @param [String] hex Public key, in hex, starting from '0x'
56
+ # @return [Integer] Balance, in
57
+ def balance(hex)
58
+ func = '70a08231' # balanceOf
59
+ padded = "000000000000000000000000#{hex[2..].downcase}"
60
+ data = "0x#{func}#{padded}"
61
+ r = jsonrpc.eth_call({ to: @contract, data: data }, 'latest')
62
+ b = r[2..].to_i(16)
63
+ @log.debug("Balance of #{hex} is #{b}")
64
+ b
65
+ end
66
+
67
+ # Send a single payment from a private address to a public one.
68
+ #
69
+ # @param [String] priv Private key, in hex
70
+ # @param [String] address Public key, in hex
71
+ # @param [Integer] amount The amount of ERC20 tokens to send
72
+ # @param [Integer] gas_limit How much gas you are ready to spend
73
+ # @param [Integer] gas_price How much gas you pay per computation unit
74
+ # @return [String] Transaction hash
75
+ def pay(priv, address, amount, gas_limit: nil, gas_price: nil)
76
+ func = 'a9059cbb' # transfer(address,uint256)
77
+ to_clean = address.downcase.sub(/^0x/, '')
78
+ to_padded = ('0' * (64 - to_clean.size)) + to_clean
79
+ amt_hex = amount.to_s(16)
80
+ amt_padded = ('0' * (64 - amt_hex.size)) + amt_hex
81
+ data = "0x#{func}#{to_padded}#{amt_padded}"
82
+ key = Eth::Key.new(priv: priv)
83
+ from = key.address
84
+ nonce = jsonrpc.eth_getTransactionCount(from, 'pending').to_i(16)
85
+ tx = Eth::Tx.new(
86
+ {
87
+ nonce:,
88
+ gas_price: gas_price || gas_best_price,
89
+ gas_limit: gas_limit || gas_estimate(from, data),
90
+ to: @contract,
91
+ value: 0,
92
+ data: data,
93
+ chain_id: @chain
94
+ }
95
+ )
96
+ tx.sign(key)
97
+ hex = "0x#{tx.hex}"
98
+ jsonrpc.eth_sendRawTransaction(hex)
99
+ @log.debug("Sent #{amount} from #{from} to #{address}: #{hex}")
100
+ hex
101
+ end
102
+
103
+ # Wait for incoming transactions and let the block know when they
104
+ # arrive. It's a blocking call, it's better to run it in a separate
105
+ # thread.
106
+ #
107
+ # @param [Array<String>] addresses Addresses to monitor
108
+ # @param [Array] ready When connected, TRUE will be added to this array
109
+ # @param [Boolean] raw TRUE if you need to get JSON events as they arrive from Websockets
110
+ def accept(addresses, connected: [], raw: false)
111
+ WebSocket::Client::Simple.connect(@wss) do |ws|
112
+ log = @log
113
+ contract = @contract
114
+ wss = @wss
115
+ ws.on(:open) do
116
+ log.debug("Connected to #{wss}")
117
+ msg = {
118
+ jsonrpc: '2.0',
119
+ id: 1,
120
+ method: 'eth_subscribe',
121
+ params: [
122
+ 'logs',
123
+ {
124
+ address: contract,
125
+ topics: [
126
+ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
127
+ nil,
128
+ addresses.map { |a| "0x000000000000000000000000#{a[2..]}" }
129
+ ]
130
+ }
131
+ ]
132
+ }
133
+ ws.send(msg.to_json)
134
+ connected.append(1)
135
+ log.debug("Subscribed to #{addresses.count} addresses")
136
+ end
137
+ ws.on(:message) do |msg|
138
+ data =
139
+ begin
140
+ JSON.parse(msg.data)
141
+ rescue StandardError
142
+ {}
143
+ end
144
+ if data['method'] == 'eth_subscription' && data.dig('params', 'result')
145
+ event = data['params']['result']
146
+ unless raw
147
+ event = {
148
+ amount: event['data'].to_i(16),
149
+ from: "0x#{event['topics'][1][26..].downcase}",
150
+ to: "0x#{event['topics'][2][26..].downcase}"
151
+ }
152
+ end
153
+ log.debug("New event arrived from #{event['address']}")
154
+ yield event
155
+ end
156
+ end
157
+ ws.on(:close) do |_e|
158
+ log.debug("Disconnected from #{wss}")
159
+ end
160
+ ws.on(:error) do |e|
161
+ log.debug("Error at #{wss}: #{e}")
162
+ end
163
+ end
164
+ end
165
+
166
+ private
167
+
168
+ def jsonrpc
169
+ JSONRPC.logger = Loog::NULL
170
+ JSONRPC::Client.new(@rpc)
171
+ end
172
+
173
+ def gas_estimate(from, data)
174
+ jsonrpc.eth_estimateGas({ from:, to: @contract, data: }, 'latest').to_i(16)
41
175
  end
42
176
 
43
- # Create new key and return its public address.
44
- def create
45
- 'none'
177
+ def gas_best_price
178
+ jsonrpc.eth_getBlockByNumber('latest', false)['baseFeePerGas'].to_i(16)
46
179
  end
47
180
  end
data/lib/erc20.rb CHANGED
@@ -27,5 +27,5 @@
27
27
  # License:: MIT
28
28
  module ERC20
29
29
  # Current version of the gem (changed by .rultor.yml on every release)
30
- VERSION = '0.0.1'
30
+ VERSION = '0.0.3'
31
31
  end
@@ -20,8 +20,14 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- require 'minitest/autorun'
23
+ require 'backtrace'
24
+ require 'donce'
25
+ require 'eth'
24
26
  require 'loog'
27
+ require 'random-port'
28
+ require 'shellwords'
29
+ require 'typhoeus'
30
+ require 'minitest/autorun'
25
31
  require_relative '../../lib/erc20'
26
32
  require_relative '../../lib/erc20/wallet'
27
33
 
@@ -30,9 +36,160 @@ require_relative '../../lib/erc20/wallet'
30
36
  # Copyright:: Copyright (c) 2025 Yegor Bugayenko
31
37
  # License:: MIT
32
38
  class TestWallet < Minitest::Test
33
- def test_simple
34
- keys = []
35
- w = ERC20::Wallet.new(keys, log: Loog::VERBOSE)
36
- refute_nil(w.create)
39
+ # At this address, in the mainnet, there are a few USDT tokens. I won't
40
+ # move them anyway, that's why tests can use this address forever.
41
+ STABLE_ADDRESS = '0xEB2fE8872A6f1eDb70a2632EA1f869AB131532f6'
42
+
43
+ # One guy private hex.
44
+ JEFF = '81a9b2114d53731ecc84b261ef6c0387dde34d5907fe7b441240cc21d61bf80a'
45
+
46
+ # Another guy private hex.
47
+ WALTER = '91f9111b1744d55361e632771a4e53839e9442a9fef45febc0a5c838c686a15b'
48
+
49
+ def test_checks_balance_on_mainnet
50
+ b = mainnet.balance(STABLE_ADDRESS)
51
+ refute_nil(b)
52
+ assert_equal(27_258_889, b)
53
+ end
54
+
55
+ def test_checks_balance_of_absent_address
56
+ a = '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
57
+ b = mainnet.balance(a)
58
+ refute_nil(b)
59
+ assert_equal(0, b)
60
+ end
61
+
62
+ def test_fails_with_invalid_infura_key
63
+ w = ERC20::Wallet.new(
64
+ rpc: 'https://mainnet.infura.io/v3/invalid-key-here',
65
+ log: Loog::NULL
66
+ )
67
+ assert_raises(StandardError) { w.balance(STABLE_ADDRESS) }
68
+ end
69
+
70
+ def test_checks_balance_on_testnet
71
+ skip('does not work')
72
+ b = testnet.balance(STABLE_ADDRESS)
73
+ refute_nil(b)
74
+ assert_predicate(b, :positive?)
75
+ end
76
+
77
+ def test_checks_balance_on_hardhat
78
+ on_hardhat do |wallet|
79
+ b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
80
+ assert_equal(123_000_100_000, b)
81
+ end
82
+ end
83
+
84
+ def test_pays_on_hardhat
85
+ on_hardhat do |wallet|
86
+ to = Eth::Key.new(priv: WALTER).address.to_s
87
+ before = wallet.balance(to)
88
+ sum = 42_000
89
+ from = Eth::Key.new(priv: JEFF).address.to_s
90
+ assert_operator(wallet.balance(from), :>, sum * 2)
91
+ wallet.pay(JEFF, to, sum)
92
+ assert_equal(before + sum, wallet.balance(to))
93
+ end
94
+ end
95
+
96
+ def test_accepts_payments_on_hardhat
97
+ walter = Eth::Key.new(priv: WALTER).address.to_s.downcase
98
+ jeff = Eth::Key.new(priv: JEFF).address.to_s.downcase
99
+ on_hardhat do |wallet|
100
+ connected = []
101
+ event = nil
102
+ daemon =
103
+ Thread.new do
104
+ wallet.accept([walter, jeff], connected:) do |e|
105
+ event = e
106
+ end
107
+ rescue StandardError => e
108
+ puts Backtrace.new(e)
109
+ end
110
+ wait_for { !connected.empty? }
111
+ sum = 77_000
112
+ wallet.pay(JEFF, walter, sum)
113
+ wait_for { !event.nil? }
114
+ daemon.kill
115
+ daemon.join(30)
116
+ assert_equal(sum, event[:amount])
117
+ assert_equal(jeff, event[:from])
118
+ assert_equal(walter, event[:to])
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def wait_for
125
+ start = Time.now
126
+ loop do
127
+ sleep(0.1)
128
+ break if yield
129
+ raise 'timeout' if Time.now - start > 60
130
+ rescue Errno::ECONNREFUSED
131
+ retry
132
+ end
133
+ end
134
+
135
+ def wait_for_port(port)
136
+ wait_for { Typhoeus::Request.get("http://localhost:#{port}").code == 200 }
137
+ end
138
+
139
+ def env(var)
140
+ key = ENV.fetch(var, nil)
141
+ skip("The #{var} environment variable is not set") if key.nil?
142
+ key
143
+ end
144
+
145
+ def mainnet
146
+ [
147
+ "https://mainnet.infura.io/v3/#{env('INFURA_KEY')}",
148
+ "https://go.getblock.io/#{env('GETBLOCK_KEY')}"
149
+ ].map do |rpc|
150
+ ERC20::Wallet.new(rpc:, log: Loog::NULL)
151
+ end.sample
152
+ end
153
+
154
+ def testnet
155
+ [
156
+ "https://sepolia.infura.io/v3/#{env('INFURA_KEY')}",
157
+ "https://go.getblock.io/#{env('GETBLOCK_SEPOILA_KEY')}"
158
+ ].map do |rpc|
159
+ ERC20::Wallet.new(rpc:, log: Loog::NULL)
160
+ end.sample
161
+ end
162
+
163
+ def on_hardhat
164
+ RandomPort::Pool::SINGLETON.acquire do |port|
165
+ donce(
166
+ home: File.join(__dir__, '../../hardhat'),
167
+ ports: { port => 8545 },
168
+ command: 'npx hardhat node',
169
+ log: Loog::NULL
170
+ ) do
171
+ wait_for_port(port)
172
+ cmd = [
173
+ '(cat hardhat.config.js)',
174
+ '(ls -al)',
175
+ '(echo y | npx hardhat ignition deploy ./ignition/modules/Foo.ts --network foo --deployment-id foo)',
176
+ '(npx hardhat ignition status foo | tail -1 | cut -d" " -f3)'
177
+ ].join(' && ')
178
+ contract = donce(
179
+ home: File.join(__dir__, '../../hardhat'),
180
+ command: "/bin/bash -c #{Shellwords.escape(cmd)}",
181
+ build_args: { 'HOST' => donce_host, 'PORT' => port },
182
+ log: Loog::NULL,
183
+ root: true
184
+ ).split("\n").last
185
+ wallet = ERC20::Wallet.new(
186
+ contract:, chain: 4242,
187
+ rpc: "http://localhost:#{port}",
188
+ wss: "ws://localhost:#{port}",
189
+ log: Loog::NULL
190
+ )
191
+ yield wallet
192
+ end
193
+ end
37
194
  end
38
195
  end
data/test/test__helper.rb CHANGED
@@ -33,3 +33,7 @@ require 'minitest/autorun'
33
33
 
34
34
  require 'minitest/reporters'
35
35
  Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
36
+
37
+ # To make tests retry on failure:
38
+ require 'minitest/retry'
39
+ Minitest::Retry.use!(methods_to_skip: [])
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.1
4
+ version: 0.0.3
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-01-31 00:00:00.000000000 Z
11
+ date: 2025-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -25,7 +25,49 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: tago
28
+ name: eth
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: jsonrpc-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: loog
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: websocket-client-simple
29
71
  requirement: !ruby/object:Gem::Requirement
30
72
  requirements:
31
73
  - - ">"
@@ -55,9 +97,11 @@ files:
55
97
  - ".github/workflows/markdown-lint.yml"
56
98
  - ".github/workflows/pdd.yml"
57
99
  - ".github/workflows/rake.yml"
100
+ - ".github/workflows/shellcheck.yml"
58
101
  - ".github/workflows/xcop.yml"
59
102
  - ".github/workflows/yamllint.yml"
60
103
  - ".gitignore"
104
+ - ".gitleaksignore"
61
105
  - ".pdd"
62
106
  - ".rubocop.yml"
63
107
  - ".rultor.yml"
@@ -69,6 +113,12 @@ files:
69
113
  - README.md
70
114
  - Rakefile
71
115
  - erc20.gemspec
116
+ - hardhat/.gitignore
117
+ - hardhat/Dockerfile
118
+ - hardhat/contracts/Foo.sol
119
+ - hardhat/hardhat.config.js
120
+ - hardhat/ignition/modules/Foo.ts
121
+ - hardhat/package.json
72
122
  - lib/erc20.rb
73
123
  - lib/erc20/wallet.rb
74
124
  - renovate.json