Skip to content

Commit a2eb791

Browse files
committed
feat(contracts): draft minimal proxy pattern with immutable args implementation
1 parent ba73ba0 commit a2eb791

25 files changed

+3620
-3462
lines changed

packages/contracts/contracts/src/core/checker/AdvancedChecker.sol

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {Checker} from "./Checker.sol";
77
/// @title AdvancedChecker.
88
/// @notice Multi-phase validation checker with pre, main, and post checks.
99
/// @dev Base contract for implementing complex validation logic with configurable check phases.
10-
abstract contract AdvancedChecker is IAdvancedChecker, Checker {
11-
constructor(address[] memory _verifiers) Checker(_verifiers) {}
12-
10+
abstract contract AdvancedChecker is Checker, IAdvancedChecker {
1311
/// @notice Entry point for validation checks.
1412
/// @param subject Address to validate.
1513
/// @param evidence Validation data.

packages/contracts/contracts/src/core/checker/BaseChecker.sol

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {Checker} from "./Checker.sol";
99
/// @dev Provides a standardized interface for implementing custom validation logic
1010
/// through the internal _check method.
1111
abstract contract BaseChecker is Checker, IBaseChecker {
12-
constructor(address[] memory _verifiers) Checker(_verifiers) {}
13-
1412
/// @notice Validates evidence for a given subject address.
1513
/// @dev External view function that delegates to internal _check implementation.
1614
/// @param subject Address to validate.

packages/contracts/contracts/src/core/checker/Checker.sol

+9-28
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,20 @@
22
pragma solidity ^0.8.20;
33

44
import {IChecker} from "../interfaces/IChecker.sol";
5+
import {LibClone} from "solady/src/utils/LibClone.sol";
56

6-
/// @title Checker
7-
/// @notice Abstract base contract for implementing attribute verification logic.
8-
/// @dev Provides infrastructure to orchestrate third-party verifiers for single checks.
7+
// @todo refactoring & comments
98
abstract contract Checker is IChecker {
10-
/// @notice Array of third-party contract addresses used for verification.
11-
/// @dev Can include existing and already deployed Checkers, NFTs, MACI polls, and/or any other contract
12-
/// that provides evidence verification. These contracts should already be deployed and operational.
13-
address[] internal verifiers;
9+
bool private _initialized;
1410

15-
/// @notice Initializes the Checker with an optional list of third-party verification contracts.
16-
/// @param _verifiers Array of addresses for existing verification contracts.
17-
/// @dev Each address should point to a deployed contract that will be consulted during verification.
18-
/// This array can remain empty if there's no reliance on external verifiers.
19-
constructor(address[] memory _verifiers) {
20-
verifiers = _verifiers;
21-
}
11+
error AlreadyInitialized();
2212

23-
/// @notice Retrieves the verifier address at a specific index.
24-
/// @param index The index of the verifier in the array.
25-
/// @return The address of the verifier at the specified index.
26-
/// @custom:throws VerifierNotFound if no address have been specified at given index.
27-
function getVerifierAtIndex(uint256 index) external view returns (address) {
28-
return _getVerifierAtIndex(index);
13+
function initialize() public virtual {
14+
if (_initialized) revert AlreadyInitialized();
15+
_initialized = true;
2916
}
3017

31-
/// @notice Internal implementation of verifier address retrieval at a specific index.
32-
/// @param index The index of the verifier in the array.
33-
/// @return The address of the verifier at the specified index.
34-
/// @custom:throws VerifierNotFound if no address have been specified at given index.
35-
function _getVerifierAtIndex(uint256 index) internal view returns (address) {
36-
if (index >= verifiers.length) revert VerifierNotFound();
37-
38-
return verifiers[index];
18+
function _getAppendedBytes() internal view returns (bytes memory) {
19+
return LibClone.argsOnClone(address(this));
3920
}
4021
}

packages/contracts/contracts/src/core/interfaces/IAdvancedChecker.sol

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.20;
33

4-
import {IChecker} from "./IChecker.sol";
5-
64
/// @title Check.
75
/// @notice Defines validation phases in the AdvancedChecker system.
86
/// @custom:values PRE - Pre-condition validation.
@@ -28,7 +26,7 @@ struct CheckStatus {
2826
/// @title IAdvancedChecker.
2927
/// @notice Defines multi-phase validation system interface.
3028
/// @dev Implement this for custom validation logic with pre/main/post checks.
31-
interface IAdvancedChecker is IChecker {
29+
interface IAdvancedChecker {
3230
/// @notice Validates subject against specified check type.
3331
/// @param subject Address to validate.
3432
/// @param evidence Validation data.

packages/contracts/contracts/src/core/interfaces/IChecker.sol

-6
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,4 @@ pragma solidity ^0.8.20;
66
interface IChecker {
77
/// @notice Core error conditions.
88
error VerifierNotFound();
9-
10-
/// @notice Retrieves the verifier address at a specific index.
11-
/// @param index The index of the verifier in the array.
12-
/// @return The address of the verifier at the specified index.
13-
/// @custom:throws VerifierNotFound if no address have been specified at given index.
14-
function getVerifierAtIndex(uint256 index) external view returns (address);
159
}

packages/contracts/contracts/src/core/interfaces/IPolicy.sol

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface IPolicy {
1414
error TargetOnly();
1515
error TargetAlreadySet();
1616
error AlreadyEnforced();
17+
error AlreadyInitialized();
1718

1819
/// @notice Returns policy trait identifier.
1920
/// @return Policy trait string (e.g., "Semaphore").

packages/contracts/contracts/src/core/policy/AdvancedPolicy.sol

+39-56
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,80 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.20;
33

4-
import {Policy} from "./Policy.sol";
54
import {IAdvancedPolicy, Check} from "../interfaces/IAdvancedPolicy.sol";
65
import {AdvancedChecker, CheckStatus} from "../checker/AdvancedChecker.sol";
6+
import {Policy} from "./Policy.sol";
7+
import {LibClone} from "solady/src/utils/LibClone.sol";
78

8-
/// @title AdvancedPolicy.
9+
/// @title AdvancedPolicy
910
/// @notice Implements advanced policy checks with pre, main, and post validation stages.
10-
/// @dev Extends Policy contract with multi-stage validation capabilities.
11+
/// @dev Extends Policy with multi-stage validation. Now clone-friendly with `initialize()`.
1112
abstract contract AdvancedPolicy is IAdvancedPolicy, Policy {
12-
/// @notice Reference to the validation checker contract.
13-
/// @dev Immutable to ensure checker cannot be changed after deployment.
14-
AdvancedChecker public immutable ADVANCED_CHECKER;
13+
/// @notice Reference to the validation checker contract. Stored, not immutable.
14+
AdvancedChecker public ADVANCED_CHECKER;
1515

1616
/// @notice Controls whether pre-condition checks are required.
17-
bool public immutable SKIP_PRE;
17+
bool public SKIP_PRE;
1818

1919
/// @notice Controls whether post-condition checks are required.
20-
bool public immutable SKIP_POST;
20+
bool public SKIP_POST;
2121

2222
/// @notice Controls whether main check can be executed multiple times.
23-
bool public immutable ALLOW_MULTIPLE_MAIN;
23+
bool public ALLOW_MULTIPLE_MAIN;
2424

2525
/// @notice Tracks validation status for each subject per target.
26-
/// @dev Maps subject => CheckStatus.
2726
mapping(address => CheckStatus) public enforced;
2827

29-
/// @notice Initializes contract with an AdvancedChecker instance and checks configs.
30-
/// @param _advancedChecker Address of the AdvancedChecker contract.
31-
/// @param _skipPre Skip pre-condition validation.
32-
/// @param _skipPost Skip post-condition validation.
33-
/// @param _allowMultipleMain Allow multiple main validations.
34-
constructor(AdvancedChecker _advancedChecker, bool _skipPre, bool _skipPost, bool _allowMultipleMain) {
35-
ADVANCED_CHECKER = _advancedChecker;
36-
SKIP_PRE = _skipPre;
37-
SKIP_POST = _skipPost;
38-
ALLOW_MULTIPLE_MAIN = _allowMultipleMain;
28+
/**
29+
* @notice Initialize function for minimal proxy clones.
30+
* Decodes appended bytes for (AdvancedChecker, skipPre, skipPost, allowMultipleMain).
31+
*/
32+
function initialize() public virtual override {
33+
// 1. Call Policy’s initialize to set ownership and `_initialized`.
34+
super.initialize();
35+
36+
// 2. Decode the appended bytes for the advanced config.
37+
bytes memory data = _getAppendedBytes();
38+
(address sender, address advCheckerAddr, bool skipPre, bool skipPost, bool allowMultipleMain) = abi.decode(
39+
data,
40+
(address, address, bool, bool, bool)
41+
);
42+
43+
_transferOwnership(sender);
44+
45+
ADVANCED_CHECKER = AdvancedChecker(advCheckerAddr);
46+
SKIP_PRE = skipPre;
47+
SKIP_POST = skipPost;
48+
ALLOW_MULTIPLE_MAIN = allowMultipleMain;
3949
}
4050

41-
/// @notice Enforces policy check for a subject.
42-
/// @dev Only callable by target contract.
43-
/// @param subject Address to validate.
44-
/// @param evidence Validation data.
45-
/// @param checkType Type of check (PRE, MAIN, POST).
51+
/// @notice Enforces a policy check for a subject, handling multi-stage logic.
52+
/// @dev Only callable by the target contract.
4653
function enforce(address subject, bytes[] calldata evidence, Check checkType) external override onlyTarget {
4754
_enforce(subject, evidence, checkType);
4855
}
4956

50-
/// @notice Internal check enforcement logic.
51-
/// @dev Handles different check types and their dependencies.
52-
/// @param subject Address to validate.
53-
/// @param evidence Validation data.
54-
/// @param checkType Type of check to perform.
55-
/// @custom:throws CannotPreCheckWhenSkipped If PRE check attempted when skipped.
56-
/// @custom:throws CannotPostCheckWhenSkipped If POST check attempted when skipped.
57-
/// @custom:throws UnsuccessfulCheck If validation fails.
58-
/// @custom:throws AlreadyEnforced If check was already completed.
59-
/// @custom:throws PreCheckNotEnforced If PRE check is required but not done.
60-
/// @custom:throws MainCheckNotEnforced If MAIN check is required but not done.
61-
/// @custom:throws MainCheckAlreadyEnforced If multiple MAIN checks not allowed.
57+
/// @notice Internal check enforcement logic for advanced multi-stage checks.
6258
function _enforce(address subject, bytes[] calldata evidence, Check checkType) internal {
6359
if (!ADVANCED_CHECKER.check(subject, evidence, checkType)) {
6460
revert UnsuccessfulCheck();
6561
}
6662

6763
CheckStatus storage status = enforced[subject];
6864

69-
// Handle PRE check.
7065
if (checkType == Check.PRE) {
7166
if (SKIP_PRE) revert CannotPreCheckWhenSkipped();
72-
if (status.pre) {
73-
revert AlreadyEnforced();
74-
}
75-
67+
if (status.pre) revert AlreadyEnforced();
7668
status.pre = true;
7769
} else if (checkType == Check.POST) {
78-
// Handle POST check.
7970
if (SKIP_POST) revert CannotPostCheckWhenSkipped();
80-
if (status.main == 0) {
81-
revert MainCheckNotEnforced();
82-
}
83-
if (status.post) {
84-
revert AlreadyEnforced();
85-
}
71+
if (status.main == 0) revert MainCheckNotEnforced();
72+
if (status.post) revert AlreadyEnforced();
8673
status.post = true;
8774
} else {
88-
// Handle MAIN check.
89-
if (!SKIP_PRE && !status.pre) {
90-
revert PreCheckNotEnforced();
91-
}
92-
if (!ALLOW_MULTIPLE_MAIN && status.main > 0) {
93-
revert MainCheckAlreadyEnforced();
94-
}
75+
// MAIN check
76+
if (!SKIP_PRE && !status.pre) revert PreCheckNotEnforced();
77+
if (!ALLOW_MULTIPLE_MAIN && status.main > 0) revert MainCheckAlreadyEnforced();
9578
status.main += 1;
9679
}
9780

packages/contracts/contracts/src/core/policy/BasePolicy.sol

+21-21
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,54 @@ pragma solidity ^0.8.20;
44
import {IBasePolicy} from "../interfaces/IBasePolicy.sol";
55
import {Policy} from "./Policy.sol";
66
import {BaseChecker} from "../checker/BaseChecker.sol";
7+
import {LibClone} from "solady/src/utils/LibClone.sol";
78

89
/// @title BasePolicy
910
/// @notice Abstract base contract for implementing specific policy checks.
1011
/// @dev Inherits from Policy and implements IBasePolicy interface.
11-
///
12-
/// Provides core functionality for enforcing policy checks through a BaseChecker
13-
/// contract. Each specific policy implementation should extend this contract
14-
/// and implement its custom checking logic.
12+
/// Now uses an `initialize()` function for minimal proxy clones.
1513
abstract contract BasePolicy is Policy, IBasePolicy {
1614
/// @notice Reference to the BaseChecker contract used for validation.
17-
/// @dev Immutable to ensure checker cannot be changed after deployment.
18-
BaseChecker public immutable BASE_CHECKER;
15+
/// @dev Stored in normal storage (not immutable) so it can be set in `initialize()`.
16+
BaseChecker public BASE_CHECKER;
1917

2018
/// @notice Tracks enforcement status for each subject per target.
21-
/// @dev Maps subject => enforcement status.
2219
mapping(address => bool) public enforced;
2320

24-
/// @notice Initializes the contract with a BaseChecker instance.
25-
/// @param _baseChecker Address of the BaseChecker contract.
26-
/// @dev The BaseChecker address cannot be changed after deployment.
27-
constructor(BaseChecker _baseChecker) {
28-
BASE_CHECKER = _baseChecker;
21+
/**
22+
* @notice Initializes the contract with a BaseChecker instance, reading from appended bytes.
23+
* Replaces the old constructor-based approach.
24+
*/
25+
function initialize() public virtual override {
26+
// 1. Call the base `Policy.initialize()` to set ownership / handle `_initialized`.
27+
super.initialize();
28+
29+
// 2. Decode the appended bytes to get the BaseChecker address (and anything else you might need).
30+
bytes memory data = _getAppendedBytes();
31+
(address sender, address baseCheckerAddr) = abi.decode(data, (address, address));
32+
33+
_transferOwnership(sender);
34+
35+
// 3. Store in the contract’s storage (previously `immutable`).
36+
BASE_CHECKER = BaseChecker(baseCheckerAddr);
2937
}
3038

3139
/// @notice External function to enforce policy checks.
3240
/// @dev Only callable by the target contract.
3341
/// @param subject Address to enforce the check on.
3442
/// @param evidence Additional data required for verification.
35-
/// @custom:throws AlreadyEnforced if check was previously enforced.
36-
/// @custom:throws UnsuccessfulCheck if the check fails.
37-
/// @custom:emits Enforced when check succeeds.
3843
function enforce(address subject, bytes[] calldata evidence) external override onlyTarget {
3944
_enforce(subject, evidence);
4045
}
4146

4247
/// @notice Internal implementation of enforcement logic.
43-
/// @dev Performs the actual check using BASE_CHECKER.
44-
/// @param subject Address to enforce the check on.
45-
/// @param evidence Additional data required for verification.
46-
/// @custom:throws AlreadyEnforced if already enforced for this subject.
47-
/// @custom:throws UnsuccessfulCheck if BASE_CHECKER.check returns false.
4848
function _enforce(address subject, bytes[] memory evidence) internal {
4949
bool checked = BASE_CHECKER.check(subject, evidence);
5050

5151
if (enforced[subject]) revert AlreadyEnforced();
5252
if (!checked) revert UnsuccessfulCheck();
5353

54-
enforced[subject] = checked;
54+
enforced[subject] = true;
5555

5656
emit Enforced(subject, target, evidence);
5757
}

0 commit comments

Comments
 (0)