From 54a48e5897889d9018ee3cefc5dd87cd7da02426 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Tue, 5 Sep 2023 17:11:10 -0400 Subject: [PATCH] break out structs into interface --- contracts/{Routing.sol => V4Router.sol} | 63 +-------------- contracts/interfaces/IV4Router.sol | 76 ++++++++++++++++++ test/{Routing.t.sol => V4Router.t.sol} | 79 ++++++++++--------- ...ntation.sol => V4RouterImplementation.sol} | 6 +- 4 files changed, 121 insertions(+), 103 deletions(-) rename contracts/{Routing.sol => V4Router.sol} (83%) create mode 100644 contracts/interfaces/IV4Router.sol rename test/{Routing.t.sol => V4Router.t.sol} (80%) rename test/shared/implementation/{RoutingImplementation.sol => V4RouterImplementation.sol} (75%) diff --git a/contracts/Routing.sol b/contracts/V4Router.sol similarity index 83% rename from contracts/Routing.sol rename to contracts/V4Router.sol index 45050509..cafadde1 100644 --- a/contracts/Routing.sol +++ b/contracts/V4Router.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import "forge-std/console.sol"; import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; import {BalanceDelta} from "@uniswap/v4-core/contracts/types/BalanceDelta.sol"; @@ -9,73 +8,15 @@ import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/contracts/types/Currency.sol"; import {TickMath} from "@uniswap/v4-core/contracts/libraries/TickMath.sol"; import {IHooks} from "@uniswap/v4-core/contracts/interfaces/IHooks.sol"; +import {IV4Router} from "./interfaces/IV4Router.sol"; /// @title UniswapV4Routing /// @notice Abstract contract that contains all internal logic needed for routing through Uniswap V4 pools -abstract contract Routing { +abstract contract V4Router is IV4Router { using CurrencyLibrary for Currency; IPoolManager immutable poolManager; - error NotPoolManager(); - error InvalidSwapType(); - error TooLittleReceived(); - error TooMuchRequested(); - - struct SwapInfo { - SwapType swapType; - address msgSender; - bytes params; - } - - struct PathKey { - Currency tradeCurrency; - uint24 fee; - int24 tickSpacing; - IHooks hooks; - } - - struct ExactInputSingleParams { - PoolKey poolKey; - bool zeroForOne; - address recipient; - uint128 amountIn; - uint128 amountOutMinimum; - uint160 sqrtPriceLimitX96; - } - - struct ExactInputParams { - Currency currencyIn; - PathKey[] path; - address recipient; - uint128 amountIn; - uint128 amountOutMinimum; - } - - struct ExactOutputSingleParams { - PoolKey poolKey; - bool zeroForOne; - address recipient; - uint128 amountOut; - uint128 amountInMaximum; - uint160 sqrtPriceLimitX96; - } - - struct ExactOutputParams { - Currency currencyOut; - PathKey[] path; - address recipient; - uint128 amountOut; - uint128 amountInMaximum; - } - - enum SwapType { - ExactInput, - ExactInputSingle, - ExactOutput, - ExactOutputSingle - } - /// @dev Only the pool manager may call this function modifier poolManagerOnly() { if (msg.sender != address(poolManager)) revert NotPoolManager(); diff --git a/contracts/interfaces/IV4Router.sol b/contracts/interfaces/IV4Router.sol new file mode 100644 index 00000000..186d92ca --- /dev/null +++ b/contracts/interfaces/IV4Router.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/console.sol"; +import {Hooks} from "@uniswap/v4-core/contracts/libraries/Hooks.sol"; +import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; +import {BalanceDelta} from "@uniswap/v4-core/contracts/types/BalanceDelta.sol"; +import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/contracts/types/Currency.sol"; +import {TickMath} from "@uniswap/v4-core/contracts/libraries/TickMath.sol"; +import {IHooks} from "@uniswap/v4-core/contracts/interfaces/IHooks.sol"; + +/// @title UniswapV4Routing +/// @notice Abstract contract that contains all internal logic needed for routing through Uniswap V4 pools +interface IV4Router { + error NotPoolManager(); + error InvalidSwapType(); + error TooLittleReceived(); + error TooMuchRequested(); + + struct SwapInfo { + SwapType swapType; + address msgSender; + bytes params; + } + + struct PathKey { + Currency tradeCurrency; + uint24 fee; + int24 tickSpacing; + IHooks hooks; + } + + struct ExactInputSingleParams { + PoolKey poolKey; + bool zeroForOne; + address recipient; + uint128 amountIn; + uint128 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + struct ExactInputParams { + Currency currencyIn; + PathKey[] path; + address recipient; + uint128 amountIn; + uint128 amountOutMinimum; + } + + struct ExactOutputSingleParams { + PoolKey poolKey; + bool zeroForOne; + address recipient; + uint128 amountOut; + uint128 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + struct ExactOutputParams { + Currency currencyOut; + PathKey[] path; + address recipient; + uint128 amountOut; + uint128 amountInMaximum; + } + + enum SwapType { + ExactInput, + ExactInputSingle, + ExactOutput, + ExactOutputSingle + } + + function lockAcquired(bytes calldata encodedSwapInfo) external returns (bytes memory); +} diff --git a/test/Routing.t.sol b/test/V4Router.t.sol similarity index 80% rename from test/Routing.t.sol rename to test/V4Router.t.sol index 739b2680..bb8eba5d 100644 --- a/test/Routing.t.sol +++ b/test/V4Router.t.sol @@ -9,18 +9,19 @@ import {MockERC20} from "@uniswap/v4-core/test/foundry-tests/utils/MockERC20.sol import {PoolModifyPositionTest} from "@uniswap/v4-core/contracts/test/PoolModifyPositionTest.sol"; import {PoolManager} from "@uniswap/v4-core/contracts/PoolManager.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; -import {Routing} from "../contracts/Routing.sol"; -import {RoutingImplementation} from "./shared/implementation/RoutingImplementation.sol"; +import {V4Router} from "../contracts/V4Router.sol"; +import {IV4Router} from "../contracts/interfaces/IV4Router.sol"; +import {V4RouterImplementation} from "./shared/implementation/V4RouterImplementation.sol"; import {PoolKey} from "@uniswap/v4-core/contracts/types/PoolKey.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/contracts/types/Currency.sol"; import {IHooks} from "@uniswap/v4-core/contracts/interfaces/IHooks.sol"; -contract RoutingTest is Test, Deployers, GasSnapshot { +contract V4RouterTest is Test, Deployers, GasSnapshot { using CurrencyLibrary for Currency; PoolManager manager; PoolModifyPositionTest positionManager; - RoutingImplementation router; + V4RouterImplementation router; MockERC20 token0; MockERC20 token1; @@ -35,7 +36,7 @@ contract RoutingTest is Test, Deployers, GasSnapshot { function setUp() public { manager = new PoolManager(500000); - router = new RoutingImplementation(manager); + router = new V4RouterImplementation(manager); positionManager = new PoolModifyPositionTest(manager); token0 = new MockERC20("Test0", "0", 18, 2 ** 128); @@ -65,14 +66,14 @@ contract RoutingTest is Test, Deployers, GasSnapshot { uint256 amountIn = 1 ether; uint256 expectedAmountOut = 992054607780215625; - Routing.ExactInputSingleParams memory params = - Routing.ExactInputSingleParams(key0, true, address(this), uint128(amountIn), 0, 0); + IV4Router.ExactInputSingleParams memory params = + IV4Router.ExactInputSingleParams(key0, true, address(this), uint128(amountIn), 0, 0); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); snapStart("RouterExactInputSingle"); - router.swap(Routing.SwapType.ExactInputSingle, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInputSingle, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -86,13 +87,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { uint256 amountIn = 1 ether; uint256 expectedAmountOut = 992054607780215625; - Routing.ExactInputSingleParams memory params = - Routing.ExactInputSingleParams(key0, false, address(this), uint128(amountIn), 0, 0); + IV4Router.ExactInputSingleParams memory params = + IV4Router.ExactInputSingleParams(key0, false, address(this), uint128(amountIn), 0, 0); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); - router.swap(Routing.SwapType.ExactInputSingle, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInputSingle, abi.encode(params)); uint256 newBalance0 = token0.balanceOf(address(this)); uint256 newBalance1 = token1.balanceOf(address(this)); @@ -107,13 +108,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token0); tokenPath.push(token1); - Routing.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); + IV4Router.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); snapStart("RouterExactIn1Hop"); - router.swap(Routing.SwapType.ExactInput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -129,11 +130,11 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token1); tokenPath.push(token0); - Routing.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); + IV4Router.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); - router.swap(Routing.SwapType.ExactInput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInput, abi.encode(params)); uint256 newBalance0 = token0.balanceOf(address(this)); uint256 newBalance1 = token1.balanceOf(address(this)); @@ -149,14 +150,14 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token0); tokenPath.push(token1); tokenPath.push(token2); - Routing.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); + IV4Router.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); uint256 prevBalance2 = token2.balanceOf(address(this)); snapStart("RouterExactIn2Hops"); - router.swap(Routing.SwapType.ExactInput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -179,13 +180,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token1); tokenPath.push(token2); tokenPath.push(token3); - Routing.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); + IV4Router.ExactInputParams memory params = getExactInputParams(tokenPath, amountIn); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance3 = token3.balanceOf(address(this)); snapStart("RouterExactIn3Hops"); - router.swap(Routing.SwapType.ExactInput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactInput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -203,14 +204,14 @@ contract RoutingTest is Test, Deployers, GasSnapshot { uint256 amountOut = 1 ether; uint256 expectedAmountIn = 1008049273448486163; - Routing.ExactOutputSingleParams memory params = - Routing.ExactOutputSingleParams(key0, true, address(this), uint128(amountOut), 0, 0); + IV4Router.ExactOutputSingleParams memory params = + IV4Router.ExactOutputSingleParams(key0, true, address(this), uint128(amountOut), 0, 0); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); snapStart("RouterExactOutputSingle"); - router.swap(Routing.SwapType.ExactOutputSingle, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutputSingle, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -224,13 +225,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { uint256 amountOut = 1 ether; uint256 expectedAmountIn = 1008049273448486163; - Routing.ExactOutputSingleParams memory params = - Routing.ExactOutputSingleParams(key0, false, address(this), uint128(amountOut), 0, 0); + IV4Router.ExactOutputSingleParams memory params = + IV4Router.ExactOutputSingleParams(key0, false, address(this), uint128(amountOut), 0, 0); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); - router.swap(Routing.SwapType.ExactOutputSingle, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutputSingle, abi.encode(params)); uint256 newBalance0 = token0.balanceOf(address(this)); uint256 newBalance1 = token1.balanceOf(address(this)); @@ -245,13 +246,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token0); tokenPath.push(token1); - Routing.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); + IV4Router.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); snapStart("RouterExactOut1Hop"); - router.swap(Routing.SwapType.ExactOutput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -267,13 +268,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token1); tokenPath.push(token0); - Routing.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); + IV4Router.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); snapStart("RouterExactOut1Hop"); - router.swap(Routing.SwapType.ExactOutput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -290,14 +291,14 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token0); tokenPath.push(token1); tokenPath.push(token2); - Routing.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); + IV4Router.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance1 = token1.balanceOf(address(this)); uint256 prevBalance2 = token2.balanceOf(address(this)); snapStart("RouterExactOut2Hops"); - router.swap(Routing.SwapType.ExactOutput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -320,13 +321,13 @@ contract RoutingTest is Test, Deployers, GasSnapshot { tokenPath.push(token1); tokenPath.push(token2); tokenPath.push(token3); - Routing.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); + IV4Router.ExactOutputParams memory params = getExactOutputParams(tokenPath, amountOut); uint256 prevBalance0 = token0.balanceOf(address(this)); uint256 prevBalance3 = token3.balanceOf(address(this)); snapStart("RouterExactOut3Hops"); - router.swap(Routing.SwapType.ExactOutput, abi.encode(params)); + router.swap(IV4Router.SwapType.ExactOutput, abi.encode(params)); snapEnd(); uint256 newBalance0 = token0.balanceOf(address(this)); @@ -359,11 +360,11 @@ contract RoutingTest is Test, Deployers, GasSnapshot { function getExactInputParams(MockERC20[] memory _tokenPath, uint256 amountIn) internal view - returns (Routing.ExactInputParams memory params) + returns (IV4Router.ExactInputParams memory params) { - Routing.PathKey[] memory path = new Routing.PathKey[](_tokenPath.length - 1); + IV4Router.PathKey[] memory path = new IV4Router.PathKey[](_tokenPath.length - 1); for (uint256 i = 0; i < _tokenPath.length - 1; i++) { - path[i] = Routing.PathKey(Currency.wrap(address(_tokenPath[i + 1])), 3000, 60, IHooks(address(0))); + path[i] = IV4Router.PathKey(Currency.wrap(address(_tokenPath[i + 1])), 3000, 60, IHooks(address(0))); } params.currencyIn = Currency.wrap(address(_tokenPath[0])); @@ -376,11 +377,11 @@ contract RoutingTest is Test, Deployers, GasSnapshot { function getExactOutputParams(MockERC20[] memory _tokenPath, uint256 amountOut) internal view - returns (Routing.ExactOutputParams memory params) + returns (IV4Router.ExactOutputParams memory params) { - Routing.PathKey[] memory path = new Routing.PathKey[](_tokenPath.length - 1); + IV4Router.PathKey[] memory path = new IV4Router.PathKey[](_tokenPath.length - 1); for (uint256 i = _tokenPath.length - 1; i > 0; i--) { - path[i - 1] = Routing.PathKey(Currency.wrap(address(_tokenPath[i - 1])), 3000, 60, IHooks(address(0))); + path[i - 1] = IV4Router.PathKey(Currency.wrap(address(_tokenPath[i - 1])), 3000, 60, IHooks(address(0))); } params.currencyOut = Currency.wrap(address(_tokenPath[_tokenPath.length - 1])); diff --git a/test/shared/implementation/RoutingImplementation.sol b/test/shared/implementation/V4RouterImplementation.sol similarity index 75% rename from test/shared/implementation/RoutingImplementation.sol rename to test/shared/implementation/V4RouterImplementation.sol index f705db31..fb43b822 100644 --- a/test/shared/implementation/RoutingImplementation.sol +++ b/test/shared/implementation/V4RouterImplementation.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.19; -import {Routing} from "../../../contracts/Routing.sol"; +import {V4Router} from "../../../contracts/V4Router.sol"; import {IPoolManager} from "@uniswap/v4-core/contracts/interfaces/IPoolManager.sol"; import {IERC20Minimal} from "@uniswap/v4-core/contracts/interfaces/external/IERC20Minimal.sol"; -contract RoutingImplementation is Routing { - constructor(IPoolManager _poolManager) Routing(_poolManager) {} +contract V4RouterImplementation is V4Router { + constructor(IPoolManager _poolManager) V4Router(_poolManager) {} function swap(SwapType swapType, bytes memory params) external { v4Swap(swapType, params);