erc20 0.0.19 → 0.0.20

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: a15afef1a8d918f6594d8ec52f92899c50559f38b8c5ca5fe1ce70cf5123c7e5
4
- data.tar.gz: 46ce6edcf81eb9a07b16e3b02cd2ca46f9238a07ef3d54566df67ea4260accc6
3
+ metadata.gz: 3c6a14d29c4065075aa4c50b8101930a95b3ed09eff93ef8e37f145da576a27e
4
+ data.tar.gz: cd4ab161e890ba8cd4fdf4d60782c3d1550cadae6b6c74f4d2cec2967e3cda04
5
5
  SHA512:
6
- metadata.gz: 9c2aaca43b303101022c8189159b08bc9193ed210675e8ed343e32849883a37ac78c6b7ad7fe0efd63eb2a91f3deda2c73280091e37d25e8f8ca8eada40f74e4
7
- data.tar.gz: 04d81a60b8ab53fd4de2559f52e76e3dbf63e8266ffc739a7b1ac046250a6420554e5beb40219efa9e4617e750827d860f64cf3a3071de76f28310143d0ff885
6
+ metadata.gz: a73ba1ba539a7d25fe7cf2ecb022ddf5d7fbdcd61cf1653d987b9cf29b5337229a2756853bc538f2ae7385bf141945cea50a5198214e8da8d29088a34dea63c9
7
+ data.tar.gz: 318f3270b986d11fb901af11181e457f89a53bc7ae89898a4c3231659edc1537933637285d51c9bac046f5be15b723d9709989def4532d0013ae872e50dadabd
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.19'
45
+ VERSION = '0.0.20'
46
46
  end
@@ -67,26 +67,24 @@ class ERC20::FakeWallet
67
67
  b
68
68
  end
69
69
 
70
- # How much ETH gas is required in order to send this ERC20 transaction.
70
+ # How much gas units is required in order to send ERC20 transaction.
71
71
  #
72
72
  # @param [String] from The departing address, in hex
73
73
  # @param [String] to Arriving address, in hex
74
- # @return [Integer] How many ETH required
75
- def gas_required(from, to = from)
76
- g = 66_000
77
- @history << { method: :gas_required, from:, to:, result: g }
78
- g
74
+ # @param [Integer] amount How many ERC20 tokens to send
75
+ # @return [Integer] How many gas units required
76
+ def gas_estimate(from, to, amount)
77
+ gas = 66_000
78
+ @history << { method: :gas_estimate, from:, to:, amount:, result: gas }
79
+ gas
79
80
  end
80
81
 
81
- # How much ETH gas is required in order to send this ETH transaction.
82
- #
83
- # @param [String] from The departing address, in hex
84
- # @param [String] to Arriving address, in hex (may be skipped)
85
- # @return [Integer] How many ETH required
86
- def eth_gas_required(from, to = from)
87
- g = 55_000
88
- @history << { method: :eth_gas_required, from:, to:, result: g }
89
- g
82
+ # What is the price of gas unit in gwei?
83
+ # @return [Integer] Price of gas unit, in gwei (0.000000001 ETH)
84
+ def gas_price
85
+ gwei = 55_555
86
+ @history << { method: :gas_price, result: gwei }
87
+ gwei
90
88
  end
91
89
 
92
90
  # Send a single ERC20 payment from a private address to a public one.
@@ -107,9 +105,9 @@ class ERC20::FakeWallet
107
105
  # @param [String] address Public key, in hex
108
106
  # @param [Integer] amount The amount of ETHs to send
109
107
  # @return [String] Transaction hash
110
- def eth_pay(priv, address, amount, gas_limit: nil, gas_price: nil)
108
+ def eth_pay(priv, address, amount, gas_price: nil)
111
109
  hex = '0x172de9cda30537eae68ab4a96163ebbb8f8a85293b8737dd2e5deb4714b14623'
112
- @history << { method: :eth_pay, priv:, address:, amount:, gas_limit:, gas_price:, result: hex }
110
+ @history << { method: :eth_pay, priv:, address:, amount:, gas_price:, result: hex }
113
111
  hex
114
112
  end
115
113
 
data/lib/erc20/wallet.rb CHANGED
@@ -159,34 +159,33 @@ class ERC20::Wallet
159
159
  b
160
160
  end
161
161
 
162
- # How much ETH gas is required in order to send ERC20 transaction.
162
+ # How much gas units is required in order to send ERC20 transaction.
163
163
  #
164
164
  # @param [String] from The departing address, in hex
165
- # @param [String] to Arriving address, in hex (it's OK to skip it)
166
- # @return [Integer] How many ETH required
167
- def gas_required(from, to = from)
165
+ # @param [String] to Arriving address, in hex
166
+ # @param [Integer] amount How many ERC20 tokens to send
167
+ # @return [Integer] How many gas units required
168
+ def gas_estimate(from, to, amount)
168
169
  raise 'Address can\'t be nil' unless from
169
170
  raise 'Address must be a String' unless from.is_a?(String)
170
171
  raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(from)
171
172
  raise 'Address can\'t be nil' unless to
172
173
  raise 'Address must be a String' unless to.is_a?(String)
173
174
  raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(to)
174
- gas_estimate(from, to, to_pay_data(from, 100_000))
175
+ raise 'Amount can\'t be nil' unless amount
176
+ raise "Amount (#{amount}) must be an Integer" unless amount.is_a?(Integer)
177
+ raise "Amount (#{amount}) must be a positive Integer" unless amount.positive?
178
+ gas = jsonrpc.eth_estimateGas({ from:, to: @contract, data: to_pay_data(to, amount) }, 'latest').to_i(16)
179
+ @log.debug("It would take #{gas} gas units to send #{amount} tokens from #{from} to #{to}")
180
+ gas
175
181
  end
176
182
 
177
- # How much ETH gas is required in order to send this ETH transaction.
178
- #
179
- # @param [String] from The departing address, in hex
180
- # @param [String] to Arriving address, in hex (it's OK to skip it)
181
- # @return [Integer] How many ETH required
182
- def eth_gas_required(from, to = from)
183
- raise 'Address can\'t be nil' unless from
184
- raise 'Address must be a String' unless from.is_a?(String)
185
- raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(from)
186
- raise 'Address can\'t be nil' unless to
187
- raise 'Address must be a String' unless to.is_a?(String)
188
- raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(to)
189
- gas_estimate(from, to)
183
+ # What is the price of gas unit in gwei?
184
+ # @return [Integer] Price of gas unit, in gwei (0.000000001 ETH)
185
+ def gas_price
186
+ gwei = jsonrpc.eth_getBlockByNumber('latest', false)['baseFeePerGas'].to_i(16)
187
+ @log.debug("The cost of one gas unit is #{gwei} gwei")
188
+ gwei
190
189
  end
191
190
 
192
191
  # Send a single ERC20 payment from a private address to a public one.
@@ -194,10 +193,10 @@ class ERC20::Wallet
194
193
  # @param [String] priv Private key, in hex
195
194
  # @param [String] address Public key, in hex
196
195
  # @param [Integer] amount The amount of ERC20 tokens to send
197
- # @param [Integer] gas_limit How much gas you're ready to spend
198
- # @param [Integer] gas_price How much gas you pay per computation unit
196
+ # @param [Integer] limit How much gas you're ready to spend
197
+ # @param [Integer] price How much gas you pay per computation unit
199
198
  # @return [String] Transaction hash
200
- def pay(priv, address, amount, gas_limit: nil, gas_price: gas_best_price)
199
+ def pay(priv, address, amount, limit: nil, price: gas_price)
201
200
  raise 'Private key can\'t be nil' unless priv
202
201
  raise 'Private key must be a String' unless priv.is_a?(String)
203
202
  raise 'Invalid format of private key' unless /^[0-9a-fA-F]{64}$/.match?(priv)
@@ -205,30 +204,29 @@ class ERC20::Wallet
205
204
  raise 'Address must be a String' unless address.is_a?(String)
206
205
  raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(address)
207
206
  raise 'Amount can\'t be nil' unless amount
208
- raise 'Amount must be an Integer' unless amount.is_a?(Integer)
209
- raise 'Amount must be a positive Integer' unless amount.positive?
210
- if gas_limit
211
- raise 'Gas limit must be an Integer' unless gas_limit.is_a?(Integer)
212
- raise 'Gas limit must be a positive Integer' unless gas_limit.positive?
207
+ raise "Amount (#{amount}) must be an Integer" unless amount.is_a?(Integer)
208
+ raise "Amount (#{amount}) must be a positive Integer" unless amount.positive?
209
+ if limit
210
+ raise 'Gas limit must be an Integer' unless limit.is_a?(Integer)
211
+ raise 'Gas limit must be a positive Integer' unless limit.positive?
213
212
  end
214
- if gas_price
215
- raise 'Gas price must be an Integer' unless gas_price.is_a?(Integer)
216
- raise 'Gas price must be a positive Integer' unless gas_price.positive?
213
+ if price
214
+ raise 'Gas price must be an Integer' unless price.is_a?(Integer)
215
+ raise 'Gas price must be a positive Integer' unless price.positive?
217
216
  end
218
217
  key = Eth::Key.new(priv: priv)
219
218
  from = key.address.to_s
220
- data = to_pay_data(address, amount)
221
219
  tnx =
222
220
  @mutex.synchronize do
223
221
  nonce = jsonrpc.eth_getTransactionCount(from, 'pending').to_i(16)
224
222
  tx = Eth::Tx.new(
225
223
  {
226
224
  nonce:,
227
- gas_price: gas_price,
228
- gas_limit: gas_limit || gas_estimate(from, @contract, data),
225
+ gas_price: price,
226
+ gas_limit: limit || gas_estimate(from, address, amount),
229
227
  to: @contract,
230
228
  value: 0,
231
- data:,
229
+ data: to_pay_data(address, amount),
232
230
  chain_id: @chain
233
231
  }
234
232
  )
@@ -245,10 +243,10 @@ class ERC20::Wallet
245
243
  # @param [String] priv Private key, in hex
246
244
  # @param [String] address Public key, in hex
247
245
  # @param [Integer] amount The amount of ERC20 tokens to send
248
- # @param [Integer] gas_limit How much gas you're ready to spend
249
- # @param [Integer] gas_price How much gas you pay per computation unit
246
+ # @param [Integer] limit How much gas you're ready to spend
247
+ # @param [Integer] price How much gas you pay per computation unit
250
248
  # @return [String] Transaction hash
251
- def eth_pay(priv, address, amount, gas_limit: nil, gas_price: gas_best_price)
249
+ def eth_pay(priv, address, amount, price: gas_price)
252
250
  raise 'Private key can\'t be nil' unless priv
253
251
  raise 'Private key must be a String' unless priv.is_a?(String)
254
252
  raise 'Invalid format of private key' unless /^[0-9a-fA-F]{64}$/.match?(priv)
@@ -256,15 +254,11 @@ class ERC20::Wallet
256
254
  raise 'Address must be a String' unless address.is_a?(String)
257
255
  raise 'Invalid format of the address' unless /^0x[0-9a-fA-F]{40}$/.match?(address)
258
256
  raise 'Amount can\'t be nil' unless amount
259
- raise 'Amount must be an Integer' unless amount.is_a?(Integer)
260
- raise 'Amount must be a positive Integer' unless amount.positive?
261
- if gas_limit
262
- raise 'Gas limit must be an Integer' unless gas_limit.is_a?(Integer)
263
- raise 'Gas limit must be a positive Integer' unless gas_limit.positive?
264
- end
265
- if gas_price
266
- raise 'Gas price must be an Integer' unless gas_price.is_a?(Integer)
267
- raise 'Gas price must be a positive Integer' unless gas_price.positive?
257
+ raise "Amount (#{amount}) must be an Integer" unless amount.is_a?(Integer)
258
+ raise "Amount (#{amount}) must be a positive Integer" unless amount.positive?
259
+ if price
260
+ raise 'Gas price must be an Integer' unless price.is_a?(Integer)
261
+ raise 'Gas price must be a positive Integer' unless price.positive?
268
262
  end
269
263
  key = Eth::Key.new(priv: priv)
270
264
  from = key.address.to_s
@@ -275,8 +269,8 @@ class ERC20::Wallet
275
269
  {
276
270
  chain_id: @chain,
277
271
  nonce:,
278
- gas_price: gas_price,
279
- gas_limit: gas_limit || gas_estimate(from, address),
272
+ gas_price: price,
273
+ gas_limit: 22_000,
280
274
  to: address,
281
275
  value: amount
282
276
  }
@@ -438,15 +432,6 @@ class ERC20::Wallet
438
432
  JSONRPC::Client.new(url, connection:)
439
433
  end
440
434
 
441
- # How much gas should be spent in order to send a transaction from one
442
- # public address to another public address, possible carrying some data
443
- # inside the transaction.
444
- def gas_estimate(from, to, data = '')
445
- gas = jsonrpc.eth_estimateGas({ from:, to:, data: }, 'latest').to_i(16)
446
- @log.debug("Estimated gas is #{gas} ETH#{data.empty? ? '' : ', for ERC20 transfer'}")
447
- gas
448
- end
449
-
450
435
  def to_pay_data(address, amount)
451
436
  func = 'a9059cbb' # transfer(address,uint256)
452
437
  to_clean = address.downcase.sub(/^0x/, '')
@@ -455,8 +440,4 @@ class ERC20::Wallet
455
440
  amt_padded = ('0' * (64 - amt_hex.size)) + amt_hex
456
441
  "0x#{func}#{to_padded}#{amt_padded}"
457
442
  end
458
-
459
- def gas_best_price
460
- jsonrpc.eth_getBlockByNumber('latest', false)['baseFeePerGas'].to_i(16)
461
- end
462
443
  end
@@ -38,14 +38,18 @@ 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('0xEB2fE8872A6f1eDb70a2632Effffffffffffffff')
41
+ def test_checks_gas_estimate
42
+ b = ERC20::FakeWallet.new.gas_estimate(
43
+ '0xEB2fE8872A6f1eDb70a2632Effffffffffffffff',
44
+ '0xfadef8ba4a5d709a2bf55b7a8798c9b438c640c1',
45
+ 44_000
46
+ )
43
47
  refute_nil(b)
44
48
  end
45
49
 
46
- def test_checks_eth_gas_required
47
- b = ERC20::FakeWallet.new.eth_gas_required('0xEB2fE8872A6f1eDb70a2632Effffffffffffffff')
48
- refute_nil(b)
50
+ def test_checks_gas_price
51
+ gwei = ERC20::FakeWallet.new.gas_price
52
+ refute_nil(gwei)
49
53
  end
50
54
 
51
55
  def test_checks_fake_balance
@@ -67,19 +67,13 @@ 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)
70
+ def test_checks_gas_estimate_on_mainnet
71
+ b = mainnet.gas_estimate(STABLE, Eth::Key.new(priv: JEFF).address.to_s, 44_000)
72
72
  refute_nil(b)
73
73
  assert_predicate(b, :positive?)
74
74
  assert_operator(b, :>, 1000)
75
75
  end
76
76
 
77
- def test_checks_same_address_gas_required_on_mainnet
78
- b = mainnet.gas_required(STABLE, STABLE)
79
- refute_nil(b)
80
- assert_predicate(b, :positive?)
81
- end
82
-
83
77
  def test_fails_with_invalid_infura_key
84
78
  skip('Apparently, even with invalid key, Infura returns balance')
85
79
  w = ERC20::Wallet.new(
@@ -107,28 +101,15 @@ class TestWallet < Minitest::Test
107
101
  assert_predicate(b, :zero?)
108
102
  end
109
103
 
110
- def test_checks_gas_required_on_hardhat
111
- on_hardhat do |wallet|
112
- b1 = wallet.gas_required(
113
- Eth::Key.new(priv: JEFF).address.to_s,
114
- Eth::Key.new(priv: WALTER).address.to_s
115
- )
116
- assert_equal(21_597, b1)
117
- b2 = wallet.gas_required(
118
- Eth::Key.new(priv: JEFF).address.to_s,
119
- Eth::Key.new(priv: JEFF).address.to_s
120
- )
121
- assert_equal(b1, b2)
122
- end
123
- end
124
-
125
- def test_checks_eth_gas_required_on_hardhat
104
+ def test_checks_gas_estimate_on_hardhat
105
+ sum = 100_000
126
106
  on_hardhat do |wallet|
127
- b = wallet.eth_gas_required(
107
+ b1 = wallet.gas_estimate(
128
108
  Eth::Key.new(priv: JEFF).address.to_s,
129
- Eth::Key.new(priv: WALTER).address.to_s
109
+ Eth::Key.new(priv: WALTER).address.to_s,
110
+ sum
130
111
  )
131
- assert_equal(21_001, b)
112
+ assert_operator(b1, :>, 21_000)
132
113
  end
133
114
  end
134
115
 
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.19
4
+ version: 0.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko