Skip to content

Commit

Permalink
feat(contracts)!: add base and advanced Checker and Excubia contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjei committed Nov 6, 2024
1 parent a6d6b68 commit e629d29
Show file tree
Hide file tree
Showing 18 changed files with 409 additions and 156 deletions.
2 changes: 1 addition & 1 deletion packages/contracts/.solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "solhint:recommended",
"rules": {
"code-complexity": ["error", 7],
"code-complexity": ["error", 10],
"compiler-version": ["error", ">=0.8.0"],
"var-name-mixedcase": "off",
"const-name-snakecase": "off",
Expand Down
69 changes: 0 additions & 69 deletions packages/contracts/contracts/src/core/Excubia.sol

This file was deleted.

73 changes: 73 additions & 0 deletions packages/contracts/contracts/src/core/checker/AdvancedChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IAdvancedChecker, Check} from "./IAdvancedChecker.sol";

struct CheckStatus {
bool pre;
uint8 main;
bool post;
}

/// @title AdvancedChecker.
/// @notice Abstract base contract which can be extended to implement a specific `AdvancedChecker`.
/// @dev The `AdvancedChecker` contract builds upon the `BaseChecker` by introducing additional validation phases.
/// It allows for pre-condition (`PRE`), main (`MAIN`), and post-condition (`POST`) checks, with the option to skip
/// pre and post checks based on constructor parameters. The `_check` method orchestrates the validation process
/// based on the specified check type.
abstract contract AdvancedChecker is IAdvancedChecker {
/// @notice Flag to determine if pre-condition checks should be skipped.
bool public skipPre;

/// @notice Flag to determine if post-condition checks should be skipped.
bool public skipPost;

/// @notice Flag to determine if main checks can be executed multiple times.
bool public allowMultipleMain;

/// @param _skipPre Indicates whether to skip pre-condition checks.
/// @param _skipPost Indicates whether to skip post-condition checks.
/// @param _allowMultipleMain Indicates whether the main check can be executed multiple times.
constructor(bool _skipPre, bool _skipPost, bool _allowMultipleMain) {
skipPre = _skipPre;
skipPost = _skipPost;
allowMultipleMain = _allowMultipleMain;
}

/// @notice Public method to check the validity of the provided data for a given address and check type.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
/// @param checkType The type of check to perform (PRE, MAIN, POST).
function check(address passerby, bytes memory data, Check checkType) external view override {
_check(passerby, data, checkType);
}

/// @notice Internal method to orchestrate the validation process based on the specified check type.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
/// @param checkType The type of check to perform (PRE, MAIN, POST).
function _check(address passerby, bytes memory data, Check checkType) internal view {
if (!skipPre && checkType == Check.PRE) {
_checkPre(passerby, data);
} else if (!skipPost && checkType == Check.POST) {
_checkPost(passerby, data);
} else if (checkType == Check.MAIN) {
_checkMain(passerby, data);
}
}

/// @notice Internal method for performing pre-condition checks.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
function _checkPre(address passerby, bytes memory data) internal view virtual {}

/// @notice Internal method for performing main checks.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
function _checkMain(address passerby, bytes memory data) internal view virtual;

/// @notice Internal method for performing post-condition checks.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
function _checkPost(address passerby, bytes memory data) internal view virtual {}
}
23 changes: 23 additions & 0 deletions packages/contracts/contracts/src/core/checker/BaseChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IBaseChecker} from "./IBaseChecker.sol";

/// @title BaseChecker.
/// @notice Abstract base contract which can be extended to implement a specific `BaseChecker`.
/// @dev The `BaseChecker` contract provides a foundational structure for implementing specific checker logic.
/// It defines a method `check` that invokes a protected `_check` method, which must be implemented by derived
/// contracts.
abstract contract BaseChecker is IBaseChecker {
/// @notice Checks the validity of the provided data for a given address.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
function check(address passerby, bytes memory data) external view override {
_check(passerby, data);
}

/// @notice Internal method to perform the actual check logic.
/// @param passerby The address to be checked.
/// @param data The data associated with the check.
function _check(address passerby, bytes memory data) internal view virtual;
}
24 changes: 24 additions & 0 deletions packages/contracts/contracts/src/core/checker/IAdvancedChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

/// @notice This enum defines the types of checks that can be performed in the AdvancedChecker system.
/// @dev The `Check` enum represents the different phases of validation in the AdvancedChecker system.
/// - `PRE`: Represents the pre-condition check that must be satisfied before the `MAIN` check can occur.
/// - `MAIN`: The primary check that is executed, which can be validated multiple times.
/// - `POST`: Represents the post-condition check that can be validated after the `MAIN` check has been completed.
enum Check {
PRE,
MAIN,
POST
}

/// @title IAdvancedChecker.
/// @notice AdvancedChecker contract interface.
interface IAdvancedChecker {
/// @dev Defines the custom `gate` protection logic.
/// @param passerby The address of the entity attempting to pass the `gate`.
/// @param data Additional data that may be required for the check.
/// @param checkType The type of check to be enforced (e.g., PRE, MAIN, POST).
function check(address passerby, bytes calldata data, Check checkType) external view; // Function to check if the
// passerby can pass the gate with a specific check type.
}
11 changes: 11 additions & 0 deletions packages/contracts/contracts/src/core/checker/IBaseChecker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

/// @title IBaseChecker
/// @notice BaseChecker contract interface that defines the basic check functionality.
interface IBaseChecker {
/// @dev Defines the custom `gate` protection logic.
/// @param passerby The address of the entity attempting to pass the `gate`.
/// @param data Additional data that may be required for the check.
function check(address passerby, bytes calldata data) external view;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {Excubia} from "./Excubia.sol";
import {IAdvancedExcubia, Check} from "./IAdvancedExcubia.sol";
import {AdvancedChecker, CheckStatus} from "../checker/AdvancedChecker.sol";

/// @title AdvancedExcubia
/// @notice Abstract base contract which can be extended to implement a specific `AdvancedExcubia`.
abstract contract AdvancedExcubia is IAdvancedExcubia, Excubia {
/// @dev Reference to the AdvancedChecker contract for validation.
AdvancedChecker public immutable ADVANCED_CHECKER;

/// @dev Tracks the check status of each address.
mapping(address => CheckStatus) public isPassed;

/// @notice Constructor to initialize the AdvancedChecker contract.
/// @param _advancedChecker The address of the AdvancedChecker contract.
constructor(AdvancedChecker _advancedChecker) {
ADVANCED_CHECKER = _advancedChecker;
}

/// @notice Passes the gate check for a given address.
/// @dev Calls the internal `_pass` function to enforce the gate logic.
/// @param passerby The address attempting to pass the gate.
/// @param data Additional data required for the check.
/// @param checkType The type of check being performed (PRE, MAIN, POST).
function pass(address passerby, bytes calldata data, Check checkType) external override onlyGate {
_pass(passerby, data, checkType);
}

/// @notice Internal function to enforce the gate passing logic.
/// @param passerby The address attempting to pass the gate.
/// @param data Additional data required for the check.
/// @param checkType The type of check being performed (PRE, MAIN, POST).
function _pass(address passerby, bytes calldata data, Check checkType) internal {
ADVANCED_CHECKER.check(passerby, data, checkType);

if (checkType == Check.PRE) {
if (ADVANCED_CHECKER.skipPre()) revert PreCheckSkipped();
else if (isPassed[passerby].pre) revert AlreadyPassed();
else isPassed[passerby].pre = true;
} else if (checkType == Check.POST) {
if (ADVANCED_CHECKER.skipPost()) revert PostCheckSkipped();
else if (isPassed[passerby].post) revert AlreadyPassed();
else isPassed[passerby].post = true;
} else if (checkType == Check.MAIN) {
if (!ADVANCED_CHECKER.allowMultipleMain() && isPassed[passerby].main > 0) revert MainCheckAlreadyEnforced();
else isPassed[passerby].main += 1;
}

emit GatePassed(passerby, gate, data, checkType);
}
}
43 changes: 43 additions & 0 deletions packages/contracts/contracts/src/core/gatekeeper/BaseExcubia.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IBaseExcubia} from "./IBaseExcubia.sol";
import {Excubia} from "./Excubia.sol";
import {BaseChecker} from "../checker/BaseChecker.sol";

/// @title BaseExcubia
/// @notice Abstract base contract which can be extended to implement a specific `BaseExcubia`.
abstract contract BaseExcubia is Excubia, IBaseExcubia {
/// @dev Reference to the BaseChecker contract for validation.
BaseChecker public immutable BASE_CHECKER;

/// @dev Tracks whether an address has passed the gate check.
mapping(address => bool) public isPassed;

/// @notice Constructor to initialize the BaseChecker contract.
/// @param _baseChecker The address of the BaseChecker contract.
constructor(BaseChecker _baseChecker) {
BASE_CHECKER = _baseChecker;
}

/// @notice Passes the gate check for a given address.
/// @dev Calls the internal `_pass` function to enforce the gate logic.
/// @param passerby The address attempting to pass the gate.
/// @param data Additional data required for the check.
function pass(address passerby, bytes calldata data) external override onlyGate {
_pass(passerby, data);
}

/// @notice Internal function to enforce the gate passing logic.
/// @param passerby The address attempting to pass the gate.
/// @param data Additional data required for the check.
function _pass(address passerby, bytes calldata data) internal {
BASE_CHECKER.check(passerby, data);

if (isPassed[passerby]) revert AlreadyPassed();

isPassed[passerby] = true;

emit GatePassed(passerby, gate, data);
}
}
33 changes: 33 additions & 0 deletions packages/contracts/contracts/src/core/gatekeeper/Excubia.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IExcubia} from "./IExcubia.sol";

/// @title Excubia abstract contract.
/// @dev This contract implements the IExcubia interface and manages the gate address.
abstract contract Excubia is IExcubia, Ownable(msg.sender) {
/// @notice The Excubia-protected contract address.
/// @dev The gate can be any contract address that requires a prior check to enable logic.
/// For example, the gate is a Semaphore group that requires the passerby
/// to meet certain criteria before joining.
address public gate;

/// @notice Modifier that restricts access to the gate address.
modifier onlyGate() {
if (msg.sender != gate) revert GateOnly();
_;
}

/// @notice Sets the gate address.
/// @dev Only the owner can set the destination `gate` address.
/// @param _gate The address of the contract to be set as the gate.
function setGate(address _gate) public virtual onlyOwner {
if (_gate == address(0)) revert ZeroAddress();
if (gate != address(0)) revert GateAlreadySet();

gate = _gate;

emit GateSet(_gate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IExcubia} from "./IExcubia.sol";
import {Check} from "../checker/IAdvancedChecker.sol";

/// @title IAdvancedExcubia
/// @notice AdvancedExcubia contract interface that extends the IExcubia interface.
interface IAdvancedExcubia is IExcubia {
/// @notice Error thrown when the PRE check is skipped.
error PreCheckSkipped();

/// @notice Error thrown when the MAIN check cannot be executed more than once.
error MainCheckAlreadyEnforced();

/// @notice Error thrown when the POST check is skipped.
error PostCheckSkipped();

/// @notice Event emitted when someone passes the `gate` check.
/// @param passerby The address of those who have successfully passed the check.
/// @param gate The address of the excubia-protected contract address.
/// @param data Additional data related to the gate check.
/// @param checkType The type of check that was performed (e.g., PRE, MAIN, POST).
event GatePassed(address indexed passerby, address indexed gate, bytes data, Check checkType);

/// @notice Enforces the custom gate passing logic.
/// @dev Must call the right `check` method based on the `checkType` to handle the logic of checking
/// passerby for specific gate.
/// @param passerby The address of the entity attempting to pass the gate.
/// @param data Additional data required for the check (e.g., encoded token identifier).
/// @param checkType The type of the check to be enforced for the passerby with the given data.
function pass(address passerby, bytes calldata data, Check checkType) external;
}
Loading

0 comments on commit e629d29

Please sign in to comment.