mirror of
				https://github.com/Instadapp/dsa-connectors.git
				synced 2024-07-29 22:37:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			228 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Solidity
		
	
	
	
	
	
// SPDX-License-Identifier: BUSL-1.1
 | 
						|
pragma solidity >=0.5.0;
 | 
						|
 | 
						|
import './LowGasSafeMath.sol';
 | 
						|
import './SafeCast.sol';
 | 
						|
 | 
						|
import './FullMath.sol';
 | 
						|
import './UnsafeMath.sol';
 | 
						|
import './FixedPoint96.sol';
 | 
						|
 | 
						|
/// @title Functions based on Q64.96 sqrt price and liquidity
 | 
						|
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
 | 
						|
library SqrtPriceMath {
 | 
						|
    using LowGasSafeMath for uint256;
 | 
						|
    using SafeCast for uint256;
 | 
						|
 | 
						|
    /// @notice Gets the next sqrt price given a delta of token0
 | 
						|
    /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
 | 
						|
    /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
 | 
						|
    /// price less in order to not send too much output.
 | 
						|
    /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
 | 
						|
    /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
 | 
						|
    /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param amount How much of token0 to add or remove from virtual reserves
 | 
						|
    /// @param add Whether to add or remove the amount of token0
 | 
						|
    /// @return The price after adding or removing amount, depending on add
 | 
						|
    function getNextSqrtPriceFromAmount0RoundingUp(
 | 
						|
        uint160 sqrtPX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        uint256 amount,
 | 
						|
        bool add
 | 
						|
    ) internal pure returns (uint160) {
 | 
						|
        // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
 | 
						|
        if (amount == 0) return sqrtPX96;
 | 
						|
        uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
 | 
						|
 | 
						|
        if (add) {
 | 
						|
            uint256 product;
 | 
						|
            if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
 | 
						|
                uint256 denominator = numerator1 + product;
 | 
						|
                if (denominator >= numerator1)
 | 
						|
                    // always fits in 160 bits
 | 
						|
                    return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
 | 
						|
            }
 | 
						|
 | 
						|
            return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
 | 
						|
        } else {
 | 
						|
            uint256 product;
 | 
						|
            // if the product overflows, we know the denominator underflows
 | 
						|
            // in addition, we must check that the denominator does not underflow
 | 
						|
            require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
 | 
						|
            uint256 denominator = numerator1 - product;
 | 
						|
            return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Gets the next sqrt price given a delta of token1
 | 
						|
    /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
 | 
						|
    /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
 | 
						|
    /// price less in order to not send too much output.
 | 
						|
    /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
 | 
						|
    /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param amount How much of token1 to add, or remove, from virtual reserves
 | 
						|
    /// @param add Whether to add, or remove, the amount of token1
 | 
						|
    /// @return The price after adding or removing `amount`
 | 
						|
    function getNextSqrtPriceFromAmount1RoundingDown(
 | 
						|
        uint160 sqrtPX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        uint256 amount,
 | 
						|
        bool add
 | 
						|
    ) internal pure returns (uint160) {
 | 
						|
        // if we're adding (subtracting), rounding down requires rounding the quotient down (up)
 | 
						|
        // in both cases, avoid a mulDiv for most inputs
 | 
						|
        if (add) {
 | 
						|
            uint256 quotient =
 | 
						|
                (
 | 
						|
                    amount <= type(uint160).max
 | 
						|
                        ? (amount << FixedPoint96.RESOLUTION) / liquidity
 | 
						|
                        : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
 | 
						|
                );
 | 
						|
 | 
						|
            return uint256(sqrtPX96).add(quotient).toUint160();
 | 
						|
        } else {
 | 
						|
            uint256 quotient =
 | 
						|
                (
 | 
						|
                    amount <= type(uint160).max
 | 
						|
                        ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
 | 
						|
                        : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
 | 
						|
                );
 | 
						|
 | 
						|
            require(sqrtPX96 > quotient);
 | 
						|
            // always fits 160 bits
 | 
						|
            return uint160(sqrtPX96 - quotient);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Gets the next sqrt price given an input amount of token0 or token1
 | 
						|
    /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
 | 
						|
    /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param amountIn How much of token0, or token1, is being swapped in
 | 
						|
    /// @param zeroForOne Whether the amount in is token0 or token1
 | 
						|
    /// @return sqrtQX96 The price after adding the input amount to token0 or token1
 | 
						|
    function getNextSqrtPriceFromInput(
 | 
						|
        uint160 sqrtPX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        uint256 amountIn,
 | 
						|
        bool zeroForOne
 | 
						|
    ) internal pure returns (uint160 sqrtQX96) {
 | 
						|
        require(sqrtPX96 > 0);
 | 
						|
        require(liquidity > 0);
 | 
						|
 | 
						|
        // round to make sure that we don't pass the target price
 | 
						|
        return
 | 
						|
            zeroForOne
 | 
						|
                ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
 | 
						|
                : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Gets the next sqrt price given an output amount of token0 or token1
 | 
						|
    /// @dev Throws if price or liquidity are 0 or the next price is out of bounds
 | 
						|
    /// @param sqrtPX96 The starting price before accounting for the output amount
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param amountOut How much of token0, or token1, is being swapped out
 | 
						|
    /// @param zeroForOne Whether the amount out is token0 or token1
 | 
						|
    /// @return sqrtQX96 The price after removing the output amount of token0 or token1
 | 
						|
    function getNextSqrtPriceFromOutput(
 | 
						|
        uint160 sqrtPX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        uint256 amountOut,
 | 
						|
        bool zeroForOne
 | 
						|
    ) internal pure returns (uint160 sqrtQX96) {
 | 
						|
        require(sqrtPX96 > 0);
 | 
						|
        require(liquidity > 0);
 | 
						|
 | 
						|
        // round to make sure that we pass the target price
 | 
						|
        return
 | 
						|
            zeroForOne
 | 
						|
                ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
 | 
						|
                : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Gets the amount0 delta between two prices
 | 
						|
    /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
 | 
						|
    /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
 | 
						|
    /// @param sqrtRatioAX96 A sqrt price
 | 
						|
    /// @param sqrtRatioBX96 Another sqrt price
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param roundUp Whether to round the amount up or down
 | 
						|
    /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
 | 
						|
    function getAmount0Delta(
 | 
						|
        uint160 sqrtRatioAX96,
 | 
						|
        uint160 sqrtRatioBX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        bool roundUp
 | 
						|
    ) internal pure returns (uint256 amount0) {
 | 
						|
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
 | 
						|
 | 
						|
        uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
 | 
						|
        uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
 | 
						|
 | 
						|
        require(sqrtRatioAX96 > 0);
 | 
						|
 | 
						|
        return
 | 
						|
            roundUp
 | 
						|
                ? UnsafeMath.divRoundingUp(
 | 
						|
                    FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
 | 
						|
                    sqrtRatioAX96
 | 
						|
                )
 | 
						|
                : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Gets the amount1 delta between two prices
 | 
						|
    /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
 | 
						|
    /// @param sqrtRatioAX96 A sqrt price
 | 
						|
    /// @param sqrtRatioBX96 Another sqrt price
 | 
						|
    /// @param liquidity The amount of usable liquidity
 | 
						|
    /// @param roundUp Whether to round the amount up, or down
 | 
						|
    /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
 | 
						|
    function getAmount1Delta(
 | 
						|
        uint160 sqrtRatioAX96,
 | 
						|
        uint160 sqrtRatioBX96,
 | 
						|
        uint128 liquidity,
 | 
						|
        bool roundUp
 | 
						|
    ) internal pure returns (uint256 amount1) {
 | 
						|
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
 | 
						|
 | 
						|
        return
 | 
						|
            roundUp
 | 
						|
                ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
 | 
						|
                : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Helper that gets signed token0 delta
 | 
						|
    /// @param sqrtRatioAX96 A sqrt price
 | 
						|
    /// @param sqrtRatioBX96 Another sqrt price
 | 
						|
    /// @param liquidity The change in liquidity for which to compute the amount0 delta
 | 
						|
    /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
 | 
						|
    function getAmount0Delta(
 | 
						|
        uint160 sqrtRatioAX96,
 | 
						|
        uint160 sqrtRatioBX96,
 | 
						|
        int128 liquidity
 | 
						|
    ) internal pure returns (int256 amount0) {
 | 
						|
        return
 | 
						|
            liquidity < 0
 | 
						|
                ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
 | 
						|
                : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
 | 
						|
    }
 | 
						|
 | 
						|
    /// @notice Helper that gets signed token1 delta
 | 
						|
    /// @param sqrtRatioAX96 A sqrt price
 | 
						|
    /// @param sqrtRatioBX96 Another sqrt price
 | 
						|
    /// @param liquidity The change in liquidity for which to compute the amount1 delta
 | 
						|
    /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
 | 
						|
    function getAmount1Delta(
 | 
						|
        uint160 sqrtRatioAX96,
 | 
						|
        uint160 sqrtRatioBX96,
 | 
						|
        int128 liquidity
 | 
						|
    ) internal pure returns (int256 amount1) {
 | 
						|
        return
 | 
						|
            liquidity < 0
 | 
						|
                ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
 | 
						|
                : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
 | 
						|
    }
 | 
						|
}
 |