Skip to content

Commit

Permalink
Add Caveats helper library
Browse files Browse the repository at this point in the history
  • Loading branch information
danfinlay committed Sep 25, 2024
1 parent fb59cfa commit 47a51b4
Show file tree
Hide file tree
Showing 19 changed files with 505 additions and 211 deletions.
289 changes: 289 additions & 0 deletions src/libraries/Caveats.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: MIT AND Apache-2.0
pragma solidity 0.8.23;

import { Caveat } from "../utils/Types.sol";
import { AllowedCalldataEnforcer } from "../enforcers/AllowedCalldataEnforcer.sol";
import { AllowedMethodsEnforcer } from "../enforcers/AllowedMethodsEnforcer.sol";
import { AllowedTargetsEnforcer } from "../enforcers/AllowedTargetsEnforcer.sol";
import { ArgsEqualityCheckEnforcer } from "../enforcers/ArgsEqualityCheckEnforcer.sol";
import { BlockNumberEnforcer } from "../enforcers/BlockNumberEnforcer.sol";
import { DeployedEnforcer } from "../enforcers/DeployedEnforcer.sol";
import { ERC20BalanceGteEnforcer } from "../enforcers/ERC20BalanceGteEnforcer.sol";
import { ERC20TransferAmountEnforcer } from "../enforcers/ERC20TransferAmountEnforcer.sol";
import { ERC721TransferEnforcer } from "../enforcers/ERC721TransferEnforcer.sol";
import { IdEnforcer } from "../enforcers/IdEnforcer.sol";
import { LimitedCallsEnforcer } from "../enforcers/LimitedCallsEnforcer.sol";
import { NativeTokenTransferAmountEnforcer } from "../enforcers/NativeTokenTransferAmountEnforcer.sol";
import { NativeBalanceGteEnforcer } from "../enforcers/NativeBalanceGteEnforcer.sol";
import { NativeTokenPaymentEnforcer } from "../enforcers/NativeTokenPaymentEnforcer.sol";
import { NonceEnforcer } from "../enforcers/NonceEnforcer.sol";
import { RedeemerEnforcer } from "../enforcers/RedeemerEnforcer.sol";
import { TimestampEnforcer } from "../enforcers/TimestampEnforcer.sol";
import { ValueLteEnforcer } from "../enforcers/ValueLteEnforcer.sol";
/**
@title Caveats
@notice This library aims to export the easier way to create caveats for tests. Its parameters should always be provided in the easiest creator-readable way, even at the cost of gas.
*/
library Caveats {
function createAllowedCalldataCaveat(
address enforcerAddress,
uint256 dataStart,
bytes memory expectedValue
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(dataStart, expectedValue);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createERC721TransferCaveat(
address enforcerAddress,
address permittedContract,
uint256 permittedTokenId
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(permittedContract, permittedTokenId);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createRedeemerCaveat(
address enforcerAddress,
address[] memory allowedRedeemers
) internal pure returns (Caveat memory) {
bytes memory terms = new bytes(allowedRedeemers.length * 20);
for (uint256 i = 0; i < allowedRedeemers.length; i++) {
bytes20 redeemer = bytes20(allowedRedeemers[i]);
for (uint256 j = 0; j < 20; j++) {
terms[i * 20 + j] = redeemer[j];
}
}

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createValueLteCaveat(
address enforcerAddress,
uint256 maxValue
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encode(maxValue);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createNativeAllowanceCaveat(
address enforcerAddress,
uint256 allowance
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encode(allowance);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createTimestampCaveat(
address enforcerAddress,
uint128 timestampAfterThreshold,
uint128 timestampBeforeThreshold
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(timestampAfterThreshold, timestampBeforeThreshold);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createNonceCaveat(
address enforcerAddress,
uint256 nonce
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encode(nonce);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createIdCaveat(
address enforcerAddress,
uint256 id
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encode(id);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createNativeBalanceGteCaveat(
address enforcerAddress,
address recipient,
uint256 minBalanceIncrease
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(recipient, minBalanceIncrease);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createNativeTokenPaymentCaveat(
address enforcerAddress,
address recipient,
uint256 amount
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(recipient, amount);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createLimitedCallsCaveat(
address enforcerAddress,
uint256 limit
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encode(limit);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createAllowedMethodsCaveat(
address enforcerAddress,
string[] memory approvedMethods
) internal pure returns (Caveat memory) {
bytes memory terms = new bytes(approvedMethods.length * 4);
uint256 offset = 0;

for (uint256 i = 0; i < approvedMethods.length; i++) {
bytes4 methodId = bytes4(keccak256(bytes(approvedMethods[i])));
assembly {
mstore(add(add(terms, 32), offset), methodId)
}
offset += 4;
}

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}
function createAllowedTargetsCaveat(
address enforcerAddress,
address[] memory allowedTargets
) internal pure returns (Caveat memory) {
bytes memory terms = new bytes(allowedTargets.length * 20);

for (uint256 i = 0; i < allowedTargets.length; i++) {
bytes20 target = bytes20(allowedTargets[i]);
for (uint256 j = 0; j < 20; j++) {
terms[i * 20 + j] = target[j];
}
}

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}
function createArgsEqualityCheckCaveat(
address enforcerAddress,
bytes memory expectedArgs
) internal pure returns (Caveat memory) {
return Caveat({
enforcer: enforcerAddress,
terms: expectedArgs,
args: ""
});
}

function createBlockNumberCaveat(
address enforcerAddress,
uint128 blockAfterThreshold,
uint128 blockBeforeThreshold
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(blockAfterThreshold, blockBeforeThreshold);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createDeployedEnforcerCaveat(
address enforcerAddress,
address expectedAddress,
bytes32 salt,
bytes memory bytecode
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(expectedAddress, salt, bytecode);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createERC20BalanceGteCaveat(
address enforcerAddress,
address token,
uint256 amount
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(token, amount);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}

function createERC20TransferAmountCaveat(
address enforcerAddress,
address token,
uint256 maxAmount
) internal pure returns (Caveat memory) {
bytes memory terms = abi.encodePacked(token, maxAmount);

return Caveat({
enforcer: enforcerAddress,
terms: terms,
args: ""
});
}
}
11 changes: 5 additions & 6 deletions test/enforcers/AllowedCalldataEnforcer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ 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";
import { Caveats } from "../../src/libraries/Caveats.sol";

contract DummyContract {
function stringFn(uint256[] calldata _str) public { }
Expand Down Expand Up @@ -209,7 +210,7 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest {
bytes memory inputTerms_ = abi.encodePacked(paramStart_, paramValue_);

Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] = Caveat({ args: hex"", enforcer: address(allowedCalldataEnforcer), terms: inputTerms_ });
caveats_[0] = Caveats.createAllowedCalldataCaveat(address(allowedCalldataEnforcer), paramStart_, abi.encode(paramValue_));
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down Expand Up @@ -248,14 +249,12 @@ contract AllowedCalldataEnforcerTest is CaveatEnforcerBaseTest {
value: 0,
callData: abi.encodeWithSelector(IERC20.transfer.selector, address(users.bob.deleGator), uint256(2))
});

// create terms for the enforcer
uint256 paramStart_ = abi.encodeWithSelector(IERC20.transfer.selector, address(0)).length;
uint256 paramValue_ = 1;
bytes memory inputTerms_ = abi.encodePacked(paramStart_, paramValue_);
uint256 dataStart = abi.encodeWithSelector(IERC20.transfer.selector, address(0)).length;
bytes memory expectedValue = abi.encode(uint256(1));

Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] = Caveat({ args: hex"", enforcer: address(allowedCalldataEnforcer), terms: inputTerms_ });
caveats_[0] = Caveats.createAllowedCalldataCaveat(address(allowedCalldataEnforcer), dataStart, expectedValue);
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down
16 changes: 9 additions & 7 deletions test/enforcers/AllowedMethodsEnforcer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AllowedMethodsEnforcer } from "../../src/enforcers/AllowedMethodsEnforc
import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol";
import { EncoderLib } from "../../src/libraries/EncoderLib.sol";
import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol";
import { Caveats } from "../../src/libraries/Caveats.sol";

contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest {
using ModeLib for ModeCode;
Expand Down Expand Up @@ -139,8 +140,9 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest {
});

Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] =
Caveat({ args: hex"", enforcer: address(allowedMethodsEnforcer), terms: abi.encodePacked(Counter.increment.selector) });
string[] memory approvedMethods = new string[](1);
approvedMethods[0] = "increment()";
caveats_[0] = Caveats.createAllowedMethodsCaveat(address(allowedMethodsEnforcer), approvedMethods);
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down Expand Up @@ -183,11 +185,11 @@ contract AllowedMethodsEnforcerTest is CaveatEnforcerBaseTest {
});

Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] = Caveat({
args: hex"",
enforcer: address(allowedMethodsEnforcer),
terms: abi.encodePacked(Counter.setCount.selector, Ownable.renounceOwnership.selector, Ownable.owner.selector)
});
string[] memory approvedMethods = new string[](3);
approvedMethods[0] = "setCount(uint256)";
approvedMethods[1] = "renounceOwnership()";
approvedMethods[2] = "owner()";
caveats_[0] = Caveats.createAllowedMethodsCaveat(address(allowedMethodsEnforcer), approvedMethods);
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down
15 changes: 8 additions & 7 deletions test/enforcers/AllowedTargetsEnforcer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { IDelegationManager } from "../../src/interfaces/IDelegationManager.sol"
import { EncoderLib } from "../../src/libraries/EncoderLib.sol";
import { ICaveatEnforcer } from "../../src/interfaces/ICaveatEnforcer.sol";
import { BasicERC20, IERC20 } from "../utils/BasicERC20.t.sol";
import { Caveats } from "../../src/libraries/Caveats.sol";

contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest {
using ModeLib for ModeCode;
Expand Down Expand Up @@ -129,11 +130,10 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest {
});

Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] = Caveat({
args: hex"",
enforcer: address(allowedTargetsEnforcer),
terms: abi.encodePacked(address(aliceDeleGatorCounter), address(testFToken1))
});
address[] memory allowedTargets = new address[](2);
allowedTargets[0] = address(aliceDeleGatorCounter);
allowedTargets[1] = address(testFToken1);
caveats_[0] = Caveats.createAllowedTargetsCaveat(address(allowedTargetsEnforcer), allowedTargets);
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down Expand Up @@ -180,8 +180,9 @@ contract AllowedTargetsEnforcerTest is CaveatEnforcerBaseTest {

// Approving the user to use the FToken1
Caveat[] memory caveats_ = new Caveat[](1);
caveats_[0] =
Caveat({ args: hex"", enforcer: address(allowedTargetsEnforcer), terms: abi.encodePacked(address(testFToken1)) });
address[] memory allowedTargets = new address[](1);
allowedTargets[0] = address(testFToken1);
caveats_[0] = Caveats.createAllowedTargetsCaveat(address(allowedTargetsEnforcer), allowedTargets);
Delegation memory delegation_ = Delegation({
delegate: address(users.bob.deleGator),
delegator: address(users.alice.deleGator),
Expand Down
Loading

0 comments on commit 47a51b4

Please sign in to comment.