Skip to content

Commit

Permalink
Fixes and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stevieraykatz committed Oct 30, 2024
1 parent c548aef commit ca4e6d4
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 27 deletions.
24 changes: 10 additions & 14 deletions src/L2/RegistrarController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {StringUtils} from "ens-contracts/ethregistrar/StringUtils.sol";

import {BASE_ETH_NODE, GRACE_PERIOD} from "src/util/Constants.sol";
import {BaseRegistrar} from "./BaseRegistrar.sol";
import {IDiscountValidator} from "./interface/IDiscountValidator.sol";
import {DiscountValidator} from "./discounts/DiscountValidator.sol";
import {IPriceOracle} from "./interface/IPriceOracle.sol";
import {L2Resolver} from "./L2Resolver.sol";
import {IReverseRegistrar} from "./interface/IReverseRegistrar.sol";
Expand Down Expand Up @@ -132,12 +132,6 @@ contract RegistrarController is Ownable {
/// @notice Thrown when the payment received is less than the price.
error InsufficientValue();

/// @notice Thrown when the specified discount's validator does not accept the discount for the sender.
///
/// @param key The discount being accessed.
/// @param data The associated `validationData`.
error InvalidDiscount(bytes32 key, bytes data);

/// @notice Thrown when the discount amount is 0.
///
/// @param key The discount being set.
Expand Down Expand Up @@ -581,16 +575,18 @@ contract RegistrarController is Ownable {
active ? activeDiscounts.add(key) : activeDiscounts.remove(key);
}

/// Validates that:
/// 1. That the discount is `active`
/// 2. That the associated `discountValidator` returns true when `isValidDiscountRegistration` is called.
/// @notice Calls the associated discount validator with `msg.sender` and `validationData`.
///
/// @dev This method calls `validateDiscountRegistration` which may revert with `DiscountValidator.InvalidDiscount`.
///
/// @param discountKey unique identifier for the discount.
/// @param validationData validation data required for discount.
function _validateDiscount(bytes32 discountKey, bytes calldata validationData) internal {
DiscountDetails memory details = discounts[discountKey];
if(!details.active) revert InactiveDiscount(discountKey);

IDiscountValidator validator = IDiscountValidator(details.discountValidator);
// if (!validator.validateDiscountRegistration(msg.sender, validationData)) {
// revert InvalidDiscount(discountKey, validationData);
// }
DiscountValidator validator = DiscountValidator(details.discountValidator);
validator.validateDiscountRegistration(msg.sender, validationData);
}

/// @notice Allows anyone to withdraw the eth accumulated on this contract back to the `paymentReceiver`.
Expand Down
25 changes: 17 additions & 8 deletions src/L2/discounts/DiscountValidator.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/// @title Discount Validator Interface
/// @title Discount Validato
///
/// @notice Common interface which all Discount Validators must implement.
/// The logic specific to each integration must ultimately be consumable as the `bool` returned from
/// `isValidDiscountRegistration`. Then, upon registration, the integrator should call `validateDiscountRegistration`
/// allowing discount-specific state writes to occur.
/// @notice Discount Validator base contract which must be inherited by implementing validators.
/// The logic specific to each integration must ultimately be consumable as:
/// 1. A `bool` returned from `isValidDiscountRegistration` for offchain pre-tx validation, and
/// 2. A call to `validateDiscountRegistration` which will revert if validation fails
abstract contract DiscountValidator {
/// @notice Thrown when the specified discount's validator does not accept the discount for the sender.
///
/// @param claimer The discount being accessed.
/// @param claimer The address of the claiming user.
/// @param data The associated `validationData`.
error InvalidDiscount(address claimer, bytes data);

/// @notice Required implementation for compatibility with IDiscountValidator.
/// @notice Required implementation for compatibility with DiscountValidator.
///
/// @dev Each implementation will have unique requirements for the data necessary to perform
/// a meaningul validation. Implementations must describe here how to pack relevant `validationData`.
Expand All @@ -26,7 +26,16 @@ abstract contract DiscountValidator {
/// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`.
function isValidDiscountRegistration(address claimer, bytes calldata validationData) public virtual view returns (bool);

function validateDiscountRegistration(address claimer, bytes calldata validationData) external virtual view {

/// @notice Required implementation for compaibility with DiscountValidator.
///
/// @dev This method reverts with `InvalidDiscount` if called with for an invalid combination of `claimer` and `validationData`.
/// By default, it simply calls `isValidDiscountRegistration`. If more sophisticated state tracking is required, overwrite this
/// method. Overwriten methods MUST still revert with `InvalidDiscount` should the data fail the validation step.
///
/// @param claimer the discount claimer's address.
/// @param validationData opaque bytes for performing the validation.
function validateDiscountRegistration(address claimer, bytes calldata validationData) external virtual {
if(!isValidDiscountRegistration(claimer, validationData)) revert InvalidDiscount(claimer, validationData);
}
}
21 changes: 21 additions & 0 deletions src/L2/interface/IDiscountValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/// @title Discount Validator Interface
///
/// @notice Common interface which all Discount Validators must implement.
/// The logic specific to each integration must ultimately be consumable as the `bool` returned from
/// `isValidDiscountRegistration`.
interface IDiscountValidator {
/// @notice Required implementation for compatibility with IDiscountValidator.
///
/// @dev Each implementation will have unique requirements for the data necessary to perform
/// a meaningul validation. Implementations must describe here how to pack relevant `validationData`.
/// Ex: `bytes validationData = abi.encode(bytes32 key, bytes32[] proof)`
///
/// @param claimer the discount claimer's address.
/// @param validationData opaque bytes for performing the validation.
///
/// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`.
function isValidDiscountRegistration(address claimer, bytes calldata validationData) external returns (bool);
}
8 changes: 6 additions & 2 deletions test/RegistrarController/DiscountedRegister.t.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {DiscountValidator} from "src/L2/discounts/DiscountValidator.sol";
import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol";
import {RegistrarController} from "src/L2/RegistrarController.sol";
import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol";
Expand All @@ -11,6 +12,7 @@ contract DiscountedRegister is RegistrarControllerBase {
vm.deal(user, 1 ether);

inactiveDiscount.active = false;
base.setAvailable(uint256(nameLabel), true);
vm.prank(owner);
controller.setDiscountDetails(inactiveDiscount);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);
Expand All @@ -26,8 +28,9 @@ contract DiscountedRegister is RegistrarControllerBase {
controller.setDiscountDetails(_getDefaultDiscount());
validator.setReturnValue(false);
uint256 price = controller.discountedRegisterPrice(name, duration, discountKey);
base.setAvailable(uint256(nameLabel), true);

vm.expectRevert(abi.encodeWithSelector(RegistrarController.InvalidDiscount.selector, discountKey, ""));
vm.expectRevert(abi.encodeWithSelector(DiscountValidator.InvalidDiscount.selector, user, ""));
vm.prank(user);
controller.discountedRegister{value: price}(_getDefaultRegisterRequest(), discountKey, "");
}
Expand Down Expand Up @@ -136,8 +139,9 @@ contract DiscountedRegister is RegistrarControllerBase {
vm.prank(user);
controller.discountedRegister{value: price}(request, discountKey, "");

vm.expectRevert(abi.encodeWithSelector(RegistrarController.AlreadyRegisteredWithDiscount.selector, user));
request.name = "newname";
base.setAvailable(uint256(keccak256(bytes(request.name))),true);
vm.expectRevert(abi.encodeWithSelector(RegistrarController.AlreadyRegisteredWithDiscount.selector, user));
vm.prank(user);
controller.discountedRegister{value: price}(request, discountKey, "");
}
Expand Down
6 changes: 3 additions & 3 deletions test/mocks/MockDiscountValidator.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "src/L2/interface/IDiscountValidator.sol";
import "src/L2/discounts/DiscountValidator.sol";

contract MockDiscountValidator is IDiscountValidator {
contract MockDiscountValidator is DiscountValidator {
bool returnValue = true;

function isValidDiscountRegistration(address, bytes calldata) external view returns (bool) {
function isValidDiscountRegistration(address, bytes calldata) public view override returns (bool) {
return returnValue;
}

Expand Down

0 comments on commit ca4e6d4

Please sign in to comment.