Skip to content

Add UpgradeableRegistrarController #123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 48 commits into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c307e78
Add upgradeable RegistrarController, make it EIP-7201 compliant'
stevieraykatz Oct 1, 2024
a578e14
Add interface to new ENS contract, add support for setting primary to…
stevieraykatz Oct 1, 2024
5dfed4a
Add initializer for reverse resolver contract
stevieraykatz Oct 1, 2024
43a53a7
Add shim for migration period
stevieraykatz Oct 1, 2024
fc735bf
Add tests for Shim
stevieraykatz Oct 2, 2024
7a0d049
lint
stevieraykatz Oct 2, 2024
8151302
Testing start
stevieraykatz Oct 10, 2024
1fc8be7
chore: forge fmt
abdulla-cb Oct 31, 2024
15ebd14
chore: typos
abdulla-cb Oct 31, 2024
d256095
forge install: openzeppelin-contracts-upgradeable
abdulla-cb Oct 31, 2024
5214bc2
feat: use v5.0.0 of openzeppelin-contracts-upgradeable
abdulla-cb Oct 31, 2024
34a313a
feat: update tests to use OpenZeppelin Upgradeable
abdulla-cb Nov 1, 2024
78e3f28
chore: fix typo
abdulla-cb Nov 1, 2024
5e64d7d
Update interface to ENS reverse resolver
stevieraykatz Apr 16, 2025
5f78d9f
Remove launch logic
stevieraykatz Apr 16, 2025
d747ca0
remove shim from this branch
stevieraykatz Apr 16, 2025
c2bc49f
Add support for new cointypes array in reverse set
stevieraykatz Apr 16, 2025
48feaf1
Add cointypes to test helper
stevieraykatz Apr 16, 2025
6211b51
Remove test for removed method
stevieraykatz Apr 16, 2025
0e17adf
Merge branch 'main' into upgradeable-registrar
stevieraykatz Apr 16, 2025
148cb2c
attempt to fix ci
stevieraykatz Apr 17, 2025
eb85c2e
Cleanup test mocks
stevieraykatz Apr 17, 2025
50a2c3f
Remove launch logic, cleanup test base
stevieraykatz Apr 17, 2025
438d626
Fix missing refs from rename
stevieraykatz Apr 17, 2025
91ce6d5
Remove reference to unused test var
stevieraykatz Apr 17, 2025
2b85da9
fix _getExpiry
stevieraykatz Apr 17, 2025
e35b156
Fix Register test to use a duration
stevieraykatz Apr 17, 2025
752aeb3
lint
stevieraykatz Apr 17, 2025
3de1f8f
fix natspec
stevieraykatz Apr 17, 2025
cadfc45
Fixes to natspec and typos per comments on PR #95
stevieraykatz Apr 18, 2025
cdd4326
Add registrar controller switch-over integration test
stevieraykatz Apr 22, 2025
4317ccc
chore: cleanup logging, formatting
stevieraykatz Apr 22, 2025
6b124db
feat: add grace period renewal test
stevieraykatz Apr 23, 2025
c1a274a
chore: typos and gas
stevieraykatz Apr 23, 2025
44be9dc
Merge branch 'main' into upgradeable-registrar
stevieraykatz May 7, 2025
9d91fa3
Fix ext link ref to branch with updated code
stevieraykatz May 7, 2025
3e0a849
add interface for new reverse registrar
stevieraykatz May 7, 2025
465e843
lint interface
stevieraykatz May 7, 2025
0102186
refactor reverse registration to leverage updated reverse registrar v2
stevieraykatz May 7, 2025
e48a19d
finish refactor to IReverseRegistrarV2
stevieraykatz May 7, 2025
b5a815c
Fix tests to account for new reverse registrar v2
stevieraykatz May 9, 2025
cce8e36
lint
stevieraykatz May 9, 2025
51dc6e9
Nits from PR comments
stevieraykatz May 27, 2025
d8cb250
cleanup tests based on renamings
stevieraykatz May 27, 2025
5a956e9
update submodules, pin solady version
stevieraykatz May 27, 2025
a3eb0c8
Revert "update submodules, pin solady version"
stevieraykatz May 27, 2025
73afd8b
Natspec and cleanup from PR
stevieraykatz May 28, 2025
ad21ff6
Reorder modifiers
stevieraykatz May 28, 2025
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@
[submodule "lib/eas-contracts"]
path = lib/eas-contracts
url = https://github.com/ethereum-attestation-service/eas-contracts
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable
14 changes: 12 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = ["@ensdomains/buffer/=lib/buffer"]
remappings = [
"@ensdomains/buffer/=lib/buffer",
"solady/=lib/solady/src/",
"forge-std/=lib/forge-std/src/",
"ens-contracts/=lib/ens-contracts/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"openzeppelin-contracts/=lib/openzeppelin-contracts",
"eas-contracts/=lib/eas-contracts/contracts/",
"verifications/=lib/verifications/src",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
]
fs_permissions = [{access = "read", path = "./script/premint/"}]
auto_detect_remappings = false

[rpc_endpoints]
sepolia="${SEPOLIA_RPC_URL}"
Expand All @@ -12,4 +23,3 @@ base-sepolia="${BASE_SEPOLIA_RPC_URL}"
[etherscan]
sepolia={url = "https://api-sepolia.etherscan.io/api", key = "${ETHERSCAN_API_KEY}"}
base-sepolia={url = "https://api-sepolia.basescan.org/api", key = "${BASE_ETHERSCAN_API_KEY}"}

1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
693 changes: 693 additions & 0 deletions src/L2/UpgradeableRegistrarController.sol

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/L2/interface/IL2ReverseRegistrar.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
pragma solidity ^0.8.23;

/// @notice Interface for the L2 Reverse Registrar.
/// https://github.com/ensdomains/ens-contracts/pull/265
/// https://github.com/ensdomains/ens-contracts/tree/feature/simplify-reverse-resolver
interface IL2ReverseRegistrar {
/// @notice Sets the `nameForAddr()` record for the calling account.
///
Expand Down
27 changes: 27 additions & 0 deletions src/L2/interface/IReverseRegistrarV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/// @title Interface for ReverseRegistrarV2
interface IReverseRegistrarV2 {
/// @notice Transfers ownership of the base-specific reverse ENS record for `msg.sender` to the provided `owner`.
///
/// @param owner The address to set as the owner of the reverse record in ENS.
///
/// @return The ENS node hash of the Base network-specific reverse record.
function claim(address owner) external returns (bytes32);

/// @notice Sets the reverse record `name` for `addr`.
///
/// @param addr The name records will be set for this address.
/// @param signatureExpiry The timestamp expiration of the signature.
/// @param name The name that will be stored for `addr`.
/// @param cointypes The array of networks-as-cointypes used in replayable reverse sets.
/// @param signature The signature bytes.
function setNameForAddrWithSignature(
Comment on lines +19 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit, missing @return natspec

address addr,
uint256 signatureExpiry,
string calldata name,
uint256[] memory cointypes,
bytes memory signature
) external returns (bytes32);
}
25 changes: 11 additions & 14 deletions test/Integration/IntegrationTestBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,18 @@ contract IntegrationTestBase is Test {
vm.stopPrank();
}

function test_integration_register() public {
vm.stopPrank();
vm.startPrank(alice);
function _getBasePrices() internal pure returns (uint256[] memory) {
uint256[] memory rentPrices = new uint256[](6);
rentPrices[0] = 316_808_781_402;
rentPrices[1] = 31_680_878_140;
rentPrices[2] = 3_168_087_814;
rentPrices[3] = 316_808_781;
rentPrices[4] = 31_680_878;
rentPrices[5] = 3_168_087; // 3,168,808.781402895 = 1e14 / (365.25 * 24 * 3600)
return rentPrices;
}

function _registerAlice() internal {
string memory name = "alice";
uint256 duration = 365.25 days;

Expand All @@ -139,15 +147,4 @@ contract IntegrationTestBase is Test {

registrarController.register{value: registerPrice}(request);
}

function _getBasePrices() internal pure returns (uint256[] memory) {
uint256[] memory rentPrices = new uint256[](6);
rentPrices[0] = 316_808_781_402;
rentPrices[1] = 31_680_878_140;
rentPrices[2] = 3_168_087_814;
rentPrices[3] = 316_808_781;
rentPrices[4] = 31_680_878;
rentPrices[5] = 3_168_087; // 3,168,808.781402895 = 1e14 / (365.25 * 24 * 3600)
return rentPrices;
}
}
119 changes: 119 additions & 0 deletions test/Integration/SwitchToUpgradeableRegistrarController.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

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

import {IntegrationTestBase} from "./IntegrationTestBase.t.sol";
import {MockL2ReverseRegistrar} from "test/mocks/MockL2ReverseRegistrar.sol";
import {MockReverseRegistrarV2} from "test/mocks/MockReverseRegistrarV2.sol";

import {ExponentialPremiumPriceOracle} from "src/L2/ExponentialPremiumPriceOracle.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";
import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol";
import {TransparentUpgradeableProxy} from
"openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import {BASE_ETH_NODE, GRACE_PERIOD} from "src/util/Constants.sol";

contract SwitchToUpgradeableRegistrarController is IntegrationTestBase {
UpgradeableRegistrarController public controllerImpl;
UpgradeableRegistrarController public controller;
TransparentUpgradeableProxy public proxy;
MockL2ReverseRegistrar public l2ReverseRegistrar;
MockReverseRegistrarV2 public reverseRegistrarv2;

address admin;
uint256 duration = 365.25 days;

uint256 constant UPGRADE_TIMESTAMP = 1746057600; // May 1 2025

function setUp() public override {
super.setUp();
_registerAlice();

vm.warp(UPGRADE_TIMESTAMP);

admin = makeAddr("admin");

l2ReverseRegistrar = new MockL2ReverseRegistrar();
reverseRegistrarv2 = new MockReverseRegistrarV2();

exponentialPremiumPriceOracle = new ExponentialPremiumPriceOracle(
_getBasePrices(), EXPIRY_AUCTION_START_PRICE, EXPIRY_AUCTION_DURATION_DAYS
);

bytes memory controllerInitData = abi.encodeWithSelector(
UpgradeableRegistrarController.initialize.selector,
baseRegistrar,
exponentialPremiumPriceOracle,
reverseRegistrarv2,
owner,
BASE_ETH_NODE,
".base.eth",
payments,
address(registrarController),
address(l2ReverseRegistrar)
);

controllerImpl = new UpgradeableRegistrarController();
proxy = new TransparentUpgradeableProxy(address(controllerImpl), admin, controllerInitData);
controller = UpgradeableRegistrarController(address(proxy));

_postDeployConfig();
}

function _postDeployConfig() internal {
vm.startPrank(owner);
baseRegistrar.addController(address(proxy));
reverseRegistrar.setControllerApproval(address(proxy), true);
defaultL2Resolver.setRegistrarController(address(proxy));
vm.stopPrank();
}

function test_canRegisterANewName() public {
string memory name = "new-name";
uint256[] memory coinTypes = new uint256[](1);
coinTypes[0] = 0x80000000 | 0x00002105;

uint256 registerPrice = controller.registerPrice(name, duration);
uint256 expectedPrice = _getBasePrices()[4] * duration;
vm.assertEq(registerPrice, expectedPrice);

UpgradeableRegistrarController.RegisterRequest memory request = UpgradeableRegistrarController.RegisterRequest({
name: name,
owner: alice,
duration: duration,
resolver: address(defaultL2Resolver),
data: new bytes[](0),
reverseRecord: true,
coinTypes: coinTypes,
signatureExpiry: block.timestamp,
signature: ""
});

vm.deal(alice, 1 ether);
vm.prank(alice);
controller.register{value: registerPrice}(request);
}

function test_canRenewExistingName() public {
string memory name = "alice";

IPriceOracle.Price memory prices = controller.rentPrice(name, duration);

vm.deal(alice, 1 ether);
vm.prank(alice);
controller.renew{value: prices.base}(name, duration);
}

function test_canRenewNameInGracePeriod() public {
string memory name = "alice";

IPriceOracle.Price memory prices = controller.rentPrice(name, duration);

vm.deal(alice, 1 ether);
vm.warp(LAUNCH_TIME + duration + GRACE_PERIOD - 1);
controller.renew{value: prices.base}(name, duration);
}
}
2 changes: 1 addition & 1 deletion test/RegistrarController/RegisterPrice.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";
contract RegisterPrice is RegistrarControllerBase {
function test_returnsRegisterPrice_fromPricingOracle() public view {
uint256 retPrice = controller.registerPrice(name, 0);
assertEq(retPrice, prices.DEFAULT_BASE_WEI() + prices.DEFAULT_PERMIUM_WEI());
assertEq(retPrice, prices.DEFAULT_BASE_WEI() + prices.DEFAULT_PREMIUM_WEI());
}

function test_fuzz_returnsRegisterPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public {
Expand Down
2 changes: 1 addition & 1 deletion test/RegistrarController/RentPrice.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ contract RentPrice is RegistrarControllerBase {
function test_returnsPrice_fromPricingOracle() public view {
IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0);
assertEq(retPrices.base, prices.DEFAULT_BASE_WEI());
assertEq(retPrices.premium, prices.DEFAULT_PERMIUM_WEI());
assertEq(retPrices.premium, prices.DEFAULT_PREMIUM_WEI());
}

function test_returnsPremium_ifTimeIsNearLaunchTime() public {
Expand Down
2 changes: 1 addition & 1 deletion test/ReverseRegistrarV2/SetBaseForwardAddr.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ contract SetBaseForwardAddr is ReverseRegistrarV2Base {
fwdNode = keccak256(abi.encodePacked(BASE_ETH_NODE, nameLabel));
}

function _getNodes() internal returns (bytes32[] memory nodes) {
function _getNodes() internal view returns (bytes32[] memory nodes) {
nodes = new bytes32[](1);
nodes[0] = fwdNode;
}
Expand Down
21 changes: 21 additions & 0 deletions test/UpgradeableRegistrarController/Available.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol";

contract Available is UpgradeableRegistrarControllerBase {
function test_returnsFalse_whenNotAvailableOnBase() public {
base.setAvailable(uint256(nameLabel), false);
assertFalse(controller.available(name));
}

function test_returnsFalse_whenInvalidLength() public {
base.setAvailable(uint256(shortNameLabel), true);
assertFalse(controller.available(shortName));
}

function test_returnsTrue_whenValidAndAvailable() public {
base.setAvailable(uint256(nameLabel), true);
assertTrue(controller.available(name));
}
}
Loading