Expanding Beyond Mainnet

I played around with this idea and created MerkleResolver.sol → Goerli Contract

The idea is that you create a shape using createShape(bytes32[]) -> bytes32 , which expects an array of cells, which are hashes of record fields, like contenthash, addr(60), text(avatar). You then assign a shape to a node using setShape(node, shape).

After that, everything works normally. When you edit a record, it clears the merkle root. When you’re done editing records, you can commit(node) and it will compute and store the merkle root. You can only set records that exist on your shape.

MerkleResolver r = new MerkleResolver();
bytes32[] memory cells = new bytes32[](3);
cells[0] = r.CELL_CONTENTHASH();
cells[1] = r.cellForAddr(60);
cells[2] = r.cellForText("avatar");
bytes32 shape = r.createShape(cells);
bytes32 node = namehash("merkle.eth");
r.setShape(node, shape);
r.setText(node, "avatar", "https://...");
r.setAddr(node, 0x51050ec063d393217B436747617aD1C2285Aeeee);
r.setContenthash(node, hex"1234");
bytes32 root = r.commit(node);

If you have a shape for cells [A,B,C], you can upgrade your shape to [A,B,C,D,…] without changing the storage layout. Shapes can be shared between nodes. A “Standard” shape could contain the records typically shown.

The root is computed as a right-padded tree with nulls. If you set a shape with 6 cells, it will delete the values for [7, 8] and then only allow storing 1-6 by mapping from setText/setAddr/setContenthash → cell → index of cell

/ \ / \ / \ / \ 
1 2 3 4 5 6 X X

Instead of manual commit(), there could be a boolean, which when true, auto-commits after a change is made. It needs multicall(), a few other features like expandShape(), and some gas analysis.