eth 0.5.8 → 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/spec.yml +2 -1
  3. data/CHANGELOG.md +32 -0
  4. data/CODE_OF_CONDUCT.md +122 -0
  5. data/CONTRIBUTING.md +61 -0
  6. data/LICENSE.txt +1 -1
  7. data/README.md +6 -2
  8. data/SECURITY.md +24 -0
  9. data/abi/ens_registry.json +436 -0
  10. data/abi/ens_resolver.json +885 -0
  11. data/lib/eth/abi/event.rb +1 -1
  12. data/lib/eth/abi/type.rb +46 -12
  13. data/lib/eth/abi.rb +72 -14
  14. data/lib/eth/address.rb +2 -2
  15. data/lib/eth/api.rb +1 -1
  16. data/lib/eth/chain.rb +19 -7
  17. data/lib/eth/client/http.rb +1 -1
  18. data/lib/eth/client/http_auth.rb +1 -1
  19. data/lib/eth/client/ipc.rb +1 -1
  20. data/lib/eth/client.rb +118 -16
  21. data/lib/eth/constant.rb +1 -1
  22. data/lib/eth/contract/event.rb +1 -1
  23. data/lib/eth/contract/function.rb +2 -2
  24. data/lib/eth/contract/function_input.rb +7 -2
  25. data/lib/eth/contract/function_output.rb +1 -1
  26. data/lib/eth/contract/initializer.rb +1 -1
  27. data/lib/eth/contract.rb +1 -1
  28. data/lib/eth/eip712.rb +2 -2
  29. data/lib/eth/ens/coin_type.rb +50 -0
  30. data/lib/eth/ens/resolver.rb +68 -24
  31. data/lib/eth/ens.rb +28 -0
  32. data/lib/eth/key/decrypter.rb +1 -1
  33. data/lib/eth/key/encrypter.rb +1 -1
  34. data/lib/eth/key.rb +5 -5
  35. data/lib/eth/rlp/decoder.rb +2 -2
  36. data/lib/eth/rlp/encoder.rb +3 -3
  37. data/lib/eth/rlp/sedes/big_endian_int.rb +1 -1
  38. data/lib/eth/rlp/sedes/binary.rb +2 -2
  39. data/lib/eth/rlp/sedes/list.rb +5 -5
  40. data/lib/eth/rlp/sedes.rb +4 -4
  41. data/lib/eth/rlp.rb +1 -1
  42. data/lib/eth/signature.rb +4 -4
  43. data/lib/eth/solidity.rb +5 -3
  44. data/lib/eth/tx/eip1559.rb +3 -3
  45. data/lib/eth/tx/eip2930.rb +3 -3
  46. data/lib/eth/tx/legacy.rb +3 -3
  47. data/lib/eth/tx.rb +5 -5
  48. data/lib/eth/unit.rb +1 -1
  49. data/lib/eth/util.rb +14 -14
  50. data/lib/eth/version.rb +2 -2
  51. data/lib/eth.rb +2 -2
  52. metadata +9 -3
  53. data/abis/ens.json +0 -422
data/lib/eth/client.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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` if you experience issues.
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` if you experience issues.
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 }[0]
267
- raise ArgumentError, "this function does not exist!" if func.nil?
268
- output = call_raw(contract, func, *args, **kwargs)
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` if you experience issues.
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
- wait_for_tx(transact(contract, function, *args, **kwargs))
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.is_hex? hash
366
- signature = Util.hex_to_bin signature if Util.is_hex? signature
367
- magic = Util.hex_to_bin magic if Util.is_hex? magic
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
- # Checkes wether a transaction is mined or not.
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 is_mined_tx?(hash)
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 is_mined_tx? hash
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 { |i| i.type }
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.is_hex? params
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.collect { |x| x.raw_type }.join(",")})"
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-2022 The Ruby-Eth Contributors
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
data/lib/eth/contract.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
data/lib/eth/eip712.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.is_hex? data
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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 default address for ENS, which applies to most chains
27
- DEFAULT_ADDRESS = "0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e".freeze
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 contract
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
- @contract = Eth::Contract.from_abi(
36
- name: "ENS",
39
+ @registry = Eth::Contract.from_abi(
40
+ name: "ENSRegistryWithFallback",
37
41
  address: address,
38
- abi: JSON.parse(File.read(File.join(File.dirname(__FILE__), "../../../abis/ens.json"))),
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 to an address
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, eg: fancy.eth
45
- # @return [String] The owner address of the name, as a hex string
46
- def resolve(ens_name)
47
- @client.call(@contract, "owner", namehash(ens_name))
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, eg: fancy.eth
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 = Digest::Keccak.new(256).hexdigest(label)
60
- node = Digest::Keccak.new(256).hexdigest([node + hash].pack("H*"))
101
+ hash = Util.keccak256(label)
102
+ node = Util.keccak256(node + hash)
61
103
  end
62
- "0x#{node}"
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
- # TODO: This is fairly complicated, and there doesn't seem to be a ruby
71
- # library which can handle it perfectly.
72
- # https://www.unicode.org/reports/tr46/tr46-27.html
73
- input.downcase
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.
data/lib/eth/key.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.is_hex? priv
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
- is_leading_zero = true
77
+ leading_zero = true
78
78
  [v].pack("N").unpack("C*").each do |byte|
79
- is_leading_zero = false if byte > 0 and is_leading_zero
80
- signature.append byte unless is_leading_zero and byte === 0
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.is_hex? rlp
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
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.is_primitive? item
45
- return encode_list item if Util.is_list? item
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
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2022 The Ruby-Eth Contributors
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.