diff --git a/src/Pool.sol b/src/Pool.sol index 15f1afe..81a20d2 100644 --- a/src/Pool.sol +++ b/src/Pool.sol @@ -87,7 +87,7 @@ contract Pool is IPool, Ownable { mapping(bytes32 => mapping(uint256 => Swap[])) public triggerAndMarketOrderBook; mapping(bytes32 => mapping(uint256 => Swap[])) public limitOrderBook; - mapping(bytes32 => uint256) public override highestPriceMarker; + mapping(bytes32 => uint256) public override highestPriceKey; address[] public poolAddress; @@ -674,8 +674,8 @@ contract Pool is IPool, Ownable { return SWAP_IDS++; } - function setHighestPriceMarker(bytes32 pairId, uint256 value) external override onlyPoolLogic { - highestPriceMarker[pairId] = value; + function setHighestPriceKey(bytes32 pairId, uint256 value) external override onlyPoolLogic { + highestPriceKey[pairId] = value; } function orderBook( diff --git a/src/PoolLogic.sol b/src/PoolLogic.sol index 3e475f8..593e5f5 100644 --- a/src/PoolLogic.sol +++ b/src/PoolLogic.sol @@ -26,12 +26,6 @@ contract PoolLogic is IPoolLogic { IPoolStates public pool; ILiquidityLogic public liquidityLogic; - struct Payout { - address swapUser; - address token; - uint256 amount; - } - modifier onlyRouter() { if (msg.sender != pool.ROUTER_ADDRESS()) revert NotRouter(msg.sender); _; @@ -138,7 +132,7 @@ contract PoolLogic is IPoolLogic { (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(token)); uint256 currentExecPrice = PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_In, decimals_In, decimals_In); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(currentExecPrice, PRICE_PRECISION); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(currentExecPrice, PRICE_PRECISION); // Get market orders (isLimitOrder = false) Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, false); @@ -150,7 +144,7 @@ contract PoolLogic is IPoolLogic { // @todo: handle trigger orders if (currentSwap.typeOfOrder == 2) { - currentSwap = _settleCurrentSwapAgainstPool(currentSwap, currentExecPrice); + currentSwap = _executeStreamAgainstPool(currentSwap); // Update the order book entry bytes memory updatedSwapData = abi.encode( pairId, @@ -175,7 +169,7 @@ contract PoolLogic is IPoolLogic { ); } } else if (currentSwap.typeOfOrder == 1 && currentSwap.executionPrice == currentExecPrice) { - currentSwap = _settleCurrentSwapAgainstPool(currentSwap, currentExecPrice); + currentSwap = _executeStreamAgainstPool(currentSwap); currentSwap.typeOfOrder++; // Update the order book entry bytes memory updatedSwapData = abi.encode( @@ -221,102 +215,7 @@ contract PoolLogic is IPoolLogic { liquidityLogic.removeLiquidity(token, user, lpUnits); } - function swap( - address user, - address tokenIn, - address tokenOut, - uint256 amountIn, - uint256 executionPrice - ) - external - onlyRouter - { - // uint256 swapId = IPoolActions(POOL_ADDRESS).getNextSwapId(); - - // Swap memory currentSwap = Swap({ - // swapID: swapId, // will be filled in if/else - // swapAmount: amountIn, - // executionPrice: executionPrice, - // swapAmountRemaining: amountIn, - // streamsCount: 0, - // swapPerStream: 0, - // streamsRemaining: 0, - // tokenIn: tokenIn, - // tokenOut: tokenOut, - // completed: false, - // amountOut: 0, - // user: user, - // dustTokenAmount: 0, - // typeOfOrder: 2 - // }); - - // (,, uint256 reserveA_In,,,) = pool.poolInfo(address(tokenIn)); - - // (,, uint256 reserveA_Out,,,) = pool.poolInfo(address(tokenOut)); - - // uint256 currentExecPrice = getExecutionPrice(reserveA_In, reserveA_Out); - // bytes32 pairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); // for one direction - // uint256 executionPriceKey = getExecutionPriceLower(executionPrice); //KEY - - // // if price of order less than current, then just insert it in order book - // if (executionPrice < currentExecPrice) { - // uint256 streamCount = getStreamCount(tokenIn, tokenOut, currentSwap.swapAmount); - // uint256 swapPerStream = currentSwap.swapAmount / streamCount; - // if (currentSwap.swapAmount % streamCount != 0) { - // currentSwap.dustTokenAmount += (currentSwap.swapAmount - (streamCount * swapPerStream)); - // currentSwap.swapAmountRemaining = streamCount * swapPerStream; - // } - // currentSwap.streamsCount = streamCount; - // currentSwap.streamsRemaining = streamCount; - // currentSwap.swapPerStream = swapPerStream; - - // _insertInOrderBook(pairId, currentSwap, executionPriceKey); - // } else { - // if (executionPrice > pool.highestPriceMarker(pairId)) { - // IPoolActions(POOL_ADDRESS).setHighestPriceMarker(pairId, executionPrice); - // } - - // uint256 executionPriceReciprocal = getReciprocalOppositePrice(executionPrice, reserveA_In); - // uint256 executionPriceLower = getExecutionPriceLower(executionPriceReciprocal); - - // currentSwap = _settleCurrentSwapAgainstOpposite( - // currentSwap, executionPriceLower, executionPrice, executionPriceReciprocal - // ); - - // if (currentSwap.completed) { - // IPoolActions(POOL_ADDRESS).transferTokens(tokenOut, user, currentSwap.amountOut); - // } else { - // /* - // * pool processing, swap should be consumed against pool, reserves will be updated in this case - // * swap should be broken down into streams - // * if stream's are completed, do something with dust token??? and transferOut the amountOut - // * if streams are not completed, then just enqueue the swap, and update the reserves. - // */ - - // uint256 streamCount = getStreamCount(tokenIn, tokenOut, currentSwap.swapAmountRemaining); - // uint256 swapPerStream = currentSwap.swapAmountRemaining / streamCount; - // if (currentSwap.swapAmountRemaining % streamCount != 0) { - // currentSwap.dustTokenAmount += (currentSwap.swapAmountRemaining - (streamCount * swapPerStream)); - // currentSwap.swapAmountRemaining = streamCount * swapPerStream; - // } - // currentSwap.streamsCount = streamCount; - // currentSwap.streamsRemaining = streamCount; - // currentSwap.swapPerStream = swapPerStream; - - // currentSwap = _settleCurrentSwapAgainstPool(currentSwap, executionPrice); // amountOut is updated - // if (currentSwap.completed) { - // IPoolActions(POOL_ADDRESS).transferTokens( - // currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut - // ); - // } else { - // _insertInOrderBook(pairId, currentSwap, executionPriceKey); - // } - // } - // } - } - function swapMarketOrder(address user, address tokenIn, address tokenOut, uint256 amountIn) public onlyRouter { - uint256 swapId = IPoolActions(POOL_ADDRESS).getNextSwapId(); (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(tokenIn)); (,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(address(tokenOut)); uint256 currentExecPrice = PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out); @@ -339,20 +238,11 @@ contract PoolLogic is IPoolLogic { }); bytes32 pairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); // for one direction - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(currentExecPrice, PRICE_PRECISION); //KEY - uint256 streamCount = getStreamCount(tokenIn, tokenOut, currentSwap.swapAmountRemaining); - uint256 swapPerStream = currentSwap.swapAmountRemaining / streamCount; - if (currentSwap.swapAmountRemaining % streamCount != 0) { - currentSwap.dustTokenAmount += (currentSwap.swapAmountRemaining - (streamCount * swapPerStream)); - currentSwap.swapAmountRemaining = streamCount * swapPerStream; - } - currentSwap.streamsCount = streamCount; - currentSwap.streamsRemaining = streamCount; - currentSwap.swapPerStream = swapPerStream; + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(currentExecPrice, PRICE_PRECISION); //KEY - // currentSwap = updateSwapStreamInfo(currentSwap); + currentSwap = _updateSwapStreamInfo(currentSwap); - currentSwap = _settleCurrentSwapAgainstPool(currentSwap, currentExecPrice); // amountOut is updated + currentSwap = _executeStreamAgainstPool(currentSwap); // amountOut is updated if (currentSwap.completed) { IPoolActions(POOL_ADDRESS).transferTokens(currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut); } else { @@ -389,24 +279,15 @@ contract PoolLogic is IPoolLogic { bytes32 pairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); // for one direction - if (triggerExecutionPrice > pool.highestPriceMarker(pairId)) { - IPoolActions(POOL_ADDRESS).setHighestPriceMarker(pairId, triggerExecutionPrice); - } + uint256 triggerExecutionPriceKey = PoolLogicLib.getExecutionPriceKey(triggerExecutionPrice, PRICE_PRECISION); - // uint256 streamCount = getStreamCount(tokenIn, tokenOut, currentSwap.swapAmountRemaining); - // uint256 swapPerStream = currentSwap.swapAmountRemaining / streamCount; - // if (currentSwap.swapAmountRemaining % streamCount != 0) { - // currentSwap.dustTokenAmount += (currentSwap.swapAmountRemaining - (streamCount * swapPerStream)); - // currentSwap.swapAmountRemaining = streamCount * swapPerStream; - // } - // currentSwap.streamsCount = streamCount; - // currentSwap.streamsRemaining = streamCount; - // currentSwap.swapPerStream = swapPerStream; + if (triggerExecutionPriceKey > pool.highestPriceKey(pairId)) { + IPoolActions(POOL_ADDRESS).setHighestPriceKey(pairId, triggerExecutionPriceKey); + } - currentSwap = updateSwapStreamInfo(currentSwap); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(triggerExecutionPrice, PRICE_PRECISION); //KEY + currentSwap = _updateSwapStreamInfo(currentSwap); - _insertInOrderBook(pairId, currentSwap, executionPriceKey, false); + _insertInOrderBook(pairId, currentSwap, triggerExecutionPriceKey, false); } function swapLimitOrder( @@ -419,17 +300,12 @@ contract PoolLogic is IPoolLogic { external onlyRouter { - // 1. we need to check if the limitOrderPrice key is higher than the MAX_LIMIT ticks limit of the current price - // key (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(tokenIn); (,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(tokenOut); - // get the current PriceKey uint256 currentExecPrice = PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out); - // check if the limitOrderPriceKey is higher than the current price key by MAX_LIMIT * times the PRICE_PRECISION - if (limitOrderPrice > currentExecPrice + MAX_LIMIT_TICKS * PRICE_PRECISION) { - // if yes, then we need to process the limit order as a market order + if (limitOrderPrice > currentExecPrice + (MAX_LIMIT_TICKS * PRICE_PRECISION)) { swapMarketOrder(user, tokenIn, tokenOut, amountIn); return; } @@ -451,59 +327,34 @@ contract PoolLogic is IPoolLogic { typeOfOrder: 3 }); - bytes32 pairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); // for one direction + bytes32 pairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); - uint256 limitOrderPriceKey = PoolLogicLib.getExecutionPriceLower(limitOrderPrice, PRICE_PRECISION); + uint256 limitOrderPriceKey = PoolLogicLib.getExecutionPriceKey(limitOrderPrice, PRICE_PRECISION); + + if (limitOrderPriceKey > pool.highestPriceKey(pairId)) { + IPoolActions(POOL_ADDRESS).setHighestPriceKey(pairId, limitOrderPriceKey); + } - /// @notice if price of order less than current, then just insert it in order book and return if (limitOrderPrice < currentExecPrice) { - currentSwap = updateSwapStreamInfo(currentSwap); + currentSwap = _updateSwapStreamInfo(currentSwap); _insertInOrderBook(pairId, currentSwap, limitOrderPriceKey, true); return; } - /// @notice update the highest price marker if the limit order price is higher than the current highest price - /// marker - if (limitOrderPrice > pool.highestPriceMarker(pairId)) { - IPoolActions(POOL_ADDRESS).setHighestPriceMarker(pairId, limitOrderPrice); - } - - // 1. process swap with opposite swaps - uint256 executionPriceReciprocal = - PoolLogicLib.getReciprocalOppositePrice(limitOrderPrice, reserveA_In, decimals_In, decimals_Out); - uint256 executionPriceKeyOpp = PoolLogicLib.getExecutionPriceLower(executionPriceReciprocal, PRICE_PRECISION); + uint256 oppositeExecutionPriceKey = + PoolLogicLib.getExecutionPriceKey(PoolLogicLib.getOppositePrice(currentExecPrice), PRICE_PRECISION); - currentSwap = _settleCurrentLimitOrderAgainstOpposite( - currentSwap, executionPriceKeyOpp, limitOrderPrice, executionPriceReciprocal - ); + currentSwap = _swappingAgainstOppositeSwaps(currentSwap, oppositeExecutionPriceKey); if (currentSwap.completed) { IPoolActions(POOL_ADDRESS).transferTokens(tokenOut, user, currentSwap.amountOut); - // 2. calculate streams count and swap per stream, then process the swap with the pool } else { - /* - * pool processing, swap should be consumed against pool, reserves will be updated in this case - * swap should be broken down into streams - * if stream's are completed, do something with dust token??? and transferOut the amountOut - * if streams are not completed, then just enqueue the swap, and update the reserves. - */ - - // uint256 streamCount = getStreamCount(tokenIn, tokenOut, currentSwap.swapAmountRemaining); - // uint256 swapPerStream = currentSwap.swapAmountRemaining / streamCount; - // if (currentSwap.swapAmountRemaining % streamCount != 0) { - // currentSwap.dustTokenAmount += (currentSwap.swapAmountRemaining - (streamCount * swapPerStream)); - // currentSwap.swapAmountRemaining = streamCount * swapPerStream; - // } - // currentSwap.streamsCount = streamCount; - // currentSwap.streamsRemaining = streamCount; - // currentSwap.swapPerStream = swapPerStream; - - currentSwap = updateSwapStreamInfo(currentSwap); - - // don't we need to insert in order book here if the limitOrderPrice is lower from the current price - - currentSwap = _settleCurrentSwapAgainstPool(currentSwap, limitOrderPrice); // amountOut is updated + // calculate streams count and swap per stream, then process the swap with the pool + + currentSwap = _updateSwapStreamInfo(currentSwap); + + currentSwap = _executeStreamAgainstPool(currentSwap); // amountOut is updated if (currentSwap.completed) { IPoolActions(POOL_ADDRESS).transferTokens(currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut); } else { @@ -514,472 +365,346 @@ contract PoolLogic is IPoolLogic { function processLimitOrders(address tokenIn, address tokenOut) external onlyRouter { bytes32 currentPairId = bytes32(abi.encodePacked(tokenIn, tokenOut)); - uint256 startingExecutionPrice = pool.highestPriceMarker(currentPairId); - uint256 priceKey = PoolLogicLib.getExecutionPriceLower(startingExecutionPrice, PRICE_PRECISION); + uint256 priceKey = pool.highestPriceKey(currentPairId); (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(tokenIn)); (,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(address(tokenOut)); - uint256 poolReservesPriceKey = PoolLogicLib.getExecutionPriceLower( + uint256 poolReservesPriceKey = PoolLogicLib.getExecutionPriceKey( PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out), PRICE_PRECISION ); // @noticewhy // this? while (priceKey > poolReservesPriceKey) { - _executeStream(currentPairId, priceKey); // Appelle la fonction pour ce priceKey. - (,, reserveA_In,,,, decimals_In) = pool.poolInfo(address(tokenIn)); - (,, reserveA_Out,,,, decimals_Out) = pool.poolInfo(address(tokenOut)); - poolReservesPriceKey = PoolLogicLib.getExecutionPriceLower( - PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out), PRICE_PRECISION - ); + bool poolSwapExecuted = _executeStreams(currentPairId, priceKey); // Appelle la fonction pour ce priceKey. + if (poolSwapExecuted) { + // @audit this code block may be substituted for a leaner dedicated function, + // can be used in processing of market orders in single price key + /** + * thinking somewhere along the lines of + * uint256 slipImpactFromSwap ~ x (proportional to TokenOutAmountOut && poolReserveTokenOut) + * return uint256 cumulativeSlipImpact += slipImpact + * // if (cumulativeSlipImpact > PRICE_PRECISION) { handle } // + */ + (,, reserveA_In,,,,) = pool.poolInfo(address(tokenIn)); + (,, reserveA_Out,,,,) = pool.poolInfo(address(tokenOut)); + poolReservesPriceKey = PoolLogicLib.getExecutionPriceKey( + PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out), + PRICE_PRECISION + ); + } + priceKey -= PRICE_PRECISION; // 1 Gwei ou autre précision utilisée. + // update A->B highest price marker // need get reserve price for the next priceKey } } - function _executeStream(bytes32 pairId, uint256 executionPriceKey) internal { + // @note dedicate some limititation on price gap and opp swaps count on single prcieKey for maintenance bot call !! + // @audit ensure we are decrementing the highestPriceKLey on the completion of swaps in memory at a given key + + function _executeStreams(bytes32 pairId, uint256 executionPriceKey) internal returns (bool poolSwapExecuted) { Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, true); if (swaps.length == 0) { - return; + return poolSwapExecuted; } uint256 swapRemoved; for (uint256 i = 0; i < swaps.length;) { Swap memory currentSwap = swaps[i]; - uint256 swapExecutionPrice = currentSwap.executionPrice; - - (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(currentSwap.tokenIn)); - (,,,,,, uint8 decimals_Out) = pool.poolInfo(address(currentSwap.tokenIn)); - uint256 executionPriceReciprocal = - PoolLogicLib.getReciprocalOppositePrice(swapExecutionPrice, reserveA_In, decimals_In, decimals_Out); - uint256 oppPriceKey = PoolLogicLib.getExecutionPriceLower(executionPriceReciprocal, PRICE_PRECISION); - currentSwap = _settleCurrentSwapAgainstOpposite( - currentSwap, oppPriceKey, swapExecutionPrice, executionPriceReciprocal + uint256 oppositeExecutionPriceKey = PoolLogicLib.getExecutionPriceKey( + PoolLogicLib.getOppositePrice(currentSwap.executionPrice), PRICE_PRECISION ); - if (currentSwap.completed) { - // if the swap is completed, we keep looping to consume the opposite swaps - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue(pairId, executionPriceKey, 0, true); - IPoolActions(POOL_ADDRESS).transferTokens(currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut); - swapRemoved++; - uint256 lastIndex = swaps.length - swapRemoved; - swaps[i] = swaps[lastIndex]; - delete swaps[lastIndex]; - if (lastIndex == 0) { - // TODO we need to decrement the priceKey and find next collection of swaps - // it means no more swaps to process for the current priceKey - return; - } - } else { - // we recalculate the streams for the current swap - // I don't think we need to save it now - uint256 streamCount = - getStreamCount(currentSwap.tokenIn, currentSwap.tokenOut, currentSwap.swapAmountRemaining); - uint256 swapPerStream = currentSwap.swapAmountRemaining / streamCount; - currentSwap.streamsCount = streamCount; - currentSwap.swapPerStream = swapPerStream; - if (currentSwap.swapAmountRemaining % streamCount != 0) { - currentSwap.dustTokenAmount += (currentSwap.swapAmountRemaining - (streamCount * swapPerStream)); - currentSwap.swapAmountRemaining = streamCount * swapPerStream; // reAssigning newTokenOutAmountIn - // without dust tokens - } - swaps[i] = currentSwap; - break; - } - } + uint256 amountInRemaining; + (currentSwap, amountInRemaining) = _streamingAgainstOppositeSwaps(currentSwap, oppositeExecutionPriceKey); - uint256 count; - for (uint256 i; i < swaps.length - 1;) { - // settle against pool; - Swap memory currentSwap = swaps[i]; - currentSwap = _settleCurrentSwapAgainstPool(currentSwap, currentSwap.executionPrice); - if (currentSwap.completed) { - swapRemoved++; - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue(pairId, executionPriceKey, i, true); - IPoolActions(POOL_ADDRESS).transferTokens(currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut); - uint256 lastIndex = swaps.length - swapRemoved; - swaps[i] = swaps[lastIndex]; - delete swaps[lastIndex]; - if (lastIndex == 0) { - break; + if (amountInRemaining == 0) { + // if completed + if (currentSwap.completed) { + // if the swap is completed, we keep looping to consume the opposite swaps + IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue(pairId, executionPriceKey, 0, true); + + swapRemoved++; + uint256 lastIndex = swaps.length - swapRemoved; + swaps[i] = swaps[lastIndex]; + delete swaps[lastIndex]; + + IPoolActions(POOL_ADDRESS).transferTokens( + currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut + ); + + if (lastIndex == 0) { + // TODO we need to decrement the priceKey and find next collection of swaps + // it means no more swaps to process for the current priceKey + return poolSwapExecuted; + } + } else { + currentSwap = _updateSwapStreamInfo(currentSwap); + bytes memory updatedSwapData = abi.encode( + pairId, + currentSwap.swapAmount, + currentSwap.swapAmountRemaining, + currentSwap.completed, + currentSwap.streamsRemaining, + currentSwap.streamsCount, + currentSwap.swapPerStream, + currentSwap.dustTokenAmount + ); + + IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap(updatedSwapData, executionPriceKey, i, true); + unchecked { + ++i; + } } + + // we go to the next swap without trying to stream against pool + continue; } else { - // update the swap - bytes memory updatedSwapData = abi.encode( - pairId, - currentSwap.swapAmount, - currentSwap.swapAmountRemaining, - currentSwap.completed, - currentSwap.streamsRemaining, - currentSwap.streamsCount, - currentSwap.swapPerStream, - currentSwap.dustTokenAmount - ); + // we go against pool with the remaining amount + // @audit the current swap is only in memory and is not saved storage + currentSwap = _swappingAgainstPool(currentSwap, amountInRemaining); + currentSwap.streamsRemaining--; + poolSwapExecuted = true; + if (currentSwap.streamsRemaining == 0) { + currentSwap.completed = true; + swapRemoved++; + IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue(pairId, executionPriceKey, i, true); + + uint256 lastIndex = swaps.length - swapRemoved; + swaps[i] = swaps[lastIndex]; + delete swaps[lastIndex]; + IPoolActions(POOL_ADDRESS).transferTokens( + currentSwap.tokenOut, currentSwap.user, currentSwap.amountOut + ); + if (lastIndex == 0) { + break; + } + } else { + // update the swap + bytes memory updatedSwapData = abi.encode( + pairId, + currentSwap.swapAmount, + currentSwap.swapAmountRemaining, + currentSwap.completed, + currentSwap.streamsRemaining, + currentSwap.streamsCount, + currentSwap.swapPerStream, + currentSwap.dustTokenAmount + ); - IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap(updatedSwapData, executionPriceKey, i, true); - unchecked { - ++i; + IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap(updatedSwapData, executionPriceKey, i, true); + unchecked { + ++i; + } } } - if (count == swaps.length - 1) { - break; - } - count++; } } - function _settleCurrentLimitOrderAgainstOpposite( + function _swappingAgainstOppositeSwaps( Swap memory currentSwap, - uint256 executionPriceOppositeKey, - uint256 executionPriceCurrentSwap, - uint256 executionPriceOppositeSwap + uint256 currentOppositePriceKey ) internal returns (Swap memory) { - uint256 initialTokenInAmountIn = currentSwap.swapAmountRemaining; - uint256 dustTokenInAmountIn = currentSwap.dustTokenAmount; - - // tokenInAmountIn is the amount of tokenIn that is remaining to be processed from the selected swap - // it contains the dust token amount - uint256 tokenInAmountIn = initialTokenInAmountIn + dustTokenInAmountIn; - address tokenIn = currentSwap.tokenIn; - address tokenOut = currentSwap.tokenOut; + uint256 amountInRemaining; - // we need to look for the opposite swaps - bytes32 oppositePairId = bytes32(abi.encodePacked(tokenOut, tokenIn)); - //NEED TO CHANGE THIS. ORDERBOOK IS NOW DIFFERENT - Swap[] memory oppositeSwaps = pool.orderBook(oppositePairId, executionPriceOppositeKey, true); + (currentSwap, amountInRemaining) = + _matchAgainstOppositeSwaps(currentSwap, currentOppositePriceKey, currentSwap.swapAmountRemaining); - if (oppositeSwaps.length == 0) { - return currentSwap; // will call pool handling function + if (amountInRemaining == 0) { + currentSwap.completed = true; } - /* - iterate on all the opposite swaps - And check that if the amountOut of the oppositeSwap < currentSwapAmountIn - if yes the consume oppositeSwap, and move on to the next oppositeSwap - transferOut the swapAmoutOut asset - - if amountOut of oppositeSwap > currentSwapAmountIn - then consume the currentSwap, break the loop - transferIn the swapAmountIn assets - update the oppositeSwap struct - */ - (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(tokenIn)); - (,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(address(tokenOut)); - - uint256 reserveAInFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPriceOppositeSwap, reserveA_Out, decimals_Out, decimals_In); - uint256 reserveAOutFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPriceCurrentSwap, reserveA_In, decimals_In, decimals_Out); - - // the number of opposite swaps - // uint256 oppositeSwapsCount = oppositeBack - oppositeFront; - // Payout[] memory oppositePayouts = new Payout[](oppositeSwapsCount); - // now we need to loop through the opposite swaps and to process them - uint256 swapRemoved; - for (uint256 i = 0; i < oppositeSwaps.length;) { - Swap memory oppositeSwap = oppositeSwaps[i]; - - // tokenOutAmountIn is the amount of tokenOut that is remaining to be processed from the opposite swap - // it contains opp swap dust token - uint256 tokenOutAmountIn = oppositeSwap.swapAmountRemaining + oppositeSwap.dustTokenAmount; - - // we need to calculate the amount of tokenOut for the given tokenInAmountIn -> tokenA -> tokenB - uint256 tokenOutAmountOut = PoolLogicLib.getAmountOut(tokenInAmountIn, reserveA_In, reserveAOutFromPrice); - - // we need to calculate the amount of tokenIn for the given tokenOutAmountIn -> tokenB -> tokenA - uint256 tokenInAmountOut = PoolLogicLib.getAmountOut(tokenOutAmountIn, reserveA_Out, reserveAInFromPrice); - - // we need to check if the amount of tokenIn that we need to send to the user is less than the amount of - // tokenIn that is remaining to be processed - if (tokenInAmountIn > tokenInAmountOut) { - // 1. we keep in memory the swapUser, tokenOut address and the amountOutSwap in memory to transfer the - // tokens - - IPoolActions(POOL_ADDRESS).transferTokens( - oppositeSwap.tokenOut, oppositeSwap.user, tokenInAmountOut + oppositeSwap.amountOut - ); - - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( - oppositePairId, executionPriceOppositeKey, i, true - ); - - uint256 newTokenInAmountIn = tokenInAmountIn - tokenInAmountOut; - - currentSwap.swapAmountRemaining = newTokenInAmountIn; - currentSwap.amountOut += tokenOutAmountIn; - tokenInAmountIn = newTokenInAmountIn; - // 4. we continue to the next oppositeSwap - - swapRemoved++; - if (swapRemoved == oppositeSwaps.length) { - break; - } - uint256 lastIndex = oppositeSwaps.length - swapRemoved; - oppositeSwaps[i] = oppositeSwaps[lastIndex]; - delete oppositeSwaps[lastIndex]; - } else { - // 1. frontSwap is completed and is taken out of the stream queue - - currentSwap.amountOut += tokenOutAmountOut; - currentSwap.completed = true; // we don't need to do this - currentSwap.dustTokenAmount = 0; - - // 2. we recalculate the oppositeSwap conditions and update it (if tokenInAmountIn == tokenInAmountOut - // we complete the oppositeSwap) + return currentSwap; + } - //both swaps consuming each other - if (tokenInAmountIn == tokenInAmountOut) { - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( - oppositePairId, executionPriceOppositeKey, i, true - ); + function _streamingAgainstOppositeSwaps( + Swap memory currentSwap, + uint256 currentOppositePriceKey + ) + internal + returns (Swap memory, uint256 amountInRemaining) + { + uint256 tokenInAmountIn = currentSwap.swapPerStream; - IPoolActions(POOL_ADDRESS).transferTokens( - oppositeSwap.tokenOut, oppositeSwap.user, tokenInAmountOut + oppositeSwap.amountOut - ); - } else { - // only front is getting consumed. so we need to update opposite one - // uint256 newTokenOutAmountIn = tokenOutAmountIn - tokenOutAmountOut; - - oppositeSwap.swapAmountRemaining = tokenOutAmountIn - tokenOutAmountOut; - oppositeSwap.amountOut += tokenInAmountIn; - - // uint256 streamCount = getStreamCount(tokenOut, tokenIn, newTokenOutAmountIn); - // uint256 swapPerStream = newTokenOutAmountIn / streamCount; - // uint256 dustTokenAmount; - // if (newTokenOutAmountIn % streamCount != 0) { - // dustTokenAmount += (newTokenOutAmountIn - (streamCount * swapPerStream)); - // newTokenOutAmountIn = streamCount * swapPerStream; // reAssigning newTokenOutAmountIn without - // dust tokens - // } - - oppositeSwap = updateSwapStreamInfo(oppositeSwap); - - // updating oppositeSwap - bytes memory updatedSwapData_opposite = abi.encode( - oppositePairId, - oppositeSwap.amountOut, - oppositeSwap.swapAmountRemaining, - oppositeSwap.completed, - oppositeSwap.streamsCount, - oppositeSwap.streamsCount, - oppositeSwap.swapPerStream, - oppositeSwap.dustTokenAmount, - 2 - ); + if (currentSwap.streamsRemaining == 1) { + tokenInAmountIn += currentSwap.dustTokenAmount; + } - IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap( - updatedSwapData_opposite, executionPriceOppositeKey, i, true - ); - } - // 3. we terminate the loop as we have completed the frontSwap - break; + (currentSwap, amountInRemaining) = + _matchAgainstOppositeSwaps(currentSwap, currentOppositePriceKey, tokenInAmountIn); + if (amountInRemaining == 0) { + currentSwap.streamsRemaining--; + if (currentSwap.streamsRemaining == 0) { + currentSwap.dustTokenAmount = 0; // don't know if we need this update + currentSwap.completed = true; // don't know if we need this update } } - return currentSwap; + return (currentSwap, amountInRemaining); } - function _settleCurrentSwapAgainstOpposite( + // @audit need a way to exit gracefully if the gap btw currentOppositePriceKey and currentOppositePriceKey is too + // big + function _matchAgainstOppositeSwaps( Swap memory currentSwap, - uint256 executionPriceOppositeKey, - uint256 executionPriceCurrentSwap, - uint256 executionPriceOppositeSwap + uint256 oppositeCurrentPoolPrice, + uint256 tokenInAmountIn ) - internal - returns (Swap memory) + private + returns (Swap memory, uint256 amountInRemaining) { - uint256 initialTokenInAmountIn = currentSwap.swapAmountRemaining; - uint256 dustTokenInAmountIn = currentSwap.dustTokenAmount; - - // tokenInAmountIn is the amount of tokenIn that is remaining to be processed from the selected swap - // it contains the dust token amount - uint256 tokenInAmountIn = initialTokenInAmountIn + dustTokenInAmountIn; + amountInRemaining = tokenInAmountIn; address tokenIn = currentSwap.tokenIn; address tokenOut = currentSwap.tokenOut; + (,,,,,, uint8 decimals_In) = pool.poolInfo(address(tokenIn)); + (,,,,,, uint8 decimals_Out) = pool.poolInfo(address(tokenOut)); + uint256 executionPriceCurrentSwap = currentSwap.executionPrice; - // we need to look for the opposite swaps bytes32 oppositePairId = bytes32(abi.encodePacked(tokenOut, tokenIn)); - Swap[] memory oppositeSwaps; + uint256 oppositeCachedPriceKey = pool.highestPriceKey(oppositePairId); - uint256 priceKey = executionPriceOppositeKey; + while (oppositeCachedPriceKey >= oppositeCurrentPoolPrice) { + Swap[] memory oppositeSwaps = pool.orderBook(oppositePairId, oppositeCachedPriceKey, true); - while (true) { - oppositeSwaps = pool.orderBook(oppositePairId, priceKey, true); if (oppositeSwaps.length == 0) { - priceKey = priceKey - PoolLogic.PRICE_PRECISION; // will call pool handling function - } else { - break; + /** + * @audit need ot handle out of bounds errors + */ + // return currentSwap; + oppositeCachedPriceKey -= PRICE_PRECISION; + IPoolActions(POOL_ADDRESS).setHighestPriceKey(oppositePairId, oppositeCachedPriceKey); + continue; } - } - - /* - iterate on all the opposite swaps - And check that if the amountOut of the oppositeSwap < currentSwapAmountIn - if yes the consume oppositeSwap, and move on to the next oppositeSwap - transferOut the swapAmoutOut asset - - if amountOut of oppositeSwap > currentSwapAmountIn - then consume the currentSwap, break the loop - transferIn the swapAmountIn assets - update the oppositeSwap struct - */ - (,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(address(tokenIn)); - (,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(address(tokenOut)); - - uint256 reserveAInFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPriceOppositeSwap, reserveA_Out, decimals_Out, decimals_In); - uint256 reserveAOutFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPriceCurrentSwap, reserveA_In, decimals_In, decimals_Out); - - // the number of opposite swaps - // uint256 oppositeSwapsCount = oppositeBack - oppositeFront; - // Payout[] memory oppositePayouts = new Payout[](oppositeSwapsCount); - - // now we need to loop through the opposite swaps and to process them - uint256 swapRemoved; - for (uint256 i = 0; i < oppositeSwaps.length;) { - Swap memory oppositeSwap = oppositeSwaps[i]; - - // tokenOutAmountIn is the amount of tokenOut that is remaining to be processed from the opposite swap - // it contains opp swap dust token - uint256 tokenOutAmountIn = oppositeSwap.swapAmountRemaining + oppositeSwap.dustTokenAmount; - - // we need to calculate the amount of tokenOut for the given tokenInAmountIn -> tokenA -> tokenB - uint256 tokenOutAmountOut = PoolLogicLib.getAmountOut(tokenInAmountIn, reserveA_In, reserveAOutFromPrice); - // we need to calculate the amount of tokenIn for the given tokenOutAmountIn -> tokenB -> tokenA - uint256 tokenInAmountOut = PoolLogicLib.getAmountOut(tokenOutAmountIn, reserveA_Out, reserveAInFromPrice); + uint256 swapRemoved; + for (uint256 i = 0; i < oppositeSwaps.length;) { + Swap memory oppositeSwap = oppositeSwaps[i]; - // we need to check if the amount of tokenIn that we need to send to the user is less than the amount of - // tokenIn that is remaining to be processed - if (tokenInAmountIn > tokenInAmountOut) { - // 1. we keep in memory the swapUser, tokenOut address and the amountOutSwap in memory to transfer the - // tokens + // tokenOutAmountIn is the amount of tokenOut that is remaining to be processed from the opposite swap + // it contains opp swap dust token + uint256 tokenOutAmountIn = oppositeSwap.swapAmountRemaining + oppositeSwap.dustTokenAmount; - IPoolActions(POOL_ADDRESS).transferTokens( - oppositeSwap.tokenOut, oppositeSwap.user, tokenInAmountOut + oppositeSwap.amountOut + // we need to calculate the amount of tokenOut for the given tokenInAmountIn -> tokenA -> tokenB + // uint256 tokenOutAmountOut = PoolLogicLib.getAmountOut(tokenInAmountIn, reserveA_In, + // reserveAOutFromPrice); + uint256 tokenOutAmountOut = PoolLogicLib.calculateAmountOutFromPrice( + tokenInAmountIn, executionPriceCurrentSwap, decimals_In, decimals_Out ); - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( - oppositePairId, executionPriceOppositeKey, i, true + // we need to calculate the amount of tokenIn for the given tokenOutAmountIn -> tokenB -> tokenA + uint256 tokenInAmountOut = PoolLogicLib.calculateAmountInFromPrice( + tokenOutAmountIn, oppositeSwap.executionPrice, decimals_In, decimals_Out ); - uint256 newTokenInAmountIn = tokenInAmountIn - tokenInAmountOut; + // we need to check if the amount of tokenIn that we need to send to the user is less than the amount of + // tokenIn that is remaining to be processed + if (tokenInAmountIn > tokenInAmountOut) { + // 1. we keep in memory the swapUser, tokenOut address and the amountOutSwap in memory to transfer + // the + // tokens - currentSwap.swapAmountRemaining = newTokenInAmountIn; - currentSwap.amountOut += tokenOutAmountIn; - tokenInAmountIn = newTokenInAmountIn; - // 4. we continue to the next oppositeSwap + IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( + oppositePairId, oppositeCachedPriceKey, i, true + ); - swapRemoved++; - if (swapRemoved == oppositeSwaps.length) { - break; - } - uint256 lastIndex = oppositeSwaps.length - swapRemoved; - oppositeSwaps[i] = oppositeSwaps[lastIndex]; - delete oppositeSwaps[lastIndex]; - } else { - // 1. frontSwap is completed and is taken out of the stream queue + uint256 newTokenInAmountIn = tokenInAmountIn - tokenInAmountOut; - currentSwap.amountOut += tokenOutAmountOut; - currentSwap.completed = true; // we don't need to do this - currentSwap.dustTokenAmount = 0; + currentSwap.swapAmountRemaining = newTokenInAmountIn; + currentSwap.amountOut += tokenOutAmountIn; + tokenInAmountIn = newTokenInAmountIn; - // 2. we recalculate the oppositeSwap conditions and update it (if tokenInAmountIn == tokenInAmountOut - // we complete the oppositeSwap) + amountInRemaining -= tokenInAmountOut; + // 4. we continue to the next oppositeSwap - //both swaps consuming each other - if (tokenInAmountIn == tokenInAmountOut) { - IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( - oppositePairId, executionPriceOppositeKey, i, true - ); + swapRemoved++; + if (swapRemoved == oppositeSwaps.length) { + break; + } + uint256 lastIndex = oppositeSwaps.length - swapRemoved; + oppositeSwaps[i] = oppositeSwaps[lastIndex]; IPoolActions(POOL_ADDRESS).transferTokens( oppositeSwap.tokenOut, oppositeSwap.user, tokenInAmountOut + oppositeSwap.amountOut ); + + delete oppositeSwaps[lastIndex]; } else { - // only front is getting consumed. so we need to update opposite one - uint256 newTokenOutAmountIn = tokenOutAmountIn - tokenOutAmountOut; - uint256 streamCount = getStreamCount(tokenOut, tokenIn, newTokenOutAmountIn); - uint256 swapPerStream = newTokenOutAmountIn / streamCount; - uint256 dustTokenAmount; - if (newTokenOutAmountIn % streamCount != 0) { - dustTokenAmount += (newTokenOutAmountIn - (streamCount * swapPerStream)); - newTokenOutAmountIn = streamCount * swapPerStream; // reAssigning newTokenOutAmountIn without - // dust tokens - } + // 1. frontSwap is completed and is taken out of the stream queue - // updating oppositeSwap - bytes memory updatedSwapData_opposite = abi.encode( - oppositePairId, - tokenOutAmountOut, - newTokenOutAmountIn, - oppositeSwap.completed, - streamCount, - streamCount, - swapPerStream, - dustTokenAmount - ); + currentSwap.amountOut += tokenOutAmountOut; + currentSwap.swapAmountRemaining = 0; + amountInRemaining = 0; - IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap( - updatedSwapData_opposite, executionPriceOppositeKey, i, true - ); + // 2. we recalculate the oppositeSwap conditions and update it (if tokenInAmountIn == + // tokenInAmountOut + // we complete the oppositeSwap) + + //both swaps consuming each other + if (tokenInAmountIn == tokenInAmountOut) { + IPoolActions(POOL_ADDRESS).dequeueSwap_pairStreamQueue( + oppositePairId, oppositeCachedPriceKey, i, true + ); + + IPoolActions(POOL_ADDRESS).transferTokens( + oppositeSwap.tokenOut, oppositeSwap.user, tokenInAmountOut + oppositeSwap.amountOut + ); + } else { + // only front is getting consumed. so we need to update opposite one + + oppositeSwap.swapAmountRemaining = tokenOutAmountIn - tokenOutAmountOut; + oppositeSwap.amountOut += tokenInAmountIn; + + oppositeSwap = _updateSwapStreamInfo(oppositeSwap); + + // updating oppositeSwap + bytes memory updatedSwapData_opposite = abi.encode( + oppositePairId, + oppositeSwap.amountOut, + oppositeSwap.swapAmountRemaining, + oppositeSwap.completed, + oppositeSwap.streamsCount, + oppositeSwap.streamsCount, + oppositeSwap.swapPerStream, + oppositeSwap.dustTokenAmount, + 2 + ); + + IPoolActions(POOL_ADDRESS).updatePairStreamQueueSwap( + updatedSwapData_opposite, oppositeCachedPriceKey, i, true + ); + } + // 3. we terminate the loop as we have completed the frontSwap + return (currentSwap, amountInRemaining); } - // 3. we terminate the loop as we have completed the frontSwap - break; } + oppositeCachedPriceKey -= PRICE_PRECISION; + IPoolActions(POOL_ADDRESS).setHighestPriceKey(oppositePairId, oppositeCachedPriceKey); + continue; } - return currentSwap; + return (currentSwap, amountInRemaining); } - function _settleCurrentSwapAgainstPool( - Swap memory currentSwap, - uint256 executionPriceCurrentSwap - ) - internal - returns (Swap memory) - { + function _executeStreamAgainstPool(Swap memory currentSwap) internal returns (Swap memory) { uint256 swapAmountIn = currentSwap.swapPerStream; - uint256 amountIn; - (uint256 reserveD_In,, uint256 reserveA_In,,,,) = pool.poolInfo(address(currentSwap.tokenIn)); - (uint256 reserveD_Out,, uint256 reserveA_Out,,,,) = pool.poolInfo(address(currentSwap.tokenOut)); + if (currentSwap.streamsRemaining == 1) { + swapAmountIn += currentSwap.dustTokenAmount; + } - // TODO we probably need to separate this limit order logic in a separate function + // @note at this point only market orders are processed at market price + uint256 amountOut; if (currentSwap.typeOfOrder == 2) { - amountIn = swapAmountIn; + (, amountOut) = _computePoolReservesAtMarket(currentSwap.tokenIn, currentSwap.tokenOut, swapAmountIn); } else { - (,,,,,, uint8 decimals_In) = pool.poolInfo(address(currentSwap.tokenIn)); - (,,,,,, uint8 decimals_Out) = pool.poolInfo(address(currentSwap.tokenOut)); - uint256 currentExecPrice = - PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out); - - uint256 expectedAmountOut = PoolLogicLib.calculateAmountOutFromPrice( - swapAmountIn, executionPriceCurrentSwap, decimals_In, decimals_Out + (, amountOut) = _computePoolReservesAtExecutionPrice( + currentSwap.tokenIn, currentSwap.tokenOut, currentSwap.executionPrice, swapAmountIn ); - // now we get the expected amount in to get the expectedAmountOut at the pool price - amountIn = - PoolLogicLib.calculateAmountInFromPrice(expectedAmountOut, currentExecPrice, decimals_In, decimals_Out); - - // uint256 extraToThePool = swapAmountIn - amountIn; - } - - // the logic here is that we add, if present, the dust token amount to the swapAmountRemaining on the last swap - // (when streamsRemaining == 1) - if (currentSwap.streamsRemaining == 1) { - amountIn += currentSwap.dustTokenAmount; } - (uint256 dToUpdate, uint256 amountOut) = - PoolLogicLib.getSwapAmountOut(amountIn, reserveA_In, reserveA_Out, reserveD_In, reserveD_Out); - - bytes memory updateReservesParams = - abi.encode(true, currentSwap.tokenIn, currentSwap.tokenOut, swapAmountIn, dToUpdate, amountOut, dToUpdate); - IPoolActions(POOL_ADDRESS).updateReserves(updateReservesParams); - currentSwap.streamsRemaining--; if (currentSwap.streamsRemaining == 0) { currentSwap.completed = true; @@ -991,6 +716,82 @@ contract PoolLogic is IPoolLogic { return currentSwap; } + function _swappingAgainstPool(Swap memory currentSwap, uint256 swapAmount) private returns (Swap memory) { + uint256 swapAmountIn = swapAmount; + + // @note at this point only market orders are processed at market price + uint256 amountOut; + if (currentSwap.typeOfOrder == 2) { + (, amountOut) = _computePoolReservesAtMarket(currentSwap.tokenIn, currentSwap.tokenOut, swapAmountIn); + } else { + (, amountOut) = _computePoolReservesAtExecutionPrice( + currentSwap.tokenIn, currentSwap.tokenOut, currentSwap.executionPrice, swapAmountIn + ); + } + + currentSwap.swapAmountRemaining -= swapAmountIn; + currentSwap.amountOut += amountOut; + + return currentSwap; + } + + // @audit should we keep this concept ?? + function _computePoolReservesAtExecutionPrice( + address tokenIn, + address tokenOut, + uint256 executionPrice, + uint256 amountToSwap + ) + private + returns (uint256 dToUpdate, uint256 amountOut) + { + uint256 swapAmountIn = amountToSwap; + + (uint256 reserveD_In,, uint256 reserveA_In,,,, uint8 decimals_In) = pool.poolInfo(tokenIn); + (uint256 reserveD_Out,, uint256 reserveA_Out,,,, uint8 decimals_Out) = pool.poolInfo(tokenOut); + + uint256 currentExecPrice = PoolLogicLib.getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out); + + // @note if executionPrice <= currentExecPrice, we need to swap at the pool price + if (executionPrice <= currentExecPrice) { + return _computePoolReservesAtMarket(tokenIn, tokenOut, swapAmountIn); + } + + uint256 expectedAmountOut = + PoolLogicLib.calculateAmountOutFromPrice(swapAmountIn, executionPrice, decimals_In, decimals_Out); + // now we get the expected amount in to get the expectedAmountOut at the pool price + uint256 amountIn = + PoolLogicLib.calculateAmountInFromPrice(expectedAmountOut, currentExecPrice, decimals_In, decimals_Out); + + (dToUpdate, amountOut) = + PoolLogicLib.getSwapAmountOut(amountIn, reserveA_In, reserveA_Out, reserveD_In, reserveD_Out); + + bytes memory updateReservesParams = + abi.encode(true, tokenIn, tokenOut, swapAmountIn, dToUpdate, amountOut, dToUpdate); + IPoolActions(POOL_ADDRESS).updateReserves(updateReservesParams); + } + + function _computePoolReservesAtMarket( + address tokenIn, + address tokenOut, + uint256 amountToSwap + ) + private + returns (uint256 dToUpdate, uint256 amountOut) + { + uint256 swapAmountIn = amountToSwap; + + (uint256 reserveD_In,, uint256 reserveA_In,,,,) = pool.poolInfo(tokenIn); + (uint256 reserveD_Out,, uint256 reserveA_Out,,,,) = pool.poolInfo(tokenOut); + + (dToUpdate, amountOut) = + PoolLogicLib.getSwapAmountOut(swapAmountIn, reserveA_In, reserveA_Out, reserveD_In, reserveD_Out); + + bytes memory updateReservesParams = + abi.encode(true, tokenIn, tokenOut, swapAmountIn, dToUpdate, amountOut, dToUpdate); + IPoolActions(POOL_ADDRESS).updateReserves(updateReservesParams); + } + function _insertInOrderBook( bytes32 pairId, Swap memory _swap, @@ -1002,37 +803,37 @@ contract PoolLogic is IPoolLogic { IPoolActions(POOL_ADDRESS).updateOrderBook(pairId, _swap, executionPriceKey, isLimitOrder); } - /** - * @notice here we are maintaining the queue for the given pairId only - * if the price is less than the execution price we add the swap to the stream queue - * if the price is greater than the execution price we add the swap to the pending queue - * @param pairId bytes32 the pairId for the given pair - * @param swapDetails Swap the swap details - * @param currentPrice uint256 the current price of the pair - */ - function _maintainQueue(bytes32 pairId, Swap memory swapDetails, uint256 currentPrice) internal { - // if execution price 0 (stream queue) , otherwise another queue - // add into queue - if (swapDetails.executionPrice <= currentPrice) { - (,, uint256 back) = pool.pairStreamQueue(pairId); - swapDetails.swapID = back; - IPoolActions(POOL_ADDRESS).enqueueSwap_pairStreamQueue(pairId, swapDetails); - } else { - (Swap[] memory swaps_pending, uint256 front, uint256 back) = pool.pairPendingQueue(pairId); - swapDetails.swapID = back; - - if (back - front == 0) { - IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); - } else { - if (swapDetails.executionPrice >= swaps_pending[back - 1].executionPrice) { - IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); - } else { - IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); - IPoolActions(POOL_ADDRESS).sortPairPendingQueue(pairId); - } - } - } - } + // /** + // * @notice here we are maintaining the queue for the given pairId only + // * if the price is less than the execution price we add the swap to the stream queue + // * if the price is greater than the execution price we add the swap to the pending queue + // * @param pairId bytes32 the pairId for the given pair + // * @param swapDetails Swap the swap details + // * @param currentPrice uint256 the current price of the pair + // */ + // function _maintainQueue(bytes32 pairId, Swap memory swapDetails, uint256 currentPrice) internal { + // // if execution price 0 (stream queue) , otherwise another queue + // // add into queue + // if (swapDetails.executionPrice <= currentPrice) { + // (,, uint256 back) = pool.pairStreamQueue(pairId); + // swapDetails.swapID = back; + // IPoolActions(POOL_ADDRESS).enqueueSwap_pairStreamQueue(pairId, swapDetails); + // } else { + // (Swap[] memory swaps_pending, uint256 front, uint256 back) = pool.pairPendingQueue(pairId); + // swapDetails.swapID = back; + + // if (back - front == 0) { + // IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); + // } else { + // if (swapDetails.executionPrice >= swaps_pending[back - 1].executionPrice) { + // IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); + // } else { + // IPoolActions(POOL_ADDRESS).enqueueSwap_pairPendingQueue(pairId, swapDetails); + // IPoolActions(POOL_ADDRESS).sortPairPendingQueue(pairId); + // } + // } + // } + // } function getStreamCount(address tokenIn, address tokenOut, uint256 amountIn) public view returns (uint256) { (uint256 reserveD_In,,,,,, uint8 decimalsIn) = pool.poolInfo(address(tokenIn)); @@ -1073,26 +874,11 @@ contract PoolLogic is IPoolLogic { return initialized; } - // function getCurrentPrice( - // address tokenIn, - // address tokenOut - // ) - // public - // view - // returns (uint256 currentPrice, uint256 reserveA_In, uint256 reserveA_Out) - // { - // uint8 decimals_In; - // uint8 decimals_Out; - // (,, reserveA_In,,,, decimals_In) = pool.poolInfo(address(tokenIn)); - // (,, reserveA_Out,,,, decimals_Out) = pool.poolInfo(address(tokenOut)); - // currentPrice = getExecutionPrice(reserveA_In, reserveA_Out, decimals_In, decimals_Out); - // } - - function updateSwapStreamInfo(Swap memory _swap) private view returns (Swap memory) { + function _updateSwapStreamInfo(Swap memory _swap) private view returns (Swap memory) { uint256 streamCount = getStreamCount(_swap.tokenIn, _swap.tokenOut, _swap.swapAmountRemaining); uint256 swapPerStream = _swap.swapAmountRemaining / streamCount; if (_swap.swapAmountRemaining % streamCount != 0) { - _swap.dustTokenAmount += (_swap.swapAmountRemaining - (streamCount * swapPerStream)); + _swap.dustTokenAmount = (_swap.swapAmountRemaining - (streamCount * swapPerStream)); _swap.swapAmountRemaining = streamCount * swapPerStream; } _swap.streamsCount = streamCount; diff --git a/src/Router.sol b/src/Router.sol index ea6968f..739e593 100644 --- a/src/Router.sol +++ b/src/Router.sol @@ -155,15 +155,6 @@ contract Router is Ownable, ReentrancyGuard, IRouter { IPoolLogic(poolStates.POOL_LOGIC()).processRemoveLiquidity(token); } - function swap(address tokenIn, address tokenOut, uint256 amountIn, uint256 executionPrice) external nonReentrant { - // if (amountIn == 0) revert InvalidAmount(); - // if (executionPrice == 0) revert InvalidExecutionPrice(); - // if (!poolExist(tokenIn) || !poolExist(tokenOut)) revert InvalidPool(); - - // IERC20(tokenIn).safeTransferFrom(msg.sender, POOL_ADDRESS, amountIn); - // IPoolLogic(poolStates.POOL_LOGIC()).swap(msg.sender, tokenIn, tokenOut, amountIn, executionPrice); - } - function swapMarketOrder(address tokenIn, address tokenOut, uint256 amountIn) external nonReentrant { if (amountIn == 0) revert InvalidAmount(); if (!poolExist(tokenIn) || !poolExist(tokenOut)) revert InvalidPool(); diff --git a/src/interfaces/pool-logic/IPoolLogicActions.sol b/src/interfaces/pool-logic/IPoolLogicActions.sol index cfac224..b99cc1b 100644 --- a/src/interfaces/pool-logic/IPoolLogicActions.sol +++ b/src/interfaces/pool-logic/IPoolLogicActions.sol @@ -42,7 +42,6 @@ interface IPoolLogicActions { ) external; function withdrawFromGlobalPool(address user, address token, uint256 amount) external; - function swap(address user, address tokenIn, address tokenOut, uint256 amountIn, uint256 executionPrice) external; function swapLimitOrder( address user, address tokenIn, diff --git a/src/interfaces/pool/IPoolActions.sol b/src/interfaces/pool/IPoolActions.sol index c26b975..cf1f1a9 100644 --- a/src/interfaces/pool/IPoolActions.sol +++ b/src/interfaces/pool/IPoolActions.sol @@ -76,7 +76,7 @@ interface IPoolActions { function getReserveA(address pool) external view returns (uint256); function getReserveD(address pool) external view returns (uint256); - function setHighestPriceMarker(bytes32 pairId, uint256 value) external; + function setHighestPriceKey(bytes32 pairId, uint256 value) external; function getPoolAddresses() external view returns (address[] memory); diff --git a/src/interfaces/pool/IPoolStates.sol b/src/interfaces/pool/IPoolStates.sol index aa74d5f..c3cf67c 100644 --- a/src/interfaces/pool/IPoolStates.sol +++ b/src/interfaces/pool/IPoolStates.sol @@ -35,6 +35,6 @@ interface IPoolStates { returns (RemoveLiquidityStream[] memory removeLiquidityStream); function globalPoolDBalance(address) external view returns (uint256); - function highestPriceMarker(bytes32) external view returns (uint256); + function highestPriceKey(bytes32) external view returns (uint256); function orderBook(bytes32 pairId, uint256 priceKey, bool isLimitOrder) external view returns (Swap[] memory); } diff --git a/src/lib/PoolLogicLib.sol b/src/lib/PoolLogicLib.sol index ac5da61..dde970d 100644 --- a/src/lib/PoolLogicLib.sol +++ b/src/lib/PoolLogicLib.sol @@ -8,6 +8,7 @@ library PoolLogicLib { using DSMath for uint256; using ScaleDecimals for uint256; + // @audit ensure that decimals are being passed in efffectively in lib functions function getExecutionPrice( uint256 reserveA_In, uint256 reserveA_Out, @@ -27,6 +28,7 @@ library PoolLogicLib { return (amountIn * reserveOut) / reserveIn; } + // @audit unit test required && scale to A to 18 function getSwapAmountOut( uint256 amountIn, uint256 reserveA_In, @@ -46,17 +48,24 @@ library PoolLogicLib { // 100000000000000000000 // 1000000000000000000 uint256 d1 = (amountIn * reserveD_In) / (amountIn + reserveA_In); + // use the wdiv divide 2 18 decimals return (d1, ((d1 * reserveA_Out) / (d1 + reserveD_Out))); } + // @audit unit test required && scale reserveA to 18 decimals + // not used yet or duplicated function getSwapAmountOutFromD(uint256 dIn, uint256 reserveA, uint256 reserveD) public pure returns (uint256) { return ((dIn * reserveA) / (dIn + reserveD)); } + // @audit unit test required && scale reserveA to 18 decimals + // not used yet function getTokenOut(uint256 dAmount, uint256 reserveA, uint256 reserveD) external pure returns (uint256) { return (dAmount * reserveA) / (dAmount + reserveD); } + // @audit unit test required && scale reserveA to 18 decimals + // not used yet function getDOut(uint256 tokenAmount, uint256 reserveA, uint256 reserveD) external pure returns (uint256) { return (tokenAmount * reserveD) / (tokenAmount + reserveA); } @@ -80,8 +89,10 @@ library PoolLogicLib { uint256 scaledAmountIn = amountIn.scaleAmountToDecimals(decimalsIn, 18); - uint256 result = ((scaledAmountIn * streamCountPrecision) / (((streamCountPrecision - poolSlippage) * reserveD))); + uint256 result = + ((scaledAmountIn * streamCountPrecision) / (((streamCountPrecision - poolSlippage) * reserveD))); return result < 1 ? 1 : result; + // @audit require a limit on the maximum number of streams } function calculateAssetTransfer( @@ -93,9 +104,10 @@ library PoolLogicLib { pure returns (uint256) { - return (reserveA.wmul(lpUnits)).wdiv(totalLpUnits); + return (reserveA * lpUnits) / totalLpUnits; } + // @audit ensure that this is being handled effectively without wmul function calculateDToDeduct( uint256 lpUnits, uint256 reserveD, @@ -105,14 +117,16 @@ library PoolLogicLib { pure returns (uint256) { - return reserveD.wmul(lpUnits).wdiv(totalLpUnits); + return (reserveD * lpUnits) / totalLpUnits; } function getPoolId(address tokenA, address tokenB) public pure returns (bytes32) { (address A, address B) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); + // @audit replace keccak for abi.encode && check dependancies return keccak256(abi.encodePacked(A, B)); } + // @audit ensure that this is being handled well with decimals function calculateLpUnitsToMint( uint256 lpUnitsDepth, // P => depth of lpUnits uint256 amount, // a => assets incoming @@ -137,6 +151,7 @@ library PoolLogicLib { return lpUnitsDepth * (num / den); } + // @note not used anymore , comment out and check for breaking changes function calculateDUnitsToMint( uint256 amount, uint256 reserveA, @@ -151,44 +166,19 @@ library PoolLogicLib { return initialDToMint; } - return reserveD.wmul(amount).wdiv(reserveA); + return (reserveD * amount) / reserveA; } - function getExecutionPriceLower(uint256 executionPrice, uint256 pricePrecision) public pure returns (uint256) { + function getExecutionPriceKey(uint256 executionPrice, uint256 pricePrecision) public pure returns (uint256) { uint256 mod = executionPrice % pricePrecision; // @audit decide decimals for precission + use global variable // for precission return executionPrice - mod; } - function getReciprocalOppositePrice( - uint256 executionPrice, - uint256 reserveA_In, - uint8 decimals_In, - uint8 decimals_Out - ) - public - pure - returns (uint256) - { - uint256 reserveA_Out = getOtherReserveFromPrice(executionPrice, reserveA_In, decimals_In, decimals_Out); - return getExecutionPrice(reserveA_Out, reserveA_In, decimals_Out, decimals_In); - } - - function getOtherReserveFromPrice( - uint256 executionPrice, - uint256 reserveA_In, - uint8 decimals_In, - uint8 decimals_Out - ) - public - pure - returns (uint256) - { - return (reserveA_In.scaleAmountToDecimals(decimals_In, 18).wdiv(executionPrice)).scaleAmountToDecimals( - 18, decimals_Out - ); // @audit confirm scaling - } - + // @audit is it gas efficient to pass decimals as inputs + // @audit ensure decimals are passed in securely on internal calls + // @audit is a high level check approach for all non-18 tokens a better way to avoid utilising scaling + // considerations on every stream execution function calculateAmountOutFromPrice( uint256 amountIn, uint256 price, @@ -238,4 +228,8 @@ library PoolLogicLib { // Scale back to TokenA decimals return scaledAmountIn.scaleAmountToDecimals(18, decimalsIn); } + + function getOppositePrice(uint256 price) public pure returns (uint256) { + return DSMath.WAD.wdiv(price); + } } diff --git a/test/integration/concrete/router/RouterSwapLimit.t.sol b/test/integration/concrete/router/RouterSwapLimit.t.sol index 9a92418..bf4a6a6 100644 --- a/test/integration/concrete/router/RouterSwapLimit.t.sol +++ b/test/integration/concrete/router/RouterSwapLimit.t.sol @@ -48,7 +48,7 @@ contract Router_SwapLimit is RouterTest { uint256 limitOrderPrice = currentExecPrice + poolLogic.MAX_LIMIT_TICKS() * poolLogic.PRICE_PRECISION() + 1; - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(currentExecPrice, poolLogic.PRICE_PRECISION()); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(currentExecPrice, poolLogic.PRICE_PRECISION()); uint256 swapperTokenABalance_beforeSwap = tokenA.balanceOf(owner); uint256 poolTokenABalance_beforeSwap = tokenA.balanceOf(address(pool)); @@ -106,7 +106,7 @@ contract Router_SwapLimit is RouterTest { uint256 swapperTokenBBalance_afterSwap = tokenB.balanceOf(owner); uint256 poolTokenBBalance_afterSwap = tokenB.balanceOf(address(pool)); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(executionPrice, poolLogic.PRICE_PRECISION()); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(executionPrice, poolLogic.PRICE_PRECISION()); Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, true); assertEq(swaps.length, 1); Swap memory swap = swaps[0]; @@ -178,26 +178,26 @@ contract Router_SwapLimit is RouterTest { router.swapLimitOrder(address(tokenA), address(tokenB), tokenASwapAmount, limitOrderPrice); vm.stopPrank(); - // uint256 swapperTokenABalance_afterSwap = tokenA.balanceOf(owner); - // uint256 poolTokenABalance_afterSwap = tokenA.balanceOf(address(pool)); - // uint256 poolTokenBBalance_afterSwap = tokenB.balanceOf(address(pool)); + uint256 swapperTokenABalance_afterSwap = tokenA.balanceOf(owner); + uint256 poolTokenABalance_afterSwap = tokenA.balanceOf(address(pool)); + uint256 poolTokenBBalance_afterSwap = tokenB.balanceOf(address(pool)); - (uint256 reserveD_tokenA_afterSwap,,,,,,) = pool.poolInfo(address(tokenA)); - (uint256 reserveD_tokenB_afterSwap,,,,,,) = pool.poolInfo(address(tokenB)); + (uint256 reserveD_tokenA_afterSwap,, uint256 reserveA_tokenA_afterSwap,,,,) = pool.poolInfo(address(tokenA)); + (uint256 reserveD_tokenB_afterSwap,, uint256 reserveA_tokenB_afterSwap,,,,) = pool.poolInfo(address(tokenB)); uint256 marketPriceAfterSwap = _getCurrentPrice(address(tokenA), address(tokenB)); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(limitOrderPrice, poolLogic.PRICE_PRECISION()); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(limitOrderPrice, poolLogic.PRICE_PRECISION()); Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, true); - // assertEq(swaps.length, 0); - // assertEq(reserveA_tokenA_beforeSwap, reserveA_tokenA_afterSwap - swapPerStream); - // assertEq(reserveA_tokenB_beforeSwap, reserveA_tokenB_afterSwap + tokenBAmountOut); - // assertEq(reserveD_tokenA_afterSwap, reserveD_tokenA_beforeSwap - dToUpdate); - // assertEq(reserveD_tokenB_afterSwap, reserveD_tokenB_beforeSwap + dToUpdate); - // assertGt(marketPriceAfterSwap, marketPriceBeforeSwap); - // assertEq(swapperTokenABalance_afterSwap, swapperTokenABalance_beforeSwap - tokenASwapAmount); - // assertEq(poolTokenABalance_afterSwap, poolTokenABalance_beforeSwap + tokenASwapAmount); - // assertEq(poolTokenBBalance_afterSwap, poolTokenBBalance_beforeSwap - tokenBAmountOut); + assertEq(swaps.length, 0); + assertEq(reserveA_tokenA_beforeSwap, reserveA_tokenA_afterSwap - swapPerStream); + assertEq(reserveA_tokenB_beforeSwap, reserveA_tokenB_afterSwap + tokenBAmountOut); + assertEq(reserveD_tokenA_afterSwap, reserveD_tokenA_beforeSwap - dToUpdate); + assertEq(reserveD_tokenB_afterSwap, reserveD_tokenB_beforeSwap + dToUpdate); + assertGt(marketPriceAfterSwap, marketPriceBeforeSwap); + assertEq(swapperTokenABalance_afterSwap, swapperTokenABalance_beforeSwap - tokenASwapAmount); + assertEq(poolTokenABalance_afterSwap, poolTokenABalance_beforeSwap + tokenASwapAmount); + assertEq(poolTokenBBalance_afterSwap, poolTokenBBalance_beforeSwap - tokenBAmountOut); } /** @@ -228,8 +228,8 @@ contract Router_SwapLimit is RouterTest { assertGt(streamCount, 1); // now let's add the opposite swap to the streaming queue - (,, uint256 reserveA_tokenA,,,,) = pool.poolInfo(address(tokenA)); - (,, uint256 reserveA_tokenB,,,,) = pool.poolInfo(address(tokenB)); + (,, uint256 reserveA_tokenA,,,, uint8 tokenADecimals) = pool.poolInfo(address(tokenA)); + (,, uint256 reserveA_tokenB,,,, uint8 tokenBdecimals) = pool.poolInfo(address(tokenB)); uint256 executionPriceOppositeSwap = _getCurrentPrice(address(tokenB), address(tokenA)); // we sub the price by 10% less executionPriceOppositeSwap -= 3 * poolLogic.PRICE_PRECISION(); @@ -239,16 +239,10 @@ contract Router_SwapLimit is RouterTest { } uint256 oppExecutionPriceKey = - PoolLogicLib.getExecutionPriceLower(executionPriceOppositeSwap, poolLogic.PRICE_PRECISION()); + PoolLogicLib.getExecutionPriceKey(executionPriceOppositeSwap, poolLogic.PRICE_PRECISION()); Swap[] memory oppositeSwaps = pool.orderBook(oppositePairId, oppExecutionPriceKey, true); - assertTrue(oppositeSwaps.length > 0); - - (,,,,,, uint8 tokenADecimals) = pool.poolInfo(address(tokenA)); - (,,,,,, uint8 tokenBdecimals) = pool.poolInfo(address(tokenB)); - uint256 reserveAInFromPrice = PoolLogicLib.getOtherReserveFromPrice( - executionPriceOppositeSwap, reserveA_tokenB, tokenBdecimals, tokenADecimals - ); + assertTrue(oppositeSwaps.length == oppositeSwapsCount); // we want to know how many tokenAAmountOut is needed to fully execute the opposite swaps uint256 tokenOutAmountIn; @@ -256,14 +250,21 @@ contract Router_SwapLimit is RouterTest { for (uint256 i = 0; i < oppositeSwaps.length; i++) { Swap memory oppositeSwap = oppositeSwaps[i]; tokenOutAmountIn += oppositeSwap.swapAmountRemaining; // tokenBAmount - tokenInAmountOut += - PoolLogicLib.getAmountOut(oppositeSwap.swapAmountRemaining, reserveA_tokenB, reserveAInFromPrice); + tokenInAmountOut += PoolLogicLib.calculateAmountInFromPrice( + oppositeSwap.swapAmountRemaining, oppositeSwap.executionPrice, tokenADecimals, tokenBdecimals + ); } - uint256 executionPrice = PoolLogicLib.getReciprocalOppositePrice( - executionPriceOppositeSwap, reserveA_tokenB, tokenBdecimals, tokenADecimals - ); + console.log("total tokenOutAmountIn (token B comming in)", tokenOutAmountIn); + console.log("total tokenInAmountOut (token A expected to go out)", tokenInAmountOut); uint256 swapTokenAAmountIn = tokenInAmountOut - 1 * 10 ** tokenA.decimals(); + // now we need to modify the execution price + vm.startPrank(owner); + tokenA.approve(address(router), swapTokenAAmountIn); + router.swapMarketOrder(address(tokenA), address(tokenB), swapTokenAAmountIn); + vm.stopPrank(); + + uint256 executionPrice = _getCurrentPrice(address(tokenA), address(tokenB)) + 1 * poolLogic.PRICE_PRECISION(); uint256 _streamCount = poolLogic.getStreamCount(address(tokenA), address(tokenB), swapTokenAAmountIn); uint256 _swapPerStream = swapTokenAAmountIn / _streamCount; @@ -271,21 +272,15 @@ contract Router_SwapLimit is RouterTest { if (swapTokenAAmountIn % _streamCount != 0) dust = (swapTokenAAmountIn - (_streamCount * _swapPerStream)); uint256 swapTokenBAmountOut; - // we loop through the opposite swaps to get the expected amount of tokenB we will receive, after fully execute - // our frontSwap swap uint256 tokenInCalculation = swapTokenAAmountIn; for (uint256 i = 0; i < oppositeSwaps.length; i++) { uint256 t = tokenInCalculation; Swap memory oppositeSwap = oppositeSwaps[i]; - uint256 reserveAInFromPrice = PoolLogicLib.getOtherReserveFromPrice( - executionPriceOppositeSwap, reserveA_tokenB, tokenBdecimals, tokenADecimals - ); - uint256 reserveAOutFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPrice, reserveA_tokenA, tokenADecimals, tokenBdecimals); uint256 oppSwapTokenAmount = oppositeSwap.swapAmountRemaining + oppositeSwap.dustTokenAmount; - uint256 oppTokenInAmountOut = - PoolLogicLib.getAmountOut(oppSwapTokenAmount, reserveA_tokenB, reserveAInFromPrice); + uint256 oppTokenInAmountOut = PoolLogicLib.calculateAmountInFromPrice( + oppSwapTokenAmount, oppositeSwap.executionPrice, tokenADecimals, tokenBdecimals + ); if (t > oppTokenInAmountOut) { swapTokenBAmountOut += oppSwapTokenAmount; @@ -300,7 +295,8 @@ contract Router_SwapLimit is RouterTest { } } } else { - swapTokenBAmountOut += PoolLogicLib.getAmountOut(t, reserveA_tokenA, reserveAOutFromPrice); + swapTokenBAmountOut += + PoolLogicLib.calculateAmountOutFromPrice(t, executionPrice, tokenADecimals, tokenBdecimals); break; } tokenInCalculation = t; @@ -319,13 +315,12 @@ contract Router_SwapLimit is RouterTest { uint256 swaperTokenABalance_afterSwap = tokenA.balanceOf(owner); uint256 swaperTokenBBalance_afterSwap = tokenB.balanceOf(owner); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(executionPrice, poolLogic.PRICE_PRECISION()); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(executionPrice, poolLogic.PRICE_PRECISION()); Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, true); assertTrue(swaps.length == 0); assertEq(swaperTokenABalance_afterSwap, swaperTokenABalance_beforeSwap - swapTokenAAmountIn); - assertGt(swaperTokenBBalance_afterSwap, swaperTokenBBalance_beforeSwap); // @audit there's mismatch between the - // expected amount of tokenB received and the actual amount very minute + assertGt(swaperTokenBBalance_afterSwap, swaperTokenBBalance_beforeSwap); } function test_swapLimitOrder_fullyExecuteSwapWithBothOppositeSwapsAndReserves() public { @@ -352,7 +347,7 @@ contract Router_SwapLimit is RouterTest { console.log("swapPerStream", oppSwapPerStream); console.log("oppDust", oppDust); - // // 2. we create the swap with the inversed price order book to totally consume all the opposite swaps + // // 2. we create the swap with the inversed price order book to totally consume all the oppositeswaps // oppExecution price is the price of the opposite swap (uint256 reserveD_tokenA,, uint256 reserveA_tokenA,,,, uint8 decimalsA) = pool.poolInfo(address(tokenA)); @@ -367,28 +362,30 @@ contract Router_SwapLimit is RouterTest { router.swapLimitOrder(address(tokenB), address(tokenA), oppSwapAmount, executionPriceOppositeSwap); } + // here we need to change the pool price with a swap + uint256 amount = 1 * 10 ** (tokenA.decimals() - 5); + vm.startPrank(owner); + tokenA.approve(address(router), amount); + router.swapMarketOrder(address(tokenA), address(tokenB), amount); + vm.stopPrank(); + // at this stage we have all the opp swaps in the rounded oppPrice order book uint256 oppExecutionPriceKey = - PoolLogicLib.getExecutionPriceLower(executionPriceOppositeSwap, poolLogic.PRICE_PRECISION()); + PoolLogicLib.getExecutionPriceKey(executionPriceOppositeSwap, poolLogic.PRICE_PRECISION()); Swap[] memory oppositeSwaps = pool.orderBook(oppositePairId, oppExecutionPriceKey, true); assertEq(oppositeSwaps.length, oppositeSwapsCount); // we calculate the amount of tokenA we need to fully execute the opposite swaps - uint256 reserveAInFromPrice = - PoolLogicLib.getOtherReserveFromPrice(executionPriceOppositeSwap, reserveA_tokenB, decimalsB, decimalsA); uint256 tokenBInOppSwaps = oppSwapAmount * oppositeSwapsCount; // oppSwapAmount contains the oppDustToken amount uint256 tokenAOutOppSwaps; for (uint256 i = 0; i < oppositeSwapsCount; i++) { - tokenAOutOppSwaps += PoolLogicLib.getAmountOut(oppSwapAmount, reserveA_tokenB, reserveAInFromPrice); + tokenAOutOppSwaps += + PoolLogicLib.calculateAmountInFromPrice(oppSwapAmount, executionPriceOppositeSwap, decimalsA, decimalsB); } - // we get the price of our swap based on the inversed price of opposite swaps - uint256 swapExecutionPrice = - PoolLogicLib.getReciprocalOppositePrice(executionPriceOppositeSwap, reserveA_tokenB, decimalsB, decimalsA); - uint256 tokenAForPoolReserveSwap = 1 * 10 ** (tokenA.decimals() - 3); uint256 swapAmount = tokenAOutOppSwaps + tokenAForPoolReserveSwap; @@ -401,6 +398,8 @@ contract Router_SwapLimit is RouterTest { } uint256 currentPrice = _getCurrentPrice(address(tokenA), address(tokenB)); + uint256 swapExecutionPrice = + _getCurrentPrice(address(tokenA), address(tokenB)) + 3 * poolLogic.PRICE_PRECISION(); uint256 expectedAmountOut = PoolLogicLib.calculateAmountOutFromPrice(swapPerStream, swapExecutionPrice, decimalsA, decimalsB); @@ -430,7 +429,7 @@ contract Router_SwapLimit is RouterTest { uint256 poolTokenABalance_afterSwap = tokenA.balanceOf(address(pool)); uint256 poolTokenBBalance_afterSwap = tokenB.balanceOf(address(pool)); - uint256 executionPriceKey = PoolLogicLib.getExecutionPriceLower(swapExecutionPrice, poolLogic.PRICE_PRECISION()); + uint256 executionPriceKey = PoolLogicLib.getExecutionPriceKey(swapExecutionPrice, poolLogic.PRICE_PRECISION()); Swap[] memory _oppositeSwaps = pool.orderBook(oppositePairId, oppExecutionPriceKey, true); Swap[] memory swaps = pool.orderBook(pairId, executionPriceKey, true); diff --git a/test/integration/invariants/Handler.t.sol b/test/integration/invariants/Handler.t.sol index 8660b01..06fe2fb 100644 --- a/test/integration/invariants/Handler.t.sol +++ b/test/integration/invariants/Handler.t.sol @@ -50,7 +50,7 @@ contract Handler is Test { MockERC20 tokenOut = tokenIn == tokenA ? tokenB : tokenA; amountIn = bound(amountIn, 1, MAX_DEPOSIT_SIZE); uint256 executionPrice = _getCurrentPrice(address(tokenIn), address(tokenOut)); - uint256 executionpriceDelta = bound(seed, 0, executionPrice / 10); + uint256 executionpriceDelta = bound(seed, 1, executionPrice / 10); bool addDelta = _getBoolFromSeed(seed); executionPrice = addDelta ? executionPrice + executionpriceDelta : executionPrice - executionpriceDelta; @@ -58,7 +58,7 @@ contract Handler is Test { tokenIn.mint(msg.sender, amountIn); tokenIn.approve(address(router), amountIn); - router.swap(address(tokenIn), address(tokenOut), amountIn, executionPrice); + router.swapLimitOrder(address(tokenIn), address(tokenOut), amountIn, executionPrice); vm.stopPrank(); }