eth 0.5.9 → 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.
data/lib/eth/client.rb CHANGED
@@ -76,6 +76,7 @@ module Eth
76
76
  #
77
77
  # @return [Eth::Address] the coinbase account address.
78
78
  def default_account
79
+ raise ArgumentError, "The default account is not available on remote connections!" unless local?
79
80
  @default_account ||= Address.new eth_coinbase["result"]
80
81
  end
81
82
 
@@ -102,6 +103,17 @@ module Eth
102
103
  eth_get_transaction_count(address, "pending")["result"].to_i 16
103
104
  end
104
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
+
105
117
  # Simply transfer Ether to an account and waits for it to be mined.
106
118
  # Uses `eth_coinbase` and external signer if no sender key is
107
119
  # provided.
@@ -118,7 +130,7 @@ module Eth
118
130
  # if no sender key is provided.
119
131
  #
120
132
  # **Note**, that many remote providers (e.g., Infura) do not provide
121
- # any accounts. Provide a `sender_key` if you experience issues.
133
+ # any accounts. Provide a `sender_key:` if you experience issues.
122
134
  #
123
135
  # @overload transfer(destination, amount)
124
136
  # @param destination [Eth::Address] the destination address.
@@ -159,6 +171,9 @@ module Eth
159
171
  return eth_send_raw_transaction(tx.hex)["result"]
160
172
  else
161
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
+
162
177
  # use the default account as sender and external signer
163
178
  params.merge!({
164
179
  from: default_account,
@@ -168,6 +183,39 @@ module Eth
168
183
  end
169
184
  end
170
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
+
171
219
  # Deploys a contract and waits for it to be mined. Uses
172
220
  # `eth_coinbase` or external signer if no sender key is provided.
173
221
  #
@@ -184,7 +232,7 @@ module Eth
184
232
  # if no sender key is provided.
185
233
  #
186
234
  # **Note**, that many remote providers (e.g., Infura) do not provide
187
- # any accounts. Provide a `sender_key` if you experience issues.
235
+ # any accounts. Provide a `sender_key:` if you experience issues.
188
236
  #
189
237
  # @overload deploy(contract)
190
238
  # @param contract [Eth::Contract] contracts to deploy.
@@ -238,6 +286,10 @@ module Eth
238
286
  tx.sign kwargs[:sender_key]
239
287
  return eth_send_raw_transaction(tx.hex)["result"]
240
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
+
241
293
  # Uses the default account as sender and external signer
242
294
  params.merge!({
243
295
  from: default_account,
@@ -266,9 +318,15 @@ module Eth
266
318
  # @param **gas_limit [Integer] optional gas limit override for deploying the contract.
267
319
  # @return [Object] returns the result of the call.
268
320
  def call(contract, function, *args, **kwargs)
269
- func = contract.functions.select { |func| func.name == function }[0]
270
- raise ArgumentError, "this function does not exist!" if func.nil?
271
- 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)
272
330
  if output&.length == 1
273
331
  return output[0]
274
332
  else
@@ -280,7 +338,7 @@ module Eth
280
338
  # contract read/write).
281
339
  #
282
340
  # **Note**, that many remote providers (e.g., Infura) do not provide
283
- # any accounts. Provide a `sender_key` if you experience issues.
341
+ # any accounts. Provide a `sender_key:` if you experience issues.
284
342
  #
285
343
  # @overload transact(contract, function)
286
344
  # @param contract [Eth::Contract] the subject contract to write to.
@@ -334,6 +392,10 @@ module Eth
334
392
  tx.sign kwargs[:sender_key]
335
393
  return eth_send_raw_transaction(tx.hex)["result"]
336
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
+
337
399
  # use the default account as sender and external signer
338
400
  params.merge!({
339
401
  from: default_account,
@@ -431,6 +493,17 @@ module Eth
431
493
 
432
494
  private
433
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
+
434
507
  # Non-transactional function call called from call().
435
508
  # @see https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call
436
509
  def call_raw(contract, func, *args, **kwargs)
@@ -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
@@ -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
data/lib/eth/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  module Eth
17
17
 
18
18
  # Defines the version of the {Eth} module.
19
- VERSION = "0.5.9".freeze
19
+ VERSION = "0.5.10".freeze
20
20
  end
data/lib/eth.rb CHANGED
@@ -32,5 +32,5 @@ require "eth/solidity"
32
32
  require "eth/tx"
33
33
  require "eth/unit"
34
34
  require "eth/util"
35
- require "eth/ens/resolver"
35
+ require "eth/ens"
36
36
  require "eth/version"
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.9
4
+ version: 0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steve Ellis
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-12-21 00:00:00.000000000 Z
12
+ date: 2023-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: keccak
@@ -105,11 +105,15 @@ files:
105
105
  - ".yardopts"
106
106
  - AUTHORS.txt
107
107
  - CHANGELOG.md
108
+ - CODE_OF_CONDUCT.md
109
+ - CONTRIBUTING.md
108
110
  - Gemfile
109
111
  - LICENSE.txt
110
112
  - README.md
111
113
  - Rakefile
112
- - abis/ens.json
114
+ - SECURITY.md
115
+ - abi/ens_registry.json
116
+ - abi/ens_resolver.json
113
117
  - bin/console
114
118
  - bin/setup
115
119
  - codecov.yml
@@ -133,6 +137,8 @@ files:
133
137
  - lib/eth/contract/function_output.rb
134
138
  - lib/eth/contract/initializer.rb
135
139
  - lib/eth/eip712.rb
140
+ - lib/eth/ens.rb
141
+ - lib/eth/ens/coin_type.rb
136
142
  - lib/eth/ens/resolver.rb
137
143
  - lib/eth/key.rb
138
144
  - lib/eth/key/decrypter.rb
@@ -181,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
187
  - !ruby/object:Gem::Version
182
188
  version: '0'
183
189
  requirements: []
184
- rubygems_version: 3.2.32
190
+ rubygems_version: 3.3.25
185
191
  signing_key:
186
192
  specification_version: 4
187
193
  summary: Ruby Ethereum library.