diff --git a/common/calculate-swap3.ts b/common/calculate-swap3.ts index 5819ae8e..1b902647 100644 --- a/common/calculate-swap3.ts +++ b/common/calculate-swap3.ts @@ -1,4 +1,4 @@ -type Swap3LiquidityProvision = { +type Swap3LiquidityPosition = { // TODO: Record who added this stuff? // Not sure if this is needed; maybe YES and NO left @@ -26,6 +26,17 @@ type Swap3LiquidityProvision = { maxTick: number } +type TickState = { + tick: number + + // Amount of liquidity added when crossing this tick from left to right + // Negative if we should remove liquidity + liquidityNet: number + + // Total liquidity referencing this pool + liquidityGross: number +} + // From https://uniswap.org/whitepaper-v3.pdf export type Swap3Pool = { // id: string @@ -35,12 +46,24 @@ export type Swap3Pool = { // 6.2 Global State liquidity: number // = sqrt(NY) - sqrtRatio: number // = sqrt(N / Y); N = # NO shares in pool + // sqrtRatio: number // = sqrt(N / Y); N = # NO shares in pool // So N = liquidity * sqrtRatio; Y = liquidity / sqrtRatio - tick: number + // Current tick number. // Stored as optimization. equal to floor(log_sqrt_1.0001(sqrtRatio)) + tick: number // TODO add fees? + + // Mapping of tick indices to tick values. + tickStates: TickState[] +} + +export function noShares(pool: Swap3Pool) { + return pool.liquidity * toRatio(pool.tick) ** 0.5 +} + +export function yesShares(pool: Swap3Pool) { + return pool.liquidity / toRatio(pool.tick) ** 0.5 } export function getSwap3Probability(pool: Swap3Pool) { @@ -68,6 +91,7 @@ export function calculateLPCost( maxTick: number, deltaL: number ) { + // TODO: this is subtly wrong, because of rounding between curTick and sqrtPrice const upperTick = Math.min(maxTick, Math.max(minTick, curTick)) const costN = toRatio(upperTick) ** 0.5 - toRatio(minTick) ** 0.5 @@ -80,6 +104,44 @@ export function calculateLPCost( } } +// TODO: Untested +function addPosition( + pool: Swap3Pool, + minTick: number, + maxTick: number, + deltaL: number +) { + const { requiredN, requiredY } = calculateLPCost( + pool.tick, + minTick, + maxTick, + deltaL + ) + console.log(`Deducting required N: ${requiredN} and required Y: ${requiredY}`) + + // Add liquidity as we pass through the larger tick + const maxTickState = pool.tickStates[maxTick] || { + tick: maxTick, + liquidityNet: 0, + liquidityGross: 0, + } + + maxTickState.liquidityNet += deltaL + maxTickState.liquidityGross += deltaL + + // And remove it as we pass through the lower one + const minTickState = pool.tickStates[minTick] || { + tick: minTick, + liquidityNet: 0, + liquidityGross: 0, + } + + minTickState.liquidityNet -= deltaL + minTickState.liquidityGross -= deltaL + + // TODO: add deltaL to liquidityGross of tickStates between minTick and maxTick +} + function toRatio(tick: number) { return 1.0001 ** tick } @@ -89,6 +151,7 @@ function toProb(tick: number) { return ratio / (ratio + 1) } +// Returns the tick for a given probability from 0 to 1 export function fromProb(prob: number) { const ratio = prob / (1 - prob) return Math.floor(Math.log(ratio) / Math.log(1.0001)) diff --git a/web/pages/swap.tsx b/web/pages/swap.tsx index b213b8ae..32de6055 100644 --- a/web/pages/swap.tsx +++ b/web/pages/swap.tsx @@ -2,7 +2,9 @@ import { calculateLPCost, fromProb, getSwap3Probability, + noShares, Swap3Pool, + yesShares, } from 'common/calculate-swap3' import { formatPercent } from 'common/util/format' import { useState } from 'react' @@ -63,24 +65,20 @@ function PoolTable(props: { pool: Swap3Pool }) { {pool.liquidity} -
- - {pool.sqrtRatio} -
{pool.tick}
- {pool.liquidity * pool.sqrtRatio} + {yesShares(pool).toFixed(2)}
- {pool.liquidity / pool.sqrtRatio} + {noShares(pool).toFixed(2)}
- + {formatPercent(getSwap3Probability(pool))}
@@ -102,7 +100,6 @@ function Graph(props: { pool: Swap3Pool }) { export default function Swap() { const [pool, setPool] = useState({ liquidity: 100, - sqrtRatio: 2, tick: fromProb(0.3), tickStates: [], }) @@ -124,7 +121,7 @@ export default function Swap() { setPool((p) => ({ @@ -139,14 +136,14 @@ export default function Swap() { setMinTick(inputPercentToTick(e))} /> Min Tick: {minTick} setMaxTick(inputPercentToTick(e))} />