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