Files
tycho-execution/foundry/test/executors/UniswapV2Executor.t.sol
Diana Carvalho 50429ad05c feat: Add swap method with tests
Changes:
- If the tokenIn is ETH, skip permit2 approval
- Make executors payable: When using delegatecall the executor inherits the execution context of whoever calls it. Our main swap function can accept ETH, it needs to be payable so by consequence the executors also need to be.
- Set uniswap v2 executor in test router
- Add tests for all possible cases of swap
- Add tests for all cases of splitSwap
- Add test functions to handle permit2 and encode swaps

--- don't change below this line ---
ENG-4041 Took 3 hours 50 minutes

Took 49 seconds


Took 14 seconds
2025-01-28 16:59:29 +00:00

97 lines
3.0 KiB
Solidity

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import "@src/executors/UniswapV2Executor.sol";
import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor {
function decodeParams(bytes calldata data)
external
pure
returns (
IERC20 inToken,
address target,
address receiver,
bool zeroForOne
)
{
return _decodeData(data);
}
function getAmountOut(address target, uint256 amountIn, bool zeroForOne)
external
view
returns (uint256 amount)
{
return _getAmountOut(target, amountIn, zeroForOne);
}
}
contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants {
using SafeERC20 for IERC20;
UniswapV2ExecutorExposed uniswapV2Exposed;
IERC20 WETH = IERC20(WETH_ADDR);
IERC20 DAI = IERC20(DAI_ADDR);
function setUp() public {
uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV2Exposed = new UniswapV2ExecutorExposed();
}
function testDecodeParams() public view {
bytes memory params =
abi.encodePacked(WETH_ADDR, address(2), address(3), false);
(IERC20 tokenIn, address target, address receiver, bool zeroForOne) =
uniswapV2Exposed.decodeParams(params);
assertEq(address(tokenIn), WETH_ADDR);
assertEq(target, address(2));
assertEq(receiver, address(3));
assertEq(zeroForOne, false);
}
function testDecodeParamsInvalidDataLength() public {
bytes memory invalidParams =
abi.encodePacked(WETH_ADDR, address(2), address(3));
vm.expectRevert(UniswapV2Executor__InvalidDataLength.selector);
uniswapV2Exposed.decodeParams(invalidParams);
}
function testAmountOut() public view {
uint256 amountOut =
uniswapV2Exposed.getAmountOut(WETH_DAI_POOL, 10 ** 18, false);
uint256 expAmountOut = 1847751195973566072891;
assertEq(amountOut, expAmountOut);
}
// triggers a uint112 overflow on purpose
function testAmountOutInt112Overflow() public view {
address target = 0x0B9f5cEf1EE41f8CCCaA8c3b4c922Ab406c980CC;
uint256 amountIn = 83638098812630667483959471576;
uint256 amountOut =
uniswapV2Exposed.getAmountOut(target, amountIn, true);
assertGe(amountOut, 0);
}
function testSwap() public {
uint256 amountIn = 10 ** 18;
uint256 amountOut = 1847751195973566072891;
bool zeroForOne = false;
bytes memory protocolData =
abi.encodePacked(WETH_ADDR, WETH_DAI_POOL, BOB, zeroForOne);
deal(WETH_ADDR, address(uniswapV2Exposed), amountIn);
uniswapV2Exposed.swap(amountIn, protocolData);
uint256 finalBalance = DAI.balanceOf(BOB);
assertGe(finalBalance, amountOut);
}
}