Skip to content

Commit

Permalink
add Permit2Forwarder interface
Browse files Browse the repository at this point in the history
  • Loading branch information
gretzke committed Nov 27, 2024
1 parent 309a35a commit 8678bd4
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/positionDescriptor bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24108
24177
16 changes: 4 additions & 12 deletions src/base/Permit2Forwarder.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {IPermit2Forwarder, IAllowanceTransfer} from "../interfaces/IPermit2Forwarder.sol";

/// @notice PermitForwarder allows permitting this contract as a spender on permit2
/// @dev This contract does not enforce the spender to be this contract, but that is the intended use case
contract Permit2Forwarder {
contract Permit2Forwarder is IPermit2Forwarder {
/// @notice the Permit2 contract to forward approvals
IAllowanceTransfer public immutable permit2;

constructor(IAllowanceTransfer _permit2) {
permit2 = _permit2;
}

/// @notice allows forwarding a single permit to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param permitSingle the permit data
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
/// @inheritdoc IPermit2Forwarder
function permit(address owner, IAllowanceTransfer.PermitSingle calldata permitSingle, bytes calldata signature)
external
payable
Expand All @@ -30,11 +26,7 @@ contract Permit2Forwarder {
}
}

/// @notice allows forwarding batch permits to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param _permitBatch a batch of approvals
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
/// @inheritdoc IPermit2Forwarder
function permitBatch(address owner, IAllowanceTransfer.PermitBatch calldata _permitBatch, bytes calldata signature)
external
payable
Expand Down
30 changes: 30 additions & 0 deletions src/interfaces/IPermit2Forwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";

/// @notice PermitForwarder allows permitting this contract as a spender on permit2
/// @dev This contract does not enforce the spender to be this contract, but that is the intended use case
interface IPermit2Forwarder {
/// @notice allows forwarding a single permit to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param permitSingle the permit data
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
/// @return err the error returned by a reverting permit call, empty if successful
function permit(address owner, IAllowanceTransfer.PermitSingle calldata permitSingle, bytes calldata signature)
external
payable
returns (bytes memory err);

/// @notice allows forwarding batch permits to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param _permitBatch a batch of approvals
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
/// @return err the error returned by a reverting permit call, empty if successful
function permitBatch(address owner, IAllowanceTransfer.PermitBatch calldata _permitBatch, bytes calldata signature)
external
payable
returns (bytes memory err);
}
4 changes: 3 additions & 1 deletion src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {IEIP712_v4} from "./IEIP712_v4.sol";
import {IMulticall_v4} from "./IMulticall_v4.sol";
import {IPoolInitializer} from "./IPoolInitializer.sol";
import {IUnorderedNonce} from "./IUnorderedNonce.sol";
import {IPermit2Forwarder} from "./IPermit2Forwarder.sol";

/// @title IPositionManager
/// @notice Interface for the PositionManager contract
Expand All @@ -21,7 +22,8 @@ interface IPositionManager is
IEIP712_v4,
IMulticall_v4,
IPoolInitializer,
IUnorderedNonce
IUnorderedNonce,
IPermit2Forwarder
{
/// @notice Thrown when the caller is not approved to modify a position
error NotApproved(address caller);
Expand Down
14 changes: 7 additions & 7 deletions test/position-managers/PositionManager.multicall.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol";
import {Planner, Plan} from "../shared/Planner.sol";
import {PosmTestSetup} from "../shared/PosmTestSetup.sol";
import {Permit2SignatureHelpers} from "../shared/Permit2SignatureHelpers.sol";
import {Permit2Forwarder} from "../../src/base/Permit2Forwarder.sol";
import {Permit2Forwarder, IPermit2Forwarder} from "../../src/base/Permit2Forwarder.sol";
import {ActionConstants} from "../../src/libraries/ActionConstants.sol";
import {IERC721Permit_v4} from "../../src/interfaces/IERC721Permit_v4.sol";

Expand Down Expand Up @@ -421,8 +421,8 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest

// bob front-runs the permits
vm.startPrank(bob);
Permit2Forwarder(address(lpm)).permit(charlie, permit0, sig0);
Permit2Forwarder(address(lpm)).permit(charlie, permit1, sig1);
lpm.permit(charlie, permit0, sig0);
lpm.permit(charlie, permit1, sig1);
vm.stopPrank();

// bob's front-run was successful
Expand All @@ -439,8 +439,8 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest

// charlie tries to mint an LP token with multicall(permit, permit, mint)
bytes[] memory calls = new bytes[](3);
calls[0] = abi.encodeWithSelector(Permit2Forwarder(address(lpm)).permit.selector, charlie, permit0, sig0);
calls[1] = abi.encodeWithSelector(Permit2Forwarder(address(lpm)).permit.selector, charlie, permit1, sig1);
calls[0] = abi.encodeWithSelector(IPermit2Forwarder.permit.selector, charlie, permit0, sig0);
calls[1] = abi.encodeWithSelector(IPermit2Forwarder.permit.selector, charlie, permit1, sig1);
bytes memory mintCall = getMintEncoded(config, 10e18, charlie, ZERO_BYTES);
calls[2] = abi.encodeWithSelector(IPositionManager.modifyLiquidities.selector, mintCall, _deadline);

Expand Down Expand Up @@ -475,7 +475,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest

// bob front-runs the permits
vm.prank(bob);
Permit2Forwarder(address(lpm)).permitBatch(charlie, permit, sig);
lpm.permitBatch(charlie, permit, sig);

// bob's front-run was successful
(uint160 _amount, uint48 _expiration, uint48 _nonce) =
Expand All @@ -491,7 +491,7 @@ contract PositionManagerMulticallTest is Test, Permit2SignatureHelpers, PosmTest

// charlie tries to mint an LP token with multicall(permitBatch, mint)
bytes[] memory calls = new bytes[](2);
calls[0] = abi.encodeWithSelector(Permit2Forwarder(address(lpm)).permitBatch.selector, charlie, permit, sig);
calls[0] = abi.encodeWithSelector(lpm.permitBatch.selector, charlie, permit, sig);
bytes memory mintCall = getMintEncoded(config, 10e18, charlie, ZERO_BYTES);
calls[1] = abi.encodeWithSelector(IPositionManager.modifyLiquidities.selector, mintCall, _deadline);

Expand Down

0 comments on commit 8678bd4

Please sign in to comment.