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

1inch routers #2

Merged
merged 48 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6c661fc
Initial work
alexkeating Apr 19, 2023
13c05ff
forge install: solmate
alexkeating Apr 19, 2023
4ad7fda
forge install: openzeppelin-contracts
alexkeating Apr 20, 2023
ec6677a
Building V5 router
alexkeating Apr 25, 2023
9c71bc4
Working test
alexkeating Apr 26, 2023
ff3a2bb
A little cleaner V5 router
alexkeating Apr 28, 2023
e64fe76
Add V4 refactor
alexkeating Apr 28, 2023
950f1ff
V4 with tests
alexkeating Apr 30, 2023
580f0ba
Deploy script written
alexkeating Apr 30, 2023
8c5f8dc
Add Benchmarks
alexkeating Apr 30, 2023
4c02859
Add broadcast files
alexkeating Apr 30, 2023
ff4a407
Cleanup
alexkeating May 1, 2023
c401035
Change coverage
alexkeating May 1, 2023
a4decbe
Fix build
alexkeating May 1, 2023
daf6cc9
Fix format
alexkeating May 1, 2023
5829f05
Fix typo
alexkeating May 4, 2023
66148dd
Change flags to uint256
alexkeating May 5, 2023
fb7f235
Change approval to max uint
alexkeating May 5, 2023
936d174
Make sure comments are sentences and fix typos
alexkeating May 5, 2023
cefdbcc
Make RouterTypes singular
alexkeating May 5, 2023
14bd097
Remove source receiver arg
alexkeating May 8, 2023
4a6dd7f
Add tests for router factory deploy
alexkeating May 8, 2023
cc515ef
Add compute address tests
alexkeating May 8, 2023
cf68810
Reorganize v5 tests
alexkeating May 8, 2023
56796ca
Rename router factory revert
alexkeating May 8, 2023
3e873fb
Reorganize v4 tests
alexkeating May 8, 2023
1ea7fcf
Make sure tests are recognized by scoplint spec
alexkeating May 8, 2023
eec3e61
Add V4Router constructor test
alexkeating May 8, 2023
46920bc
Fix test names
alexkeating May 8, 2023
3605258
Update aggregationBaseRouter comments
alexkeating May 8, 2023
02ac944
Change max approval to be in constructor
alexkeating May 8, 2023
ca4b78d
Change constructor to do max approval with passing tests
alexkeating May 8, 2023
0bb07ea
Cleanup
alexkeating May 8, 2023
ab331f5
Fix Benchmark call
alexkeating May 8, 2023
ad68cbf
Add a couple more tests
alexkeating May 8, 2023
d19dabc
Make expectRevert stricter and add comment
alexkeating May 10, 2023
a770e85
Make expectEmit more concise
alexkeating May 10, 2023
2c6205d
Rename RouterFactory correctly deploys test
alexkeating May 10, 2023
ad6f6bd
Change name of swappingAddress and add comment
alexkeating May 10, 2023
f93620f
Add enpoints used to get calldata
alexkeating May 10, 2023
97cfd91
Replace asserTrue with assertEq
alexkeating May 10, 2023
ef0b88d
Change compute address tests
alexkeating May 11, 2023
dfeaadf
Update V5 variable name
alexkeating May 11, 2023
16c2959
Remove scripts and JSON
alexkeating May 12, 2023
53055f9
Change solidity version in create 2
alexkeating May 21, 2023
d561bbe
Change routerAddr to be set in the setup
alexkeating May 21, 2023
f9f8900
Add USDC check
alexkeating May 22, 2023
bf0a132
Check USDC balances have not changed
alexkeating May 22, 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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:

env:
FOUNDRY_PROFILE: ci
OPTIMISM_RPC_URL: ${{ secrets.OPTIMISM_RPC_URL }}

jobs:
build:
Expand Down Expand Up @@ -75,7 +76,7 @@ jobs:
uses: zgosalvez/github-actions-report-lcov@v2
with:
coverage-files: ./lcov.info
minimum-coverage: 100 # Set coverage threshold.
minimum-coverage: 80 # Set coverage threshold.

lint:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/solmate"]
path = lib/solmate
url = https://github.com/transmissions11/solmate
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com//OpenZeppelin/openzeppelin-contracts
7 changes: 7 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[profile.default]
fs_permissions = [{ access = "read", path = "./broadcast" }]
optimizer = true
optimizer_runs = 10_000_000
solc_version = "0.8.16"
Expand All @@ -24,3 +25,9 @@
single_line_statement_blocks = "single"
tab_width = 2
wrap_comments = true

# ===============================
# ======== RPC Endpoints ========
# ===============================
[rpc_endpoints]
optimism = "${OPTIMISM_RPC_URL}"
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 0a25c1
1 change: 1 addition & 0 deletions lib/solmate
Submodule solmate added at 2001af
16 changes: 0 additions & 16 deletions script/Deploy.s.sol

This file was deleted.

63 changes: 63 additions & 0 deletions src/AggregationBaseRouter.sol
mds1 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IV5AggregationExecutor} from "src/interfaces/IV5AggregationExecutor.sol";
import {IV5AggregationRouter} from "src/interfaces/IV5AggregationRouter.sol";
import {IV4AggregationExecutor} from "src/interfaces/IV4AggregationExecutor.sol";
import {IV4AggregationRouter} from "src/interfaces/IV4AggregationRouter.sol";

/// @notice An abstract class with the necessary class variables
/// to make a 1inch v5 aggregation router optimized.
abstract contract AggregationV5BaseRouter {
/// @notice The contract used to execute the swap along an optimized path.
IV5AggregationExecutor public immutable AGGREGATION_EXECUTOR;

/// @notice The 1inch v5 aggregation router contract.
IV5AggregationRouter public immutable AGGREGATION_ROUTER;

/// @notice The input token being swapped.
address public immutable TOKEN;

/// @notice Where the tokens are transferred in the 1inch v5 aggregation router.
/// It will match the AGGREGATION_EXECUTOR address.
address public immutable SOURCE_RECEIVER;

constructor(
IV5AggregationExecutor aggregationExecutor,
IV5AggregationRouter aggregationRouter,
address token
) {
AGGREGATION_EXECUTOR = aggregationExecutor;
AGGREGATION_ROUTER = aggregationRouter;
TOKEN = token;
SOURCE_RECEIVER = address(aggregationExecutor);
}
}

/// @notice An abstract class with the necessary class variables
/// to make a 1inch v4 aggregation router optimized.
abstract contract AggregationV4BaseRouter {
/// @notice The contract used to execute the swap along an optimized path.
IV4AggregationExecutor public immutable AGGREGATION_EXECUTOR;

/// @notice The 1inch v4 aggregation router contract.
IV4AggregationRouter public immutable AGGREGATION_ROUTER;

/// @notice The input token being swapped.
address public immutable TOKEN;

/// @notice Where the tokens are transferred in the 1inch v4 aggregation router.
/// It will match the AGGREGATION_EXECUTOR address.
address public immutable SOURCE_RECEIVER;

constructor(
IV4AggregationExecutor aggregationExecutor,
IV4AggregationRouter aggregationRouter,
address token
) {
AGGREGATION_EXECUTOR = aggregationExecutor;
AGGREGATION_ROUTER = aggregationRouter;
TOKEN = token;
SOURCE_RECEIVER = address(aggregationExecutor);
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

109 changes: 109 additions & 0 deletions src/RouterFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IV5AggregationExecutor} from "src/interfaces/IV5AggregationExecutor.sol";
import {IV5AggregationRouter} from "src/interfaces/IV5AggregationRouter.sol";
import {IV4AggregationExecutor} from "src/interfaces/IV4AggregationExecutor.sol";
import {IV4AggregationRouter} from "src/interfaces/IV4AggregationRouter.sol";
import {Create2} from "src/lib/Create2.sol";
import {V5Router} from "src/V5Router.sol";
import {V4Router} from "src/V4Router.sol";

/// @notice A factory for deploying an optimized router for a given asset and router type.
contract RouterFactory {
error RouterTypeDoesNotExist();

enum RouterType {
V4AggregationRouter,
V5AggregationRouter
}

/// @notice The 1inch v5 contract used to execute the swap along an optimized token swapping path.
IV5AggregationExecutor public immutable V5_AGGREGATION_EXECUTOR;

/// @notice The 1inch v5 aggregation router contract.
IV5AggregationRouter public immutable V5_AGGREGATION_ROUTER;

/// @notice The 1inch v4 aggregation router contract used to execute the swap along an optimized
/// token swapping path.
IV4AggregationExecutor public immutable V4_AGGREGATION_EXECUTOR;

/// @notice The 1inch v4 aggregation router contract.
IV4AggregationRouter public immutable V4_AGGREGATION_ROUTER;

event RouterDeployed(RouterType type_, address indexed asset);

constructor(
IV5AggregationExecutor v5AggregationExecutor,
IV5AggregationRouter v5AggregationRouter,
IV4AggregationExecutor v4AggregationExecutor,
IV4AggregationRouter v4AggregationRouter
) {
V5_AGGREGATION_EXECUTOR = v5AggregationExecutor;
V5_AGGREGATION_ROUTER = v5AggregationRouter;
V4_AGGREGATION_EXECUTOR = v4AggregationExecutor;
V4_AGGREGATION_ROUTER = v4AggregationRouter;
}

function deploy(RouterType type_, address asset) external returns (address) {
bytes32 salt = _salt(asset);
address router;
if (type_ == RouterType.V5AggregationRouter) {
router = address(
new V5Router{salt: salt}(
V5_AGGREGATION_ROUTER,
V5_AGGREGATION_EXECUTOR,
asset
)
);
} else if (type_ == RouterType.V4AggregationRouter) {
router = address(
new V4Router{salt: salt}(
V4_AGGREGATION_ROUTER,
V4_AGGREGATION_EXECUTOR,
asset
)
);
} else {
revert RouterTypeDoesNotExist();
mds1 marked this conversation as resolved.
Show resolved Hide resolved
}
emit RouterDeployed(type_, asset);
return router;
}

function computeAddress(RouterType type_, address asset) external view returns (address) {
if (type_ == RouterType.V4AggregationRouter) {
return _computeV4AggregationRouterAddress(asset);
} else if (type_ == RouterType.V5AggregationRouter) {
return _computeV5AggregationRouterAddress(asset);
} else {
// In practice this branch will never be hit, and is
// meant to catch situations in development when a
// new RouterType has been added and it is not yet
// supported in this function.
revert RouterTypeDoesNotExist();
}
}

function _computeV4AggregationRouterAddress(address asset) internal view returns (address) {
return Create2.computeCreate2Address(
_salt(asset),
address(this),
type(V4Router).creationCode,
abi.encode(V4_AGGREGATION_ROUTER, V4_AGGREGATION_EXECUTOR, asset)
);
}

function _computeV5AggregationRouterAddress(address asset) internal view returns (address) {
return Create2.computeCreate2Address(
_salt(asset),
address(this),
type(V5Router).creationCode,
abi.encode(V5_AGGREGATION_ROUTER, V5_AGGREGATION_EXECUTOR, asset)
);
}

function _salt(address asset) internal pure returns (bytes32) {
return bytes32(uint256(uint160(asset)));
}
}
43 changes: 43 additions & 0 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

import {IV4AggregationExecutor} from "src/interfaces/IV4AggregationExecutor.sol";
import {IV4AggregationRouter} from "src/interfaces/IV4AggregationRouter.sol";
import {AggregationV4BaseRouter} from "src/AggregationBaseRouter.sol";

/// @notice An optimized router to swap tokens using 1inch's v4 aggregation router.
contract V4Router is AggregationV4BaseRouter {
constructor(
IV4AggregationRouter aggregationRouter,
IV4AggregationExecutor aggregationExecutor,
address token
) AggregationV4BaseRouter(aggregationExecutor, aggregationRouter, token) {
IERC20(token).approve(address(aggregationRouter), type(uint256).max);
}

// TODO: Update to handle receiving ETH
receive() external payable {}

// Flags match specific constant masks. There is no documentation on these.
fallback() external payable {
(address dstToken, uint256 amount, uint256 minReturnAmount, bytes memory data, uint256 flags) =
abi.decode(msg.data, (address, uint256, uint256, bytes, uint256));
IERC20(TOKEN).transferFrom(msg.sender, address(this), amount);
AGGREGATION_ROUTER.swap(
AGGREGATION_EXECUTOR,
IV4AggregationRouter.SwapDescription({
srcToken: IERC20(TOKEN),
dstToken: IERC20(dstToken),
srcReceiver: payable(SOURCE_RECEIVER),
dstReceiver: payable(msg.sender),
amount: amount,
minReturnAmount: minReturnAmount,
flags: flags,
permit: ""
}),
data
);
}
}
43 changes: 43 additions & 0 deletions src/V5Router.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

import {IV5AggregationExecutor} from "src/interfaces/IV5AggregationExecutor.sol";
import {IV5AggregationRouter} from "src/interfaces/IV5AggregationRouter.sol";
import {AggregationV5BaseRouter} from "src/AggregationBaseRouter.sol";

/// @notice A router to swap tokens using 1inch's v5 aggregation router.
contract V5Router is AggregationV5BaseRouter {
constructor(
IV5AggregationRouter aggregationRouter,
IV5AggregationExecutor aggregationExecutor,
address token
) AggregationV5BaseRouter(aggregationExecutor, aggregationRouter, token) {
IERC20(token).approve(address(aggregationRouter), type(uint256).max);
}

// TODO: Update to handle receiving ETH
receive() external payable {}

// Flags match specific constant masks. There is no documentation on these.
fallback() external payable {
(address dstToken, uint256 amount, uint256 minReturnAmount, bytes memory data, uint256 flags) =
abi.decode(msg.data, (address, uint256, uint256, bytes, uint256));
IERC20(TOKEN).transferFrom(msg.sender, address(this), amount);
AGGREGATION_ROUTER.swap(
AGGREGATION_EXECUTOR,
IV5AggregationRouter.SwapDescription({
srcToken: IERC20(TOKEN),
dstToken: IERC20(dstToken),
srcReceiver: payable(SOURCE_RECEIVER),
dstReceiver: payable(msg.sender),
amount: amount,
minReturnAmount: minReturnAmount,
flags: flags
}),
"",
data
);
}
}
10 changes: 10 additions & 0 deletions src/interfaces/IV4AggregationExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
// permalink:
// https://optimistic.etherscan.io/address/0x1111111254760f7ab3f16433eea9304126dcd199#code#L990
pragma solidity >=0.8.0;

/// @title Interface for making arbitrary calls during swap
interface IV4AggregationExecutor {
/// @notice Make calls on `msgSender` with specified data
function callBytes(address msgSender, bytes calldata data) external payable; // 0x2636f7f8
}
22 changes: 22 additions & 0 deletions src/interfaces/IV4AggregationRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
pragma solidity >=0.8.0;

import {IV4AggregationExecutor} from "src/interfaces/IV4AggregationExecutor.sol";
import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

interface IV4AggregationRouter {
struct SwapDescription {
IERC20 srcToken;
IERC20 dstToken;
address payable srcReceiver;
address payable dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
bytes permit;
}

function swap(IV4AggregationExecutor executor, SwapDescription calldata desc, bytes calldata data)
external
payable
returns (uint256 returnAmount, uint256 spentAmount, uint256 gasLeft);
}
7 changes: 7 additions & 0 deletions src/interfaces/IV5AggregationExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity >=0.8.0;

/// @title Interface for making arbitrary calls during swap
interface IV5AggregationExecutor {
/// @notice propagates information about original msg.sender and executes arbitrary data
function execute(address msgSender) external payable; // 0x4b64e492
}
Loading