Skip to content

Commit

Permalink
Merge pull request #38 from primitivefinance/rc
Browse files Browse the repository at this point in the history
fix(liquidity-quote): updates quote math using reported price
  • Loading branch information
Alexangelj authored Mar 21, 2022
2 parents b1ecb5c + 3ce707c commit 8d7f863
Show file tree
Hide file tree
Showing 6 changed files with 2,450 additions and 2,069 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"prettier": ">=2.0.0"
},
"dependencies": {
"@primitivefi/rmm-manager": "^1.0.0",
"@primitivefi/rmm-math": "^2.0.0-rc.1",
"@primitivefi/rmm-manager": "^1.1.0",
"@primitivefi/rmm-math": "^2.0.0-beta.3",
"@uniswap/sdk-core": "^3.0.1",
"shelljs": "0.8.5",
"tiny-invariant": "^1.1.0",
Expand All @@ -52,7 +52,7 @@
"ethers": "^5.4.6",
"husky": "^7.0.4",
"npm-run-all": "^4.1.5",
"tsdx": "^0.14.1",
"tsdx": "^0.7.2",
"web3-units": "^1.3.3"
},
"engines": {
Expand Down
39 changes: 31 additions & 8 deletions src/entities/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ export class Pool extends Calibration {
*/
liquidityQuote(amount: Wei, sideOfPool: PoolSides): { delRisky: Wei; delStable: Wei; delLiquidity: Wei } {
const { reserveRisky, reserveStable, liquidity } = this
return Pool.getLiquidityQuote(amount, sideOfPool, reserveRisky, reserveStable, liquidity)
const price = this.reportedPriceOfRisky
return Pool.getLiquidityQuote(amount, sideOfPool, reserveRisky, reserveStable, liquidity, price)
}

/**
Expand All @@ -377,7 +378,8 @@ export class Pool extends Calibration {
sideOfPool: PoolSides,
reserveRisky: Wei,
reserveStable: Wei,
liquidity: Wei
liquidity: Wei,
reportedPriceOfRisky?: Wei
): { delRisky: Wei; delStable: Wei; delLiquidity: Wei } {
invariant(liquidity.gt(0), `Liquidity must be greater than zero`)

Expand All @@ -391,18 +393,38 @@ 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.`
)
delRisky = amount
delLiquidity = liquidity.mul(delRisky).div(reserveRisky)
delStable = reserveStable.mul(delLiquidity).div(liquidity)
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
}
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.`
)
delStable = amount
delLiquidity = liquidity.mul(delStable).div(reserveStable)
delRisky = reserveRisky.mul(delLiquidity).div(liquidity)

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
}

break
case PoolSides.RMM_LP:
delLiquidity = amount
Expand Down Expand Up @@ -464,6 +486,7 @@ export class Pool extends Calibration {
const tau = this.tau.years
const spot = Swaps.getReportedPriceOfRisky(risky, this.strike.float, this.sigma.float, tau)
if (isNaN(spot)) return undefined
if (!isFinite(spot)) return undefined
return parseWei(spot, this.stable.decimals)
}

Expand Down
18 changes: 16 additions & 2 deletions src/peripheryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,9 +404,12 @@ export abstract class PeripheryManager extends SelfPermit {
)

// if curve should be created
let createData: string | undefined = undefined

if (options.createPool) {
invariant(!options.fromMargin, 'Cannot pay from margin when creating, set fromMargin to false.')
calldatas.push(PeripheryManager.encodeCreate(pool, options.delLiquidity))
createData = PeripheryManager.encodeCreate(pool, options.delLiquidity)
calldatas.push(createData)
} else {
calldatas.push(
PeripheryManager.INTERFACE.encodeFunctionData('allocate', [
Expand All @@ -429,7 +432,18 @@ export abstract class PeripheryManager extends SelfPermit {
const wrapped = options.useNative.wrapped
invariant(pool.risky.equals(wrapped) || pool.stable.equals(wrapped), 'No Weth')

const wrappedAmount = pool.risky.equals(wrapped) ? options.delRisky.raw : options.delStable.raw
let wrappedAmount: BigNumber

if (options.createPool && typeof createData !== 'undefined') {
const decoded = PeripheryManager.INTERFACE.decodeFunctionData('create', createData)
const riskyPerLp: BigNumber = decoded[decoded.length - 2]
const liquidity: BigNumber = decoded[decoded.length - 1]
const amount: BigNumber = riskyPerLp.mul(liquidity).div(Engine.PRECISION.raw) // weth token per liquidity * liquidity / 1e18

wrappedAmount = pool.risky.equals(wrapped) ? amount : options.delStable.raw
} else {
wrappedAmount = pool.risky.equals(wrapped) ? options.delRisky.raw : options.delStable.raw
}

if (wrappedAmount.gte(0)) {
calldatas.push(PeripheryManager.INTERFACE.encodeFunctionData('refundETH'))
Expand Down
61 changes: 61 additions & 0 deletions test/PeripheryManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PeripheryManager } from '../src/peripheryManager'

import { AddressOne } from './shared/constants'
import { usePool, usePoolWithDecimals, useWethPool } from './shared/fixture'
import { Engine } from '../src/entities/engine'

function decode(frag: string, data: any) {
return PeripheryManager.INTERFACE.decodeFunctionData(frag, data)
Expand Down Expand Up @@ -462,6 +463,66 @@ describe('Periphery Manager', function() {
expect(value).toBe('0x00')
})

it('successful when creating pool using native', async function() {
const recipient = from
const fromMargin = false
const createPool = true
const delRisky = parseWei(0.3, wethPool.risky.decimals)
const delStable = parseWei(3, wethPool.stable.decimals)
const delLiquidity = parseWei(1, 18)

const { calldata, value } = PeripheryManager.allocateCallParameters(wethPool, {
recipient,
fromMargin,
delRisky,
delStable,
delLiquidity,
createPool,
useNative,
slippageTolerance
})

const decimals = pool.risky.decimals
const reference = pool.referencePriceOfRisky ?? pool.reportedPriceOfRisky
const riskyPerLp = reference
? parseWei(
Swaps.getRiskyReservesGivenReferencePrice(
pool.strike.float,
pool.sigma.float,
pool.tau.years,
reference.float
),
decimals
)
: undefined
if (!riskyPerLp) throw Error('Risky per lp is undefined')

const createData = [
wethPool.risky.address,
wethPool.stable.address,
wethPool.strike.raw,
wethPool.sigma.raw,
wethPool.maturity.raw,
wethPool.gamma.raw,
riskyPerLp.raw,
delLiquidity.raw
]

const multicall = decode('multicall', calldata)

const createDecoded = decode('create', multicall.data[0])
createData.forEach((item, i) => expect(item.toString()).toStrictEqual(createDecoded[i].toString()))

const refundETHDecoded = decode('refundETH', multicall.data[1])
expect(refundETHDecoded).toBeDefined()
expect(value).toBe(
riskyPerLp
.mul(delLiquidity)
.div(Engine.PRECISION)
.raw.toHexString()
)
})

it('fails if delRisky is 0', async function() {
const recipient = from
const fromMargin = false
Expand Down
8 changes: 4 additions & 4 deletions test/Pool.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,28 +112,28 @@ describe('Test pool', function() {
it('pool.liquidityQuote() risky', async function() {
const amount = parseWei('0.5')
const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RISKY)
const delStable = liquidityQuote.delLiquidity.mul(pool.reserveStable).div(pool.liquidity)
const delStable = pool.reserveStable.mul(liquidityQuote.delLiquidity).div(pool.liquidity)
expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float)
})

it('pool.liquidityQuote() stable', async function() {
const amount = parseWei('0.5')
const liquidityQuote = pool.liquidityQuote(amount, PoolSides.STABLE)
const delRisky = liquidityQuote.delLiquidity.mul(pool.reserveRisky).div(pool.liquidity)
const delRisky = pool.reserveRisky.mul(liquidityQuote.delLiquidity).div(pool.liquidity)
expect(liquidityQuote.delRisky.float).toBeCloseTo(delRisky.float)
})

it('pool.liquidityQuote() RMM', async function() {
const amount = parseWei('0.5')
const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RMM_LP)
const delStable = liquidityQuote.delLiquidity.mul(pool.reserveStable).div(pool.liquidity)
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() {
const amount = parseWei('0.5')
const liquidityQuote = pool.liquidityQuote(amount, PoolSides.RISKY)
const delStable = liquidityQuote.delLiquidity.mul(pool.reserveStable).div(pool.liquidity)
const delStable = pool.reserveStable.mul(liquidityQuote.delLiquidity).div(pool.liquidity)
expect(liquidityQuote.delStable.float).toBeCloseTo(delStable.float)
})

Expand Down
Loading

0 comments on commit 8d7f863

Please sign in to comment.