erc20 0.0.19 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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