Skip to content
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

Finish ENSIP-19 integration #95

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
27 changes: 27 additions & 0 deletions src/L2/ReverseRegistrarShim.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {IReverseRegistrar} from "./interface/IReverseRegistrar.sol";
import {IL2ReverseResolver} from "./interface/IL2ReverseResolver.sol";

contract ReverseRegistrarShim {
address public immutable reverseRegistrar;
address public immutable reverseResolver;
address public immutable l2Resolver;

constructor(address reverseRegistrar_, address reverseResolver_, address l2Resolver_) {

Check notice

Code scanning / Slither

Missing zero address validation Low

Check notice

Code scanning / Slither

Missing zero address validation Low

Check notice

Code scanning / Slither

Missing zero address validation Low

reverseRegistrar = reverseRegistrar_;
reverseResolver = reverseResolver_;
l2Resolver = l2Resolver_;
}

function setNameForAddrWithSignature(
address addr,
string calldata name,
uint256 signatureExpiry,
bytes memory signature
) external returns (bytes32) {
IReverseRegistrar(reverseRegistrar).setNameForAddr(addr, msg.sender, l2Resolver, name);
return IL2ReverseResolver(reverseResolver).setNameForAddrWithSignature(addr, name, signatureExpiry, signature);
}
}

Check warning

Code scanning / Slither

Missing inheritance Warning

676 changes: 676 additions & 0 deletions src/L2/UpgradeableRegistrarController.sol

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions src/L2/interface/IL2ReverseResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IL2ReverseResolver {
/**
* @dev Sets the name for an addr using a signature that can be verified with ERC1271.
* @param addr The reverse record to set
* @param name The name of the reverse record
* @param signatureExpiry Date when the signature expires
* @param signature The resolver of the reverse node
* @return The ENS node hash of the reverse record.
*/
function setNameForAddrWithSignature(
address addr,
string calldata name,
uint256 signatureExpiry,
bytes memory signature
) external returns (bytes32);
}
34 changes: 34 additions & 0 deletions test/ReverseRegistrarShim/ReverseRegistrarShimBase.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {Test} from "forge-std/Test.sol";
import {ReverseRegistrarShim} from "src/L2/ReverseRegistrarShim.sol";
import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol";
import {MockReverseResolver} from "test/mocks/MockReverseResolver.sol";
import {MockPublicResolver} from "test/mocks/MockPublicResolver.sol";

contract ReverseRegistrarShimBase is Test {
MockReverseResolver revRes;
MockReverseRegistrar revReg;
MockPublicResolver resolver;

ReverseRegistrarShim public shim;

address userA;
address userB;
string nameA = "userAName";
string nameB = "userBName";

uint256 signatureExpiry = 0;
bytes signature;

function setUp() external {
revRes = new MockReverseResolver();
revReg = new MockReverseRegistrar();
resolver = new MockPublicResolver();
shim = new ReverseRegistrarShim(address(revReg), address(revRes), address(resolver));

userA = makeAddr("userA");
userB = makeAddr("userB");
}
}
28 changes: 28 additions & 0 deletions test/ReverseRegistrarShim/SetNameForAddrWithSignature.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ReverseRegistrarShimBase} from "./ReverseRegistrarShimBase.t.sol";
import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol";
import {MockReverseResolver} from "test/mocks/MockReverseResolver.sol";

contract SetNameForAddrWithSignature is ReverseRegistrarShimBase {
function test_setsNameForAddr_onReverseRegistrar() public {
vm.prank(userA);
vm.expectCall(
address(revReg),
abi.encodeWithSelector(MockReverseRegistrar.setNameForAddr.selector, userA, userA, address(resolver), nameA)
);
shim.setNameForAddrWithSignature(userA, nameA, signatureExpiry, signature);
}

function test_setsNameForAddr_onReverseResolver() public {
vm.prank(userA);
vm.expectCall(
address(revRes),
abi.encodeWithSelector(
MockReverseResolver.setNameForAddrWithSignature.selector, userA, nameA, signatureExpiry, signature
)
);
shim.setNameForAddrWithSignature(userA, nameA, signatureExpiry, signature);
}
}
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));
}
}
144 changes: 144 additions & 0 deletions test/UpgradeableRegistrarController/DiscountedRegister.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol";
import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";

contract DiscountedRegister is UpgradeableRegistrarControllerBase {
function test_reverts_ifTheDiscountIsInactive() public {
UpgradeableRegistrarController.DiscountDetails memory inactiveDiscount = _getDefaultDiscount();
vm.deal(user, 1 ether);

inactiveDiscount.active = false;
vm.prank(owner);
controller.setDiscountDetails(inactiveDiscount);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);

vm.expectRevert(abi.encodeWithSelector(UpgradeableRegistrarController.InactiveDiscount.selector, discountKey));
vm.prank(user);
controller.discountedRegister{value: price}(_getDefaultRegisterRequest(), discountKey, "");
}

function test_reverts_whenInvalidDiscountRegistration() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
validator.setReturnValue(false);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);

vm.expectRevert(abi.encodeWithSelector(UpgradeableRegistrarController.InvalidDiscount.selector, discountKey, ""));
vm.prank(user);
controller.discountedRegister{value: price}(_getDefaultRegisterRequest(), discountKey, "");
}

function test_reverts_whenNameNotAvailble() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), false);

vm.expectRevert(abi.encodeWithSelector(UpgradeableRegistrarController.NameNotAvailable.selector, name));
vm.prank(user);
controller.discountedRegister{value: price}(_getDefaultRegisterRequest(), discountKey, "");
}

function test_reverts_whenDurationTooShort() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), true);

UpgradeableRegistrarController.RegisterRequest memory shortDurationRequest = _getDefaultRegisterRequest();
uint256 shortDuration = controller.MIN_REGISTRATION_DURATION() - 1;
shortDurationRequest.duration = shortDuration;
vm.expectRevert(abi.encodeWithSelector(UpgradeableRegistrarController.DurationTooShort.selector, shortDuration));
vm.prank(user);
controller.discountedRegister{value: price}(shortDurationRequest, discountKey, "");
}

function test_reverts_whenValueTooSmall() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
prices.setPrice(name, IPriceOracle.Price({base: 1 ether, premium: 0}));
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), true);

vm.expectRevert(UpgradeableRegistrarController.InsufficientValue.selector);
vm.prank(user);
controller.discountedRegister{value: price - 1}(_getDefaultRegisterRequest(), discountKey, "");
}

function test_registersWithDiscountSuccessfully() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), true);
UpgradeableRegistrarController.RegisterRequest memory request = _getDefaultRegisterRequest();
uint256 expires = block.timestamp + request.duration;
base.setNameExpires(uint256(nameLabel), expires);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);

vm.expectEmit(address(controller));
emit UpgradeableRegistrarController.ETHPaymentProcessed(user, price);
vm.expectEmit(address(controller));
emit UpgradeableRegistrarController.NameRegistered(request.name, nameLabel, user, expires);
vm.expectEmit(address(controller));
emit UpgradeableRegistrarController.DiscountApplied(user, discountKey);

vm.prank(user);
controller.discountedRegister{value: price}(request, discountKey, "");

bytes memory retByte = resolver.firstBytes();
assertEq(keccak256(retByte), keccak256(request.data[0]));
assertTrue(reverse.hasClaimed(user));
address[] memory addrs = new address[](1);
addrs[0] = user;
assertTrue(controller.hasRegisteredWithDiscount(addrs));
}

function test_sendsARefund_ifUserOverpayed() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), true);
UpgradeableRegistrarController.RegisterRequest memory request = _getDefaultRegisterRequest();
uint256 expires = block.timestamp + request.duration;
base.setNameExpires(uint256(nameLabel), expires);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);

vm.prank(user);
controller.discountedRegister{value: price + 1}(request, discountKey, "");

uint256 expectedBalance = 1 ether - price;
assertEq(user.balance, expectedBalance);
}

function test_reverts_ifTheRegistrantHasAlreadyRegisteredWithDiscount() public {
vm.deal(user, 1 ether);
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());
validator.setReturnValue(true);
base.setAvailable(uint256(nameLabel), true);
UpgradeableRegistrarController.RegisterRequest memory request = _getDefaultRegisterRequest();
uint256 expires = block.timestamp + request.duration;
base.setNameExpires(uint256(nameLabel), expires);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);

vm.prank(user);
controller.discountedRegister{value: price}(request, discountKey, "");

vm.expectRevert(abi.encodeWithSelector(UpgradeableRegistrarController.AlreadyRegisteredWithDiscount.selector, user));
request.name = "newname";
vm.prank(user);
controller.discountedRegister{value: price}(request, discountKey, "");
}
}
29 changes: 29 additions & 0 deletions test/UpgradeableRegistrarController/DiscountedRegisterPrice.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol";
import {RegistrarController} from "src/L2/RegistrarController.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";

contract DiscountedRegisterPrice is UpgradeableRegistrarControllerBase {
function test_returnsADiscountedPrice_whenThePriceIsGreaterThanTheDiscount(uint256 price) public {
vm.assume(price > discountAmount);
prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0}));
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());

uint256 expectedPrice = price - discountAmount;
uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey);
assertEq(retPrice, expectedPrice);
}

function test_returnsZero_whenThePriceIsLessThanOrEqualToTheDiscount(uint256 price) public {
vm.assume(price > 0 && price <= discountAmount);
prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0}));
vm.prank(owner);
controller.setDiscountDetails(_getDefaultDiscount());

uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey);
assertEq(retPrice, 0);
}
}
35 changes: 35 additions & 0 deletions test/UpgradeableRegistrarController/RecoverFunds.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol";
import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";
import {Ownable} from "solady/auth/Ownable.sol";
import {MockUSDC} from "test/mocks/MockUSDC.sol";

contract RecoverFunds is UpgradeableRegistrarControllerBase {
MockUSDC public usdc;

function test_reverts_ifCalledByNonOwner(address caller, uint256 amount) public {
vm.assume(caller != owner);
vm.assume(amount > 0 && amount < type(uint128).max);
vm.expectRevert(Ownable.Unauthorized.selector);
vm.prank(caller);
controller.recoverFunds(address(usdc), caller, amount);
}

function test_allowsTheOwnerToRecoverFunds(uint256 amount) public {
vm.assume(amount > 0 && amount < type(uint128).max);
_setupTokenAndAssignBalanceToController(amount);
assertEq(usdc.balanceOf(owner), 0);

vm.prank(owner);
controller.recoverFunds(address(usdc), owner, amount);
assertEq(usdc.balanceOf(owner), amount);
}

function _setupTokenAndAssignBalanceToController(uint256 balance) internal {
usdc = new MockUSDC();
usdc.mint(address(controller), balance);
}
}
Loading
Loading