eth/client: support resolving ENS domains
I think this may tie or interfere with the implementation of #72 also.
An ::EnsResolver could be part of the client. Once you instantiate a client, it should have the ability to resolve ::Ens to ::Address
That would be great!
NameHash: https://docs.ens.domains/contract-api-reference/name-processing
Resolver: https://docs.ens.domains/dapp-developer-guide/resolving-names
same address on all networks: 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e
https://etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e#readContract
I'll work on this next
Working on this now.
@mculp / @q9f I was looking for this as well, and started this here: https://github.com/withfabricxyz/eth.rb/commit/7194b7e4a3af6f4bb2b1578bb410390697aafac3
The biggest issue is in normalizing the domain. The ICU libraries for ruby don't seem to do exactly what is needed. This will work for basic names, but it's not properly encoding tokens with Cyrillic characters, etc. I tried a few libraries, which generate puny code symbols, but that isn't quite enough. See: https://github.com/jcranmer/idna-uts46 , which is used by https://github.com/ensdomains/eth-ens-namehash/blob/master/index.js#L26
Thank you, Dan.
I would love to consider accepting this for now. Some thoughts:
- adding a test case to resolve a name on mainnet (we can set an INFURA_ID in CI)
- maybe renaming
Eth::Ens::Resolver::DEFAULT_ADDRESStoEth::Ens::DEFAULT_REGISTRYor something meaningul in the ::Ens namespace - We already deploy the registry to our test chain, maybe we can try to set a name and resolve it straight away? https://github.com/q9f/eth.rb/blob/main/spec/eth/client_spec.rb#L107-L113
In general, we can split this up in multiple PRs if you want to only submit the resolver for now and look into the encoding issues later, I'd be happy to review it.
@dansimpson The encoding issue was the issue I ran into as well when trying to work on this. I was trying to follow the ENS spec to the T and couldn't seem to find a library that correctly encoded all names.
Thanks for doing this
@dansimpson FYI this is the recursive namehash implementation that I had. I ported it from the python reference implementation.
def namehash(name)
if name.empty?
"\0" * 32
else
label, _, remainder = name.partition('.')
Eth::Util.keccak256(namehash(remainder) + Eth::Util.keccak256(label))
end
end
@dansimpson If you open a PR, I have a couple pretty minor comments I'd like to make on your changes.
@mculp
def namehash(name)
if name.empty?
Eth::Util.prefix_hex("00"*32)
else
label, _, remainder = name.partition('.')
Eth::Util.prefix_hex(
sha3(Eth::Util.hex_to_bin(namehash(remainder) + sha3(label)))
)
end
end
def sha3 str
Eth::Util.bin_to_hex(Eth::Util.keccak256(str))
end
def labelhash label
Eth::Util.prefix_hex Eth::Util.bin_to_hex(Eth::Util.keccak256(label))
end
@musik yeah, once this gets merged these conversions should be easier
#150
#192
infura = Eth::Client.create("https://mainnet.infura.io/v3/#{ENV['INFURA_TOKEN']}")
# => #<Eth::Client::Http:0x00005614b684fd70
infura.resolve_ens "ncwc6edqldzy6mlo.eth"
# => "0xde270e46d63b1816d1b798cff473c4ba238aca73"
https://github.com/q9f/eth.rb/wiki/ENS-Resolver