Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,80 @@ describe('concentrate-liquidity-math', () => {
expect(actualGt).toBe(330119361793825978647n)
})

describe('single-sided position at 1800 USDC per WETH market price', () => {
// USDC < WETH
const USDC_DECIMALS = 6n
const WETH_DECIMALS = 18n

const sqrtFromHumanUsdcPerWeth = (usdcPerWeth: bigint): bigint => {
const rawPrice = (10n ** WETH_DECIMALS * ONE_E18) / (usdcPerWeth * 10n ** USDC_DECIMALS)

return bigintSqrt(rawPrice * ONE_E18)
}

// Higher USDC-per-WETH human quote => lower sqrt(P) for this pair
const sqrtPrice1500 = sqrtFromHumanUsdcPerWeth(1500n)
const sqrtPrice1800 = sqrtFromHumanUsdcPerWeth(1800n)
const sqrtPrice2200 = sqrtFromHumanUsdcPerWeth(2200n)

const maxAvailableBalanceUSDC = parseUnits('1000000', 6)
const maxAvailableBalanceWETH = parseUnits('400', 18)

it('should build a WETH-only position when the range (1800 - 2200) is above the market', () => {
// Spot at 1800 equals the sqrt max bound => no USDC is needed
const { targetL, actualLt, actualGt } = computeLiquidityFromAmounts(
maxAvailableBalanceUSDC,
maxAvailableBalanceWETH,
sqrtPrice1800,
sqrtPrice2200,
sqrtPrice1800,
)

expect(actualLt).toBe(0n)
expect(actualGt).toBe(399999999999999999543n)
expect(targetL).toBe(177765578793446005n)
})

it('should build a USDC-only position when the range (1500 - 1800) is below the market', () => {
// Spot at 1800 equals the sqrt min bound => no WETH is needed
const { targetL, actualLt, actualGt } = computeLiquidityFromAmounts(
maxAvailableBalanceUSDC,
maxAvailableBalanceWETH,
sqrtPrice1800,
sqrtPrice1800,
sqrtPrice1500,
)

expect(actualLt).toBe(999999999999n)
expect(actualGt).toBe(0n)
expect(targetL).toBe(270520801110890067n)
})

it('should recover the spot at the range bound from WETH-only balances', () => {
const { liquidity, sqrtPriceSpot } = computeLiquidityAndPrice(
0n,
399999999999999999543n,
sqrtPrice2200,
sqrtPrice1800,
)

expect(liquidity).toBe(177765578793445997n)
expect(sqrtPriceSpot).toBe(23570226039552883488070n)
})

it('should recover the spot at the range bound from USDC-only balances', () => {
const { liquidity, sqrtPriceSpot } = computeLiquidityAndPrice(
999999999999n,
0n,
sqrtPrice1800,
sqrtPrice1500,
)

expect(liquidity).toBe(270520801110619533n)
expect(sqrtPriceSpot).toBe(23570226039551772382874n)
})
})

it('should compute available liquidity for the given price range and one specified amount', () => {
// USDC < WETH
const USDC_DECIMALS = 6n
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,85 @@ describe('PriceRange', () => {
})
})

describe('single-sided positions (1800 USDC per WETH market price)', () => {
const maxUsdc = 1_000_000n * 10n ** 6n
const maxWeth = 400n * 10n ** 18n

const maxReservesUsdcWeth = (): TokenReserves => ({
reserveA: TokenReserve.new({ token: USDC, reserve: maxUsdc }),
reserveB: TokenReserve.new({ token: WETH, reserve: maxWeth }),
})

/**
* Range 1800-2200 USDC per WETH with market at 1800: spot sits at the range bound
* closest to the market. Bounds are ordered by sqrt(P), so the higher human quote
* (2200) is the `minPrice` and the lower one (1800) is the `maxPrice`.
*/
const rangeAboveMarket = (): PriceRange =>
PriceRange.new({
minPrice: Price.fromHuman('2200', pairUsdcQuoteWethBase),
spotPrice: Price.fromHuman('1800', pairUsdcQuoteWethBase),
maxPrice: Price.fromHuman('1800', pairUsdcQuoteWethBase),
})

/** Range 1500-1800 USDC per WETH with market at 1800 (spot at the other bound). */
const rangeBelowMarket = (): PriceRange =>
PriceRange.new({
minPrice: Price.fromHuman('1800', pairUsdcQuoteWethBase),
spotPrice: Price.fromHuman('1800', pairUsdcQuoteWethBase),
maxPrice: Price.fromHuman('1500', pairUsdcQuoteWethBase),
})

it('should allocate only WETH when the range is above the market price', () => {
const result = rangeAboveMarket().computeMaxAllocation(maxReservesUsdcWeth())

expect(result.reserve0.reserve).toBe(0n)
expect(result.reserve1.reserve).toBe(399999999999999999543n)
})

it('should allocate only USDC when the range is below the market price', () => {
const result = rangeBelowMarket().computeMaxAllocation(maxReservesUsdcWeth())

expect(result.reserve0.reserve).toBe(999999999999n)
expect(result.reserve1.reserve).toBe(0n)
})

it('should compute WETH-only fixed allocation for the range above the market price', () => {
const fixedWeth = 100n * 10n ** 18n

const result = rangeAboveMarket().computeFixedAllocation(
TokenReserve.new({ token: WETH, reserve: fixedWeth }),
)

expect(result.reserve0.reserve).toBe(0n)
expect(result.reserve1.reserve).toBe(99999999999999999323n)
})

it('should compute USDC-only fixed allocation for the range below the market price', () => {
const fixedUsdc = 500_000n * 10n ** 6n

const result = rangeBelowMarket().computeFixedAllocation(
TokenReserve.new({ token: USDC, reserve: fixedUsdc }),
)

expect(result.reserve0.reserve).toBe(499999999999n)
expect(result.reserve1.reserve).toBe(0n)
})

it('should recover the 1800 spot price from a USDC-only allocation', () => {
const range = rangeBelowMarket()
const allocation = range.computeMaxAllocation(maxReservesUsdcWeth())

const recovered = PriceRange.fromPriceBounds(
{ minPrice: range.minPrice, maxPrice: range.maxPrice },
{ reserveA: allocation.reserve0, reserveB: allocation.reserve1 },
)

expect(recovered.spotPrice.toSqrt()).toBe(23570226039551772382874n)
expect(recovered.spotPrice.toHuman(USDC)).toBe('1800')
})
})

describe('fromPriceBounds', () => {
const sqrtPriceMin = 9n * 10n ** 17n
const sqrtPriceMax = 11n * 10n ** 17n
Expand Down
Loading
Loading