Rewrite client for new public APIs to use fetch
instead of callables (#241)
* Rename `lib/firebase/api-call` -> `lib/firebase/fn-call` This relieves ambiguity now that we will be using our actual public API in the client. * Rewrite client API calls to createContract, placeBet * Tiny fixup for client market creation code
This commit is contained in:
parent
d4a49789d1
commit
20f4b97d8b
|
@ -4,7 +4,7 @@ import { useState } from 'react'
|
|||
import { Contract } from 'common/contract'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { addLiquidity } from 'web/lib/firebase/api-call'
|
||||
import { addLiquidity } from 'web/lib/firebase/fn-call'
|
||||
import { AmountInput } from './amount-input'
|
||||
import { Row } from './layout/row'
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Answer } from 'common/answer'
|
|||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { placeBet } from 'web/lib/firebase/api-call'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
import { Row } from '../layout/row'
|
||||
import { Spacer } from '../layout/spacer'
|
||||
import {
|
||||
|
@ -52,22 +52,26 @@ export function AnswerBetPanel(props: {
|
|||
setError(undefined)
|
||||
setIsSubmitting(true)
|
||||
|
||||
const result = await placeBet({
|
||||
placeBet({
|
||||
amount: betAmount,
|
||||
outcome: answerId,
|
||||
contractId: contract.id,
|
||||
}).then((r) => r.data as any)
|
||||
|
||||
console.log('placed bet. Result:', result)
|
||||
|
||||
if (result?.status === 'success') {
|
||||
setIsSubmitting(false)
|
||||
setBetAmount(undefined)
|
||||
props.closePanel()
|
||||
} else {
|
||||
setError(result?.message || 'Error placing bet')
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
})
|
||||
.then((r) => {
|
||||
console.log('placed bet. Result:', r)
|
||||
setIsSubmitting(false)
|
||||
setBetAmount(undefined)
|
||||
props.closePanel()
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof APIError) {
|
||||
setError(e.toString())
|
||||
} else {
|
||||
console.error(e)
|
||||
setError('Error placing bet')
|
||||
}
|
||||
setIsSubmitting(false)
|
||||
})
|
||||
}
|
||||
|
||||
const betDisabled = isSubmitting || !betAmount || error
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useState } from 'react'
|
|||
|
||||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { Col } from '../layout/col'
|
||||
import { resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
||||
import { Row } from '../layout/row'
|
||||
import { ChooseCancelSelector } from '../yes-no-selector'
|
||||
import { ResolveConfirmationButton } from '../confirmation-button'
|
||||
|
|
|
@ -5,7 +5,7 @@ import Textarea from 'react-expanding-textarea'
|
|||
import { DPM, FreeResponse, FullContract } from 'common/contract'
|
||||
import { BuyAmountInput } from '../amount-input'
|
||||
import { Col } from '../layout/col'
|
||||
import { createAnswer } from 'web/lib/firebase/api-call'
|
||||
import { createAnswer } from 'web/lib/firebase/fn-call'
|
||||
import { Row } from '../layout/row'
|
||||
import {
|
||||
formatMoney,
|
||||
|
|
|
@ -16,7 +16,8 @@ import {
|
|||
import { Title } from './title'
|
||||
import { firebaseLogin, User } from 'web/lib/firebase/users'
|
||||
import { Bet } from 'common/bet'
|
||||
import { placeBet, sellShares } from 'web/lib/firebase/api-call'
|
||||
import { APIError, placeBet } from 'web/lib/firebase/api-call'
|
||||
import { sellShares } from 'web/lib/firebase/fn-call'
|
||||
import { AmountInput, BuyAmountInput } from './amount-input'
|
||||
import { InfoTooltip } from './info-tooltip'
|
||||
import { BinaryOutcomeLabel } from './outcome-label'
|
||||
|
@ -240,23 +241,27 @@ function BuyPanel(props: {
|
|||
setError(undefined)
|
||||
setIsSubmitting(true)
|
||||
|
||||
const result = await placeBet({
|
||||
placeBet({
|
||||
amount: betAmount,
|
||||
outcome: betChoice,
|
||||
contractId: contract.id,
|
||||
}).then((r) => r.data as any)
|
||||
|
||||
console.log('placed bet. Result:', result)
|
||||
|
||||
if (result?.status === 'success') {
|
||||
setIsSubmitting(false)
|
||||
setWasSubmitted(true)
|
||||
setBetAmount(undefined)
|
||||
if (onBuySuccess) onBuySuccess()
|
||||
} else {
|
||||
setError(result?.message || 'Error placing bet')
|
||||
setIsSubmitting(false)
|
||||
}
|
||||
})
|
||||
.then((r) => {
|
||||
console.log('placed bet. Result:', r)
|
||||
setIsSubmitting(false)
|
||||
setWasSubmitted(true)
|
||||
setBetAmount(undefined)
|
||||
if (onBuySuccess) onBuySuccess()
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e instanceof APIError) {
|
||||
setError(e.toString())
|
||||
} else {
|
||||
console.error(e)
|
||||
setError('Error placing bet')
|
||||
}
|
||||
setIsSubmitting(false)
|
||||
})
|
||||
}
|
||||
|
||||
const betDisabled = isSubmitting || !betAmount || error
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
} from 'web/lib/firebase/contracts'
|
||||
import { Row } from './layout/row'
|
||||
import { UserLink } from './user-page'
|
||||
import { sellBet } from 'web/lib/firebase/api-call'
|
||||
import { sellBet } from 'web/lib/firebase/fn-call'
|
||||
import { ConfirmationButton } from './confirmation-button'
|
||||
import { OutcomeLabel, YesLabel, NoLabel } from './outcome-label'
|
||||
import { filterDefined } from 'common/util/array'
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useRouter } from 'next/router'
|
|||
import { useState } from 'react'
|
||||
import { PlusCircleIcon } from '@heroicons/react/solid'
|
||||
import { parseWordsAsTags } from 'common/util/parse'
|
||||
import { createFold } from 'web/lib/firebase/api-call'
|
||||
import { createFold } from 'web/lib/firebase/fn-call'
|
||||
import { foldPath } from 'web/lib/firebase/folds'
|
||||
import { toCamelCase } from 'common/util/format'
|
||||
import { ConfirmationButton } from '../confirmation-button'
|
||||
|
|
|
@ -6,7 +6,7 @@ import { User } from 'web/lib/firebase/users'
|
|||
import { NumberCancelSelector } from './yes-no-selector'
|
||||
import { Spacer } from './layout/spacer'
|
||||
import { ResolveConfirmationButton } from './confirmation-button'
|
||||
import { resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
||||
import { NumericContract } from 'common/contract'
|
||||
import { BucketInput } from './bucket-input'
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { User } from 'web/lib/firebase/users'
|
|||
import { YesNoCancelSelector } from './yes-no-selector'
|
||||
import { Spacer } from './layout/spacer'
|
||||
import { ResolveConfirmationButton } from './confirmation-button'
|
||||
import { resolveMarket } from 'web/lib/firebase/api-call'
|
||||
import { resolveMarket } from 'web/lib/firebase/fn-call'
|
||||
import { ProbabilitySelector } from './probability-selector'
|
||||
import { DPM_CREATOR_FEE } from 'common/fees'
|
||||
import { getProbability } from 'common/calculate'
|
||||
|
|
|
@ -1,80 +1,45 @@
|
|||
import { httpsCallable } from 'firebase/functions'
|
||||
import { Fold } from 'common/fold'
|
||||
import { Txn } from 'common/txn'
|
||||
import { User } from 'common/user'
|
||||
import { randomString } from 'common/util/random'
|
||||
import './init'
|
||||
import { functions } from './init'
|
||||
import { auth } from './users'
|
||||
import { app, functions } from './init'
|
||||
|
||||
export const cloudFunction = <RequestData, ResponseData>(name: string) =>
|
||||
httpsCallable<RequestData, ResponseData>(functions, name)
|
||||
|
||||
export const createContract = cloudFunction('createContract')
|
||||
|
||||
export const createFold = cloudFunction<
|
||||
{ name: string; about: string; tags: string[] },
|
||||
{ status: 'error' | 'success'; message?: string; fold?: Fold }
|
||||
>('createFold')
|
||||
|
||||
export const transact = cloudFunction<
|
||||
Omit<Txn, 'id' | 'createdTime'>,
|
||||
{ status: 'error' | 'success'; message?: string; txn?: Txn }
|
||||
>('transact')
|
||||
|
||||
export const placeBet = cloudFunction('placeBet')
|
||||
|
||||
export const sellBet = cloudFunction('sellBet')
|
||||
|
||||
export const sellShares = cloudFunction<
|
||||
{ contractId: string; shares: number; outcome: 'YES' | 'NO' },
|
||||
{ status: 'error' | 'success'; message?: string }
|
||||
>('sellShares')
|
||||
|
||||
export const createAnswer = cloudFunction<
|
||||
{ contractId: string; text: string; amount: number },
|
||||
{
|
||||
status: 'error' | 'success'
|
||||
message?: string
|
||||
answerId?: string
|
||||
betId?: string
|
||||
export class APIError extends Error {
|
||||
code: number
|
||||
constructor(code: number, message: string) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'APIError'
|
||||
}
|
||||
>('createAnswer')
|
||||
}
|
||||
|
||||
export const resolveMarket = cloudFunction<
|
||||
{
|
||||
outcome: string
|
||||
value?: number
|
||||
contractId: string
|
||||
probabilityInt?: number
|
||||
resolutions?: { [outcome: string]: number }
|
||||
},
|
||||
{ status: 'error' | 'success'; message?: string }
|
||||
>('resolveMarket')
|
||||
|
||||
export const createUser: () => Promise<User | null> = () => {
|
||||
let deviceToken = window.localStorage.getItem('device-token')
|
||||
if (!deviceToken) {
|
||||
deviceToken = randomString()
|
||||
window.localStorage.setItem('device-token', deviceToken)
|
||||
export async function call(name: string, method: string, params: any) {
|
||||
const user = auth.currentUser
|
||||
if (user == null) {
|
||||
throw new Error('Must be signed in to make API calls.')
|
||||
}
|
||||
|
||||
return cloudFunction('createUser')({ deviceToken })
|
||||
.then((r) => (r.data as any)?.user || null)
|
||||
.catch(() => null)
|
||||
const token = await user.getIdToken()
|
||||
const region = functions.region
|
||||
const projectId = app.options.projectId
|
||||
const url = `https://${region}-${projectId}.cloudfunctions.net/${name}`
|
||||
const req = new Request(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: method,
|
||||
body: JSON.stringify({ data: params }),
|
||||
})
|
||||
return await fetch(req).then(async (resp) => {
|
||||
const json = (await resp.json()) as { [k: string]: any }
|
||||
if (json.data.status == 'error') {
|
||||
throw new APIError(resp.status, json.data.message)
|
||||
}
|
||||
return json.data
|
||||
})
|
||||
}
|
||||
|
||||
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 function createContract(params: any) {
|
||||
return call('createContract', 'POST', params)
|
||||
}
|
||||
|
||||
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 function placeBet(params: any) {
|
||||
return call('placeBet', 'POST', params)
|
||||
}
|
||||
|
|
76
web/lib/firebase/fn-call.ts
Normal file
76
web/lib/firebase/fn-call.ts
Normal file
|
@ -0,0 +1,76 @@
|
|||
import { httpsCallable } from 'firebase/functions'
|
||||
import { Fold } from 'common/fold'
|
||||
import { Txn } from 'common/txn'
|
||||
import { User } from 'common/user'
|
||||
import { randomString } from 'common/util/random'
|
||||
import './init'
|
||||
import { functions } from './init'
|
||||
|
||||
export const cloudFunction = <RequestData, ResponseData>(name: string) =>
|
||||
httpsCallable<RequestData, ResponseData>(functions, name)
|
||||
|
||||
export const createFold = cloudFunction<
|
||||
{ name: string; about: string; tags: string[] },
|
||||
{ status: 'error' | 'success'; message?: string; fold?: Fold }
|
||||
>('createFold')
|
||||
|
||||
export const transact = cloudFunction<
|
||||
Omit<Txn, 'id' | 'createdTime'>,
|
||||
{ status: 'error' | 'success'; message?: string; txn?: Txn }
|
||||
>('transact')
|
||||
|
||||
export const sellBet = cloudFunction('sellBet')
|
||||
|
||||
export const sellShares = cloudFunction<
|
||||
{ contractId: string; shares: number; outcome: 'YES' | 'NO' },
|
||||
{ status: 'error' | 'success'; message?: string }
|
||||
>('sellShares')
|
||||
|
||||
export const createAnswer = cloudFunction<
|
||||
{ contractId: string; text: string; amount: number },
|
||||
{
|
||||
status: 'error' | 'success'
|
||||
message?: string
|
||||
answerId?: string
|
||||
betId?: string
|
||||
}
|
||||
>('createAnswer')
|
||||
|
||||
export const resolveMarket = cloudFunction<
|
||||
{
|
||||
outcome: string
|
||||
value?: number
|
||||
contractId: string
|
||||
probabilityInt?: number
|
||||
resolutions?: { [outcome: string]: number }
|
||||
},
|
||||
{ status: 'error' | 'success'; message?: string }
|
||||
>('resolveMarket')
|
||||
|
||||
export const createUser: () => Promise<User | null> = () => {
|
||||
let deviceToken = window.localStorage.getItem('device-token')
|
||||
if (!deviceToken) {
|
||||
deviceToken = randomString()
|
||||
window.localStorage.setItem('device-token', deviceToken)
|
||||
}
|
||||
|
||||
return cloudFunction('createUser')({ deviceToken })
|
||||
.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 }))
|
||||
}
|
|
@ -22,7 +22,7 @@ import _ from 'lodash'
|
|||
|
||||
import { app } from './init'
|
||||
import { PrivateUser, User } from 'common/user'
|
||||
import { createUser } from './api-call'
|
||||
import { createUser } from './fn-call'
|
||||
import { getValue, getValues, listenForValue, listenForValues } from './utils'
|
||||
import { DAY_MS } from 'common/util/time'
|
||||
import { feed } from 'common/feed'
|
||||
|
|
|
@ -10,7 +10,7 @@ import { Spacer } from 'web/components/layout/spacer'
|
|||
import { User } from 'common/user'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { Linkify } from 'web/components/linkify'
|
||||
import { transact } from 'web/lib/firebase/api-call'
|
||||
import { transact } from 'web/lib/firebase/fn-call'
|
||||
import { charities, Charity } from 'common/charity'
|
||||
import { useRouter } from 'next/router'
|
||||
import Custom404 from '../404'
|
||||
|
|
|
@ -124,26 +124,24 @@ export function NewContract(props: { question: string; tag?: string }) {
|
|||
|
||||
setIsSubmitting(true)
|
||||
|
||||
const result: any = await createContract(
|
||||
removeUndefinedProps({
|
||||
question,
|
||||
outcomeType,
|
||||
description,
|
||||
initialProb,
|
||||
ante,
|
||||
closeTime,
|
||||
tags: category ? [category] : undefined,
|
||||
min,
|
||||
max,
|
||||
})
|
||||
).then((r) => r.data || {})
|
||||
|
||||
if (result.status !== 'success') {
|
||||
console.log('error creating contract', result)
|
||||
return
|
||||
try {
|
||||
const result = await createContract(
|
||||
removeUndefinedProps({
|
||||
question,
|
||||
outcomeType,
|
||||
description,
|
||||
initialProb,
|
||||
ante,
|
||||
closeTime,
|
||||
tags: category ? [category] : undefined,
|
||||
min,
|
||||
max,
|
||||
})
|
||||
)
|
||||
await router.push(contractPath(result.contract as Contract))
|
||||
} catch (e) {
|
||||
console.log('error creating contract', e)
|
||||
}
|
||||
|
||||
await router.push(contractPath(result.contract as Contract))
|
||||
}
|
||||
|
||||
const descriptionPlaceholder =
|
||||
|
|
|
@ -155,7 +155,7 @@ ${TEST_VALUE}
|
|||
ante,
|
||||
closeTime,
|
||||
tags: parseWordsAsTags(tags),
|
||||
}).then((r) => (r.data as any).contract)
|
||||
}).then((r) => r.contract)
|
||||
|
||||
setCreatedContracts((prev) => [...prev, contract])
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ ${TEST_VALUE}
|
|||
<label className="label mb-1 gap-2">
|
||||
<span>Market ante</span>
|
||||
<InfoTooltip
|
||||
text={`Subsidize your market to encourage trading. Ante bets are set to match your initial probability.
|
||||
text={`Subsidize your market to encourage trading. Ante bets are set to match your initial probability.
|
||||
You earn ${0.01 * 100}% of trading volume.`}
|
||||
/>
|
||||
</label>
|
||||
|
|
|
@ -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/api-call'
|
||||
import { changeUserInfo } from 'web/lib/firebase/fn-call'
|
||||
import { uploadImage } from 'web/lib/firebase/storage'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
|
|
Loading…
Reference in New Issue
Block a user