Skip to content

Commit 359b3fb

Browse files
committed
Finish Addr Resolver + unit tests
1 parent 9f1cf35 commit 359b3fb

File tree

3 files changed

+97
-13
lines changed

3 files changed

+97
-13
lines changed

src/L2/resolver/ABIResolver.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {ResolverBase} from "./ResolverBase.sol";
1414
/// @author Coinbase (https://github.com/base-org/basenames)
1515
abstract contract ABIResolver is IABIResolver, ResolverBase {
1616
struct ABIResolverStorage {
17-
/// @notice ABI storage (`bytes`) by content type, node, and version.
17+
/// @notice ABI record (`bytes`) by content type, node, and version.
1818
mapping(uint64 version => mapping(bytes32 node => mapping(uint256 contentType => bytes data))) versionable_abis;
1919
}
2020

2121
/// @notice Thrown when setting an ABI with an invalid content type.
2222
error InvalidContentType();
2323

24-
/// @notice EIP-7201 storage location
24+
/// @notice EIP-7201 storage location.
2525
/// keccak256(abi.encode(uint256(keccak256("abi.resolver.storage")) - 1)) & ~bytes32(uint256(0xff));
2626
bytes32 private constant ABI_RESOLVER_STORAGE = 0x76dc89e1c49d3cda8f11a131d381f3dbd0df1919a4e1a669330a2763d2821400;
2727

src/L2/resolver/AddrResolver.sol

+41-11
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,45 @@ import {IAddressResolver} from "ens-contracts/resolvers/profiles/IAddressResolve
66

77
import {ResolverBase} from "./ResolverBase.sol";
88

9+
/// @title AddrResolver
10+
///
11+
/// @notice ENSIP-11 compliant Address Resolver. Adaptation of the ENS AddrResolver.sol profile contract, with
12+
/// EIP-7201 storage compliance.
13+
/// https://github.com/ensdomains/ens-contracts/blob/staging/contracts/resolvers/profiles/AddrResolver.sol
14+
///
15+
/// @author Coinbase (https://github.com/base-org/basenames)
916
abstract contract AddrResolver is IAddrResolver, IAddressResolver, ResolverBase {
1017
struct AddrResolverStorage {
18+
/// @notice Address record per cointype, node and version.
1119
mapping(uint64 version => mapping(bytes32 node => mapping(uint256 cointype => bytes addr)))
1220
versionable_addresses;
1321
}
1422

23+
/// @notice EIP-7201 storage location.
1524
// keccak256(abi.encode(uint256(keccak256("addr.resolver.storage")) - 1)) & ~bytes32(uint256(0xff));
1625
bytes32 constant ADDR_RESOLVER_STORAGE = 0x1871a91a9a944f867849820431bb11c2d1625edae573523bceb5b38b8b8a7500;
1726

27+
/// @notice Ethereum mainnet network-as-cointype.
1828
uint256 private constant COIN_TYPE_ETH = 60;
1929

20-
/**
21-
* Sets the address associated with an ENS node.
22-
* May only be called by the owner of that node in the ENS registry.
23-
* @param node The node to update.
24-
* @param a The address to set.
25-
*/
30+
31+
/// @notice Sets the address associated with an ENS node.
32+
///
33+
/// @dev May only be called by the owner of that node in the ENS registry.
34+
///
35+
/// @param node The node to update.
36+
/// @param a The address to set.
2637
function setAddr(bytes32 node, address a) external virtual authorised(node) {
2738
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
2839
}
2940

30-
/**
31-
* Returns the address associated with an ENS node.
32-
* @param node The ENS node to query.
33-
* @return The associated address.
34-
*/
41+
/// @notice Returns the address associated with a specified ENS `node`.
42+
///
43+
/// @dev Returns the `addr` record for the Ethereum Mainnet network-as-cointype.
44+
///
45+
/// @param node The ENS node to query.
46+
///
47+
/// @return The associated address.
3548
function addr(bytes32 node) public view virtual override returns (address payable) {
3649
bytes memory a = addr(node, COIN_TYPE_ETH);
3750
if (a.length == 0) {
@@ -40,6 +53,11 @@ abstract contract AddrResolver is IAddrResolver, IAddressResolver, ResolverBase
4053
return bytesToAddress(a);
4154
}
4255

56+
/// @notice Set the network or coin-specific address for an ENS `node`.
57+
///
58+
/// @param node The ENS node to update.
59+
/// @param coinType The coinType for this address.
60+
/// @param a The network-agnostic bytes of the address.
4361
function setAddr(bytes32 node, uint256 coinType, bytes memory a) public virtual authorised(node) {
4462
emit AddressChanged(node, coinType, a);
4563
if (coinType == COIN_TYPE_ETH) {
@@ -49,29 +67,41 @@ abstract contract AddrResolver is IAddrResolver, IAddressResolver, ResolverBase
4967
= a;
5068
}
5169

70+
/// @notice Returns the address of the `node` for a specified `coinType`.
71+
///
72+
/// @dev Complies with ENSIP-9 and ENSIP-11.
73+
///
74+
/// @param node The ENS node to update.
75+
/// @param coinType The coinType to fetch.
76+
///
77+
/// @return The address of the specified `node` for the specified `coinType`.
5278
function addr(bytes32 node, uint256 coinType) public view virtual override returns (bytes memory) {
5379
return _getAddrResolverStorage().versionable_addresses[_getResolverBaseStorage().recordVersions[node]][node][coinType];
5480
}
5581

82+
/// @notice ERC-165 compliance.
5683
function supportsInterface(bytes4 interfaceID) public view virtual override returns (bool) {
5784
return interfaceID == type(IAddrResolver).interfaceId || interfaceID == type(IAddressResolver).interfaceId
5885
|| super.supportsInterface(interfaceID);
5986
}
6087

88+
/// @notice Helper to convert bytes into an EVM address object.
6189
function bytesToAddress(bytes memory b) internal pure returns (address payable a) {
6290
require(b.length == 20);
6391
assembly {
6492
a := div(mload(add(b, 32)), exp(256, 12))
6593
}
6694
}
6795

96+
/// @notice Helper to convert an EVM address to a bytes object.
6897
function addressToBytes(address a) internal pure returns (bytes memory b) {
6998
b = new bytes(20);
7099
assembly {
71100
mstore(add(b, 32), mul(a, exp(256, 12)))
72101
}
73102
}
74103

104+
/// @notice EIP-7201 storage pointer fetch helper.
75105
function _getAddrResolverStorage() internal pure returns (AddrResolverStorage storage $) {
76106
assembly {
77107
$.slot := ADDR_RESOLVER_STORAGE
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.23;
3+
4+
import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol";
5+
import {AddrResolver} from "src/L2/resolver/AddrResolver.sol";
6+
7+
contract SetAddr is UpgradeableL2ResolverBase {
8+
uint256 BTC_COINTYPE = 0;
9+
uint256 ETH_COINTYPE = 60;
10+
uint256 BASE_COINTYPE = 2147492101;
11+
12+
function test_setsAnETHAddress_byDefault(address a) public {
13+
vm.prank(user);
14+
resolver.setAddr(node, a);
15+
assertEq(resolver.addr(node), a);
16+
assertEq(bytesToAddress(resolver.addr(node, ETH_COINTYPE)), a);
17+
}
18+
19+
function test_setsAnETHAddress(address a) public {
20+
vm.prank(user);
21+
resolver.setAddr(node, ETH_COINTYPE, addressToBytes(a));
22+
assertEq(resolver.addr(node), a);
23+
assertEq(bytesToAddress(resolver.addr(node, ETH_COINTYPE)), a);
24+
}
25+
26+
function test_setsABaseAddress(address a) public {
27+
vm.prank(user);
28+
resolver.setAddr(node, BASE_COINTYPE, addressToBytes(a));
29+
assertEq(bytesToAddress(resolver.addr(node, BASE_COINTYPE)), a);
30+
}
31+
32+
function test_setsABtcAddress() public {
33+
bytes memory satoshi = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa";
34+
vm.prank(user);
35+
resolver.setAddr(node, BTC_COINTYPE, satoshi);
36+
assertEq(keccak256(resolver.addr(node, BTC_COINTYPE)), keccak256(satoshi));
37+
}
38+
39+
/// @notice Helper to convert bytes into an EVM address object.
40+
function bytesToAddress(bytes memory b) internal pure returns (address payable a) {
41+
require(b.length == 20);
42+
assembly {
43+
a := div(mload(add(b, 32)), exp(256, 12))
44+
}
45+
}
46+
47+
/// @notice Helper to convert an EVM address to a bytes object.
48+
function addressToBytes(address a) internal pure returns (bytes memory b) {
49+
b = new bytes(20);
50+
assembly {
51+
mstore(add(b, 32), mul(a, exp(256, 12)))
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)