diff --git a/.forge-snapshots/V4Router_Bytecode.snap b/.forge-snapshots/V4Router_Bytecode.snap index 0f15a960..9c58b982 100644 --- a/.forge-snapshots/V4Router_Bytecode.snap +++ b/.forge-snapshots/V4Router_Bytecode.snap @@ -1 +1 @@ -6865 \ No newline at end of file +7063 \ 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 200b076e..0c5ecb08 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeIn_sweepETH.snap @@ -1 +1 @@ -121378 \ No newline at end of file +121413 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap index eaba0d1d..da6da4ca 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap @@ -1 +1 @@ -116679 \ No newline at end of file +116714 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap index 82d6a826..f78f106c 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap @@ -1 +1 @@ -125494 \ No newline at end of file +125529 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap index b62c9b2c..4645416b 100644 --- a/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap +++ b/.forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap @@ -1 +1 @@ -129411 \ No newline at end of file +129446 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops.snap b/.forge-snapshots/V4Router_ExactOut2Hops.snap index c5327835..69edacf1 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops.snap @@ -1 +1 @@ -179239 \ No newline at end of file +179274 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap index edc46c28..93480400 100644 --- a/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap @@ -1 +1 @@ -175123 \ No newline at end of file +175158 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops.snap b/.forge-snapshots/V4Router_ExactOut3Hops.snap index dd6100a3..920f4b7a 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops.snap @@ -1 +1 @@ -229074 \ No newline at end of file +229109 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap index ecf0f6d4..cb98d71e 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap @@ -1 +1 @@ -224982 \ No newline at end of file +225017 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap index d3a34986..403f0404 100644 --- a/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap @@ -1 +1 @@ -220283 \ No newline at end of file +220318 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle.snap b/.forge-snapshots/V4Router_ExactOutputSingle.snap index 9fde061b..1c160394 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle.snap @@ -1 +1 @@ -128684 \ No newline at end of file +128716 \ 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 68c0e2f2..1601b618 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeIn_sweepETH.snap @@ -1 +1 @@ -120651 \ No newline at end of file +120683 \ No newline at end of file diff --git a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap index 587b0484..ff5267a5 100644 --- a/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap +++ b/.forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap @@ -1 +1 @@ -116027 \ No newline at end of file +116059 \ No newline at end of file diff --git a/src/V4Router.sol b/src/V4Router.sol index fb806eb4..b33d8d90 100644 --- a/src/V4Router.sol +++ b/src/V4Router.sol @@ -121,13 +121,18 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { } function _swapExactOutputSingle(IV4Router.ExactOutputSingleParams calldata params) private { + uint128 amountOut = params.amountOut; + if (amountOut == ActionConstants.OPEN_DELTA) { + amountOut = + _getFullDebt(params.zeroForOne ? params.poolKey.currency1 : params.poolKey.currency0).toUint128(); + } uint128 amountIn = ( uint256( -int256( _swap( params.poolKey, params.zeroForOne, - int256(uint256(params.amountOut)), + int256(uint256(amountOut)), params.sqrtPriceLimitX96, params.hookData ) @@ -146,6 +151,10 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver { Currency currencyOut = params.currencyOut; PathKey calldata pathKey; + if (amountOut == ActionConstants.OPEN_DELTA) { + amountOut = _getFullDebt(currencyOut).toUint128(); + } + for (uint256 i = pathLength; i > 0; i--) { pathKey = params.path[i - 1]; (PoolKey memory poolKey, bool oneForZero) = pathKey.getPoolAndSwapDirection(currencyOut); diff --git a/test/router/V4Router.t.sol b/test/router/V4Router.t.sol index ea2696bc..e32da052 100644 --- a/test/router/V4Router.t.sol +++ b/test/router/V4Router.t.sol @@ -534,6 +534,35 @@ contract V4RouterTest is RoutingTestHelpers { assertEq(outputBalanceAfter - outputBalanceBefore, amountOut); } + function test_swapExactOutputSingle_swapOpenDelta() public { + uint256 expectedAmountIn = 1008049273448486163; + + IV4Router.ExactOutputSingleParams memory params = IV4Router.ExactOutputSingleParams( + key0, true, ActionConstants.OPEN_DELTA, uint128(expectedAmountIn + 1), 0, bytes("") + ); + + plan = plan.add(Actions.TAKE, abi.encode(key0.currency1, ActionConstants.ADDRESS_THIS, 1 ether)); + plan = plan.add(Actions.SWAP_EXACT_OUT_SINGLE, abi.encode(params)); + plan = plan.add(Actions.SETTLE, abi.encode(key0.currency0, ActionConstants.OPEN_DELTA, true)); + + bytes memory data = plan.encode(); + + uint256 callerInputBefore = key0.currency0.balanceOfSelf(); + uint256 routerInputBefore = key0.currency1.balanceOfSelf(); + uint256 callerOutputBefore = key0.currency1.balanceOfSelf(); + + router.executeActions(data); + + uint256 callerInputAfter = key0.currency0.balanceOfSelf(); + uint256 routerInputAfter = key0.currency1.balanceOfSelf(); + uint256 callerOutputAfter = key0.currency1.balanceOfSelf(); + + // caller paid + assertEq(callerInputBefore - expectedAmountIn, callerInputAfter); + assertEq(routerInputBefore, routerInputAfter); + assertEq(callerOutputBefore, callerOutputAfter); + } + function test_swapExactOut_revertsForAmountIn() public { uint256 amountOut = 1 ether; uint256 expectedAmountIn = 1008049273448486163; @@ -641,6 +670,36 @@ contract V4RouterTest is RoutingTestHelpers { assertEq(outputBalanceAfter - outputBalanceBefore, amountOut); } + function test_swapExactOut_swapOpenDelta() public { + uint256 expectedAmountIn = 1008049273448486163; + + tokenPath.push(currency0); + tokenPath.push(currency1); + + IV4Router.ExactOutputParams memory params = _getExactOutputParams(tokenPath, ActionConstants.OPEN_DELTA); + + plan = plan.add(Actions.TAKE, abi.encode(key0.currency1, ActionConstants.ADDRESS_THIS, 1 ether)); + plan = plan.add(Actions.SWAP_EXACT_OUT, abi.encode(params)); + plan = plan.add(Actions.SETTLE, abi.encode(key0.currency0, ActionConstants.OPEN_DELTA, true)); + + bytes memory data = plan.encode(); + + uint256 callerInputBefore = key0.currency0.balanceOfSelf(); + uint256 routerInputBefore = key0.currency1.balanceOfSelf(); + uint256 callerOutputBefore = key0.currency1.balanceOfSelf(); + + router.executeActions(data); + + uint256 callerInputAfter = key0.currency0.balanceOfSelf(); + uint256 routerInputAfter = key0.currency1.balanceOfSelf(); + uint256 callerOutputAfter = key0.currency1.balanceOfSelf(); + + // caller paid + assertEq(callerInputBefore - expectedAmountIn, callerInputAfter); + assertEq(routerInputBefore, routerInputAfter); + assertEq(callerOutputBefore, callerOutputAfter); + } + /*////////////////////////////////////////////////////////////// ETH -> ERC20 and ERC20 -> ETH EXACT OUTPUT //////////////////////////////////////////////////////////////*/