2022-01-05 05:51:26 +00:00
|
|
|
import * as functions from 'firebase-functions'
|
|
|
|
import * as admin from 'firebase-admin'
|
|
|
|
|
2022-01-10 22:49:04 +00:00
|
|
|
import { chargeUser, getUser } from './utils'
|
2022-01-10 21:07:57 +00:00
|
|
|
import { Contract } from '../../common/contract'
|
|
|
|
import { slugify } from '../../common/util/slugify'
|
2022-01-15 06:43:15 +00:00
|
|
|
import { randomString } from '../../common/util/random'
|
2022-01-10 22:49:04 +00:00
|
|
|
import { getNewContract } from '../../common/new-contract'
|
2022-01-14 23:39:17 +00:00
|
|
|
import { getAnteBets, MINIMUM_ANTE } from '../../common/antes'
|
2022-01-05 05:51:26 +00:00
|
|
|
|
|
|
|
export const createContract = functions
|
|
|
|
.runWith({ minInstances: 1 })
|
|
|
|
.https.onCall(
|
|
|
|
async (
|
|
|
|
data: {
|
|
|
|
question: string
|
|
|
|
description: string
|
|
|
|
initialProb: number
|
2022-01-24 22:33:02 +00:00
|
|
|
ante: number
|
|
|
|
closeTime: number
|
2022-01-05 05:51:26 +00:00
|
|
|
},
|
|
|
|
context
|
|
|
|
) => {
|
|
|
|
const userId = context?.auth?.uid
|
|
|
|
if (!userId) return { status: 'error', message: 'Not authorized' }
|
|
|
|
|
|
|
|
const creator = await getUser(userId)
|
|
|
|
if (!creator) return { status: 'error', message: 'User not found' }
|
|
|
|
|
|
|
|
const { question, description, initialProb, ante, closeTime } = data
|
|
|
|
|
2022-01-05 18:23:58 +00:00
|
|
|
if (!question || !initialProb)
|
|
|
|
return { status: 'error', message: 'Missing contract attributes' }
|
|
|
|
|
2022-01-14 23:39:17 +00:00
|
|
|
if (initialProb < 1 || initialProb > 99)
|
|
|
|
return { status: 'error', message: 'Invalid initial probability' }
|
|
|
|
|
2022-01-08 17:51:31 +00:00
|
|
|
if (
|
2022-01-14 23:39:17 +00:00
|
|
|
ante === undefined ||
|
|
|
|
ante < MINIMUM_ANTE ||
|
|
|
|
ante > creator.balance ||
|
|
|
|
isNaN(ante) ||
|
|
|
|
!isFinite(ante)
|
2022-01-08 17:51:31 +00:00
|
|
|
)
|
2022-01-05 05:51:26 +00:00
|
|
|
return { status: 'error', message: 'Invalid ante' }
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
'creating contract for',
|
|
|
|
creator.username,
|
|
|
|
'on',
|
|
|
|
question,
|
|
|
|
'ante:',
|
|
|
|
ante || 0
|
|
|
|
)
|
|
|
|
|
|
|
|
const slug = await getSlug(question)
|
|
|
|
|
|
|
|
const contractRef = firestore.collection('contracts').doc()
|
|
|
|
|
|
|
|
const contract = getNewContract(
|
|
|
|
contractRef.id,
|
|
|
|
slug,
|
|
|
|
creator,
|
|
|
|
question,
|
|
|
|
description,
|
|
|
|
initialProb,
|
|
|
|
ante,
|
|
|
|
closeTime
|
|
|
|
)
|
|
|
|
|
2022-01-10 22:49:04 +00:00
|
|
|
if (ante) await chargeUser(creator.id, ante)
|
2022-01-05 05:51:26 +00:00
|
|
|
|
|
|
|
await contractRef.create(contract)
|
2022-01-12 19:01:04 +00:00
|
|
|
|
|
|
|
if (ante) {
|
|
|
|
const yesBetDoc = firestore
|
|
|
|
.collection(`contracts/${contract.id}/bets`)
|
|
|
|
.doc()
|
|
|
|
|
|
|
|
const noBetDoc = firestore
|
|
|
|
.collection(`contracts/${contract.id}/bets`)
|
|
|
|
.doc()
|
|
|
|
|
|
|
|
const { yesBet, noBet } = getAnteBets(
|
|
|
|
creator,
|
|
|
|
contract,
|
|
|
|
yesBetDoc.id,
|
|
|
|
noBetDoc.id
|
|
|
|
)
|
|
|
|
await yesBetDoc.set(yesBet)
|
|
|
|
await noBetDoc.set(noBet)
|
|
|
|
}
|
|
|
|
|
2022-01-05 05:51:26 +00:00
|
|
|
return { status: 'success', contract }
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
const getSlug = async (question: string) => {
|
2022-01-10 22:49:04 +00:00
|
|
|
const proposedSlug = slugify(question)
|
2022-01-05 05:51:26 +00:00
|
|
|
|
|
|
|
const preexistingContract = await getContractFromSlug(proposedSlug)
|
|
|
|
|
|
|
|
return preexistingContract
|
|
|
|
? proposedSlug + '-' + randomString()
|
|
|
|
: proposedSlug
|
|
|
|
}
|
|
|
|
|
|
|
|
const firestore = admin.firestore()
|
|
|
|
|
|
|
|
export async function getContractFromSlug(slug: string) {
|
|
|
|
const snap = await firestore
|
|
|
|
.collection('contracts')
|
|
|
|
.where('slug', '==', slug)
|
|
|
|
.get()
|
|
|
|
|
|
|
|
return snap.empty ? undefined : (snap.docs[0].data() as Contract)
|
|
|
|
}
|