Skip to content

Commit 3418e9d

Browse files
committed
demo: add permit flow
1 parent 81e9690 commit 3418e9d

File tree

5 files changed

+94
-41
lines changed

5 files changed

+94
-41
lines changed

.gas-snapshot

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
OrdersTest:test_fill_ERC20() (gas: 70364)
2-
OrdersTest:test_fill_ETH() (gas: 68414)
3-
OrdersTest:test_fill_both() (gas: 166580)
4-
OrdersTest:test_fill_multiETH() (gas: 131926)
5-
OrdersTest:test_fill_underflowETH() (gas: 115281)
6-
OrdersTest:test_initiate_ERC20() (gas: 81435)
7-
OrdersTest:test_initiate_ETH() (gas: 44949)
8-
OrdersTest:test_initiate_both() (gas: 118677)
9-
OrdersTest:test_initiate_multiERC20() (gas: 688417)
10-
OrdersTest:test_initiate_multiETH() (gas: 75304)
11-
OrdersTest:test_onlyBuilder() (gas: 12815)
12-
OrdersTest:test_orderExpired() (gas: 27956)
13-
OrdersTest:test_sweepERC20() (gas: 60402)
14-
OrdersTest:test_sweepETH() (gas: 81940)
15-
OrdersTest:test_underflowETH() (gas: 63528)
16-
PassageTest:test_configureEnter() (gas: 82311)
1+
OrdersTest:test_fill_ERC20() (gas: 71146)
2+
OrdersTest:test_fill_ETH() (gas: 69199)
3+
OrdersTest:test_fill_both() (gas: 167356)
4+
OrdersTest:test_fill_multiETH() (gas: 132702)
5+
OrdersTest:test_fill_underflowETH() (gas: 116064)
6+
OrdersTest:test_initiate_ERC20() (gas: 82237)
7+
OrdersTest:test_initiate_ETH() (gas: 45740)
8+
OrdersTest:test_initiate_both() (gas: 119473)
9+
OrdersTest:test_initiate_multiERC20() (gas: 689209)
10+
OrdersTest:test_initiate_multiETH() (gas: 76087)
11+
OrdersTest:test_onlyBuilder() (gas: 12793)
12+
OrdersTest:test_orderExpired() (gas: 28646)
13+
OrdersTest:test_sweepERC20() (gas: 60380)
14+
OrdersTest:test_sweepETH() (gas: 82682)
15+
OrdersTest:test_underflowETH() (gas: 64288)
16+
PassageTest:test_configureEnter() (gas: 82357)
1717
PassageTest:test_disallowedEnter() (gas: 17916)
18-
PassageTest:test_enter() (gas: 25563)
18+
PassageTest:test_enter() (gas: 25541)
1919
PassageTest:test_enterToken() (gas: 64332)
2020
PassageTest:test_enterToken_defaultChain() (gas: 62915)
21-
PassageTest:test_enterTransact() (gas: 60890)
22-
PassageTest:test_enter_defaultChain() (gas: 24033)
21+
PassageTest:test_enterTransact() (gas: 60936)
22+
PassageTest:test_enter_defaultChain() (gas: 24011)
2323
PassageTest:test_fallback() (gas: 21534)
24-
PassageTest:test_onlyTokenAdmin() (gas: 16926)
24+
PassageTest:test_onlyTokenAdmin() (gas: 16927)
2525
PassageTest:test_receive() (gas: 21384)
26-
PassageTest:test_setUp() (gas: 16968)
26+
PassageTest:test_setUp() (gas: 16991)
2727
PassageTest:test_transact() (gas: 58562)
2828
PassageTest:test_transact_defaultChain() (gas: 57475)
29-
PassageTest:test_withdraw() (gas: 59033)
29+
PassageTest:test_withdraw() (gas: 59011)
3030
ZenithTest:test_addSequencer() (gas: 88121)
3131
ZenithTest:test_badSignature() (gas: 37241)
3232
ZenithTest:test_incorrectHostBlock() (gas: 35086)

src/Orders.sol

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5+
import {Permit, PermitLib} from "./Permit.sol";
56

67
/// @notice Tokens sent by the swapper as inputs to the order
78
/// @dev From ERC-7683
@@ -28,7 +29,7 @@ struct Output {
2829
}
2930

3031
/// @notice Contract capable of processing fulfillment of intent-based Orders.
31-
abstract contract OrderDestination {
32+
abstract contract OrderDestination is PermitLib {
3233
/// @notice Emitted when Order Outputs are sent to their recipients.
3334
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
3435
event Filled(Output[] outputs);
@@ -39,7 +40,10 @@ abstract contract OrderDestination {
3940
/// @dev NOTE that here, Output.chainId denotes the *origin* chainId.
4041
/// @param outputs - The Outputs to be transferred.
4142
/// @custom:emits Filled
42-
function fill(Output[] memory outputs) external payable {
43+
function fill(Output[] memory outputs, Permit[] memory permits) external payable {
44+
// first, execute token permissions to set token allowance
45+
_permit(permits);
46+
4347
// transfer outputs
4448
uint256 value = msg.value;
4549
for (uint256 i; i < outputs.length; i++) {
@@ -57,7 +61,7 @@ abstract contract OrderDestination {
5761
}
5862

5963
/// @notice Contract capable of registering initiation of intent-based Orders.
60-
abstract contract OrderOrigin {
64+
abstract contract OrderOrigin is PermitLib {
6165
/// @notice Thrown when an Order is submitted with a deadline that has passed.
6266
error OrderExpired();
6367

@@ -86,10 +90,17 @@ abstract contract OrderOrigin {
8690
/// @param outputs - The token amounts that must be received on their target chain(s) in order for the Order to be executed.
8791
/// @custom:reverts OrderExpired if the deadline has passed.
8892
/// @custom:emits Order if the transaction mines.
89-
function initiate(uint256 deadline, Input[] memory inputs, Output[] memory outputs) external payable {
93+
function initiate(uint256 deadline, Input[] memory inputs, Output[] memory outputs, Permit[] memory permits)
94+
external
95+
payable
96+
{
9097
// check that the deadline hasn't passed
9198
if (block.timestamp > deadline) revert OrderExpired();
9299

100+
// execute token permissions to set token allowance
101+
// DRAWBACKS: (1) sign different permit messages for each token
102+
_permit(permits);
103+
93104
// transfer inputs to this contract
94105
_transferInputs(inputs);
95106

src/Passage.sol

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
pragma solidity ^0.8.24;
33

44
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
5+
import {Permit, PermitLib} from "./Permit.sol";
56

67
/// @notice A contract deployed to Host chain that allows tokens to enter the rollup,
78
/// and enables Builders to fulfill requests to exchange tokens on the Rollup for tokens on the Host.
8-
contract Passage {
9+
contract Passage is PermitLib {
910
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
1011
uint256 public immutable defaultRollupChainId;
1112

@@ -106,6 +107,18 @@ contract Passage {
106107
enterToken(defaultRollupChainId, rollupRecipient, token, amount);
107108
}
108109

110+
/// @notice Allows ERC20 tokens to enter the rollup with a permit message.
111+
function enterTokenPermit(
112+
uint256 rollupChainId,
113+
address rollupRecipient,
114+
address token,
115+
uint256 amount,
116+
Permit memory permit
117+
) external {
118+
_permit(permit);
119+
enterToken(rollupChainId, rollupRecipient, token, amount);
120+
}
121+
109122
/// @notice Allows a special transaction to be sent to the rollup with sender == L1 msg.sender.
110123
/// @dev Transaction is processed after normal rollup block execution.
111124
/// @dev See `enterTransact` for docs.

src/Permit.sol

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.24;
3+
4+
import {IERC20Permit} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
5+
6+
struct Permit {
7+
address token;
8+
address owner;
9+
address spender;
10+
uint256 value;
11+
uint256 deadline;
12+
uint8 v;
13+
bytes32 r;
14+
bytes32 s;
15+
}
16+
17+
abstract contract PermitLib {
18+
function _permit(Permit memory permit) internal {
19+
IERC20Permit(permit.token).permit(
20+
permit.owner, permit.spender, permit.value, permit.deadline, permit.v, permit.r, permit.s
21+
);
22+
}
23+
24+
function _permit(Permit[] memory permits) internal {
25+
for (uint256 i; i < permits.length; i++) {
26+
_permit(permits[i]);
27+
}
28+
}
29+
}

test/Orders.t.sol

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.24;
33

44
import {Test, console2} from "forge-std/Test.sol";
5-
import {RollupOrders, Input, Output, OrderOrigin} from "../src/Orders.sol";
5+
import {RollupOrders, Input, Output, OrderOrigin, Permit} from "../src/Orders.sol";
66
import {TestERC20} from "./Helpers.t.sol";
77
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
88

@@ -50,7 +50,7 @@ contract OrdersTest is Test {
5050
vm.expectCall(
5151
token, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), amount)
5252
);
53-
target.initiate(deadline, inputs, outputs);
53+
target.initiate(deadline, inputs, outputs, new Permit[](0));
5454
}
5555

5656
// input ETH
@@ -61,7 +61,7 @@ contract OrdersTest is Test {
6161
// expect Order event is initiated
6262
vm.expectEmit();
6363
emit Order(deadline, inputs, outputs);
64-
target.initiate{value: amount}(deadline, inputs, outputs);
64+
target.initiate{value: amount}(deadline, inputs, outputs, new Permit[](0));
6565

6666
// ETH is held in target contract
6767
assertEq(address(target).balance, amount);
@@ -78,7 +78,7 @@ contract OrdersTest is Test {
7878
vm.expectCall(
7979
token, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), amount)
8080
);
81-
target.initiate{value: amount}(deadline, inputs, outputs);
81+
target.initiate{value: amount}(deadline, inputs, outputs, new Permit[](0));
8282

8383
// ETH is held in target contract
8484
assertEq(address(target).balance, amount);
@@ -103,7 +103,7 @@ contract OrdersTest is Test {
103103
vm.expectCall(
104104
token2, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), amount * 2)
105105
);
106-
target.initiate(deadline, inputs, outputs);
106+
target.initiate(deadline, inputs, outputs, new Permit[](0));
107107
}
108108

109109
// input multiple ETH inputs
@@ -116,7 +116,7 @@ contract OrdersTest is Test {
116116
// expect Order event is initiated
117117
vm.expectEmit();
118118
emit Order(deadline, inputs, outputs);
119-
target.initiate{value: amount * 3}(deadline, inputs, outputs);
119+
target.initiate{value: amount * 3}(deadline, inputs, outputs, new Permit[](0));
120120

121121
// ETH is held in target contract
122122
assertEq(address(target).balance, amount * 3);
@@ -130,14 +130,14 @@ contract OrdersTest is Test {
130130

131131
// total ETH inputs should be amount + 1; function should underflow only sending amount
132132
vm.expectRevert();
133-
target.initiate{value: amount}(deadline, inputs, outputs);
133+
target.initiate{value: amount}(deadline, inputs, outputs, new Permit[](0));
134134
}
135135

136136
function test_orderExpired() public {
137137
vm.warp(block.timestamp + 1);
138138

139139
vm.expectRevert(OrderOrigin.OrderExpired.selector);
140-
target.initiate(deadline, inputs, outputs);
140+
target.initiate(deadline, inputs, outputs, new Permit[](0));
141141
}
142142

143143
function test_sweepETH() public {
@@ -146,7 +146,7 @@ contract OrdersTest is Test {
146146

147147
// initiate an ETH order
148148
inputs[0].token = address(0);
149-
target.initiate{value: amount}(deadline, inputs, outputs);
149+
target.initiate{value: amount}(deadline, inputs, outputs, new Permit[](0));
150150

151151
assertEq(address(target).balance, amount);
152152

@@ -182,7 +182,7 @@ contract OrdersTest is Test {
182182

183183
vm.expectEmit();
184184
emit Filled(outputs);
185-
target.fill{value: amount}(outputs);
185+
target.fill{value: amount}(outputs, new Permit[](0));
186186

187187
// ETH is transferred to recipient
188188
assertEq(recipient.balance, amount);
@@ -192,7 +192,7 @@ contract OrdersTest is Test {
192192
vm.expectEmit();
193193
emit Filled(outputs);
194194
vm.expectCall(token, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), recipient, amount));
195-
target.fill(outputs);
195+
target.fill(outputs, new Permit[](0));
196196
}
197197

198198
function test_fill_both() public {
@@ -203,7 +203,7 @@ contract OrdersTest is Test {
203203
vm.expectEmit();
204204
emit Filled(outputs);
205205
vm.expectCall(token, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), recipient, amount));
206-
target.fill{value: amount * 2}(outputs);
206+
target.fill{value: amount * 2}(outputs, new Permit[](0));
207207

208208
// ETH is transferred to recipient
209209
assertEq(recipient.balance, amount * 2);
@@ -219,7 +219,7 @@ contract OrdersTest is Test {
219219
// expect Order event is initiated
220220
vm.expectEmit();
221221
emit Filled(outputs);
222-
target.fill{value: amount * 3}(outputs);
222+
target.fill{value: amount * 3}(outputs, new Permit[](0));
223223

224224
// ETH is transferred to recipient
225225
assertEq(recipient.balance, amount * 3);
@@ -233,6 +233,6 @@ contract OrdersTest is Test {
233233

234234
// total ETH outputs should be `amount` + 1; function should underflow only sending `amount`
235235
vm.expectRevert();
236-
target.fill{value: amount}(outputs);
236+
target.fill{value: amount}(outputs, new Permit[](0));
237237
}
238238
}

0 commit comments

Comments
 (0)