manifold/functions/src/utils.ts
James Grugett b2501d8145
Free response (#47)
* Answer datatype and MULTI outcome type for Contract

* Create free answer contract

* Automatically sort Tailwind classes with Prettier (#45)

* Add Prettier Tailwind plugin

* Autoformat Tailwind classes with Prettier

* Allow for non-binary contracts in contract page and related components

* logo with white inside, transparent bg

* Create answer

* Some UI for showing answers

* Answer bet panel

* Convert rest of calcuate file to generic multi contracts

* Working betting with ante'd NONE answer

* Numbered answers. Layout & calculation tweaks

* Can bet. More layout tweaks!

* Resolve answer UI

* Resolve multi market

* Resolved market UI

* Fix feed and cards for multi contracts

* Sell bets. Various fixes

* Tweaks for trades page

* Always dev mode

* Create answer bet has isAnte: true

* Fix  card showing 0% for multi contracts

* Fix grouped bets feed for multi outcomes

* None option converted to none of the above label at bottom of list. Button to resolve none.

* Tweaks to no answers yet, resolve button layout

* Show ante bets on new answers in the feed

* Update placeholder text for description

* Consolidate firestore rules for subcollections

* Remove Contract and Bet type params. Use string type for outcomes.

* Increase char limit to 10k for answers. Preserve line breaks.

* Don't show resolve options after answer chosen

* Fix type error in script

* Remove NONE resolution option

* Change outcomeType to include 'MULTI' and 'FREE_RESPONSE'

* Show bet probability change and payout when creating answer

* User info change: also change answers

* Append answers to contract field 'answers'

* sort trades by resolved

* Don't include trailing !:,.; in links

* Stop flooring inputs into formatMoney

* Revert "Stop flooring inputs into formatMoney"

This reverts commit 2f7ab18429.

* Consistently floor user.balance

* Expand create panel on focus

From Richard Hanania's feedback

* welcome email: include link to manifold

* Fix home page in dev on branches that are not free-response

* Close emails (#50)

* script init for stephen dev

* market close emails

* order of operations

* template email

* sendMarketCloseEmail: handle unsubscribe

* remove debugging

* marketCloseEmails: every hour

* sendMarketCloseEmails: check undefined

* marketCloseEmails: "every hour" => "every 1 hours"

* Set up a read API using Vercel serverless functions (#49)

* Set up read API using Vercel serverless functions

Featuring:
/api/v0/markets
/api/v0/market/[contractId]
/api/v0/slug/[contractSlug]

* Include tags in API

* Tweaks. Remove filter for only binary contract

* Fix bet probability change for NO bets

* Put back isProd calculation

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: mantikoros <sgrugett@gmail.com>
Co-authored-by: mantikoros <95266179+mantikoros@users.noreply.github.com>
2022-02-17 17:00:19 -06:00

80 lines
2.2 KiB
TypeScript

import * as admin from 'firebase-admin'
import { Contract } from '../../common/contract'
import { PrivateUser, User } from '../../common/user'
export const getValue = async <T>(collection: string, doc: string) => {
const snap = await admin.firestore().collection(collection).doc(doc).get()
return snap.exists ? (snap.data() as T) : undefined
}
export const getValues = async <T>(query: admin.firestore.Query) => {
const snap = await query.get()
return snap.docs.map((doc) => doc.data() as T)
}
export const getContract = (contractId: string) => {
return getValue<Contract>('contracts', contractId)
}
export const getUser = (userId: string) => {
return getValue<User>('users', userId)
}
export const getPrivateUser = (userId: string) => {
return getValue<PrivateUser>('private-users', userId)
}
export const getUserByUsername = async (username: string) => {
const snap = await firestore
.collection('users')
.where('username', '==', username)
.get()
return snap.empty ? undefined : (snap.docs[0].data() as User)
}
const firestore = admin.firestore()
const updateUserBalance = (
userId: string,
delta: number,
isDeposit = false
) => {
return firestore.runTransaction(async (transaction) => {
const userDoc = firestore.doc(`users/${userId}`)
const userSnap = await transaction.get(userDoc)
if (!userSnap.exists) return
const user = userSnap.data() as User
const newUserBalance = user.balance + delta
if (newUserBalance < 0)
throw new Error(
`User (${userId}) balance cannot be negative: ${newUserBalance}`
)
if (isDeposit) {
const newTotalDeposits = (user.totalDeposits || 0) + delta
transaction.update(userDoc, { totalDeposits: newTotalDeposits })
}
transaction.update(userDoc, { balance: newUserBalance })
})
}
export const payUser = (userId: string, payout: number, isDeposit = false) => {
if (!isFinite(payout) || payout <= 0)
throw new Error('Payout is not positive: ' + payout)
return updateUserBalance(userId, payout, isDeposit)
}
export const chargeUser = (userId: string, charge: number) => {
if (!isFinite(charge) || charge <= 0)
throw new Error('User charge is not positive: ' + charge)
return updateUserBalance(userId, -charge)
}