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 26 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
250286
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118092
111740
1 change: 1 addition & 0 deletions .forge-snapshots/SwapRouter02ExecutorPermitAndExecute.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
305777
55 changes: 55 additions & 0 deletions src/sample-executors/BaseExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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";
import {Multicall} from "./Multicall.sol";

struct PermitData {
address token;
bytes data;
}

abstract contract BaseExecutor is IReactorCallback, Multicall, Owned {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
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 execute(SignedOrder memory order, bytes memory callbackData) external payable {
Copy link
Collaborator

Choose a reason for hiding this comment

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

should these be onlyOwner?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

mmm i thought we wanted execute to be gated by onlyWhitelstedCaller?

reactor.executeWithCallback{value: msg.value}(order, callbackData);
}

function executeBatch(SignedOrder[] memory orders, bytes memory callbackData) external payable {
reactor.executeBatchWithCallback{value: msg.value}(orders, callbackData);
}

/// @notice execute a signed 2612-style permit
/// the transaction will revert if the permit cannot be executed
function permit(PermitData memory permitData) public {
(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) =
abi.decode(permitData.data, (address, address, uint256, uint256, uint8, bytes32, bytes32));
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
ERC20(permitData.token).permit(owner, spender, value, deadline, v, r, 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 permitData) external {
for (uint256 i = 0; i < permitData.length;) {
zhongeric marked this conversation as resolved.
Show resolved Hide resolved
permit(permitData[i]);
unchecked {
i++;
}
}
}

receive() external payable {}
}
25 changes: 25 additions & 0 deletions src/sample-executors/Multicall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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) {
// 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;
}
}
}
33 changes: 13 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,23 @@ 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 multicall(bytes[] calldata data) public payable override onlyWhitelistedCaller returns (bytes[] memory) {
return super.multicall(data);
}

/// @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 +86,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 +110,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