Merge branch 'main' into router/tnl/more-protocol-integration

This commit is contained in:
dianacarvalho1
2025-04-23 09:44:22 +01:00
committed by Diana Carvalho
20 changed files with 805 additions and 314 deletions

View File

@@ -4,84 +4,123 @@ const hre = require("hardhat");
// Comment out the executors you don't want to deploy
const executors_to_deploy = {
"ethereum":[
// USV2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
]},
// SUSHISWAP - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac",
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303"
]},
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x1097053Fd2ea711dad45caCcc45EfF7548fCB362",
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d"
]},
// USV3 -Args: Factory, Pool Init Code Hash
{exchange: "UniswapV3Executor", args: [
"0x1F98431c8aD98523631AE4a59f267346ea31F984",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]},
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash
{exchange: "UniswapV3Executor", args: [
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9",
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2"
]},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]},
{exchange: "BalancerV2Executor", args: []},
// Args: Ekubo core contract
{exchange: "EkuboExecutor", args: [
"0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444"
]}
],
"base":[
// Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
]},
// SUSHISWAP V2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x71524B4f93c58fcbF659783284E38825f0622859",
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303"
]},
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d"
]},
// USV3 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV3Executor", args: [
"0x33128a8fC17869897dcE68Ed026d694621f6FDfD",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]},
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash
{exchange: "UniswapV3Executor", args: [
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9",
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2"
]},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]},
{exchange: "BalancerV2Executor", args: []},
],
"unichain":[
// Args: Factory, Pool Init Code Hash
{exchange: "UniswapV2Executor", args: [
"0x1f98400000000000000000000000000000000002",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
]},
// USV3 - Args: Factory, Pool Init Code Hash
{exchange: "UniswapV3Executor", args: [
"0x1f98400000000000000000000000000000000003",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x1f98400000000000000000000000000000000004"]},
],
"ethereum": [
// USV2 - Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
30
]
},
// SUSHISWAP - Args: Factory, Pool Init Code Hash, Fee BPS, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac",
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303",
30
]
},
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x1097053Fd2ea711dad45caCcc45EfF7548fCB362",
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d",
25
]
},
// USV3 -Args: Factory, Pool Init Code Hash
{
exchange: "UniswapV3Executor", args: [
"0x1F98431c8aD98523631AE4a59f267346ea31F984",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]
},
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash
{
exchange: "UniswapV3Executor", args: [
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9",
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2"
]
},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]},
{exchange: "BalancerV2Executor", args: []},
// Args: Ekubo core contract
{
exchange: "EkuboExecutor", args: [
"0xe0e0e08A6A4b9Dc7bD67BCB7aadE5cF48157d444"
]
},
// Args: ETH address in curve pools
{
exchange: "CurveExecutor", args: [
"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
]
}
],
"base": [
// Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
30
]
},
// SUSHISWAP V2 - Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x71524B4f93c58fcbF659783284E38825f0622859",
"0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303",
30
]
},
// PANCAKESWAP V2 - Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x02a84c1b3BBD7401a5f7fa98a384EBC70bB5749E",
"0x57224589c67f3f30a6b0d7a1b54cf3153ab84563bc609ef41dfb34f8b2974d2d",
25
]
},
// USV3 - Args: Factory, Pool Init Code Hash
{
exchange: "UniswapV3Executor", args: [
"0x33128a8fC17869897dcE68Ed026d694621f6FDfD",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]
},
// PANCAKESWAP V3 - Args: Deployer, Pool Init Code Hash
{
exchange: "UniswapV3Executor", args: [
"0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9",
"0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2"
]
},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]},
{exchange: "BalancerV2Executor", args: []},
],
"unichain": [
// Args: Factory, Pool Init Code Hash, Fee BPS
{
exchange: "UniswapV2Executor", args: [
"0x1f98400000000000000000000000000000000002",
"0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f",
30
]
},
// USV3 - Args: Factory, Pool Init Code Hash
{
exchange: "UniswapV3Executor", args: [
"0x1f98400000000000000000000000000000000003",
"0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54"
]
},
// Args: Pool manager
{exchange: "UniswapV4Executor", args: ["0x1f98400000000000000000000000000000000004"]},
],
}
async function main() {

View File

@@ -48,7 +48,7 @@ contract BalancerV2Executor is IExecutor, TokenTransfer {
if (needsApproval) {
// slither-disable-next-line unused-return
tokenIn.approve(VAULT, type(uint256).max);
tokenIn.forceApprove(VAULT, type(uint256).max);
}
IVault.SingleSwap memory singleSwap = IVault.SingleSwap({

View File

@@ -81,7 +81,7 @@ contract CurveExecutor is IExecutor, TokenTransfer {
if (tokenApprovalNeeded && tokenIn != nativeToken) {
// slither-disable-next-line unused-return
IERC20(tokenIn).approve(address(pool), type(uint256).max);
IERC20(tokenIn).forceApprove(address(pool), type(uint256).max);
}
/// Inspired by Curve's router contract: https://github.com/curvefi/curve-router-ng/blob/9ab006ca848fc7f1995b6fbbecfecc1e0eb29e2a/contracts/Router.vy#L44

View File

@@ -10,6 +10,7 @@ error UniswapV2Executor__InvalidDataLength();
error UniswapV2Executor__InvalidTarget();
error UniswapV2Executor__InvalidFactory();
error UniswapV2Executor__InvalidInitCode();
error UniswapV2Executor__InvalidFee();
contract UniswapV2Executor is IExecutor, TokenTransfer {
using SafeERC20 for IERC20;
@@ -17,10 +18,14 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
address public immutable factory;
bytes32 public immutable initCode;
address private immutable self;
uint256 public immutable feeBps;
constructor(address _factory, bytes32 _initCode, address _permit2)
TokenTransfer(_permit2)
{
constructor(
address _factory,
bytes32 _initCode,
address _permit2,
uint256 _feeBps
) TokenTransfer(_permit2) {
if (_factory == address(0)) {
revert UniswapV2Executor__InvalidFactory();
}
@@ -29,6 +34,10 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
}
factory = _factory;
initCode = _initCode;
if (_feeBps > 30) {
revert UniswapV2Executor__InvalidFee();
}
feeBps = _feeBps;
self = address(this);
}
@@ -100,9 +109,9 @@ contract UniswapV2Executor is IExecutor, TokenTransfer {
}
require(reserveIn > 0 && reserveOut > 0, "L");
uint256 amountInWithFee = amountIn * 997;
uint256 amountInWithFee = amountIn * (10000 - feeBps);
uint256 numerator = amountInWithFee * uint256(reserveOut);
uint256 denominator = (uint256(reserveIn) * 1000) + amountInWithFee;
uint256 denominator = (uint256(reserveIn) * 10000) + amountInWithFee;
amount = numerator / denominator;
}

View File

@@ -129,13 +129,7 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer {
int256, /* amount1Delta */
bytes calldata /* data */
) external {
uint256 dataOffset = 4 + 32 + 32 + 32; // Skip selector + 2 ints + data_offset
uint256 dataLength =
uint256(bytes32(msg.data[dataOffset:dataOffset + 32]));
bytes calldata fullData = msg.data[4:dataOffset + 32 + dataLength];
handleCallback(fullData);
handleCallback(msg.data);
}
function _decodeData(bytes calldata data)

View File

@@ -367,4 +367,22 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
assertTrue(success, "Call Failed");
assertEq(balanceAfter - balanceBefore, 1475644707225677606);
}
function testSingleSwapIntegrationNoTransferIn() public {
// Tests swapping WETH -> DAI on a USV2 pool assuming that the tokens are already inside the router
deal(WETH_ADDR, tychoRouterAddr, 1 ether);
uint256 balanceBefore = IERC20(DAI_ADDR).balanceOf(ALICE);
vm.startPrank(ALICE);
// Encoded solution generated using `test_single_swap_strategy_encoder_no_transfer_in`
(bool success,) = tychoRouterAddr.call(
hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000"
);
vm.stopPrank();
uint256 balanceAfter = IERC20(DAI_ADDR).balanceOf(ALICE);
assertTrue(success, "Call Failed");
assertEq(balanceAfter - balanceBefore, 2659881924818443699787);
}
}

View File

@@ -99,7 +99,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper {
IPoolManager poolManager = IPoolManager(poolManagerAddress);
usv2Executor =
new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS);
new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS, 30);
usv3Executor =
new UniswapV3Executor(factoryV3, initCodeV3, PERMIT2_ADDRESS);
usv4Executor = new UniswapV4Executor(poolManager, PERMIT2_ADDRESS);

View File

@@ -254,15 +254,15 @@ contract CurveExecutorTest is Test, Constants {
function testStableSwapPool() public {
// Swapping CRVUSD -> USDT on a StableSwap pool, deployed by factory 0x4F8846Ae9380B90d2E71D5e3D042dff3E7ebb40d (plain pool)
uint256 amountIn = 1 ether;
deal(CRVUSD_ADDR, address(curveExecutorExposed), amountIn);
deal(USDT_ADDR, address(curveExecutorExposed), amountIn);
bytes memory data =
_getData(CRVUSD_ADDR, USDT_ADDR, CRVUSD_USDT_POOL, 1, ALICE);
_getData(USDT_ADDR, CRVUSD_ADDR, CRVUSD_USDT_POOL, 1, ALICE);
uint256 amountOut = curveExecutorExposed.swap(amountIn, data);
assertEq(amountOut, 999910);
assertEq(IERC20(USDT_ADDR).balanceOf(ALICE), amountOut);
assertEq(amountOut, 10436946786333182306400100);
assertEq(IERC20(CRVUSD_ADDR).balanceOf(ALICE), amountOut);
}
function testMetaPool() public {

View File

@@ -8,9 +8,12 @@ import {Constants} from "../Constants.sol";
import {Permit2TestHelper} from "../Permit2TestHelper.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor {
constructor(address _factory, bytes32 _initCode, address _permit2)
UniswapV2Executor(_factory, _initCode, _permit2)
{}
constructor(
address _factory,
bytes32 _initCode,
address _permit2,
uint256 _feeBps
) UniswapV2Executor(_factory, _initCode, _permit2, _feeBps) {}
function decodeParams(bytes calldata data)
external
@@ -63,17 +66,19 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper {
uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV2Exposed = new UniswapV2ExecutorExposed(
USV2_FACTORY_ETHEREUM, USV2_POOL_CODE_INIT_HASH, PERMIT2_ADDRESS
USV2_FACTORY_ETHEREUM, USV2_POOL_CODE_INIT_HASH, PERMIT2_ADDRESS, 30
);
sushiswapV2Exposed = new UniswapV2ExecutorExposed(
SUSHISWAPV2_FACTORY_ETHEREUM,
SUSHIV2_POOL_CODE_INIT_HASH,
PERMIT2_ADDRESS
PERMIT2_ADDRESS,
30
);
pancakeswapV2Exposed = new UniswapV2ExecutorExposed(
PANCAKESWAPV2_FACTORY_ETHEREUM,
PANCAKEV2_POOL_CODE_INIT_HASH,
PERMIT2_ADDRESS
PERMIT2_ADDRESS,
25
);
permit2 = IAllowanceTransfer(PERMIT2_ADDRESS);
}

View File

@@ -96,6 +96,29 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper {
);
}
function testSwapIntegration() public {
uint256 amountIn = 10 ** 18;
deal(WETH_ADDR, address(uniswapV3Exposed), amountIn);
uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI
bool zeroForOne = false;
bytes memory data = encodeUniswapV3Swap(
WETH_ADDR,
DAI_ADDR,
address(this),
DAI_WETH_USV3,
zeroForOne,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
);
uint256 amountOut = uniswapV3Exposed.swap(amountIn, data);
assertGe(amountOut, expAmountOut);
assertEq(IERC20(WETH_ADDR).balanceOf(address(uniswapV3Exposed)), 0);
assertGe(IERC20(DAI_ADDR).balanceOf(address(this)), expAmountOut);
}
function testDecodeParamsInvalidDataLength() public {
bytes memory invalidParams =
abi.encodePacked(WETH_ADDR, address(2), address(3));