diff --git a/.forge-snapshots/Payments_swap_settleFromCaller_takeAll.snap b/.forge-snapshots/Payments_swap_settleFromCaller_takeAll.snap index 359fe270..422a77b8 100644 --- a/.forge-snapshots/Payments_swap_settleFromCaller_takeAll.snap +++ b/.forge-snapshots/Payments_swap_settleFromCaller_takeAll.snap @@ -1 +1 @@ -134624 \ No newline at end of file +134171 \ No newline at end of file diff --git a/.forge-snapshots/Payments_swap_settleFromRouter_takeAll.snap b/.forge-snapshots/Payments_swap_settleFromRouter_takeAll.snap index 531b737f..dcc72d1c 100644 --- a/.forge-snapshots/Payments_swap_settleFromRouter_takeAll.snap +++ b/.forge-snapshots/Payments_swap_settleFromRouter_takeAll.snap @@ -1 +1 @@ -127419 \ No newline at end of file +126966 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_Bytecode.snap b/.forge-snapshots/V4Router_Bytecode.snap index 176adc06..c6e2c74d 100644 --- a/.forge-snapshots/V4Router_Bytecode.snap +++ b/.forge-snapshots/V4Router_Bytecode.snap @@ -1 +1 @@ -7256 \ No newline at end of file +6942 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap index 5e921c67..ec0b0f29 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap @@ -1 +1 @@ -120842 \ No newline at end of file +120501 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap index 08cdf419..75bad55d 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap @@ -1 +1 @@ -120037 \ No newline at end of file +119696 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap index 0b31226d..d83695c7 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap @@ -1 +1 @@ -128909 \ No newline at end of file +128568 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap index b0014c40..82eb5f4d 100644 --- a/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap @@ -1 +1 @@ -135739 \ No newline at end of file +135398 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops.snap b/.forge-snapshots/V4Router_ExactIn2Hops.snap index 9d667ef7..b02cff01 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops.snap @@ -1 +1 @@ -187091 \ No newline at end of file +186897 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap index 148dfc2b..6f17f2bb 100644 --- a/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap @@ -1 +1 @@ -179026 \ No newline at end of file +178832 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops.snap b/.forge-snapshots/V4Router_ExactIn3Hops.snap index 4b1f74c9..de05bf83 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops.snap @@ -1 +1 @@ -238470 \ No newline at end of file +238421 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap index 6f650a6c..5665d2e3 100644 --- a/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap @@ -1 +1 @@ -230429 \ No newline at end of file +230380 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle.snap b/.forge-snapshots/V4Router_ExactInputSingle.snap index 359fe270..422a77b8 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle.snap @@ -1 +1 @@ -134624 \ No newline at end of file +134171 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap index 91201572..146c27b9 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap @@ -1 +1 @@ -119727 \ No newline at end of file +119274 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap index 88e84454..52615b42 100644 --- a/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap @@ -1 +1 @@ -118900 \ No newline at end of file +118447 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap index 138766b9..57bde469 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap @@ -1 +1 @@ -126645 \ No newline at end of file +126298 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap index 3349c4ac..019035c6 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap @@ -1 +1 @@ -120878 \ No newline at end of file +120531 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap index a7c25aee..d40b084a 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap @@ -1 +1 @@ -129750 \ No newline at end of file +129403 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap index bf35d421..715f8dca 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap @@ -1 +1 @@ -134551 \ No newline at end of file +134204 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index bb356d28..abc303ec 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -186512 \ No newline at end of file +186306 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap index f272fb2c..4b39aaa9 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap @@ -1 +1 @@ -183407 \ No newline at end of file +183201 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index 1193ab2c..f111bdc0 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -238515 \ No newline at end of file +238448 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index 44b58748..d6cabe72 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -235434 \ No newline at end of file +235367 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index 3f438bdf..30c1022c 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -229667 \ No newline at end of file +229600 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle.snap b/.forge-snapshots/V4Router_ExactOutputSingle.snap index 22146d12..c97de2ba 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle.snap @@ -1 +1 @@ -133428 \ No newline at end of file +132975 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap index 314b0b06..5c208ff2 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap @@ -1 +1 @@ -125522 \ No newline at end of file +125069 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap index ffb42b7f..0465d75c 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap @@ -1 +1 @@ -119813 \ No newline at end of file +119360 \ No newline at end of file diff --git a/src/V4Router.sol b/src/V4Router.sol index 6cb03671..cc31c69c 100644 --- a/src/V4Router.sol +++ b/src/V4Router.sol @@ -31,13 +31,17 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { // swap actions and payment actions in different blocks for gas efficiency if (action < Actions.SETTLE) { if (action == Actions.SWAP_EXACT_IN) { - _swapExactInput(abi.decode(params, (IV4Router.ExactInputParams))); + IV4Router.ExactInputParams calldata swapParams = params.decodeSwapExactInParams(); + _swapExactInput(swapParams); } else if (action == Actions.SWAP_EXACT_IN_SINGLE) { - _swapExactInputSingle(abi.decode(params, (IV4Router.ExactInputSingleParams))); + IV4Router.ExactInputSingleParams calldata swapParams = params.decodeSwapExactInSingleParams(); + _swapExactInputSingle(swapParams); } else if (action == Actions.SWAP_EXACT_OUT) { - _swapExactOutput(abi.decode(params, (IV4Router.ExactOutputParams))); + IV4Router.ExactOutputParams calldata swapParams = params.decodeSwapExactOutParams(); + _swapExactOutput(swapParams); } else if (action == Actions.SWAP_EXACT_OUT_SINGLE) { - _swapExactOutputSingle(abi.decode(params, (IV4Router.ExactOutputSingleParams))); + IV4Router.ExactOutputSingleParams calldata swapParams = params.decodeSwapExactOutSingleParams(); + _swapExactOutputSingle(swapParams); } else { revert UnsupportedAction(action); } @@ -62,7 +66,7 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { } } - function _swapExactInputSingle(IV4Router.ExactInputSingleParams memory params) private { + function _swapExactInputSingle(IV4Router.ExactInputSingleParams calldata params) private { uint128 amountOut = _swap( params.poolKey, params.zeroForOne, @@ -73,14 +77,14 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { if (amountOut < params.amountOutMinimum) revert TooLittleReceived(); } - function _swapExactInput(IV4Router.ExactInputParams memory params) private { + function _swapExactInput(IV4Router.ExactInputParams calldata params) private { unchecked { // Caching for gas savings uint256 pathLength = params.path.length; uint128 amountOut; uint128 amountIn = params.amountIn; Currency currencyIn = params.currencyIn; - PathKey memory pathKey; + PathKey calldata pathKey; for (uint256 i = 0; i < pathLength; i++) { pathKey = params.path[i]; @@ -96,7 +100,7 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { } } - function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams memory params) private { + function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams calldata params) private { uint128 amountIn = ( -_swap( params.poolKey, @@ -109,14 +113,14 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { if (amountIn > params.amountInMaximum) revert TooMuchRequested(); } - function _swapExactOutput(IV4Router.ExactOutputParams memory params) private { + function _swapExactOutput(IV4Router.ExactOutputParams calldata params) private { unchecked { // Caching for gas savings uint256 pathLength = params.path.length; uint128 amountIn; uint128 amountOut = params.amountOut; Currency currencyOut = params.currencyOut; - PathKey memory pathKey; + PathKey calldata pathKey; for (uint256 i = pathLength; i > 0; i--) { pathKey = params.path[i - 1]; @@ -136,7 +140,7 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, - bytes memory hookData + bytes calldata hookData ) private returns (int128 reciprocalAmount) { unchecked { BalanceDelta delta = poolManager.swap( diff --git a/src/lens/Quoter.sol b/src/lens/Quoter.sol index dd1cc7d3..fcf63d0c 100644 --- a/src/lens/Quoter.sol +++ b/src/lens/Quoter.sol @@ -155,7 +155,7 @@ contract Quoter is IQuoter, SafeCallback { } /// @dev quote an ExactInput swap along a path of tokens, then revert with the result - function _quoteExactInput(QuoteExactParams memory params) public selfOnly returns (bytes memory) { + function _quoteExactInput(QuoteExactParams calldata params) public selfOnly returns (bytes memory) { uint256 pathLength = params.path.length; QuoteResult memory result = QuoteResult({ @@ -198,7 +198,7 @@ contract Quoter is IQuoter, SafeCallback { } /// @dev quote an ExactInput swap on a pool, then revert with the result - function _quoteExactInputSingle(QuoteExactSingleParams memory params) public selfOnly returns (bytes memory) { + function _quoteExactInputSingle(QuoteExactSingleParams calldata params) public selfOnly returns (bytes memory) { (, int24 tickBefore,,) = poolManager.getSlot0(params.poolKey.toId()); (BalanceDelta deltas, uint160 sqrtPriceX96After, int24 tickAfter) = _swap( @@ -223,7 +223,7 @@ contract Quoter is IQuoter, SafeCallback { } /// @dev quote an ExactOutput swap along a path of tokens, then revert with the result - function _quoteExactOutput(QuoteExactParams memory params) public selfOnly returns (bytes memory) { + function _quoteExactOutput(QuoteExactParams calldata params) public selfOnly returns (bytes memory) { uint256 pathLength = params.path.length; QuoteResult memory result = QuoteResult({ @@ -269,7 +269,7 @@ contract Quoter is IQuoter, SafeCallback { } /// @dev quote an ExactOutput swap on a pool, then revert with the result - function _quoteExactOutputSingle(QuoteExactSingleParams memory params) public selfOnly returns (bytes memory) { + function _quoteExactOutputSingle(QuoteExactSingleParams calldata params) public selfOnly returns (bytes memory) { // if no price limit has been specified, cache the output amount for comparison in the swap callback if (params.sqrtPriceLimitX96 == 0) amountOutCached = params.exactAmount; @@ -303,7 +303,7 @@ contract Quoter is IQuoter, SafeCallback { bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, - bytes memory hookData + bytes calldata hookData ) private returns (BalanceDelta deltas, uint160 sqrtPriceX96After, int24 tickAfter) { deltas = poolManager.swap( poolKey, diff --git a/src/libraries/CalldataDecoder.sol b/src/libraries/CalldataDecoder.sol index 11ba49d4..b2ea3c38 100644 --- a/src/libraries/CalldataDecoder.sol +++ b/src/libraries/CalldataDecoder.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import {PositionConfig} from "./PositionConfig.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {IV4Router} from "../interfaces/IV4Router.sol"; /// @title Library for abi decoding in calldata library CalldataDecoder { @@ -110,6 +111,54 @@ library CalldataDecoder { hookData = params.toBytes(10); } + /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputParams)) + function decodeSwapExactInParams(bytes calldata params) + internal + pure + returns (IV4Router.ExactInputParams calldata swapParams) + { + // ExactInputParams is a variable length struct so we just have to look up its location + assembly ("memory-safe") { + swapParams := add(params.offset, calldataload(params.offset)) + } + } + + /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputSingleParams)) + function decodeSwapExactInSingleParams(bytes calldata params) + internal + pure + returns (IV4Router.ExactInputSingleParams calldata swapParams) + { + // ExactInputSingleParams is a variable length struct so we just have to look up its location + assembly ("memory-safe") { + swapParams := add(params.offset, calldataload(params.offset)) + } + } + + /// @dev equivalent to: abi.decode(params, (IV4Router.ExactOutputParams)) + function decodeSwapExactOutParams(bytes calldata params) + internal + pure + returns (IV4Router.ExactOutputParams calldata swapParams) + { + // ExactOutputParams is a variable length struct so we just have to look up its location + assembly ("memory-safe") { + swapParams := add(params.offset, calldataload(params.offset)) + } + } + + /// @dev equivalent to: abi.decode(params, (IV4Router.ExactInputSingleParams)) + function decodeSwapExactOutSingleParams(bytes calldata params) + internal + pure + returns (IV4Router.ExactOutputSingleParams calldata swapParams) + { + // ExactOutputSingleParams is a variable length struct so we just have to look up its location + assembly ("memory-safe") { + swapParams := add(params.offset, calldataload(params.offset)) + } + } + /// @dev equivalent to: abi.decode(params, (Currency)) in calldata function decodeCurrency(bytes calldata params) internal pure returns (Currency currency) { assembly ("memory-safe") { diff --git a/src/libraries/PathKey.sol b/src/libraries/PathKey.sol index 38884eb4..a286076d 100644 --- a/src/libraries/PathKey.sol +++ b/src/libraries/PathKey.sol @@ -14,7 +14,7 @@ struct PathKey { } library PathKeyLib { - function getPoolAndSwapDirection(PathKey memory params, Currency currencyIn) + function getPoolAndSwapDirection(PathKey calldata params, Currency currencyIn) internal pure returns (PoolKey memory poolKey, bool zeroForOne) diff --git a/test/libraries/CalldataDecoder.t.sol b/test/libraries/CalldataDecoder.t.sol index bfc9c08e..25f8593d 100644 --- a/test/libraries/CalldataDecoder.t.sol +++ b/test/libraries/CalldataDecoder.t.sol @@ -2,9 +2,13 @@ pragma solidity ^0.8.24; import "forge-std/Test.sol"; +import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; + import {MockCalldataDecoder} from "../mocks/MockCalldataDecoder.sol"; import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; -import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {IV4Router} from "../../src/interfaces/IV4Router.sol"; +import {PathKey} from "../../src/libraries/PathKey.sol"; contract CalldataDecoderTest is Test { MockCalldataDecoder decoder; @@ -83,6 +87,56 @@ contract CalldataDecoderTest is Test { _assertEq(_config, config); } + function test_fuzz_decodeSwapExactInParams(IV4Router.ExactInputParams calldata _swapParams) public view { + bytes memory params = abi.encode(_swapParams); + IV4Router.ExactInputParams memory swapParams = decoder.decodeSwapExactInParams(params); + + assertEq(Currency.unwrap(swapParams.currencyIn), Currency.unwrap(_swapParams.currencyIn)); + assertEq(swapParams.amountIn, _swapParams.amountIn); + assertEq(swapParams.amountOutMinimum, _swapParams.amountOutMinimum); + _assertEq(swapParams.path, _swapParams.path); + } + + function test_fuzz_decodeSwapExactInSingleParams(IV4Router.ExactInputSingleParams calldata _swapParams) + public + view + { + bytes memory params = abi.encode(_swapParams); + IV4Router.ExactInputSingleParams memory swapParams = decoder.decodeSwapExactInSingleParams(params); + + assertEq(swapParams.zeroForOne, _swapParams.zeroForOne); + assertEq(swapParams.amountIn, _swapParams.amountIn); + assertEq(swapParams.amountOutMinimum, _swapParams.amountOutMinimum); + assertEq(swapParams.sqrtPriceLimitX96, _swapParams.sqrtPriceLimitX96); + assertEq(swapParams.hookData, _swapParams.hookData); + _assertEq(swapParams.poolKey, _swapParams.poolKey); + } + + function test_fuzz_decodeSwapExactOutParams(IV4Router.ExactOutputParams calldata _swapParams) public view { + bytes memory params = abi.encode(_swapParams); + IV4Router.ExactOutputParams memory swapParams = decoder.decodeSwapExactOutParams(params); + + assertEq(Currency.unwrap(swapParams.currencyOut), Currency.unwrap(_swapParams.currencyOut)); + assertEq(swapParams.amountOut, _swapParams.amountOut); + assertEq(swapParams.amountInMaximum, _swapParams.amountInMaximum); + _assertEq(swapParams.path, _swapParams.path); + } + + function test_fuzz_decodeSwapExactOutSingleParams(IV4Router.ExactOutputSingleParams calldata _swapParams) + public + view + { + bytes memory params = abi.encode(_swapParams); + IV4Router.ExactOutputSingleParams memory swapParams = decoder.decodeSwapExactOutSingleParams(params); + + assertEq(swapParams.zeroForOne, _swapParams.zeroForOne); + assertEq(swapParams.amountOut, _swapParams.amountOut); + assertEq(swapParams.amountInMaximum, _swapParams.amountInMaximum); + assertEq(swapParams.sqrtPriceLimitX96, _swapParams.sqrtPriceLimitX96); + assertEq(swapParams.hookData, _swapParams.hookData); + _assertEq(swapParams.poolKey, _swapParams.poolKey); + } + function test_fuzz_decodeCurrencyAndAddress(Currency _currency, address __address) public view { bytes memory params = abi.encode(_currency, __address); (Currency currency, address _address) = decoder.decodeCurrencyAndAddress(params); @@ -106,13 +160,28 @@ contract CalldataDecoderTest is Test { assertEq(amount, _amount); } + function _assertEq(PathKey[] memory path1, PathKey[] memory path2) internal pure { + assertEq(path1.length, path2.length); + for (uint256 i = 0; i < path1.length; i++) { + assertEq(Currency.unwrap(path1[i].intermediateCurrency), Currency.unwrap(path2[i].intermediateCurrency)); + assertEq(path1[i].fee, path2[i].fee); + assertEq(path1[i].tickSpacing, path2[i].tickSpacing); + assertEq(address(path1[i].hooks), address(path2[i].hooks)); + assertEq(path1[i].hookData, path2[i].hookData); + } + } + function _assertEq(PositionConfig memory config1, PositionConfig memory config2) internal pure { - assertEq(Currency.unwrap(config1.poolKey.currency0), Currency.unwrap(config2.poolKey.currency0)); - assertEq(Currency.unwrap(config1.poolKey.currency1), Currency.unwrap(config2.poolKey.currency1)); - assertEq(config1.poolKey.fee, config2.poolKey.fee); - assertEq(config1.poolKey.tickSpacing, config2.poolKey.tickSpacing); - assertEq(address(config1.poolKey.hooks), address(config2.poolKey.hooks)); + _assertEq(config1.poolKey, config2.poolKey); assertEq(config1.tickLower, config2.tickLower); assertEq(config1.tickUpper, config2.tickUpper); } + + function _assertEq(PoolKey memory key1, PoolKey memory key2) internal pure { + assertEq(Currency.unwrap(key1.currency0), Currency.unwrap(key2.currency0)); + assertEq(Currency.unwrap(key1.currency1), Currency.unwrap(key2.currency1)); + assertEq(key1.fee, key2.fee); + assertEq(key1.tickSpacing, key2.tickSpacing); + assertEq(address(key1.hooks), address(key2.hooks)); + } } diff --git a/test/mocks/MockCalldataDecoder.sol b/test/mocks/MockCalldataDecoder.sol index b60bd707..2839e6a5 100644 --- a/test/mocks/MockCalldataDecoder.sol +++ b/test/mocks/MockCalldataDecoder.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.24; import {PositionConfig} from "../../src/libraries/PositionConfig.sol"; import {CalldataDecoder} from "../../src/libraries/CalldataDecoder.sol"; +import {IV4Router} from "../../src/interfaces/IV4Router.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; // we need to use a mock contract to make the calls happen in calldata not memory @@ -38,6 +39,38 @@ contract MockCalldataDecoder { return params.decodeBurnParams(); } + function decodeSwapExactInParams(bytes calldata params) + external + pure + returns (IV4Router.ExactInputParams calldata swapParams) + { + return params.decodeSwapExactInParams(); + } + + function decodeSwapExactInSingleParams(bytes calldata params) + external + pure + returns (IV4Router.ExactInputSingleParams calldata swapParams) + { + return params.decodeSwapExactInSingleParams(); + } + + function decodeSwapExactOutParams(bytes calldata params) + external + pure + returns (IV4Router.ExactOutputParams calldata swapParams) + { + return params.decodeSwapExactOutParams(); + } + + function decodeSwapExactOutSingleParams(bytes calldata params) + external + pure + returns (IV4Router.ExactOutputSingleParams calldata swapParams) + { + return params.decodeSwapExactOutSingleParams(); + } + function decodeMintParams(bytes calldata params) external pure