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 20 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
2 changes: 1 addition & 1 deletion .forge-snapshots/SwapRouter02ExecutorExecute.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
262973
248358
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118092
109807
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions .forge-snapshots/SwapRouter02ExecutorPermitAndExecute.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
302552
110 changes: 110 additions & 0 deletions src/sample-executors/BaseExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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 {ERC20} from "solmate/src/tokens/ERC20.sol";

import {ResolvedOrder, SignedOrder} from "../base/ReactorStructs.sol";
import {IReactorCallback} from "../interfaces/IReactorCallback.sol";
import {IReactor} from "../interfaces/IReactor.sol";

library Commands {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
bytes1 internal constant COMMAND_TYPE_MASK = 0x0f;

uint256 constant EXECUTE = 0x00;
uint256 constant EXECUTE_BATCH = 0x01;
uint256 constant PERMIT = 0x02;
uint256 constant PERMIT_BATCH = 0x03;
// 0x04 - 0x0f are reserved for future use
}

struct PermitData {
address token;
bytes data;
}

abstract contract BaseExecutor is IReactorCallback, Owned {
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
error InvalidCommandType(uint256 commandType);

IReactor public immutable reactor;

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

function reactorCallback(ResolvedOrder[] calldata, bytes calldata callbackData) external virtual;
zhongeric marked this conversation as resolved.
Show resolved Hide resolved

function _restrictCall() internal virtual {}
zhongeric marked this conversation as resolved.
Show resolved Hide resolved

function dispatch(bytes calldata commands, bytes[] calldata inputs) external payable {
_restrictCall();
uint256 numCommands = commands.length;
if (inputs.length != numCommands) revert LengthMismatch();

// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex = 0; commandIndex < numCommands;) {
uint256 command = uint8(commands[commandIndex] & Commands.COMMAND_TYPE_MASK);
bytes calldata input = inputs[commandIndex];

if (command == Commands.EXECUTE) {
SignedOrder memory order;
bytes memory callbackData;

(order.order, order.sig, callbackData) = abi.decode(input, (bytes, bytes, bytes));
_execute(order, callbackData);
} else if (command == Commands.EXECUTE_BATCH) {
(bytes[] memory orderInputs, bytes memory callbackData) = abi.decode(input, (bytes[], bytes));
SignedOrder[] memory orders = new SignedOrder[](orderInputs.length);
for (uint256 i = 0; i < orderInputs.length; i++) {
SignedOrder memory order;
(order.order, order.sig) = abi.decode(orderInputs[i], (bytes, bytes));
orders[i] = order;
}

_executeBatch(orders, callbackData);
} else if (command == Commands.PERMIT) {
PermitData memory permit = abi.decode(input, (PermitData));
_permit(permit);
} else if (command == Commands.PERMIT_BATCH) {
PermitData[] memory permits = abi.decode(input, (PermitData[]));
_permitBatch(permits);
} else {
revert InvalidCommandType(command);
}
unchecked {
commandIndex++;
}
}
}

function _execute(SignedOrder memory order, bytes memory callbackData) internal {
reactor.executeWithCallback(order, callbackData);
}

function _executeBatch(SignedOrder[] memory orders, bytes memory callbackData) internal {
reactor.executeBatchWithCallback(orders, callbackData);
}

/// @notice execute a signed 2612-style permit
/// the transaction will revert if the permit cannot be executed
/// must be called before the call to the reactor
function _permit(PermitData memory permit) internal {
(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) =
abi.decode(permit.data, (address, address, uint256, uint256, uint8, bytes32, bytes32));
ERC20(permit.token).permit(owner, spender, value, deadline, v, r, s);
}

function _permitBatch(PermitData[] memory permits) internal {
for (uint256 i = 0; i < permits.length;) {
_permit(permits[i]);
unchecked {
i++;
}
}
}

receive() external payable {}
}
28 changes: 8 additions & 20 deletions src/sample-executors/SwapRouter02Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ 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 {BaseExecutor} from "./BaseExecutor.sol";

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

Expand All @@ -21,10 +22,9 @@ contract SwapRouter02Executor is IReactorCallback, Owned {
/// @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 @@ -41,30 +41,21 @@ contract SwapRouter02Executor is IReactorCallback, Owned {
}

constructor(address _whitelistedCaller, IReactor _reactor, address _owner, ISwapRouter02 _swapRouter02)
Owned(_owner)
BaseExecutor(_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 {
reactor.executeWithCallback(order, callbackData);
}

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

/// @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 @@ -114,7 +105,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