eth 0.5.8 → 0.5.10
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/spec.yml +2 -1
- data/CHANGELOG.md +32 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +61 -0
- data/LICENSE.txt +1 -1
- data/README.md +6 -2
- data/SECURITY.md +24 -0
- data/abi/ens_registry.json +436 -0
- data/abi/ens_resolver.json +885 -0
- data/lib/eth/abi/event.rb +1 -1
- data/lib/eth/abi/type.rb +46 -12
- data/lib/eth/abi.rb +72 -14
- data/lib/eth/address.rb +2 -2
- data/lib/eth/api.rb +1 -1
- data/lib/eth/chain.rb +19 -7
- data/lib/eth/client/http.rb +1 -1
- data/lib/eth/client/http_auth.rb +1 -1
- data/lib/eth/client/ipc.rb +1 -1
- data/lib/eth/client.rb +118 -16
- data/lib/eth/constant.rb +1 -1
- data/lib/eth/contract/event.rb +1 -1
- data/lib/eth/contract/function.rb +2 -2
- data/lib/eth/contract/function_input.rb +7 -2
- data/lib/eth/contract/function_output.rb +1 -1
- data/lib/eth/contract/initializer.rb +1 -1
- data/lib/eth/contract.rb +1 -1
- data/lib/eth/eip712.rb +2 -2
- data/lib/eth/ens/coin_type.rb +50 -0
- data/lib/eth/ens/resolver.rb +68 -24
- data/lib/eth/ens.rb +28 -0
- data/lib/eth/key/decrypter.rb +1 -1
- data/lib/eth/key/encrypter.rb +1 -1
- data/lib/eth/key.rb +5 -5
- data/lib/eth/rlp/decoder.rb +2 -2
- data/lib/eth/rlp/encoder.rb +3 -3
- data/lib/eth/rlp/sedes/big_endian_int.rb +1 -1
- data/lib/eth/rlp/sedes/binary.rb +2 -2
- data/lib/eth/rlp/sedes/list.rb +5 -5
- data/lib/eth/rlp/sedes.rb +4 -4
- data/lib/eth/rlp.rb +1 -1
- data/lib/eth/signature.rb +4 -4
- data/lib/eth/solidity.rb +5 -3
- data/lib/eth/tx/eip1559.rb +3 -3
- data/lib/eth/tx/eip2930.rb +3 -3
- data/lib/eth/tx/legacy.rb +3 -3
- data/lib/eth/tx.rb +5 -5
- data/lib/eth/unit.rb +1 -1
- data/lib/eth/util.rb +14 -14
- data/lib/eth/version.rb +2 -2
- data/lib/eth.rb +2 -2
- metadata +9 -3
- data/abis/ens.json +0 -422
data/lib/eth/client.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -37,6 +37,9 @@ module Eth
|
|
37
37
|
# The default gas limit for the transaction, defaults to {Tx::DEFAULT_GAS_LIMIT}.
|
38
38
|
attr_accessor :gas_limit
|
39
39
|
|
40
|
+
# A custom error type if a contract interaction fails.
|
41
|
+
class ContractExecutionError < StandardError; end
|
42
|
+
|
40
43
|
# Creates a new RPC-Client, either by providing an HTTP/S host or
|
41
44
|
# an IPC path. Supports basic authentication with username and password.
|
42
45
|
#
|
@@ -73,6 +76,7 @@ module Eth
|
|
73
76
|
#
|
74
77
|
# @return [Eth::Address] the coinbase account address.
|
75
78
|
def default_account
|
79
|
+
raise ArgumentError, "The default account is not available on remote connections!" unless local?
|
76
80
|
@default_account ||= Address.new eth_coinbase["result"]
|
77
81
|
end
|
78
82
|
|
@@ -99,6 +103,17 @@ module Eth
|
|
99
103
|
eth_get_transaction_count(address, "pending")["result"].to_i 16
|
100
104
|
end
|
101
105
|
|
106
|
+
# Resolves an ENS name to an Ethereum address on the connected chain.
|
107
|
+
#
|
108
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
109
|
+
# @param registry [String] the address for the ENS registry.
|
110
|
+
# @param coin_type [Integer] the coin type as per EIP-2304.
|
111
|
+
# @return [Eth::Address] the Ethereum address resolved from the ENS record.
|
112
|
+
def resolve_ens(ens_name, registry = Ens::DEFAULT_ADDRESS, coin_type = Ens::CoinType::ETHEREUM)
|
113
|
+
ens = Ens::Resolver.new(self, registry)
|
114
|
+
ens.resolve(ens_name, coin_type)
|
115
|
+
end
|
116
|
+
|
102
117
|
# Simply transfer Ether to an account and waits for it to be mined.
|
103
118
|
# Uses `eth_coinbase` and external signer if no sender key is
|
104
119
|
# provided.
|
@@ -115,7 +130,7 @@ module Eth
|
|
115
130
|
# if no sender key is provided.
|
116
131
|
#
|
117
132
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
118
|
-
# any accounts. Provide a `sender_key
|
133
|
+
# any accounts. Provide a `sender_key:` if you experience issues.
|
119
134
|
#
|
120
135
|
# @overload transfer(destination, amount)
|
121
136
|
# @param destination [Eth::Address] the destination address.
|
@@ -156,6 +171,9 @@ module Eth
|
|
156
171
|
return eth_send_raw_transaction(tx.hex)["result"]
|
157
172
|
else
|
158
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
|
+
|
159
177
|
# use the default account as sender and external signer
|
160
178
|
params.merge!({
|
161
179
|
from: default_account,
|
@@ -165,6 +183,39 @@ module Eth
|
|
165
183
|
end
|
166
184
|
end
|
167
185
|
|
186
|
+
# Transfers a token that implements the ERC20 `transfer()` interface.
|
187
|
+
#
|
188
|
+
# See {#transfer_erc20} for params and overloads.
|
189
|
+
#
|
190
|
+
# @return [Object] returns the result of the transaction.
|
191
|
+
def transfer_erc20_and_wait(erc20_contract, destination, amount, **kwargs)
|
192
|
+
transact_and_wait(erc20_contract, "transfer", destination, amount, **kwargs)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Transfers a token that implements the ERC20 `transfer()` interface.
|
196
|
+
#
|
197
|
+
# **Note**, that many remote providers (e.g., Infura) do not provide
|
198
|
+
# any accounts. Provide a `sender_key:` if you experience issues.
|
199
|
+
#
|
200
|
+
# @overload transfer_erc20(erc20_contract, destination, amount)
|
201
|
+
# @param erc20_contract [Eth::Contract] the ERC20 contract to write to.
|
202
|
+
# @param destination [Eth::Address] the destination address.
|
203
|
+
# @param amount [Integer] the transfer amount (mind the `decimals()`).
|
204
|
+
# @overload transfer_erc20(erc20_contract, destination, amount, **kwargs)
|
205
|
+
# @param erc20_contract [Eth::Contract] the ERC20 contract to write to.
|
206
|
+
# @param destination [Eth::Address] the destination address.
|
207
|
+
# @param amount [Integer] the transfer amount (mind the `decimals()`).
|
208
|
+
# @param **sender_key [Eth::Key] the sender private key.
|
209
|
+
# @param **legacy [Boolean] enables legacy transactions (pre-EIP-1559).
|
210
|
+
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
211
|
+
# @param **nonce [Integer] optional specific nonce for transaction.
|
212
|
+
# @param **tx_value [Integer] optional transaction value field filling.
|
213
|
+
# @return [Object] returns the result of the transaction.
|
214
|
+
def transfer_erc20(erc20_contract, destination, amount, **kwargs)
|
215
|
+
destination = destination.to_s if destination.instance_of? Eth::Address
|
216
|
+
transact(erc20_contract, "transfer", destination, amount, **kwargs)
|
217
|
+
end
|
218
|
+
|
168
219
|
# Deploys a contract and waits for it to be mined. Uses
|
169
220
|
# `eth_coinbase` or external signer if no sender key is provided.
|
170
221
|
#
|
@@ -181,7 +232,7 @@ module Eth
|
|
181
232
|
# if no sender key is provided.
|
182
233
|
#
|
183
234
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
184
|
-
# any accounts. Provide a `sender_key
|
235
|
+
# any accounts. Provide a `sender_key:` if you experience issues.
|
185
236
|
#
|
186
237
|
# @overload deploy(contract)
|
187
238
|
# @param contract [Eth::Contract] contracts to deploy.
|
@@ -235,6 +286,10 @@ module Eth
|
|
235
286
|
tx.sign kwargs[:sender_key]
|
236
287
|
return eth_send_raw_transaction(tx.hex)["result"]
|
237
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
|
+
|
238
293
|
# Uses the default account as sender and external signer
|
239
294
|
params.merge!({
|
240
295
|
from: default_account,
|
@@ -263,9 +318,15 @@ module Eth
|
|
263
318
|
# @param **gas_limit [Integer] optional gas limit override for deploying the contract.
|
264
319
|
# @return [Object] returns the result of the call.
|
265
320
|
def call(contract, function, *args, **kwargs)
|
266
|
-
func = contract.functions.select { |func| func.name == function }
|
267
|
-
raise ArgumentError, "this function does not exist!" if func.nil?
|
268
|
-
|
321
|
+
func = contract.functions.select { |func| func.name == function }
|
322
|
+
raise ArgumentError, "this function does not exist!" if func.nil? || func.size === 0
|
323
|
+
selected_func = func.first
|
324
|
+
func.each do |f|
|
325
|
+
if f.inputs.size === args.size
|
326
|
+
selected_func = f
|
327
|
+
end
|
328
|
+
end
|
329
|
+
output = call_raw(contract, selected_func, *args, **kwargs)
|
269
330
|
if output&.length == 1
|
270
331
|
return output[0]
|
271
332
|
else
|
@@ -277,7 +338,7 @@ module Eth
|
|
277
338
|
# contract read/write).
|
278
339
|
#
|
279
340
|
# **Note**, that many remote providers (e.g., Infura) do not provide
|
280
|
-
# any accounts. Provide a `sender_key
|
341
|
+
# any accounts. Provide a `sender_key:` if you experience issues.
|
281
342
|
#
|
282
343
|
# @overload transact(contract, function)
|
283
344
|
# @param contract [Eth::Contract] the subject contract to write to.
|
@@ -331,6 +392,10 @@ module Eth
|
|
331
392
|
tx.sign kwargs[:sender_key]
|
332
393
|
return eth_send_raw_transaction(tx.hex)["result"]
|
333
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
|
+
|
334
399
|
# use the default account as sender and external signer
|
335
400
|
params.merge!({
|
336
401
|
from: default_account,
|
@@ -345,9 +410,15 @@ module Eth
|
|
345
410
|
#
|
346
411
|
# See {#transact} for params and overloads.
|
347
412
|
#
|
413
|
+
# @raise [Client::ContractExecutionError] if the execution fails.
|
348
414
|
# @return [Object] returns the result of the transaction.
|
349
415
|
def transact_and_wait(contract, function, *args, **kwargs)
|
350
|
-
|
416
|
+
begin
|
417
|
+
hash = wait_for_tx(transact(contract, function, *args, **kwargs))
|
418
|
+
return hash if tx_succeeded? hash
|
419
|
+
rescue IOError => e
|
420
|
+
raise ContractExecutionError, e
|
421
|
+
end
|
351
422
|
end
|
352
423
|
|
353
424
|
# Provides an interface to call `isValidSignature` as per EIP-1271 on a given
|
@@ -362,9 +433,9 @@ module Eth
|
|
362
433
|
# @raise [ArgumentError] in case the contract cannot be called yet.
|
363
434
|
def is_valid_signature(contract, hash, signature, magic = "1626ba7e")
|
364
435
|
raise ArgumentError, "Contract not deployed yet." if contract.address.nil?
|
365
|
-
hash = Util.hex_to_bin hash if Util.
|
366
|
-
signature = Util.hex_to_bin signature if Util.
|
367
|
-
magic = Util.hex_to_bin magic if Util.
|
436
|
+
hash = Util.hex_to_bin hash if Util.hex? hash
|
437
|
+
signature = Util.hex_to_bin signature if Util.hex? signature
|
438
|
+
magic = Util.hex_to_bin magic if Util.hex? magic
|
368
439
|
result = call(contract, "isValidSignature", hash, signature)
|
369
440
|
return result === magic
|
370
441
|
end
|
@@ -377,15 +448,24 @@ module Eth
|
|
377
448
|
@id = 0
|
378
449
|
end
|
379
450
|
|
380
|
-
#
|
451
|
+
# Checks whether a transaction is mined or not.
|
381
452
|
#
|
382
453
|
# @param hash [String] the transaction hash.
|
383
454
|
# @return [Boolean] true if included in a block.
|
384
|
-
def
|
455
|
+
def tx_mined?(hash)
|
385
456
|
mined_tx = eth_get_transaction_by_hash hash
|
386
457
|
!mined_tx.nil? && !mined_tx["result"].nil? && !mined_tx["result"]["blockNumber"].nil?
|
387
458
|
end
|
388
459
|
|
460
|
+
# Checks whether a contract transaction succeeded or not.
|
461
|
+
#
|
462
|
+
# @param hash [String] the transaction hash.
|
463
|
+
# @return [Boolean] true if status is success.
|
464
|
+
def tx_succeeded?(hash)
|
465
|
+
tx_receipt = eth_get_transaction_receipt(hash)
|
466
|
+
!tx_receipt.nil? && !tx_receipt["result"].nil? && tx_receipt["result"]["status"] == "0x1"
|
467
|
+
end
|
468
|
+
|
389
469
|
# Waits for an transaction to be mined by the connected chain.
|
390
470
|
#
|
391
471
|
# @param hash [String] the transaction hash.
|
@@ -397,7 +477,7 @@ module Eth
|
|
397
477
|
retry_rate = 0.1
|
398
478
|
loop do
|
399
479
|
raise Timeout::Error if ((Time.now - start_time) > timeout)
|
400
|
-
return hash if
|
480
|
+
return hash if tx_mined? hash
|
401
481
|
sleep retry_rate
|
402
482
|
end
|
403
483
|
end
|
@@ -413,6 +493,17 @@ module Eth
|
|
413
493
|
|
414
494
|
private
|
415
495
|
|
496
|
+
# Allows to determine if we work with a local connectoin
|
497
|
+
def local?
|
498
|
+
if self.instance_of? Eth::Client::Ipc
|
499
|
+
return true
|
500
|
+
elsif self.host === "127.0.0.1" || self.host === "localhost"
|
501
|
+
return true
|
502
|
+
else
|
503
|
+
return false
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
416
507
|
# Non-transactional function call called from call().
|
417
508
|
# @see https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call
|
418
509
|
def call_raw(contract, func, *args, **kwargs)
|
@@ -430,7 +521,7 @@ module Eth
|
|
430
521
|
|
431
522
|
# Encodes function call payloads.
|
432
523
|
def call_payload(fun, args)
|
433
|
-
types = fun.inputs.map
|
524
|
+
types = fun.inputs.map(&:parsed_type)
|
434
525
|
encoded_str = Util.bin_to_hex(Eth::Abi.encode(types, args))
|
435
526
|
Util.prefix_hex(fun.signature + (encoded_str.empty? ? "0" * 64 : encoded_str))
|
436
527
|
end
|
@@ -460,17 +551,28 @@ module Eth
|
|
460
551
|
@id += 1
|
461
552
|
end
|
462
553
|
|
554
|
+
# expects Hash object
|
555
|
+
def camelize!(params)
|
556
|
+
params.transform_keys! do |k|
|
557
|
+
k = k.to_s.split(/_/).map(&:capitalize).join
|
558
|
+
k[0] = k[0].downcase
|
559
|
+
k.to_sym
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
463
563
|
# Recursively marshals all request parameters.
|
464
564
|
def marshal(params)
|
565
|
+
params = params.dup
|
465
566
|
if params.is_a? Array
|
466
567
|
return params.map! { |param| marshal(param) }
|
467
568
|
elsif params.is_a? Hash
|
569
|
+
params = camelize!(params)
|
468
570
|
return params.transform_values! { |param| marshal(param) }
|
469
571
|
elsif params.is_a? Numeric
|
470
572
|
return Util.prefix_hex "#{params.to_i.to_s(16)}"
|
471
573
|
elsif params.is_a? Address
|
472
574
|
return params.to_s
|
473
|
-
elsif Util.
|
575
|
+
elsif Util.hex? params
|
474
576
|
return Util.prefix_hex params
|
475
577
|
else
|
476
578
|
return params
|
data/lib/eth/constant.rb
CHANGED
data/lib/eth/contract/event.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -43,7 +43,7 @@ module Eth
|
|
43
43
|
# @param inputs [Array<Eth::Contract::FunctionInput>] function input class list.
|
44
44
|
# @return [String] function string.
|
45
45
|
def self.calc_signature(name, inputs)
|
46
|
-
"#{name}(#{inputs.
|
46
|
+
"#{name}(#{inputs.map { |x| x.parsed_type.to_s }.join(",")})"
|
47
47
|
end
|
48
48
|
|
49
49
|
# Encodes a function signature.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -26,7 +26,7 @@ module Eth
|
|
26
26
|
# @param data [Hash] contract abi data.
|
27
27
|
def initialize(data)
|
28
28
|
@raw_type = data["type"]
|
29
|
-
@type = Eth::Abi::Type.parse(data["type"])
|
29
|
+
@type = Eth::Abi::Type.parse(data["type"], data["components"])
|
30
30
|
@name = data["name"]
|
31
31
|
end
|
32
32
|
|
@@ -34,5 +34,10 @@ module Eth
|
|
34
34
|
def type
|
35
35
|
@type.base_type + @type.sub_type + @type.dimensions.map { |dimension| "[#{dimension > 0 ? dimension : ""}]" }.join("")
|
36
36
|
end
|
37
|
+
|
38
|
+
# Returns parsed types.
|
39
|
+
def parsed_type
|
40
|
+
@type
|
41
|
+
end
|
37
42
|
end
|
38
43
|
end
|
data/lib/eth/contract.rb
CHANGED
data/lib/eth/eip712.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -149,7 +149,7 @@ module Eth
|
|
149
149
|
# @return [Array] the data in the data structure we want to hash.
|
150
150
|
# @raise [TypedDataError] if the data fails validation.
|
151
151
|
def enforce_typed_data(data)
|
152
|
-
data = JSON.parse data if Util.
|
152
|
+
data = JSON.parse data if Util.hex? data
|
153
153
|
raise TypedDataError, "Data is missing, try again with data." if data.nil? or data.empty?
|
154
154
|
raise TypedDataError, "Data types are missing." if data[:types].nil? or data[:types].empty?
|
155
155
|
raise TypedDataError, "Data primaryType is missing." if data[:primaryType].nil? or data[:primaryType].empty?
|
@@ -0,0 +1,50 @@
|
|
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
|
+
# Provides the {Eth} module.
|
16
|
+
module Eth
|
17
|
+
# Provides ENS specific functionality
|
18
|
+
# ref: https://ens.domains
|
19
|
+
module Ens
|
20
|
+
# Provides EIP-2304 / SLIP-44 cointypes to resolve ENS addresses.
|
21
|
+
# ref: https://eips.ethereum.org/EIPS/eip-2304
|
22
|
+
module CoinType
|
23
|
+
extend self
|
24
|
+
|
25
|
+
# ENS coin type for Bitcoin.
|
26
|
+
BITCOIN = 0.freeze
|
27
|
+
|
28
|
+
# ENS coin type for Litecoin.
|
29
|
+
LITECOIN = 2.freeze
|
30
|
+
|
31
|
+
# ENS coin type for Dogecoin.
|
32
|
+
DOGECOIN = 3.freeze
|
33
|
+
|
34
|
+
# ENS coin type for Ethereum.
|
35
|
+
ETHEREUM = 60.freeze
|
36
|
+
|
37
|
+
# ENS coin type for Ethereum Classic.
|
38
|
+
ETHEREUM_CLASSIC = 61.freeze
|
39
|
+
|
40
|
+
# ENS coin type for Rootstock.
|
41
|
+
ROOTSTOCK = 137.freeze
|
42
|
+
|
43
|
+
# ENS coin type for Bitcoin Cash.
|
44
|
+
BITCOIN_CASH = 145.freeze
|
45
|
+
|
46
|
+
# ENS coin type for Binance.
|
47
|
+
BINANCE = 714.freeze
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/eth/ens/resolver.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -16,6 +16,7 @@
|
|
16
16
|
|
17
17
|
# Provides the {Eth} module.
|
18
18
|
module Eth
|
19
|
+
|
19
20
|
# Provides ENS specific functionality
|
20
21
|
# ref: https://ens.domains
|
21
22
|
module Ens
|
@@ -23,43 +24,84 @@ module Eth
|
|
23
24
|
# Utility class for resolving ENS names to Ethereum addresses
|
24
25
|
class Resolver
|
25
26
|
|
26
|
-
# The
|
27
|
-
|
27
|
+
# The client instance used to resolve the ENS.
|
28
|
+
attr_accessor :client
|
29
|
+
|
30
|
+
# The address of the ENS registry on the given chain.
|
31
|
+
attr_accessor :registry
|
28
32
|
|
29
|
-
# Create an instance of the ENS Resolver
|
33
|
+
# Create an instance of the ENS Resolver.
|
30
34
|
#
|
31
|
-
# @param client [Eth::Client] The client instance
|
32
|
-
# @param address [String] The address of the ENS
|
35
|
+
# @param client [Eth::Client] The client instance used to resolve the ENS.
|
36
|
+
# @param address [String] The address of the ENS registry on the given chain.
|
33
37
|
def initialize(client, address = DEFAULT_ADDRESS)
|
34
38
|
@client = client
|
35
|
-
@
|
36
|
-
name: "
|
39
|
+
@registry = Eth::Contract.from_abi(
|
40
|
+
name: "ENSRegistryWithFallback",
|
37
41
|
address: address,
|
38
|
-
abi: JSON.parse(File.read(File.join(File.dirname(__FILE__), "../../../
|
42
|
+
abi: JSON.parse(File.read(File.join(File.dirname(__FILE__), "../../../abi/ens_registry.json"))),
|
39
43
|
)
|
40
44
|
end
|
41
45
|
|
42
|
-
# Resolve an ENS name
|
46
|
+
# Resolve an ENS name owner.
|
47
|
+
#
|
48
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
49
|
+
# @return [String] The owner address of the name as a hex string.
|
50
|
+
def owner(ens_name)
|
51
|
+
@client.call(@registry, "owner", namehash(ens_name))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Retrieve the public resolver for the given ENS name.
|
43
55
|
#
|
44
|
-
# @param ens_name [String] The ENS name,
|
45
|
-
# @return [
|
46
|
-
|
47
|
-
|
56
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
57
|
+
# @return [Eth::Contract] The public resolver contract that can be used
|
58
|
+
# to resolve ENS names.
|
59
|
+
def resolver(ens_name)
|
60
|
+
address = @client.call(@registry, "resolver", namehash(ens_name))
|
61
|
+
Eth::Contract.from_abi(
|
62
|
+
name: "ENSPublicResolver",
|
63
|
+
address: address,
|
64
|
+
abi: JSON.parse(File.read(File.join(File.dirname(__FILE__), "../../../abi/ens_resolver.json"))),
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Resolve an ENS name to an address.
|
69
|
+
#
|
70
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
71
|
+
# @return [String] The owner address of the name as a hex string.
|
72
|
+
def resolve(ens_name, coin_type = Ens::CoinType::ETHEREUM)
|
73
|
+
if coin_type === Ens::CoinType::ETHEREUM
|
74
|
+
return @client.call(resolver(ens_name), "addr", namehash(ens_name))
|
75
|
+
elsif coin_type === Ens::CoinType::ETHEREUM_CLASSIC
|
76
|
+
data = @client.call(resolver(ens_name), "addr", namehash(ens_name), coin_type)
|
77
|
+
return Util.bin_to_prefixed_hex data
|
78
|
+
else
|
79
|
+
raise NotImplementedError, "Coin type #{coin_type} not implemented!"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Resolve a text record for a given ENS name.
|
84
|
+
#
|
85
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
86
|
+
# @param key [String] The key for the text record, e.g., `url`.
|
87
|
+
# @return [String] The text record.
|
88
|
+
def text(ens_name, key = "description")
|
89
|
+
@client.call(resolver(ens_name), "text", namehash(ens_name), key)
|
48
90
|
end
|
49
91
|
|
50
92
|
# Generate node for the given domain name
|
51
93
|
# See: https://docs.ens.domains/contract-api-reference/name-processing
|
52
94
|
#
|
53
|
-
# @param ens_name [String] The ENS name,
|
54
|
-
# @return [String] The node as a hex string
|
95
|
+
# @param ens_name [String] The ENS name, e.g., `fancy.eth`.
|
96
|
+
# @return [String] The node as a hex string.
|
55
97
|
def namehash(ens_name)
|
56
|
-
node = ("0" * 64)
|
98
|
+
node = Util.hex_to_bin("0" * 64)
|
57
99
|
name = normalize(ens_name)
|
58
100
|
name.split(".").reverse.each do |label|
|
59
|
-
hash =
|
60
|
-
node =
|
101
|
+
hash = Util.keccak256(label)
|
102
|
+
node = Util.keccak256(node + hash)
|
61
103
|
end
|
62
|
-
|
104
|
+
Util.bin_to_prefixed_hex node
|
63
105
|
end
|
64
106
|
|
65
107
|
# Normalize a string as specified by http://unicode.org/reports/tr46/
|
@@ -67,10 +109,12 @@ module Eth
|
|
67
109
|
# @param input [String] The input string
|
68
110
|
# @return [String] The normalized output string
|
69
111
|
def normalize(input)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
112
|
+
name = input.dup
|
113
|
+
if name.gsub!(/[`~!@#$%^&*()_=+\[\]{}<>,;:'"\/\\|?]/, "").nil?
|
114
|
+
return input.downcase
|
115
|
+
else
|
116
|
+
raise ArgumentError, "Provided ENS name contains illegal characters: #{input}"
|
117
|
+
end
|
74
118
|
end
|
75
119
|
end
|
76
120
|
end
|
data/lib/eth/ens.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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 "eth/ens/coin_type"
|
16
|
+
require "eth/ens/resolver"
|
17
|
+
|
18
|
+
# Provides the {Eth} module.
|
19
|
+
module Eth
|
20
|
+
# Provides ENS specific functionality
|
21
|
+
# ref: https://ens.domains
|
22
|
+
module Ens
|
23
|
+
extend self
|
24
|
+
|
25
|
+
# The default address for ENS, which applies to most chains
|
26
|
+
DEFAULT_ADDRESS = Address.new("0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e").freeze
|
27
|
+
end
|
28
|
+
end
|
data/lib/eth/key/decrypter.rb
CHANGED
data/lib/eth/key/encrypter.rb
CHANGED
data/lib/eth/key.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -51,7 +51,7 @@ module Eth
|
|
51
51
|
unless priv.nil?
|
52
52
|
|
53
53
|
# Converts hex private keys to binary strings.
|
54
|
-
priv = Util.hex_to_bin priv if Util.
|
54
|
+
priv = Util.hex_to_bin priv if Util.hex? priv
|
55
55
|
|
56
56
|
# Creates a keypair from existing private key data.
|
57
57
|
key = ctx.key_pair_from_private_key priv
|
@@ -74,10 +74,10 @@ module Eth
|
|
74
74
|
compact, recovery_id = context.sign_recoverable(@private_key, blob).compact
|
75
75
|
signature = compact.bytes
|
76
76
|
v = Chain.to_v recovery_id, chain_id
|
77
|
-
|
77
|
+
leading_zero = true
|
78
78
|
[v].pack("N").unpack("C*").each do |byte|
|
79
|
-
|
80
|
-
signature.append byte unless
|
79
|
+
leading_zero = false if byte > 0 and leading_zero
|
80
|
+
signature.append byte unless leading_zero and byte === 0
|
81
81
|
end
|
82
82
|
Util.bin_to_hex signature.pack "c*"
|
83
83
|
end
|
data/lib/eth/rlp/decoder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -31,7 +31,7 @@ module Eth
|
|
31
31
|
# @raise [Eth::Rlp::DecodingError] if the input string does not end after
|
32
32
|
# the root item.
|
33
33
|
def perform(rlp)
|
34
|
-
rlp = Util.hex_to_bin rlp if Util.
|
34
|
+
rlp = Util.hex_to_bin rlp if Util.hex? rlp
|
35
35
|
rlp = Util.str_to_bytes rlp
|
36
36
|
begin
|
37
37
|
item, next_start = consume_item rlp, 0
|
data/lib/eth/rlp/encoder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2016-
|
1
|
+
# Copyright (c) 2016-2023 The Ruby-Eth Contributors
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -41,8 +41,8 @@ module Eth
|
|
41
41
|
# Encodes the raw item.
|
42
42
|
def encode_raw(item)
|
43
43
|
return item if item.instance_of? Rlp::Data
|
44
|
-
return encode_primitive item if Util.
|
45
|
-
return encode_list item if Util.
|
44
|
+
return encode_primitive item if Util.primitive? item
|
45
|
+
return encode_list item if Util.list? item
|
46
46
|
raise EncodingError "Cannot encode object of type #{item.class.name}"
|
47
47
|
end
|
48
48
|
|