From 7822c4f9132b6d64a1281f6e54a8515cb0d242d3 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 28 Jan 2025 19:34:58 -0500 Subject: [PATCH] feat: USV3 verification --- .gitmodules | 3 ++ foundry/lib/v3-periphery | 1 + foundry/src/TychoRouter.sol | 42 ++++++++++++++++++++++++++- foundry/test/TychoRouterTestSetup.sol | 7 +++-- 4 files changed, 50 insertions(+), 3 deletions(-) create mode 160000 foundry/lib/v3-periphery diff --git a/.gitmodules b/.gitmodules index b573165..5d16ef5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "foundry/lib/v2-core"] path = foundry/lib/v2-core url = https://github.com/uniswap/v2-core +[submodule "foundry/lib/v3-periphery"] + path = foundry/lib/v3-periphery + url = https://github.com/Uniswap/v3-periphery diff --git a/foundry/lib/v3-periphery b/foundry/lib/v3-periphery new file mode 160000 index 0000000..80f26c8 --- /dev/null +++ b/foundry/lib/v3-periphery @@ -0,0 +1 @@ +Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058 diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index bac2f47..3ae63b0 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -59,10 +59,13 @@ contract TychoRouter is ); event FeeSet(uint256 indexed oldFee, uint256 indexed newFee); - constructor(address _permit2, address weth) { + address private immutable _usv3Factory; + + constructor(address _permit2, address weth, address usv3Factory) { permit2 = IAllowanceTransfer(_permit2); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _weth = IWETH(weth); + _usv3Factory = usv3Factory; } /** @@ -340,4 +343,41 @@ contract TychoRouter is * @dev Allows this contract to receive native token */ receive() external payable {} + + /** + * @dev Called by UniswapV3 pool when swapping on it. + * See in IUniswapV3SwapCallback for documentation. + */ + function uniswapV3SwapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata msgData + ) external { + (uint256 amountOwed, address tokenOwed) = + _verifyUSV3Callback(amount0Delta, amount1Delta, msgData); + IERC20(tokenOwed).safeTransfer(msg.sender, amountOwed); + } + + function _verifyUSV3Callback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) + internal + pure + returns (uint256 amountOwed, address tokenOwed) + { + address tokenIn = address(bytes20(data[0:20])); + address tokenOut = address(bytes20(data[20:40])); + uint24 fee = uint24(bytes3(data[40:43])); + + CallbackValidationV2.verifyCallback( + _usv3Factory, tokenIn, tokenOut, fee + ); + + amountOwed = + amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); + + return (amountOwed, tokenOwed); + } } diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 91edbf1..53b74b9 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -8,7 +8,9 @@ import "@src/TychoRouter.sol"; import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; contract TychoRouterExposed is TychoRouter { - constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {} + constructor(address _permit2, address weth, address usv3Factory) + TychoRouter(_permit2, weth, usv3Factory) + {} function wrapETH(uint256 amount) external payable { return _wrapETH(amount); @@ -39,7 +41,8 @@ contract TychoRouterTestSetup is Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.startPrank(ADMIN); - tychoRouter = new TychoRouterExposed(permit2Address, WETH_ADDR); + tychoRouter = + new TychoRouterExposed(permit2Address, WETH_ADDR, address(0)); tychoRouterAddr = address(tychoRouter); tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER);