Summary
resolver-options already lets an operator route a TLD to a contract and an RPC endpoint ([tld:][contract-addr@]url), but the ENS client assumes the contract is an ENS registry and calls owner/resolver on it before reading contenthash. Name services that skip the registry and expose the ENS resolver profile directly on the name contract cannot be resolved, although they store Swarm contenthash records in the standard encoding.
Concrete case: the Gwei Name Service (.gwei, https://gwei.domains, contracts https://github.com/lucadonnoh/gwei-names). Its NameNFT at 0x9D51D507BC7264d4fE8Ad1cf7Fe191933A0a81d6 (Ethereum mainnet) implements
function contenthash(bytes32 node) public view returns (bytes memory)
function addr(bytes32 node) public view returns (address)
function addr(bytes32 node, uint256 coinType) public view returns (bytes memory)
function text(bytes32 node, string calldata key) public view returns (string memory)
function supportsInterface(bytes4) // 0x3b3b57de, 0xf1cb7e06, 0x59d1d43c, 0xbc1c58d1
with EIP-137 namehash nodes (token id = uint256(namehash)), but has no owner(bytes32) / resolver(bytes32) registry functions. Freedom Browser resolves .gwei natively and <name>.gwei.domains serves Swarm content, so .gwei sites on Swarm exist today; Bee (and therefore Swarm Desktop) cannot open them.
Where it fails
pkg/resolver/client/ens/ens.go:
wrapDial calls registry.Owner("") on the configured address to check that "the ENS registry client is deployed" — fails for a resolver-only contract.
wrapResolve calls registry.Owner(name), then registry.Resolver(name), then Contenthash(); the first two have no counterpart on GNS. (Its contractAddr parameter is currently unused, _ common.Address.)
Proposal
Treat the configured contract as a resolver when it is not a registry, either by auto-detection or by an explicit marker:
- In
wrapDial, if registry.Owner("") fails, probe supportsInterface(0xbc1c58d1) (contenthash profile, EIP-165) on the same address; if true, remember the address as a direct resolver.
- In
wrapResolve, for a direct resolver: r, err := goens.NewResolverAt(ethCl, name, contractAddr) then r.Contenthash() and goens.ContenthashToString as today. Skip the owner check (an unregistered name yields an empty contenthash → ErrNotFound).
Alternatively an explicit syntax, e.g. gwei:resolver=0x9D51…81d6@https://rpc, if auto-detection is undesirable. go-ens already has everything needed (NewResolverAt, Contenthash), so the change is confined to ens.go plus a unit test with a mocked resolver-only contract.
Expected result
resolver-options:
- "gwei:0x9D51D507BC7264d4fE8Ad1cf7Fe191933A0a81d6@https://<mainnet rpc>"
GET /bzz/swarmtyp.gwei/ serves the Swarm feed manifest the name points at (today b656fac57eb02756af40279cf70275969c9f9219818af7cceee34101f169a100; the name is live for testing, contenthash set with the Swarm codec). The same mechanism would cover other registry-less services that adopt the resolver profile.
Context
swarmtyp (https://github.com/petfold/swarmtyp), a collaborative Typst editor served from Swarm, uses swarmtyp.gwei as its address; users on Freedom or a web gateway can open it by name, users on Swarm Desktop / Bee have to use the raw reference. Notes in docs/decisions.md D-25 of that repo.
Summary
resolver-optionsalready lets an operator route a TLD to a contract and an RPC endpoint ([tld:][contract-addr@]url), but the ENS client assumes the contract is an ENS registry and callsowner/resolveron it before readingcontenthash. Name services that skip the registry and expose the ENS resolver profile directly on the name contract cannot be resolved, although they store Swarmcontenthashrecords in the standard encoding.Concrete case: the Gwei Name Service (
.gwei, https://gwei.domains, contracts https://github.com/lucadonnoh/gwei-names). ItsNameNFTat0x9D51D507BC7264d4fE8Ad1cf7Fe191933A0a81d6(Ethereum mainnet) implementswith EIP-137 namehash nodes (token id =
uint256(namehash)), but has noowner(bytes32)/resolver(bytes32)registry functions. Freedom Browser resolves.gweinatively and<name>.gwei.domainsserves Swarm content, so.gweisites on Swarm exist today; Bee (and therefore Swarm Desktop) cannot open them.Where it fails
pkg/resolver/client/ens/ens.go:wrapDialcallsregistry.Owner("")on the configured address to check that "the ENS registry client is deployed" — fails for a resolver-only contract.wrapResolvecallsregistry.Owner(name), thenregistry.Resolver(name), thenContenthash(); the first two have no counterpart on GNS. (ItscontractAddrparameter is currently unused,_ common.Address.)Proposal
Treat the configured contract as a resolver when it is not a registry, either by auto-detection or by an explicit marker:
wrapDial, ifregistry.Owner("")fails, probesupportsInterface(0xbc1c58d1)(contenthash profile, EIP-165) on the same address; if true, remember the address as a direct resolver.wrapResolve, for a direct resolver:r, err := goens.NewResolverAt(ethCl, name, contractAddr)thenr.Contenthash()andgoens.ContenthashToStringas today. Skip the owner check (an unregistered name yields an empty contenthash →ErrNotFound).Alternatively an explicit syntax, e.g.
gwei:resolver=0x9D51…81d6@https://rpc, if auto-detection is undesirable. go-ens already has everything needed (NewResolverAt,Contenthash), so the change is confined toens.goplus a unit test with a mocked resolver-only contract.Expected result
GET /bzz/swarmtyp.gwei/serves the Swarm feed manifest the name points at (todayb656fac57eb02756af40279cf70275969c9f9219818af7cceee34101f169a100; the name is live for testing, contenthash set with the Swarm codec). The same mechanism would cover other registry-less services that adopt the resolver profile.Context
swarmtyp (https://github.com/petfold/swarmtyp), a collaborative Typst editor served from Swarm, uses
swarmtyp.gweias its address; users on Freedom or a web gateway can open it by name, users on Swarm Desktop / Bee have to use the raw reference. Notes indocs/decisions.mdD-25 of that repo.