erc20 0.0.1 → 0.0.2

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: bf1f0dbee9d090079bd263a79eeafc0093866fc5503d7a1130b26e7b77ca8630
4
+ data.tar.gz: 105f04b7df7e9b8f45a3c34acb739fb6b74238e9f7180b624ceb724f8a4c1570
5
5
  SHA512:
6
- metadata.gz: 935689bf9f9e0bc69a8d5415bf30d5f24f0c64c3057a5e5496229f73c1a4a0f65c51f2d243959f6cf0be39d44e35f19eaa4d0df1e62320d1861b9d28196de33c
7
- data.tar.gz: 875542239945570f71fa515d478d70ba705b5c85a66d1109b1e138e71b318cdba01044c6a51a791912b2551c0f5d5cc02ff7022e7f1e560dfa45faa86d875c03
6
+ metadata.gz: e86283d48937fca5b956c0963b19c6b25b25226d6a60bd737e37d6201cc5dc82879d33ab79ff3ef919728762da601aa9d1bc56aaa85b94532f7edcafd29f04eb
7
+ data.tar.gz: f11fd8b7d8b017c4fbdc70b4442aa054344fee9451fd029e7826da8fe12961ac60447522945e83b941ab7ccf28a67299a9077c47b114f8736336d42c6b16088f
@@ -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: 50
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,11 @@ PATH
3
3
  specs:
4
4
  erc20 (0.0.0)
5
5
  backtrace (> 0)
6
+ eth (~> 0.4)
7
+ jsonrpc-client (> 0)
8
+ loog (> 0)
6
9
  tago (> 0)
10
+ websocket-client-simple (> 0)
7
11
 
8
12
  GEM
9
13
  remote: https://rubygems.org/
@@ -50,8 +54,39 @@ GEM
50
54
  date (3.4.1)
51
55
  diff-lcs (1.5.1)
52
56
  docile (1.4.1)
57
+ donce (0.1.0)
58
+ backtrace (> 0)
59
+ os (> 0)
60
+ qbash (> 0)
53
61
  drb (2.2.1)
62
+ elapsed (0.0.1)
63
+ loog (> 0)
64
+ tago (> 0)
54
65
  erubi (1.13.1)
66
+ eth (0.5.13)
67
+ forwardable (~> 1.3)
68
+ keccak (~> 1.3)
69
+ konstructor (~> 1.0)
70
+ openssl (>= 2.2, < 4.0)
71
+ rbsecp256k1 (~> 6.0)
72
+ scrypt (~> 3.0)
73
+ ethon (0.16.0)
74
+ ffi (>= 1.15.0)
75
+ event_emitter (0.2.6)
76
+ faraday (2.12.2)
77
+ faraday-net_http (>= 2.0, < 3.5)
78
+ json
79
+ logger
80
+ faraday-net_http (3.4.0)
81
+ net-http (>= 0.5.0)
82
+ ffi (1.17.1-arm64-darwin)
83
+ ffi (1.17.1-x64-mingw-ucrt)
84
+ ffi (1.17.1-x86_64-darwin)
85
+ ffi (1.17.1-x86_64-linux-gnu)
86
+ ffi-compiler (1.3.2)
87
+ ffi (>= 1.15.5)
88
+ rake
89
+ forwardable (1.3.3)
55
90
  i18n (1.14.7)
56
91
  concurrent-ruby (~> 1.0)
57
92
  io-console (0.8.0)
@@ -59,19 +94,31 @@ GEM
59
94
  pp (>= 0.6.0)
60
95
  rdoc (>= 4.0.0)
61
96
  reline (>= 0.4.2)
62
- json (2.9.1)
97
+ json (2.10.0)
98
+ jsonrpc-client (0.1.4)
99
+ faraday
100
+ multi_json (>= 1.1.0)
101
+ keccak (1.3.2)
102
+ konstructor (1.0.2)
63
103
  language_server-protocol (3.17.0.4)
64
104
  logger (1.6.5)
65
105
  loofah (2.24.0)
66
106
  crass (~> 1.0.2)
67
107
  nokogiri (>= 1.12.0)
68
108
  loog (0.6.0)
109
+ mini_portile2 (2.8.8)
69
110
  minitest (5.25.4)
70
111
  minitest-reporters (1.7.1)
71
112
  ansi
72
113
  builder
73
114
  minitest (>= 5.0)
74
115
  ruby-progressbar
116
+ minitest-retry (0.2.5)
117
+ minitest (>= 5.0)
118
+ multi_json (1.15.0)
119
+ mutex_m (0.3.0)
120
+ net-http (0.6.0)
121
+ uri
75
122
  nokogiri (1.18.2-arm64-darwin)
76
123
  racc (~> 1.4)
77
124
  nokogiri (1.18.2-x64-mingw-ucrt)
@@ -80,16 +127,24 @@ GEM
80
127
  racc (~> 1.4)
81
128
  nokogiri (1.18.2-x86_64-linux-gnu)
82
129
  racc (~> 1.4)
130
+ openssl (3.3.0)
131
+ os (1.1.4)
83
132
  parallel (1.26.3)
84
- parser (3.3.7.0)
133
+ parser (3.3.7.1)
85
134
  ast (~> 2.4.1)
86
135
  racc
136
+ pkg-config (1.5.9)
87
137
  pp (0.6.2)
88
138
  prettyprint
89
139
  prettyprint (0.2.0)
90
140
  psych (5.2.3)
91
141
  date
92
142
  stringio
143
+ qbash (0.4.0)
144
+ backtrace (> 0)
145
+ elapsed (> 0)
146
+ loog (> 0)
147
+ tago (> 0)
93
148
  racc (1.8.1)
94
149
  rack (3.1.9)
95
150
  rack-session (2.1.0)
@@ -116,13 +171,19 @@ GEM
116
171
  zeitwerk (~> 2.6)
117
172
  rainbow (3.1.1)
118
173
  rake (13.2.1)
119
- rdoc (6.11.0)
174
+ random-port (0.7.5)
175
+ tago (> 0)
176
+ rbsecp256k1 (6.0.0)
177
+ mini_portile2 (~> 2.8)
178
+ pkg-config (~> 1.5)
179
+ rubyzip (~> 2.3)
180
+ rdoc (6.12.0)
120
181
  psych (>= 4.0.0)
121
182
  regexp_parser (2.10.0)
122
183
  reline (0.6.0)
123
184
  io-console (~> 0.5)
124
185
  rexml (3.4.0)
125
- rspec-core (3.13.2)
186
+ rspec-core (3.13.3)
126
187
  rspec-support (~> 3.13.0)
127
188
  rspec-expectations (3.13.3)
128
189
  diff-lcs (>= 1.2.0, < 2.0)
@@ -130,7 +191,7 @@ GEM
130
191
  rspec-mocks (3.13.2)
131
192
  diff-lcs (>= 1.2.0, < 2.0)
132
193
  rspec-support (~> 3.13.0)
133
- rspec-rails (7.1.0)
194
+ rspec-rails (7.1.1)
134
195
  actionpack (>= 7.0)
135
196
  activesupport (>= 7.0)
136
197
  railties (>= 7.0)
@@ -139,7 +200,7 @@ GEM
139
200
  rspec-mocks (~> 3.13)
140
201
  rspec-support (~> 3.13)
141
202
  rspec-support (3.13.2)
142
- rubocop (1.71.1)
203
+ rubocop (1.71.2)
143
204
  json (~> 2.3)
144
205
  language_server-protocol (>= 3.17.0)
145
206
  parallel (~> 1.10)
@@ -157,9 +218,15 @@ GEM
157
218
  rubocop-performance (1.23.1)
158
219
  rubocop (>= 1.48.1, < 2.0)
159
220
  rubocop-ast (>= 1.31.1, < 2.0)
221
+ rubocop-rake (0.6.0)
222
+ rubocop (~> 1.0)
160
223
  rubocop-rspec (3.4.0)
161
224
  rubocop (~> 1.61)
162
225
  ruby-progressbar (1.13.0)
226
+ rubyzip (2.4.1)
227
+ scrypt (3.0.8)
228
+ ffi-compiler (>= 1.0, < 2.0)
229
+ rake (>= 9, < 14)
163
230
  securerandom (0.4.1)
164
231
  simplecov (0.22.0)
165
232
  docile (~> 1.1)
@@ -176,6 +243,8 @@ GEM
176
243
  threads (0.4.1)
177
244
  backtrace (~> 0)
178
245
  concurrent-ruby (~> 1.0)
246
+ typhoeus (1.4.1)
247
+ ethon (>= 0.9.0)
179
248
  tzinfo (2.0.6)
180
249
  concurrent-ruby (~> 1.0)
181
250
  unicode-display_width (3.1.4)
@@ -183,6 +252,12 @@ GEM
183
252
  unicode-emoji (4.0.4)
184
253
  uri (1.0.2)
185
254
  useragent (0.16.11)
255
+ websocket (1.2.11)
256
+ websocket-client-simple (0.9.0)
257
+ base64
258
+ event_emitter
259
+ mutex_m
260
+ websocket
186
261
  yard (0.9.37)
187
262
  zeitwerk (2.7.1)
188
263
 
@@ -196,19 +271,26 @@ PLATFORMS
196
271
  x86_64-linux
197
272
 
198
273
  DEPENDENCIES
274
+ backtrace (> 0)
275
+ donce (> 0)
199
276
  erc20!
200
277
  loog (> 0)
201
278
  minitest (= 5.25.4)
202
279
  minitest-reporters (= 1.7.1)
280
+ minitest-retry (= 0.2.5)
281
+ qbash (> 0)
203
282
  rake (= 13.2.1)
204
- rspec-rails (= 7.1.0)
205
- rubocop (= 1.71.1)
283
+ random-port (> 0)
284
+ rspec-rails (= 7.1.1)
285
+ rubocop (= 1.71.2)
206
286
  rubocop-minitest (= 0.36.0)
207
287
  rubocop-performance (= 1.23.1)
288
+ rubocop-rake (> 0)
208
289
  rubocop-rspec (= 3.4.0)
209
290
  simplecov (= 0.22.0)
210
291
  simplecov-cobertura (= 2.1.0)
211
292
  threads (= 0.4.1)
293
+ typhoeus (> 0)
212
294
  yard (= 0.9.37)
213
295
 
214
296
  BUNDLED WITH
data/README.md CHANGED
@@ -11,27 +11,47 @@
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 library makes manipulations with [Etherium] [ERC20] tokens
15
+ as simple as they can be, if you have a provider of
16
+ [JSON-RPC] and [WebSocket] Etherium APIs, for example
17
+ [Infura], [GetBlock], or [Alchemy]:
16
18
 
17
19
  ```ruby
18
- # List of private keys:
19
- keys = ['...', '...']
20
-
21
20
  # Create a wallet:
22
- w = ERC20::Wallet.new(keys)
21
+ w = ERC20::Wallet.new(
22
+ contract: ERC20::Wallet.USDT, # hex of it
23
+ rpc: 'https://mainnet.infura.io/v3/<your-key>',
24
+ wss: 'wss://mainnet.infura.io/v3/<your-key>',
25
+ log: $stdout
26
+ )
23
27
 
24
- # Add new key and return its public address:
25
- address = w.create
28
+ # Check balance on the address:
29
+ usdt = w.balance(address)
26
30
 
27
- # Send a few tokens to someone:
28
- w.send(to_address, amount)
31
+ # Send a few tokens to someone and get transaction hash:
32
+ txn = w.pay(private_key, to_address, amount)
29
33
 
30
34
  # Stay waiting, and trigger the block when transactions arrive:
31
- w.accept(&block)
35
+ addresses = ['0x...', '0x...']
36
+ w.accept(addresses) do |event|
37
+ puts event['data'].to_i(16)) # how much
38
+ puts "0x#{event['topics'][1][26..]}" # who sent the payment
39
+ puts "0x#{event['topics'][2][26..]}" # who was the receiver
40
+ end
41
+ ```
42
+
43
+ To generate a new private key, use [eth](https://rubygems.org/gems/eth):
44
+
45
+ ```ruby
46
+ require 'eth'
47
+ key = Eth::Key.new.private_hex
32
48
  ```
33
49
 
34
- That's it.
50
+ To get address from private one:
51
+
52
+ ```ruby
53
+ public_hex = Eth::Key.new(priv: key).address
54
+ ```
35
55
 
36
56
  ## How to contribute
37
57
 
@@ -48,3 +68,11 @@ bundle exec rake
48
68
  ```
49
69
 
50
70
  If it's clean and you don't see any error messages, submit your pull request.
71
+
72
+ [Etherium]: https://en.wikipedia.org/wiki/Ethereum
73
+ [ERC20]: https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
74
+ [JSON-RPC]: https://ethereum.org/en/developers/docs/apis/json-rpc/
75
+ [Websocket]: https://ethereum.org/en/developers/tutorials/using-websockets/
76
+ [Infura]: https://infura.io/
77
+ [Alchemy]: https://alchemy.com/
78
+ [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,10 @@ 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 'eth', '~>0.4'
44
+ s.add_dependency 'jsonrpc-client', '>0'
45
+ s.add_dependency 'loog', '>0'
43
46
  s.add_dependency 'tago', '>0'
47
+ s.add_dependency 'websocket-client-simple', '>0'
44
48
  s.metadata['rubygems_mfa_required'] = 'true'
45
49
  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,133 @@ 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
+ r[2..].to_i(16)
63
+ end
64
+
65
+ # Send a single payment from a private address to a public one.
66
+ #
67
+ # @param [String] priv Private key, in hex
68
+ # @param [String] address Public key, in hex
69
+ # @param [Integer] amount The amount of ERC20 tokens to send
70
+ # @param [Integer] gas_limit How much gas you are ready to spend
71
+ # @param [Integer] gas_price How much gas you pay per computation unit
72
+ # @return [String] Transaction hash
73
+ def pay(priv, address, amount, gas_limit: nil, gas_price: nil)
74
+ func = 'a9059cbb' # transfer(address,uint256)
75
+ to_clean = address.downcase.sub(/^0x/, '')
76
+ to_padded = ('0' * (64 - to_clean.size)) + to_clean
77
+ amt_hex = amount.to_s(16)
78
+ amt_padded = ('0' * (64 - amt_hex.size)) + amt_hex
79
+ data = "0x#{func}#{to_padded}#{amt_padded}"
80
+ key = Eth::Key.new(priv: priv)
81
+ from = key.address
82
+ nonce = jsonrpc.eth_getTransactionCount(from, 'pending').to_i(16)
83
+ tx = Eth::Tx.new(
84
+ {
85
+ nonce:,
86
+ gas_price: gas_price || gas_best_price,
87
+ gas_limit: gas_limit || gas_estimate(from, data),
88
+ to: @contract,
89
+ value: 0,
90
+ data: data,
91
+ chain_id: @chain
92
+ }
93
+ )
94
+ tx.sign(key)
95
+ jsonrpc.eth_sendRawTransaction("0x#{tx.hex}")
96
+ end
97
+
98
+ # Wait for incoming transactions and let the block know when they
99
+ # arrive. It's a blocking call, it's better to run it in a separate
100
+ # thread.
101
+ #
102
+ # @param [Array<String>] addresses Addresses to monitor
103
+ # @param [Array] ready When connected, TRUE will be added to this array
104
+ def accept(addresses, connected: [])
105
+ WebSocket::Client::Simple.connect(@wss) do |ws|
106
+ log = @log
107
+ contract = @contract
108
+ wss = @wss
109
+ ws.on(:open) do
110
+ log.debug("Connected to #{wss}")
111
+ msg = {
112
+ jsonrpc: '2.0',
113
+ id: 1,
114
+ method: 'eth_subscribe',
115
+ params: [
116
+ 'logs',
117
+ {
118
+ address: contract,
119
+ topics: [
120
+ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
121
+ nil,
122
+ addresses.map { |a| "0x000000000000000000000000#{a[2..]}" }
123
+ ]
124
+ }
125
+ ]
126
+ }
127
+ ws.send(msg.to_json)
128
+ connected.append(1)
129
+ end
130
+ ws.on(:message) do |msg|
131
+ data =
132
+ begin
133
+ JSON.parse(msg.data)
134
+ rescue StandardError
135
+ {}
136
+ end
137
+ if data['method'] == 'eth_subscription' && data.dig('params', 'result')
138
+ event = data['params']['result']
139
+ log.debug("New transaction from: #{event['address']}")
140
+ yield event
141
+ end
142
+ end
143
+ ws.on(:close) do |_e|
144
+ log.debug("Disconnected from #{wss}")
145
+ end
146
+ ws.on(:error) do |e|
147
+ log.debug("Error at #{wss}: #{e}")
148
+ end
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def jsonrpc
155
+ JSONRPC.logger = Loog::NULL
156
+ JSONRPC::Client.new(@rpc)
157
+ end
158
+
159
+ def gas_estimate(from, data)
160
+ jsonrpc.eth_estimateGas({ from:, to: @contract, data: }, 'latest').to_i(16)
41
161
  end
42
162
 
43
- # Create new key and return its public address.
44
- def create
45
- 'none'
163
+ def gas_best_price
164
+ jsonrpc.eth_getBlockByNumber('latest', false)['baseFeePerGas'].to_i(16)
46
165
  end
47
166
  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.2'
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,162 @@ 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['data'].to_i(16))
117
+ from = "0x#{event['topics'][1][26..].downcase}"
118
+ assert_equal(jeff, from)
119
+ to = "0x#{event['topics'][2][26..].downcase}"
120
+ assert_equal(walter, to)
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ def wait_for
127
+ start = Time.now
128
+ loop do
129
+ sleep(0.1)
130
+ break if yield
131
+ raise 'timeout' if Time.now - start > 15
132
+ rescue Errno::ECONNREFUSED
133
+ retry
134
+ end
135
+ end
136
+
137
+ def wait_for_port(port)
138
+ wait_for { Typhoeus::Request.get("http://localhost:#{port}").code == 200 }
139
+ end
140
+
141
+ def env(var)
142
+ key = ENV.fetch(var, nil)
143
+ skip("The #{var} environment variable is not set") if key.nil?
144
+ key
145
+ end
146
+
147
+ def mainnet
148
+ [
149
+ "https://mainnet.infura.io/v3/#{env('INFURA_KEY')}",
150
+ "https://go.getblock.io/#{env('GETBLOCK_KEY')}"
151
+ ].map do |rpc|
152
+ ERC20::Wallet.new(rpc:, log: Loog::NULL)
153
+ end.sample
154
+ end
155
+
156
+ def testnet
157
+ [
158
+ "https://sepolia.infura.io/v3/#{env('INFURA_KEY')}",
159
+ "https://go.getblock.io/#{env('GETBLOCK_SEPOILA_KEY')}"
160
+ ].map do |rpc|
161
+ ERC20::Wallet.new(rpc:, log: Loog::NULL)
162
+ end.sample
163
+ end
164
+
165
+ def on_hardhat
166
+ RandomPort::Pool::SINGLETON.acquire do |port|
167
+ donce(
168
+ home: File.join(__dir__, '../../hardhat'),
169
+ ports: { port => 8545 },
170
+ command: 'npx hardhat node',
171
+ log: Loog::NULL
172
+ ) do
173
+ wait_for_port(port)
174
+ cmd = [
175
+ '(cat hardhat.config.js)',
176
+ '(ls -al)',
177
+ '(echo y | npx hardhat ignition deploy ./ignition/modules/Foo.ts --network foo --deployment-id foo)',
178
+ '(npx hardhat ignition status foo | tail -1 | cut -d" " -f3)'
179
+ ].join(' && ')
180
+ contract = donce(
181
+ home: File.join(__dir__, '../../hardhat'),
182
+ command: "/bin/bash -c #{Shellwords.escape(cmd)}",
183
+ build_args: { 'HOST' => donce_host, 'PORT' => port },
184
+ log: Loog::NULL,
185
+ root: true
186
+ ).split("\n").last
187
+ wallet = ERC20::Wallet.new(
188
+ contract:, chain: 4242,
189
+ rpc: "http://localhost:#{port}",
190
+ wss: "ws://localhost:#{port}",
191
+ log: Loog::NULL
192
+ )
193
+ yield wallet
194
+ end
195
+ end
37
196
  end
38
197
  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.2
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
@@ -24,6 +24,48 @@ dependencies:
24
24
  - - ">"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
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'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: tago
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +80,20 @@ dependencies:
38
80
  - - ">"
39
81
  - !ruby/object:Gem::Version
40
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: websocket-client-simple
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">"
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">"
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
41
97
  description: A simple library for making ERC20 manipulations as easy as theycan be
42
98
  for cryptocurrency newbies
43
99
  email: yegor256@gmail.com
@@ -55,9 +111,11 @@ files:
55
111
  - ".github/workflows/markdown-lint.yml"
56
112
  - ".github/workflows/pdd.yml"
57
113
  - ".github/workflows/rake.yml"
114
+ - ".github/workflows/shellcheck.yml"
58
115
  - ".github/workflows/xcop.yml"
59
116
  - ".github/workflows/yamllint.yml"
60
117
  - ".gitignore"
118
+ - ".gitleaksignore"
61
119
  - ".pdd"
62
120
  - ".rubocop.yml"
63
121
  - ".rultor.yml"
@@ -69,6 +127,12 @@ files:
69
127
  - README.md
70
128
  - Rakefile
71
129
  - erc20.gemspec
130
+ - hardhat/.gitignore
131
+ - hardhat/Dockerfile
132
+ - hardhat/contracts/Foo.sol
133
+ - hardhat/hardhat.config.js
134
+ - hardhat/ignition/modules/Foo.ts
135
+ - hardhat/package.json
72
136
  - lib/erc20.rb
73
137
  - lib/erc20/wallet.rb
74
138
  - renovate.json