-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
87c50b1
commit 30d3350
Showing
5 changed files
with
333 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// SPDX-License-Identifier: MIT AND Apache-2.0 | ||
pragma solidity 0.8.23; | ||
|
||
import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; | ||
import { ModeLib } from "@erc7579/lib/ModeLib.sol"; | ||
|
||
import { CaveatEnforcer } from "./CaveatEnforcer.sol"; | ||
import { Execution, ModeCode } from "../utils/Types.sol"; | ||
import { CALLTYPE_SINGLE, CALLTYPE_BATCH } from "../utils/Constants.sol"; | ||
|
||
/** | ||
* @title NoCalldataEnforcer | ||
* @dev This contract enforces that the execution has no calldata. | ||
* @dev This caveat enforcer only works when the execution is in single mode. | ||
*/ | ||
contract NoCalldataEnforcer is CaveatEnforcer { | ||
using ExecutionLib for bytes; | ||
|
||
////////////////////////////// Public Methods ////////////////////////////// | ||
|
||
/** | ||
* @notice Allows the delegator to restrict the calldata that is executed | ||
* @dev This function enforces that the execution has no calldata. | ||
* @param _mode The execution mode for the execution. | ||
* @param _executionCallData The execution the delegate is trying try to execute. | ||
*/ | ||
function beforeHook( | ||
bytes calldata, | ||
bytes calldata, | ||
ModeCode _mode, | ||
bytes calldata _executionCallData, | ||
bytes32, | ||
address, | ||
address | ||
) | ||
public | ||
pure | ||
override | ||
{ | ||
if (ModeLib.getCallType(_mode) == CALLTYPE_SINGLE) { | ||
(,, bytes calldata callData_) = _executionCallData.decodeSingle(); | ||
require(callData_.length == 0, "NoCalldataEnforcer:calldata-not-allowed"); | ||
} else if (ModeLib.getCallType(_mode) == CALLTYPE_BATCH) { | ||
(Execution[] calldata executions_) = _executionCallData.decodeBatch(); | ||
for (uint256 i = 0; i < executions_.length; i++) { | ||
require(executions_[i].callData.length == 0, "NoCalldataEnforcer:calldata-not-allowed"); | ||
} | ||
} else { | ||
revert("NoCalldataEnforcer:invalid-calltype"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// SPDX-License-Identifier: MIT AND Apache-2.0 | ||
pragma solidity 0.8.23; | ||
|
||
import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; | ||
import { ModeLib } from "@erc7579/lib/ModeLib.sol"; | ||
|
||
import { CaveatEnforcer } from "./CaveatEnforcer.sol"; | ||
import { Execution, ModeCode } from "../utils/Types.sol"; | ||
import { CALLTYPE_SINGLE, CALLTYPE_BATCH } from "../utils/Constants.sol"; | ||
|
||
/** | ||
* @title NoValueEnforcer | ||
* @dev This contract enforces that the execution has no value. | ||
* @dev This caveat enforcer only works when the execution is in single mode. | ||
*/ | ||
contract NoValueEnforcer is CaveatEnforcer { | ||
using ExecutionLib for bytes; | ||
|
||
////////////////////////////// Public Methods ////////////////////////////// | ||
|
||
/** | ||
* @notice Allows the delegator to restrict the value that is executed | ||
* @dev This function enforces that the execution has no value. | ||
* @param _mode The execution mode for the execution. | ||
* @param _executionCallData The execution the delegate is trying try to execute. | ||
*/ | ||
function beforeHook( | ||
bytes calldata, | ||
bytes calldata, | ||
ModeCode _mode, | ||
bytes calldata _executionCallData, | ||
bytes32, | ||
address, | ||
address | ||
) | ||
public | ||
pure | ||
override | ||
{ | ||
if (ModeLib.getCallType(_mode) == CALLTYPE_SINGLE) { | ||
(, uint256 value_,) = _executionCallData.decodeSingle(); | ||
require(value_ == 0, "NoValueEnforcer:value-not-allowed"); | ||
} else if (ModeLib.getCallType(_mode) == CALLTYPE_BATCH) { | ||
(Execution[] calldata executions_) = _executionCallData.decodeBatch(); | ||
for (uint256 i = 0; i < executions_.length; i++) { | ||
require(executions_[i].value == 0, "NoValueEnforcer:value-not-allowed"); | ||
} | ||
} else { | ||
revert("NoValueEnforcer:invalid-calltype"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// SPDX-License-Identifier: MIT AND Apache-2.0 | ||
pragma solidity 0.8.23; | ||
|
||
import "forge-std/Test.sol"; | ||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import { BytesLib } from "@bytes-utils/BytesLib.sol"; | ||
import { ModeLib } from "@erc7579/lib/ModeLib.sol"; | ||
import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; | ||
|
||
import { Counter } from "../utils/Counter.t.sol"; | ||
import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; | ||
import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; | ||
import { NoCalldataEnforcer } from "../../src/enforcers/NoCalldataEnforcer.sol"; | ||
import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol"; | ||
import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; | ||
import { BasicERC20, IERC20 } from "../utils/BasicERC20.t.sol"; | ||
import { BasicCF721 } from "../utils/BasicCF721.t.sol"; | ||
import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; | ||
|
||
contract DummyContract { | ||
function stringFn(uint256[] calldata _str) public { } | ||
function arrayFn(string calldata _str) public { } | ||
} | ||
|
||
contract NoCalldataEnforcerTest is CaveatEnforcerBaseTest { | ||
using ModeLib for ModeCode; | ||
|
||
////////////////////////////// State ////////////////////////////// | ||
NoCalldataEnforcer public noCalldataEnforcer; | ||
DummyContract public c; | ||
|
||
ModeCode public singleMode = ModeLib.encodeSimpleSingle(); | ||
ModeCode public batchMode = ModeLib.encodeSimpleBatch(); | ||
|
||
////////////////////// Set up ////////////////////// | ||
|
||
function setUp() public override { | ||
super.setUp(); | ||
noCalldataEnforcer = new NoCalldataEnforcer(); | ||
vm.label(address(noCalldataEnforcer), "No Calldata Enforcer"); | ||
c = new DummyContract(); | ||
} | ||
|
||
////////////////////// Valid cases ////////////////////// | ||
|
||
// should allow an execution in single mode with no calldata | ||
function test_singleMethodNoCalldataIsAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); | ||
|
||
vm.prank(address(delegationManager)); | ||
noCalldataEnforcer.beforeHook(hex"", hex"", singleMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
// should allow an execution in batch mode with no calldata | ||
function test_batchMethodNoCalldataIsAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
Execution[] memory executions_ = new Execution[](2); | ||
executions_[0] = execution_; | ||
executions_[1] = execution_; | ||
bytes memory executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
noCalldataEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
////////////////////// Invalid cases ////////////////////// | ||
|
||
// should not allow an execution in single mode with calldata | ||
function test_singleMethodCalldataIsNotAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ | ||
target: address(c), | ||
value: 0, | ||
callData: abi.encodeWithSelector(DummyContract.stringFn.selector, uint256(1)) | ||
}); | ||
bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoCalldataEnforcer:calldata-not-allowed"); | ||
noCalldataEnforcer.beforeHook(hex"", hex"", singleMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
// should not allow an execution in batch mode with calldata | ||
function test_batchMethodCalldataIsNotAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ | ||
target: address(c), | ||
value: 0, | ||
callData: abi.encodeWithSelector(DummyContract.stringFn.selector, uint256(1)) | ||
}); | ||
Execution[] memory executions_ = new Execution[](2); | ||
executions_[0] = execution_; | ||
executions_[1] = execution_; | ||
bytes memory executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoCalldataEnforcer:calldata-not-allowed"); | ||
noCalldataEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
|
||
// Make a subset of the executions have no calldata | ||
execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
executions_[0] = execution_; | ||
executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoCalldataEnforcer:calldata-not-allowed"); | ||
noCalldataEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
////////////////////// Integration ////////////////////// | ||
|
||
function _getEnforcer() internal view override returns (ICaveatEnforcer) { | ||
return ICaveatEnforcer(address(noCalldataEnforcer)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// SPDX-License-Identifier: MIT AND Apache-2.0 | ||
pragma solidity 0.8.23; | ||
|
||
import "forge-std/Test.sol"; | ||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import { BytesLib } from "@bytes-utils/BytesLib.sol"; | ||
import { ModeLib } from "@erc7579/lib/ModeLib.sol"; | ||
import { ExecutionLib } from "@erc7579/lib/ExecutionLib.sol"; | ||
|
||
import { Counter } from "../utils/Counter.t.sol"; | ||
import { Execution, Caveat, Delegation, ModeCode } from "../../src/utils/Types.sol"; | ||
import { CaveatEnforcerBaseTest } from "./CaveatEnforcerBaseTest.t.sol"; | ||
import { NoValueEnforcer } from "../../src/enforcers/NoValueEnforcer.sol"; | ||
import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol"; | ||
import { EncoderLib } from "../../src/libraries/EncoderLib.sol"; | ||
import { BasicERC20, IERC20 } from "../utils/BasicERC20.t.sol"; | ||
import { BasicCF721 } from "../utils/BasicCF721.t.sol"; | ||
import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol"; | ||
|
||
contract DummyContract { | ||
function stringFn(uint256[] calldata _str) public { } | ||
function arrayFn(string calldata _str) public { } | ||
} | ||
|
||
contract NoValueEnforcerTest is CaveatEnforcerBaseTest { | ||
using ModeLib for ModeCode; | ||
|
||
////////////////////////////// State ////////////////////////////// | ||
NoValueEnforcer public noValueEnforcer; | ||
DummyContract public c; | ||
|
||
ModeCode public singleMode = ModeLib.encodeSimpleSingle(); | ||
ModeCode public batchMode = ModeLib.encodeSimpleBatch(); | ||
|
||
////////////////////// Set up ////////////////////// | ||
|
||
function setUp() public override { | ||
super.setUp(); | ||
noValueEnforcer = new NoValueEnforcer(); | ||
vm.label(address(noValueEnforcer), "No Value Enforcer"); | ||
c = new DummyContract(); | ||
} | ||
|
||
////////////////////// Valid cases ////////////////////// | ||
|
||
// should allow an execution in single mode with no calldata | ||
function test_singleMethodNoCalldataIsAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); | ||
|
||
vm.prank(address(delegationManager)); | ||
noValueEnforcer.beforeHook(hex"", hex"", singleMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
// should allow an execution in batch mode with no calldata | ||
function test_batchMethodNoCalldataIsAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
Execution[] memory executions_ = new Execution[](2); | ||
executions_[0] = execution_; | ||
executions_[1] = execution_; | ||
bytes memory executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
noValueEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
////////////////////// Invalid cases ////////////////////// | ||
|
||
// should not allow an execution in single mode with calldata | ||
function test_singleMethodCalldataIsNotAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 1, callData: hex"" }); | ||
bytes memory executionCallData_ = ExecutionLib.encodeSingle(execution_.target, execution_.value, execution_.callData); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoValueEnforcer:value-not-allowed"); | ||
noValueEnforcer.beforeHook(hex"", hex"", singleMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
// should not allow an execution in batch mode with calldata | ||
function test_batchMethodCalldataIsNotAllowed() public { | ||
// Create the execution that would be executed | ||
Execution memory execution_ = Execution({ target: address(c), value: 1, callData: hex"" }); | ||
Execution[] memory executions_ = new Execution[](2); | ||
executions_[0] = execution_; | ||
executions_[1] = execution_; | ||
bytes memory executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoValueEnforcer:value-not-allowed"); | ||
noValueEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
|
||
// Make a subset of the executions have no calldata | ||
execution_ = Execution({ target: address(c), value: 0, callData: hex"" }); | ||
executions_[0] = execution_; | ||
executionCallData_ = ExecutionLib.encodeBatch(executions_); | ||
|
||
vm.prank(address(delegationManager)); | ||
vm.expectRevert("NoValueEnforcer:value-not-allowed"); | ||
noValueEnforcer.beforeHook(hex"", hex"", batchMode, executionCallData_, keccak256(""), address(0), address(0)); | ||
} | ||
|
||
////////////////////// Integration ////////////////////// | ||
|
||
function _getEnforcer() internal view override returns (ICaveatEnforcer) { | ||
return ICaveatEnforcer(address(noValueEnforcer)); | ||
} | ||
} |