Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/contracts/L2Resolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT

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.

Empty space here

pragma solidity ^0.8.30;

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.

Let's use a fixed .sol version.

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.

why is that?

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 mean it's no big deal but a good practice to use the exact same version everywhere.


import {IENS} from 'src/interfaces/IENS.sol';
import {IL2Resolver} from 'src/interfaces/IL2Resolver.sol';
import {INameWrapper} from 'src/interfaces/INameWrapper.sol';

/**
* @title L2Resolver
* @author @defi-wonderland

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.

do we usually set the author?

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.

Yes sir

* @notice Minimal L2 Resolver system for ENS. It resolves ENS names to their corresponding
* formatted chain identifiers and allows reverse lookup by mapping them back to their primary ENS names.
* EIP-7930 is an example of supported chain identifier.
*/
contract L2Resolver is IL2Resolver {
/// @notice A dedicated node hash for storing reverse lookup entries within this contract's records mapping.
bytes32 private constant _REVERSE_LOOKUP_NODE = bytes32(keccak256('reverse.chain.id.eip7930'));

/// @inheritdoc IL2Resolver
string public constant CHAIN_IDENTIFIER_EIP7930_KEY = 'chain.id.eip7930';

/// @inheritdoc IL2Resolver
IENS public immutable ENS_REGISTRY;

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.

@0xAustrian what do you think about the visibility of these variables? I went with public so I could unit test them. I kept _REVERSE_LOOKUP_NODE since it will be used in internal logic

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 would set everything on public tbh, even REVERSE_LOOKUP_NODE


/// @inheritdoc IL2Resolver
INameWrapper public immutable NAME_WRAPPER;

/// @inheritdoc IL2Resolver
address public immutable REVERSE_ADMIN;

/// @inheritdoc IL2Resolver
bytes32 public immutable PARENT_NODE;

/// @inheritdoc IL2Resolver
mapping(bytes32 _node => mapping(string _key => bytes _data)) public records;

/**
* @notice Constructor
* @param _ensRegistry The ENS registry address.
* @param _nameWrapper The ENS name wrapper address.
* @param _reverseAdmin The reverse admin address.
* @param _parentNode The parent node namehash.
*/
constructor(address _ensRegistry, address _nameWrapper, address _reverseAdmin, bytes32 _parentNode) {
ENS_REGISTRY = IENS(_ensRegistry);
NAME_WRAPPER = INameWrapper(_nameWrapper);
REVERSE_ADMIN = _reverseAdmin;
PARENT_NODE = _parentNode;
}
}
9 changes: 9 additions & 0 deletions src/interfaces/IENS.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/**
* @title IENS
* @author @defi-wonderland
* @notice Interface for the ENS registry.
*/
interface IENS {}
72 changes: 72 additions & 0 deletions src/interfaces/IL2Resolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.30;

import {IENS} from './IENS.sol';
import {INameWrapper} from './INameWrapper.sol';

/**
* @title IL2Resolver
* @author @defi-wonderland
*/
interface IL2Resolver {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/

/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/

/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/**
* @notice The key used in records to store the EIP-7930 chain identifier bytes for a forward resolution.
* @return _key The key used for a forward resolution.
*/
function CHAIN_IDENTIFIER_EIP7930_KEY() external view returns (string memory _key);

/**
* @notice Stores arbitrary data for each ENS node.
* @dev For domain-to-chain-identifier resolution, the key will be a pre-defined string.
* (e.g., CHAIN_IDENTIFIER_EIP7930_KEY) and data will be the raw EIP-7930 chain identifier
* bytes (formatted with AddressLength = 0).
* @dev For EIP-7930-chain-identifier-to-domain resolution (reverse lookup), the REVERSE_LOOKUP_NODE is used.
* The key is constructed by concatenating a prefix (e.g., CHAIN_IDENTIFIER_EIP7930_KEY)with the hexadecimal string
* representation of the keccak256 hash of the EIP-7930 chain identifier bytes. The data stored is the
* ABI-encoded human-readable ENS name string (e.g., abi.encode("optimism.l2.eth")).
* @param _node The ENS node to store the data for.
* @param _key The key to store the data under.
* @return _data The data stored under the key.
*/
function records(bytes32 _node, string calldata _key) external view returns (bytes memory _data);

/**
* @notice The ENS registry.
* @return _ensRegistry The ENS registry address.
*/
function ENS_REGISTRY() external view returns (IENS _ensRegistry);

/**
* @notice The ENS name wrapper.
* @return _nameWrapper The ENS name wrapper address.
*/
function NAME_WRAPPER() external view returns (INameWrapper _nameWrapper);

/**
* @notice The admin authorized to set the reverse lookup node values.
* @return _adminAddress The admin address.
*/
function REVERSE_ADMIN() external view returns (address _adminAddress);

/**
* @notice The parent node namehash for wildcard resolution and authorization.
* @return _parentNode The parent node namehash.
*/
function PARENT_NODE() external view returns (bytes32 _parentNode);
}
9 changes: 9 additions & 0 deletions src/interfaces/INameWrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

/**
* @title INameWrapper
* @author @defi-wonderland
* @notice Interface for the ENS Name Wrapper.
*/
interface INameWrapper {}
10 changes: 10 additions & 0 deletions test/integration/L2Resolver.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.

This is so the CI does not fail, right? In that case I think we can temporarily comment the integration tests section in the workflow (sorry, forgot to mention that. It would have been a good addition to the PR#1).

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {Test} from 'forge-std/Test.sol';

contract IntegrationL2Resolver is Test {
function test() external {
vm.skip(true);
}
}
28 changes: 28 additions & 0 deletions test/unit/L2Resolver.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {Test} from 'forge-std/Test.sol';
import {L2Resolver} from 'src/contracts/L2Resolver.sol';

contract UnitL2Resolverconstructor is Test {
L2Resolver internal _l2Resolver;
address internal _ensRegistry = makeAddr('ENSRegistry');
address internal _nameWrapper = makeAddr('NameWrapper');
address internal _reverseAdmin = makeAddr('ReverseAdmin');
bytes32 internal _parentNode = bytes32(abi.encode('l2.eth'));

function setUp() external {
_l2Resolver = new L2Resolver(_ensRegistry, _nameWrapper, _reverseAdmin, _parentNode);
}

function test_WhenDeployingTheContract() external view {
// it sets the ENS registry address
assertEq(address(_l2Resolver.ENS_REGISTRY()), _ensRegistry);
// it sets the name wrapper address
assertEq(address(_l2Resolver.NAME_WRAPPER()), _nameWrapper);
// it sets the reverse admin address
assertEq(_l2Resolver.REVERSE_ADMIN(), _reverseAdmin);
// it sets the parent node namehash
assertEq(_l2Resolver.PARENT_NODE(), _parentNode);
}
}
6 changes: 6 additions & 0 deletions test/unit/L2Resolver.tree
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
UnitL2Resolver::constructor
└── when deploying the contract
├── it sets the ENS registry address
├── it sets the name wrapper address
├── it sets the reverse admin address
└── it sets the parent node namehash
Loading