From b1328319ab20d081a02e25b239b2f7eca5de73b2 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 17 Apr 2022 16:38:17 -0700 Subject: [PATCH] fix(#39): changes liquidity quote math to liquidity method and proper rounding --- src/entities/pool.ts | 58 +++------- test/PeripheryManager.spec.ts | 209 ++++++++++++++++++++-------------- test/Pool.spec.ts | 74 ++++++++---- test/shared/fixture.ts | 68 +++++++++-- 4 files changed, 243 insertions(+), 166 deletions(-) diff --git a/src/entities/pool.ts b/src/entities/pool.ts index 760f757..fe7c1e6 100644 --- a/src/entities/pool.ts +++ b/src/entities/pool.ts @@ -17,7 +17,7 @@ import { weiToWei } from '../utils' export enum PoolSides { RISKY = 'RISKY', STABLE = 'STABLE', - RMM_LP = 'RMM_LP' + RMM_LP = 'RMM_LP', } /** @@ -206,7 +206,7 @@ export class Pool extends Calibration { reserveStable, liquidity, invariant, - chainId + chainId, } = pool.properties const risky = { address: riskyAddress, name: riskyName, symbol: riskySymbol, decimals: riskyDecimals } @@ -363,8 +363,7 @@ export class Pool extends Calibration { */ liquidityQuote(amount: Wei, sideOfPool: PoolSides): { delRisky: Wei; delStable: Wei; delLiquidity: Wei } { const { reserveRisky, reserveStable, liquidity } = this - const price = this.reportedPriceOfRisky - return Pool.getLiquidityQuote(amount, sideOfPool, reserveRisky, reserveStable, liquidity, price) + return Pool.getLiquidityQuote(amount, sideOfPool, reserveRisky, reserveStable, liquidity) } /** @@ -378,8 +377,7 @@ export class Pool extends Calibration { sideOfPool: PoolSides, reserveRisky: Wei, reserveStable: Wei, - liquidity: Wei, - reportedPriceOfRisky?: Wei + liquidity: Wei ): { delRisky: Wei; delStable: Wei; delLiquidity: Wei } { invariant(liquidity.gt(0), `Liquidity must be greater than zero`) @@ -393,43 +391,23 @@ export class Pool extends Calibration { reserveRisky.gt(0), `Reserve risky is 0. It must be greater than 0 because its being used as a denominator to compute LP tokens to mint.` ) - if (typeof reportedPriceOfRisky === 'undefined') { - delRisky = amount - delLiquidity = liquidity.mul(delRisky).div(reserveRisky) - delStable = reserveStable.mul(delLiquidity).div(liquidity) - } else { - delRisky = amount - delStable = reportedPriceOfRisky.mul(delRisky).div(parseWei(1, delRisky.decimals)) - delLiquidity = liquidity.mul(delRisky).div(reserveRisky) - const computedLiquidity = liquidity.mul(delStable).div(reserveStable) - delLiquidity = delLiquidity.lt(computedLiquidity) ? delLiquidity : computedLiquidity - } + delRisky = amount // not rounded + delLiquidity = liquidity.mul(delRisky).div(reserveRisky.add(1)) // rounds down + delStable = reserveStable.mul(delLiquidity).div(liquidity).add(1) // rounds up break case PoolSides.STABLE: invariant( reserveStable.gt(0), `Reserve stable is 0. It must be greater than 0 because its being used as a denominator to compute LP tokens to mint.` ) - - if (typeof reportedPriceOfRisky === 'undefined') { - delStable = amount - delLiquidity = liquidity.mul(delStable).div(reserveStable) - delRisky = reserveRisky.mul(delLiquidity).div(liquidity) - } else { - delStable = amount - delRisky = parseWei(1, delRisky.decimals) - .mul(delStable) - .div(reportedPriceOfRisky) - delLiquidity = liquidity.mul(delRisky).div(reserveRisky) - const computedLiquidity = liquidity.mul(delStable).div(reserveStable) - delLiquidity = delLiquidity.lt(computedLiquidity) ? delLiquidity : computedLiquidity - } - + delStable = amount // not rounded + delLiquidity = liquidity.mul(delStable).div(reserveStable.add(1)) // rounds down + delRisky = reserveRisky.mul(delLiquidity).div(liquidity).add(1) // rounds up break case PoolSides.RMM_LP: - delLiquidity = amount - delRisky = reserveRisky.mul(delLiquidity).div(liquidity) - delStable = reserveStable.mul(delLiquidity).div(liquidity) + delLiquidity = amount // not rounded + delRisky = reserveRisky.mul(delLiquidity).div(liquidity).add(1) // rounds up + delStable = reserveStable.mul(delLiquidity).div(liquidity).add(1) // rounds up break default: break @@ -465,12 +443,8 @@ export class Pool extends Calibration { // Computes the price of the token multiplied by amount of the token and dividing by 10^decimals, canceling out the tokens decimals const values = [ - parseWei(priceOfRisky, 18) - .mul(reserve0) - .div(parseWei(1, reserve0.decimals)), - parseWei(priceOfStable, 18) - .mul(reserve1) - .div(parseWei(1, reserve1.decimals)) + parseWei(priceOfRisky, 18).mul(reserve0).div(parseWei(1, reserve0.decimals)), + parseWei(priceOfStable, 18).mul(reserve1).div(parseWei(1, reserve1.decimals)), ] const sum = values[0].add(values[1]) // both have 18 decimals @@ -501,7 +475,7 @@ export class Pool extends Calibration { this.strike.float, this.sigma.float, this.gamma.float, - this.tau.add(120).years + this.tau.add(120).years, ] as const return args } diff --git a/test/PeripheryManager.spec.ts b/test/PeripheryManager.spec.ts index 9459400..3890bb0 100644 --- a/test/PeripheryManager.spec.ts +++ b/test/PeripheryManager.spec.ts @@ -8,19 +8,19 @@ import { Swaps } from '../src/entities/swaps' import { PeripheryManager } from '../src/peripheryManager' import { AddressOne } from './shared/constants' -import { usePool, usePoolWithDecimals, useWethPool } from './shared/fixture' +import { usePool, useImbalancedPool, usePoolWithDecimals, useWethPool } from './shared/fixture' import { Engine } from '../src/entities/engine' function decode(frag: string, data: any) { return PeripheryManager.INTERFACE.decodeFunctionData(frag, data) } -describe('Periphery Manager', function() { +describe('Periphery Manager', function () { let pool: Pool, from: string, wethPool: Pool, useNative: NativeCurrency, lowDecimalPool: Pool const slippageTolerance = parsePercentage(0.05) - beforeEach(async function() { + beforeEach(async function () { pool = usePool() wethPool = useWethPool() lowDecimalPool = usePoolWithDecimals(6) @@ -28,14 +28,14 @@ describe('Periphery Manager', function() { useNative = Ether.onChain(1) }) - it('getFactory returns the ethers factory', async function() { + it('getFactory returns the ethers factory', async function () { expect(PeripheryManager.getFactory()).toStrictEqual( new ContractFactory(PeripheryManager.INTERFACE, PeripheryManager.BYTECODE) ) }) - describe('#encodeCreate', function() { - it('successful', async function() { + describe('#encodeCreate', function () { + it('successful', async function () { const liquidity = parseWei(1, 18) const decimals = pool.risky.decimals @@ -63,7 +63,7 @@ describe('Periphery Manager', function() { pool.maturity.raw, pool.gamma.raw, riskyPerLp.raw, - liquidity.raw + liquidity.raw, ] const calldata = PeripheryManager.encodeCreate(pool, liquidity) @@ -71,20 +71,20 @@ describe('Periphery Manager', function() { data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) }) - it('fails with wrong liquidity decimals', async function() { + it('fails with wrong liquidity decimals', async function () { const liquidity = parseWei(1, 9) expect(() => PeripheryManager.encodeCreate(pool, liquidity)).toThrow() }) - it('fails if reference price is not set', async function() { + it('fails if reference price is not set', async function () { pool.referencePriceOfRisky = undefined const liquidity = parseWei(1, 18) expect(() => PeripheryManager.encodeCreate(pool, liquidity)).toThrow() }) }) - describe('#createCallParameters', function() { - it('successful', async function() { + describe('#createCallParameters', function () { + it('successful', async function () { const liquidity = parseWei(1, 18) const decimals = pool.risky.decimals const reference = pool.referencePriceOfRisky ?? pool.reportedPriceOfRisky @@ -111,7 +111,7 @@ describe('Periphery Manager', function() { pool.maturity.raw, pool.gamma.raw, riskyPerLp.raw, - liquidity.raw + liquidity.raw, ] const { calldata, value } = PeripheryManager.createCallParameters(pool, liquidity) @@ -121,8 +121,8 @@ describe('Periphery Manager', function() { }) }) - describe('#depositCallParameters', function() { - it('encoded calldata matches decoded arguments', async function() { + describe('#depositCallParameters', function () { + it('encoded calldata matches decoded arguments', async function () { const recipient = from const risky = pool.risky const stable = pool.stable @@ -135,7 +135,7 @@ describe('Periphery Manager', function() { expect(value).toBe('0x00') }) - it('uses native token successfully', async function() { + it('uses native token successfully', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -145,7 +145,7 @@ describe('Periphery Manager', function() { ).toBe(amountRisky.raw.toHexString()) }) - it('fails with wrong risky decimals', async function() { + it('fails with wrong risky decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1) const amountStable = parseWei(1, 6) @@ -154,7 +154,7 @@ describe('Periphery Manager', function() { ).toThrow() }) - it('fails with wrong stable decimals', async function() { + it('fails with wrong stable decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1, 6) const amountStable = parseWei(1) @@ -163,21 +163,21 @@ describe('Periphery Manager', function() { ).toThrow() }) - it('fails with address zero as recipient', async function() { + it('fails with address zero as recipient', async function () { const recipient = AddressZero const amountRisky = parseWei(1) const amountStable = parseWei(1) expect(() => PeripheryManager.depositCallParameters(pool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with 0 amounts', async function() { + it('fails with 0 amounts', async function () { const recipient = from const amountRisky = parseWei(0) const amountStable = parseWei(0) expect(() => PeripheryManager.depositCallParameters(pool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with using native on a pool which does not have a wrapped token', async function() { + it('fails with using native on a pool which does not have a wrapped token', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -188,8 +188,8 @@ describe('Periphery Manager', function() { }) }) - describe('#encodeWithdraw', function() { - it('successful', async function() { + describe('#encodeWithdraw', function () { + it('successful', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -199,7 +199,7 @@ describe('Periphery Manager', function() { data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) }) - it('uses native token successfully', async function() { + it('uses native token successfully', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -208,7 +208,7 @@ describe('Periphery Manager', function() { recipient, amountRisky, amountStable, - useNative + useNative, }) const unwrapCalldata = calldatas[1] @@ -222,35 +222,35 @@ describe('Periphery Manager', function() { sweepData.forEach((data, i) => expect(data).toStrictEqual(sweepDecoded[i])) }) - it('fails with wrong risky decimals', async function() { + it('fails with wrong risky decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1) const amountStable = parseWei(1, 6) expect(() => PeripheryManager.encodeWithdraw(lowDecimalPool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with wrong stable decimals', async function() { + it('fails with wrong stable decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1, 6) const amountStable = parseWei(1) expect(() => PeripheryManager.encodeWithdraw(lowDecimalPool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with address zero as recipient', async function() { + it('fails with address zero as recipient', async function () { const recipient = AddressZero const amountRisky = parseWei(1) const amountStable = parseWei(1) expect(() => PeripheryManager.encodeWithdraw(pool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with both 0 amounts', async function() { + it('fails with both 0 amounts', async function () { const recipient = from const amountRisky = parseWei(0) const amountStable = parseWei(0) expect(() => PeripheryManager.encodeWithdraw(pool, { recipient, amountRisky, amountStable })).toThrow() }) - it('fails with using native on a pool which does not have a wrapped token', async function() { + it('fails with using native on a pool which does not have a wrapped token', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -259,15 +259,15 @@ describe('Periphery Manager', function() { }) }) - describe('#withdrawCallParameters', function() { - it('successful', async function() { + describe('#withdrawCallParameters', function () { + it('successful', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) const { calldata, value } = PeripheryManager.withdrawCallParameters(pool, { recipient, amountRisky, - amountStable + amountStable, }) const data = [recipient, pool.address, amountRisky.raw, amountStable.raw] @@ -277,7 +277,7 @@ describe('Periphery Manager', function() { expect(value).toBe('0x00') }) - it('fails with wrong risky decimals', async function() { + it('fails with wrong risky decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1) const amountStable = parseWei(1, 6) @@ -286,7 +286,7 @@ describe('Periphery Manager', function() { ).toThrow() }) - it('fails with wrong stable decimals', async function() { + it('fails with wrong stable decimals', async function () { const recipient = AddressZero const amountRisky = parseWei(1, 6) const amountStable = parseWei(1) @@ -295,7 +295,7 @@ describe('Periphery Manager', function() { ).toThrow() }) - it('successful with multicall calldata bundle when using native', async function() { + it('successful with multicall calldata bundle when using native', async function () { const recipient = from const amountRisky = parseWei(1) const amountStable = parseWei(1) @@ -304,14 +304,14 @@ describe('Periphery Manager', function() { recipient, amountRisky, amountStable, - useNative + useNative, }) expect(value).toBe('0x00') }) }) - describe('#allocateCallParameters', function() { - it('successful', async function() { + describe('#allocateCallParameters', function () { + it('successful', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, pool.risky.decimals) @@ -324,7 +324,7 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) const data = [ recipient, @@ -333,14 +333,52 @@ describe('Periphery Manager', function() { pool.stable.address, delRisky.raw, delStable.raw, - fromMargin + fromMargin, + ] + const decoded = decode('allocate', calldata) + data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) + expect(value).toBe('0x00') + }) + + it('uses getLiquidityQuote to allocate liquidity', async function () { + const recipient = from + const fromMargin = false + const imbalancedPool = useImbalancedPool() + + const amount = parseWei(2, imbalancedPool.stable.decimals) + const sideOfPool = PoolSides.STABLE + const { delRisky, delStable, delLiquidity } = imbalancedPool.liquidityQuote(amount, sideOfPool) + + const { calldata, value } = PeripheryManager.allocateCallParameters(imbalancedPool, { + recipient, + fromMargin, + delRisky, + delStable, + delLiquidity, + slippageTolerance, + }) + const data = [ + recipient, + imbalancedPool.poolId, + imbalancedPool.risky.address, + imbalancedPool.stable.address, + delRisky.raw, + delStable.raw, + fromMargin, ] const decoded = decode('allocate', calldata) data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) expect(value).toBe('0x00') + + const actualLiquidity = imbalancedPool.liquidity.mul(delRisky).div(imbalancedPool.reserveRisky) + const computedLiquidity = imbalancedPool.liquidity.mul(amount).div(imbalancedPool.reserveStable) + const MAX_DEVIATION = 1 / 100 + + expect(computedLiquidity.float).toBeCloseTo(actualLiquidity.float + actualLiquidity.float * MAX_DEVIATION) + expect(computedLiquidity.float).toBeCloseTo(actualLiquidity.float - actualLiquidity.float * MAX_DEVIATION) }) - it('should have a minLiquidity equal to delLiquidity when slippageTolerance is 0', async function() { + it('should have a minLiquidity equal to delLiquidity when slippageTolerance is 0', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, pool.risky.decimals) @@ -353,7 +391,7 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance: parsePercentage(0) + slippageTolerance: parsePercentage(0), }) const data = [ recipient, @@ -362,7 +400,7 @@ describe('Periphery Manager', function() { pool.stable.address, delRisky.raw, delStable.raw, - fromMargin + fromMargin, ] const decoded = decode('allocate', calldata) data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) @@ -370,7 +408,7 @@ describe('Periphery Manager', function() { expect(decoded[decoded.length - 1].toString()).toStrictEqual(delLiquidity.toString()) }) - it('successful using native', async function() { + it('successful using native', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, wethPool.risky.decimals) @@ -384,7 +422,7 @@ describe('Periphery Manager', function() { delStable, delLiquidity, useNative, - slippageTolerance + slippageTolerance, }) const allocateData = [ @@ -394,7 +432,7 @@ describe('Periphery Manager', function() { wethPool.stable.address, delRisky.raw, delStable.raw, - fromMargin + fromMargin, ] const multicall = decode('multicall', calldata) @@ -413,7 +451,7 @@ describe('Periphery Manager', function() { ) }) - it('successful when creating pool instead', async function() { + it('successful when creating pool instead', async function () { const recipient = from const fromMargin = false const createPool = true @@ -428,7 +466,7 @@ describe('Periphery Manager', function() { delStable, delLiquidity, createPool, - slippageTolerance + slippageTolerance, }) const decimals = pool.risky.decimals @@ -454,7 +492,7 @@ describe('Periphery Manager', function() { wethPool.maturity.raw, wethPool.gamma.raw, riskyPerLp.raw, - delLiquidity.raw + delLiquidity.raw, ] const decoded = decode('create', calldata) @@ -463,7 +501,7 @@ describe('Periphery Manager', function() { expect(value).toBe('0x00') }) - it('successful when creating pool using native', async function() { + it('successful when creating pool using native', async function () { const recipient = from const fromMargin = false const createPool = true @@ -479,7 +517,7 @@ describe('Periphery Manager', function() { delLiquidity, createPool, useNative, - slippageTolerance + slippageTolerance, }) const decimals = pool.risky.decimals @@ -505,7 +543,7 @@ describe('Periphery Manager', function() { wethPool.maturity.raw, wethPool.gamma.raw, riskyPerLp.raw, - delLiquidity.raw + delLiquidity.raw, ] const multicall = decode('multicall', calldata) @@ -515,15 +553,10 @@ describe('Periphery Manager', function() { const refundETHDecoded = decode('refundETH', multicall.data[1]) expect(refundETHDecoded).toBeDefined() - expect(value).toBe( - riskyPerLp - .mul(delLiquidity) - .div(Engine.PRECISION) - .raw.toHexString() - ) + expect(value).toBe(riskyPerLp.mul(delLiquidity).div(Engine.PRECISION).raw.toHexString()) }) - it('fails if delRisky is 0', async function() { + it('fails if delRisky is 0', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0, pool.risky.decimals) @@ -537,12 +570,12 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails with wrong risky decimals', async function() { + it('fails with wrong risky decimals', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, lowDecimalPool.risky.decimals + 1) @@ -555,12 +588,12 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails with wrong stable decimals', async function() { + it('fails with wrong stable decimals', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, lowDecimalPool.risky.decimals) @@ -573,12 +606,12 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails if delStable is 0', async function() { + it('fails if delStable is 0', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, pool.risky.decimals) @@ -592,12 +625,12 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails if delLiquidity is 0', async function() { + it('fails if delLiquidity is 0', async function () { const recipient = from const fromMargin = false const delRisky = parseWei(0.3, pool.risky.decimals) @@ -611,12 +644,12 @@ describe('Periphery Manager', function() { delRisky, delStable, delLiquidity, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails when createPool and fromMargin are both true', async function() { + it('fails when createPool and fromMargin are both true', async function () { const recipient = from const fromMargin = true const createPool = true @@ -632,14 +665,14 @@ describe('Periphery Manager', function() { delStable, delLiquidity, createPool, - slippageTolerance + slippageTolerance, }) ).toThrow() }) }) - describe('#removeCallParameters', function() { - it('successful', async function() { + describe('#removeCallParameters', function () { + it('successful', async function () { const recipient = from const toMargin = true const delRisky = parseWei(0.3, pool.risky.decimals) @@ -656,7 +689,7 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance + slippageTolerance, }) const data = [pool.address, pool.poolId, delLiquidity.raw] @@ -665,7 +698,7 @@ describe('Periphery Manager', function() { expect(value).toBe('0x00') }) - it('should have same minRisky as expectedRisky and same minStable as expectedStable with 0 slippage tolerance', async function() { + it('should have same minRisky as expectedRisky and same minStable as expectedStable with 0 slippage tolerance', async function () { const recipient = from const toMargin = true const delLiquidity = parseWei(1, 18) @@ -681,7 +714,7 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance: parsePercentage(0) + slippageTolerance: parsePercentage(0), }) const data = [pool.address, pool.poolId, delLiquidity.raw] @@ -692,7 +725,7 @@ describe('Periphery Manager', function() { expect(decoded[decoded.length - 1].toString()).toStrictEqual(delStable.toString()) }) - it('fails if delLiquidity is zero', async function() { + it('fails if delLiquidity is zero', async function () { const recipient = from const toMargin = false const delRisky = parseWei(0.3, pool.risky.decimals) @@ -710,12 +743,12 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails with wrong risky decimals', async function() { + it('fails with wrong risky decimals', async function () { const recipient = from const toMargin = false const delRisky = parseWei(0.3, lowDecimalPool.risky.decimals + 1) @@ -732,12 +765,12 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails with wrong stable decimals', async function() { + it('fails with wrong stable decimals', async function () { const recipient = from const toMargin = false const delRisky = parseWei(0.3, lowDecimalPool.risky.decimals) @@ -754,12 +787,12 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance + slippageTolerance, }) ).toThrow() }) - it('fails with wrong liquidity decimals', async function() { + it('fails with wrong liquidity decimals', async function () { const recipient = from const toMargin = false const delRisky = parseWei(0.3, lowDecimalPool.risky.decimals) @@ -776,14 +809,14 @@ describe('Periphery Manager', function() { delRisky, delStable, recipient, - slippageTolerance + slippageTolerance, }) ).toThrow() }) }) - describe('#safeTransferFromParameters', function() { - it('successful', async function() { + describe('#safeTransferFromParameters', function () { + it('successful', async function () { const recipient = from const sender = from const amount = parseWei(0.1, pool.risky.decimals) @@ -798,8 +831,8 @@ describe('Periphery Manager', function() { }) }) - describe('#batchTransferFromParameters', function() { - it('successful', async function() { + describe('#batchTransferFromParameters', function () { + it('successful', async function () { const recipient = from const sender = from const amounts = [parseWei(0.1, pool.risky.decimals)] @@ -807,7 +840,7 @@ describe('Periphery Manager', function() { const { calldata, value } = PeripheryManager.batchTransferFromParameters({ sender, recipient, ids, amounts }) - const data = [sender, recipient, ids.map(v => BigNumber.from(v).toString()), amounts.map(v => v.raw), '0x'] + const data = [sender, recipient, ids.map((v) => BigNumber.from(v).toString()), amounts.map((v) => v.raw), '0x'] const decoded = decode('safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)', calldata) data.forEach((item, i) => expect(item.toString()).toStrictEqual(decoded[i].toString())) expect(value).toBe('0x00') diff --git a/test/Pool.spec.ts b/test/Pool.spec.ts index 1281cb3..f24f3e7 100644 --- a/test/Pool.spec.ts +++ b/test/Pool.spec.ts @@ -4,18 +4,20 @@ import { parseWei, Time } from 'web3-units' import { Swaps } from '../src/entities/swaps' import { Pool, PoolSides } from '../src/entities/pool' -import { usePool } from './shared/fixture' +import { usePool, useImbalancedPool } from './shared/fixture' import { AddressOne, EMPTY_CALIBRATION } from './shared' import { PoolInterface } from 'src' -describe('Test pool', function() { +describe('Test pool', function () { let pool: Pool + let imbalancedPool: Pool - beforeEach(async function() { + beforeEach(async function () { pool = usePool() + imbalancedPool = useImbalancedPool() }) - it('from', async function() { + it('from', async function () { const token0 = new Token(1, AddressOne, 18) const token1 = new Token(1, AddressOne, 18) const spot = parseWei(10, token1.decimals) @@ -28,7 +30,7 @@ describe('Test pool', function() { const reserve = { reserveRisky: '1.345867008995041e+21', reserveStable: stable ? parseWei(stable, token1.decimals).toString() : '0', - liquidity: parseWei(1, 18).toString() + liquidity: parseWei(1, 18).toString(), } const invariant = '3.345867008995041e+21' const uri: PoolInterface = { @@ -56,15 +58,15 @@ describe('Test pool', function() { reserveRisky: reserve.reserveRisky, reserveStable: reserve.reserveStable, liquidity: reserve.liquidity, - invariant: invariant - } + invariant: invariant, + }, } const pool = Pool.from(uri, spot.float) expect(pool.poolId).toBeDefined() }) - it('fromReferencePrice', async function() { + it('fromReferencePrice', async function () { const token0 = new Token(1, AddressOne, 18) const token1 = new Token(1, AddressOne, 18) const spot = parseWei(10, token1.decimals) @@ -77,7 +79,7 @@ describe('Test pool', function() { const reserve = { reserveRisky: '1.345867008995041e+21', reserveStable: stable ? parseWei(stable, token1.decimals).toString() : '0', - liquidity: parseWei(1, 18).toString() + liquidity: parseWei(1, 18).toString(), } const pool = Pool.fromReferencePrice( spot.float, @@ -89,7 +91,7 @@ describe('Test pool', function() { sigma: sigma.toString(), maturity: maturity.toString(), gamma: gamma.toString(), - lastTimestamp: lastTimestamp.toString() + lastTimestamp: lastTimestamp.toString(), }, token0.chainId ) @@ -98,66 +100,88 @@ describe('Test pool', function() { expect(pool.liquidity.toString()).toEqual(reserve.liquidity) }) - it('gets the current liquidity value', async function() { + it('gets the current liquidity value', async function () { const current = pool.getCurrentLiquidityValue(pool?.referencePriceOfRisky?.float ?? 1) expect(current.valuePerLiquidity.float).toBeGreaterThan(0) }) - it('#lastTimestamp', async function() { + it('#lastTimestamp', async function () { const time = new Time(Time.now + 10) pool.lastTimestamp = time expect(pool.lastTimestamp.raw).toEqual(time.raw) }) - it('pool.liquidityQuote() risky', async function() { + it('pool.liquidityQuote() risky', async function () { const amount = parseWei('0.5') const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RISKY) const delStable = pool.reserveStable.mul(liquidityQuote.delLiquidity).div(pool.liquidity) expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float) }) - it('pool.liquidityQuote() stable', async function() { + it('pool.liquidityQuote() stable', async function () { const amount = parseWei('0.5') const liquidityQuote = pool.liquidityQuote(amount, PoolSides.STABLE) const delRisky = pool.reserveRisky.mul(liquidityQuote.delLiquidity).div(pool.liquidity) expect(liquidityQuote.delRisky.float).toBeCloseTo(delRisky.float) }) - it('pool.liquidityQuote() RMM', async function() { + it('pool.liquidityQuote() RMM', async function () { const amount = parseWei('0.5') const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RMM_LP) const delStable = pool.reserveStable.mul(liquidityQuote.delLiquidity).div(pool.liquidity) expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float) }) - it('pool.liquidityQuote() with a fresh pool', async function() { + it('pool.liquidityQuote() with a fresh pool', async function () { const amount = parseWei('0.5') const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RISKY) const delStable = pool.reserveStable.mul(liquidityQuote.delLiquidity).div(pool.liquidity) expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float) }) - it('#remaining', async function() { + it('imbalancedPool.liquidityQuote() risky', async function () { + const amount = parseWei('0.5') + const liquidityQuote = imbalancedPool.liquidityQuote(amount, PoolSides.RISKY) + const delRisky = amount + const delLiquidity = imbalancedPool.liquidity.mul(delRisky).div(imbalancedPool.reserveRisky) + const delStable = imbalancedPool.reserveStable.mul(delLiquidity).div(imbalancedPool.liquidity) + expect(liquidityQuote.delRisky.float).toBeCloseTo(delRisky.float) + expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float) + expect(liquidityQuote.delLiquidity.float).toBeCloseTo(delLiquidity.float) + }) + + it('imbalancedPool.liquidityQuote() stable', async function () { + const amount = parseWei('0.5') + const liquidityQuote = imbalancedPool.liquidityQuote(amount, PoolSides.STABLE) + const delStable = amount + const delLiquidity = imbalancedPool.liquidity.mul(liquidityQuote.delRisky).div(imbalancedPool.reserveRisky) + const delRisky = imbalancedPool.reserveRisky.mul(delLiquidity).div(imbalancedPool.liquidity) + expect(liquidityQuote.delRisky.float).toBeCloseTo(delRisky.float) + expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float) + expect(liquidityQuote.delLiquidity.float).toBeCloseTo(delLiquidity.float) + }) + + it('#remaining', async function () { expect(pool.remaining.raw).toEqual(Time.now >= pool.maturity.raw ? 0 : pool.maturity.sub(pool.lastTimestamp).raw) }) - it('#expired', async function() { + it('#expired', async function () { expect(pool.expired).toBe(Time.now >= pool.maturity.raw ? true : false) }) - it('#delta', async function() { + it('#delta', async function () { expect(pool.delta).toBeGreaterThan(0) }) - it('#premium', async function() { + it('#premium', async function () { expect(pool.premium).toBeGreaterThan(0) }) - it('#inTheMoney', async function() { + it('#inTheMoney', async function () { expect(pool.inTheMoney).toBe(pool.reportedPriceOfRisky && pool.reportedPriceOfRisky.float >= pool.strike.float) }) - it('#reportedPriceOfRisky', async function() { + it('#reportedPriceOfRisky', async function () { expect(pool.reportedPriceOfRisky).toBeDefined() }) - it('#swapArgs', async function() { + it('#swapArgs', async function () { const args = pool.swapArgs const check = [ pool.risky.decimals, @@ -168,7 +192,7 @@ describe('Test pool', function() { pool.strike.float, pool.sigma.float, pool.gamma.float, - pool.tau.add(120).years + pool.tau.add(120).years, ] args.forEach((arg, i) => expect(arg).toStrictEqual(check[i])) }) @@ -192,7 +216,7 @@ describe('Test pool', function() { const amountIn = parseWei(0.0001, pool.stable.decimals).float expect(pool.amountOut(tokenIn, amountIn).output).toBeGreaterThan(0) }) */ - it('#derivativeOut', async function() { + it('#derivativeOut', async function () { const tokenIn = pool.risky const amountIn = 0 expect(pool.derivativeOut(tokenIn, amountIn)).toBeGreaterThan(0) diff --git a/test/shared/fixture.ts b/test/shared/fixture.ts index 38c5885..daaf732 100644 --- a/test/shared/fixture.ts +++ b/test/shared/fixture.ts @@ -18,7 +18,7 @@ export function usePoolWithDecimals(decimals: number): Pool { strike: parseWei(10, decimals).toString(), sigma: '1000', maturity: Time.YearInSeconds.toString(), - gamma: '9900' + gamma: '9900', }) const lastTimestamp = new Time(1) const tau = maturity.sub(lastTimestamp).years @@ -27,7 +27,7 @@ export function usePoolWithDecimals(decimals: number): Pool { const reserve = { reserveRisky: risky ? parseWei(risky, token0.decimals).toString() : '0', reserveStable: stable ? parseWei(stable, token1.decimals).toString() : '0', - liquidity: parseWei(1, 18).toString() + liquidity: parseWei(1, 18).toString(), } const uri: PoolInterface = { name: 'Pool', @@ -53,8 +53,8 @@ export function usePoolWithDecimals(decimals: number): Pool { lastTimestamp: lastTimestamp.toString(), reserveRisky: reserve.reserveRisky, reserveStable: reserve.reserveStable, - liquidity: reserve.liquidity - } + liquidity: reserve.liquidity, + }, } const pool = Pool.from(uri, spot.float) return pool @@ -73,7 +73,7 @@ export function usePool(): Pool { const reserve = { reserveRisky: risky ? parseWei(risky, token0.decimals).toString() : '0', reserveStable: stable ? parseWei(stable, token1.decimals).toString() : '0', - liquidity: parseWei(1, 18).toString() + liquidity: parseWei(1, 18).toString(), } const uri: PoolInterface = { name: 'Pool', @@ -99,8 +99,54 @@ export function usePool(): Pool { lastTimestamp: lastTimestamp.toString(), reserveRisky: reserve.reserveRisky, reserveStable: reserve.reserveStable, - liquidity: reserve.liquidity - } + liquidity: reserve.liquidity, + }, + } + const pool = Pool.from(uri, spot.float) + return pool +} + +export function useImbalancedPool(): Pool { + const token0 = new Token(1, AddressZero, 18) + const token1 = new Token(1, AddressZero, 18) + const spot = parseWei(10, token1.decimals) + + const { strike, sigma, maturity, gamma } = EMPTY_CALIBRATION + const lastTimestamp = new Time(1) + const tau = maturity.sub(lastTimestamp).years + const risky = Swaps.getRiskyReservesGivenReferencePrice(strike.float, sigma.float, tau, spot.float) + const stable = Swaps.getStableGivenRisky(strike.float, sigma.float, tau, risky) + const reserve = { + reserveRisky: risky ? parseWei(risky, token0.decimals).toString() : '0', + reserveStable: stable ? parseWei(stable * 1.1, token1.decimals).toString() : '0', + liquidity: parseWei(1, 18).toString(), + } + const uri: PoolInterface = { + name: 'Pool', + image: '', + license: '', + creator: '', + description: 'Regular pool', + properties: { + chainId: '1', + factory: AddressZero, + riskyName: token0.name, + riskyAddress: token0.address, + riskySymbol: token0.symbol, + riskyDecimals: token0.decimals, + stableName: token1.name, + stableDecimals: token1.decimals, + stableSymbol: token1.symbol, + stableAddress: token1.address, + strike: strike.toString(), + sigma: sigma.toString(), + maturity: maturity.toString(), + gamma: gamma.toString(), + lastTimestamp: lastTimestamp.toString(), + reserveRisky: reserve.reserveRisky, + reserveStable: reserve.reserveStable, + liquidity: reserve.liquidity, + }, } const pool = Pool.from(uri, spot.float) return pool @@ -115,7 +161,7 @@ export function useWethPool(): Pool { strike: parseWei(10, token1.decimals).toString(), sigma: '1000', maturity: Time.YearInSeconds.toString(), - gamma: '9900' + gamma: '9900', }) const lastTimestamp = new Time(1) const tau = maturity.sub(lastTimestamp).years @@ -124,7 +170,7 @@ export function useWethPool(): Pool { const reserve = { reserveRisky: risky ? parseWei(risky, token0.decimals).toString() : '0', reserveStable: stable ? parseWei(stable, token1.decimals).toString() : '0', - liquidity: parseWei(1, 18).toString() + liquidity: parseWei(1, 18).toString(), } const uri: PoolInterface = { name: 'Pool', @@ -150,8 +196,8 @@ export function useWethPool(): Pool { lastTimestamp: lastTimestamp.toString(), reserveRisky: reserve.reserveRisky, reserveStable: reserve.reserveStable, - liquidity: reserve.liquidity - } + liquidity: reserve.liquidity, + }, } const pool = Pool.from(uri, spot.float) return pool