Skip to content
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

refactor: add BaseExecutor with built in permit #202

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6b72474
Add basic swaprouter02executor with permit
zhongeric Aug 17, 2023
45fa560
Clean up tests
zhongeric Aug 17, 2023
0708ef9
Fix up test
zhongeric Aug 17, 2023
deea3af
Change executorWithPermit to inherit old executor
zhongeric Aug 17, 2023
ea07183
fix comments
zhongeric Aug 18, 2023
4d7cb05
Add sanity assertion
zhongeric Sep 8, 2023
962dcc7
Add base executor
zhongeric Sep 12, 2023
6328274
SwapRouter02Executor inherits from baseExecutor
zhongeric Sep 12, 2023
d0a7ff0
fmt
zhongeric Sep 12, 2023
6429b25
Fix tests, update gas snaps
zhongeric Sep 14, 2023
d8b0ce4
Add back integration test
zhongeric Sep 14, 2023
ab81231
Add permit and execute
zhongeric Sep 14, 2023
2d182a1
Add permit tests
zhongeric Sep 14, 2023
2231d74
Merge branch 'main' into add-base-executor
zhongeric Sep 14, 2023
68f1fcb
Take PermitData struct internally in baseExecutor
zhongeric Sep 14, 2023
a509343
unchecked ++
zhongeric Sep 14, 2023
122ed24
remove unused snapshot
zhongeric Sep 14, 2023
fa80cda
rm unused file
zhongeric Sep 14, 2023
7cee8ef
remove universal router
zhongeric Sep 14, 2023
7df8e44
forge fmt#
zhongeric Sep 14, 2023
ef4ea2f
Switch to multicall style
zhongeric Sep 19, 2023
af93887
forge fmt
zhongeric Sep 19, 2023
8e9caaf
Remove comment
zhongeric Sep 19, 2023
ad9622d
add comment
zhongeric Sep 19, 2023
00703ba
remove dep
zhongeric Sep 19, 2023
643398a
Remove unused erorrs
zhongeric Sep 19, 2023
0cb79d7
override execute funcs and add onlyWhiteListed modifier
zhongeric Sep 25, 2023
c33b4e4
comments
zhongeric Sep 25, 2023
2dcef7c
forge fmt
zhongeric Sep 25, 2023
6fc26bd
remove msg.value
zhongeric Sep 25, 2023
ebe31f9
Save state, add dai special casing
zhongeric Sep 27, 2023
d3526ef
import permit2Lib from permit2
zhongeric Sep 28, 2023
acacd7e
Use permit2 from Permit2Lib
zhongeric Sep 28, 2023
f7d3df6
forge fmt
zhongeric Sep 28, 2023
b1e5f03
move .length out
zhongeric Sep 28, 2023
8c6d149
remove unused state vars
zhongeric Oct 4, 2023
e67d845
Add integration test for DAI permit
zhongeric Oct 4, 2023
114333c
add comment
zhongeric Oct 4, 2023
093964a
Move onlyReactor to BaseExecutor
zhongeric Oct 5, 2023
7e5f863
Rename to BaseExecutorWithPermit
zhongeric Oct 5, 2023
858125d
Add extra BaseExecutor
zhongeric Oct 5, 2023
882f9e5
Add comemnts
zhongeric Oct 5, 2023
fa2d1b1
Bubble up custom errors in multicall
zhongeric Oct 5, 2023
40220e2
remove console2
zhongeric Oct 5, 2023
eadd405
Add test for multicall ownable
zhongeric Oct 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
197283
197273
Original file line number Diff line number Diff line change
@@ -1 +1 @@
207049
207039
Original file line number Diff line number Diff line change
@@ -1 +1 @@
260720
260710
Original file line number Diff line number Diff line change
@@ -1 +1 @@
190845
190835
2 changes: 1 addition & 1 deletion .forge-snapshots/SwapRouter02ExecutorExecute.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
262973
250261
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118092
111715
1 change: 1 addition & 0 deletions .forge-snapshots/SwapRouter02ExecutorPermitAndExecute.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
305647
1 change: 1 addition & 0 deletions src/interfaces/IReactorCallback.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface IReactorCallback {
/// @notice Called by the reactor during the execution of an order
/// @param resolvedOrders Has inputs and outputs
/// @param callbackData The callbackData specified for an order execution
/// @dev Must only be called by the reactor
/// @dev Must have approved each token and amount in outputs to the msg.sender
function reactorCallback(ResolvedOrder[] memory resolvedOrders, bytes memory callbackData) external;
}
43 changes: 43 additions & 0 deletions src/sample-executors/BaseExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Owned} from "solmate/src/auth/Owned.sol";
import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";
import {ResolvedOrder, SignedOrder} from "../base/ReactorStructs.sol";
import {IReactorCallback} from "../interfaces/IReactorCallback.sol";
import {IReactor} from "../interfaces/IReactor.sol";
import {Multicall} from "./Multicall.sol";

abstract contract BaseExecutor is IReactorCallback, Multicall, Owned {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
IReactor public immutable reactor;

/// @notice thrown if reactorCallback is called by an address other than the reactor
error MsgSenderNotReactor();
error NotImplemented();

constructor(IReactor _reactor, address _owner) Owned(_owner) {
reactor = _reactor;
}

modifier onlyReactor() {
if (msg.sender != address(reactor)) {
revert MsgSenderNotReactor();
}
_;
}

/// @inheritdoc IReactorCallback
/// @dev any overriding function MUST use the onlyReactor modifier
function reactorCallback(ResolvedOrder[] calldata, bytes calldata) external virtual onlyReactor {}
Comment on lines +29 to +31
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since solidity doesn't require an overriding function to use the same modifier, I'm tempted to have this base contract not inherit the IReactor interface entirely and leave that up to integrations


/// @notice execute a signed order
/// @dev consider restricting who can call this function
function execute(SignedOrder memory order, bytes memory callbackData) public payable virtual;

/// @notice execute a batch of signed orders
/// @dev consider restricting who can call this function
function executeBatch(SignedOrder[] memory orders, bytes memory callbackData) public payable virtual;

/// @notice required to receive native outputs
receive() external payable {}
}
40 changes: 40 additions & 0 deletions src/sample-executors/BaseExecutorWithPermit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ERC20} from "solmate/src/tokens/ERC20.sol";
import {IReactor} from "../interfaces/IReactor.sol";
import {BaseExecutor} from "./BaseExecutor.sol";
import {Permit2Lib} from "permit2/src/libraries/Permit2Lib.sol";

struct PermitData {
ERC20 token;
address owner;
address spender;
uint256 amount;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}

abstract contract BaseExecutorWithPermit is BaseExecutor {
constructor(IReactor _reactor, address _owner) BaseExecutor(_reactor, _owner) {}

/// @notice execute a signed ERC2612 permit
/// the transaction will revert if the permit cannot be executed
function permit(PermitData memory data) public {
Permit2Lib.permit2(data.token, data.owner, data.spender, data.amount, data.deadline, data.v, data.r, data.s);
}

/// @notice execute a batch of signed 2612-style permits
/// the transaction will revert if any of the permits cannot be executed
function permitBatch(PermitData[] memory data) external {
uint256 length = data.length;
for (uint256 i = 0; i < length;) {
permit(data[i]);
unchecked {
i++;
}
}
}
}
31 changes: 31 additions & 0 deletions src/sample-executors/Multicall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.16;

/// @title Multicall
/// @notice Code from https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/Multicall.sol
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall {
rileydcampbell marked this conversation as resolved.
Show resolved Hide resolved
function multicall(bytes[] calldata data) public payable virtual returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);

if (!success) {
// handle custom errors
if (result.length == 4) {
assembly {
revert(add(result, 0x20), mload(result))
}
}
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}

results[i] = result;
}
}
}
49 changes: 24 additions & 25 deletions src/sample-executors/SwapRouter02Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ import {IReactor} from "../interfaces/IReactor.sol";
import {CurrencyLibrary} from "../lib/CurrencyLibrary.sol";
import {ResolvedOrder, OutputToken, SignedOrder} from "../base/ReactorStructs.sol";
import {ISwapRouter02} from "../external/ISwapRouter02.sol";
import {BaseExecutorWithPermit} from "./BaseExecutorWithPermit.sol";

/// @notice A fill contract that uses SwapRouter02 to execute trades
contract SwapRouter02Executor is IReactorCallback, Owned {
contract SwapRouter02Executor is BaseExecutorWithPermit {
using SafeTransferLib for ERC20;
using CurrencyLibrary for address;

/// @notice thrown if reactorCallback is called with a non-whitelisted filler
error CallerNotWhitelisted();
/// @notice thrown if reactorCallback is called by an address other than the reactor
error MsgSenderNotReactor();

ISwapRouter02 private immutable swapRouter02;
address private immutable whitelistedCaller;
IReactor private immutable reactor;
WETH private immutable weth;
ISwapRouter02 public immutable swapRouter02;
address public immutable whitelistedCaller;
WETH public immutable weth;

modifier onlyWhitelistedCaller() {
if (msg.sender != whitelistedCaller) {
Expand All @@ -33,38 +31,39 @@ contract SwapRouter02Executor is IReactorCallback, Owned {
_;
}

modifier onlyReactor() {
if (msg.sender != address(reactor)) {
revert MsgSenderNotReactor();
}
_;
}

constructor(address _whitelistedCaller, IReactor _reactor, address _owner, ISwapRouter02 _swapRouter02)
Owned(_owner)
BaseExecutorWithPermit(_reactor, _owner)
{
whitelistedCaller = _whitelistedCaller;
reactor = _reactor;
swapRouter02 = _swapRouter02;
weth = WETH(payable(_swapRouter02.WETH9()));
}

/// @notice assume that we already have all output tokens
function execute(SignedOrder calldata order, bytes calldata callbackData) external onlyWhitelistedCaller {
function execute(SignedOrder memory order, bytes memory callbackData)
public
payable
override
onlyWhitelistedCaller
{
reactor.executeWithCallback(order, callbackData);
}

/// @notice assume that we already have all output tokens
function executeBatch(SignedOrder[] calldata orders, bytes calldata callbackData) external onlyWhitelistedCaller {
function executeBatch(SignedOrder[] memory orders, bytes memory callbackData)
public
payable
override
onlyWhitelistedCaller
{
reactor.executeBatchWithCallback(orders, callbackData);
}

/// @inheritdoc IReactorCallback
/// @notice fill UniswapX orders using SwapRouter02
/// @param callbackData It has the below encoded:
/// address[] memory tokensToApproveForSwapRouter02: Max approve these tokens to swapRouter02
/// address[] memory tokensToApproveForReactor: Max approve these tokens to reactor
/// bytes[] memory multicallData: Pass into swapRouter02.multicall()
function reactorCallback(ResolvedOrder[] calldata, bytes calldata callbackData) external onlyReactor {
function reactorCallback(ResolvedOrder[] calldata, bytes calldata callbackData) external override onlyReactor {
(
address[] memory tokensToApproveForSwapRouter02,
address[] memory tokensToApproveForReactor,
Expand Down Expand Up @@ -93,7 +92,10 @@ contract SwapRouter02Executor is IReactorCallback, Owned {
/// @notice This function can be used to convert ERC20s to ETH that remains in this contract
/// @param tokensToApprove Max approve these tokens to swapRouter02
/// @param multicallData Pass into swapRouter02.multicall()
function multicall(ERC20[] calldata tokensToApprove, bytes[] calldata multicallData) external onlyOwner {
function swapRouter02Multicall(ERC20[] calldata tokensToApprove, bytes[] calldata multicallData)
external
onlyOwner
{
for (uint256 i = 0; i < tokensToApprove.length; i++) {
tokensToApprove[i].safeApprove(address(swapRouter02), type(uint256).max);
}
Expand All @@ -114,7 +116,4 @@ contract SwapRouter02Executor is IReactorCallback, Owned {
function withdrawETH(address recipient) external onlyOwner {
SafeTransferLib.safeTransferETH(recipient, address(this).balance);
}

/// @notice Necessary for this contract to receive ETH when calling unwrapWETH()
receive() external payable {}
}
Loading