eth 0.5.10 → 0.5.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +1 -1
- data/.github/workflows/spec.yml +8 -3
- data/CHANGELOG.md +16 -0
- data/eth.gemspec +4 -1
- data/lib/eth/abi/decoder.rb +135 -0
- data/lib/eth/abi/encoder.rb +304 -0
- data/lib/eth/abi/event.rb +13 -2
- data/lib/eth/abi/type.rb +1 -1
- data/lib/eth/abi.rb +10 -385
- data/lib/eth/api.rb +45 -51
- data/lib/eth/chain.rb +9 -0
- data/lib/eth/client/http.rb +18 -4
- data/lib/eth/client/ipc.rb +2 -2
- data/lib/eth/client.rb +59 -125
- data/lib/eth/contract.rb +11 -1
- data/lib/eth/solidity.rb +7 -4
- data/lib/eth/tx/eip1559.rb +10 -5
- data/lib/eth/tx/eip2930.rb +10 -5
- data/lib/eth/tx/legacy.rb +0 -2
- data/lib/eth/tx.rb +9 -2
- data/lib/eth/util.rb +1 -0
- data/lib/eth/version.rb +11 -2
- metadata +20 -5
- data/lib/eth/client/http_auth.rb +0 -73
data/lib/eth/client/ipc.rb
CHANGED
@@ -24,7 +24,7 @@ module Eth
|
|
24
24
|
attr_accessor :path
|
25
25
|
|
26
26
|
# Constructor for the IPC Client. Should not be used; use
|
27
|
-
# {Client.create}
|
27
|
+
# {Client.create} instead.
|
28
28
|
#
|
29
29
|
# @param path [String] an URI pointing to an IPC RPC-API.
|
30
30
|
def initialize(path)
|
@@ -36,7 +36,7 @@ module Eth
|
|
36
36
|
#
|
37
37
|
# @param payload [Hash] the RPC request parameters.
|
38
38
|
# @return [String] a JSON-encoded response.
|
39
|
-
def
|
39
|
+
def send_request(payload)
|
40
40
|
socket = UNIXSocket.new(@path)
|
41
41
|
socket.puts(payload)
|
42
42
|
read = socket.recvmsg(nil)[0]
|
data/lib/eth/client.rb
CHANGED
@@ -34,28 +34,22 @@ module Eth
|
|
34
34
|
# The default transaction max fee per gas in Wei, defaults to {Tx::DEFAULT_GAS_PRICE}.
|
35
35
|
attr_accessor :max_fee_per_gas
|
36
36
|
|
37
|
-
# The default gas limit for the transaction, defaults to {Tx::DEFAULT_GAS_LIMIT}.
|
38
|
-
attr_accessor :gas_limit
|
39
|
-
|
40
37
|
# A custom error type if a contract interaction fails.
|
41
38
|
class ContractExecutionError < StandardError; end
|
42
39
|
|
43
40
|
# Creates a new RPC-Client, either by providing an HTTP/S host or
|
44
41
|
# an IPC path. Supports basic authentication with username and password.
|
45
42
|
#
|
46
|
-
# **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE}
|
47
|
-
# {Tx::DEFAULT_GAS_PRICE
|
48
|
-
# {#
|
49
|
-
# custom values prior to submitting transactions.
|
43
|
+
# **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE}
|
44
|
+
# and {Tx::DEFAULT_GAS_PRICE. Use {#max_priority_fee_per_gas} and
|
45
|
+
# {#max_fee_per_gas} to set custom values prior to submitting transactions.
|
50
46
|
#
|
51
47
|
# @param host [String] either an HTTP/S host or an IPC path.
|
52
48
|
# @return [Eth::Client::Ipc] an IPC client.
|
53
|
-
# @return [Eth::Client::HttpAuth] an HTTP client with basic authentication.
|
54
49
|
# @return [Eth::Client::Http] an HTTP client.
|
55
50
|
# @raise [ArgumentError] in case it cannot determine the client type.
|
56
51
|
def self.create(host)
|
57
52
|
return Client::Ipc.new host if host.end_with? ".ipc"
|
58
|
-
return Client::HttpAuth.new host if Regexp.new(":.*@.*:", Regexp::IGNORECASE).match host
|
59
53
|
return Client::Http.new host if host.start_with? "http"
|
60
54
|
raise ArgumentError, "Unable to detect client type!"
|
61
55
|
end
|
@@ -66,7 +60,6 @@ module Eth
|
|
66
60
|
@id = 0
|
67
61
|
@max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE
|
68
62
|
@max_fee_per_gas = Tx::DEFAULT_GAS_PRICE
|
69
|
-
@gas_limit = Tx::DEFAULT_GAS_LIMIT
|
70
63
|
end
|
71
64
|
|
72
65
|
# Gets the default account (coinbase) of the connected client.
|
@@ -76,7 +69,7 @@ module Eth
|
|
76
69
|
#
|
77
70
|
# @return [Eth::Address] the coinbase account address.
|
78
71
|
def default_account
|
79
|
-
raise ArgumentError, "The default account is not available on remote connections!" unless local?
|
72
|
+
raise ArgumentError, "The default account is not available on remote connections!" unless local? || @default_account
|
80
73
|
@default_account ||= Address.new eth_coinbase["result"]
|
81
74
|
end
|
82
75
|
|
@@ -146,41 +139,10 @@ module Eth
|
|
146
139
|
params = {
|
147
140
|
value: amount,
|
148
141
|
to: destination,
|
149
|
-
gas_limit:
|
142
|
+
gas_limit: Tx::DEFAULT_GAS_LIMIT,
|
150
143
|
chain_id: chain_id,
|
151
144
|
}
|
152
|
-
|
153
|
-
params.merge!({
|
154
|
-
gas_price: max_fee_per_gas,
|
155
|
-
})
|
156
|
-
else
|
157
|
-
params.merge!({
|
158
|
-
priority_fee: max_priority_fee_per_gas,
|
159
|
-
max_gas_fee: max_fee_per_gas,
|
160
|
-
})
|
161
|
-
end
|
162
|
-
unless kwargs[:sender_key].nil?
|
163
|
-
|
164
|
-
# use the provided key as sender and signer
|
165
|
-
params.merge!({
|
166
|
-
from: kwargs[:sender_key].address,
|
167
|
-
nonce: kwargs[:nonce] || get_nonce(kwargs[:sender_key].address),
|
168
|
-
})
|
169
|
-
tx = Eth::Tx.new(params)
|
170
|
-
tx.sign kwargs[:sender_key]
|
171
|
-
return eth_send_raw_transaction(tx.hex)["result"]
|
172
|
-
else
|
173
|
-
|
174
|
-
# we do not allow accessing accounts on remote connections
|
175
|
-
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
|
176
|
-
|
177
|
-
# use the default account as sender and external signer
|
178
|
-
params.merge!({
|
179
|
-
from: default_account,
|
180
|
-
nonce: kwargs[:nonce] || get_nonce(default_account),
|
181
|
-
})
|
182
|
-
return eth_send_transaction(params)["result"]
|
183
|
-
end
|
145
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
184
146
|
end
|
185
147
|
|
186
148
|
# Transfers a token that implements the ERC20 `transfer()` interface.
|
@@ -207,7 +169,7 @@ module Eth
|
|
207
169
|
# @param amount [Integer] the transfer amount (mind the `decimals()`).
|
208
170
|
# @param **sender_key [Eth::Key] the sender private key.
|
209
171
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
210
|
-
# @param **gas_limit [Integer] optional gas limit override for
|
172
|
+
# @param **gas_limit [Integer] optional gas limit override for the transfer.
|
211
173
|
# @param **nonce [Integer] optional specific nonce for transaction.
|
212
174
|
# @param **tx_value [Integer] optional transaction value field filling.
|
213
175
|
# @return [Object] returns the result of the transaction.
|
@@ -266,37 +228,7 @@ module Eth
|
|
266
228
|
chain_id: chain_id,
|
267
229
|
data: data,
|
268
230
|
}
|
269
|
-
|
270
|
-
params.merge!({
|
271
|
-
gas_price: max_fee_per_gas,
|
272
|
-
})
|
273
|
-
else
|
274
|
-
params.merge!({
|
275
|
-
priority_fee: max_priority_fee_per_gas,
|
276
|
-
max_gas_fee: max_fee_per_gas,
|
277
|
-
})
|
278
|
-
end
|
279
|
-
unless kwargs[:sender_key].nil?
|
280
|
-
# Uses the provided key as sender and signer
|
281
|
-
params.merge!({
|
282
|
-
from: kwargs[:sender_key].address,
|
283
|
-
nonce: kwargs[:nonce] || get_nonce(kwargs[:sender_key].address),
|
284
|
-
})
|
285
|
-
tx = Eth::Tx.new(params)
|
286
|
-
tx.sign kwargs[:sender_key]
|
287
|
-
return eth_send_raw_transaction(tx.hex)["result"]
|
288
|
-
else
|
289
|
-
|
290
|
-
# Does not allow accessing accounts on remote connections
|
291
|
-
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
|
292
|
-
|
293
|
-
# Uses the default account as sender and external signer
|
294
|
-
params.merge!({
|
295
|
-
from: default_account,
|
296
|
-
nonce: kwargs[:nonce] || get_nonce(default_account),
|
297
|
-
})
|
298
|
-
return eth_send_transaction(params)["result"]
|
299
|
-
end
|
231
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
300
232
|
end
|
301
233
|
|
302
234
|
# Calls a contract function without executing it
|
@@ -315,7 +247,6 @@ module Eth
|
|
315
247
|
# @param *args optional function arguments.
|
316
248
|
# @param **sender_key [Eth::Key] the sender private key.
|
317
249
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
318
|
-
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
319
250
|
# @return [Object] returns the result of the call.
|
320
251
|
def call(contract, function, *args, **kwargs)
|
321
252
|
func = contract.functions.select { |func| func.name == function }
|
@@ -328,9 +259,9 @@ module Eth
|
|
328
259
|
end
|
329
260
|
output = call_raw(contract, selected_func, *args, **kwargs)
|
330
261
|
if output&.length == 1
|
331
|
-
|
262
|
+
output[0]
|
332
263
|
else
|
333
|
-
|
264
|
+
output
|
334
265
|
end
|
335
266
|
end
|
336
267
|
|
@@ -354,7 +285,7 @@ module Eth
|
|
354
285
|
# @param **sender_key [Eth::Key] the sender private key.
|
355
286
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
356
287
|
# @param **address [Eth::Address] contract address.
|
357
|
-
# @param **gas_limit [Integer] optional gas limit override for
|
288
|
+
# @param **gas_limit [Integer] optional gas limit override for transacting with the contract.
|
358
289
|
# @param **nonce [Integer] optional specific nonce for transaction.
|
359
290
|
# @param **tx_value [Integer] optional transaction value field filling.
|
360
291
|
# @return [Object] returns the result of the transaction.
|
@@ -362,7 +293,7 @@ module Eth
|
|
362
293
|
gas_limit = if kwargs[:gas_limit]
|
363
294
|
kwargs[:gas_limit]
|
364
295
|
else
|
365
|
-
Tx.estimate_intrinsic_gas(contract.bin)
|
296
|
+
Tx.estimate_intrinsic_gas(contract.bin)
|
366
297
|
end
|
367
298
|
fun = contract.functions.select { |func| func.name == function }[0]
|
368
299
|
params = {
|
@@ -372,37 +303,7 @@ module Eth
|
|
372
303
|
to: kwargs[:address] || contract.address,
|
373
304
|
data: call_payload(fun, args),
|
374
305
|
}
|
375
|
-
|
376
|
-
params.merge!({
|
377
|
-
gas_price: max_fee_per_gas,
|
378
|
-
})
|
379
|
-
else
|
380
|
-
params.merge!({
|
381
|
-
priority_fee: max_priority_fee_per_gas,
|
382
|
-
max_gas_fee: max_fee_per_gas,
|
383
|
-
})
|
384
|
-
end
|
385
|
-
unless kwargs[:sender_key].nil?
|
386
|
-
# use the provided key as sender and signer
|
387
|
-
params.merge!({
|
388
|
-
from: kwargs[:sender_key].address,
|
389
|
-
nonce: kwargs[:nonce] || get_nonce(kwargs[:sender_key].address),
|
390
|
-
})
|
391
|
-
tx = Eth::Tx.new(params)
|
392
|
-
tx.sign kwargs[:sender_key]
|
393
|
-
return eth_send_raw_transaction(tx.hex)["result"]
|
394
|
-
else
|
395
|
-
|
396
|
-
# do not allow accessing accounts on remote connections
|
397
|
-
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
|
398
|
-
|
399
|
-
# use the default account as sender and external signer
|
400
|
-
params.merge!({
|
401
|
-
from: default_account,
|
402
|
-
nonce: kwargs[:nonce] || get_nonce(default_account),
|
403
|
-
})
|
404
|
-
return eth_send_transaction(params)["result"]
|
405
|
-
end
|
306
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
406
307
|
end
|
407
308
|
|
408
309
|
# Executes a contract function with a transaction and waits for it
|
@@ -437,7 +338,7 @@ module Eth
|
|
437
338
|
signature = Util.hex_to_bin signature if Util.hex? signature
|
438
339
|
magic = Util.hex_to_bin magic if Util.hex? magic
|
439
340
|
result = call(contract, "isValidSignature", hash, signature)
|
440
|
-
|
341
|
+
result === magic
|
441
342
|
end
|
442
343
|
|
443
344
|
# Gives control over resetting the RPC request ID back to zero.
|
@@ -496,11 +397,45 @@ module Eth
|
|
496
397
|
# Allows to determine if we work with a local connectoin
|
497
398
|
def local?
|
498
399
|
if self.instance_of? Eth::Client::Ipc
|
499
|
-
|
400
|
+
true
|
500
401
|
elsif self.host === "127.0.0.1" || self.host === "localhost"
|
501
|
-
|
402
|
+
true
|
502
403
|
else
|
503
|
-
|
404
|
+
false
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Prepares a transaction to be send for the given params.
|
409
|
+
def send_transaction(params, legacy, key, nonce)
|
410
|
+
if legacy
|
411
|
+
params.merge!({ gas_price: max_fee_per_gas })
|
412
|
+
else
|
413
|
+
params.merge!({
|
414
|
+
priority_fee: max_priority_fee_per_gas,
|
415
|
+
max_gas_fee: max_fee_per_gas,
|
416
|
+
})
|
417
|
+
end
|
418
|
+
unless key.nil?
|
419
|
+
|
420
|
+
# use the provided key as sender and signer
|
421
|
+
params.merge!({
|
422
|
+
from: key.address,
|
423
|
+
nonce: nonce || get_nonce(key.address),
|
424
|
+
})
|
425
|
+
tx = Eth::Tx.new(params)
|
426
|
+
tx.sign key
|
427
|
+
eth_send_raw_transaction(tx.hex)["result"]
|
428
|
+
else
|
429
|
+
|
430
|
+
# do not allow accessing accounts on remote connections
|
431
|
+
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
|
432
|
+
|
433
|
+
# use the default account as sender and external signer
|
434
|
+
params.merge!({
|
435
|
+
from: default_account,
|
436
|
+
nonce: nonce || get_nonce(default_account),
|
437
|
+
})
|
438
|
+
eth_send_transaction(params)["result"]
|
504
439
|
end
|
505
440
|
end
|
506
441
|
|
@@ -541,9 +476,9 @@ module Eth
|
|
541
476
|
params: marshal(args),
|
542
477
|
id: next_id,
|
543
478
|
}
|
544
|
-
output = JSON.parse(
|
479
|
+
output = JSON.parse(send_request(payload.to_json))
|
545
480
|
raise IOError, output["error"]["message"] unless output["error"].nil?
|
546
|
-
|
481
|
+
output
|
547
482
|
end
|
548
483
|
|
549
484
|
# Increments the request id.
|
@@ -564,18 +499,18 @@ module Eth
|
|
564
499
|
def marshal(params)
|
565
500
|
params = params.dup
|
566
501
|
if params.is_a? Array
|
567
|
-
|
502
|
+
params.map! { |param| marshal(param) }
|
568
503
|
elsif params.is_a? Hash
|
569
504
|
params = camelize!(params)
|
570
|
-
|
505
|
+
params.transform_values! { |param| marshal(param) }
|
571
506
|
elsif params.is_a? Numeric
|
572
|
-
|
507
|
+
Util.prefix_hex "#{params.to_i.to_s(16)}"
|
573
508
|
elsif params.is_a? Address
|
574
|
-
|
509
|
+
params.to_s
|
575
510
|
elsif Util.hex? params
|
576
|
-
|
511
|
+
Util.prefix_hex params
|
577
512
|
else
|
578
|
-
|
513
|
+
params
|
579
514
|
end
|
580
515
|
end
|
581
516
|
end
|
@@ -583,5 +518,4 @@ end
|
|
583
518
|
|
584
519
|
# Load the client/* libraries
|
585
520
|
require "eth/client/http"
|
586
|
-
require "eth/client/http_auth"
|
587
521
|
require "eth/client/ipc"
|
data/lib/eth/contract.rb
CHANGED
@@ -14,6 +14,8 @@
|
|
14
14
|
|
15
15
|
# -*- encoding : ascii-8bit -*-
|
16
16
|
|
17
|
+
require "forwardable"
|
18
|
+
|
17
19
|
# Provides the {Eth} module.
|
18
20
|
module Eth
|
19
21
|
|
@@ -27,11 +29,19 @@ module Eth
|
|
27
29
|
|
28
30
|
# Constructor of the {Eth::Contract} class.
|
29
31
|
#
|
32
|
+
# **Note**, do not use this directly. Use
|
33
|
+
# {from_abi}, {from_bin}, or {from_file}!
|
34
|
+
#
|
30
35
|
# @param name [String] contract name.
|
31
36
|
# @param bin [String] contract bin string.
|
32
37
|
# @param abi [String] contract abi string.
|
33
38
|
def initialize(name, bin, abi)
|
34
|
-
|
39
|
+
|
40
|
+
# The contract name will be the class name and needs title casing.
|
41
|
+
_name = name.dup
|
42
|
+
_name[0] = name[0].upcase
|
43
|
+
|
44
|
+
@name = _name
|
35
45
|
@bin = bin
|
36
46
|
@abi = abi
|
37
47
|
@constructor_inputs, @functions, @events = parse_abi(abi)
|
data/lib/eth/solidity.rb
CHANGED
@@ -28,10 +28,12 @@ module Eth
|
|
28
28
|
|
29
29
|
# Instantiates a Solidity `solc` system compiler binding that can be
|
30
30
|
# used to compile Solidity contracts.
|
31
|
-
|
31
|
+
#
|
32
|
+
# @param path [String] optional override of the solidity compiler path.
|
33
|
+
def initialize(path = nil)
|
32
34
|
|
33
|
-
# Currently only supports `solc`.
|
34
|
-
solc = get_compiler_path
|
35
|
+
# Currently only supports `solc`. Try to override with `path`.
|
36
|
+
solc = path || get_compiler_path
|
35
37
|
raise SystemCallError, "Unable to find the solc compiler path!" if solc.nil?
|
36
38
|
@compiler = solc
|
37
39
|
end
|
@@ -43,9 +45,10 @@ module Eth
|
|
43
45
|
def compile(contract)
|
44
46
|
raise Errno::ENOENT, "Contract file not found: #{contract}" unless File.exist? contract
|
45
47
|
flag_opt = "--optimize"
|
48
|
+
flag_ir = "--via-ir"
|
46
49
|
flag_json = "--combined-json=bin,abi"
|
47
50
|
path = File.realpath contract
|
48
|
-
output, error, status = Open3.capture3 @compiler, flag_opt, flag_json, path
|
51
|
+
output, error, status = Open3.capture3 @compiler, flag_opt, flag_ir, flag_json, path
|
49
52
|
raise SystemCallError, "Unable to run solc compiler!" if status.exitstatus === 127
|
50
53
|
raise CompilerError, error unless status.success?
|
51
54
|
json = JSON.parse output
|
data/lib/eth/tx/eip1559.rb
CHANGED
@@ -186,11 +186,16 @@ module Eth
|
|
186
186
|
# last but not least, set the type.
|
187
187
|
@type = TYPE_1559
|
188
188
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
189
|
+
unless recovery_id.nil?
|
190
|
+
# recover sender address
|
191
|
+
v = Chain.to_v recovery_id, chain_id
|
192
|
+
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id)
|
193
|
+
address = Util.public_key_to_address(public_key).to_s
|
194
|
+
@sender = Tx.sanitize_address address
|
195
|
+
else
|
196
|
+
# keep the 'from' field blank
|
197
|
+
@sender = Tx.sanitize_address nil
|
198
|
+
end
|
194
199
|
end
|
195
200
|
|
196
201
|
# Creates an unsigned copy of a transaction payload.
|
data/lib/eth/tx/eip2930.rb
CHANGED
@@ -181,11 +181,16 @@ module Eth
|
|
181
181
|
# last but not least, set the type.
|
182
182
|
@type = TYPE_2930
|
183
183
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
184
|
+
unless recovery_id.nil?
|
185
|
+
# recover sender address
|
186
|
+
v = Chain.to_v recovery_id, chain_id
|
187
|
+
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v.to_s(16)}", chain_id)
|
188
|
+
address = Util.public_key_to_address(public_key).to_s
|
189
|
+
@sender = Tx.sanitize_address address
|
190
|
+
else
|
191
|
+
# keep the 'from' field blank
|
192
|
+
@sender = Tx.sanitize_address nil
|
193
|
+
end
|
189
194
|
end
|
190
195
|
|
191
196
|
# Creates an unsigned copy of a transaction payload.
|
data/lib/eth/tx/legacy.rb
CHANGED
@@ -154,13 +154,11 @@ module Eth
|
|
154
154
|
_set_signature(v, r, s)
|
155
155
|
|
156
156
|
unless chain_id.nil?
|
157
|
-
|
158
157
|
# recover sender address
|
159
158
|
public_key = Signature.recover(unsigned_hash, "#{r.rjust(64, "0")}#{s.rjust(64, "0")}#{v}", chain_id)
|
160
159
|
address = Util.public_key_to_address(public_key).to_s
|
161
160
|
@sender = Tx.sanitize_address address
|
162
161
|
else
|
163
|
-
|
164
162
|
# keep the 'from' field blank
|
165
163
|
@sender = Tx.sanitize_address nil
|
166
164
|
end
|
data/lib/eth/tx.rb
CHANGED
@@ -51,6 +51,9 @@ module Eth
|
|
51
51
|
# The calldata gas cost of a zero byte.
|
52
52
|
COST_ZERO_BYTE = 4.freeze
|
53
53
|
|
54
|
+
# The initcode gas cost for each word (32 bytes).
|
55
|
+
COST_INITCODE_WORD = 2.freeze
|
56
|
+
|
54
57
|
# The access list gas cost of a storage key as per EIP-2930.
|
55
58
|
COST_STORAGE_KEY = 1_900.freeze
|
56
59
|
|
@@ -156,7 +159,7 @@ module Eth
|
|
156
159
|
end
|
157
160
|
|
158
161
|
# Estimates intrinsic gas for provided call data (EIP-2028) and
|
159
|
-
# access lists (EIP-2930).
|
162
|
+
# access lists (EIP-2930). Respects initcode word cost (EIP-3860).
|
160
163
|
#
|
161
164
|
# @param data [String] the call data.
|
162
165
|
# @param list [Array] the access list.
|
@@ -173,6 +176,10 @@ module Eth
|
|
173
176
|
# count non-zero bytes
|
174
177
|
none = data.size - zero
|
175
178
|
gas += none * COST_NON_ZERO_BYTE
|
179
|
+
|
180
|
+
# count "words" as per EIP-3860
|
181
|
+
word_count = (data.length.to_f / 32.0).ceil
|
182
|
+
gas += word_count * COST_INITCODE_WORD
|
176
183
|
end
|
177
184
|
unless list.nil? or list.empty?
|
178
185
|
list.each do |entry|
|
@@ -187,7 +194,7 @@ module Eth
|
|
187
194
|
end
|
188
195
|
end
|
189
196
|
end
|
190
|
-
return gas
|
197
|
+
return gas.to_i
|
191
198
|
end
|
192
199
|
|
193
200
|
# Validates the common transaction fields such as nonce, gas limit,
|
data/lib/eth/util.rb
CHANGED
@@ -28,6 +28,7 @@ module Eth
|
|
28
28
|
# @return [Eth::Address] an Ethereum address.
|
29
29
|
def public_key_to_address(str)
|
30
30
|
str = hex_to_bin str if hex? str
|
31
|
+
str = Secp256k1::PublicKey.from_data(str).uncompressed
|
31
32
|
bytes = keccak256(str[1..-1])[-20..-1]
|
32
33
|
Address.new bin_to_prefixed_hex bytes
|
33
34
|
end
|
data/lib/eth/version.rb
CHANGED
@@ -15,6 +15,15 @@
|
|
15
15
|
# Provides the {Eth} module.
|
16
16
|
module Eth
|
17
17
|
|
18
|
-
# Defines the version of the {Eth} module.
|
19
|
-
|
18
|
+
# Defines the major version of the {Eth} module.
|
19
|
+
MAJOR = 0.freeze
|
20
|
+
|
21
|
+
# Defines the minor version of the {Eth} module.
|
22
|
+
MINOR = 5.freeze
|
23
|
+
|
24
|
+
# Defines the patch version of the {Eth} module.
|
25
|
+
PATCH = 11.freeze
|
26
|
+
|
27
|
+
# Defines the version string of the {Eth} module.
|
28
|
+
VERSION = [MAJOR, MINOR, PATCH].join(".").freeze
|
20
29
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Ellis
|
@@ -9,8 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-08-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: forwardable
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.3'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.3'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: keccak
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,14 +59,14 @@ dependencies:
|
|
45
59
|
requirements:
|
46
60
|
- - "~>"
|
47
61
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
62
|
+
version: '6.0'
|
49
63
|
type: :runtime
|
50
64
|
prerelease: false
|
51
65
|
version_requirements: !ruby/object:Gem::Requirement
|
52
66
|
requirements:
|
53
67
|
- - "~>"
|
54
68
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
69
|
+
version: '6.0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
71
|
name: openssl
|
58
72
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,6 +134,8 @@ files:
|
|
120
134
|
- eth.gemspec
|
121
135
|
- lib/eth.rb
|
122
136
|
- lib/eth/abi.rb
|
137
|
+
- lib/eth/abi/decoder.rb
|
138
|
+
- lib/eth/abi/encoder.rb
|
123
139
|
- lib/eth/abi/event.rb
|
124
140
|
- lib/eth/abi/type.rb
|
125
141
|
- lib/eth/address.rb
|
@@ -127,7 +143,6 @@ files:
|
|
127
143
|
- lib/eth/chain.rb
|
128
144
|
- lib/eth/client.rb
|
129
145
|
- lib/eth/client/http.rb
|
130
|
-
- lib/eth/client/http_auth.rb
|
131
146
|
- lib/eth/client/ipc.rb
|
132
147
|
- lib/eth/constant.rb
|
133
148
|
- lib/eth/contract.rb
|
data/lib/eth/client/http_auth.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
|
-
#
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
-
# you may not use this file except in compliance with the License.
|
5
|
-
# You may obtain a copy of the License at
|
6
|
-
#
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
-
#
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
-
# See the License for the specific language governing permissions and
|
13
|
-
# limitations under the License.
|
14
|
-
|
15
|
-
require "net/http"
|
16
|
-
|
17
|
-
# Provides the {Eth} module.
|
18
|
-
module Eth
|
19
|
-
|
20
|
-
# Provides an HTTP/S-RPC client with basic authentication.
|
21
|
-
class Client::HttpAuth < Client
|
22
|
-
|
23
|
-
# The host of the HTTP endpoint.
|
24
|
-
attr_reader :host
|
25
|
-
|
26
|
-
# The port of the HTTP endpoint.
|
27
|
-
attr_reader :port
|
28
|
-
|
29
|
-
# The full URI of the HTTP endpoint, including path.
|
30
|
-
attr_reader :uri
|
31
|
-
|
32
|
-
# Attribute indicator for SSL.
|
33
|
-
attr_reader :ssl
|
34
|
-
|
35
|
-
# Attribute for user.
|
36
|
-
attr_reader :user
|
37
|
-
|
38
|
-
# Constructor for the HTTP Client. Should not be used; use
|
39
|
-
# {Client.create} intead.
|
40
|
-
#
|
41
|
-
# @param host [String] an URI pointing to an HTTP RPC-API.
|
42
|
-
def initialize(host)
|
43
|
-
super
|
44
|
-
uri = URI.parse(host)
|
45
|
-
raise ArgumentError, "Unable to parse the HTTP-URI!" unless ["http", "https"].include? uri.scheme
|
46
|
-
@host = uri.host
|
47
|
-
@port = uri.port
|
48
|
-
@ssl = uri.scheme == "https"
|
49
|
-
@user = uri.user
|
50
|
-
@password = uri.password
|
51
|
-
@uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}")
|
52
|
-
end
|
53
|
-
|
54
|
-
# Sends an RPC request to the connected HTTP client.
|
55
|
-
#
|
56
|
-
# @param payload [Hash] the RPC request parameters.
|
57
|
-
# @return [String] a JSON-encoded response.
|
58
|
-
def send(payload)
|
59
|
-
http = Net::HTTP.new(@host, @port)
|
60
|
-
http.use_ssl = @ssl
|
61
|
-
header = { "Content-Type" => "application/json" }
|
62
|
-
request = Net::HTTP::Post.new(@uri, header)
|
63
|
-
request.body = payload
|
64
|
-
response = http.request(request)
|
65
|
-
response.body
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
# Attribute for password.
|
72
|
-
attr_reader :password
|
73
|
-
end
|