From 84e5e6bf3a3cfc0d5861a1fb1e11fa0568e54080 Mon Sep 17 00:00:00 2001 From: marsclimber0109 Date: Fri, 26 Apr 2024 01:15:46 +0900 Subject: [PATCH] chore: minor typo revision --- contracts/mixins/LiquidityCurve.sol | 300 +++++++++----- contracts/mixins/MarketSequencer.sol | 372 +++++++++++------ contracts/mixins/TradeMatcher.sol | 586 ++++++++++++++++++--------- 3 files changed, 818 insertions(+), 440 deletions(-) diff --git a/contracts/mixins/LiquidityCurve.sol b/contracts/mixins/LiquidityCurve.sol index 345a4fb0..df280375 100644 --- a/contracts/mixins/LiquidityCurve.sol +++ b/contracts/mixins/LiquidityCurve.sol @@ -3,14 +3,14 @@ pragma solidity 0.8.19; pragma experimental ABIEncoderV2; -import '../libraries/TickMath.sol'; -import '../libraries/FixedPoint.sol'; -import '../libraries/LiquidityMath.sol'; -import '../libraries/SafeCast.sol'; -import '../libraries/PoolSpecs.sol'; -import '../libraries/CurveMath.sol'; -import '../libraries/CurveCache.sol'; -import './StorageLayout.sol'; +import "../libraries/TickMath.sol"; +import "../libraries/FixedPoint.sol"; +import "../libraries/LiquidityMath.sol"; +import "../libraries/SafeCast.sol"; +import "../libraries/PoolSpecs.sol"; +import "../libraries/CurveMath.sol"; +import "../libraries/CurveCache.sol"; +import "./StorageLayout.sol"; /* @title Liquidity Curve Mixin * @notice Tracks the state of the locally stable constant product AMM liquid curve @@ -26,83 +26,101 @@ contract LiquidityCurve is StorageLayout { using CurveMath for CurveMath.CurveState; /* @notice Copies the current state of the curve in EVM storage to a memory clone. - * @dev Use for light-weight gas ergonomics when iterarively operating on the + * @dev Use for light-weight gas ergonomics when iteratively operating on the * curve. But it's the callers responsibility to persist the changes back * to storage when complete. */ - function snapCurve (bytes32 poolIdx) view internal returns - (CurveMath.CurveState memory curve) { + function snapCurve( + bytes32 poolIdx + ) internal view returns (CurveMath.CurveState memory curve) { curve = curves_[poolIdx]; require(curve.priceRoot_ > 0); } /* @notice Snapshots the curve for pool initialization operation. * @dev This only skips the initialization check from snapCurve() does *not* assert - * that the curve was not previously initialized. That's the caller's + * that the curve was not previously initialized. That's the caller's * responsibility */ - function snapCurveInit (bytes32 poolIdx) view internal returns - (CurveMath.CurveState memory) { + function snapCurveInit( + bytes32 poolIdx + ) internal view returns (CurveMath.CurveState memory) { return curves_[poolIdx]; } /* @notice Snapshots the curve to memory, but verifies that the price occurs within * a pre-specified price range. If not, reverts the entire transaction. */ - function snapCurveInRange (bytes32 poolIdx, uint128 minPrice, - uint128 maxPrice) view internal returns - (CurveMath.CurveState memory curve) { + function snapCurveInRange( + bytes32 poolIdx, + uint128 minPrice, + uint128 maxPrice + ) internal view returns (CurveMath.CurveState memory curve) { curve = snapCurve(poolIdx); - require(curve.priceRoot_ >= minPrice && curve.priceRoot_ <= maxPrice, "RC"); + require( + curve.priceRoot_ >= minPrice && curve.priceRoot_ <= maxPrice, + "RC" + ); } - /* @notice Writes a CurveState modified in memory back into persistent storage. + /* @notice Writes a CurveState modified in memory back into persistent storage. * Use for the working copy from snapCurve when finalized. */ - function commitCurve (bytes32 poolIdx, CurveMath.CurveState memory curve) - internal { + function commitCurve( + bytes32 poolIdx, + CurveMath.CurveState memory curve + ) internal { curves_[poolIdx] = curve; } - + /* @notice Called whenever a user adds a fixed amount of concentrated liquidity * to the curve. This must be called regardless of whether the liquidity is * in-range at the current curve price or not. - * @dev After being called this will alter the curve to reflect the new liquidity, - * but it's the callers responsibility to make sure that the required + * @dev After being called this will alter the curve to reflect the new liquidity, + * but it's the callers responsibility to make sure that the required * collateral is actually collected. * * @param curve The liquidity curve object that range liquidity will be added to. * @param liquidity The amount of liquidity being added. Represented in the form of * sqrt(X*Y) where X,Y are the virtual reserves of the tokens in a * constant product AMM. Calculate the same whether in-range or not. - * @param lowerTick The tick index corresponding to the bottom of the concentrated + * @param lowerTick The tick index corresponding to the bottom of the concentrated * liquidity range. - * @param upperTick The tick index corresponding to the bottom of the concentrated + * @param upperTick The tick index corresponding to the bottom of the concentrated * liquidity range. * - * @return base - The amount of base token collateral that must be collected + * @return base - The amount of base token collateral that must be collected * following the addition of this liquidity. - * @return quote - The amount of quote token collateral that must be collected + * @return quote - The amount of quote token collateral that must be collected * following the addition of this liquidity. */ - function liquidityReceivable (CurveMath.CurveState memory curve, uint128 liquidity, - int24 lowerTick, int24 upperTick) - internal pure returns (uint128, uint128) { - (uint128 base, uint128 quote, bool inRange) = - liquidityFlows(curve.priceRoot_, liquidity, lowerTick, upperTick); + function liquidityReceivable( + CurveMath.CurveState memory curve, + uint128 liquidity, + int24 lowerTick, + int24 upperTick + ) internal pure returns (uint128, uint128) { + (uint128 base, uint128 quote, bool inRange) = liquidityFlows( + curve.priceRoot_, + liquidity, + lowerTick, + upperTick + ); bumpConcentrated(curve, liquidity, inRange); return chargeConservative(base, quote, inRange); } - /* @notice Equivalent to above, but used when adding non-range bound constant + /* @notice Equivalent to above, but used when adding non-range bound constant * product ambient liquidity. - * @dev Like above, it's the caller's responsibility to collect the necessary + * @dev Like above, it's the caller's responsibility to collect the necessary * collateral to add to the pool. * * @param curve The liquidity curve object that ambient liquidity will be added to. - * @param seeds The number of ambient seeds being added. Note that this is + * @param seeds The number of ambient seeds being added. Note that this is * denominated as seeds *not* liquidity. The amount of liquidity * contributed will be based on the current seed->liquidity conversion - * rate on the curve. (See CurveMath.sol.) + * rate on the curve. (See CurveMath.sol. liq = seed * deflator) * @return The base and quote token flows from the user required to add this amount * of liquidity to the curve. */ - function liquidityReceivable (CurveMath.CurveState memory curve, uint128 seeds) - internal pure returns (uint128, uint128) { + function liquidityReceivable( + CurveMath.CurveState memory curve, + uint128 seeds + ) internal pure returns (uint128, uint128) { (uint128 base, uint128 quote) = liquidityFlows(curve, seeds); bumpAmbient(curve, seeds); return chargeConservative(base, quote, true); @@ -112,51 +130,67 @@ contract LiquidityCurve is StorageLayout { * accordingly and calculates the amount of collateral payable to the user. * This must be called for all removes regardless of whether the liquidity * is in range or not. - * @dev It's the caller's responsibility to actually return the collateral to the + * @dev It's the caller's responsibility to actually return the collateral to the * user. This method will only calculate what's owed, but won't actually pay it. * - * - * @param curve The liquidity curve object that concentrated liquidity will be + * + * @param curve The liquidity curve object that concentrated liquidity will be * removed from. * @param liquidity The amount of liquidity being removed, whether in-range or not. * Represented in the form of sqrt(X*Y) where x,Y are the virtual * reserves of a constant product AMM. * @param rewardRate The total cumulative earned but unclaimed rewards on the staked * liquidity. Used to increment the payout with the rewards, and - * burn the ambient liquidity tied to the rewards. (See + * burn the ambient liquidity tied to the rewards. (See * CurveMath.sol for more.) Represented as a 128-bit fixed point * cumulative growth rate of ambient seeds per unit of liquidity. - * @param lowerTick The tick index corresponding to the bottom of the concentrated + * @param lowerTick The tick index corresponding to the bottom of the concentrated * liquidity range. - * @param upperTick The tick index corresponding to the bottom of the concentrated + * @param upperTick The tick index corresponding to the bottom of the concentrated * liquidity range. * * @return base - The amount of base token collateral that can be paid out following - * the removal of the liquidity. Always rounded down to favor + * the removal of the liquidity. Always rounded down to favor * collateral stability. * @return quote - The amount of base token collateral that can be paid out following - * the removal of the liquidity. Always rounded down to favor + * the removal of the liquidity. Always rounded down to favor * collateral stability. */ - function liquidityPayable (CurveMath.CurveState memory curve, uint128 liquidity, - uint64 rewardRate, int24 lowerTick, int24 upperTick) - internal pure returns (uint128 base, uint128 quote) { - (base, quote) = liquidityPayable(curve, liquidity, lowerTick, upperTick); + function liquidityPayable( + CurveMath.CurveState memory curve, + uint128 liquidity, + uint64 rewardRate, + int24 lowerTick, + int24 upperTick + ) internal pure returns (uint128 base, uint128 quote) { + (base, quote) = liquidityPayable( + curve, + liquidity, + lowerTick, + upperTick + ); (base, quote) = stackRewards(base, quote, curve, liquidity, rewardRate); - } + } - function stackRewards (uint128 base, uint128 quote, - CurveMath.CurveState memory curve, - uint128 liquidity, uint64 rewardRate) - internal pure returns (uint128, uint128) { + function stackRewards( + uint128 base, + uint128 quote, + CurveMath.CurveState memory curve, + uint128 liquidity, + uint64 rewardRate + ) internal pure returns (uint128, uint128) { if (rewardRate > 0) { // Round down reward sees on payout, in contrast to rounding them up on - // incremental accumulation (see CurveAssimilate.sol). This mathematicaly + // incremental accumulation (see CurveAssimilate.sol). This mathematically // guarantees that we never try to burn more tokens than exist on the curve. - uint128 rewards = FixedPoint.mulQ48(liquidity, rewardRate).toUint128By144(); - + uint128 rewards = FixedPoint + .mulQ48(liquidity, rewardRate) + .toUint128By144(); + if (rewards > 0) { - (uint128 baseRewards, uint128 quoteRewards) = - liquidityPayable(curve, rewards); + (uint128 baseRewards, uint128 quoteRewards) = liquidityPayable( + curve, + rewards + ); base += baseRewards; quote += quoteRewards; } @@ -164,50 +198,65 @@ contract LiquidityCurve is StorageLayout { return (base, quote); } - /* @notice The same as the above liquidityPayable() but called when accumulated + /* @notice The same as the above liquidityPayable() but called when accumulated * rewards are zero. */ - function liquidityPayable (CurveMath.CurveState memory curve, uint128 liquidity, - int24 lowerTick, int24 upperTick) - internal pure returns (uint128 base, uint128 quote) { + function liquidityPayable( + CurveMath.CurveState memory curve, + uint128 liquidity, + int24 lowerTick, + int24 upperTick + ) internal pure returns (uint128 base, uint128 quote) { bool inRange; - (base, quote, inRange) = liquidityFlows(curve.priceRoot_, liquidity, - lowerTick, upperTick); + (base, quote, inRange) = liquidityFlows( + curve.priceRoot_, + liquidity, + lowerTick, + upperTick + ); bumpConcentrated(curve, -(liquidity.toInt128Sign()), inRange); } /* @notice Same as above liquidityPayable() but used for non-range based ambient * constant product liquidity. * - * @param curve The liquidity curve object that ambient liquidity will be + * @param curve The liquidity curve object that ambient liquidity will be * removed from. - * @param seeds The number of ambient seeds being added. Note that this is + * @param seeds The number of ambient seeds being added. Note that this is * denominated as seeds *not* liquidity. The amount of liquidity * contributed will be based on the current seed->liquidity conversion - * rate on the curve. (See CurveMath.sol.) + * rate on the curve. (See CurveMath.sol. liq = seed * deflator) * @return base - The amount of base token collateral that can be paid out following - * the removal of the liquidity. Always rounded down to favor + * the removal of the liquidity. Always rounded down to favor * collateral stability. * @return quote - The amount of base token collateral that can be paid out following - * the removal of the liquidity. Always rounded down to favor + * the removal of the liquidity. Always rounded down to favor * collateral stability. */ - function liquidityPayable (CurveMath.CurveState memory curve, uint128 seeds) - internal pure returns (uint128 base, uint128 quote) { + function liquidityPayable( + CurveMath.CurveState memory curve, + uint128 seeds + ) internal pure returns (uint128 base, uint128 quote) { (base, quote) = liquidityFlows(curve, seeds); bumpAmbient(curve, -(seeds.toInt128Sign())); } - function liquidityHeldPayable (CurveMath.CurveState memory curve, uint128 liquidity, - uint64 rewards, KnockoutLiq.KnockoutPosLoc memory loc) - internal pure returns (uint128 base, uint128 quote) { + function liquidityHeldPayable( + CurveMath.CurveState memory curve, + uint128 liquidity, + uint64 rewards, + KnockoutLiq.KnockoutPosLoc memory loc + ) internal pure returns (uint128 base, uint128 quote) { (base, quote) = liquidityHeldPayable(liquidity, loc); (base, quote) = stackRewards(base, quote, curve, liquidity, rewards); } - function liquidityHeldPayable (uint128 liquidity, - KnockoutLiq.KnockoutPosLoc memory loc) - internal pure returns (uint128 base, uint128 quote) { - (uint128 bidPrice, uint128 askPrice) = translateTickRange - (loc.lowerTick_, loc.upperTick_); + function liquidityHeldPayable( + uint128 liquidity, + KnockoutLiq.KnockoutPosLoc memory loc + ) internal pure returns (uint128 base, uint128 quote) { + (uint128 bidPrice, uint128 askPrice) = translateTickRange( + loc.lowerTick_, + loc.upperTick_ + ); if (loc.isBid_) { quote = liquidity.deltaQuote(bidPrice, askPrice); } else { @@ -216,44 +265,62 @@ contract LiquidityCurve is StorageLayout { } /* @notice Directly increments the ambient liquidity on the curve. */ - function bumpAmbient (CurveMath.CurveState memory curve, uint128 seedDelta) - private pure { + function bumpAmbient( + CurveMath.CurveState memory curve, + uint128 seedDelta + ) private pure { bumpAmbient(curve, seedDelta.toInt128Sign()); } /* @notice Directly increments the ambient liquidity on the curve. */ - function bumpAmbient (CurveMath.CurveState memory curve, int128 seedDelta) - private pure { + function bumpAmbient( + CurveMath.CurveState memory curve, + int128 seedDelta + ) private pure { curve.ambientSeeds_ = curve.ambientSeeds_.addDelta(seedDelta); } /* @notice Directly increments the concentrated liquidity on the curve, depending * on whether it's in range. */ - function bumpConcentrated (CurveMath.CurveState memory curve, - uint128 liqDelta, bool inRange) private pure { + function bumpConcentrated( + CurveMath.CurveState memory curve, + uint128 liqDelta, + bool inRange + ) private pure { bumpConcentrated(curve, liqDelta.toInt128Sign(), inRange); } /* @notice Directly increments the concentrated liquidity on the curve, depending - * on whether it's in range. */ - function bumpConcentrated (CurveMath.CurveState memory curve, - int128 liqDelta, bool inRange) private pure { + * on whether it's in range. */ + function bumpConcentrated( + CurveMath.CurveState memory curve, + int128 liqDelta, + bool inRange + ) private pure { if (inRange) { curve.concLiq_ = curve.concLiq_.addDelta(liqDelta); } } - /* @notice Calculates the liquidity flows associated with the concentrated liquidity * from a range order. * @dev Uses fixed-point math that rounds down up to 2 wei from the true real valued * flows. Safe to pay this flow, but when pool is receiving caller must make sure * to round up for collateral safety. */ - function liquidityFlows (uint128 price, uint128 liquidity, - int24 bidTick, int24 askTick) - private pure returns (uint128 baseDebit, uint128 quoteDebit, bool inRange) { - (uint128 bidPrice, uint128 askPrice) = - translateTickRange(bidTick, askTick); + function liquidityFlows( + uint128 price, + uint128 liquidity, + int24 bidTick, + int24 askTick + ) + private + pure + returns (uint128 baseDebit, uint128 quoteDebit, bool inRange) + { + (uint128 bidPrice, uint128 askPrice) = translateTickRange( + bidTick, + askTick + ); if (price < bidPrice) { quoteDebit = liquidity.deltaQuote(bidPrice, askPrice); @@ -267,13 +334,15 @@ contract LiquidityCurve is StorageLayout { } /* @notice Calculates the liquidity flows associated with the concentrated liquidity - * from a range order. + * from a range order. * @dev Uses fixed-point math that rounds down at each division. Because there are * divisions, max precision loss is under 2 wei. Safe to pay this flow, but when - * when pool is receiving, caller must make sure to round up for collateral + * when pool is receiving, caller must make sure to round up for collateral * safety. */ - function liquidityFlows (CurveMath.CurveState memory curve, uint128 seeds) - private pure returns (uint128 baseDebit, uint128 quoteDebit) { + function liquidityFlows( + CurveMath.CurveState memory curve, + uint128 seeds + ) private pure returns (uint128 baseDebit, uint128 quoteDebit) { uint128 liq = CompoundMath.inflateLiqSeed(seeds, curve.seedDeflator_); baseDebit = FixedPoint.mulQ64(liq, curve.priceRoot_).toUint128By192(); quoteDebit = FixedPoint.divQ64(liq, curve.priceRoot_).toUint128By192(); @@ -281,22 +350,26 @@ contract LiquidityCurve is StorageLayout { /* @notice Called exactly once at the initializing of the pool. Initializes the * liquidity curve at an arbitrary price. - * @dev Throws error if price was already initialized. + * @dev Throws error if price was already initialized. * * @param curve The liquidity curve for the pool being initialized. * @param priceRoot - Square root of the price. Represented as Q64.64 fixed point. */ - function initPrice (CurveMath.CurveState memory curve, uint128 priceRoot) - internal pure { + function initPrice( + CurveMath.CurveState memory curve, + uint128 priceRoot + ) internal pure { int24 tick = TickMath.getTickAtSqrtRatio(priceRoot); require(tick >= TickMath.MIN_TICK && tick <= TickMath.MAX_TICK, "R"); - + require(curve.priceRoot_ == 0, "N"); curve.priceRoot_ = priceRoot; } /* @notice Converts a price tick index range into a range of prices. */ - function translateTickRange (int24 lowerTick, int24 upperTick) - private pure returns (uint128 bidPrice, uint128 askPrice) { + function translateTickRange( + int24 lowerTick, + int24 upperTick + ) private pure returns (uint128 bidPrice, uint128 askPrice) { require(upperTick > lowerTick); require(lowerTick >= TickMath.MIN_TICK); require(upperTick <= TickMath.MAX_TICK); @@ -311,9 +384,14 @@ contract LiquidityCurve is StorageLayout { /* @notice Rounds liquidity flows up in cases where we want to be conservative with * collateral. */ - function chargeConservative (uint128 liqBase, uint128 liqQuote, bool inRange) - private pure returns (uint128, uint128) { - return ((liqBase > 0 || inRange) ? liqBase + TOKEN_ROUND : 0, - (liqQuote > 0 || inRange) ? liqQuote + TOKEN_ROUND : 0); + function chargeConservative( + uint128 liqBase, + uint128 liqQuote, + bool inRange + ) private pure returns (uint128, uint128) { + return ( + (liqBase > 0 || inRange) ? liqBase + TOKEN_ROUND : 0, + (liqQuote > 0 || inRange) ? liqQuote + TOKEN_ROUND : 0 + ); } } diff --git a/contracts/mixins/MarketSequencer.sol b/contracts/mixins/MarketSequencer.sol index 1d73d1ed..e58eaca9 100644 --- a/contracts/mixins/MarketSequencer.sol +++ b/contracts/mixins/MarketSequencer.sol @@ -2,24 +2,23 @@ pragma solidity 0.8.19; -import '../libraries/Directives.sol'; -import '../libraries/PoolSpecs.sol'; -import '../libraries/PriceGrid.sol'; -import '../libraries/SwapCurve.sol'; -import '../libraries/CurveMath.sol'; -import '../libraries/CurveRoll.sol'; -import '../libraries/CurveCache.sol'; -import '../libraries/Chaining.sol'; -import './PositionRegistrar.sol'; -import './LiquidityCurve.sol'; -import './LevelBook.sol'; -import './TradeMatcher.sol'; +import "../libraries/Directives.sol"; +import "../libraries/PoolSpecs.sol"; +import "../libraries/PriceGrid.sol"; +import "../libraries/SwapCurve.sol"; +import "../libraries/CurveMath.sol"; +import "../libraries/CurveRoll.sol"; +import "../libraries/CurveCache.sol"; +import "../libraries/Chaining.sol"; +import "./PositionRegistrar.sol"; +import "./LiquidityCurve.sol"; +import "./LevelBook.sol"; +import "./TradeMatcher.sol"; /* @title Market sequencer. * @notice Mixin class that's responsibile for coordinating one or multiple sequetial * trade actions within a single liqudity pool. */ contract MarketSequencer is TradeMatcher { - using SafeCast for int256; using SafeCast for int128; using SafeCast for uint256; @@ -35,19 +34,21 @@ contract MarketSequencer is TradeMatcher { using Chaining for Chaining.PairFlow; using Chaining for Chaining.RollTarget; - /* @notice Performs a sequence of an arbitrary potential combination of mints, - * burns, and swaps on a single pool. + /* @notice Performs a sequence of an arbitrary potential combination of mints, + * burns, and swaps on a single pool. * - * @param flow Output accumulator, into which we'll net and and add the token flows + * @param flow Output accumulator, into which we'll net and and add the token flows * associated with the trade actions in this call. * @param dir A directive specifying an arbitrary sequences of action. * @param cntx Provides the execution context for the operation, including the pool * to execute on and it's pre-loaded specs, off-grid price improvement - * settings, and parameters for rolling gap-failled quantities if they + * settings, and parameters for rolling gap-filled quantities if they * appear in the directive. */ - function tradeOverPool (Chaining.PairFlow memory flow, - Directives.PoolDirective memory dir, - Chaining.ExecCntx memory cntx) internal { + function tradeOverPool( + Chaining.PairFlow memory flow, + Directives.PoolDirective memory dir, + Chaining.ExecCntx memory cntx + ) internal { // To avoid repeatedly loading and storing the curve on each operation, we load // it once into memory... CurveCache.Cache memory curve; @@ -60,13 +61,20 @@ contract MarketSequencer is TradeMatcher { /* @notice Performs a single swap over the pool. * @param dir The user-specified directive governing the size, direction and limit * price of the swap to be performed. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @return flow The net token flows generated by the swap. */ - function swapOverPool (Directives.SwapDirective memory dir, - PoolSpecs.PoolCursor memory pool) - internal returns (Chaining.PairFlow memory flow) { + function swapOverPool( + Directives.SwapDirective memory dir, + PoolSpecs.PoolCursor memory pool + ) internal returns (Chaining.PairFlow memory flow) { CurveMath.CurveState memory curve = snapCurve(pool.hash_); - sweepSwapLiq(flow, curve, curve.priceRoot_.getTickAtSqrtRatio(), dir, pool); + sweepSwapLiq( + flow, + curve, + curve.priceRoot_.getTickAtSqrtRatio(), + dir, + pool + ); commitCurve(pool.hash_, curve); } @@ -78,7 +86,7 @@ contract MarketSequencer is TradeMatcher { * order. * @param liq The amount of liquidity being minted represented as the equivalent to * sqrt(X*Y) in a constant product AMM pool. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param minPrice The minimum acceptable curve price to mint liquidity. If curve * price falls outside this point, the transaction is reverted. * @param maxPrice The maximum acceptable curve price to mint liquidity. If curve @@ -91,16 +99,29 @@ contract MarketSequencer is TradeMatcher { * positive as it's paid to the pool from the user. * @return quoteFlow The total amount of quote-side token collateral that must be * committed to the pool as part of the mint. */ - function mintOverPool (int24 bidTick, int24 askTick, uint128 liq, - PoolSpecs.PoolCursor memory pool, - uint128 minPrice, uint128 maxPrice, - address lpConduit) - internal returns (int128 baseFlow, int128 quoteFlow) { - CurveMath.CurveState memory curve = snapCurveInRange - (pool.hash_, minPrice, maxPrice); - (baseFlow, quoteFlow) = - mintRange(curve, curve.priceRoot_.getTickAtSqrtRatio(), - bidTick, askTick, liq, pool.hash_, lpConduit); + function mintOverPool( + int24 bidTick, + int24 askTick, + uint128 liq, + PoolSpecs.PoolCursor memory pool, + uint128 minPrice, + uint128 maxPrice, + address lpConduit + ) internal returns (int128 baseFlow, int128 quoteFlow) { + CurveMath.CurveState memory curve = snapCurveInRange( + pool.hash_, + minPrice, + maxPrice + ); + (baseFlow, quoteFlow) = mintRange( + curve, + curve.priceRoot_.getTickAtSqrtRatio(), + bidTick, + askTick, + liq, + pool.hash_, + lpConduit + ); PriceGrid.verifyFit(bidTick, askTick, pool.head_.tickSize_); commitCurve(pool.hash_, curve); } @@ -113,7 +134,7 @@ contract MarketSequencer is TradeMatcher { * order. * @param liq The amount of liquidity to burn represented as the equivalent to * sqrt(X*Y) in a constant product AMM pool. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param minPrice The minimum acceptable curve price to mint liquidity. If curve * price falls outside this point, the transaction is reverted. * @param maxPrice The maximum acceptable curve price to mint liquidity. If curve @@ -124,15 +145,29 @@ contract MarketSequencer is TradeMatcher { * negative as it's paid from the pool to the user. * @return quoteFlow The total amount of quote-side token collateral that is returned * from the pool as part of the burn. */ - function burnOverPool (int24 bidTick, int24 askTick, uint128 liq, - PoolSpecs.PoolCursor memory pool, - uint128 minPrice, uint128 maxPrice, address lpConduit) - internal returns (int128 baseFlow, int128 quoteFlow) { - CurveMath.CurveState memory curve = snapCurveInRange - (pool.hash_, minPrice, maxPrice); - (baseFlow, quoteFlow) = - burnRange(curve, curve.priceRoot_.getTickAtSqrtRatio(), - bidTick, askTick, liq, pool.hash_, lpConduit); + function burnOverPool( + int24 bidTick, + int24 askTick, + uint128 liq, + PoolSpecs.PoolCursor memory pool, + uint128 minPrice, + uint128 maxPrice, + address lpConduit + ) internal returns (int128 baseFlow, int128 quoteFlow) { + CurveMath.CurveState memory curve = snapCurveInRange( + pool.hash_, + minPrice, + maxPrice + ); + (baseFlow, quoteFlow) = burnRange( + curve, + curve.priceRoot_.getTickAtSqrtRatio(), + bidTick, + askTick, + liq, + pool.hash_, + lpConduit + ); commitCurve(pool.hash_, curve); } @@ -142,7 +177,7 @@ contract MarketSequencer is TradeMatcher { * order. * @param askTick The price tick associated with the upper boundary of the range * order. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param minPrice The minimum acceptable curve price to mint liquidity. If curve * price falls outside this point, the transaction is reverted. * @param maxPrice The maximum acceptable curve price to mint liquidity. If curve @@ -153,15 +188,27 @@ contract MarketSequencer is TradeMatcher { * negative as it's paid from the pool to the user. * @return quoteFlow The total amount of quote-side token collateral that is returned * from the pool as part of the burn. */ - function harvestOverPool (int24 bidTick, int24 askTick, - PoolSpecs.PoolCursor memory pool, - uint128 minPrice, uint128 maxPrice, address lpConduit) - internal returns (int128 baseFlow, int128 quoteFlow) { - CurveMath.CurveState memory curve = snapCurveInRange - (pool.hash_, minPrice, maxPrice); - (baseFlow, quoteFlow) = - harvestRange(curve, curve.priceRoot_.getTickAtSqrtRatio(), - bidTick, askTick, pool.hash_, lpConduit); + function harvestOverPool( + int24 bidTick, + int24 askTick, + PoolSpecs.PoolCursor memory pool, + uint128 minPrice, + uint128 maxPrice, + address lpConduit + ) internal returns (int128 baseFlow, int128 quoteFlow) { + CurveMath.CurveState memory curve = snapCurveInRange( + pool.hash_, + minPrice, + maxPrice + ); + (baseFlow, quoteFlow) = harvestRange( + curve, + curve.priceRoot_.getTickAtSqrtRatio(), + bidTick, + askTick, + pool.hash_, + lpConduit + ); commitCurve(pool.hash_, curve); } @@ -169,7 +216,7 @@ contract MarketSequencer is TradeMatcher { * * @param liq The amount of liquidity being minted represented as the equivalent to * sqrt(X*Y) in a constant product AMM pool. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param minPrice The minimum acceptable curve price to mint liquidity. If curve * price falls outside this point, the transaction is reverted. * @param maxPrice The maximum acceptable curve price to mint liquidity. If curve @@ -182,22 +229,27 @@ contract MarketSequencer is TradeMatcher { * positive as it's paid to the pool from the user. * @return quoteFlow The total amount of quote-side token collateral that must be * committed to the pool as part of the mint. */ - function mintOverPool (uint128 liq, PoolSpecs.PoolCursor memory pool, - uint128 minPrice, uint128 maxPrice, address lpConduit) - internal returns (int128 baseFlow, int128 quoteFlow) { - CurveMath.CurveState memory curve = snapCurveInRange - (pool.hash_, minPrice, maxPrice); - (baseFlow, quoteFlow) = - mintAmbient(curve, liq, pool.hash_, lpConduit); + function mintOverPool( + uint128 liq, + PoolSpecs.PoolCursor memory pool, + uint128 minPrice, + uint128 maxPrice, + address lpConduit + ) internal returns (int128 baseFlow, int128 quoteFlow) { + CurveMath.CurveState memory curve = snapCurveInRange( + pool.hash_, + minPrice, + maxPrice + ); + (baseFlow, quoteFlow) = mintAmbient(curve, liq, pool.hash_, lpConduit); commitCurve(pool.hash_, curve); } - /* @notice Burns ambient liquidity on to the pool's curve. * * @param liq The amount of liquidity to burn represented as the equivalent to * sqrt(X*Y) in a constant product AMM pool. - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param minPrice The minimum acceptable curve price to mint liquidity. If curve * price falls outside this point, the transaction is reverted. * @param maxPrice The maximum acceptable curve price to mint liquidity. If curve @@ -208,13 +260,19 @@ contract MarketSequencer is TradeMatcher { * as it's paid from the pool to the user. * @return quoteFlow The total amount of quote-side token collateral that is returned * from the pool as part of the burn. */ - function burnOverPool (uint128 liq, PoolSpecs.PoolCursor memory pool, - uint128 minPrice, uint128 maxPrice, address lpConduit) - internal returns (int128 baseFlow, int128 quoteFlow) { - CurveMath.CurveState memory curve = snapCurveInRange - (pool.hash_, minPrice, maxPrice); - (baseFlow, quoteFlow) = - burnAmbient(curve, liq, pool.hash_, lpConduit); + function burnOverPool( + uint128 liq, + PoolSpecs.PoolCursor memory pool, + uint128 minPrice, + uint128 maxPrice, + address lpConduit + ) internal returns (int128 baseFlow, int128 quoteFlow) { + CurveMath.CurveState memory curve = snapCurveInRange( + pool.hash_, + minPrice, + maxPrice + ); + (baseFlow, quoteFlow) = burnAmbient(curve, liq, pool.hash_, lpConduit); commitCurve(pool.hash_, curve); } @@ -224,7 +282,7 @@ contract MarketSequencer is TradeMatcher { * the caller's responsibility to make sure this is never called on an already * initialized pool. * - * @param pool The pre-loaded speciication and hash of the pool to be swapped against. + * @param pool The pre-loaded specification and hash of the pool to be swapped against. * @param price The initial price to set the curve at. Represented as the square root * of price in Q64.64 fixed point. * @param initLiq The initial ambient liquidity commitment that will be permanetely @@ -235,22 +293,28 @@ contract MarketSequencer is TradeMatcher { * committed to the pool as part of the mint. Will always be * positive as it's paid to the pool from the user. * @return quoteFlow The total amount of quote-side token collateral that must be - * committed to the pool as part of the mint. */ - function initCurve (PoolSpecs.PoolCursor memory pool, - uint128 price, uint128 initLiq) - internal returns (int128 baseFlow, int128 quoteFlow) { + * committed to the pool as part of the mint. */ + function initCurve( + PoolSpecs.PoolCursor memory pool, + uint128 price, + uint128 initLiq + ) internal returns (int128 baseFlow, int128 quoteFlow) { CurveMath.CurveState memory curve = snapCurveInit(pool.hash_); initPrice(curve, price); - if (initLiq == 0) { initLiq = 1; } + if (initLiq == 0) { + initLiq = 1; + } (baseFlow, quoteFlow) = lockAmbient(curve, initLiq); commitCurve(pool.hash_, curve); } /* @notice Appplies the pool directive on to a pre-loaded liquidity curve. */ - function applyToCurve (Chaining.PairFlow memory flow, - Directives.PoolDirective memory dir, - CurveCache.Cache memory curve, - Chaining.ExecCntx memory cntx) private { + function applyToCurve( + Chaining.PairFlow memory flow, + Directives.PoolDirective memory dir, + CurveCache.Cache memory curve, + Chaining.ExecCntx memory cntx + ) private { if (!dir.chain_.swapDefer_) { applySwap(flow, dir.swap_, curve, cntx); } @@ -262,96 +326,134 @@ contract MarketSequencer is TradeMatcher { } /* @notice Applies the swap directive on to a pre-loaded liquidity curve. */ - function applySwap (Chaining.PairFlow memory flow, - Directives.SwapDirective memory dir, - CurveCache.Cache memory curve, - Chaining.ExecCntx memory cntx) private { + function applySwap( + Chaining.PairFlow memory flow, + Directives.SwapDirective memory dir, + CurveCache.Cache memory curve, + Chaining.ExecCntx memory cntx + ) private { cntx.roll_.plugSwapGap(dir, flow); if (dir.qty_ != 0) { - callSwap(flow, curve, dir, cntx.pool_); + callSwap(flow, curve, dir, cntx.pool_); } } /* @notice Applies an ambient liquidity directive to a pre-loaded liquidity curve. */ - function applyAmbient (Chaining.PairFlow memory flow, - Directives.AmbientDirective memory dir, - CurveCache.Cache memory curve, - Chaining.ExecCntx memory cntx) private { + function applyAmbient( + Chaining.PairFlow memory flow, + Directives.AmbientDirective memory dir, + CurveCache.Cache memory curve, + Chaining.ExecCntx memory cntx + ) private { cntx.roll_.plugLiquidity(dir, curve.curve_, flow); - + if (dir.liquidity_ > 0) { - (int128 base, int128 quote) = dir.isAdd_ ? - callMintAmbient(curve, dir.liquidity_, cntx.pool_.hash_) : - callBurnAmbient(curve, dir.liquidity_, cntx.pool_.hash_); - + (int128 base, int128 quote) = dir.isAdd_ + ? callMintAmbient(curve, dir.liquidity_, cntx.pool_.hash_) + : callBurnAmbient(curve, dir.liquidity_, cntx.pool_.hash_); + flow.accumFlow(base, quote); } } - /* @notice Applies zero, one or a series of concentrated liquidity directives to a + /* @notice Applies zero, one or a series of concentrated liquidity directives to a * pre-loaded liquidity curve. */ - function applyConcentrated (Chaining.PairFlow memory flow, - Directives.ConcentratedDirective[] memory dirs, - CurveCache.Cache memory curve, - Chaining.ExecCntx memory cntx) private { - unchecked { // Only arithmetic in block is ++i which will never overflow - for (uint i = 0; i < dirs.length; ++i) { - (int128 nextBase, int128 nextQuote) = applyConcentrated - (curve, flow, cntx, dirs[i]); - flow.accumFlow(nextBase, nextQuote); - } + function applyConcentrated( + Chaining.PairFlow memory flow, + Directives.ConcentratedDirective[] memory dirs, + CurveCache.Cache memory curve, + Chaining.ExecCntx memory cntx + ) private { + unchecked { + // Only arithmetic in block is ++i which will never overflow + for (uint i = 0; i < dirs.length; ++i) { + (int128 nextBase, int128 nextQuote) = applyConcentrated( + curve, + flow, + cntx, + dirs[i] + ); + flow.accumFlow(nextBase, nextQuote); + } } } /* Applies a single concentrated liquidity range order to the liquidity curve. */ - function applyConcentrated (CurveCache.Cache memory curve, - Chaining.PairFlow memory flow, - Chaining.ExecCntx memory cntx, - Directives.ConcentratedDirective memory bend) - private returns (int128, int128) { - + function applyConcentrated( + CurveCache.Cache memory curve, + Chaining.PairFlow memory flow, + Chaining.ExecCntx memory cntx, + Directives.ConcentratedDirective memory bend + ) private returns (int128, int128) { // If ticks are relative, normalize against current pool price. if (bend.isTickRel_) { int24 priceTick = curve.pullPriceTick(); bend.lowTick_ = priceTick + bend.lowTick_; bend.highTick_ = priceTick + bend.highTick_; - require((bend.lowTick_ >= TickMath.MIN_TICK) && + require( + (bend.lowTick_ >= TickMath.MIN_TICK) && (bend.highTick_ <= TickMath.MAX_TICK) && - (bend.lowTick_ <= bend.highTick_), "RT"); + (bend.lowTick_ <= bend.highTick_), + "RT" + ); } // If liquidity is set based on rolling balance, dynamically set in base // liquidity space. - cntx.roll_.plugLiquidity(bend, curve.curve_, bend.lowTick_, - bend.highTick_, flow); + cntx.roll_.plugLiquidity( + bend, + curve.curve_, + bend.lowTick_, + bend.highTick_, + flow + ); if (bend.isAdd_) { - bool offGrid = cntx.improve_.verifyFit(bend.lowTick_, bend.highTick_, - bend.liquidity_, - cntx.pool_.head_.tickSize_, - curve.pullPriceTick()); + bool offGrid = cntx.improve_.verifyFit( + bend.lowTick_, + bend.highTick_, + bend.liquidity_, + cntx.pool_.head_.tickSize_, + curve.pullPriceTick() + ); // Off-grid positions are only eligible when the LP has committed // to a minimum liquidity commitment above some threshold. This opens // up the possibility of a user minting an off-grid LP position above the // the threshold, then partially burning the position to resize the position *below* - // the threhsold. - // To prevent this all off-grid positions are marked as atomic which prevents partial - // (but not full) burns. An off-grid LP wishing to reduce their position must fully - // burn the position, then mint a new position, which will be checked that it meets + // the threhsold. + // To prevent this all off-grid positions are marked as atomic which prevents partial + // (but not full) burns. An off-grid LP wishing to reduce their position must fully + // burn the position, then mint a new position, which will be checked that it meets // the size threshold at mint time. if (offGrid) { - markPosAtomic(lockHolder_, cntx.pool_.hash_, - bend.lowTick_, bend.highTick_); + markPosAtomic( + lockHolder_, + cntx.pool_.hash_, + bend.lowTick_, + bend.highTick_ + ); } } - if (bend.liquidity_ == 0) { return (0, 0); } - return bend.isAdd_ ? - callMintRange(curve, bend.lowTick_, bend.highTick_, - bend.liquidity_, cntx.pool_.hash_) : - callBurnRange(curve, bend.lowTick_, bend.highTick_, - bend.liquidity_, cntx.pool_.hash_); + if (bend.liquidity_ == 0) { + return (0, 0); + } + return + bend.isAdd_ + ? callMintRange( + curve, + bend.lowTick_, + bend.highTick_, + bend.liquidity_, + cntx.pool_.hash_ + ) + : callBurnRange( + curve, + bend.lowTick_, + bend.highTick_, + bend.liquidity_, + cntx.pool_.hash_ + ); } - } diff --git a/contracts/mixins/TradeMatcher.sol b/contracts/mixins/TradeMatcher.sol index 8583a439..70153c8f 100644 --- a/contracts/mixins/TradeMatcher.sol +++ b/contracts/mixins/TradeMatcher.sol @@ -2,20 +2,20 @@ pragma solidity 0.8.19; -import '../libraries/Directives.sol'; -import '../libraries/PoolSpecs.sol'; -import '../libraries/PriceGrid.sol'; -import '../libraries/SwapCurve.sol'; -import '../libraries/CurveMath.sol'; -import '../libraries/CurveRoll.sol'; -import '../libraries/Chaining.sol'; -import '../interfaces/ICrocLpConduit.sol'; -import './PositionRegistrar.sol'; -import './LiquidityCurve.sol'; -import './LevelBook.sol'; -import './KnockoutCounter.sol'; -import './ProxyCaller.sol'; -import './AgentMask.sol'; +import "../libraries/Directives.sol"; +import "../libraries/PoolSpecs.sol"; +import "../libraries/PriceGrid.sol"; +import "../libraries/SwapCurve.sol"; +import "../libraries/CurveMath.sol"; +import "../libraries/CurveRoll.sol"; +import "../libraries/Chaining.sol"; +import "../interfaces/ICrocLpConduit.sol"; +import "./PositionRegistrar.sol"; +import "./LiquidityCurve.sol"; +import "./LevelBook.sol"; +import "./KnockoutCounter.sol"; +import "./ProxyCaller.sol"; +import "./AgentMask.sol"; /* @title Trade matcher mixin * @notice Provides a unified facility for calling the core atomic trade actions @@ -25,9 +25,12 @@ import './AgentMask.sol'; * 3) Burn ambient liquidity * 4) Burn range liquidity * 5) Swap */ -contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, - ProxyCaller { - +contract TradeMatcher is + PositionRegistrar, + LiquidityCurve, + KnockoutCounter, + ProxyCaller +{ using SafeCast for int256; using SafeCast for int128; using SafeCast for uint256; @@ -44,15 +47,15 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, /* @notice Mints ambient liquidity (i.e. liquidity that stays active at every * price point) on to the curve. - * + * * @param curve The object representing the pre-loaded liquidity curve. Will be - * updated in memory after this call, but it's the caller's + * updated in memory after this call, but it's the caller's * responsbility to check it back into storage. * @param liqAdded The amount of ambient liquidity being minted represented as * sqrt(X*Y) where X,Y are the collateral reserves in a constant- * product AMM * @param poolHash The hash indexing the pool this liquidity curve applies to. - * @param lpOwner The address of the ICrocLpConduit the LP position will be + * @param lpOwner The address of the ICrocLpConduit the LP position will be * assigned to. (If zero the user will directly own the LP.) * * @return baseFlow The amount of base-side token collateral required by this @@ -60,11 +63,18 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * the user to the pool. * @return quoteFlow The amount of quote-side token collateral required by thhis * operation. */ - function mintAmbient (CurveMath.CurveState memory curve, uint128 liqAdded, - bytes32 poolHash, address lpOwner) - internal returns (int128 baseFlow, int128 quoteFlow) { - uint128 liqSeeds = mintPosLiq(lpOwner, poolHash, liqAdded, - curve.seedDeflator_); + function mintAmbient( + CurveMath.CurveState memory curve, + uint128 liqAdded, + bytes32 poolHash, + address lpOwner + ) internal returns (int128 baseFlow, int128 quoteFlow) { + uint128 liqSeeds = mintPosLiq( + lpOwner, + poolHash, + liqAdded, + curve.seedDeflator_ + ); depositConduit(poolHash, liqSeeds, curve.seedDeflator_, lpOwner); (uint128 base, uint128 quote) = liquidityReceivable(curve, liqSeeds); @@ -73,16 +83,18 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, /* @notice Like mintAmbient(), but the liquidity is permanetely locked into the pool, * and therefore cannot be later burned by the user. */ - function lockAmbient (CurveMath.CurveState memory curve, uint128 liqAdded) - internal pure returns (int128, int128) { + function lockAmbient( + CurveMath.CurveState memory curve, + uint128 liqAdded + ) internal pure returns (int128, int128) { (uint128 base, uint128 quote) = liquidityReceivable(curve, liqAdded); - return signMintFlow(base, quote); + return signMintFlow(base, quote); } /* @notice Burns ambient liquidity from the curve. - * + * * @param curve The object representing the pre-loaded liquidity curve. Will be - * updated in memory after this call, but it's the caller's + * updated in memory after this call, but it's the caller's * responsbility to check it back into storage. * @param liqAdded The amount of ambient liquidity being minted represented as * sqrt(X*Y) where X,Y are the collateral reserves in a constant- @@ -94,20 +106,28 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * the pool to the user. * @return quoteFlow The amount of quote-side token collateral returned by this * operation. */ - function burnAmbient (CurveMath.CurveState memory curve, uint128 liqBurned, - bytes32 poolHash, address lpOwner) - internal returns (int128, int128) { - uint128 liqSeeds = burnPosLiq(lpOwner, poolHash, liqBurned, curve.seedDeflator_); + function burnAmbient( + CurveMath.CurveState memory curve, + uint128 liqBurned, + bytes32 poolHash, + address lpOwner + ) internal returns (int128, int128) { + uint128 liqSeeds = burnPosLiq( + lpOwner, + poolHash, + liqBurned, + curve.seedDeflator_ + ); withdrawConduit(poolHash, liqSeeds, curve.seedDeflator_, lpOwner); - + (uint128 base, uint128 quote) = liquidityPayable(curve, liqSeeds); return signBurnFlow(base, quote); } /* @notice Mints concernated liquidity within a range on to the curve. - * + * * @param curve The object representing the pre-loaded liquidity curve. Will be - * updated in memory after this call, but it's the caller's + * updated in memory after this call, but it's the caller's * responsbility to check it back into storage. * @param prickTick The tick index of the curve's current price. * @param lowTick The tick index of the lower boundary of the range order. @@ -116,35 +136,55 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * sqrt(X*Y) where X,Y are the collateral reserves in a constant- * product AMM * @param poolHash The hash indexing the pool this liquidity curve applies to. - * @param lpConduit The address of the ICrocLpConduit the LP position will be + * @param lpConduit The address of the ICrocLpConduit the LP position will be * assigned to. (If zero the user will directly own the LP.) * * @return baseFlow The amount of base-side token collateral required by this * operations. Will always be positive indicating, a debit from * the user to the pool. - * @return quoteFlow The amount of quote-side token collateral required by thhis + * @return quoteFlow The amount of quote-side token collateral required by this * operation. */ - function mintRange (CurveMath.CurveState memory curve, int24 priceTick, - int24 lowTick, int24 highTick, uint128 liquidity, - bytes32 poolHash, address lpOwner) - internal returns (int128 baseFlow, int128 quoteFlow) { - uint72 feeMileage = addBookLiq72(poolHash, priceTick, lowTick, highTick, - liquidity.liquidityToLots(), - curve.concGrowth_); - - mintPosLiq(lpOwner, poolHash, lowTick, highTick, - liquidity, feeMileage); - depositConduit(poolHash, lowTick, highTick, liquidity, feeMileage, lpOwner); - - (uint128 base, uint128 quote) = liquidityReceivable - (curve, liquidity, lowTick, highTick); + function mintRange( + CurveMath.CurveState memory curve, + int24 priceTick, + int24 lowTick, + int24 highTick, + uint128 liquidity, + bytes32 poolHash, + address lpOwner + ) internal returns (int128 baseFlow, int128 quoteFlow) { + uint72 feeMileage = addBookLiq72( + poolHash, + priceTick, + lowTick, + highTick, + liquidity.liquidityToLots(), + curve.concGrowth_ + ); + + mintPosLiq(lpOwner, poolHash, lowTick, highTick, liquidity, feeMileage); + depositConduit( + poolHash, + lowTick, + highTick, + liquidity, + feeMileage, + lpOwner + ); + + (uint128 base, uint128 quote) = liquidityReceivable( + curve, + liquidity, + lowTick, + highTick + ); (baseFlow, quoteFlow) = signMintFlow(base, quote); } /* @notice Burns concernated liquidity within a specific range off of the curve. - * + * * @param curve The object representing the pre-loaded liquidity curve. Will be - * updated in memory after this call, but it's the caller's + * updated in memory after this call, but it's the caller's * responsbility to check it back into storage. * @param prickTick The tick index of the curve's current price. * @param lowTick The tick index of the lower boundary of the range order. @@ -159,64 +199,130 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * the pool to the user. * @return quoteFlow The amount of quote-side token collateral returned by this * operation. */ - function burnRange (CurveMath.CurveState memory curve, int24 priceTick, - int24 lowTick, int24 highTick, uint128 liquidity, - bytes32 poolHash, address lpOwner) - internal returns (int128, int128) { - uint72 feeMileage = removeBookLiq72(poolHash, priceTick, lowTick, highTick, - liquidity.liquidityToLots(), - curve.concGrowth_); - uint64 rewards = burnPosLiq(lpOwner, poolHash, lowTick, highTick, liquidity, - feeMileage); - withdrawConduit(poolHash, lowTick, highTick, - liquidity, feeMileage, lpOwner); - (uint128 base, uint128 quote) = liquidityPayable(curve, liquidity, rewards, - lowTick, highTick); + function burnRange( + CurveMath.CurveState memory curve, + int24 priceTick, + int24 lowTick, + int24 highTick, + uint128 liquidity, + bytes32 poolHash, + address lpOwner + ) internal returns (int128, int128) { + uint72 feeMileage = removeBookLiq72( + poolHash, + priceTick, + lowTick, + highTick, + liquidity.liquidityToLots(), + curve.concGrowth_ + ); + uint64 rewards = burnPosLiq( + lpOwner, + poolHash, + lowTick, + highTick, + liquidity, + feeMileage + ); + withdrawConduit( + poolHash, + lowTick, + highTick, + liquidity, + feeMileage, + lpOwner + ); + (uint128 base, uint128 quote) = liquidityPayable( + curve, + liquidity, + rewards, + lowTick, + highTick + ); return signBurnFlow(base, quote); } - /* @notice Dispatches the call to the ICrocLpConduit with the ambient liquidity + /* @notice Dispatches the call to the ICrocLpConduit with the ambient liquidity * LP position that was minted. */ - function depositConduit (bytes32 poolHash, uint128 liqSeeds, uint72 deflator, - address lpConduit) private { + function depositConduit( + bytes32 poolHash, + uint128 liqSeeds, + uint72 deflator, + address lpConduit + ) private { // Equivalent to calling concentrated liquidity deposit with lowTick=0 and highTick=0 // Since a true range order can never have a width of zero, the receiving deposit // contract should recognize these values as always representing ambient liquidity int24 NA_LOW_TICK = 0; int24 NA_HIGH_TICK = 0; - depositConduit(poolHash, NA_LOW_TICK, NA_HIGH_TICK, liqSeeds, deflator, lpConduit); + depositConduit( + poolHash, + NA_LOW_TICK, + NA_HIGH_TICK, + liqSeeds, + deflator, + lpConduit + ); } - /* @notice Dispatches the call to the ICrocLpConduit with the concentrated liquidity + /* @notice Dispatches the call to the ICrocLpConduit with the concentrated liquidity * LP position that was minted. */ - function depositConduit (bytes32 poolHash, int24 lowTick, int24 highTick, - uint128 liq, uint72 mileage, address lpConduit) private { + function depositConduit( + bytes32 poolHash, + int24 lowTick, + int24 highTick, + uint128 liq, + uint72 mileage, + address lpConduit + ) private { if (lpConduit != lockHolder_) { - bool doesAccept = ICrocLpConduit(lpConduit). - depositCrocLiq(lockHolder_, poolHash, lowTick, highTick, liq, mileage); + bool doesAccept = ICrocLpConduit(lpConduit).depositCrocLiq( + lockHolder_, + poolHash, + lowTick, + highTick, + liq, + mileage + ); require(doesAccept, "LP"); } } /* @notice Withdraws and sends ownership of the ambient liquidity to a third party conduit * explicitly nominated by the caller. */ - function withdrawConduit (bytes32 poolHash, uint128 liqSeeds, uint72 deflator, - address lpConduit) private { + function withdrawConduit( + bytes32 poolHash, + uint128 liqSeeds, + uint72 deflator, + address lpConduit + ) private { withdrawConduit(poolHash, 0, 0, liqSeeds, deflator, lpConduit); } /* @notice Withdraws and sends ownership of the liquidity to a third party conduit * explicitly nominated by the caller. */ - function withdrawConduit (bytes32 poolHash, int24 lowTick, int24 highTick, - uint128 liq, uint72 mileage, address lpConduit) private { + function withdrawConduit( + bytes32 poolHash, + int24 lowTick, + int24 highTick, + uint128 liq, + uint72 mileage, + address lpConduit + ) private { if (lpConduit != lockHolder_) { - bool doesAccept = ICrocLpConduit(lpConduit). - withdrawCrocLiq(lockHolder_, poolHash, lowTick, highTick, liq, mileage); + bool doesAccept = ICrocLpConduit(lpConduit).withdrawCrocLiq( + lockHolder_, + poolHash, + lowTick, + highTick, + liq, + mileage + ); require(doesAccept, "LP"); } } - /* @notice Mints a new knockout liquidity position, or adds to a previous position, + /* @notice Mints a new knockout liquidity position, or adds to a previous position, * and updates the curve and debit flows accordingly. * * @param curve The current state of the liquidity curve. @@ -227,15 +333,29 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * @param knockoutBits The bitwise knockout parameters currently set on the pool. * * @return The incrmental base and quote debit flows from this action. */ - function mintKnockout (CurveMath.CurveState memory curve, int24 priceTick, - KnockoutLiq.KnockoutPosLoc memory loc, - uint128 liquidity, bytes32 poolHash, uint8 knockoutBits) - internal returns (int128 baseFlow, int128 quoteFlow) { - addKnockoutLiq(poolHash, knockoutBits, priceTick, curve.concGrowth_, loc, - liquidity.liquidityToLots()); - - (uint128 base, uint128 quote) = liquidityReceivable - (curve, liquidity, loc.lowerTick_, loc.upperTick_); + function mintKnockout( + CurveMath.CurveState memory curve, + int24 priceTick, + KnockoutLiq.KnockoutPosLoc memory loc, + uint128 liquidity, + bytes32 poolHash, + uint8 knockoutBits + ) internal returns (int128 baseFlow, int128 quoteFlow) { + addKnockoutLiq( + poolHash, + knockoutBits, + priceTick, + curve.concGrowth_, + loc, + liquidity.liquidityToLots() + ); + + (uint128 base, uint128 quote) = liquidityReceivable( + curve, + liquidity, + loc.lowerTick_, + loc.upperTick_ + ); (baseFlow, quoteFlow) = signMintFlow(base, quote); } @@ -249,15 +369,28 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * @param poolHash The hash of the pool the curve applies to * * @return The incrmental base and quote debit flows from this action. */ - function burnKnockout (CurveMath.CurveState memory curve, int24 priceTick, - KnockoutLiq.KnockoutPosLoc memory loc, - uint128 liquidity, bytes32 poolHash) - internal returns (int128 baseFlow, int128 quoteFlow) { - (, , uint64 rewards) = rmKnockoutLiq(poolHash, priceTick, curve.concGrowth_, - loc, liquidity.liquidityToLots()); - - (uint128 base, uint128 quote) = liquidityPayable - (curve, liquidity, rewards, loc.lowerTick_, loc.upperTick_); + function burnKnockout( + CurveMath.CurveState memory curve, + int24 priceTick, + KnockoutLiq.KnockoutPosLoc memory loc, + uint128 liquidity, + bytes32 poolHash + ) internal returns (int128 baseFlow, int128 quoteFlow) { + (, , uint64 rewards) = rmKnockoutLiq( + poolHash, + priceTick, + curve.concGrowth_, + loc, + liquidity.liquidityToLots() + ); + + (uint128 base, uint128 quote) = liquidityPayable( + curve, + liquidity, + rewards, + loc.lowerTick_, + loc.upperTick_ + ); (baseFlow, quoteFlow) = signBurnFlow(base, quote); } @@ -271,16 +404,28 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * hash of the knockout slot * @param poolHash The hash of the pool the curve applies to * - * @return The incrmental base and quote debit flows from this action. */ - function claimKnockout (CurveMath.CurveState memory curve, - KnockoutLiq.KnockoutPosLoc memory loc, - uint160 root, uint256[] memory proof, bytes32 poolHash) - internal returns (int128 baseFlow, int128 quoteFlow) { - (uint96 lots, uint64 rewards) = claimPostKnockout(poolHash, loc, root, proof); + * @return The incremental base and quote debit flows from this action. */ + function claimKnockout( + CurveMath.CurveState memory curve, + KnockoutLiq.KnockoutPosLoc memory loc, + uint160 root, + uint256[] memory proof, + bytes32 poolHash + ) internal returns (int128 baseFlow, int128 quoteFlow) { + (uint96 lots, uint64 rewards) = claimPostKnockout( + poolHash, + loc, + root, + proof + ); uint128 liquidity = lots.lotsToLiquidity(); - - (uint128 base, uint128 quote) = liquidityHeldPayable - (curve, liquidity, rewards, loc); + + (uint128 base, uint128 quote) = liquidityHeldPayable( + curve, + liquidity, + rewards, + loc + ); (baseFlow, quoteFlow) = signBurnFlow(base, quote); } @@ -292,10 +437,12 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * @param root The root of the supplied Merkle proof * @param pivotTime The pivotTime of the knockout slot at the time the position was * minted. - * @return The incrmental base and quote debit flows from this action. */ - function recoverKnockout (KnockoutLiq.KnockoutPosLoc memory loc, - uint32 pivotTime, bytes32 poolHash) - internal returns (int128 baseFlow, int128 quoteFlow) { + * @return The incremental base and quote debit flows from this action. */ + function recoverKnockout( + KnockoutLiq.KnockoutPosLoc memory loc, + uint32 pivotTime, + bytes32 poolHash + ) internal returns (int128 baseFlow, int128 quoteFlow) { uint96 lots = recoverPostKnockout(poolHash, loc, pivotTime); uint128 liquidity = lots.lotsToLiquidity(); @@ -304,10 +451,10 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, } /* @notice Harvests the accumulated rewards on a concentrated liquidity position. - * + * * @param curve The object representing the pre-loaded liquidity curve. Will be - * updated in memory after this call, but it's the caller's - * responsbility to check it back into storage. + * updated in memory after this call, but it's the caller's + * responsibility to check it back into storage. * @param prickTick The tick index of the curve's current price. * @param lowTick The tick index of the lower boundary of the range order. * @param highTick The tick index of the upper boundary of the range order. @@ -318,32 +465,50 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * the pool to the user. * @return quoteFlow The amount of quote-side token collateral returned by this * operation. */ - function harvestRange (CurveMath.CurveState memory curve, int24 priceTick, - int24 lowTick, int24 highTick, bytes32 poolHash, - address lpOwner) - internal returns (int128, int128) { - uint72 feeMileage = clockFeeOdometer72(poolHash, priceTick, lowTick, highTick, - curve.concGrowth_); - uint128 rewards = harvestPosLiq(lpOwner, poolHash, - lowTick, highTick, feeMileage); + function harvestRange( + CurveMath.CurveState memory curve, + int24 priceTick, + int24 lowTick, + int24 highTick, + bytes32 poolHash, + address lpOwner + ) internal returns (int128, int128) { + uint72 feeMileage = clockFeeOdometer72( + poolHash, + priceTick, + lowTick, + highTick, + curve.concGrowth_ + ); + uint128 rewards = harvestPosLiq( + lpOwner, + poolHash, + lowTick, + highTick, + feeMileage + ); withdrawConduit(poolHash, lowTick, highTick, 0, feeMileage, lpOwner); (uint128 base, uint128 quote) = liquidityPayable(curve, rewards); return signBurnFlow(base, quote); } - + /* @notice Converts the unsigned flow associated with a mint operation to a pair * net settlement flow. (Will always be positive because a mint requires use * to pay collateral to the pool.) */ - function signMintFlow (uint128 base, uint128 quote) private pure - returns (int128, int128) { + function signMintFlow( + uint128 base, + uint128 quote + ) private pure returns (int128, int128) { return (base.toInt128Sign(), quote.toInt128Sign()); } /* @notice Converts the unsigned flow associated with a burn operation to a pair * net settlement flow. (Will always be negative because a burn requires use * to pay collateral to the pool.) */ - function signBurnFlow (uint128 base, uint128 quote) private pure - returns (int128, int128){ + function signBurnFlow( + uint128 base, + uint128 quote + ) private pure returns (int128, int128) { return (-(base.toInt128Sign()), -(quote.toInt128Sign())); } @@ -356,25 +521,32 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * it iteratively re-adjusts the AMM curve on every level cross, and performs * the necessary book-keeping on each crossed level entry. * - * @param accum The accumulator for the flows generated by the executable swap. - * The realized flows on the swap will be written into the memory-based - * accumulator fields of this struct. The caller is responsible for - * ultaimtely paying and collecting those flows. - * @param curve The starting liquidity curve state. Any changes created by the - * swap on this struct are updated in memory. But the caller is + * @param accum The accumulator for the flows generated by the executable swap. + * The realized flows on the swap will be written into the memory-based + * accumulator fields of this struct. The caller is responsible for + * ultimately paying and collecting those flows. + * @param curve The starting liquidity curve state. Any changes created by the + * swap on this struct are updated in memory. But the caller is * responsible for committing the final state to EVM storage. * @param midTick The price tick associated with the current price on the curve. * @param swap The user specified directive governing the size, direction and limit * price of the swap to be executed. * @param pool The pool's market specification notably its swap fee rate and the * protocol take rate. */ - function sweepSwapLiq (Chaining.PairFlow memory accum, - CurveMath.CurveState memory curve, int24 midTick, - Directives.SwapDirective memory swap, - PoolSpecs.PoolCursor memory pool) internal { - require(swap.isBuy_ ? curve.priceRoot_ <= swap.limitPrice_ : - curve.priceRoot_ >= swap.limitPrice_, "SD"); - + function sweepSwapLiq( + Chaining.PairFlow memory accum, + CurveMath.CurveState memory curve, + int24 midTick, + Directives.SwapDirective memory swap, + PoolSpecs.PoolCursor memory pool + ) internal { + require( + swap.isBuy_ + ? curve.priceRoot_ <= swap.limitPrice_ + : curve.priceRoot_ >= swap.limitPrice_, + "SD" + ); + // Keep iteratively executing more quantity until we either reach our limit price // or have zero quantity left to execute. bool doMore = hasSwapLeft(curve, swap); @@ -382,28 +554,33 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, // Swap to furthest point we can based on the local bitmap. Don't bother // seeking a bump outside the local neighborhood yet, because we're not sure // if the swap will exhaust the bitmap. - (int24 bumpTick, bool spillsOver) = pinBitmap - (pool.hash_, swap.isBuy_, midTick); + (int24 bumpTick, bool spillsOver) = pinBitmap( + pool.hash_, + swap.isBuy_, + midTick + ); curve.swapToLimit(accum, swap, pool.head_, bumpTick); - - + // The swap can be in one of four states at this point: 1) qty exhausted, // 2) limit price reached, 3) bump or barrier point reached on the curve. // The former two indicate the swap is complete. The latter means we have to // find the next bump point and possibly adjust AMM liquidity. doMore = hasSwapLeft(curve, swap); if (doMore) { - // The spillsOver variable indicates that we reached stopped because we // reached the end of the local bitmap, rather than actually hitting a // level bump. Therefore we should query the global bitmap, find the next // bump point, and keep swapping across the constant-product curve until // if/when we hit that point. if (spillsOver) { - int24 liqTick = seekMezzSpill(pool.hash_, bumpTick, swap.isBuy_); + int24 liqTick = seekMezzSpill( + pool.hash_, + bumpTick, + swap.isBuy_ + ); bool tightSpill = (bumpTick == liqTick); bumpTick = liqTick; - + // In some corner cases the local bitmap border also happens to // be the next bump point. If so, we're done with this inner section. // Otherwise, we keep swapping since we still have some distance on @@ -413,13 +590,19 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, doMore = hasSwapLeft(curve, swap); } } - + // Perform book-keeping related to crossing the level bump, update // the locally tracked tick of the curve price (rather than wastefully // we calculating it since we already know it), then begin the swap // loop again. if (doMore) { - midTick = knockInTick(accum, bumpTick, curve, swap, pool.hash_); + midTick = knockInTick( + accum, + bumpTick, + curve, + swap, + pool.hash_ + ); } } } @@ -427,26 +610,27 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, /* @notice Determines if we've terminated the swap execution. I.e. fully exhausted * the specified swap quantity *OR* hit the directive's limit price. */ - function hasSwapLeft (CurveMath.CurveState memory curve, - Directives.SwapDirective memory swap) - private pure returns (bool) { - bool inLimit = swap.isBuy_ ? - curve.priceRoot_ < swap.limitPrice_ : - curve.priceRoot_ > swap.limitPrice_; + function hasSwapLeft( + CurveMath.CurveState memory curve, + Directives.SwapDirective memory swap + ) private pure returns (bool) { + bool inLimit = swap.isBuy_ + ? curve.priceRoot_ < swap.limitPrice_ + : curve.priceRoot_ > swap.limitPrice_; return inLimit && (swap.qty_ > 0); } - /* @notice Performs all the necessary book keeping related to crossing an extant - * level bump on the curve. + /* @notice Performs all the necessary book keeping related to crossing an extant + * level bump on the curve. * * @dev Note that this function updates the level book data structure directly on * the EVM storage. But it only updates the liquidity curve state *in memory*. * This is for gas efficiency reasons, as the same curve struct may be updated - * many times in a single swap. The caller must take responsibility for - * committing the final curve state back to EVM storage. + * many times in a single swap. The caller must take responsibility for + * committing the final curve state back to EVM storage. * * @params bumpTick The tick index where the bump occurs. - * @params isBuy The direction the bump happens from. If true, curve's price is + * @params isBuy The direction the bump happens from. If true, curve's price is * moving through the bump starting from a lower price and going to a * higher price. If false, the opposite. * @params curve The pre-bump state of the local constant-product AMM curve. Updated @@ -454,48 +638,62 @@ contract TradeMatcher is PositionRegistrar, LiquidityCurve, KnockoutCounter, * bump. * @param swap The user directive governing the size, direction and limit price of the * swap to be executed. - * @param poolHash The key hash mapping to the pool we're executive over. + * @param poolHash The key hash mapping to the pool we're executive over. * * @return The tick index that the curve and its price are living in after the call * completes. */ - function knockInTick (Chaining.PairFlow memory accum, int24 bumpTick, - CurveMath.CurveState memory curve, - Directives.SwapDirective memory swap, - bytes32 poolHash) private - returns (int24) { + function knockInTick( + Chaining.PairFlow memory accum, + int24 bumpTick, + CurveMath.CurveState memory curve, + Directives.SwapDirective memory swap, + bytes32 poolHash + ) private returns (int24) { unchecked { - if (!Bitmaps.isTickFinite(bumpTick)) { return bumpTick; } - bumpLiquidity(curve, bumpTick, swap.isBuy_, poolHash); - - (int128 paidBase, int128 paidQuote, uint128 burnSwap) = - curve.shaveAtBump(swap.inBaseQty_, swap.isBuy_, swap.qty_); - accum.accumFlow(paidBase, paidQuote); - - // burn down qty from shaveAtBump is always validated to be less than remaining swap.qty_ - // so this will never underflow - swap.qty_ -= burnSwap; - - // When selling down, the next tick leg actually occurs *below* the bump tick - // because the bump barrier is the first price on a tick. - return swap.isBuy_ ? - bumpTick : - bumpTick - 1; // Valid ticks are well above {min(int128)-1}, so will never underflow + if (!Bitmaps.isTickFinite(bumpTick)) { + return bumpTick; + } + bumpLiquidity(curve, bumpTick, swap.isBuy_, poolHash); + + (int128 paidBase, int128 paidQuote, uint128 burnSwap) = curve + .shaveAtBump(swap.inBaseQty_, swap.isBuy_, swap.qty_); + accum.accumFlow(paidBase, paidQuote); + + // burn down qty from shaveAtBump is always validated to be less than remaining swap.qty_ + // so this will never underflow + swap.qty_ -= burnSwap; + + // When selling down, the next tick leg actually occurs *below* the bump tick + // because the bump barrier is the first price on a tick. + return swap.isBuy_ ? bumpTick : bumpTick - 1; // Valid ticks are well above {min(int128)-1}, so will never underflow } } - /* @notice Performs the book-keeping related to crossing a concentrated liquidity + /* @notice Performs the book-keeping related to crossing a concentrated liquidity * bump tick, and adjusts the in-memory curve object with the change of * AMM liquidity. */ - function bumpLiquidity (CurveMath.CurveState memory curve, - int24 bumpTick, bool isBuy, bytes32 poolHash) private { - (int128 liqDelta, bool knockoutFlag) = - crossLevel(poolHash, bumpTick, isBuy, curve.concGrowth_); + function bumpLiquidity( + CurveMath.CurveState memory curve, + int24 bumpTick, + bool isBuy, + bytes32 poolHash + ) private { + (int128 liqDelta, bool knockoutFlag) = crossLevel( + poolHash, + bumpTick, + isBuy, + curve.concGrowth_ + ); curve.concLiq_ = curve.concLiq_.addDelta(liqDelta); if (knockoutFlag) { - int128 knockoutDelta = callCrossFlag - (poolHash, bumpTick, isBuy, curve.concGrowth_); + int128 knockoutDelta = callCrossFlag( + poolHash, + bumpTick, + isBuy, + curve.concGrowth_ + ); curve.concLiq_ = curve.concLiq_.addDelta(knockoutDelta); } - } + } }