eth 0.5.9 → 0.5.10

Sign up to get free protection for your applications and to get access to all the features.
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.