Skip to content

Commit

Permalink
Add SignedPermission struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ilikesymmetry committed Oct 9, 2024
1 parent 9670fb0 commit 5bfa1b4
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 16 deletions.
27 changes: 17 additions & 10 deletions src/SpendPermissions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ contract SpendPermissions {
uint160 allowance;
}

/// @notice A signed permit to approve a recurring allowance.
struct SignedPermission {
/// @dev Recurring allowance parameters.
RecurringAllowance recurringAllowance;
/// @dev User signature to validate via EIP-1271.
bytes signature;
}

/// @notice Cycle parameters and spend usage.
struct CycleUsage {
/// @dev Start time of the cycle (unix seconds).
Expand Down Expand Up @@ -150,26 +158,25 @@ contract SpendPermissions {
/// @param recipient Address to withdraw tokens to.
/// @param value Amount of token attempting to withdraw (wei).
function withdraw(bytes calldata context, address recipient, uint160 value) external {
(RecurringAllowance memory recurringAllowance, bytes memory signature) =
abi.decode(context, (RecurringAllowance, bytes));
permit(recurringAllowance, signature);
withdraw(recurringAllowance, recipient, value);
SignedPermission memory signedPermission = abi.decode(context, (SignedPermission));
permit(signedPermission);
withdraw(signedPermission.recurringAllowance, recipient, value);
}

/// @notice Approve a recurring allowance via a signature from the account.
///
/// @param recurringAllowance Details of the recurring allowance.
/// @param signature Signed hash of the recurring allowance data.
function permit(RecurringAllowance memory recurringAllowance, bytes memory signature) public {
/// @param signedPermission Signed recurring allowance permission.
function permit(SignedPermission memory signedPermission) public {
// validate signature over recurring allowance data
if (
IERC1271(recurringAllowance.account).isValidSignature(getHash(recurringAllowance), signature)
!= IERC1271.isValidSignature.selector
IERC1271(signedPermission.recurringAllowance.account).isValidSignature(
getHash(signedPermission.recurringAllowance), signedPermission.signature
) != IERC1271.isValidSignature.selector
) {
revert UnauthorizedRecurringAllowance();
}

_approve(recurringAllowance);
_approve(signedPermission.recurringAllowance);
}

/// @notice Withdraw tokens using a recurring allowance.
Expand Down
9 changes: 5 additions & 4 deletions src/SpendPermissionsPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ contract SpendPermissionsPaymaster is SpendPermissions, Ownable2Step, IPaymaster
requireSender(entryPoint())
returns (bytes memory postOpContext, uint256 validationData)
{
(RecurringAllowance memory recurringAllowance, bytes memory signature, uint256 withdrawAmount) =
abi.decode(userOp.paymasterAndData[20:], (RecurringAllowance, bytes, uint256));
(SignedPermission memory signedPermission, uint256 withdrawAmount) =
abi.decode(userOp.paymasterAndData[20:], (SignedPermission, uint256));
RecurringAllowance memory recurringAllowance = signedPermission.recurringAllowance;

// require withdraw amount not less than max gas cost
if (withdrawAmount < maxGasCost) {
Expand All @@ -74,8 +75,8 @@ contract SpendPermissionsPaymaster is SpendPermissions, Ownable2Step, IPaymaster
}

// apply permit if signature length non-zero
if (signature.length > 0) {
permit(recurringAllowance, signature);
if (signedPermission.signature.length > 0) {
permit(signedPermission);
}

// check total spend value does not overflow max value
Expand Down
1 change: 1 addition & 0 deletions test/src/SpendPermissions/Debug.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ contract DebugTest is Test, Base {
}

function test_withdraw(address recipient) public {
vm.assume(recipient != address(spendPermissions));
SpendPermissions.RecurringAllowance memory recurringAllowance = _createRecurringAllowance();

vm.prank(address(account));
Expand Down
8 changes: 6 additions & 2 deletions test/src/SpendPermissionsPaymaster/Debug.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ contract DebugTest is Test, Base {

vm.assertEq(account.isValidSignature(hash, signature), IERC1271.isValidSignature.selector);

bytes memory paymasterData = abi.encode(recurringAllowance, signature, allowance);
SpendPermissions.SignedPermission memory signedPermission =
SpendPermissions.SignedPermission(recurringAllowance, signature);
bytes memory paymasterData = abi.encode(signedPermission, allowance);

userOp.paymasterAndData = abi.encodePacked(address(spendPermissions), paymasterData);

Expand Down Expand Up @@ -79,7 +81,9 @@ contract DebugTest is Test, Base {

vm.assertEq(account.isValidSignature(hash, signature), IERC1271.isValidSignature.selector);

spendPermissions.permit(recurringAllowance, signature);
SpendPermissions.SignedPermission memory signedPermission =
SpendPermissions.SignedPermission(recurringAllowance, signature);
spendPermissions.permit(signedPermission);

vm.assertTrue(spendPermissions.isAuthorized(recurringAllowance));

Expand Down

0 comments on commit 5bfa1b4

Please sign in to comment.