Create a test interface for Uniswap v3
This commit is contained in:
parent
4aec46f880
commit
4b09640e60
95
common/calculate-swap3.ts
Normal file
95
common/calculate-swap3.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
type Swap3LiquidityProvision = {
|
||||
// TODO: Record who added this stuff?
|
||||
|
||||
// Not sure if this is needed; maybe YES and NO left
|
||||
// amount: number // M$ quantity
|
||||
|
||||
// For now, only support YES and NO outcome tokens
|
||||
// TODO: replace with Outcome
|
||||
// Hm, is this...
|
||||
// 1. Number of shares left in this particular pool?
|
||||
// 2. Fixed at injection time?
|
||||
pool: { YES: number; NO: number }
|
||||
|
||||
// Uniswap uses 0.01, 0.003, 0.0005. Let's stick with 0.003 for now.
|
||||
// fee: number
|
||||
|
||||
// Min/max is expressed as a odds ratio of cost of YES to cost of NO
|
||||
// E.g. ratio of 1 = 1:1 = 50%; ratio of 3 = 3:1 = 75%
|
||||
// minRatio: number
|
||||
// maxRatio: number
|
||||
minTick: number
|
||||
// minTick = loq_sqrt_1.0001(sqrtRatio)
|
||||
// sqrt(1.0001)^(minTick) = sqrtRatio
|
||||
// minRatio = 1.0001^minTick
|
||||
// e.g. minTick = 20k => 7.3883
|
||||
maxTick: number
|
||||
}
|
||||
|
||||
// From https://uniswap.org/whitepaper-v3.pdf
|
||||
export type Swap3Pool = {
|
||||
// id: string
|
||||
// userId: string
|
||||
// contractId: string
|
||||
// createdTime: number
|
||||
|
||||
// 6.2 Global State
|
||||
liquidity: number // = sqrt(NY)
|
||||
sqrtRatio: number // = sqrt(N / Y); N = # NO shares in pool
|
||||
// So N = liquidity * sqrtRatio; Y = liquidity / sqrtRatio
|
||||
|
||||
tick: number
|
||||
// Stored as optimization. equal to floor(log_sqrt_1.0001(sqrtRatio))
|
||||
// TODO add fees?
|
||||
}
|
||||
|
||||
export function getSwap3Probability(pool: Swap3Pool) {
|
||||
// Probability is given by N / (N + Y)
|
||||
// const N = pool.liquidity * pool.sqrtRatio
|
||||
// const Y = pool.liquidity / pool.sqrtRatio
|
||||
// return N / (N + Y)
|
||||
|
||||
// To check: this should be equal to toProb(pool.tick)?
|
||||
return toProb(pool.tick)
|
||||
}
|
||||
|
||||
function calculatePurchase(
|
||||
pool: Swap3Pool,
|
||||
amount: number,
|
||||
outcome: 'YES' | 'NO'
|
||||
) {
|
||||
const shares = 10
|
||||
const newPool = {}
|
||||
}
|
||||
|
||||
export function calculateLPCost(
|
||||
curTick: number,
|
||||
minTick: number,
|
||||
maxTick: number,
|
||||
deltaL: number
|
||||
) {
|
||||
const upperTick = Math.min(maxTick, Math.max(minTick, curTick))
|
||||
const costN = toRatio(upperTick) ** 0.5 - toRatio(minTick) ** 0.5
|
||||
|
||||
const lowerTick = Math.max(minTick, Math.min(maxTick, curTick))
|
||||
const costY = 1 / toRatio(lowerTick) ** 0.5 - 1 / toRatio(maxTick) ** 0.5
|
||||
|
||||
return {
|
||||
requiredN: deltaL * costN,
|
||||
requiredY: deltaL * costY,
|
||||
}
|
||||
}
|
||||
|
||||
function toRatio(tick: number) {
|
||||
return 1.0001 ** tick
|
||||
}
|
||||
|
||||
function toProb(tick: number) {
|
||||
const ratio = toRatio(tick)
|
||||
return ratio / (ratio + 1)
|
||||
}
|
||||
|
||||
export function fromProb(prob: number) {
|
||||
const ratio = prob / (1 - prob)
|
||||
return Math.log(ratio) / Math.log(1.0001)
|
||||
}
|
166
web/pages/swap.tsx
Normal file
166
web/pages/swap.tsx
Normal file
|
@ -0,0 +1,166 @@
|
|||
import {
|
||||
calculateLPCost,
|
||||
fromProb,
|
||||
getSwap3Probability,
|
||||
Swap3Pool,
|
||||
} from 'common/calculate-swap3'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { useState } from 'react'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
|
||||
const users = {
|
||||
alice: {
|
||||
M: 100,
|
||||
YES: 0,
|
||||
NO: 0,
|
||||
},
|
||||
bob: {
|
||||
M: 200,
|
||||
YES: 0,
|
||||
NO: 0,
|
||||
},
|
||||
kipply: {
|
||||
M: 300,
|
||||
YES: 0,
|
||||
NO: 0,
|
||||
},
|
||||
}
|
||||
|
||||
function BalanceTable() {
|
||||
/* Display all users current M, YES, and NO in a table */
|
||||
return (
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2">User</th>
|
||||
<th className="px-4 py-2">M</th>
|
||||
<th className="px-4 py-2">YES</th>
|
||||
<th className="px-4 py-2">NO</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.keys(users).map((user) => (
|
||||
<tr key={user}>
|
||||
<td className="px-4 py-2">{user}</td>
|
||||
<td className="px-4 py-2">{users[user].M}</td>
|
||||
<td className="px-4 py-2">{users[user].YES}</td>
|
||||
<td className="px-4 py-2">{users[user].NO}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
/* Show the values in pool */
|
||||
function PoolTable(props: { pool: Swap3Pool }) {
|
||||
const { pool } = props
|
||||
return (
|
||||
<Row className="gap-4">
|
||||
<div>
|
||||
<label>Liquidity: </label>
|
||||
{pool.liquidity}
|
||||
</div>
|
||||
<div>
|
||||
<label>Sqrt Ratio: </label>
|
||||
{pool.sqrtRatio}
|
||||
</div>
|
||||
<div>
|
||||
<label>Tick: </label>
|
||||
{pool.tick}
|
||||
</div>
|
||||
<div>
|
||||
<label>Pool YES: </label>
|
||||
{pool.liquidity * pool.sqrtRatio}
|
||||
</div>
|
||||
<div>
|
||||
<label>Pool NO: </label>
|
||||
{pool.liquidity / pool.sqrtRatio}
|
||||
</div>
|
||||
<div>
|
||||
<label>Prob: </label>
|
||||
{formatPercent(getSwap3Probability(pool))}
|
||||
</div>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
// Stored as optimization. equal to floor(log_sqrt_1.0001(sqrtRatio))
|
||||
function tick(sqrtRatio: number) {
|
||||
return Math.floor(Math.log(sqrtRatio) / Math.log(1.0001))
|
||||
}
|
||||
|
||||
export default function Swap() {
|
||||
const [pool, setPool] = useState({
|
||||
liquidity: 100,
|
||||
sqrtRatio: 2,
|
||||
tick: tick(0.1),
|
||||
})
|
||||
|
||||
const [minTick, setMinTick] = useState(0)
|
||||
const [maxTick, setMaxTick] = useState(0)
|
||||
|
||||
const { requiredN, requiredY } = calculateLPCost(
|
||||
pool.tick,
|
||||
minTick,
|
||||
maxTick,
|
||||
100 // deltaL
|
||||
)
|
||||
|
||||
return (
|
||||
<Col className="mx-auto max-w-2xl gap-20 p-4">
|
||||
<BalanceTable />
|
||||
<PoolTable pool={pool} />
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Current Prob"
|
||||
type="number"
|
||||
onChange={(e) =>
|
||||
setPool((p) => ({
|
||||
...p,
|
||||
tick: inputPercentToTick(e),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
|
||||
<Col>
|
||||
Alice: Add liquidity
|
||||
<input className="input" placeholder="Amount" type="number" />
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Min"
|
||||
type="number"
|
||||
onChange={(e) => setMinTick(inputPercentToTick(e))}
|
||||
/>
|
||||
Min Tick: {minTick}
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Max"
|
||||
type="number"
|
||||
onChange={(e) => setMaxTick(inputPercentToTick(e))}
|
||||
/>
|
||||
Max Tick: {maxTick}
|
||||
<Row className="gap-2 py-2">
|
||||
<div>Y required: {requiredY}</div>
|
||||
<div>N required: {requiredN}</div>{' '}
|
||||
</Row>
|
||||
<button className="btn">Create pool</button>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
Bob: Buy Tokens
|
||||
{/* <input className="input" placeholder="User" type="text" /> */}
|
||||
<input className="input" placeholder="Amount" type="number" />
|
||||
<Row className="gap-2">
|
||||
<button className="btn">Buy YES</button>
|
||||
<button className="btn">Buy NO</button>
|
||||
</Row>
|
||||
</Col>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function inputPercentToTick(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
return fromProb(parseFloat(event.target.value) / 100)
|
||||
}
|
Loading…
Reference in New Issue
Block a user