Sorry for late party…
We’re simplifying our design with IPNS+IPLD and IPNS+IPFS support so we can skip direct ENS+IPLD support for now…
tldr; ENS records for domain.eth
is stored in gateway.tld/ipns/f<hash>/.well-known/eth/domain/<record>.json
for CCIP read.
It’s fully backwards compatible without any breaking changes to active ENSIPs or contenthash format, it works for both IPNS+IPFS and IPNS+IPLD.
IPFS directory to IPLD json format,
example :
{
".well-known": {
"eth": {
"domain": {
"contenthash.json": {
"data": "<abi.encode(contenthash)>"
},
"_addr": {
"60.json": {
"data": "<abi.encode(eth_address)>"
}
},
"avatar.json": {
"data": "<abi.encode(string('eip155:1/erc1155:0xb32979486938aa9694bfc898f35dbed459f44424/10063')>"
}
}
}
}
}
Off-chain records Manager/Resolver contract (WIP),
still missing final signature format/validation.
DNS decode
uint256 index = 1; // domain level index
uint256 i = 1; // counter
uint256 len = uint8(bytes1(name[:1])); // length of label
bytes[] memory _labels = new bytes[](42); // maximum 42 allowed levels in sub.sub...domain.eth
_labels[0] = name[1:i += len];
string memory _path = string(_labels[0]); // suffix after '/.well-known/'
string memory _domain = _path; // full domain as string
/// @dev DNSDecode()
while (name[i] > 0x0) {
len = uint8(bytes1(name[i:++i]));
_labels[index] = name[i:i += len];
_domain = string.concat(_domain, ".", string(_labels[index]));
_path = string.concat(string(_labels[index]), "/", _path);
++index;
}
// check if the name contains .eth as root
// bool dotETH = (keccak256(abi.encodePacked(bytes32(0), keccak256(_labels[index - 1]))) == roothash);
bytes32 _node;
bytes32 _namehash = keccak256(abi.encodePacked(bytes32(0), keccak256(bytes(_labels[--index])))); // MUST be equal to roothash of '.eth'
bytes memory _ipns; // contenthash
while (index > 0) {
_namehash = keccak256(abi.encodePacked(_namehash, keccak256(bytes(_labels[--index]))));
if (contenthash[_namehash].length != 0) {
_ipns = contenthash[_namehash];
_node = _namehash;
}
}
// require(_node == bytes32(data[4:36]), "BAD_NAMEHASH");
bytes4 selector to file name:
bytes4 func = bytes4(data[:4]);
string memory _pathJSON;
if (bytes(funcToFile[func]).length > 0) {
_pathJSON = funcToFile[func];
} else if (func == iResolver.text.selector) {
_pathJSON = abi.decode(data[36:], (string));
} else if (func == iOverloadResolver.addr.selector) {
_pathJSON = string.concat("_addr/", uintToNumString(abi.decode(data[36:], (uint256))));
} else if (func == iResolver.interfaceImplementer.selector) {
_pathJSON =
string.concat("_interface/", bytes2HexString(abi.encodePacked(abi.decode(data[36:], (bytes4))), 0));
} else if (func == iResolver.ABI.selector) {
// recheck this
_pathJSON = string.concat("_abi/", uintToNumString(abi.decode(data[36:], (uint256))));
} else if (func == iResolver.dnsRecord.selector) {
(bytes32 _name, uint16 resource) = abi.decode(data[36:], (bytes32, uint16));
_pathJSON =
string.concat("_dns/", bytes2HexString(abi.encodePacked(_name), 0), "/", uintToNumString(resource));
} else {
revert NotImplemented(func);
}
IPNS Keys :
Deterministic IPNS keys are generated using domain info, owner info text as part of deterministic signature request (SIWx) *we’ve issues with ABNF URI validation for DApps with multi-entry points as SIWx requires fixed URI, so it’s normal signature request instead of full SIWx feature in wallets.
It’s part of SIWx + HKDF that we’re working on to remix all ENS, ETH/L2s, Nostr & IPFS/IPNS as alternative to Whisper (shh) messaging protocol, e.g., our https://dostr.eth.limo using SIWx in Nostr Client to generate deterministic schnorr/secp256k1 keys.