-
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Caveats helper library #22
base: main
Are you sure you want to change the base?
Conversation
This is my current best draft of a preprompt to help development of caveat enforcers. It will need to be kept up to date when we change interfaces. I believe I have it up to date with main now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation looks very good for simplifying the use of caveats. However, I have some concerns about two aspects:
-
File Size Limitations: This file has a size limit, but the number of caveats can be unlimited. Eventually, we might reach the maximum file size.
-
Extensibility and Custom Caveats: We aim to allow anyone to create their own caveats. If we centralize the logic in this library, developers would need to submit PRs to include their new caveats here, which could hinder flexibility.
Additionally, while this approach is useful from a Solidity-only perspective, when using the SDK we should have a caveat builder that accepts specific parameters and handles the encoding properly. This way, developers don't need to be concerned with the encoding process.
I was thinking—though it might seem a bit unconventional since we encode and decode primarily for gas optimization—but what if we add the caveat encoding and decoding function inside the caveats themselves, this way caveat authors need to handle this encoding themselves?
I would look something like this:
- Renamed the
getTermsInfo
todecodeTerms
- Added
encodeTerms
function
import { CaveatEnforcer } from "./CaveatEnforcer.sol";
import { ModeCode } from "../utils/Types.sol";
/**
* @title Nonce Enforcer Contract
* @dev This contract extends the CaveatEnforcer contract. It provides functionality to add an nonce to a delegation and enable
* multi delegation revocation based on that nonce by incrementing the current nonce.
*/
contract NonceEnforcer is CaveatEnforcer {
////////////////////// State //////////////////////
mapping(address delegationManager => mapping(address delegator => uint256 nonce)) public currentNonce;
////////////////////////////// Events //////////////////////////////
event UsedNonce(address indexed delegationManager, address indexed delegator, uint256 nonce);
////////////////////////////// Public Methods //////////////////////////////
/**
* @notice
* @param _terms A uint256 representing the nonce used in the delegation.
*/
function beforeHook(
bytes calldata _terms,
bytes calldata,
ModeCode,
bytes calldata,
bytes32,
address _delegator,
address
)
public
view
override
{
uint256 nonce_ = decodeTerms(_terms);
require(currentNonce[msg.sender][_delegator] == nonce_, "NonceEnforcer:invalid-nonce");
}
/**
* @notice Encodes the terms used in this CaveatEnforcer.
* @param _nonce The nonce to enforce.
* @return terms_ The ID used in the delegation.
*/
function encodeTerms(uint256 _nonce) public pure returns (bytes memory terms_) {
return abi.encodePacked(_nonce);
}
/**
* @notice Decodes the terms used in this CaveatEnforcer.
* @param _terms encoded data that is used during the execution hooks.
* @return nonce_ The ID used in the delegation.
*/
function decodeTerms(bytes calldata _terms) public pure returns (uint256 nonce_) {
require(_terms.length == 32, "NonceEnforcer:invalid-terms-length");
nonce_ = uint256(bytes32(_terms));
}
/**
* @notice Increments the nonce of a delegator. This invalidates all previous delegations with the old nonce.
* @dev The message sender must be the delegator.
* @param _delegationManager the address of the delegation manager that the user is using.
*/
function incrementNonce(address _delegationManager) external {
uint256 oldNonce_;
unchecked {
oldNonce_ = currentNonce[_delegationManager][msg.sender]++;
}
emit UsedNonce(_delegationManager, msg.sender, oldNonce_);
}
}
Then to use it we would do:
Caveat memory caveat_ = Caveat(address(nonceEnforcer), nonceEnforcer.encode(99), hex"");
// OR
Caveat memory caveat_ = Caveat({
enforcer: address(nonceEnforcer),
terms: nonceEnforcer.encode(99),
args: hex""
});
// OR Through a Lib
library Caveats {
function getCaveat(address _enforcerAddress, bytes memory _terms) internal pure returns (Caveat memory) {
return Caveat({
enforcer: _enforcerAddress,
terms: _terms,
args: ""
});
}
}
Caveat memory caveat_ = Caveats.getCaveat(address(nonceEnforcer), nonceEnforcer.encode(99));
Caveats Library: Simplified Caveat Creation for Tests
This PR introduces the
Caveats
library, designed to streamline the process of creating caveats for testing purposes. The library provides a set of easy-to-use functions that generate caveat structures with minimal input, prioritizing readability over gas efficiency.Key Features
Library Interface
The
Caveats
library exposes the following functions, each returning aCaveat
struct:createAllowedCalldataCaveat(address, uint256, bytes)
createERC721TransferCaveat(address, address, uint256)
createRedeemerCaveat(address, address[])
createValueLteCaveat(address, uint256)
createNativeAllowanceCaveat(address, uint256)
createTimestampCaveat(address, uint128, uint128)
createPasswordCaveat(address, uint256)
createNonceCaveat(address, uint256)
createIdCaveat(address, uint256)
createNativeBalanceGteCaveat(address, address, uint256)
createNativeTokenPaymentCaveat(address, address, uint256)
createLimitedCallsCaveat(address, uint256)
createAllowedMethodsCaveat(address, string[])
createAllowedTargetsCaveat(address, address[])
createArgsEqualityCheckCaveat(address, bytes)
createBlockNumberCaveat(address, uint128, uint128)
createDeployedEnforcerCaveat(address, address, bytes32, bytes)
createERC20BalanceGteCaveat(address, address, uint256)
createERC20TransferAmountCaveat(address, address, uint256)
Each function takes the enforcer address as its first parameter, followed by caveat-specific parameters. The library handles the necessary abi encoding and packing internally, returning a properly formatted
Caveat
struct.Usage Example
This library significantly simplifies caveat creation in tests, improving code readability and reducing the potential for errors when manually constructing caveat structures.