Merge branch 'main' into limit-orders

This commit is contained in:
James Grugett 2022-07-09 14:42:46 -05:00
parent 5be2ea8583
commit 89ac26417b
16 changed files with 407 additions and 502 deletions

View File

@ -64,11 +64,7 @@ function calculateCpmmShares(
: n + bet - (k * (bet + y) ** -p) ** (1 / (1 - p))
}
export function getCpmmLiquidityFee(
state: CpmmState,
bet: number,
outcome: string
) {
export function getCpmmFees(state: CpmmState, bet: number, outcome: string) {
const prob = getCpmmProbabilityAfterBetBeforeFees(state, outcome, bet)
const betP = outcome === 'YES' ? 1 - prob : prob
@ -89,7 +85,7 @@ export function calculateCpmmSharesAfterFee(
outcome: string
) {
const { pool, p } = state
const { remainingBet } = getCpmmLiquidityFee(state, bet, outcome)
const { remainingBet } = getCpmmFees(state, bet, outcome)
return calculateCpmmShares(pool, p, remainingBet, outcome)
}
@ -100,9 +96,7 @@ export function calculateCpmmPurchase(
outcome: string
) {
const { pool, p } = state
const { remainingBet, fees } = getCpmmLiquidityFee(state, bet, outcome)
// const remainingBet = bet
// const fees = noFees
const { remainingBet, fees } = getCpmmFees(state, bet, outcome)
const shares = calculateCpmmShares(pool, p, remainingBet, outcome)
const { YES: y, NO: n } = pool

View File

@ -76,14 +76,14 @@ const computeFill = (
? Math.min(matchedBet.limitProb, limitProb ?? 1)
: Math.max(matchedBet.limitProb, limitProb ?? 0)
const poolAmount =
const buyAmount =
limit === undefined
? amount
: Math.min(amount, calculateCpmmAmount(cpmmState, limit, outcome))
const { shares, newPool, newP, fees } = calculateCpmmPurchase(
cpmmState,
poolAmount,
buyAmount,
outcome
)
const newState = { pool: newPool, p: newP }
@ -92,7 +92,7 @@ const computeFill = (
maker: {
matchedBetId: null,
shares,
amount: poolAmount,
amount: buyAmount,
state: newState,
fees,
timestamp,
@ -100,7 +100,7 @@ const computeFill = (
taker: {
matchedBetId: null,
shares,
amount: poolAmount,
amount: buyAmount,
timestamp,
},
}

View File

@ -1,55 +1,47 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { z } from 'zod'
import { Contract } from '../../common/contract'
import { User } from '../../common/user'
import { removeUndefinedProps } from '../../common/util/object'
import { redeemShares } from './redeem-shares'
import { getNewLiquidityProvision } from '../../common/add-liquidity'
import { APIError, newEndpoint, validate } from './api'
export const addLiquidity = functions.runWith({ minInstances: 1 }).https.onCall(
async (
data: {
amount: number
contractId: string
},
context
) => {
const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' }
const bodySchema = z.object({
contractId: z.string(),
amount: z.number().gt(0),
})
const { amount, contractId } = data
export const addliquidity = newEndpoint({}, async (req, auth) => {
const { amount, contractId } = validate(bodySchema, req.body)
if (amount <= 0 || isNaN(amount) || !isFinite(amount))
return { status: 'error', message: 'Invalid amount' }
if (!isFinite(amount)) throw new APIError(400, 'Invalid amount')
// run as transaction to prevent race conditions
return await firestore
.runTransaction(async (transaction) => {
const userDoc = firestore.doc(`users/${userId}`)
const userDoc = firestore.doc(`users/${auth.uid}`)
const userSnap = await transaction.get(userDoc)
if (!userSnap.exists)
return { status: 'error', message: 'User not found' }
if (!userSnap.exists) throw new APIError(400, 'User not found')
const user = userSnap.data() as User
const contractDoc = firestore.doc(`contracts/${contractId}`)
const contractSnap = await transaction.get(contractDoc)
if (!contractSnap.exists)
return { status: 'error', message: 'Invalid contract' }
if (!contractSnap.exists) throw new APIError(400, 'Invalid contract')
const contract = contractSnap.data() as Contract
if (
contract.mechanism !== 'cpmm-1' ||
(contract.outcomeType !== 'BINARY' &&
contract.outcomeType !== 'PSEUDO_NUMERIC')
)
return { status: 'error', message: 'Invalid contract' }
throw new APIError(400, 'Invalid contract')
const { closeTime } = contract
if (closeTime && Date.now() > closeTime)
return { status: 'error', message: 'Trading is closed' }
throw new APIError(400, 'Trading is closed')
if (user.balance < amount)
return { status: 'error', message: 'Insufficient balance' }
if (user.balance < amount) throw new APIError(400, 'Insufficient balance')
const newLiquidityProvisionDoc = firestore
.collection(`contracts/${contractId}/liquidity`)
@ -83,7 +75,7 @@ export const addLiquidity = functions.runWith({ minInstances: 1 }).https.onCall(
const newTotalDeposits = user.totalDeposits - amount
if (!isFinite(newBalance)) {
throw new Error('Invalid user balance for ' + user.username)
throw new APIError(500, 'Invalid user balance for ' + user.username)
}
transaction.update(userDoc, {
@ -93,13 +85,12 @@ export const addLiquidity = functions.runWith({ minInstances: 1 }).https.onCall(
transaction.create(newLiquidityProvisionDoc, newLiquidityProvision)
return { status: 'success', newLiquidityProvision }
return newLiquidityProvision
})
.then(async (result) => {
await redeemShares(userId, contractId)
await redeemShares(auth.uid, contractId)
return result
})
}
)
})
const firestore = admin.firestore()

View File

@ -1,5 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { z } from 'zod'
import { getUser } from './utils'
import { Contract } from '../../common/contract'
@ -11,37 +11,23 @@ import {
} from '../../common/util/clean-username'
import { removeUndefinedProps } from '../../common/util/object'
import { Answer } from '../../common/answer'
import { APIError, newEndpoint, validate } from './api'
export const changeUserInfo = functions
.runWith({ minInstances: 1 })
.https.onCall(
async (
data: {
username?: string
name?: string
avatarUrl?: string
},
context
) => {
const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' }
const user = await getUser(userId)
if (!user) return { status: 'error', message: 'User not found' }
const { username, name, avatarUrl } = data
return await changeUser(user, { username, name, avatarUrl })
.then(() => {
console.log('succesfully changed', user.username, 'to', data)
return { status: 'success' }
const bodySchema = z.object({
username: z.string().optional(),
name: z.string().optional(),
avatarUrl: z.string().optional(),
})
.catch((e) => {
console.log('Error', e.message)
return { status: 'error', message: e.message }
export const changeuserinfo = newEndpoint({}, async (req, auth) => {
const { username, name, avatarUrl } = validate(bodySchema, req.body)
const user = await getUser(auth.uid)
if (!user) throw new APIError(400, 'User not found')
await changeUser(user, { username, name, avatarUrl })
return { message: 'Successfully changed user info.' }
})
}
)
export const changeUser = async (
user: User,
@ -55,14 +41,14 @@ export const changeUser = async (
if (update.username) {
update.username = cleanUsername(update.username)
if (!update.username) {
throw new Error('Invalid username')
throw new APIError(400, 'Invalid username')
}
const sameNameUser = await transaction.get(
firestore.collection('users').where('username', '==', update.username)
)
if (!sameNameUser.empty) {
throw new Error('Username already exists')
throw new APIError(400, 'Username already exists')
}
}
@ -104,17 +90,10 @@ export const changeUser = async (
)
const answerUpdate: Partial<Answer> = removeUndefinedProps(update)
await transaction.update(userRef, userUpdate)
await Promise.all(
commentSnap.docs.map((d) => transaction.update(d.ref, commentUpdate))
)
await Promise.all(
answerSnap.docs.map((d) => transaction.update(d.ref, answerUpdate))
)
await contracts.docs.map((d) => transaction.update(d.ref, contractUpdate))
transaction.update(userRef, userUpdate)
commentSnap.docs.forEach((d) => transaction.update(d.ref, commentUpdate))
answerSnap.docs.forEach((d) => transaction.update(d.ref, answerUpdate))
contracts.docs.forEach((d) => transaction.update(d.ref, contractUpdate))
})
}

View File

@ -1,15 +1,17 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { z } from 'zod'
import { User } from 'common/user'
import { Manalink } from 'common/manalink'
import { runTxn, TxnData } from './transact'
import { APIError, newEndpoint, validate } from './api'
export const claimManalink = functions
.runWith({ minInstances: 1 })
.https.onCall(async (slug: string, context) => {
const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' }
const bodySchema = z.object({
slug: z.string(),
})
export const claimmanalink = newEndpoint({}, async (req, auth) => {
const { slug } = validate(bodySchema, req.body)
// Run as transaction to prevent race conditions.
return await firestore.runTransaction(async (transaction) => {
@ -17,85 +19,85 @@ export const claimManalink = functions
const manalinkDoc = firestore.doc(`manalinks/${slug}`)
const manalinkSnap = await transaction.get(manalinkDoc)
if (!manalinkSnap.exists) {
return { status: 'error', message: 'Manalink not found' }
throw new APIError(400, 'Manalink not found')
}
const manalink = manalinkSnap.data() as Manalink
const { amount, fromId, claimedUserIds } = manalink
if (amount <= 0 || isNaN(amount) || !isFinite(amount))
return { status: 'error', message: 'Invalid amount' }
throw new APIError(500, 'Invalid amount')
const fromDoc = firestore.doc(`users/${fromId}`)
const fromSnap = await transaction.get(fromDoc)
if (!fromSnap.exists) {
return { status: 'error', message: `User ${fromId} not found` }
throw new APIError(500, `User ${fromId} not found`)
}
const fromUser = fromSnap.data() as User
// Only permit one redemption per user per link
if (claimedUserIds.includes(userId)) {
return {
status: 'error',
message: `${fromUser.name} already redeemed manalink ${slug}`,
}
if (claimedUserIds.includes(auth.uid)) {
throw new APIError(400, `You already redeemed manalink ${slug}`)
}
// Disallow expired or maxed out links
if (manalink.expiresTime != null && manalink.expiresTime < Date.now()) {
return {
status: 'error',
message: `Manalink ${slug} expired on ${new Date(
throw new APIError(
400,
`Manalink ${slug} expired on ${new Date(
manalink.expiresTime
).toLocaleString()}`,
}
).toLocaleString()}`
)
}
if (
manalink.maxUses != null &&
manalink.maxUses <= manalink.claims.length
) {
return {
status: 'error',
message: `Manalink ${slug} has reached its max uses of ${manalink.maxUses}`,
}
throw new APIError(
400,
`Manalink ${slug} has reached its max uses of ${manalink.maxUses}`
)
}
if (fromUser.balance < amount) {
return {
status: 'error',
message: `Insufficient balance: ${fromUser.name} needed ${amount} for this manalink but only had ${fromUser.balance} `,
}
throw new APIError(
400,
`Insufficient balance: ${fromUser.name} needed ${amount} for this manalink but only had ${fromUser.balance} `
)
}
// Actually execute the txn
const data: TxnData = {
fromId,
fromType: 'USER',
toId: userId,
toId: auth.uid,
toType: 'USER',
amount,
token: 'M$',
category: 'MANALINK',
description: `Manalink ${slug} claimed: ${amount} from ${fromUser.username} to ${userId}`,
description: `Manalink ${slug} claimed: ${amount} from ${fromUser.username} to ${auth.uid}`,
}
const result = await runTxn(transaction, data)
const txnId = result.txn?.id
if (!txnId) {
return { status: 'error', message: result.message }
throw new APIError(
500,
result.message ?? 'An error occurred posting the transaction.'
)
}
// Update the manalink object with this info
const claim = {
toId: userId,
toId: auth.uid,
txnId,
claimedTime: Date.now(),
}
transaction.update(manalinkDoc, {
claimedUserIds: [...claimedUserIds, userId],
claimedUserIds: [...claimedUserIds, auth.uid],
claims: [...manalink.claims, claim],
})
return { status: 'success', message: 'Manalink claimed' }
return { message: 'Manalink claimed' }
})
})

View File

@ -1,5 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { z } from 'zod'
import { Contract } from '../../common/contract'
import { User } from '../../common/user'
@ -7,55 +7,41 @@ import { getNewMultiBetInfo } from '../../common/new-bet'
import { Answer, MAX_ANSWER_LENGTH } from '../../common/answer'
import { getContract, getValues } from './utils'
import { sendNewAnswerEmail } from './emails'
import { APIError, newEndpoint, validate } from './api'
export const createAnswer = functions
.runWith({ minInstances: 1, secrets: ['MAILGUN_KEY'] })
.https.onCall(
async (
data: {
contractId: string
amount: number
text: string
},
context
) => {
const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' }
const bodySchema = z.object({
contractId: z.string().max(MAX_ANSWER_LENGTH),
amount: z.number().gt(0),
text: z.string(),
})
const { contractId, amount, text } = data
const opts = { secrets: ['MAILGUN_KEY'] }
if (amount <= 0 || isNaN(amount) || !isFinite(amount))
return { status: 'error', message: 'Invalid amount' }
export const createanswer = newEndpoint(opts, async (req, auth) => {
const { contractId, amount, text } = validate(bodySchema, req.body)
if (!text || typeof text !== 'string' || text.length > MAX_ANSWER_LENGTH)
return { status: 'error', message: 'Invalid text' }
if (!isFinite(amount)) throw new APIError(400, 'Invalid amount')
// Run as transaction to prevent race conditions.
const result = await firestore.runTransaction(async (transaction) => {
const userDoc = firestore.doc(`users/${userId}`)
const answer = await firestore.runTransaction(async (transaction) => {
const userDoc = firestore.doc(`users/${auth.uid}`)
const userSnap = await transaction.get(userDoc)
if (!userSnap.exists)
return { status: 'error', message: 'User not found' }
if (!userSnap.exists) throw new APIError(400, 'User not found')
const user = userSnap.data() as User
if (user.balance < amount)
return { status: 'error', message: 'Insufficient balance' }
if (user.balance < amount) throw new APIError(400, 'Insufficient balance')
const contractDoc = firestore.doc(`contracts/${contractId}`)
const contractSnap = await transaction.get(contractDoc)
if (!contractSnap.exists)
return { status: 'error', message: 'Invalid contract' }
if (!contractSnap.exists) throw new APIError(400, 'Invalid contract')
const contract = contractSnap.data() as Contract
if (contract.outcomeType !== 'FREE_RESPONSE')
return {
status: 'error',
message: 'Requires a free response contract',
}
throw new APIError(400, 'Requires a free response contract')
const { closeTime, volume } = contract
if (closeTime && Date.now() > closeTime)
return { status: 'error', message: 'Trading is closed' }
throw new APIError(400, 'Trading is closed')
const [lastAnswer] = await getValues<Answer>(
firestore
@ -64,8 +50,7 @@ export const createAnswer = functions
.limit(1)
)
if (!lastAnswer)
return { status: 'error', message: 'Could not fetch last answer' }
if (!lastAnswer) throw new APIError(500, 'Could not fetch last answer')
const number = lastAnswer.number + 1
const id = `${number}`
@ -96,9 +81,7 @@ export const createAnswer = functions
getNewMultiBetInfo(answerId, amount, contract, loanAmount)
const newBalance = user.balance - amount
const betDoc = firestore
.collection(`contracts/${contractId}/bets`)
.doc()
const betDoc = firestore.collection(`contracts/${contractId}/bets`).doc()
transaction.create(betDoc, {
id: betDoc.id,
userId: user.id,
@ -113,16 +96,14 @@ export const createAnswer = functions
volume: volume + amount,
})
return { status: 'success', answerId, betId: betDoc.id, answer }
return answer
})
const { answer } = result
const contract = await getContract(contractId)
if (answer && contract) await sendNewAnswerEmail(answer, contract)
return result
}
)
return answer
})
const firestore = admin.firestore()

View File

@ -3,12 +3,9 @@ import * as admin from 'firebase-admin'
admin.initializeApp()
// v1
// export * from './keep-awake'
export * from './claim-manalink'
export * from './transact'
export * from './stripe'
export * from './create-user'
export * from './create-answer'
export * from './on-create-bet'
export * from './on-create-comment-on-contract'
export * from './on-view'
@ -16,9 +13,7 @@ export * from './unsubscribe'
export * from './update-metrics'
export * from './update-stats'
export * from './backup-db'
export * from './change-user-info'
export * from './market-close-notifications'
export * from './add-liquidity'
export * from './on-create-answer'
export * from './on-update-contract'
export * from './on-create-contract'
@ -33,11 +28,15 @@ export * from './on-create-txn'
// v2
export * from './health'
export * from './change-user-info'
export * from './create-answer'
export * from './place-bet'
export * from './cancel-bet'
export * from './sell-bet'
export * from './sell-shares'
export * from './claim-manalink'
export * from './create-contract'
export * from './add-liquidity'
export * from './withdraw-liquidity'
export * from './create-group'
export * from './resolve-market'

View File

@ -1,5 +1,5 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { z } from 'zod'
import { CPMMContract } from '../../common/contract'
import { User } from '../../common/user'
@ -10,36 +10,26 @@ import { Bet } from '../../common/bet'
import { getProbability } from '../../common/calculate'
import { noFees } from '../../common/fees'
import { APIError } from './api'
import { APIError, newEndpoint, validate } from './api'
import { redeemShares } from './redeem-shares'
export const withdrawLiquidity = functions
.runWith({ minInstances: 1 })
.https.onCall(
async (
data: {
contractId: string
},
context
) => {
const userId = context?.auth?.uid
if (!userId) return { status: 'error', message: 'Not authorized' }
const bodySchema = z.object({
contractId: z.string(),
})
const { contractId } = data
if (!contractId)
return { status: 'error', message: 'Missing contract id' }
export const withdrawliquidity = newEndpoint({}, async (req, auth) => {
const { contractId } = validate(bodySchema, req.body)
return await firestore
.runTransaction(async (trans) => {
const lpDoc = firestore.doc(`users/${userId}`)
const lpDoc = firestore.doc(`users/${auth.uid}`)
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.')
if (!contractSnap.exists) throw new APIError(400, 'Contract not found.')
const contract = contractSnap.data() as CPMMContract
const liquidityCollection = firestore.collection(
@ -52,18 +42,13 @@ export const withdrawLiquidity = functions
(doc) => doc.data() as LiquidityProvision
)
const userShares = getUserLiquidityShares(
userId,
contract,
liquidities
)
const userShares = getUserLiquidityShares(auth.uid, 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
(_, i) => !liquidities[i].isAnte && liquidities[i].userId === auth.uid
)
.forEach((doc) => trans.update(doc.ref, { amount: 0 }))
@ -98,7 +83,7 @@ export const withdrawLiquidity = functions
shares - payout < 1 // don't create bet if less than 1 share
? undefined
: ({
userId: userId,
userId: auth.uid,
contractId: contract.id,
amount:
(outcome === 'YES' ? prob : 1 - prob) * (shares - payout),
@ -114,9 +99,7 @@ export const withdrawLiquidity = functions
.filter((x) => x !== undefined)
for (const bet of bets) {
const doc = firestore
.collection(`contracts/${contract.id}/bets`)
.doc()
const doc = firestore.collection(`contracts/${contract.id}/bets`).doc()
trans.create(doc, { id: doc.id, ...bet })
}
@ -124,15 +107,10 @@ export const withdrawLiquidity = functions
})
.then(async (result) => {
// redeem surplus bet with pre-existing bets
await redeemShares(userId, contractId)
console.log('userid', userId, 'withdraws', result)
return { status: 'success', userShares: result }
await redeemShares(auth.uid, contractId)
console.log('userid', auth.uid, 'withdraws', result)
return result
})
.catch((e) => {
return { status: 'error', message: e.message }
})
}
)
const firestore = admin.firestore()

View File

@ -6,7 +6,7 @@ import { findBestMatch } from 'string-similarity'
import { FreeResponseContract } from 'common/contract'
import { BuyAmountInput } from '../amount-input'
import { Col } from '../layout/col'
import { createAnswer } from 'web/lib/firebase/fn-call'
import { APIError, createAnswer } from 'web/lib/firebase/api-call'
import { Row } from '../layout/row'
import {
formatMoney,
@ -46,20 +46,23 @@ export function CreateAnswerPanel(props: { contract: FreeResponseContract }) {
if (canSubmit) {
setIsSubmitting(true)
const result = await createAnswer({
try {
await createAnswer({
contractId: contract.id,
text,
amount: betAmount,
}).then((r) => r.data)
setIsSubmitting(false)
if (result.status === 'success') {
})
setText('')
setBetAmount(10)
setAmountError(undefined)
setPossibleDuplicateAnswer(undefined)
} else setAmountError(result.message)
} catch (e) {
if (e instanceof APIError) {
setAmountError(e.toString())
}
}
setIsSubmitting(false)
}
}

View File

@ -30,7 +30,7 @@ import { useUserContractBets } from 'web/hooks/use-user-bets'
import {
calculateCpmmSale,
getCpmmProbability,
getCpmmLiquidityFee,
getCpmmFees,
} from 'common/calculate-cpmm'
import {
getFormattedMappedValue,
@ -356,7 +356,7 @@ function BuyPanel(props: {
const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0
const currentReturnPercent = formatPercent(currentReturn)
const cpmmFees = getCpmmLiquidityFee(
const cpmmFees = getCpmmFees(
contract,
betAmount ?? 0,
betChoice ?? 'YES'

View File

@ -134,6 +134,13 @@ export function QuickBet(props: { contract: Contract; user: User }) {
})
}
if (outcomeType === 'FREE_RESPONSE')
return (
<Col className="relative -my-4 -mr-5 min-w-[5.5rem] justify-center gap-2 pr-5 pl-1 align-middle">
<QuickOutcomeView contract={contract} previewProb={previewProb} />
</Col>
)
return (
<Col
className={clsx(

View File

@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'
import { CPMMContract } from 'common/contract'
import { formatMoney } from 'common/util/format'
import { useUser } from 'web/hooks/use-user'
import { addLiquidity, withdrawLiquidity } from 'web/lib/firebase/fn-call'
import { addLiquidity, withdrawLiquidity } from 'web/lib/firebase/api-call'
import { AmountInput } from './amount-input'
import { Row } from './layout/row'
import { useUserLiquidity } from 'web/hooks/use-liquidity'
@ -90,14 +90,10 @@ function AddLiquidityPanel(props: { contract: CPMMContract }) {
setIsSuccess(false)
addLiquidity({ amount, contractId })
.then((r) => {
if (r.status === 'success') {
.then((_) => {
setIsSuccess(true)
setError(undefined)
setIsLoading(false)
} else {
setError('Server error')
}
})
.catch((_) => setError('Server error'))

View File

@ -50,6 +50,21 @@ export function getFunctionUrl(name: string) {
}
}
export function createAnswer(params: any) {
return call(getFunctionUrl('createanswer'), 'POST', params)
}
export function changeUserInfo(params: any) {
return call(getFunctionUrl('changeuserinfo'), 'POST', params)
}
export function addLiquidity(params: any) {
return call(getFunctionUrl('addliquidity'), 'POST', params)
}
export function withdrawLiquidity(params: any) {
return call(getFunctionUrl('withdrawliquidity'), 'POST', params)
}
export function createMarket(params: any) {
return call(getFunctionUrl('createmarket'), 'POST', params)
}
@ -74,6 +89,10 @@ export function sellBet(params: any) {
return call(getFunctionUrl('sellbet'), 'POST', params)
}
export function claimManalink(params: any) {
return call(getFunctionUrl('claimmanalink'), 'POST', params)
}
export function createGroup(params: any) {
return call(getFunctionUrl('creategroup'), 'POST', params)
}

View File

@ -9,26 +9,11 @@ import { safeLocalStorage } from '../util/local'
export const cloudFunction = <RequestData, ResponseData>(name: string) =>
httpsCallable<RequestData, ResponseData>(functions, name)
export const withdrawLiquidity = cloudFunction<
{ contractId: string },
{ status: 'error' | 'success'; userShares: { [outcome: string]: number } }
>('withdrawLiquidity')
export const transact = cloudFunction<
Omit<Txn, 'id' | 'createdTime'>,
{ status: 'error' | 'success'; message?: string; txn?: Txn }
>('transact')
export const createAnswer = cloudFunction<
{ contractId: string; text: string; amount: number },
{
status: 'error' | 'success'
message?: string
answerId?: string
betId?: string
}
>('createAnswer')
export const createUser: () => Promise<User | null> = () => {
const local = safeLocalStorage()
let deviceToken = local?.getItem('device-token')
@ -41,24 +26,3 @@ export const createUser: () => Promise<User | null> = () => {
.then((r) => (r.data as any)?.user || null)
.catch(() => null)
}
export const changeUserInfo = (data: {
username?: string
name?: string
avatarUrl?: string
}) => {
return cloudFunction('changeUserInfo')(data)
.then((r) => r.data as { status: string; message?: string })
.catch((e) => ({ status: 'error', message: e.message }))
}
export const addLiquidity = (data: { amount: number; contractId: string }) => {
return cloudFunction('addLiquidity')(data)
.then((r) => r.data as { status: string })
.catch((e) => ({ status: 'error', message: e.message }))
}
export const claimManalink = cloudFunction<
string,
{ status: 'error' | 'success'; message?: string }
>('claimManalink')

View File

@ -2,7 +2,7 @@ import { useRouter } from 'next/router'
import { useState } from 'react'
import { SEO } from 'web/components/SEO'
import { Title } from 'web/components/title'
import { claimManalink } from 'web/lib/firebase/fn-call'
import { claimManalink } from 'web/lib/firebase/api-call'
import { useManalink } from 'web/lib/firebase/manalinks'
import { ManalinkCard } from 'web/components/manalink-card'
import { useUser } from 'web/hooks/use-user'
@ -42,10 +42,7 @@ export default function ClaimPage() {
if (user == null) {
await firebaseLogin()
}
const result = await claimManalink(manalink.slug)
if (result.data.status == 'error') {
throw new Error(result.data.message)
}
await claimManalink({ slug: manalink.slug })
user && router.push(`/${user.username}?claimed-mana=yes`)
} catch (e) {
console.log(e)

View File

@ -9,7 +9,7 @@ import { Title } from 'web/components/title'
import { usePrivateUser, useUser } from 'web/hooks/use-user'
import { formatMoney } from 'common/util/format'
import { cleanDisplayName, cleanUsername } from 'common/util/clean-username'
import { changeUserInfo } from 'web/lib/firebase/fn-call'
import { changeUserInfo } from 'web/lib/firebase/api-call'
import { uploadImage } from 'web/lib/firebase/storage'
import { Col } from 'web/components/layout/col'
import { Row } from 'web/components/layout/row'
@ -85,12 +85,9 @@ export default function ProfilePage() {
if (newName) {
setName(newName)
await changeUserInfo({ name: newName })
.catch(() => ({ status: 'error' }))
.then((r) => {
if (r.status === 'error') setName(user?.name || '')
})
await changeUserInfo({ name: newName }).catch((_) =>
setName(user?.name || '')
)
} else {
setName(user?.name || '')
}
@ -101,11 +98,9 @@ export default function ProfilePage() {
if (newUsername) {
setUsername(newUsername)
await changeUserInfo({ username: newUsername })
.catch(() => ({ status: 'error' }))
.then((r) => {
if (r.status === 'error') setUsername(user?.username || '')
})
await changeUserInfo({ username: newUsername }).catch((_) =>
setUsername(user?.username || '')
)
} else {
setUsername(user?.username || '')
}