There are a few well-known contracts throughout ENS that tools such as the manager app need to look up - a primary example is the .eth registrar controller contract. Currently this is handled via the interface discovery method described in ENSIP 8, which works fine because there is a natural name associated with the contract already (in this case, .eth
).
However, we’re likely to see the ENS ecosystem develop more special-purpose utility contracts. One example is the name wrapper, which will permit applying restrictions to names and a lot of other functionality; this is intended to have only a single implementation, and has no obvious name of its own.
Another example is a ‘universal resolver’, a contract that can perform all the resolution steps for a name other than normalisation. Up until now, resolving an ENS name has only required two contract calls, so such a construct was mostly useful for bulk lookups, but with ENS on L2 (ENSIP 10 and EIP 3668) this is going to change; a single lookup will require at least 3 calls, and will require more if the name uses wildcards or CCIP read to fetch offchain data. A universal resolver can perform most of these steps in a single call, substantially reducing latency.
We could simply assign these contracts names under ‘ens.eth’ or ‘ensdao.eth’, but this comes with a number of shortcomings:
- The names could expire (though this seems unlikely)
- Unless the owner of the parent name uses the name wrapper to revoke permissions, they could change the contract for that name, resulting in behavioural changes.
- Looking up these names requires following the resolution process, which adds significant overhead to discovery; ironic if you’re looking up the universal resolver for example.
I’d like to propose creating a new pseudo-TLD specifically for discovery of system-internal contracts such as the name wrapper and universal resolver. A natural name for this would be ‘_’, but we could even make this pseudo-TLD a hash with no preimage - for example the zero hash or some other similar example, in which case it would have no textual representation.
In either case, the pseudo-TLD would be owned by a contract that exposes a function to insert or update a contract address. When called by an authorised caller with an address and name, it would:
- Create or update the subdomain
name._
and set its resolver address to the address supplied. - Create
x.name._
, wherex
is a sequentially increasing version number, and set its resolver address to the address supplied.
Note that this pseudo-TLD would use the resolver record to store the addresses of these internal contracts, despite them not being resolvers; this saves a lookup step and the need to provision a resolver for each, which seems a worthwhile tradeoff for this discovery mechanism.
The intention then is that libraries such as ethers could fetch, for example, 1.universalresolver._
and know for certain that this will remain the same; they can opt in to upgrades by using the new version manually. Or they can fetch universalresolver._
and always get the latest version.
Thoughts?