-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathL2Resolver.sol
More file actions
116 lines (100 loc) · 4.73 KB
/
Copy pathL2Resolver.sol
File metadata and controls
116 lines (100 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {IERC165} from 'openzeppelin-contracts/interfaces/IERC165.sol';
import {IENS} from 'src/interfaces/IENS.sol';
import {IExtendedResolver} from 'src/interfaces/IExtendedResolver.sol';
import {IL2Resolver} from 'src/interfaces/IL2Resolver.sol';
import {INameWrapper} from 'src/interfaces/INameWrapper.sol';
/**
* @title L2Resolver
* @author @defi-wonderland
* @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, IERC165 {
/// @inheritdoc IL2Resolver
bytes32 public 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;
/// @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;
}
/// @inheritdoc IL2Resolver
function setRecord(bytes32 _node, string calldata _key, bytes calldata _value) external {
if (_node == REVERSE_LOOKUP_NODE) {
// If the node is the reverse lookup node, then the caller must be the reverse admin
if (msg.sender != REVERSE_ADMIN) revert NotAuthorized();
} else {
address _owner = _getOwner(_node);
if (_owner != address(0)) {
// If the node is owned by the ENSRegistry, then the caller must be an approved operator or the owner
if (!_isApprovedOperatorOrOwner(msg.sender, _owner)) revert NotAuthorized();
} else {
// If the node is not owned by the ENSRegistry, then it could be a wildcard and caller must be an approved
// operator or the owner of the parent node
address _parentOwner = _getOwner(PARENT_NODE);
if (_parentOwner == address(0)) revert NotAuthorized();
if (!_isApprovedOperatorOrOwner(msg.sender, _parentOwner)) revert NotAuthorized();
}
}
records[_node][_key] = _value;
emit RecordSet(_node, _key, _value);
}
/// @inheritdoc IL2Resolver
function getRecord(bytes32 _node, string calldata _key) external view returns (bytes memory _data) {
_data = records[_node][_key];
}
/// @inheritdoc IL2Resolver
function chainId(bytes32 _node) external view returns (bytes memory _chainId) {
_chainId = this.getRecord(_node, CHAIN_IDENTIFIER_EIP7930_KEY);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool _isSupported) {
_isSupported = _interfaceId == type(IERC165).interfaceId || _interfaceId == type(IExtendedResolver).interfaceId;
}
/**
* @notice Returns the owner of a node.
* @dev If the node is the NameWrapper, then the domain is wrapped and it gets the owner from the NameWrapper.
* @dev If the owner is missing, then the domain is not registered or it could be a wildcard node.
* @param _node The node to get the owner of.
* @return _owner The owner of the node. Returns address(0) if the node is not owned by the ENSRegistry.
*/
function _getOwner(bytes32 _node) private view returns (address _owner) {
_owner = ENS_REGISTRY.owner(_node);
if (_owner == address(NAME_WRAPPER)) {
_owner = NAME_WRAPPER.ownerOf(uint256(_node));
}
}
/**
* @notice Returns whether the caller is an owner or an approved operator for the owner
* @param _caller The address to check.
* @param _owner The owner of the node.
* @return _approved True if the _caller is an owner or an approved operator for the _owner, false otherwise.
*/
function _isApprovedOperatorOrOwner(address _caller, address _owner) private view returns (bool _approved) {
if (_owner == _caller) return true;
if (ENS_REGISTRY.isApprovedForAll(_owner, _caller)) return true;
if (address(NAME_WRAPPER) != address(0) && NAME_WRAPPER.isApprovedForAll(_owner, _caller)) return true;
}
}