Separate out fees (#159)
* deduct market ante from profits * display creator fees in stats * show creator earnings in stats * separate out creator, liquidity fees in payouts and deduct from profits
This commit is contained in:
parent
a5b0372a6e
commit
5135135e79
|
@ -27,7 +27,12 @@ export const getDpmCancelPayouts = (
|
|||
payout: (bet.amount / betSum) * poolTotal,
|
||||
}))
|
||||
|
||||
return [payouts, contract.collectedFees ?? noFees]
|
||||
return {
|
||||
payouts,
|
||||
creatorPayout: 0,
|
||||
liquidityPayouts: [],
|
||||
collectedFees: contract.collectedFees ?? noFees,
|
||||
}
|
||||
}
|
||||
|
||||
export const getDpmStandardPayouts = (
|
||||
|
@ -59,7 +64,10 @@ export const getDpmStandardPayouts = (
|
|||
liquidityFee: 0,
|
||||
}
|
||||
|
||||
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? {})
|
||||
const collectedFees = addObjects<Fees>(
|
||||
finalFees,
|
||||
contract.collectedFees ?? {}
|
||||
)
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
|
@ -72,11 +80,12 @@ export const getDpmStandardPayouts = (
|
|||
creatorFee
|
||||
)
|
||||
|
||||
const totalPayouts = payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee
|
||||
|
||||
return [totalPayouts, fees]
|
||||
return {
|
||||
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
|
||||
creatorPayout: creatorFee,
|
||||
liquidityPayouts: [],
|
||||
collectedFees,
|
||||
}
|
||||
}
|
||||
|
||||
export const getDpmMktPayouts = (
|
||||
|
@ -114,7 +123,10 @@ export const getDpmMktPayouts = (
|
|||
liquidityFee: 0,
|
||||
}
|
||||
|
||||
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? {})
|
||||
const collectedFees = addObjects<Fees>(
|
||||
finalFees,
|
||||
contract.collectedFees ?? {}
|
||||
)
|
||||
|
||||
console.log(
|
||||
'resolved MKT',
|
||||
|
@ -127,11 +139,12 @@ export const getDpmMktPayouts = (
|
|||
creatorFee
|
||||
)
|
||||
|
||||
const totalPayouts = payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee
|
||||
|
||||
return [totalPayouts, fees]
|
||||
return {
|
||||
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
|
||||
creatorPayout: creatorFee,
|
||||
liquidityPayouts: [],
|
||||
collectedFees,
|
||||
}
|
||||
}
|
||||
|
||||
export const getPayoutsMultiOutcome = (
|
||||
|
@ -169,7 +182,10 @@ export const getPayoutsMultiOutcome = (
|
|||
liquidityFee: 0,
|
||||
}
|
||||
|
||||
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? noFees)
|
||||
const collectedFees = addObjects<Fees>(
|
||||
finalFees,
|
||||
contract.collectedFees ?? noFees
|
||||
)
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
|
@ -181,10 +197,10 @@ export const getPayoutsMultiOutcome = (
|
|||
'creator fee',
|
||||
creatorFee
|
||||
)
|
||||
|
||||
const totalPayouts = payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee
|
||||
|
||||
return [totalPayouts, fees]
|
||||
return {
|
||||
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
|
||||
creatorPayout: creatorFee,
|
||||
liquidityPayouts: [],
|
||||
collectedFees,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Bet } from './bet'
|
|||
import { getProbability } from './calculate'
|
||||
import { getCpmmLiquidityPoolWeights } from './calculate-cpmm'
|
||||
import { Binary, CPMM, FixedPayouts, FullContract } from './contract'
|
||||
import { noFees } from './fees'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
|
||||
export const getFixedCancelPayouts = (
|
||||
|
@ -15,13 +16,16 @@ export const getFixedCancelPayouts = (
|
|||
payout: lp.amount,
|
||||
}))
|
||||
|
||||
return bets
|
||||
const payouts = bets
|
||||
.filter((b) => !b.isAnte && !b.isLiquidityProvision)
|
||||
.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: bet.amount,
|
||||
}))
|
||||
.concat(liquidityPayouts)
|
||||
|
||||
const creatorPayout = 0
|
||||
|
||||
return { payouts, creatorPayout, liquidityPayouts, collectedFees: noFees }
|
||||
}
|
||||
|
||||
export const getStandardFixedPayouts = (
|
||||
|
@ -37,7 +41,8 @@ export const getStandardFixedPayouts = (
|
|||
payout: shares,
|
||||
}))
|
||||
|
||||
const creatorPayout = contract.collectedFees.creatorFee
|
||||
const { collectedFees } = contract
|
||||
const creatorPayout = collectedFees.creatorFee
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
|
@ -50,10 +55,13 @@ export const getStandardFixedPayouts = (
|
|||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolPayouts(contract, outcome, liquidities))
|
||||
const liquidityPayouts = getLiquidityPoolPayouts(
|
||||
contract,
|
||||
outcome,
|
||||
liquidities
|
||||
)
|
||||
|
||||
return { payouts, creatorPayout, liquidityPayouts, collectedFees }
|
||||
}
|
||||
|
||||
export const getLiquidityPoolPayouts = (
|
||||
|
@ -88,7 +96,8 @@ export const getMktFixedPayouts = (
|
|||
return { userId, payout: betP * shares }
|
||||
})
|
||||
|
||||
const creatorPayout = contract.collectedFees.creatorFee
|
||||
const { collectedFees } = contract
|
||||
const creatorPayout = collectedFees.creatorFee
|
||||
|
||||
console.log(
|
||||
'resolved PROB',
|
||||
|
@ -101,10 +110,9 @@ export const getMktFixedPayouts = (
|
|||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolProbPayouts(contract, p, liquidities))
|
||||
const liquidityPayouts = getLiquidityPoolProbPayouts(contract, p, liquidities)
|
||||
|
||||
return { payouts, creatorPayout, liquidityPayouts, collectedFees }
|
||||
}
|
||||
|
||||
export const getLiquidityPoolProbPayouts = (
|
||||
|
|
|
@ -38,6 +38,18 @@ export const getLoanPayouts = (bets: Bet[]): Payout[] => {
|
|||
return _.toPairs(loansByUser).map(([userId, payout]) => ({ userId, payout }))
|
||||
}
|
||||
|
||||
export const groupPayoutsByUser = (payouts: Payout[]) => {
|
||||
const groups = _.groupBy(payouts, (payout) => payout.userId)
|
||||
return _.mapValues(groups, (group) => _.sumBy(group, (g) => g.payout))
|
||||
}
|
||||
|
||||
export type PayoutInfo = {
|
||||
payouts: Payout[]
|
||||
creatorPayout: number
|
||||
liquidityPayouts: Payout[]
|
||||
collectedFees: Fees
|
||||
}
|
||||
|
||||
export const getPayouts = (
|
||||
outcome: string,
|
||||
resolutions: {
|
||||
|
@ -47,16 +59,15 @@ export const getPayouts = (
|
|||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
): [Payout[], Fees] => {
|
||||
): PayoutInfo => {
|
||||
if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') {
|
||||
const payouts = getFixedPayouts(
|
||||
return getFixedPayouts(
|
||||
outcome,
|
||||
contract,
|
||||
bets,
|
||||
liquidities,
|
||||
resolutionProbability
|
||||
)
|
||||
return [payouts, contract.collectedFees]
|
||||
}
|
||||
|
||||
return getDpmPayouts(
|
||||
|
@ -74,7 +85,7 @@ export const getFixedPayouts = (
|
|||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
): Payout[] => {
|
||||
) => {
|
||||
switch (outcome) {
|
||||
case 'YES':
|
||||
case 'NO':
|
||||
|
@ -100,36 +111,27 @@ export const getDpmPayouts = (
|
|||
contract: Contract,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
): PayoutInfo => {
|
||||
const openBets = bets.filter((b) => !b.isSold && !b.sale)
|
||||
|
||||
switch (outcome) {
|
||||
case 'YES':
|
||||
case 'NO':
|
||||
return getDpmStandardPayouts(outcome, contract, openBets) as [
|
||||
Payout[],
|
||||
Fees
|
||||
]
|
||||
return getDpmStandardPayouts(outcome, contract, openBets)
|
||||
|
||||
case 'MKT':
|
||||
return contract.outcomeType === 'FREE_RESPONSE'
|
||||
? (getPayoutsMultiOutcome(
|
||||
? getPayoutsMultiOutcome(
|
||||
resolutions,
|
||||
contract as FullContract<DPM, Multi | FreeResponse>,
|
||||
openBets
|
||||
) as [Payout[], Fees])
|
||||
: (getDpmMktPayouts(contract, openBets, resolutionProbability) as [
|
||||
Payout[],
|
||||
Fees
|
||||
])
|
||||
)
|
||||
: getDpmMktPayouts(contract, openBets, resolutionProbability)
|
||||
case 'CANCEL':
|
||||
return getDpmCancelPayouts(contract, openBets) as [Payout[], Fees]
|
||||
return getDpmCancelPayouts(contract, openBets)
|
||||
|
||||
default:
|
||||
// Outcome is a free response answer id.
|
||||
return getDpmStandardPayouts(outcome, contract, openBets) as [
|
||||
Payout[],
|
||||
Fees
|
||||
]
|
||||
return getDpmStandardPayouts(outcome, contract, openBets)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ export function scoreUsersByContract(
|
|||
bets,
|
||||
(bet) => bet.isSold || bet.sale
|
||||
)
|
||||
const [resolvePayouts] = getPayouts(
|
||||
const { payouts: resolvePayouts } = getPayouts(
|
||||
resolution,
|
||||
{},
|
||||
contract,
|
||||
|
|
|
@ -127,7 +127,7 @@ export const createContract = functions
|
|||
manaLimitPerUser ?? 0
|
||||
)
|
||||
|
||||
if (!isFree && ante) await chargeUser(creator.id, ante)
|
||||
if (!isFree && ante) await chargeUser(creator.id, ante, true)
|
||||
|
||||
await contractRef.create(contract)
|
||||
|
||||
|
|
|
@ -7,7 +7,12 @@ import { User } from '../../common/user'
|
|||
import { Bet } from '../../common/bet'
|
||||
import { getUser, isProd, payUser } from './utils'
|
||||
import { sendMarketResolutionEmail } from './emails'
|
||||
import { getLoanPayouts, getPayouts } from '../../common/payouts'
|
||||
import {
|
||||
getLoanPayouts,
|
||||
getPayouts,
|
||||
groupPayoutsByUser,
|
||||
Payout,
|
||||
} from '../../common/payouts'
|
||||
import { removeUndefinedProps } from '../../common/util/object'
|
||||
import { LiquidityProvision } from '../../common/liquidity-provision'
|
||||
|
||||
|
@ -89,14 +94,15 @@ export const resolveMarket = functions
|
|||
(doc) => doc.data() as LiquidityProvision
|
||||
)
|
||||
|
||||
const [payouts, collectedFees] = getPayouts(
|
||||
outcome,
|
||||
resolutions ?? {},
|
||||
contract,
|
||||
bets,
|
||||
liquidities,
|
||||
resolutionProbability
|
||||
)
|
||||
const { payouts, creatorPayout, liquidityPayouts, collectedFees } =
|
||||
getPayouts(
|
||||
outcome,
|
||||
resolutions ?? {},
|
||||
contract,
|
||||
bets,
|
||||
liquidities,
|
||||
resolutionProbability
|
||||
)
|
||||
|
||||
await contractDoc.update(
|
||||
removeUndefinedProps({
|
||||
|
@ -115,28 +121,26 @@ export const resolveMarket = functions
|
|||
const openBets = bets.filter((b) => !b.isSold && !b.sale)
|
||||
const loanPayouts = getLoanPayouts(openBets)
|
||||
|
||||
if (!isProd) console.log('payouts:', payouts)
|
||||
if (!isProd)
|
||||
console.log(
|
||||
'payouts:',
|
||||
payouts,
|
||||
'creator payout:',
|
||||
creatorPayout,
|
||||
'liquidity payout:'
|
||||
)
|
||||
|
||||
const groups = _.groupBy(
|
||||
[...payouts, ...loanPayouts],
|
||||
(payout) => payout.userId
|
||||
)
|
||||
const userPayouts = _.mapValues(groups, (group) =>
|
||||
_.sumBy(group, (g) => g.payout)
|
||||
)
|
||||
if (creatorPayout)
|
||||
await processPayouts(
|
||||
[{ userId: creatorId, payout: creatorPayout }],
|
||||
true
|
||||
)
|
||||
|
||||
const groupsWithoutLoans = _.groupBy(payouts, (payout) => payout.userId)
|
||||
const userPayoutsWithoutLoans = _.mapValues(groupsWithoutLoans, (group) =>
|
||||
_.sumBy(group, (g) => g.payout)
|
||||
)
|
||||
await processPayouts(liquidityPayouts, true)
|
||||
|
||||
const payoutPromises = Object.entries(userPayouts).map(
|
||||
([userId, payout]) => payUser(userId, payout)
|
||||
)
|
||||
const result = await processPayouts([...payouts, ...loanPayouts])
|
||||
|
||||
const result = await Promise.all(payoutPromises)
|
||||
.catch((e) => ({ status: 'error', message: e }))
|
||||
.then(() => ({ status: 'success' }))
|
||||
const userPayoutsWithoutLoans = groupPayoutsByUser(payouts)
|
||||
|
||||
await sendResolutionEmails(
|
||||
openBets,
|
||||
|
@ -152,6 +156,18 @@ export const resolveMarket = functions
|
|||
}
|
||||
)
|
||||
|
||||
const processPayouts = async (payouts: Payout[], isDeposit = false) => {
|
||||
const userPayouts = groupPayoutsByUser(payouts)
|
||||
|
||||
const payoutPromises = Object.entries(userPayouts).map(([userId, payout]) =>
|
||||
payUser(userId, payout, isDeposit)
|
||||
)
|
||||
|
||||
return await Promise.all(payoutPromises)
|
||||
.catch((e) => ({ status: 'error', message: e }))
|
||||
.then(() => ({ status: 'success' }))
|
||||
}
|
||||
|
||||
const sendResolutionEmails = async (
|
||||
openBets: Bet[],
|
||||
userPayouts: { [userId: string]: number },
|
||||
|
|
|
@ -24,7 +24,8 @@ async function checkIfPayOutAgain(contractRef: DocRef, contract: Contract) {
|
|||
|
||||
if (loanedBets.length && contract.resolution) {
|
||||
const { resolution, resolutions, resolutionProbability } = contract as any
|
||||
const [payouts] = getPayouts(
|
||||
|
||||
const { payouts } = getPayouts(
|
||||
resolution,
|
||||
resolutions,
|
||||
contract,
|
||||
|
|
|
@ -79,9 +79,13 @@ export const payUser = (userId: string, payout: number, isDeposit = false) => {
|
|||
return updateUserBalance(userId, payout, isDeposit)
|
||||
}
|
||||
|
||||
export const chargeUser = (userId: string, charge: number) => {
|
||||
export const chargeUser = (
|
||||
userId: string,
|
||||
charge: number,
|
||||
isAnte?: boolean
|
||||
) => {
|
||||
if (!isFinite(charge) || charge <= 0)
|
||||
throw new Error('User charge is not positive: ' + charge)
|
||||
|
||||
return updateUserBalance(userId, -charge)
|
||||
return updateUserBalance(userId, -charge, isAnte)
|
||||
}
|
||||
|
|
|
@ -85,6 +85,11 @@ export function ContractInfoDialog(props: { contract: Contract; bets: Bet[] }) {
|
|||
<td>{formatMoney(contract.volume)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Creator earnings</td>
|
||||
<td>{formatMoney(contract.collectedFees.creatorFee)}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Traders</td>
|
||||
<td>{tradersCount}</td>
|
||||
|
|
Loading…
Reference in New Issue
Block a user