feat(libraries): add sqrt-price delta math

This commit is contained in:
dualfroz committed 2026-08-23 22:40:00 +02:00
1 parent 72e670cd72
commit 7a1aff4521
1 file changed
+147
+147
View File
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.36;
import {FullMath} from "./FullMath.sol";
import {UnsafeMath} from "./UnsafeMath.sol";
import {FixedPoint96} from "./FixedPoint96.sol";
import {SafeCast} from "./SafeCast.sol";
/// @title Sqrt-price and token amount deltas along the constant-product curve.
library SqrtPriceMath {
using SafeCast for uint256;
function getNextSqrtPriceFromAmount0RoundingUp(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add)
internal
pure
returns (uint160)
{
if (amount == 0) return sqrtPX96;
uint256 numerator1 = uint256(liquidity) << FixedPoint96.resolution;
unchecked {
if (add) {
uint256 product = amount * sqrtPX96;
if (product / amount == sqrtPX96) {
uint256 denominator = numerator1 + product;
if (denominator >= numerator1) {
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
}
}
return uint160(UnsafeMath.divRoundingUp(numerator1, numerator1 / sqrtPX96 + amount));
} else {
uint256 product = amount * sqrtPX96;
require(product / amount == sqrtPX96 && numerator1 > product, "SqrtPriceMath: A0");
uint256 denominator = numerator1 - product;
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
}
}
}
function getNextSqrtPriceFromAmount1RoundingDown(uint160 sqrtPX96, uint128 liquidity, uint256 amount, bool add)
internal
pure
returns (uint160)
{
unchecked {
if (add) {
uint256 quotient =
(amount <= type(uint160).max
? (amount << FixedPoint96.resolution) / liquidity
: FullMath.mulDiv(amount, FixedPoint96.q96, liquidity));
return (uint256(sqrtPX96) + quotient).toUint160();
} else {
uint256 quotient =
(amount <= type(uint160).max
? UnsafeMath.divRoundingUp(amount << FixedPoint96.resolution, liquidity)
: FullMath.mulDivRoundingUp(amount, FixedPoint96.q96, liquidity));
require(sqrtPX96 > quotient, "SqrtPriceMath: A1");
return uint160(sqrtPX96 - quotient);
}
}
}
function getNextSqrtPriceFromInput(uint160 sqrtPX96, uint128 liquidity, uint256 amountIn, bool zeroForOne)
internal
pure
returns (uint160 sqrtQX96)
{
require(sqrtPX96 > 0, "SqrtPriceMath: PRICE");
require(liquidity > 0, "SqrtPriceMath: LIQ");
return zeroForOne
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
}
function getNextSqrtPriceFromOutput(uint160 sqrtPX96, uint128 liquidity, uint256 amountOut, bool zeroForOne)
internal
pure
returns (uint160 sqrtQX96)
{
require(sqrtPX96 > 0, "SqrtPriceMath: PRICE");
require(liquidity > 0, "SqrtPriceMath: LIQ");
return zeroForOne
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
}
function getAmount0Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity, bool roundUp)
internal
pure
returns (uint256 amount0)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 numerator1 = uint256(liquidity) << FixedPoint96.resolution;
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
require(sqrtRatioAX96 > 0, "SqrtPriceMath: A0Z");
return roundUp
? UnsafeMath.divRoundingUp(
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), sqrtRatioAX96
)
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
}
}
function getAmount1Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, uint128 liquidity, bool roundUp)
internal
pure
returns (uint256 amount1)
{
unchecked {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return roundUp
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.q96)
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.q96);
}
}
function getAmount0Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity)
internal
pure
returns (int256 amount0)
{
unchecked {
return liquidity < 0
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
}
function getAmount1Delta(uint160 sqrtRatioAX96, uint160 sqrtRatioBX96, int128 liquidity)
internal
pure
returns (int256 amount1)
{
unchecked {
return liquidity < 0
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
}
}