Skip to content

feat: implements setRecord function and unit tests - #6

Merged
0xItadaki merged 11 commits into
devfrom
fet/set-record-and-tests
Aug 20, 2025
Merged

feat: implements setRecord function and unit tests#6
0xItadaki merged 11 commits into
devfrom
fet/set-record-and-tests

Conversation

@0xItadaki

Copy link
Copy Markdown
Collaborator

🤖 Linear

Closes EFI-435, EFI-426

@linear

linear Bot commented Aug 19, 2025

Copy link
Copy Markdown

EFI-435 setRecord

EFI-426 setRecord

  • function setRecord(bytes32 _node, string calldata _key, bytes calldata _value) external /* authorized */
    • Sets raw bytes for a given node and key in the records mapping.
    • Authorization logic is implemented directly within this function (see below).
    • Emits: RecordSet(bytes32 indexed _node, string indexed _key, bytes _value)

Authorization Model

Write operations via setRecord(bytes32 node, string calldata key, bytes calldata value) must be permissioned. The authorization logic is implemented directly within the L2Resolver contract. It should generally follow the ENS ownership model:

  • To set a record for a specific node (e.g., namehash("optimism.l2.eth")), msg.sender must be the beneficial owner of that node (potentially resolved via ENS.owner(node) and checking against INameWrapper.ownerOf(node) if applicable) or an operator approved by the beneficial owner.
  • For non-registered 3LDs (wildcard scenario where node corresponds to something like namehash("unregistered.l2.eth")), msg.sender must be the beneficial owner of the parent domain (e.g., l2.eth) or their approved operator.
  • To set a reverse EIP-7930 chain identifier mapping (i.e., when node == REVERSE_LOOKUP_NODE), msg.sender must be a designated administrative entity (e.g., the owner of l2.eth or a specific authorized address). This control for REVERSE_LOOKUP_NODE writes is crucial for the integrity of reverse lookups.

The L2Resolver will require a reference to the ENS Registry (and potentially INameWrapper) to implement this authorization logic.

Comment thread test/unit/L2Resolver.tree
├── when the caller is an ENSregistry approved operator of the parent
│ ├── it emits the RecordSet event
│ └── it sets the record
├── when the caller is a NameWrapper approved operator of the parent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This branch is probably not realistic for our use case, but it is still a possible option

@0xItadaki 0xItadaki self-assigned this Aug 19, 2025
@0xItadaki
0xItadaki requested a review from 0xAustrian August 19, 2025 16:14
Comment thread src/interfaces/IENS.sol
Comment thread src/interfaces/INameWrapper.sol
assertEq(_l2Resolver.PARENT_NODE(), _parentNodeNameHash);
}

modifier whenTheNodeIsTheReverseLookupNode() {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one was generated by bulloak and I don't really need it. Not sure if it is better to leave it or run it with the option that doesn't generate modifiers. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep it as is

@0xAustrian 0xAustrian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good job. I want to double check the tests again, but looks good.

Comment thread src/interfaces/IENS.sol

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can import this repo as a package and use ENS and INameWrapper, wdyt?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it worth it when we just need a few lines of code for the interfaces? wouldn't it make the contract bigger by importing all those additional functions?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, lets do it your way

Comment thread src/contracts/L2Resolver.sol Outdated
*/
function _getOwner(bytes32 _node) private view returns (address _owner) {
_owner = ENS_REGISTRY.owner(_node);
if (_owner == address(0)) return address(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can safely remove this line, the behaviour will be the same.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would revert if the NAME_WRAPPER is not set since it would try to do NAME_WRAPPER.ownerOf(uint256(_node));, right?

Maybe we should validate that none of those addresses are zero in the constructor?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, do you mind adding that to another linear task?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. I removed the validation in this PR

Comment thread src/contracts/L2Resolver.sol Outdated
Comment thread test/unit/L2Resolver.t.sol Outdated
function test_SetRecordWhenTheCallerIsTheReverseAdmin() external {
// it emits the RecordSet event
vm.prank(_reverseAdmin);
vm.expectEmit(true, true, false, true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vm.expectEmit(); also works

@0xItadaki 0xItadaki Aug 20, 2025

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but that wouldn't be testing if the arguments are indexed, right? Or it doesn't matter since I am emitting that same event that I expect?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think vm.expectEmit() checks all the topics and data.

Comment thread test/unit/L2Resolver.t.sol Outdated
Comment thread src/contracts/L2Resolver.sol
Comment thread src/contracts/L2Resolver.sol

@0xAustrian 0xAustrian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good, will approve once vm.expectEmit is fixed

@0xItadaki
0xItadaki requested a review from 0xAustrian August 20, 2025 12:03
@0xItadaki
0xItadaki merged commit 3ac3ffb into dev Aug 20, 2025
4 checks passed
@0xItadaki
0xItadaki deleted the fet/set-record-and-tests branch August 20, 2025 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants