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