erc20 0.0.16 → 0.0.17

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: 7d2dd0547d65ca59e601a28f93a2b35b45ae8b3dc57fe6a09bd296d13fe807fd
4
- data.tar.gz: a69c35dfd60d083f891a840285411373e7996937481f7c52a63f2ce9c09e2462
3
+ metadata.gz: 53d1ed4b12b1b7e295242f5345c3ebd4f22e8fe83af31bd2bcb4f27ec8a0b4ca
4
+ data.tar.gz: e377b3185614081e92f72a7fda5c9a8019de72a3497b9d28259e13f17a5f1ab0
5
5
  SHA512:
6
- metadata.gz: 1ddfb2214d688465488e224c59460e185b8bb361acadfd3b4d215053392f53dcf23367366b29a577b563be982432233efcc72ba5f4e6f6cae6651189b221c53c
7
- data.tar.gz: d8f93754ea6baf64d46591aa3ab52559bb601785df08d3071f51d5e283c7d0ad7c2b226bdaec12abbef913750136f5e895a302a451aa4ddac2225b862497631f
6
+ metadata.gz: 892709a9568e7837a708feefd8d7880d309fae9f921d1a8266e1b7b995acb1575a6305af92a1dd5dab7b9e7a7f3da9cc5b0b2539a9984eac91d291f59c5d86d1
7
+ data.tar.gz: d945d2ec483e4cc14ff76683294941cfaf9603963baf8c3acbf64189ee59aead1ec70e6b2d292c889d790c8f8f6fcd59c5327bb2a5223a138821fd5592971178
data/lib/erc20/erc20.rb CHANGED
@@ -42,5 +42,5 @@
42
42
  # License:: MIT
43
43
  module ERC20
44
44
  # Current version of the gem (changed by the +.rultor.yml+ on every release)
45
- VERSION = '0.0.16'
45
+ VERSION = '0.0.17'
46
46
  end
@@ -59,6 +59,24 @@ class ERC20::FakeWallet
59
59
  42_000_000
60
60
  end
61
61
 
62
+ # How much ETH gas is required in order to send this ETH transaction.
63
+ #
64
+ # @param [String] _from The departing address, in hex
65
+ # @param [String] _to Arriving address, in hex
66
+ # @return [Integer] How many ETH required
67
+ def eth_gas_required(_from, _to)
68
+ 55_000
69
+ end
70
+
71
+ # How much ETH gas is required in order to send this ERC20 transaction.
72
+ #
73
+ # @param [String] _from The departing address, in hex
74
+ # @param [String] _to Arriving address, in hex
75
+ # @return [Integer] How many ETH required
76
+ def gas_required(_from, _to)
77
+ 66_000
78
+ end
79
+
62
80
  # Send a single ERC20 payment from a private address to a public one.
63
81
  #
64
82
  # @param [String] _priv Private key, in hex
data/lib/erc20/wallet.rb CHANGED
@@ -159,6 +159,24 @@ class ERC20::Wallet
159
159
  b
160
160
  end
161
161
 
162
+ # How much ETH gas is required in order to send this ETH transaction.
163
+ #
164
+ # @param [String] from The departing address, in hex
165
+ # @param [String] to Arriving address, in hex
166
+ # @return [Integer] How many ETH required
167
+ def eth_gas_required(from, to)
168
+ gas_estimate(from, to)
169
+ end
170
+
171
+ # How much ETH gas is required in order to send this ERC20 transaction.
172
+ #
173
+ # @param [String] from The departing address, in hex
174
+ # @param [String] to Arriving address, in hex
175
+ # @return [Integer] How many ETH required
176
+ def gas_required(from, to)
177
+ gas_estimate(from, to, to_pay_data(from, 100_000))
178
+ end
179
+
162
180
  # Send a single ERC20 payment from a private address to a public one.
163
181
  #
164
182
  # @param [String] priv Private key, in hex
@@ -167,7 +185,7 @@ class ERC20::Wallet
167
185
  # @param [Integer] gas_limit How much gas you're ready to spend
168
186
  # @param [Integer] gas_price How much gas you pay per computation unit
169
187
  # @return [String] Transaction hash
170
- def pay(priv, address, amount, gas_limit: nil, gas_price: nil)
188
+ def pay(priv, address, amount, gas_limit: nil, gas_price: gas_best_price)
171
189
  raise 'Private key can\'t be nil' unless priv
172
190
  raise 'Private key must be a String' unless priv.is_a?(String)
173
191
  raise 'Invalid format of private key' unless /^[0-9a-fA-F]{64}$/.match?(priv)
@@ -185,25 +203,20 @@ class ERC20::Wallet
185
203
  raise 'Gas price must be an Integer' unless gas_price.is_a?(Integer)
186
204
  raise 'Gas price must be a positive Integer' unless gas_price.positive?
187
205
  end
188
- func = 'a9059cbb' # transfer(address,uint256)
189
- to_clean = address.downcase.sub(/^0x/, '')
190
- to_padded = ('0' * (64 - to_clean.size)) + to_clean
191
- amt_hex = amount.to_s(16)
192
- amt_padded = ('0' * (64 - amt_hex.size)) + amt_hex
193
- data = "0x#{func}#{to_padded}#{amt_padded}"
194
206
  key = Eth::Key.new(priv: priv)
195
207
  from = key.address.to_s
208
+ data = to_pay_data(address, amount)
196
209
  tnx =
197
210
  @mutex.synchronize do
198
211
  nonce = jsonrpc.eth_getTransactionCount(from, 'pending').to_i(16)
199
212
  tx = Eth::Tx.new(
200
213
  {
201
214
  nonce:,
202
- gas_price: gas_price || gas_best_price,
215
+ gas_price: gas_price,
203
216
  gas_limit: gas_limit || gas_estimate(from, @contract, data),
204
217
  to: @contract,
205
218
  value: 0,
206
- data: data,
219
+ data:,
207
220
  chain_id: @chain
208
221
  }
209
222
  )
@@ -223,7 +236,7 @@ class ERC20::Wallet
223
236
  # @param [Integer] gas_limit How much gas you're ready to spend
224
237
  # @param [Integer] gas_price How much gas you pay per computation unit
225
238
  # @return [String] Transaction hash
226
- def eth_pay(priv, address, amount, gas_limit: nil, gas_price: nil)
239
+ def eth_pay(priv, address, amount, gas_limit: nil, gas_price: gas_best_price)
227
240
  raise 'Private key can\'t be nil' unless priv
228
241
  raise 'Private key must be a String' unless priv.is_a?(String)
229
242
  raise 'Invalid format of private key' unless /^[0-9a-fA-F]{64}$/.match?(priv)
@@ -243,7 +256,6 @@ class ERC20::Wallet
243
256
  end
244
257
  key = Eth::Key.new(priv: priv)
245
258
  from = key.address.to_s
246
- data = ''
247
259
  tnx =
248
260
  @mutex.synchronize do
249
261
  nonce = jsonrpc.eth_getTransactionCount(from, 'pending').to_i(16)
@@ -251,11 +263,10 @@ class ERC20::Wallet
251
263
  {
252
264
  chain_id: @chain,
253
265
  nonce:,
254
- gas_price: gas_price || gas_best_price,
255
- gas_limit: gas_limit || gas_estimate(from, address, data),
266
+ gas_price: gas_price,
267
+ gas_limit: gas_limit || gas_estimate(from, address),
256
268
  to: address,
257
- value: amount,
258
- data:
269
+ value: amount
259
270
  }
260
271
  )
261
272
  tx.sign(key)
@@ -415,10 +426,22 @@ class ERC20::Wallet
415
426
  JSONRPC::Client.new(url, connection:)
416
427
  end
417
428
 
418
- def gas_estimate(from, to, data)
429
+ # How much gas should be spent in order to send a transaction from one
430
+ # public address to another public address, possible carrying some data
431
+ # inside the transaction.
432
+ def gas_estimate(from, to, data = '')
419
433
  jsonrpc.eth_estimateGas({ from:, to:, data: }, 'latest').to_i(16)
420
434
  end
421
435
 
436
+ def to_pay_data(address, amount)
437
+ func = 'a9059cbb' # transfer(address,uint256)
438
+ to_clean = address.downcase.sub(/^0x/, '')
439
+ to_padded = ('0' * (64 - to_clean.size)) + to_clean
440
+ amt_hex = amount.to_s(16)
441
+ amt_padded = ('0' * (64 - amt_hex.size)) + amt_hex
442
+ "0x#{func}#{to_padded}#{amt_padded}"
443
+ end
444
+
422
445
  def gas_best_price
423
446
  jsonrpc.eth_getBlockByNumber('latest', false)['baseFeePerGas'].to_i(16)
424
447
  end
@@ -38,6 +38,22 @@ require_relative '../test__helper'
38
38
  # Copyright:: Copyright (c) 2025 Yegor Bugayenko
39
39
  # License:: MIT
40
40
  class TestFakeWallet < Minitest::Test
41
+ def test_checks_gas_required
42
+ b = ERC20::FakeWallet.new.gas_required(
43
+ '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff',
44
+ '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
45
+ )
46
+ refute_nil(b)
47
+ end
48
+
49
+ def test_checks_eth_gas_required
50
+ b = ERC20::FakeWallet.new.eth_gas_required(
51
+ '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff',
52
+ '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff'
53
+ )
54
+ refute_nil(b)
55
+ end
56
+
41
57
  def test_checks_fake_balance
42
58
  b = ERC20::FakeWallet.new.balance('0xEB2fE8872A6f1eDb70a2632Effffffffffffffff')
43
59
  refute_nil(b)
@@ -38,7 +38,7 @@ require_relative '../test__helper'
38
38
  # Copyright:: Copyright (c) 2025 Yegor Bugayenko
39
39
  # License:: MIT
40
40
  class TestWallet < Minitest::Test
41
- # At this address, in Etherium mainnet, there are $8 USDT. I won't
41
+ # At this address, in Etherium mainnet, there are $8 USDT and 0.0042 ETH. I won't
42
42
  # move them anyway, that's why tests can use this address forever.
43
43
  STABLE = '0x7232148927F8a580053792f44D4d59d40Fd00ABD'
44
44
 
@@ -51,13 +51,13 @@ class TestWallet < Minitest::Test
51
51
  def test_checks_balance_on_mainnet
52
52
  b = mainnet.balance(STABLE)
53
53
  refute_nil(b)
54
- assert_equal(8_000_000, b)
54
+ assert_equal(8_000_000, b) # this is $8 USDT
55
55
  end
56
56
 
57
57
  def test_checks_eth_balance_on_mainnet
58
58
  b = mainnet.eth_balance(STABLE)
59
59
  refute_nil(b)
60
- assert_equal(0, b)
60
+ assert_equal(4_200_000_000_000_000, b) # this is 0.0042 ETH
61
61
  end
62
62
 
63
63
  def test_checks_balance_of_absent_address
@@ -67,6 +67,12 @@ class TestWallet < Minitest::Test
67
67
  assert_equal(0, b)
68
68
  end
69
69
 
70
+ def test_checks_gas_required_on_mainnet
71
+ b = mainnet.gas_required(STABLE, Eth::Key.new(priv: JEFF).address.to_s)
72
+ refute_nil(b)
73
+ assert_predicate(b, :positive?)
74
+ end
75
+
70
76
  def test_fails_with_invalid_infura_key
71
77
  skip('Apparently, even with invalid key, Infura returns balance')
72
78
  w = ERC20::Wallet.new(
@@ -94,6 +100,26 @@ class TestWallet < Minitest::Test
94
100
  assert_predicate(b, :zero?)
95
101
  end
96
102
 
103
+ def test_checks_gas_required_on_hardhat
104
+ on_hardhat do |wallet|
105
+ b = wallet.gas_required(
106
+ Eth::Key.new(priv: JEFF).address.to_s,
107
+ Eth::Key.new(priv: WALTER).address.to_s
108
+ )
109
+ assert_equal(21_597, b)
110
+ end
111
+ end
112
+
113
+ def test_checks_eth_gas_required_on_hardhat
114
+ on_hardhat do |wallet|
115
+ b = wallet.eth_gas_required(
116
+ Eth::Key.new(priv: JEFF).address.to_s,
117
+ Eth::Key.new(priv: WALTER).address.to_s
118
+ )
119
+ assert_equal(21_001, b)
120
+ end
121
+ end
122
+
97
123
  def test_checks_balance_on_hardhat
98
124
  on_hardhat do |wallet|
99
125
  b = wallet.balance(Eth::Key.new(priv: JEFF).address.to_s)
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.16
4
+ version: 0.0.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko