withdraw liquidity: redeem surplus shares, adjust total depostits, adjust amount for current prob, set isLiquidityProvision for surplus bets, error handling
This commit is contained in:
		
							parent
							
								
									699bddcb2a
								
							
						
					
					
						commit
						3858f8fbea
					
				|  | @ -11,6 +11,7 @@ import { getProbability } from '../../common/calculate' | |||
| import { noFees } from '../../common/fees' | ||||
| 
 | ||||
| import { APIError } from './api' | ||||
| import { redeemShares } from './redeem-shares' | ||||
| 
 | ||||
| export const withdrawLiquidity = functions | ||||
|   .runWith({ minInstances: 1 }) | ||||
|  | @ -28,83 +29,103 @@ export const withdrawLiquidity = functions | |||
|       if (!contractId) | ||||
|         return { status: 'error', message: 'Missing contract id' } | ||||
| 
 | ||||
|       const result = await firestore.runTransaction(async (trans) => { | ||||
|         const lpDoc = firestore.doc(`users/${userId}`) | ||||
|         const lpSnap = await trans.get(lpDoc) | ||||
|         if (!lpSnap.exists) throw new APIError(400, 'User not found.') | ||||
|         const lp = lpSnap.data() as User | ||||
|       return await firestore | ||||
|         .runTransaction(async (trans) => { | ||||
|           const lpDoc = firestore.doc(`users/${userId}`) | ||||
|           const lpSnap = await trans.get(lpDoc) | ||||
|           if (!lpSnap.exists) throw new APIError(400, 'User not found.') | ||||
|           const lp = lpSnap.data() as User | ||||
| 
 | ||||
|         const contractDoc = firestore.doc(`contracts/${contractId}`) | ||||
|         const contractSnap = await trans.get(contractDoc) | ||||
|         if (!contractSnap.exists) throw new APIError(400, 'Contract not found.') | ||||
|         const contract = contractSnap.data() as CPMMContract | ||||
|           const contractDoc = firestore.doc(`contracts/${contractId}`) | ||||
|           const contractSnap = await trans.get(contractDoc) | ||||
|           if (!contractSnap.exists) | ||||
|             throw new APIError(400, 'Contract not found.') | ||||
|           const contract = contractSnap.data() as CPMMContract | ||||
| 
 | ||||
|         const liquidityCollection = firestore.collection( | ||||
|           `contracts/${contractId}/liquidity` | ||||
|         ) | ||||
| 
 | ||||
|         const liquiditiesSnap = await trans.get(liquidityCollection) | ||||
| 
 | ||||
|         const liquidities = liquiditiesSnap.docs.map( | ||||
|           (doc) => doc.data() as LiquidityProvision | ||||
|         ) | ||||
| 
 | ||||
|         const userShares = getUserLiquidityShares(userId, contract, liquidities) | ||||
| 
 | ||||
|         // zero all added amounts for now
 | ||||
|         // can add support for partial withdrawals in the future
 | ||||
|         liquiditiesSnap.docs | ||||
|           .filter( | ||||
|             (_, i) => !liquidities[i].isAnte && liquidities[i].userId === userId | ||||
|           const liquidityCollection = firestore.collection( | ||||
|             `contracts/${contractId}/liquidity` | ||||
|           ) | ||||
|           .forEach((doc) => trans.update(doc.ref, { amount: 0 })) | ||||
| 
 | ||||
|         const payout = Math.min(...Object.values(userShares)) | ||||
|         if (payout <= 0) return {} | ||||
|           const liquiditiesSnap = await trans.get(liquidityCollection) | ||||
| 
 | ||||
|         const newBalance = lp.balance + payout | ||||
|         trans.update(lpDoc, { balance: newBalance }) | ||||
|           const liquidities = liquiditiesSnap.docs.map( | ||||
|             (doc) => doc.data() as LiquidityProvision | ||||
|           ) | ||||
| 
 | ||||
|         const newPool = subtractObjects(contract.pool, userShares) | ||||
|         const newTotalLiquidity = contract.totalLiquidity - payout | ||||
|         trans.update(contractDoc, { | ||||
|           pool: newPool, | ||||
|           totalLiquidity: newTotalLiquidity, | ||||
|           const userShares = getUserLiquidityShares( | ||||
|             userId, | ||||
|             contract, | ||||
|             liquidities | ||||
|           ) | ||||
| 
 | ||||
|           // zero all added amounts for now
 | ||||
|           // can add support for partial withdrawals in the future
 | ||||
|           liquiditiesSnap.docs | ||||
|             .filter( | ||||
|               (_, i) => | ||||
|                 !liquidities[i].isAnte && liquidities[i].userId === userId | ||||
|             ) | ||||
|             .forEach((doc) => trans.update(doc.ref, { amount: 0 })) | ||||
| 
 | ||||
|           const payout = Math.min(...Object.values(userShares)) | ||||
|           if (payout <= 0) return {} | ||||
| 
 | ||||
|           const newBalance = lp.balance + payout | ||||
|           const newTotalDeposits = lp.totalDeposits + payout | ||||
|           trans.update(lpDoc, { | ||||
|             balance: newBalance, | ||||
|             totalDeposits: newTotalDeposits, | ||||
|           } as Partial<User>) | ||||
| 
 | ||||
|           const newPool = subtractObjects(contract.pool, userShares) | ||||
|           const newTotalLiquidity = contract.totalLiquidity - payout | ||||
|           trans.update(contractDoc, { | ||||
|             pool: newPool, | ||||
|             totalLiquidity: newTotalLiquidity, | ||||
|           }) | ||||
| 
 | ||||
|           const prob = getProbability(contract) | ||||
| 
 | ||||
|           // surplus shares become user's bets
 | ||||
|           const bets = Object.entries(userShares) | ||||
|             .map(([outcome, shares]) => | ||||
|               shares - payout < 1 // don't create bet if less than 1 share
 | ||||
|                 ? undefined | ||||
|                 : ({ | ||||
|                     userId: userId, | ||||
|                     contractId: contract.id, | ||||
|                     amount: | ||||
|                       (outcome === 'YES' ? prob : 1 - prob) * (shares - payout), | ||||
|                     shares: shares - payout, | ||||
|                     outcome, | ||||
|                     probBefore: prob, | ||||
|                     probAfter: prob, | ||||
|                     createdTime: Date.now(), | ||||
|                     isLiquidityProvision: true, | ||||
|                     fees: noFees, | ||||
|                   } as Omit<Bet, 'id'>) | ||||
|             ) | ||||
|             .filter((x) => x !== undefined) | ||||
| 
 | ||||
|           for (const bet of bets) { | ||||
|             const doc = firestore | ||||
|               .collection(`contracts/${contract.id}/bets`) | ||||
|               .doc() | ||||
|             trans.create(doc, { id: doc.id, ...bet }) | ||||
|           } | ||||
| 
 | ||||
|           return userShares | ||||
|         }) | ||||
|         .then(async (result) => { | ||||
|           // redeem surplus bet with pre-existing bets
 | ||||
|           await redeemShares(userId, contractId) | ||||
| 
 | ||||
|         const prob = getProbability(contract) | ||||
| 
 | ||||
|         // surplus shares become user's bets
 | ||||
|         const bets = Object.entries(userShares) | ||||
|           .map(([outcome, shares]) => | ||||
|             shares - payout < 1 // don't create bet if less than 1 share
 | ||||
|               ? undefined | ||||
|               : ({ | ||||
|                   userId: userId, | ||||
|                   contractId: contract.id, | ||||
|                   amount: shares - payout, | ||||
|                   shares: shares - payout, | ||||
|                   outcome, | ||||
|                   probBefore: prob, | ||||
|                   probAfter: prob, | ||||
|                   createdTime: Date.now(), | ||||
|                   fees: noFees, | ||||
|                 } as Omit<Bet, 'id'>) | ||||
|           ) | ||||
|           .filter((x) => x !== undefined) | ||||
| 
 | ||||
|         for (const bet of bets) { | ||||
|           const doc = firestore | ||||
|             .collection(`contracts/${contract.id}/bets`) | ||||
|             .doc() | ||||
|           trans.create(doc, { id: doc.id, ...bet }) | ||||
|         } | ||||
| 
 | ||||
|         return userShares | ||||
|       }) | ||||
| 
 | ||||
|       console.log('userid', userId, 'withdraws', result) | ||||
|       return { status: 'success', userShares: result } | ||||
|           console.log('userid', userId, 'withdraws', result) | ||||
|           return { status: 'success', userShares: result } | ||||
|         }) | ||||
|         .catch((e) => { | ||||
|           return { status: 'error', message: e.message } | ||||
|         }) | ||||
|     } | ||||
|   ) | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user