Reverse Resolution in off-chain settings

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.

I guess to summarise this topic, @matoken.eth’s proposal is the way to go for L2 reverse resolution, “[userAddress].[evmChainCointype].reverse” is a smart way to point to the corresponding chain, otherwise the “[userAddress].addr.reverse” would still follow the same flow of pointing to a resolver which could be @raffy’s generalised resolver or a custom resolver. The resolver would either resolve the node normally, or revert with an error to start the CCIP-read/ENSIP-10 flow. Once @raffy’s PR gets approved, this would make the flow easier to implement for clients.

What are the use-cases for chain-specific names?

Use(1) — For an EOA, this is the same address reversing to different names depending on the chain: 1 → raffy.eth vs 42161 → raffy.arb. A use-case might be having a different display name on a gamer chain. However, apps shouldn’t display raffy.arb unless addr(evm(42161)) == EOA, right?

I’m not sure there’s anything else?

For the opposite case, eg. non-create2 deployments, you’d have different addresses that want to have same identity, but this is already supported.

Use(2) — For that situation, you might want both deployments to have identical reverse mappings:

  • chain 1: reverse(A) → {1 → A, 2 → B}
  • chain 2: reverse(B) → {1 → A, 2 → B}

Note that reverse names aren’t really input by users.

The advantage of {addr}.{chain}.reverse is that {chain}.reverse can be catch-all wildcard. But any on-chain record for this is a separate registry entry. This name is also non-trivial to synthesize in Solidity.

Advantages of a potential new interface: name(node, chain) returns (string) would be:

  • {addr}.addr.reverse can still be the universal convention on all chains
    • And should be native if possible
  • Trivial to query: name(node, block.chainid)
  • only need 1 registry entry
  • EOA’s can use either:
    1. an updated PR which answers the same name for any chain by default
    2. a new lightweight EOANameResolver which has setName(name) and setName(name, chain) and answers name(node) and name(node, chain) using either the chain-specific name or the default name as a fallback.
  • You could still have a {chain}.reverse wildcard that just returns the result of name("{addr}.addr.reverse", chain) but this would just be a nerdy convenience.
  • A single on-chain record can go offchain resolve("{addr}.addr.reverse", name(_, chain)) and lookup a database of (addr, chain)name
  • You can detect for this with EIP-165.

Potentially, this should be name(node, coin).

Also it should be stated somewhere that reverse names must normalize, but they don’t need to be normalized, proper names like FirstLast.eth and PlaceLocation.eth and 4️⃣.eth should be encouraged.

Contracts cannot have a single primary name across multiple chains, because there’s no way to guarantee the same contract is deployed at the same address on every chain. It’s possible for both the same contract to have different addresses on different chains, and for different contracts to have the same address on different chains. As a result, at a minimum we need a chain-specific way to resolve contract primary names.

We’re also implementing a fallback reverse resolver, that allows a user to sign a message setting their default primary name for all chains. We’d expect most EOAs to use this for most chains.

This won’t work, because the address could be controlled by a different contract on each chain.

1 Like

I agree there should be a way at name-level to say addr(C).2.reverse but shouldn’t this just be an evm-gateway query that reads the native addr(C).addr.reverse on chain 2?

  • chain A: addr(X).B.reverse if A != B → chain B: addr(X).addr.reverse (Offchain)
  • chain B: addr(X).B.reverseaddr(X).addr.reverse (Onchain)
  • chain B: addr(X).addr.reverse (Native)

To me, the common case is name() on the current chain, which I think should always be native, since the registrar enforces ownership.

  • Any chain: eoa X or contract X or X.owner() can set addr(X).addr.reverse to an ENS Name.

However, unless an EOA goes around and sets their reverse on every chain, they have no name on that chain.

Is it safe to make the assumption that any EOA with one transaction on any chain is universally an EOA?

It makes sense to me that an EOA that sets a primary on mainnet can apply to all other chains, but which name wins: the default record or the per-chain record?

This could be handled in the wildcard contract on mainnet that routes {addr}.{chain}.reverse by checking if extcodesize(addr) = 0, but why can’t that mapping be controlled by the owner?


The scenario I was thinking of:

  • What’s 0x5105’s name on arb?
    • on-chain
      • name(5105.addr.reverse, 42161)raffy.arb
      • name(5105.addr.reverse, *)raffy.eth
    • off-chain
      • name(5105.42161.reverse) → CCIP-Read → raffy.arb
  • I have coin X on chain 1, how do I find coin X on chain 2?
    • 1: name(addr(X).addr.reverse, 2)

We already support nameaddress:

  • addr(raffy.eth, ENSIP11(42161)) can answer 0x5105

But we don’t support the generic inverse: namename

  1. name(raffy.eth, 42161)raffy.arb
  2. name(5105.addr.reverse, 42161)raffy.arb

That mapping could just be a key-scheme for text() using a Solidity-friendly encoding.

  • name(node, 123) === text(node, "eip155:123")