(A) can be served by the use of default reverse/primary name so I am not sure why you think itās only useful for (B) (this fallback option is actually proposed by @Premm.eth). As I also mentioned in the modified post above, the ability to specify chain specific primary name is more of a security requirement and security does come as a highest priority in crypto imo
Agreed not to compromise on security. Hopefully thereās a path that doesnāt compromise security while also not compromising on the value propositions of ENS or on ENS market adoption.
Thatās basically the role of the default primary name. Contract accounts cannot set the default primary name because it only accepts signature which only EOA can generate.
As I repeated, you donāt have to set chain specific name if you only set the default primary name.
The ability to specify chain specific address has been discussed in other places. The ability to specify which chain to accept payment/transactions is actually a useful UX feature to give assurance to the end users that recipients wonāt lose their funding by sending assets to a wrong chain.
Thereās a lot of words here and Iām not sure I understand, so Iām just gonna say things:
Reverse resolution {addr}.addr.reverse
is just address
ā name
.
I see no purpose in having anything more complicated than this, so Iām against the current subdomain ā resolver = PR ā PR.setName() setup simply for gas and complexity.
For example, do we really need this? raffy.51050ec063d393217b436747617ad1c2285aeeee.addr.reverse
Note: I think thereās value in having EoA-claimable nodes in the registry, but theyāre massive overkill for what reverse names do for 99.99999% of use-cases.
The fact that contracts arenāt widely named in 2024 is also odd to me.
A much simpler registry would just store reverse-key (node or address)
ā resolver address
outside of the ENS tree ā aka 1 slot on-chain. This registry would only be editable through DAO approved registrars using different proving mechanisms. These should be trustless and irrevocable.
*.addr.reverse
(or *.reverse
) can be wildcard, and it consults this simplified registry, and forwards the request to the resolver set for that address.
-
There should be a simple storage contract
L
(literal) that storesreverse-key
āname (string)
and any EOA who wants string-based on-chain names can use that. -
There should be another contract
W
(wrapper) that has a mapreverse-key
ānode
which converts your reverse address namehash to a wrapped namehash, and then just callsnames()
, eliminating the need to store a name because itās already stored on-chain. -
If you want a different proving mechanism, deploy another contract (eg. signature, contract ownership, etc.). For example, why canāt contracts just be asked for their own primary? For example, if a contract sets their
reverse-key
āF
(contract forward),F
can be a contract that simply callsC.primaryName()
when itās asked toF.resolve(C.addr.reverse, name())
. -
If you set your
reverse-key
ācustom resolver
, then that contract can do whatever, and go off-chain.
If weāre considering using CCIP for reverse resolution, which requires client changes, we should use a solution like this, as it gives maximum control to the owner and minimizes gas.
Some examples:
-
If I have 100 EoAs, I should be able to deploy a contract
Q
that answersresolve(*.reverse.addr, name())
for any of those 100 addresses, and responds"raffy.eth"
. And then for each of those 100 EoAs, setreverse key
āQ
. I should also be able to provide some kind of delegated signature, so I can do this in 1 transaction. -
If I deploy a new contract, I should be able implement a function
primaryName() returns (string)
that is my primary name. During the constructor, I claim that name by finding that registrar by name, and then calling claim() on it. That should cost almost nothing (eg. setresolver key
āF
(forwarding contract above). -
For virtual names of basename B, EoAs should be able to set
reverse-key
āB
and then that wildcard contract (likely off-chain) can provide the primary.
Additionally, name(node)
could be generalized to name(node, chain)
.
A related idea from Expanding Beyond Mainnet was having a special coinType
which indicates universal EVM deployment address (maybe 0x8000003c, evm bit + 60).
Thank you everyone for the rich and enlightening discussion on this topic. The insights shared have greatly contributed to evolving my understanding and perspective regarding the implementation of our proposal.
Firstly, I apologize for the unconventional format of my initial proposal. Instead of a traditional, well-structured proposal, I opted for a more illustrative, step-by-step guide. I realize now the importance of summarizing our concept more succinctly and cohesively:
At the core, our proposal aims to enable off-chain subnames to set primary names, facilitating reverse resolution. Our goal was to devise a solution that would seamlessly integrate without disrupting existing implementations while also optimizing the gas costs for users. Our primary focus was on off-chain mechanisms, although, with hindsight, I acknowledge the potential for extending this to include L2 chains through a more generalized approach.
The critical component for off-chain solutions is establishing an on-chain pointer. This pointer would direct the reverse resolution process to a customer resolver being utilized for off-chain resolutions. Our proposed custom resolver is relatively straightforward, featuring a resolve function capable of enabling address reverse resolution via CCIP-Read (similar to the forward resolution flow). However, incorporating @raffy ās suggestion for a ādelegated signature mechanismā could further enhance efficiency, although it would necessitate additional modifications.
Thank you @matoken.eth for sharing the EVM reverse resolution proposal, I really appreciate the approach you are taking by setting a generalized solution for L2s, āwithout resolver customisabilityā, and I completely agree with the way you approached this to account for smart contract accounts and not only EOAs. But I also agree with @lightwalker.eth that āENS onboarding is incomplete if only forward resolution is configured. A complete ENS onboarding should include configuring a primary name that isnāt āsandboxedā to a particular app or chainā for off-chain names.
I am more than happy to continue working on this with you guys, but I am unsure of future steps. For example, I donāt know if it would be better to have two separate proposals, one being the EVM reverse resolution which is accounting for L2 chains (along with contract accounts) and one for off-chains solutions, or merge them both in a generalized big proposal, I am unsure of how feasible this would be, given the divergence between the two proposals.
As a quick demo, I set the TOR as my reverse resolver and set the context to use this demo which returns the requesting IP address + the current time as the reverse name.
My resolver already supports CCIP everywhere so this just works:
Exactly! I think weāre aligned on the flow, the only difference is that I was using a custom resolver instead of the TOR (btw cool name), but it should be pretty straightforward moving the implementation towards a generalised resolver, as it makes senseā¦
I think I had missed this part when first reading the replies to the thread. This is a really interesting alternative, instead of having the user perform a one time transaction. Could you please elaborate on how this would work?
In general, I think the only feature we need from reverse resolution is address
ā resolver
, as we must store at least one slot on-chain (as no default is possible) so why not just store a resolver, and call resolver.name(address) view returns (string)
with CCIP-Read enabled.
However, setSubnodeRecord
is sufficient. I wonder if itās cheaper if you call it twice in the same tx and zero everything but the resolver.
I think the only necessary change (which I personally think is the current standard) is that ENSIP-10 resolution should apply universally ā so if the reverse resolver is wildcard, you must call resolve(name())
.
If this is a change, might as well make it: name(node, chain) returns (string)
With this clarification in middleware, ENS could offer free? offchain reverse at signup by setting their resolver and storing an off-chain record. Obviously, they would be encouraged to set an on-chain record, but it doesnāt have to be right away.
It would be interesting to see if setSubnodeRecord is indeed cheaper. I was experimenting, and I didnāt try it. This was the cheapest flow I had found
Call the function recordExists() on the ENS Registry contract, the next steps would depend on its response:
- True: Call ["function setResolver(bytes32 node, address resolver)ā] on ENS Registry contract and pass the node and the off-chain resolver address. Costs 31,165 gas units.
- False: Call [āfunction claimWithResolver(address owner, address resolver)ā] on Reverse Registrar Contract and pass the address along with the off-chain resolver address. Costs 44,002 gas units and 81,002 gas units for the first time.
Iād have to review the contract to be sure, but setting just the resolver would be sufficient (ENSIP-10 doesnāt check owner), but thereās no setSubnodeResolver()
so you must call setSubnodeRecord()
(setOwner
+ setResolver
) but the parent could zero owner before returning using a different registrar.
Iām not sure what it is youāre actually proposing; your first post reads like a description of how things work today, to me. What is it that you think should be changed?
It was intended as a proposal to standardize the flow for setting a primary name for subnames, not a proposal for changes
It does look like you can save some gas using this technique.
I deployed a BudgetReverseRegistrar
and then set a resolver from an EOA, where:
function claim(address resolver) external {
bytes32 hash = sha3HexAddress(msg.sender);
ens.setSubnodeRecord(REVERSE_NODE, hash, address(this), resolver, 0);
ens.setSubnodeOwner(REVERSE_NODE, hash, address(0));
}
This is just exploiting the fact that the owner of a reverse record is superfluous as the reverse registrar lets the rightful owner reclaim it.
If that resolver was an wildcard off-chain resolver, an external server would just answer resolve("{addr}.addr.reverse", name())
which is address
ā name
.
85k gas for any length name (since no name stored on-chain)
The current reverse registrar for short names is 114k gas (34% more expensive) and 160k gas for a 33-character primary.
IMO, setting a wildcard on addr.reverse
seems dangerous since it lets that resolver provide a name for any address, but individually setting each addressās resolver to the same wildcard seems fine. Minimizing gas to set your reverse resolver to an offchain database seems reasonable for on-boarding.
Like I described previously, using the same setup, new contracts could set their resolver to themselves during construction, and then just answer their own name(node)
query.
Or set their resolver to some fixed deployment, like DotEthContractNameWildcardResolver
, which on resolve("{addr}.addr.reverse", name())
, reads addr.dotethname()
from the corresponding contract and appends ".eth"
.
I believe Viem uses the UR but ethers was using non-ENSIP-10 resolution for lookupAddress()
. I submit a PR to fix this.
I guess this is a reasonable price for the user, as it is also optional. Setting the primary name would cost him ā 80k during onboarding and ā40k if he already had interacted with ens previously (as he would only need to set the resolver address).
This is great, thank you. Feel free to use this library until the PR is merged. After that, you can switch back to ethers since address resolution is the only feature it supports.
Namesys offchain resolver āccip2.ethā itās fully compatible with reverse name as we use ../.well-known/eth/domain/sub/..
format, so for reverse records it can use ../.well-known/reverse/addr/<address>
as records storageā¦ but for simplicity we prefer to use default on-chain resolver coz off-chain setup requires new reverse domain/storage costing extra gas/txs, and users are most likely not going to update their reverse records more frequently per address.
If you are using coinType, it wonāt be usable for new L2 chains or development chains especially OP Stack / Arbitrum Orbit chains that are yet to have their own token or are not planned to have their own token.
coinType is defined by slip44 which is not related to the existence of coins.
For EVM chain, we have ENSIP to convert evm chain id to the cointype
There are concerns that come to mind regarding the deployment and integration of the L2ReverseRegistrar:
- Is the L2ReverseRegistrar deployed deterministically, allowing the deployment address to be predicted from the coinType?
- If not, integrating this feature into SDKs and wallets becomes challenging, as addresses must be manually input for each chain, and there are thousands of chains.
- An example of this issue is the UniversalResolver, whose address changes with each upgrade, leading to SDKs not following new upgrades and continuing to use an older version.
- Can the L2ReverseRegistrar be deployed on a new chain without obtaining permission from ENS and still have integration with SDKs and wallets?
A live example of an obsolete UniversalResolver problem is found in Viem and I have just opened a pull request to upgrade the UniversalResolver.