eth 0.5.10 → 0.5.12
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 +4 -4
- data/.github/workflows/codeql.yml +5 -5
- data/.github/workflows/docs.yml +3 -3
- data/.github/workflows/spec.yml +8 -4
- data/CHANGELOG.md +52 -0
- data/eth.gemspec +4 -1
- data/lib/eth/abi/decoder.rb +154 -0
- data/lib/eth/abi/encoder.rb +304 -0
- data/lib/eth/abi/event.rb +37 -5
- data/lib/eth/abi/type.rb +10 -2
- data/lib/eth/abi.rb +10 -385
- data/lib/eth/api.rb +45 -52
- 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 +71 -133
- data/lib/eth/contract/event.rb +16 -2
- data/lib/eth/contract.rb +11 -1
- data/lib/eth/eip712.rb +5 -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 +21 -6
- data/lib/eth/client/http_auth.rb +0 -73
data/lib/eth/client/http.rb
CHANGED
@@ -17,7 +17,7 @@ require "net/http"
|
|
17
17
|
# Provides the {Eth} module.
|
18
18
|
module Eth
|
19
19
|
|
20
|
-
# Provides an HTTP/S-RPC client.
|
20
|
+
# Provides an HTTP/S-RPC client with basic authentication.
|
21
21
|
class Client::Http < Client
|
22
22
|
|
23
23
|
# The host of the HTTP endpoint.
|
@@ -32,8 +32,11 @@ module Eth
|
|
32
32
|
# Attribute indicator for SSL.
|
33
33
|
attr_reader :ssl
|
34
34
|
|
35
|
+
# Attribute for user.
|
36
|
+
attr_reader :user
|
37
|
+
|
35
38
|
# Constructor for the HTTP Client. Should not be used; use
|
36
|
-
# {Client.create}
|
39
|
+
# {Client.create} instead.
|
37
40
|
#
|
38
41
|
# @param host [String] an URI pointing to an HTTP RPC-API.
|
39
42
|
def initialize(host)
|
@@ -43,14 +46,20 @@ module Eth
|
|
43
46
|
@host = uri.host
|
44
47
|
@port = uri.port
|
45
48
|
@ssl = uri.scheme == "https"
|
46
|
-
|
49
|
+
if !(uri.user.nil? && uri.password.nil?)
|
50
|
+
@user = uri.user
|
51
|
+
@password = uri.password
|
52
|
+
@uri = URI("#{uri.scheme}://#{uri.user}:#{uri.password}@#{@host}:#{@port}#{uri.path}")
|
53
|
+
else
|
54
|
+
@uri = URI("#{uri.scheme}://#{@host}:#{@port}#{uri.path}")
|
55
|
+
end
|
47
56
|
end
|
48
57
|
|
49
58
|
# Sends an RPC request to the connected HTTP client.
|
50
59
|
#
|
51
60
|
# @param payload [Hash] the RPC request parameters.
|
52
61
|
# @return [String] a JSON-encoded response.
|
53
|
-
def
|
62
|
+
def send_request(payload)
|
54
63
|
http = Net::HTTP.new(@host, @port)
|
55
64
|
http.use_ssl = @ssl
|
56
65
|
header = { "Content-Type" => "application/json" }
|
@@ -60,4 +69,9 @@ module Eth
|
|
60
69
|
response.body
|
61
70
|
end
|
62
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Attribute for password.
|
76
|
+
attr_reader :password
|
63
77
|
end
|
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
@@ -25,7 +25,7 @@ module Eth
|
|
25
25
|
# The connected network's chain ID.
|
26
26
|
attr_reader :chain_id
|
27
27
|
|
28
|
-
# The connected network's client
|
28
|
+
# The connected network's client default account.
|
29
29
|
attr_accessor :default_account
|
30
30
|
|
31
31
|
# The default transaction max priority fee per gas in Wei, defaults to {Tx::DEFAULT_PRIORITY_FEE}.
|
@@ -34,8 +34,8 @@ 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
|
38
|
-
attr_accessor :
|
37
|
+
# The block number used for archive calls.
|
38
|
+
attr_accessor :block_number
|
39
39
|
|
40
40
|
# A custom error type if a contract interaction fails.
|
41
41
|
class ContractExecutionError < StandardError; end
|
@@ -43,19 +43,16 @@ module Eth
|
|
43
43
|
# Creates a new RPC-Client, either by providing an HTTP/S host or
|
44
44
|
# an IPC path. Supports basic authentication with username and password.
|
45
45
|
#
|
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.
|
46
|
+
# **Note**, this sets the folling gas defaults: {Tx::DEFAULT_PRIORITY_FEE}
|
47
|
+
# and {Tx::DEFAULT_GAS_PRICE. Use {#max_priority_fee_per_gas} and
|
48
|
+
# {#max_fee_per_gas} to set custom values prior to submitting transactions.
|
50
49
|
#
|
51
50
|
# @param host [String] either an HTTP/S host or an IPC path.
|
52
51
|
# @return [Eth::Client::Ipc] an IPC client.
|
53
|
-
# @return [Eth::Client::HttpAuth] an HTTP client with basic authentication.
|
54
52
|
# @return [Eth::Client::Http] an HTTP client.
|
55
53
|
# @raise [ArgumentError] in case it cannot determine the client type.
|
56
54
|
def self.create(host)
|
57
55
|
return Client::Ipc.new host if host.end_with? ".ipc"
|
58
|
-
return Client::HttpAuth.new host if Regexp.new(":.*@.*:", Regexp::IGNORECASE).match host
|
59
56
|
return Client::Http.new host if host.start_with? "http"
|
60
57
|
raise ArgumentError, "Unable to detect client type!"
|
61
58
|
end
|
@@ -66,18 +63,17 @@ module Eth
|
|
66
63
|
@id = 0
|
67
64
|
@max_priority_fee_per_gas = Tx::DEFAULT_PRIORITY_FEE
|
68
65
|
@max_fee_per_gas = Tx::DEFAULT_GAS_PRICE
|
69
|
-
@gas_limit = Tx::DEFAULT_GAS_LIMIT
|
70
66
|
end
|
71
67
|
|
72
|
-
# Gets the default account (
|
68
|
+
# Gets the default account (first account) of the connected client.
|
73
69
|
#
|
74
70
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
75
71
|
# any accounts.
|
76
72
|
#
|
77
|
-
# @return [Eth::Address] the
|
73
|
+
# @return [Eth::Address] the default account address.
|
78
74
|
def default_account
|
79
|
-
raise ArgumentError, "The default account is not available on remote connections!" unless local?
|
80
|
-
@default_account ||= Address.new
|
75
|
+
raise ArgumentError, "The default account is not available on remote connections!" unless local? || @default_account
|
76
|
+
@default_account ||= Address.new eth_accounts["result"].first
|
81
77
|
end
|
82
78
|
|
83
79
|
# Gets the chain ID of the connected network.
|
@@ -115,7 +111,7 @@ module Eth
|
|
115
111
|
end
|
116
112
|
|
117
113
|
# Simply transfer Ether to an account and waits for it to be mined.
|
118
|
-
# Uses `
|
114
|
+
# Uses `eth_accounts` and external signer if no sender key is
|
119
115
|
# provided.
|
120
116
|
#
|
121
117
|
# See {#transfer} for params and overloads.
|
@@ -126,7 +122,7 @@ module Eth
|
|
126
122
|
end
|
127
123
|
|
128
124
|
# Simply transfer Ether to an account without any call data or
|
129
|
-
# access lists attached. Uses `
|
125
|
+
# access lists attached. Uses `eth_accounts` and external signer
|
130
126
|
# if no sender key is provided.
|
131
127
|
#
|
132
128
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
@@ -146,41 +142,10 @@ module Eth
|
|
146
142
|
params = {
|
147
143
|
value: amount,
|
148
144
|
to: destination,
|
149
|
-
gas_limit:
|
145
|
+
gas_limit: Tx::DEFAULT_GAS_LIMIT,
|
150
146
|
chain_id: chain_id,
|
151
147
|
}
|
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
|
148
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
184
149
|
end
|
185
150
|
|
186
151
|
# Transfers a token that implements the ERC20 `transfer()` interface.
|
@@ -207,7 +172,7 @@ module Eth
|
|
207
172
|
# @param amount [Integer] the transfer amount (mind the `decimals()`).
|
208
173
|
# @param **sender_key [Eth::Key] the sender private key.
|
209
174
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
210
|
-
# @param **gas_limit [Integer] optional gas limit override for
|
175
|
+
# @param **gas_limit [Integer] optional gas limit override for the transfer.
|
211
176
|
# @param **nonce [Integer] optional specific nonce for transaction.
|
212
177
|
# @param **tx_value [Integer] optional transaction value field filling.
|
213
178
|
# @return [Object] returns the result of the transaction.
|
@@ -217,7 +182,7 @@ module Eth
|
|
217
182
|
end
|
218
183
|
|
219
184
|
# Deploys a contract and waits for it to be mined. Uses
|
220
|
-
# `
|
185
|
+
# `eth_accounts` or external signer if no sender key is provided.
|
221
186
|
#
|
222
187
|
# See {#deploy} for params and overloads.
|
223
188
|
#
|
@@ -228,7 +193,7 @@ module Eth
|
|
228
193
|
contract.address = Address.new(addr).to_s
|
229
194
|
end
|
230
195
|
|
231
|
-
# Deploys a contract. Uses `
|
196
|
+
# Deploys a contract. Uses `eth_accounts` or external signer
|
232
197
|
# if no sender key is provided.
|
233
198
|
#
|
234
199
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
@@ -266,37 +231,7 @@ module Eth
|
|
266
231
|
chain_id: chain_id,
|
267
232
|
data: data,
|
268
233
|
}
|
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
|
234
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
300
235
|
end
|
301
236
|
|
302
237
|
# Calls a contract function without executing it
|
@@ -315,7 +250,6 @@ module Eth
|
|
315
250
|
# @param *args optional function arguments.
|
316
251
|
# @param **sender_key [Eth::Key] the sender private key.
|
317
252
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
318
|
-
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
319
253
|
# @return [Object] returns the result of the call.
|
320
254
|
def call(contract, function, *args, **kwargs)
|
321
255
|
func = contract.functions.select { |func| func.name == function }
|
@@ -328,9 +262,9 @@ module Eth
|
|
328
262
|
end
|
329
263
|
output = call_raw(contract, selected_func, *args, **kwargs)
|
330
264
|
if output&.length == 1
|
331
|
-
|
265
|
+
output[0]
|
332
266
|
else
|
333
|
-
|
267
|
+
output
|
334
268
|
end
|
335
269
|
end
|
336
270
|
|
@@ -354,7 +288,7 @@ module Eth
|
|
354
288
|
# @param **sender_key [Eth::Key] the sender private key.
|
355
289
|
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
356
290
|
# @param **address [Eth::Address] contract address.
|
357
|
-
# @param **gas_limit [Integer] optional gas limit override for
|
291
|
+
# @param **gas_limit [Integer] optional gas limit override for transacting with the contract.
|
358
292
|
# @param **nonce [Integer] optional specific nonce for transaction.
|
359
293
|
# @param **tx_value [Integer] optional transaction value field filling.
|
360
294
|
# @return [Object] returns the result of the transaction.
|
@@ -362,7 +296,7 @@ module Eth
|
|
362
296
|
gas_limit = if kwargs[:gas_limit]
|
363
297
|
kwargs[:gas_limit]
|
364
298
|
else
|
365
|
-
Tx.estimate_intrinsic_gas(contract.bin)
|
299
|
+
Tx.estimate_intrinsic_gas(contract.bin)
|
366
300
|
end
|
367
301
|
fun = contract.functions.select { |func| func.name == function }[0]
|
368
302
|
params = {
|
@@ -372,37 +306,7 @@ module Eth
|
|
372
306
|
to: kwargs[:address] || contract.address,
|
373
307
|
data: call_payload(fun, args),
|
374
308
|
}
|
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
|
309
|
+
send_transaction(params, kwargs[:legacy], kwargs[:sender_key], kwargs[:nonce])
|
406
310
|
end
|
407
311
|
|
408
312
|
# Executes a contract function with a transaction and waits for it
|
@@ -437,7 +341,7 @@ module Eth
|
|
437
341
|
signature = Util.hex_to_bin signature if Util.hex? signature
|
438
342
|
magic = Util.hex_to_bin magic if Util.hex? magic
|
439
343
|
result = call(contract, "isValidSignature", hash, signature)
|
440
|
-
|
344
|
+
result === magic
|
441
345
|
end
|
442
346
|
|
443
347
|
# Gives control over resetting the RPC request ID back to zero.
|
@@ -496,11 +400,45 @@ module Eth
|
|
496
400
|
# Allows to determine if we work with a local connectoin
|
497
401
|
def local?
|
498
402
|
if self.instance_of? Eth::Client::Ipc
|
499
|
-
|
403
|
+
true
|
500
404
|
elsif self.host === "127.0.0.1" || self.host === "localhost"
|
501
|
-
|
405
|
+
true
|
502
406
|
else
|
503
|
-
|
407
|
+
false
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# Prepares a transaction to be send for the given params.
|
412
|
+
def send_transaction(params, legacy, key, nonce)
|
413
|
+
if legacy
|
414
|
+
params.merge!({ gas_price: max_fee_per_gas })
|
415
|
+
else
|
416
|
+
params.merge!({
|
417
|
+
priority_fee: max_priority_fee_per_gas,
|
418
|
+
max_gas_fee: max_fee_per_gas,
|
419
|
+
})
|
420
|
+
end
|
421
|
+
unless key.nil?
|
422
|
+
|
423
|
+
# use the provided key as sender and signer
|
424
|
+
params.merge!({
|
425
|
+
from: key.address,
|
426
|
+
nonce: nonce || get_nonce(key.address),
|
427
|
+
})
|
428
|
+
tx = Eth::Tx.new(params)
|
429
|
+
tx.sign key
|
430
|
+
eth_send_raw_transaction(tx.hex)["result"]
|
431
|
+
else
|
432
|
+
|
433
|
+
# do not allow accessing accounts on remote connections
|
434
|
+
raise ArgumentError, "The default account is not available on remote connections, please provide a :sender_key!" unless local?
|
435
|
+
|
436
|
+
# use the default account as sender and external signer
|
437
|
+
params.merge!({
|
438
|
+
from: default_account,
|
439
|
+
nonce: nonce || get_nonce(default_account),
|
440
|
+
})
|
441
|
+
eth_send_transaction(params)["result"]
|
504
442
|
end
|
505
443
|
end
|
506
444
|
|
@@ -534,16 +472,17 @@ module Eth
|
|
534
472
|
|
535
473
|
# Prepares parameters and sends the command to the client.
|
536
474
|
def send_command(command, args)
|
537
|
-
|
475
|
+
@block_number ||= "latest"
|
476
|
+
args << block_number if ["eth_getBalance", "eth_call"].include? command
|
538
477
|
payload = {
|
539
478
|
jsonrpc: "2.0",
|
540
479
|
method: command,
|
541
480
|
params: marshal(args),
|
542
481
|
id: next_id,
|
543
482
|
}
|
544
|
-
output = JSON.parse(
|
483
|
+
output = JSON.parse(send_request(payload.to_json))
|
545
484
|
raise IOError, output["error"]["message"] unless output["error"].nil?
|
546
|
-
|
485
|
+
output
|
547
486
|
end
|
548
487
|
|
549
488
|
# Increments the request id.
|
@@ -564,18 +503,18 @@ module Eth
|
|
564
503
|
def marshal(params)
|
565
504
|
params = params.dup
|
566
505
|
if params.is_a? Array
|
567
|
-
|
506
|
+
params.map! { |param| marshal(param) }
|
568
507
|
elsif params.is_a? Hash
|
569
508
|
params = camelize!(params)
|
570
|
-
|
509
|
+
params.transform_values! { |param| marshal(param) }
|
571
510
|
elsif params.is_a? Numeric
|
572
|
-
|
511
|
+
Util.prefix_hex "#{params.to_i.to_s(16)}"
|
573
512
|
elsif params.is_a? Address
|
574
|
-
|
513
|
+
params.to_s
|
575
514
|
elsif Util.hex? params
|
576
|
-
|
515
|
+
Util.prefix_hex params
|
577
516
|
else
|
578
|
-
|
517
|
+
params
|
579
518
|
end
|
580
519
|
end
|
581
520
|
end
|
@@ -583,5 +522,4 @@ end
|
|
583
522
|
|
584
523
|
# Load the client/* libraries
|
585
524
|
require "eth/client/http"
|
586
|
-
require "eth/client/http_auth"
|
587
525
|
require "eth/client/ipc"
|
data/lib/eth/contract/event.rb
CHANGED
@@ -26,9 +26,11 @@ module Eth
|
|
26
26
|
# @param data [Hash] contract event data.
|
27
27
|
def initialize(data)
|
28
28
|
@name = data["name"]
|
29
|
-
@input_types = data["inputs"].collect
|
29
|
+
@input_types = data["inputs"].collect do |x|
|
30
|
+
type_name x
|
31
|
+
end
|
30
32
|
@inputs = data["inputs"].collect { |x| x["name"] }
|
31
|
-
@event_string =
|
33
|
+
@event_string = Abi::Event.signature(data)
|
32
34
|
@signature = Digest::Keccak.hexdigest(@event_string, 256)
|
33
35
|
end
|
34
36
|
|
@@ -38,5 +40,17 @@ module Eth
|
|
38
40
|
def set_address(address)
|
39
41
|
@address = address.nil? ? nil : Eth::Address.new(address).address
|
40
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def type_name(x)
|
47
|
+
type = x["type"]
|
48
|
+
case type
|
49
|
+
when "tuple"
|
50
|
+
"(#{x["components"].collect { |c| type_name(c) }.join(",")})"
|
51
|
+
else
|
52
|
+
type
|
53
|
+
end
|
54
|
+
end
|
41
55
|
end
|
42
56
|
end
|
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/eip712.rb
CHANGED
@@ -114,9 +114,13 @@ module Eth
|
|
114
114
|
value = data[field[:name].to_sym]
|
115
115
|
type = field[:type]
|
116
116
|
raise NotImplementedError, "Arrays currently unimplemented for EIP-712." if type.end_with? "]"
|
117
|
-
if type == "string"
|
117
|
+
if type == "string"
|
118
118
|
encoded_types.push "bytes32"
|
119
119
|
encoded_values.push Util.keccak256 value
|
120
|
+
elsif type == "bytes"
|
121
|
+
encoded_types.push "bytes32"
|
122
|
+
value = Util.hex_to_bin value
|
123
|
+
encoded_values.push Util.keccak256 value
|
120
124
|
elsif !types[type.to_sym].nil?
|
121
125
|
encoded_types.push "bytes32"
|
122
126
|
value = encode_data type, value, types
|
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 = 12.freeze
|
26
|
+
|
27
|
+
# Defines the version string of the {Eth} module.
|
28
|
+
VERSION = [MAJOR, MINOR, PATCH].join(".").freeze
|
20
29
|
end
|