Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2cf672fac1 | ||
|
989fa1306b | ||
|
6ac29106f2 | ||
|
3898ec95ed | ||
|
4cd35c8742 | ||
|
7acd3aed93 | ||
|
06c49be05b | ||
|
17ae9d953d | ||
|
853c45d564 | ||
|
2fdb962a14 | ||
|
81064e67cf | ||
|
8734a14e6b | ||
|
ab3b88112f | ||
|
538ae323d7 | ||
|
4b09640e60 |
260
common/calculate-swap3.ts
Normal file
260
common/calculate-swap3.ts
Normal file
|
@ -0,0 +1,260 @@
|
|||
type Swap3LiquidityPosition = {
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
|
||||
// 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) {
|
||||
// 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, // In M$
|
||||
outcome: 'YES' | 'NO'
|
||||
) {}
|
||||
|
||||
export function calculateLPCost(
|
||||
curTick: number,
|
||||
minTick: number,
|
||||
maxTick: number,
|
||||
deltaL: number
|
||||
) {
|
||||
// TODO: this is subtly wrong, because of rounding between curTick and sqrtPrice
|
||||
// Also below in buyYES
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a preview of the new pool + number of YES shares purchased.
|
||||
// Does NOT modify the pool
|
||||
// Hm, logic is pretty complicated. Let's see if we can simplify this.
|
||||
export function buyYes(
|
||||
pool: Swap3Pool,
|
||||
amount: number // In M$
|
||||
) {
|
||||
const tickStates = sortedTickStates(pool)
|
||||
let tick = pool.tick
|
||||
let stateIndex = 0
|
||||
let amountLeft = amount
|
||||
let yesPurchased = 0
|
||||
// Stop if there's epsilon M$ left, due to rounding issues
|
||||
while (amountLeft > 1e-6) {
|
||||
// Find the current & next states for this tick
|
||||
while (tick >= tickStates[stateIndex + 1].tick) {
|
||||
stateIndex++
|
||||
if (stateIndex > tickStates.length - 2) {
|
||||
// We've reached the end of the tick states...
|
||||
throw new Error('Ran out of tick states')
|
||||
}
|
||||
}
|
||||
const state = tickStates[stateIndex]
|
||||
const nextState = tickStates[stateIndex + 1]
|
||||
|
||||
// nextState.tick purchases through the bucket; fullTick uses the remaining amountLeft
|
||||
const fullCostN = amountLeft / state.liquidityGross
|
||||
// Note: fullTick is NOT floored here; it's for the sqrtPrice to buy up to
|
||||
const fullTick = fromRatioUnfloored((fullCostN + toRatio(tick) ** 0.5) ** 2)
|
||||
const nextTick = Math.min(nextState.tick, fullTick)
|
||||
|
||||
// Copied from above; TODO extract to common function?
|
||||
const noCost = toRatio(nextTick) ** 0.5 - toRatio(tick) ** 0.5
|
||||
const yesCost = 1 / toRatio(tick) ** 0.5 - 1 / toRatio(nextTick) ** 0.5
|
||||
|
||||
amountLeft -= noCost * state.liquidityGross
|
||||
yesPurchased += yesCost * state.liquidityGross
|
||||
tick = Math.floor(nextTick)
|
||||
}
|
||||
|
||||
// Right now we eat the epsilon amounntLeft as a fee. Could return it, shrug.
|
||||
return {
|
||||
newPoolTick: tick,
|
||||
yesPurchased,
|
||||
}
|
||||
}
|
||||
|
||||
// Currently, this mutates the pool. Should it return a new object instead?
|
||||
export 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 smaller tick
|
||||
const minTickState = pool.tickStates[minTick] || {
|
||||
tick: minTick,
|
||||
liquidityNet: 0,
|
||||
liquidityGross: 0,
|
||||
}
|
||||
|
||||
minTickState.liquidityNet += deltaL
|
||||
pool.tickStates[minTick] = minTickState
|
||||
|
||||
// And remove it as we pass through the larger one
|
||||
const maxTickState = pool.tickStates[maxTick] || {
|
||||
tick: maxTick,
|
||||
liquidityNet: 0,
|
||||
liquidityGross: 0,
|
||||
}
|
||||
|
||||
maxTickState.liquidityNet -= deltaL
|
||||
pool.tickStates[maxTick] = maxTickState
|
||||
|
||||
return pool
|
||||
}
|
||||
|
||||
export function addBalancer(pool: Swap3Pool, p: number, deltaL: number) {
|
||||
// TODO: math is borked, shouldn't be returning infinity
|
||||
function tickL(tick: number) {
|
||||
const q = 1 - p
|
||||
return deltaL * 2 * p ** q * q ** p * 1.0001 ** ((p - 0.5) * tick)
|
||||
}
|
||||
|
||||
// See how much liquidity is provided at +/- 5pp around p
|
||||
// const minTick = fromProb(p - 0.1)
|
||||
// const maxTick = fromProb(p + 0.05)
|
||||
const minTick = fromProb(0.000001)
|
||||
const maxTick = fromProb(0.999999)
|
||||
let totalN = 0
|
||||
let totalY = 0
|
||||
const stride = 300
|
||||
for (let t = minTick; t <= maxTick; t += stride) {
|
||||
// console.log('liquidity at tick ', t, toProb(t), tickL(t))
|
||||
const { requiredN, requiredY } = calculateLPCost(
|
||||
fromProb(p),
|
||||
t,
|
||||
t + stride,
|
||||
tickL(t)
|
||||
)
|
||||
totalN += requiredN
|
||||
totalY += requiredY
|
||||
// Add liquidity
|
||||
addPosition(pool, t, t + stride, tickL(t))
|
||||
}
|
||||
|
||||
console.log('rough number of ticks', (maxTick - minTick) / stride)
|
||||
console.log(`Total N: ${totalN} and total Y: ${totalY}`)
|
||||
grossLiquidity(pool)
|
||||
return pool
|
||||
}
|
||||
|
||||
// This also mutates the pool directly
|
||||
export function grossLiquidity(pool: Swap3Pool) {
|
||||
let liquidityGross = 0
|
||||
for (const tickState of sortedTickStates(pool)) {
|
||||
liquidityGross += tickState.liquidityNet
|
||||
tickState.liquidityGross = liquidityGross
|
||||
}
|
||||
return pool
|
||||
}
|
||||
|
||||
export function sortedTickStates(pool: Swap3Pool) {
|
||||
return Object.values(pool.tickStates).sort((a, b) => a.tick - b.tick)
|
||||
}
|
||||
|
||||
function toRatio(tick: number) {
|
||||
return 1.0001 ** tick
|
||||
}
|
||||
|
||||
export function toProb(tick: number) {
|
||||
const ratio = toRatio(tick)
|
||||
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 fromRatio(ratio)
|
||||
}
|
||||
|
||||
function fromRatio(ratio: number) {
|
||||
return Math.floor(Math.log(ratio) / Math.log(1.0001))
|
||||
}
|
||||
|
||||
function fromRatioUnfloored(ratio: number) {
|
||||
return Math.log(ratio) / Math.log(1.0001)
|
||||
}
|
121
web/components/contract/liquidity-graph.tsx
Normal file
121
web/components/contract/liquidity-graph.tsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { CartesianMarkerProps, DatumValue } from '@nivo/core'
|
||||
import { Point, ResponsiveLine } from '@nivo/line'
|
||||
import { NUMERIC_GRAPH_COLOR } from 'common/numeric-constants'
|
||||
import { memo } from 'react'
|
||||
import { range } from 'lodash'
|
||||
import { getDpmOutcomeProbabilities } from '../../../common/calculate-dpm'
|
||||
import { NumericContract } from '../../../common/contract'
|
||||
import { useWindowSize } from '../../hooks/use-window-size'
|
||||
import { Col } from '../layout/col'
|
||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||
|
||||
export type GraphPoint = {
|
||||
// A probability between 0 and 1
|
||||
x: number
|
||||
// Amount of liquidity
|
||||
y: number
|
||||
}
|
||||
|
||||
export const LiquidityGraph = memo(function NumericGraph(props: {
|
||||
min?: 0
|
||||
max?: 1
|
||||
points: GraphPoint[]
|
||||
height?: number
|
||||
marker?: number // Value between min and max to highlight on x-axis
|
||||
previewMarker?: number
|
||||
}) {
|
||||
const { height, min, max, points, marker, previewMarker } = props
|
||||
|
||||
// Really maxLiquidity
|
||||
const maxLiquidity = 500
|
||||
const data = [{ id: 'Probability', data: points, color: NUMERIC_GRAPH_COLOR }]
|
||||
|
||||
const yTickValues = [
|
||||
0,
|
||||
0.25 * maxLiquidity,
|
||||
0.5 * maxLiquidity,
|
||||
0.75 * maxLiquidity,
|
||||
maxLiquidity,
|
||||
]
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
const numXTickValues = !width || width < 800 ? 2 : 5
|
||||
|
||||
const markers: CartesianMarkerProps<DatumValue>[] = []
|
||||
if (marker) {
|
||||
markers.push({
|
||||
axis: 'x',
|
||||
value: marker,
|
||||
lineStyle: { stroke: '#000', strokeWidth: 2 },
|
||||
legend: `Implied: ${formatPercent(marker)}`,
|
||||
})
|
||||
}
|
||||
if (previewMarker) {
|
||||
markers.push({
|
||||
axis: 'x',
|
||||
value: previewMarker,
|
||||
lineStyle: { stroke: '#8888', strokeWidth: 2 },
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full overflow-hidden"
|
||||
style={{ height: height ?? (!width || width >= 800 ? 350 : 250) }}
|
||||
>
|
||||
<ResponsiveLine
|
||||
data={data}
|
||||
yScale={{ min: 0, max: maxLiquidity, type: 'linear' }}
|
||||
yFormat={formatLiquidity}
|
||||
axisLeft={{
|
||||
tickValues: yTickValues,
|
||||
format: formatLiquidity,
|
||||
}}
|
||||
xScale={{
|
||||
type: 'linear',
|
||||
min: min,
|
||||
max: max,
|
||||
}}
|
||||
xFormat={(d) => `${formatLargeNumber(+d, 3)}`}
|
||||
axisBottom={{
|
||||
tickValues: numXTickValues,
|
||||
format: (d) => `${formatLargeNumber(+d, 3)}`,
|
||||
}}
|
||||
colors={{ datum: 'color' }}
|
||||
pointSize={0}
|
||||
enableSlices="x"
|
||||
sliceTooltip={({ slice }) => {
|
||||
const point = slice.points[0]
|
||||
return <Tooltip point={point} />
|
||||
}}
|
||||
enableGridX={!!width && width >= 800}
|
||||
enableArea
|
||||
margin={{ top: 20, right: 28, bottom: 22, left: 50 }}
|
||||
markers={markers}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
function formatLiquidity(y: DatumValue) {
|
||||
const p = Math.round(+y * 100) / 100
|
||||
return `${p}L`
|
||||
}
|
||||
|
||||
function Tooltip(props: { point: Point }) {
|
||||
const { point } = props
|
||||
return (
|
||||
<Col className="border border-gray-300 bg-white py-2 px-3">
|
||||
<div
|
||||
className="pb-1"
|
||||
style={{
|
||||
color: point.serieColor,
|
||||
}}
|
||||
>
|
||||
<strong>{point.serieId}</strong> {point.data.yFormatted}
|
||||
</div>
|
||||
<div>{formatLargeNumber(+point.data.x)}</div>
|
||||
</Col>
|
||||
)
|
||||
}
|
269
web/pages/swap.tsx
Normal file
269
web/pages/swap.tsx
Normal file
|
@ -0,0 +1,269 @@
|
|||
import {
|
||||
addBalancer,
|
||||
addPosition,
|
||||
buyYes,
|
||||
calculateLPCost,
|
||||
fromProb,
|
||||
getSwap3Probability,
|
||||
grossLiquidity,
|
||||
noShares,
|
||||
sortedTickStates,
|
||||
Swap3Pool,
|
||||
toProb,
|
||||
yesShares,
|
||||
} from 'common/calculate-swap3'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { useState } from 'react'
|
||||
import { LiquidityGraph } from 'web/components/contract/liquidity-graph'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { addLiquidity } from 'web/lib/firebase/fn-call'
|
||||
|
||||
const users: Record<string, any> = {
|
||||
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>Implied: </label>
|
||||
{formatPercent(getSwap3Probability(pool))}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Liquidity: </label>
|
||||
{pool.liquidity}
|
||||
</div>
|
||||
<div>
|
||||
<label>Tick: </label>
|
||||
{pool.tick}
|
||||
</div>
|
||||
<div>
|
||||
<label>Pool YES: </label>
|
||||
{yesShares(pool).toFixed(2)}
|
||||
</div>
|
||||
<div>
|
||||
<label>Pool NO: </label>
|
||||
{noShares(pool).toFixed(2)}
|
||||
</div>
|
||||
</Row>
|
||||
{/* Render each tickState as another row in a table */}
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="px-4 py-2">Tick</th>
|
||||
<th className="px-4 py-2">Prob</th>
|
||||
<th className="px-4 py-2">Net Liquidity</th>
|
||||
<th className="px-4 py-2">Gross Liquidity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{sortedTickStates(pool).map((tickState, i) => (
|
||||
<tr key={i}>
|
||||
<td className="px-4 py-2">{tickState.tick}</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatPercent(toProb(tickState.tick))}
|
||||
</td>
|
||||
<td className="px-4 py-2">{tickState.liquidityNet}</td>
|
||||
<td className="px-4 py-2">{tickState.liquidityGross}</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Graph(props: { pool: Swap3Pool; previewMarker?: number }) {
|
||||
const { pool, previewMarker } = props
|
||||
const points = []
|
||||
let lastGross = 0
|
||||
for (const tickState of sortedTickStates(pool)) {
|
||||
const { tick, liquidityGross } = tickState
|
||||
points.push({ x: toProb(tick), y: lastGross })
|
||||
points.push({ x: toProb(tick), y: liquidityGross })
|
||||
lastGross = liquidityGross
|
||||
}
|
||||
return (
|
||||
<LiquidityGraph
|
||||
points={points}
|
||||
marker={toProb(pool.tick)}
|
||||
previewMarker={previewMarker}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function LiquidityPanel(props: {
|
||||
pool: Swap3Pool
|
||||
setPool: (pool: Swap3Pool) => void
|
||||
}) {
|
||||
const { pool, setPool } = props
|
||||
const [minTick, setMinTick] = useState(0)
|
||||
const [maxTick, setMaxTick] = useState(0)
|
||||
const [deltaL, setDeltaL] = useState(100)
|
||||
|
||||
const { requiredN, requiredY } = calculateLPCost(
|
||||
pool.tick,
|
||||
minTick,
|
||||
maxTick,
|
||||
deltaL
|
||||
)
|
||||
|
||||
return (
|
||||
<Col>
|
||||
<h2 className="my-2 text-xl">Add liquidity</h2>
|
||||
{/* <input className="input" placeholder="Amount" type="number" /> */}
|
||||
<Row className="gap-2">
|
||||
<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} */}
|
||||
<input
|
||||
className="input"
|
||||
placeholder="delta Liquidity"
|
||||
type="number"
|
||||
value={deltaL}
|
||||
onChange={(e) => setDeltaL(parseFloat(e.target.value))}
|
||||
/>
|
||||
</Row>
|
||||
<Row className="gap-2 py-2">
|
||||
<div>Y required: {requiredY.toFixed(2)}</div>
|
||||
<div>N required: {requiredN.toFixed(2)}</div>{' '}
|
||||
</Row>
|
||||
<button
|
||||
className="btn"
|
||||
onClick={() => {
|
||||
addPosition(pool, minTick, maxTick, deltaL)
|
||||
grossLiquidity(pool)
|
||||
setPool({ ...pool })
|
||||
}}
|
||||
>
|
||||
Create pool
|
||||
</button>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Swap() {
|
||||
// Set up an initial pool with 100 liquidity from 0% to 100%
|
||||
// TODO: Not sure why maxTick of 2**23 breaks it, but 2**20 is okay...
|
||||
let INIT_POOL: Swap3Pool = {
|
||||
liquidity: 0,
|
||||
tick: fromProb(0.3),
|
||||
tickStates: [],
|
||||
}
|
||||
INIT_POOL = addPosition(INIT_POOL, -(2 ** 23), 2 ** 20, 1)
|
||||
// INIT_POOL = addPosition(INIT_POOL, fromProb(0.32), fromProb(0.35), 100)
|
||||
INIT_POOL = addBalancer(INIT_POOL, 0.3, 100)
|
||||
INIT_POOL = grossLiquidity(INIT_POOL)
|
||||
|
||||
const [pool, setPool] = useState(INIT_POOL)
|
||||
const [buyAmount, setBuyAmount] = useState(0)
|
||||
|
||||
const { newPoolTick, yesPurchased } = buyYes(pool, buyAmount)
|
||||
|
||||
return (
|
||||
<Col className="mx-auto max-w-2xl gap-10 p-4">
|
||||
{/* <BalanceTable /> */}
|
||||
{/* <PoolTable pool={pool} /> */}
|
||||
<Graph
|
||||
pool={pool}
|
||||
previewMarker={
|
||||
newPoolTick === pool.tick ? undefined : toProb(newPoolTick)
|
||||
}
|
||||
/>
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Current%"
|
||||
type="number"
|
||||
onChange={(e) => {
|
||||
pool.tick = inputPercentToTick(e)
|
||||
setPool({ ...pool })
|
||||
}}
|
||||
/>
|
||||
|
||||
<LiquidityPanel pool={pool} setPool={setPool} />
|
||||
|
||||
<Col>
|
||||
<h2 className="my-2 text-xl">Limit Order</h2>
|
||||
TODO
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<h2 className="my-2 text-xl">Buy Shares</h2>
|
||||
{/* <input className="input" placeholder="User" type="text" /> */}
|
||||
<input
|
||||
className="input"
|
||||
placeholder="Amount"
|
||||
type="number"
|
||||
onChange={(e) => setBuyAmount(parseFloat(e.target.value))}
|
||||
/>
|
||||
<Row className="gap-2 py-2">
|
||||
<div>Y shares purchaseable: {yesPurchased.toFixed(2)}</div>
|
||||
<div>New Tick: {newPoolTick}</div>
|
||||
<div>New prob: {formatPercent(toProb(newPoolTick))}</div>
|
||||
</Row>
|
||||
<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