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.
- checksums.yaml +4 -4
- data/.github/workflows/spec.yml +1 -0
- data/CHANGELOG.md +18 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +61 -0
- data/README.md +5 -1
- data/SECURITY.md +24 -0
- data/abi/ens_registry.json +436 -0
- data/abi/ens_resolver.json +885 -0
- data/lib/eth/client.rb +79 -6
- data/lib/eth/ens/coin_type.rb +50 -0
- data/lib/eth/ens/resolver.rb +67 -23
- data/lib/eth/ens.rb +28 -0
- data/lib/eth/version.rb +1 -1
- data/lib/eth.rb +1 -1
- metadata +10 -4
- data/abis/ens.json +0 -422
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
|
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
|
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 }
|
270
|
-
raise ArgumentError, "this function does not exist!" if func.nil?
|
271
|
-
|
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
|
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
|
data/lib/eth/ens/resolver.rb
CHANGED
@@ -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/version.rb
CHANGED
data/lib/eth.rb
CHANGED
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.
|
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:
|
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
|
-
-
|
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.
|
190
|
+
rubygems_version: 3.3.25
|
185
191
|
signing_key:
|
186
192
|
specification_version: 4
|
187
193
|
summary: Ruby Ethereum library.
|