Skip to content

Commit

Permalink
Migrate liquidity docs to ADD_SUBSIDY txns
Browse files Browse the repository at this point in the history
- add txn for antes, house subsidies
  • Loading branch information
sipec committed Apr 5, 2024
1 parent e6dc3aa commit f06def8
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 215 deletions.
25 changes: 11 additions & 14 deletions backend/api/src/add-subsidy.ts
Expand Up @@ -5,6 +5,7 @@ import { getNewLiquidityProvision } from 'common/add-liquidity'
import { APIError, type APIHandler } from './helpers/endpoint'
import { SUBSIDY_FEE } from 'common/economy'
import { runTxn } from 'shared/txn/run-txn'
import { type Txn } from 'common/txn'

export const addLiquidity: APIHandler<
'market/:contractId/add-liquidity'
Expand Down Expand Up @@ -35,21 +36,14 @@ export const addLiquidity: APIHandler<

if (user.balance < amount) throw new APIError(403, 'Insufficient balance')

const newLiquidityProvisionDoc = firestore
.collection(`contracts/${contractId}/liquidity`)
.doc()

const subsidyAmount = (1 - SUBSIDY_FEE) * amount

const { newLiquidityProvision, newTotalLiquidity, newSubsidyPool } =
getNewLiquidityProvision(
user.id,
subsidyAmount,
contract,
newLiquidityProvisionDoc.id
)
const { newTotalLiquidity, newSubsidyPool } = getNewLiquidityProvision(
subsidyAmount,
contract
)

await runTxn(transaction, {
const { status, message, txn } = await runTxn(transaction, {
fromId: user.id,
amount: amount,
toId: contractId,
Expand All @@ -59,6 +53,10 @@ export const addLiquidity: APIHandler<
fromType: 'USER',
})

if (status === 'error' || !txn) {
throw new APIError(500, message ?? 'Unknown error')
}

transaction.update(contractDoc, {
subsidyPool: newSubsidyPool,
totalLiquidity: newTotalLiquidity,
Expand All @@ -70,8 +68,7 @@ export const addLiquidity: APIHandler<
throw new APIError(500, 'Invalid user balance for ' + user.username)
}

transaction.create(newLiquidityProvisionDoc, newLiquidityProvision)
return newLiquidityProvision
return txn as Txn
})
}

Expand Down
15 changes: 7 additions & 8 deletions backend/api/src/create-answer-cpmm.ts
Expand Up @@ -17,11 +17,13 @@ import { isAdminId } from 'common/envs/constants'
import { Bet } from 'common/bet'
import { floatingEqual } from 'common/util/math'
import { noFees } from 'common/fees'
import { getCpmmInitialLiquidity } from 'common/antes'
import { getCpmmInitialLiquidityTxn } from 'common/antes'
import { addUserToContractFollowers } from 'shared/follow-market'
import { log } from 'shared/utils'
import { createNewAnswerOnContractNotification } from 'shared/create-notification'
import { removeUndefinedProps } from 'common/util/object'
import { addHouseSubsidyToAnswer } from 'shared/helpers/add-house-subsidy'
import { runTxn } from 'shared/txn/run-txn'

export const createAnswerCPMM: APIHandler<'market/:contractId/answer'> = async (
props,
Expand Down Expand Up @@ -167,18 +169,15 @@ export const createAnswerCpmmMain = async (
transaction.update(contractDoc, {
totalLiquidity: FieldValue.increment(ANSWER_COST),
})
const liquidityDoc = firestore
.collection(`contracts/${contract.id}/liquidity`)
.doc()
const lp = getCpmmInitialLiquidity(

const lp = getCpmmInitialLiquidityTxn(
user.id,
contract,
liquidityDoc.id,
ANSWER_COST,
createdTime,
newAnswer.id
)
transaction.create(liquidityDoc, lp)

await runTxn(transaction, lp)
}

return { newAnswerId: newAnswer.id, contract, user }
Expand Down
16 changes: 6 additions & 10 deletions backend/api/src/create-market.ts
@@ -1,6 +1,6 @@
import * as admin from 'firebase-admin'
import { FieldValue, Transaction } from 'firebase-admin/firestore'
import { getCpmmInitialLiquidity } from 'common/antes'
import { getCpmmInitialLiquidityTxn } from 'common/antes'
import {
add_answers_mode,
Contract,
Expand Down Expand Up @@ -593,18 +593,14 @@ async function generateAntes(
outcomeType === 'MULTIPLE_CHOICE' ||
outcomeType === 'NUMBER'
) {
const liquidityDoc = firestore
.collection(`contracts/${contract.id}/liquidity`)
.doc()

const lp = getCpmmInitialLiquidity(
const lp = getCpmmInitialLiquidityTxn(
providerId,
contract as CPMMBinaryContract | CPMMMultiContract,
liquidityDoc.id,
ante,
contract.createdTime
ante
)

await liquidityDoc.set(lp)
await firestore.runTransaction(async (transaction) => {
runTxn(transaction, lp)
})
}
}
63 changes: 24 additions & 39 deletions backend/functions/src/triggers/on-create-liquidity-provision.ts
@@ -1,45 +1,30 @@
import * as functions from 'firebase-functions'
import { getContract, getUser, log } from 'shared/utils'
import { getContract, getUser } from 'shared/utils'
import { createFollowOrMarketSubsidizedNotification } from 'shared/create-notification'
import { LiquidityProvision } from 'common/liquidity-provision'
import { addUserToContractFollowers } from 'shared/follow-market'
import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
HOUSE_LIQUIDITY_PROVIDER_ID,
} from 'common/antes'
import { secrets } from 'common/secrets'
import { AddSubsidyTxn } from 'common/txn'

export const onCreateLiquidityProvision = functions
.runWith({ secrets })
.firestore.document('contracts/{contractId}/liquidity/{liquidityId}')
.onCreate(async (change, context) => {
const liquidity = change.data() as LiquidityProvision
const { eventId } = context
// TODO: add this as continuation of add liquidity instances

// Ignore Manifold Markets liquidity for now - users see a notification for free market liquidity provision
if (
liquidity.isAnte ||
liquidity.userId === HOUSE_LIQUIDITY_PROVIDER_ID ||
liquidity.userId === DEV_HOUSE_LIQUIDITY_PROVIDER_ID
)
return
export const onCreateLiquidityProvision = async (txn: AddSubsidyTxn) => {
// Ignore Manifold Markets liquidity for now - users see a notification for free market liquidity provision
if (txn.fromType === 'BANK' || txn.data.isAnte) {
return
}

log(`onCreateLiquidityProvision: ${JSON.stringify(liquidity)}`)
const contract = await getContract(txn.toId)
if (!contract)
throw new Error('Could not find contract corresponding with liquidity')

const contract = await getContract(liquidity.contractId)
if (!contract)
throw new Error('Could not find contract corresponding with liquidity')

const liquidityProvider = await getUser(liquidity.userId)
if (!liquidityProvider) throw new Error('Could not find liquidity provider')
await addUserToContractFollowers(contract.id, liquidityProvider.id)
await createFollowOrMarketSubsidizedNotification(
contract.id,
'liquidity',
'created',
liquidityProvider,
eventId,
liquidity.amount.toString(),
{ contract }
)
})
const liquidityProvider = await getUser(txn.fromId)
if (!liquidityProvider) throw new Error('Could not find liquidity provider')
await addUserToContractFollowers(contract.id, liquidityProvider.id)
await createFollowOrMarketSubsidizedNotification(
contract.id,
'liquidity',
'created',
liquidityProvider,
txn.id,
txn.amount.toString(),
{ contract }
)
}
72 changes: 33 additions & 39 deletions backend/shared/src/helpers/add-house-subsidy.ts
@@ -1,44 +1,42 @@
import * as admin from 'firebase-admin'
import { FieldValue } from 'firebase-admin/firestore'

import { CPMMContract, CPMMMultiContract } from 'common/contract'
import { isProd } from 'shared/utils'
import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
HOUSE_LIQUIDITY_PROVIDER_ID,
} from 'common/antes'
import { getNewLiquidityProvision } from 'common/add-liquidity'
import { APIError } from 'common/api/utils'
import { runTxnFromBank } from 'shared/txn/run-txn'

const firestore = admin.firestore()

export const addHouseSubsidy = (contractId: string, amount: number) => {
return firestore.runTransaction(async (transaction) => {
const newLiquidityProvisionDoc = firestore
.collection(`contracts/${contractId}/liquidity`)
.doc()

const providerId = isProd()
? HOUSE_LIQUIDITY_PROVIDER_ID
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID

const contractDoc = firestore.doc(`contracts/${contractId}`)
const snap = await transaction.get(contractDoc)
const contract = snap.data() as CPMMContract | CPMMMultiContract

const { newLiquidityProvision, newTotalLiquidity, newSubsidyPool } =
getNewLiquidityProvision(
providerId,
amount,
contract,
newLiquidityProvisionDoc.id
)
const { newTotalLiquidity, newSubsidyPool } = getNewLiquidityProvision(
amount,
contract
)

const { status, message, txn } = await runTxnFromBank(transaction, {
fromType: 'BANK',
amount,
toId: contractId,
toType: 'CONTRACT',
category: 'ADD_SUBSIDY',
token: 'M$',
})

if (status === 'error') {
throw new APIError(500, message ?? 'Unknown error')
}

transaction.update(contractDoc, {
subsidyPool: newSubsidyPool,
totalLiquidity: newTotalLiquidity,
} as Partial<CPMMContract>)

transaction.create(newLiquidityProvisionDoc, newLiquidityProvision)
return txn
})
}

Expand All @@ -48,25 +46,21 @@ export const addHouseSubsidyToAnswer = (
amount: number
) => {
return firestore.runTransaction(async (transaction) => {
const newLiquidityProvisionDoc = firestore
.collection(`contracts/${contractId}/liquidity`)
.doc()

const providerId = isProd()
? HOUSE_LIQUIDITY_PROVIDER_ID
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID

const contractDoc = firestore.doc(`contracts/${contractId}`)
const snap = await transaction.get(contractDoc)
const contract = snap.data() as CPMMContract | CPMMMultiContract

const { newLiquidityProvision } = getNewLiquidityProvision(
providerId,
const { status, message, txn } = await runTxnFromBank(transaction, {
fromType: 'BANK',
amount,
contract,
newLiquidityProvisionDoc.id,
answerId
)
toId: contractId,
toType: 'CONTRACT',
category: 'ADD_SUBSIDY',
token: 'M$',
data: { answerId },
})

if (status === 'error') {
throw new APIError(500, message ?? 'Unknown error')
}

transaction.update(contractDoc, {
totalLiquidity: FieldValue.increment(amount),
Expand All @@ -80,6 +74,6 @@ export const addHouseSubsidyToAnswer = (
subsidyPool: FieldValue.increment(amount),
})

transaction.create(newLiquidityProvisionDoc, newLiquidityProvision)
return txn
})
}
26 changes: 19 additions & 7 deletions backend/shared/src/resolve-market-helpers.ts
Expand Up @@ -35,6 +35,7 @@ import { recordContractEdit } from 'shared/record-contract-edit'
import { createSupabaseDirectClient } from './supabase/init'
import { Answer } from 'common/answer'
import { acquireLock, releaseLock } from './firestore-lock'
import { convertTxn } from 'common/supabase/txns'

export type ResolutionParams = {
outcome: string
Expand Down Expand Up @@ -255,14 +256,26 @@ export const getDataAndPayoutInfo = async (
answerId: string | undefined
) => {
const { id: contractId, creatorId, outcomeType } = unresolvedContract
const liquiditiesSnap = await firestore
.collection(`contracts/${contractId}/liquidity`)
.get()

const liquidityDocs = liquiditiesSnap.docs.map(
(doc) => doc.data() as LiquidityProvision
const pg = createSupabaseDirectClient()

const liquidityTxns = await pg.map(
`select * from txns
where category = 'ADD_SUBSIDY'
and to_id = $1`,
[contractId],
convertTxn
)

const liquidityDocs: LiquidityProvision[] = liquidityTxns.map((txn) => ({
id: txn.id,
userId: txn.fromId,
contractId,
createdTime: txn.createdTime,
isAnte: txn.data?.isAnte,
answerId: txn.data?.answerId,
amount: txn.amount,
}))

const liquidities =
unresolvedContract.mechanism === 'cpmm-multi-1' &&
outcomeType !== 'NUMBER' &&
Expand All @@ -278,7 +291,6 @@ export const getDataAndPayoutInfo = async (
) {
// Load bets from supabase as an optimization.
// This type of multi choice generates a lot of extra bets that have shares = 0.
const pg = createSupabaseDirectClient()
bets = await pg.map(
`select * from contract_bets
where contract_id = $1
Expand Down

0 comments on commit f06def8

Please sign in to comment.