diff --git a/contracts/math/clamm.ral b/contracts/math/clamm.ral index e1b9fca..3477b41 100644 --- a/contracts/math/clamm.ral +++ b/contracts/math/clamm.ral @@ -1,6 +1,4 @@ -Contract CLAMM( - uints: Uints -) extends Log(){ +Contract CLAMM(uints: Uints) extends Log() { enum CLAMMError { InvalidTickIndex = 800 InvalidTickSpacing = 801 @@ -131,10 +129,17 @@ Contract CLAMM( deltaSqrtPrice = sqrtPriceB - sqrtPriceA } + let mut result = U512 { lower: 0, higher: 0 } + if (roundingUp) { - return (mulUp(deltaSqrtPrice, liquidity, LiquidityScale) + almostOne(SqrtPriceScale)) / one(SqrtPriceScale) + result = uints.bigMulDiv256(deltaSqrtPrice, liquidity, one(LiquidityScale)) + result = uints.bigAdd512(result, uints.toU512(almostOne(SqrtPriceScale))) + result = uints.bigDiv512(result, one(SqrtPriceScale), 1) + return uints.toU256(result) } else { - return mul(deltaSqrtPrice, liquidity, LiquidityScale) / one(SqrtPriceScale) + result = uints.bigMulDiv256(deltaSqrtPrice, liquidity, one(LiquidityScale)) + result = uints.bigDiv512(result, one(SqrtPriceScale), 1) + return uints.toU256(result) } } diff --git a/contracts/math/uints.ral b/contracts/math/uints.ral index 8baddb3..356e47e 100644 --- a/contracts/math/uints.ral +++ b/contracts/math/uints.ral @@ -49,9 +49,9 @@ Contract Uints () { } pub fn bigAdd512(a: U512, b: U512) -> U512 { - let (aLowerBLower, overflow) = overflowingAdd(a.lower, b.lower) - let aHigherBHigherOverflow = wrappingAdd(wrappingAdd(a.higher, b.higher), overflow) - return U512 { lower: aLowerBLower, higher: aHigherBHigherOverflow } + let (aLowerB, overflow) = overflowingAdd(a.lower, b.lower) + let aHigherOverflow = wrappingAdd(wrappingAdd(a.higher, b.higher), overflow) + return U512 { lower: aLowerB, higher: aHigherOverflow } } pub fn bigDivWrapper(a: U512, b: U256, bDenominator: U256, up: Bool) -> U512 { @@ -133,7 +133,7 @@ Contract Uints () { pub fn bigMulDivUp256(a: U256, b: U256, bDenominator: U256) -> U512 { let mut result = bigMul256(a, b) - result = bigAdd512(result, U512 { higher: 0, lower: bDenominator - 1 }) + result = bigAdd512(result, toU512(bDenominator - 1)) result = bigDiv512(result, bDenominator, 1) return result diff --git a/test/clamm.test.ts b/test/clamm.test.ts index 891b5b9..40de60b 100644 --- a/test/clamm.test.ts +++ b/test/clamm.test.ts @@ -536,19 +536,143 @@ describe('math tests', () => { expect(resultDown).toEqual(0n) }) }) - test('get delta y', async () => { - const clamm = await deployCLAMM(sender) - const sqrtPriceA = 234878324943782000000000000n - const sqrtPriceB = 87854456421658000000000000n - const liquidity = 983983249092n - const paramsUp = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: true } } - const paramsDown = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } - const resultUp = (await clamm.contractInstance.methods.getDeltaY(paramsUp)).returns - const resultDown = (await clamm.contractInstance.methods.getDeltaY(paramsDown)).returns - // 144669023.842474597804911408 - expect(resultUp).toEqual(1446690239n) - expect(resultDown).toEqual(1446690238n) + + describe('get delta y', () => { + let clamm: CLAMMInstance + + beforeEach(async () => { + clamm = (await deployCLAMM(sender)).contractInstance + }) + + test('zero at zero liquidity', async () => { + const sqrtPriceA = 1_000000000000000000000000n + const sqrtPriceB = 1_000000000000000000000000n + const liquidity = 0n + + const params = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + const result = await clamm.methods.getDeltaY(params) + + expect(result.returns).toEqual(0n) + }) + + test('equal at equal liquidity', async () => { + const sqrtPriceA = 1_000000000000000000000000n + const sqrtPriceB = 2_000000000000000000000000n + const liquidity = 2_00000n + + const params = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + const result = await clamm.methods.getDeltaY(params) + + expect(result.returns).toEqual(2n) + }) + + test('big numbers', async () => { + const sqrtPriceA = 234_878324943782000000000000n + const sqrtPriceB = 87_854456421658000000000000n + const liquidity = 9839832_49092n + + const paramsUp = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: true } } + const paramsDown = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + const resultUp = await clamm.methods.getDeltaY(paramsUp) + const resultDown = await clamm.methods.getDeltaY(paramsDown) + + expect(resultUp.returns).toEqual(1446690239n) + expect(resultDown.returns).toEqual(1446690238n) + }) + + test('big', async () => { + const sqrtPriceA = 1_000000000000000000000000n + const sqrtPriceB = 2_000000000000000000000000n + const liquidity = (2n ** 64n - 1n) * 1_00000n + + const paramsUp = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: true } } + const paramsDown = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + const resultUp = await clamm.methods.getDeltaY(paramsUp) + const resultDown = await clamm.methods.getDeltaY(paramsDown) + + expect(resultUp.returns).toEqual(liquidity / 1_00000n) + expect(resultDown.returns).toEqual(liquidity / 1_00000n) + }) + + test('overflow', async () => { + const sqrtPriceA = 1_000000000000000000000000n + const sqrtPriceB = 2n ** 256n - 1n + const liquidity = 2n ** 256n - 1n + + const paramsUp = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: true } } + const paramsDown = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + await expectError(clamm.methods.getDeltaY(paramsUp)) + await expectError(clamm.methods.getDeltaY(paramsDown)) + }) + + test('huge liquidity', async () => { + const sqrtPriceA = 1_000000000000000000000000n + const sqrtPriceB = 1_000000000000000001000000n + const liquidity = 2n ** 256n - 1n + + const paramsUp = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: true } } + const paramsDown = { args: { sqrtPriceA, sqrtPriceB, liquidity, roundingUp: false } } + const resultUp = await clamm.methods.getDeltaY(paramsUp) + const resultDown = await clamm.methods.getDeltaY(paramsDown) + + expect(resultUp.returns).toEqual(1157920892373161954235709850086879078532699846656405641n) + expect(resultDown.returns).toEqual(1157920892373161954235709850086879078532699846656405640n) + }) + }) + + describe('get delta y - domain', () => { + let clamm: CLAMMInstance + const minSqrtPrice = 15258932000000000000n + const maxSqrtPrice = 65535_383934512647000000000000n + const minLiquidity = 1n + const maxLiquidity = 2n ** 256n - 1n + + beforeEach(async () => { + clamm = (await deployCLAMM(sender)).contractInstance + }) + + it('maximize delta sqrt price and liquidity', async () => { + const paramsUp = { + args: { sqrtPriceA: maxSqrtPrice, sqrtPriceB: minSqrtPrice, liquidity: maxLiquidity, roundingUp: true } + } + const paramsDown = { + args: { sqrtPriceA: maxSqrtPrice, sqrtPriceB: minSqrtPrice, liquidity: maxLiquidity, roundingUp: false } + } + const resultUp = await clamm.methods.getDeltaY(paramsUp) + const resultDown = await clamm.methods.getDeltaY(paramsDown) + + expect(resultUp.returns).toEqual(75884790229800029582010010030152469040784228171629896039591333116952600000001n) + expect(resultDown.returns).toEqual(75884790229800029582010010030152469040784228171629896039591333116952599999999n) + }) + + it('can be zero', async () => { + const params = { + args: { sqrtPriceA: maxSqrtPrice, sqrtPriceB: maxSqrtPrice - 1n, liquidity: minLiquidity, roundingUp: false } + } + const result = await clamm.methods.getDeltaY(params) + + expect(result.returns).toEqual(0n) + }) + + it('liquidity is zero', async () => { + const params = { + args: { sqrtPriceA: maxSqrtPrice, sqrtPriceB: minSqrtPrice, liquidity: 0n, roundingUp: true } + } + const result = await clamm.methods.getDeltaY(params) + + expect(result.returns).toEqual(0n) + }) + + it('all max', async () => { + const params = { + args: { sqrtPriceA: maxSqrtPrice, sqrtPriceB: maxSqrtPrice, liquidity: maxLiquidity, roundingUp: true } + } + const result = await clamm.methods.getDeltaY(params) + + expect(result.returns).toEqual(0n) + }) }) + test('get next sqrt price x up', async () => { const clamm = await deployCLAMM(sender) {