Skip to content

Commit 9e31679

Browse files
authored
Change MigratedWrappedNameRegistry.getResolver() (#150)
* added ENSV1Resolver.sol for V1-only fallback * added ENSV1Resolver.test.ts and deploy script * changed MigratedWrappedNameRegistry.getResolver() * updated MigratedWrappedNameRegistry.t.sol
1 parent a6e60b3 commit 9e31679

File tree

10 files changed

+303
-214
lines changed

10 files changed

+303
-214
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { artifacts, execute } from "@rocketh";
2+
3+
export default execute(
4+
async ({ get, deploy, namedAccounts: { deployer } }) => {
5+
const ensRegistryV1 =
6+
get<(typeof artifacts.ENSRegistry)["abi"]>("ENSRegistry");
7+
8+
const batchGatewayProvider = get<(typeof artifacts.GatewayProvider)["abi"]>(
9+
"BatchGatewayProvider",
10+
);
11+
12+
await deploy("ENSV1Resolver", {
13+
account: deployer,
14+
artifact: artifacts.ENSV1Resolver,
15+
args: [ensRegistryV1.address, batchGatewayProvider.address],
16+
});
17+
},
18+
{
19+
tags: ["ENSV1Resolver", "l1"],
20+
dependencies: ["ENSRegistry", "BatchGatewayProvider"],
21+
},
22+
);

contracts/deploy/l1/03_MigratedWrappedNameRegistry.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export default execute(
2121
const verifiableFactory =
2222
get<(typeof artifacts.VerifiableFactory)["abi"]>("VerifiableFactory");
2323

24+
const ensV1Resolver =
25+
get<(typeof artifacts.ENSV1Resolver)["abi"]>("ENSV1Resolver");
26+
2427
await deploy("MigratedWrappedNameRegistryImpl", {
2528
account: deployer,
2629
artifact: artifacts.MigratedWrappedNameRegistry,
@@ -31,6 +34,7 @@ export default execute(
3134
registryDatastore.address,
3235
hcaFactory.address,
3336
registryMetadata.address,
37+
ensV1Resolver.address,
3438
],
3539
});
3640
},
@@ -41,6 +45,7 @@ export default execute(
4145
"HCAFactory",
4246
"ETHRegistry",
4347
"VerifiableFactory",
48+
"ENSV1Resolver",
4449
],
4550
},
4651
);

contracts/foundry.lock

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,20 @@
1111
"lib/forge-std": {
1212
"rev": "77041d2ce690e692d6e03cc812b57d1ddaa4d505"
1313
},
14-
"lib/openzeppelin-contracts": {
15-
"rev": "e4f70216d759d8e6a64144a9e1f7bbeed78e7079"
16-
},
1714
"lib/openzeppelin-contracts-upgradeable": {
1815
"rev": "5fc3fee14043035097ae718387200f4f4daaa982"
1916
},
2017
"lib/openzeppelin-contracts-v4": {
2118
"rev": "54b3f14346da01ba0d159114b399197fea8b7cda"
2219
},
23-
"lib/solsha1": {
24-
"rev": "c4fbe97cf5e8c1b8d607001588fd23abb5bfb923"
20+
"lib/buffer": {
21+
"rev": "82cc81935de1d1a82e021cf1030d902c5248982b"
22+
},
23+
"lib/openzeppelin-contracts": {
24+
"rev": "e4f70216d759d8e6a64144a9e1f7bbeed78e7079"
25+
},
26+
"lib/verifiable-factory": {
27+
"rev": "c47c0e61ce03b3ab5891a3b743287b54aee9f021"
2528
},
2629
"lib/surge-taiko-mono": {
2730
"tag": {
@@ -32,7 +35,10 @@
3235
"lib/unruggable-gateways": {
3336
"rev": "3d68742832c68bf9a158c7bcd1aa47fad511b213"
3437
},
35-
"lib/verifiable-factory": {
36-
"rev": "c47c0e61ce03b3ab5891a3b743287b54aee9f021"
38+
"lib/solsha1": {
39+
"rev": "c4fbe97cf5e8c1b8d607001588fd23abb5bfb923"
40+
},
41+
"lib/ens-contracts": {
42+
"rev": "a392133a641f7a88dd8d08616e1113e1b6d0416a"
3743
}
3844
}

contracts/src/L1/registry/MigratedWrappedNameRegistry.sol

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity >=0.8.13;
33

4-
import {RegistryUtils} from "@ens/contracts/universalResolver/RegistryUtils.sol";
54
import {NameCoder} from "@ens/contracts/utils/NameCoder.sol";
65
import {INameWrapper, PARENT_CANNOT_CONTROL} from "@ens/contracts/wrapper/INameWrapper.sol";
76
import {VerifiableFactory} from "@ensdomains/verifiable-factory/VerifiableFactory.sol";
@@ -21,7 +20,6 @@ import {IRegistryMetadata} from "../../common/registry/interfaces/IRegistryMetad
2120
import {IStandardRegistry} from "../../common/registry/interfaces/IStandardRegistry.sol";
2221
import {RegistryRolesLib} from "../../common/registry/libraries/RegistryRolesLib.sol";
2322
import {PermissionedRegistry} from "../../common/registry/PermissionedRegistry.sol";
24-
import {LibLabel} from "../../common/utils/LibLabel.sol";
2523
import {LockedNamesLib} from "../migration/libraries/LockedNamesLib.sol";
2624
import {ParentNotMigrated, LabelNotMigrated} from "../migration/MigrationErrors.sol";
2725

@@ -54,6 +52,8 @@ contract MigratedWrappedNameRegistry is
5452

5553
IPermissionedRegistry public immutable ETH_REGISTRY;
5654

55+
address public immutable FALLBACK_RESOLVER;
56+
5757
////////////////////////////////////////////////////////////////////////
5858
// Storage
5959
////////////////////////////////////////////////////////////////////////
@@ -76,11 +76,13 @@ contract MigratedWrappedNameRegistry is
7676
VerifiableFactory factory,
7777
IRegistryDatastore datastore,
7878
IHCAFactoryBasic hcaFactory,
79-
IRegistryMetadata metadataProvider
79+
IRegistryMetadata metadataProvider,
80+
address fallbackResolver
8081
) PermissionedRegistry(datastore, hcaFactory, metadataProvider, _msgSender(), 0) {
8182
NAME_WRAPPER = nameWrapper;
8283
ETH_REGISTRY = ethRegistry;
8384
FACTORY = factory;
85+
FALLBACK_RESOLVER = fallbackResolver;
8486
// Prevents initialization on the implementation contract
8587
_disableInitializers();
8688
}
@@ -171,36 +173,20 @@ contract MigratedWrappedNameRegistry is
171173
return this.onERC1155BatchReceived.selector;
172174
}
173175

174-
function getResolver(string calldata label) external view override returns (address) {
175-
uint256 canonicalId = LibLabel.labelToCanonicalId(label);
176-
IRegistryDatastore.Entry memory entry = this.getEntry(canonicalId);
177-
uint64 expires = entry.expiry;
178-
179-
// Use fallback resolver for unregistered names
180-
if (expires == 0) {
181-
// Construct complete domain name for registry lookup
182-
bytes memory dnsEncodedName = abi.encodePacked(
183-
bytes1(uint8(bytes(label).length)),
184-
label,
185-
parentDnsEncodedName
186-
);
187-
188-
// Retrieve resolver from legacy registry system
189-
(address resolverAddress, , ) = RegistryUtils.findResolver(
190-
NAME_WRAPPER.ens(),
191-
dnsEncodedName,
192-
0
193-
);
194-
return resolverAddress;
195-
}
196-
197-
// Return no resolver for expired names
198-
if (expires <= block.timestamp) {
199-
return address(0);
176+
/// @inheritdoc PermissionedRegistry
177+
/// @dev Restore the latest resolver to `FALLBACK_RESOLVER` upon visiting migratable children.
178+
function getResolver(
179+
string calldata label
180+
) public view override(PermissionedRegistry) returns (address) {
181+
bytes32 node = NameCoder.namehash(
182+
NameCoder.namehash(parentDnsEncodedName, 0),
183+
keccak256(bytes(label))
184+
);
185+
(address owner, uint32 fuses, ) = NAME_WRAPPER.getData(uint256(node));
186+
if (owner != address(this) && (fuses & PARENT_CANNOT_CONTROL) != 0) {
187+
return FALLBACK_RESOLVER;
200188
}
201-
202-
// Return the configured resolver for registered names
203-
return entry.resolver;
189+
return super.getResolver(label);
204190
}
205191

206192
////////////////////////////////////////////////////////////////////////
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.13;
3+
4+
import {CCIPReader} from "@ens/contracts/ccipRead/CCIPReader.sol";
5+
import {IGatewayProvider} from "@ens/contracts/ccipRead/IGatewayProvider.sol";
6+
import {IExtendedResolver} from "@ens/contracts/resolvers/profiles/IExtendedResolver.sol";
7+
import {ResolverFeatures} from "@ens/contracts/resolvers/ResolverFeatures.sol";
8+
import {RegistryUtils, ENS} from "@ens/contracts/universalResolver/RegistryUtils.sol";
9+
import {ResolverCaller} from "@ens/contracts/universalResolver/ResolverCaller.sol";
10+
import {IERC7996} from "@ens/contracts/utils/IERC7996.sol";
11+
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
12+
13+
/// @notice Resolver that performs resolutions using ENSv1.
14+
///
15+
/// Basically an UniversalResolverV1 (ResolverCaller + RegistryUtils) that implements IExtendedResolver.
16+
///
17+
contract ENSV1Resolver is IExtendedResolver, IERC7996, ResolverCaller, ERC165 {
18+
////////////////////////////////////////////////////////////////////////
19+
// Constants
20+
////////////////////////////////////////////////////////////////////////
21+
22+
ENS public immutable REGISTRY_V1;
23+
24+
/// @dev Shared batch gateway provider.
25+
IGatewayProvider public immutable BATCH_GATEWAY_PROVIDER;
26+
27+
////////////////////////////////////////////////////////////////////////
28+
// Initialization
29+
////////////////////////////////////////////////////////////////////////
30+
31+
constructor(
32+
ENS registryV1,
33+
IGatewayProvider batchGatewayProvider
34+
) CCIPReader(DEFAULT_UNSAFE_CALL_GAS) {
35+
REGISTRY_V1 = registryV1;
36+
BATCH_GATEWAY_PROVIDER = batchGatewayProvider;
37+
}
38+
39+
/// @inheritdoc ERC165
40+
function supportsInterface(
41+
bytes4 interfaceId
42+
) public view virtual override(ERC165) returns (bool) {
43+
return
44+
type(IExtendedResolver).interfaceId == interfaceId ||
45+
type(IERC7996).interfaceId == interfaceId ||
46+
super.supportsInterface(interfaceId);
47+
}
48+
49+
/// @inheritdoc IERC7996
50+
function supportsFeature(bytes4 feature) external pure returns (bool) {
51+
return ResolverFeatures.RESOLVE_MULTICALL == feature;
52+
}
53+
54+
////////////////////////////////////////////////////////////////////////
55+
// Implementation
56+
////////////////////////////////////////////////////////////////////////
57+
58+
function resolve(
59+
bytes calldata name,
60+
bytes calldata data
61+
) external view returns (bytes memory) {
62+
(address resolver, , ) = RegistryUtils.findResolver(REGISTRY_V1, name, 0);
63+
callResolver(resolver, name, data, false, "", BATCH_GATEWAY_PROVIDER.gateways());
64+
}
65+
}

contracts/src/common/registry/PermissionedRegistry.sol

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -93,22 +93,6 @@ contract PermissionedRegistry is
9393
emit ResolverUpdated(tokenId, resolver);
9494
}
9595

96-
/// @inheritdoc IRegistry
97-
function getSubregistry(
98-
string calldata label
99-
) external view virtual override(BaseRegistry, IRegistry) returns (IRegistry) {
100-
IRegistryDatastore.Entry memory entry = getEntry(LibLabel.labelToCanonicalId(label));
101-
return _isExpired(entry.expiry) ? IRegistry(address(0)) : entry.subregistry;
102-
}
103-
104-
/// @inheritdoc IRegistry
105-
function getResolver(
106-
string calldata label
107-
) external view virtual override(BaseRegistry, IRegistry) returns (address) {
108-
IRegistryDatastore.Entry memory entry = getEntry(LibLabel.labelToCanonicalId(label));
109-
return _isExpired(entry.expiry) ? address(0) : entry.resolver;
110-
}
111-
11296
/// @inheritdoc IPermissionedRegistry
11397
function getTokenObserver(uint256 anyId) external view returns (ITokenObserver) {
11498
return _tokenObservers[LibLabel.getCanonicalId(anyId)];
@@ -178,6 +162,22 @@ contract PermissionedRegistry is
178162
return super.revokeRoles(getResource(anyId), roleBitmap, account);
179163
}
180164

165+
/// @inheritdoc IRegistry
166+
function getSubregistry(
167+
string calldata label
168+
) public view virtual override(BaseRegistry, IRegistry) returns (IRegistry) {
169+
IRegistryDatastore.Entry memory entry = getEntry(LibLabel.labelToCanonicalId(label));
170+
return _isExpired(entry.expiry) ? IRegistry(address(0)) : entry.subregistry;
171+
}
172+
173+
/// @inheritdoc IRegistry
174+
function getResolver(
175+
string calldata label
176+
) public view virtual override(BaseRegistry, IRegistry) returns (address) {
177+
IRegistryDatastore.Entry memory entry = getEntry(LibLabel.labelToCanonicalId(label));
178+
return _isExpired(entry.expiry) ? address(0) : entry.resolver;
179+
}
180+
181181
/// @inheritdoc ERC1155Singleton
182182
function uri(uint256 tokenId) public view override returns (string memory) {
183183
return _tokenURI(tokenId);

contracts/test/integration/fixtures/deployV1Fixture.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,8 @@ export async function deployV1Fixture(
119119
);
120120
}
121121
// set resolver on leaf
122-
await ensRegistry.write.setResolver([namehash(name), resolverAddress], {
123-
account,
124-
});
125-
return { name, labels, resolverAddress };
122+
const node = namehash(name);
123+
await ensRegistry.write.setResolver([node, resolverAddress], { account });
124+
return { name, labels, resolverAddress, node };
126125
}
127126
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { shouldSupportInterfaces } from "@ensdomains/hardhat-chai-matchers-viem/behaviour";
2+
import hre from "hardhat";
3+
import { describe, it } from "vitest";
4+
5+
import {
6+
type KnownProfile,
7+
bundleCalls,
8+
makeResolutions,
9+
} from "../../utils/resolutions.js";
10+
import { shouldSupportFeatures } from "../../utils/supportsFeatures.js";
11+
import { dnsEncodeName } from "../../utils/utils.js";
12+
import { deployV1Fixture } from "../fixtures/deployV1Fixture.js";
13+
import { deployV2Fixture } from "../fixtures/deployV2Fixture.js";
14+
15+
const network = await hre.network.connect();
16+
17+
async function fixture() {
18+
const mainnetV1 = await deployV1Fixture(network, true);
19+
const mainnetV2 = await deployV2Fixture(network, true);
20+
const ensV1Resolver = await network.viem.deployContract("ENSV1Resolver", [
21+
mainnetV1.ensRegistry.address,
22+
mainnetV1.batchGatewayProvider.address,
23+
]);
24+
return { mainnetV1, mainnetV2, ensV1Resolver };
25+
}
26+
27+
describe("ENSV1Resolver", () => {
28+
/// https://github.com/ensdomains/ens-contracts/blob/staging/test/universalResolver/TestResolverCaller.test.ts
29+
/// https://github.com/ensdomains/ens-contracts/blob/staging/test/universalResolver/TestUniversalResolver.test.ts
30+
31+
shouldSupportInterfaces({
32+
contract: () =>
33+
network.networkHelpers.loadFixture(fixture).then((F) => F.ensV1Resolver),
34+
interfaces: ["IERC165", "IERC7996", "IExtendedResolver"],
35+
});
36+
37+
shouldSupportFeatures({
38+
contract: () =>
39+
network.networkHelpers.loadFixture(fixture).then((F) => F.ensV1Resolver),
40+
features: {
41+
RESOLVER: ["RESOLVE_MULTICALL"],
42+
},
43+
});
44+
45+
it("2LD", async () => {
46+
const F = await network.networkHelpers.loadFixture(fixture);
47+
const kp: KnownProfile = {
48+
name: "test.eth",
49+
};
50+
const res = bundleCalls(makeResolutions(kp));
51+
await F.mainnetV1.setupName(kp);
52+
await F.mainnetV1.publicResolver.write.multicall([
53+
res.resolutions.map((x) => x.write),
54+
]);
55+
res.expect(
56+
await F.ensV1Resolver.read.resolve([dnsEncodeName(kp.name), res.call]),
57+
);
58+
});
59+
60+
it("3LD", async () => {
61+
const F = await network.networkHelpers.loadFixture(fixture);
62+
const kp: KnownProfile = {
63+
name: "sub.test.eth",
64+
};
65+
const res = bundleCalls(makeResolutions(kp));
66+
await F.mainnetV1.setupName(kp);
67+
await F.mainnetV1.publicResolver.write.multicall([
68+
res.resolutions.map((x) => x.write),
69+
]);
70+
res.expect(
71+
await F.ensV1Resolver.read.resolve([dnsEncodeName(kp.name), res.call]),
72+
);
73+
});
74+
});

contracts/test/unit/L1/migration/L1LockedMigrationController.t.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ contract L1LockedMigrationControllerTest is Test, ERC1155Holder {
111111

112112
address owner = address(this);
113113
address user = address(0x1234);
114+
address fallbackResolver = address(0);
114115

115116
string testLabel = "test";
116117
uint256 testTokenId;
@@ -130,7 +131,8 @@ contract L1LockedMigrationControllerTest is Test, ERC1155Holder {
130131
factory,
131132
datastore,
132133
hcaFactory,
133-
metadata
134+
metadata,
135+
fallbackResolver
134136
);
135137

136138
// Setup eth registry

0 commit comments

Comments
 (0)