From 5e361469d72b6ed5393beda32ce3266d43ac8cdf Mon Sep 17 00:00:00 2001 From: Anna Carroll Date: Fri, 12 Jul 2024 17:13:32 +0100 Subject: [PATCH] demo: add permit flow --- .gas-snapshot | 44 ++++++++++++++++++++++---------------------- src/Orders.sol | 26 ++++++++++++++++++++++---- src/Passage.sol | 15 ++++++++++++++- src/Permit.sol | 29 +++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 src/Permit.sol diff --git a/.gas-snapshot b/.gas-snapshot index 1a8998c..39f6c81 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,32 +1,32 @@ -OrdersTest:test_fill_ERC20() (gas: 70364) -OrdersTest:test_fill_ETH() (gas: 68414) -OrdersTest:test_fill_both() (gas: 166580) -OrdersTest:test_fill_multiETH() (gas: 131926) -OrdersTest:test_fill_underflowETH() (gas: 115281) -OrdersTest:test_initiate_ERC20() (gas: 81435) -OrdersTest:test_initiate_ETH() (gas: 44949) -OrdersTest:test_initiate_both() (gas: 118677) -OrdersTest:test_initiate_multiERC20() (gas: 688417) -OrdersTest:test_initiate_multiETH() (gas: 75304) -OrdersTest:test_onlyBuilder() (gas: 12815) -OrdersTest:test_orderExpired() (gas: 27956) -OrdersTest:test_sweepERC20() (gas: 60402) -OrdersTest:test_sweepETH() (gas: 81940) -OrdersTest:test_underflowETH() (gas: 63528) -PassageTest:test_configureEnter() (gas: 82311) +OrdersTest:test_fill_ERC20() (gas: 70408) +OrdersTest:test_fill_ETH() (gas: 68458) +OrdersTest:test_fill_both() (gas: 166624) +OrdersTest:test_fill_multiETH() (gas: 131970) +OrdersTest:test_fill_underflowETH() (gas: 115325) +OrdersTest:test_initiate_ERC20() (gas: 81505) +OrdersTest:test_initiate_ETH() (gas: 45019) +OrdersTest:test_initiate_both() (gas: 118738) +OrdersTest:test_initiate_multiERC20() (gas: 688478) +OrdersTest:test_initiate_multiETH() (gas: 75365) +OrdersTest:test_onlyBuilder() (gas: 12859) +OrdersTest:test_orderExpired() (gas: 28026) +OrdersTest:test_sweepERC20() (gas: 60446) +OrdersTest:test_sweepETH() (gas: 82054) +OrdersTest:test_underflowETH() (gas: 63589) +PassageTest:test_configureEnter() (gas: 82357) PassageTest:test_disallowedEnter() (gas: 17916) -PassageTest:test_enter() (gas: 25563) +PassageTest:test_enter() (gas: 25541) PassageTest:test_enterToken() (gas: 64332) PassageTest:test_enterToken_defaultChain() (gas: 62915) -PassageTest:test_enterTransact() (gas: 60890) -PassageTest:test_enter_defaultChain() (gas: 24033) +PassageTest:test_enterTransact() (gas: 60936) +PassageTest:test_enter_defaultChain() (gas: 24011) PassageTest:test_fallback() (gas: 21534) -PassageTest:test_onlyTokenAdmin() (gas: 16926) +PassageTest:test_onlyTokenAdmin() (gas: 16927) PassageTest:test_receive() (gas: 21384) -PassageTest:test_setUp() (gas: 16968) +PassageTest:test_setUp() (gas: 16991) PassageTest:test_transact() (gas: 58562) PassageTest:test_transact_defaultChain() (gas: 57475) -PassageTest:test_withdraw() (gas: 59033) +PassageTest:test_withdraw() (gas: 59011) ZenithTest:test_addSequencer() (gas: 88121) ZenithTest:test_badSignature() (gas: 37241) ZenithTest:test_incorrectHostBlock() (gas: 35086) diff --git a/src/Orders.sol b/src/Orders.sol index aa02e52..019d9cb 100644 --- a/src/Orders.sol +++ b/src/Orders.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.24; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {Permit, PermitLib} from "./Permit.sol"; /// @notice Tokens sent by the swapper as inputs to the order /// @dev From ERC-7683 @@ -28,7 +29,7 @@ struct Output { } /// @notice Contract capable of processing fulfillment of intent-based Orders. -abstract contract OrderDestination { +abstract contract OrderDestination is PermitLib { /// @notice Emitted when Order Outputs are sent to their recipients. /// @dev NOTE that here, Output.chainId denotes the *origin* chainId. event Filled(Output[] outputs); @@ -39,7 +40,7 @@ abstract contract OrderDestination { /// @dev NOTE that here, Output.chainId denotes the *origin* chainId. /// @param outputs - The Outputs to be transferred. /// @custom:emits Filled - function fill(Output[] memory outputs) external payable { + function fill(Output[] memory outputs) public payable { // transfer outputs uint256 value = msg.value; for (uint256 i; i < outputs.length; i++) { @@ -54,10 +55,17 @@ abstract contract OrderDestination { // emit emit Filled(outputs); } + + function fillPermit(Output[] memory outputs, Permit[] memory permits) external payable { + // first, execute token permissions to set token allowance + _permit(permits); + // then, fill orders + fill(outputs); + } } /// @notice Contract capable of registering initiation of intent-based Orders. -abstract contract OrderOrigin { +abstract contract OrderOrigin is PermitLib { /// @notice Thrown when an Order is submitted with a deadline that has passed. error OrderExpired(); @@ -86,7 +94,7 @@ abstract contract OrderOrigin { /// @param outputs - The token amounts that must be received on their target chain(s) in order for the Order to be executed. /// @custom:reverts OrderExpired if the deadline has passed. /// @custom:emits Order if the transaction mines. - function initiate(uint256 deadline, Input[] memory inputs, Output[] memory outputs) external payable { + function initiate(uint256 deadline, Input[] memory inputs, Output[] memory outputs) public payable { // check that the deadline hasn't passed if (block.timestamp > deadline) revert OrderExpired(); @@ -97,6 +105,16 @@ abstract contract OrderOrigin { emit Order(deadline, inputs, outputs); } + function initiatePermit(Permit[] memory permits, uint256 deadline, Input[] memory inputs, Output[] memory outputs) + external + payable + { + // first, execute token permissions to set token allowance + _permit(permits); + // then, initiate the Order + initiate(deadline, inputs, outputs); + } + /// @notice Transfer the Order inputs to this contract, where they can be collected by the Order filler. function _transferInputs(Input[] memory inputs) internal { uint256 value = msg.value; diff --git a/src/Passage.sol b/src/Passage.sol index 8dd3c85..dc7d1d3 100644 --- a/src/Passage.sol +++ b/src/Passage.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.24; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {Permit, PermitLib} from "./Permit.sol"; /// @notice A contract deployed to Host chain that allows tokens to enter the rollup, /// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host. -contract Passage { +contract Passage is PermitLib { /// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive(). uint256 public immutable defaultRollupChainId; @@ -106,6 +107,18 @@ contract Passage { enterToken(defaultRollupChainId, rollupRecipient, token, amount); } + /// @notice Allows ERC20 tokens to enter the rollup with a permit message. + function enterTokenPermit( + uint256 rollupChainId, + address rollupRecipient, + address token, + uint256 amount, + Permit memory permit + ) external { + _permit(permit); + enterToken(rollupChainId, rollupRecipient, token, amount); + } + /// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender. /// @dev Transaction is processed after normal rollup block execution. /// @dev See `enterTransact` for docs. diff --git a/src/Permit.sol b/src/Permit.sol new file mode 100644 index 0000000..0e8d0d0 --- /dev/null +++ b/src/Permit.sol @@ -0,0 +1,29 @@ + // SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol"; + +struct Permit { + address token; + address owner; + address spender; + uint256 value; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; +} + +abstract contract PermitLib { + function _permit(Permit memory permit) internal { + IERC20Permit(permit.token).permit( + permit.owner, permit.spender, permit.value, permit.deadline, permit.v, permit.r, permit.s + ); + } + + function _permit(Permit[] memory permits) internal { + for (uint256 i; i < permits.length; i++) { + _permit(permits[i]); + } + } +}