Ethereum Name Service (ENS)
The Ethereum Name Service (ENS) is analogous to the Domain Name Service. It enables users and developers to use human-friendly names in place of error-prone hexadecimal addresses, content hashes, and more.
The ens
module is included with web3.py. It provides an interface to look up
domains and addresses, add resolver records, or get and set metadata.
Note
web3.py v6.6.0
introduced ENS name normalization standard
ENSIP-15.
This update to ENS name validation and normalization won’t affect ~99%
of names but may prevent invalid names from being created and from interacting with
the ENS contracts via web3.py. We feel strongly that this change, though breaking,
is in the best interest of our users as it ensures compatibility with the latest ENS
standards.
Setup
Create an ENS
object (named ns
below) in one of three ways:
# automatic detection
from ens.auto import ns
# or, with a provider
from web3 import IPCProvider
from ens import ENS
provider = IPCProvider(...)
ns = ENS(provider)
# or, with a w3 instance
# Note: This inherits the w3 middlewares from the w3 instance and adds a stalecheck middleware to the middleware onion.
# It also inherits the provider and codec from the w3 instance, as well as the ``strict_bytes_type_checking`` flag value.
from ens import ENS
w3 = Web3(...)
ns = ENS.from_web3(w3)
Asynchronous support is available via the AsyncENS
module:
from ens import AsyncENS
ns = AsyncENS(provider)
Note that an ens
module instance is also available on the w3
instance.
The first time it’s used, web3.py will create the ens
instance using
ENS.from_web3(w3)
or AsyncENS.from_web3(w3)
as appropriate.
# instantiate w3 instance
from web3 import Web3, IPCProvider
w3 = Web3(IPCProvider(...))
# use the module
w3.ens.address('ethereum.eth')
- ens.strict_bytes_type_checking
The
ENS
instance has astrict_bytes_type_checking
flag that toggles the flag with the same name on theWeb3
instance attached to theENS
instance. You may disable the stricter bytes type checking that is loaded by default using this flag. For more examples, see Disabling Strict Checks for Bytes TypesIf instantiating a standalone ENS instance using
ENS.from_web3()
, the ENS instance will inherit the value of the flag on the Web3 instance at time of instantiation.>>> from web3 import Web3, EthereumTesterProvider >>> from ens import ENS >>> w3 = Web3(EthereumTesterProvider()) >>> assert w3.strict_bytes_type_checking # assert strict by default >>> w3.is_encodable('bytes2', b'1') False >>> w3.strict_bytes_type_checking = False >>> w3.is_encodable('bytes2', b'1') # zero-padded, so encoded to: b'1\x00' True >>> ns = ENS.from_web3(w3) >>> # assert inherited from w3 at time of instantiation via ENS.from_web3() >>> assert ns.strict_bytes_type_checking is False >>> ns.w3.is_encodable('bytes2', b'1') True >>> # assert these are now separate instances >>> ns.strict_bytes_type_checking = True >>> ns.w3.is_encodable('bytes2', b'1') False >>> # assert w3 flag value remains >>> assert w3.strict_bytes_type_checking is False >>> w3.is_encodable('bytes2', b'1') True
However, if accessing the
ENS
class via theWeb3
instance as a module (w3.ens
), since all modules use the sameWeb3
object reference under the hood (the parentw3
object), changing thestrict_bytes_type_checking
flag value onw3
also changes the flag state forw3.ens.w3
and all modules.>>> from web3 import Web3, EthereumTesterProvider >>> w3 = Web3(EthereumTesterProvider()) >>> assert w3.strict_bytes_type_checking # assert strict by default >>> w3.is_encodable('bytes2', b'1') False >>> w3.strict_bytes_type_checking = False >>> w3.is_encodable('bytes2', b'1') # zero-padded, so encoded to: b'1\x00' True >>> assert w3 == w3.ens.w3 # assert same object >>> assert not w3.ens.w3.strict_bytes_type_checking >>> w3.ens.w3.is_encodable('bytes2', b'1') True >>> # sanity check on eth module as well >>> assert not w3.eth.w3.strict_bytes_type_checking >>> w3.eth.w3.is_encodable('bytes2', b'1') True
Usage
Name Info
Get the Address for an ENS Name
from ens.auto import ns
eth_address = ns.address('ens.eth')
assert eth_address == '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7'
The ENS
module has no opinion as to which TLD (Top Level Domain) you can use,
but will not infer a TLD if it is not provided with the name.
Multichain Address Resolution
ENSIP-9
introduced multichain address resolution, allowing users to resolve addresses from
different chains, specified by the coin type index from
SLIP44. The
address()
method on the ENS
class supports multichain address resolution via
the coin_type
keyword argument.
from ens.auto import ns
eth_address = ns.address('ens.eth', coin_type=60) # ETH is coin_type 60
assert eth_address == '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7'
Get the ENS Name for an Address
domain = ns.name('0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7')
# name() also accepts the bytes version of the address
assert ns.name(b'\xfe\x89\xccz\xbb,A\x83h:\xb7\x16S\xc4\xcd\xc9\xb0-D\xb7') == domain
# confirm that the name resolves back to the address that you looked up:
assert ns.address(domain) == '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7'
Note
For accuracy, and as a recommendation from the ENS documentation on
reverse resolution,
the ENS
module now verifies that the forward resolution matches the address with every call to get the
name()
for an address. This is the only sure way to know whether the reverse resolution is correct. Anyone can
claim any name, only forward resolution implies that the owner of the name gave their stamp of approval.
Get the Owner of a Name
eth_address = ns.owner('exchange.eth')
Set Up Your Name and Address
Link a Name to an Address
You can set up your name so that address()
will show the address it points to. In order to do so,
you must already be the owner of the domain (or its parent).
ns.setup_address('ens.eth', '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7')
In the common case where you want to point the name to the owning address, you can skip the address.
ns.setup_address('ens.eth')
You can claim arbitrarily deep subdomains.
ns.setup_address('supreme.executive.power.derives.from.a.mandate.from.the.masses.ens.eth')
# wait for the transaction to be mined, then:
assert (
ns.address('supreme.executive.power.derives.from.a.mandate.from.the.masses.ens.eth')
== '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7'
)
Warning
Gas costs scale up with the number of subdomains!
Multichain Address Support
ENSIP-9
introduced multichain address resolution, allowing users to resolve addresses from
different chains, specified by the coin type index from
SLIP44. The
setup_address()
method on the ENS
class supports multichain address setup
via the coin_type
keyword argument.
from ens.auto import ns
ns.setup_address('ens.eth', coin_type=60) # ETH is coin_type 60
assert ns.address('ens.eth', coin_type=60) == '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7'
Link an Address to a Name
You can set up your address so that name()
will show the name that points to it.
This is like Caller ID. It enables you and others to take an account and determine what name points to it. Sometimes this is referred to as “reverse” resolution. The ENS Reverse Resolver is used for this functionality.
ns.setup_name('ens.eth', '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7')
If you don’t supply the address, setup_name()
will assume you want the
address returned by address()
.
ns.setup_name('ens.eth')
If the name doesn’t already point to an address, setup_name()
will
call setup_address()
for you.
Wait for the transaction to be mined, then:
assert ns.name('0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7') == 'ens.eth'
Text Records
Set Text Metadata for an ENS Record
As the owner of an ENS record, you can add text metadata. A list of supported fields can be found in the ENS documentation. You’ll need to setup the address first, and then the text can be set:
ns.setup_address('ens.eth', '0xFe89cc7aBB2C4183683ab71653C4cdc9B02D44b7')
ns.set_text('ens.eth', 'url', 'https://example.com')
A transaction dictionary can be passed as the last argument if desired:
transaction_dict = {'from': '0x123...'}
ns.set_text('ens.eth', 'url', 'https://example.com', transaction_dict)
If the transaction dictionary is not passed, sensible defaults will be used, and if
a transaction dictionary is passed but does not have a from
value,
the default will be the owner
.
Read Text Metadata for an ENS Record
Anyone can read the data from an ENS Record:
url = ns.get_text('ens.eth', 'url')
assert url == 'https://example.com'
Working With Resolvers
Get the Resolver for an ENS Record
You can get the resolver for an ENS name via the resolver()
method.
>>> resolver = ns.resolver('ens.eth')
>>> resolver.address
'0x5B2063246F2191f18F2675ceDB8b28102e957458'
Wildcard Resolution Support
The ENS
module supports Wildcard Resolution for resolvers that implement the ExtendedResolver
interface
as described in ENSIP-10.
Resolvers that implement the extended resolver interface should return True
when calling the
supportsInterface()
function with the extended resolver interface id "0x9061b923"
and should resolve subdomains
to a unique address.