diff --git a/common/bet.ts b/common/bet.ts index a7795488..3ef71ed6 100644 --- a/common/bet.ts +++ b/common/bet.ts @@ -1,3 +1,5 @@ +import { Fees } from './fees' + export type Bet = { id: string userId: string @@ -6,7 +8,7 @@ export type Bet = { amount: number // bet size; negative if SELL bet loanAmount?: number outcome: string - shares: number // dynamic parimutuel pool weight; negative if SELL bet + shares: number // dynamic parimutuel pool weight or fixed ; negative if SELL bet probBefore: number probAfter: number @@ -17,6 +19,8 @@ export type Bet = { // TODO: add sale time? } + fees: Fees + isSold?: boolean // true if this BUY bet has been sold isAnte?: boolean isLiquidityProvision?: boolean diff --git a/common/calculate-cpmm.ts b/common/calculate-cpmm.ts index 39433e08..74218a42 100644 --- a/common/calculate-cpmm.ts +++ b/common/calculate-cpmm.ts @@ -1,8 +1,8 @@ import * as _ from 'lodash' + import { Bet } from './bet' -import { deductFixedFees } from './calculate-fixed-payouts' import { Binary, CPMM, FullContract } from './contract' -import { CREATOR_FEE } from './fees' +import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees' export function getCpmmProbability(pool: { [outcome: string]: number }) { // For binary contracts only. @@ -35,8 +35,6 @@ function calculateCpmmShares( return shares } -export const CPMM_LIQUIDITY_FEE = 0.02 - export function getCpmmLiquidityFee( contract: FullContract, bet: number, @@ -44,9 +42,16 @@ export function getCpmmLiquidityFee( ) { const p = getCpmmProbability(contract.pool) const betP = outcome === 'YES' ? 1 - p : p - const fee = CPMM_LIQUIDITY_FEE * betP * bet - const remainingBet = bet - fee - return { fee, remainingBet } + + const liquidityFee = LIQUIDITY_FEE * betP * bet + const platformFee = PLATFORM_FEE * betP * bet + const creatorFee = CREATOR_FEE * betP * bet + const fees: Fees = { liquidityFee, platformFee, creatorFee } + + const totalFees = liquidityFee + platformFee + creatorFee + const remainingBet = bet - totalFees + + return { remainingBet, fees } } export function calculateCpmmSharesAfterFee( @@ -66,7 +71,7 @@ export function calculateCpmmPurchase( outcome: string ) { const { pool } = contract - const { remainingBet } = getCpmmLiquidityFee(contract, bet, outcome) + const { remainingBet, fees } = getCpmmLiquidityFee(contract, bet, outcome) const shares = calculateCpmmShares(pool, remainingBet, outcome) const { YES: y, NO: n } = pool @@ -78,7 +83,7 @@ export function calculateCpmmPurchase( const newPool = { YES: newY, NO: newN } - return { shares, newPool } + return { shares, newPool, fees } } export function calculateCpmmShareValue( @@ -103,7 +108,7 @@ export function calculateCpmmSale( const rawSaleValue = calculateCpmmShareValue(contract, shares, outcome) - const { fee, remainingBet: saleValue } = getCpmmLiquidityFee( + const { fees, remainingBet: saleValue } = getCpmmLiquidityFee( contract, rawSaleValue, outcome === 'YES' ? 'NO' : 'YES' @@ -112,6 +117,8 @@ export function calculateCpmmSale( const { pool } = contract const { YES: y, NO: n } = pool + const { liquidityFee: fee } = fees + const [newY, newN] = outcome === 'YES' ? [y + shares - saleValue + fee, n - saleValue + fee] @@ -119,11 +126,7 @@ export function calculateCpmmSale( const newPool = { YES: newY, NO: newN } - const profit = saleValue - bet.amount - const creatorFee = CREATOR_FEE * Math.max(0, profit) - const saleAmount = deductFixedFees(bet.amount, saleValue) - - return { saleValue, newPool, creatorFee, saleAmount } + return { saleValue, newPool, fees } } export function getCpmmProbabilityAfterSale( diff --git a/common/calculate-dpm.ts b/common/calculate-dpm.ts index 76576fc0..76b464e5 100644 --- a/common/calculate-dpm.ts +++ b/common/calculate-dpm.ts @@ -1,7 +1,7 @@ import * as _ from 'lodash' import { Bet } from './bet' import { Binary, DPM, FullContract } from './contract' -import { FEES } from './fees' +import { DPM_FEES } from './fees' export function getDpmProbability(totalShares: { [outcome: string]: number }) { // For binary contracts only. @@ -176,7 +176,7 @@ export function calculateStandardDpmPayout( const winnings = (shares / total) * poolTotal // profit can be negative if using phantom shares - return amount + (1 - FEES) * Math.max(0, winnings - amount) + return amount + (1 - DPM_FEES) * Math.max(0, winnings - amount) } export function calculateDpmPayoutAfterCorrectBet( @@ -268,7 +268,7 @@ export function resolvedDpmPayout(contract: FullContract, bet: Bet) { export const deductDpmFees = (betAmount: number, winnings: number) => { return winnings > betAmount - ? betAmount + (1 - FEES) * (winnings - betAmount) + ? betAmount + (1 - DPM_FEES) * (winnings - betAmount) : winnings } diff --git a/common/calculate-fixed-payouts.ts b/common/calculate-fixed-payouts.ts index 3d2e3a97..9c6ff536 100644 --- a/common/calculate-fixed-payouts.ts +++ b/common/calculate-fixed-payouts.ts @@ -1,7 +1,6 @@ import { Bet } from './bet' import { getProbability } from './calculate' import { Binary, FixedPayouts, FullContract } from './contract' -import { FEES } from './fees' export function calculateFixedPayout( contract: FullContract, @@ -19,9 +18,9 @@ export function calculateFixedCancelPayout(bet: Bet) { } export function calculateStandardFixedPayout(bet: Bet, outcome: string) { - const { amount, outcome: betOutcome, shares } = bet + const { outcome: betOutcome, shares } = bet if (betOutcome !== outcome) return 0 - return deductFixedFees(amount, shares) + return shares } function calculateFixedMktPayout( @@ -34,17 +33,9 @@ function calculateFixedMktPayout( ? resolutionProbability : getProbability(contract) - const { outcome, amount, shares } = bet + const { outcome, shares } = bet const betP = outcome === 'YES' ? p : 1 - p - const winnings = betP * shares - return deductFixedFees(amount, winnings) -} - -export const deductFixedFees = (betAmount: number, winnings: number) => { - return winnings - // return winnings > betAmount - // ? betAmount + (1 - FEES) * (winnings - betAmount) - // : winnings + return betP * shares } diff --git a/common/calculate.ts b/common/calculate.ts index 6d25243e..5f1106b9 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -1,6 +1,6 @@ import { Bet } from './bet' import { - calculateCpmmShareValue, + calculateCpmmSale, getCpmmProbability, getCpmmOutcomeProbabilityAfterBet, getCpmmProbabilityAfterSale, @@ -16,10 +16,7 @@ import { getDpmOutcomeProbabilityAfterBet, getDpmProbabilityAfterSale, } from './calculate-dpm' -import { - calculateFixedPayout, - deductFixedFees, -} from './calculate-fixed-payouts' +import { calculateFixedPayout } from './calculate-fixed-payouts' import { Binary, Contract, CPMM, DPM, FullContract } from './contract' export function getProbability(contract: FullContract) { @@ -72,16 +69,13 @@ export function calculateShares( export function calculateSaleAmount(contract: Contract, bet: Bet) { return contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY' - ? deductFixedFees( - bet.amount, - calculateCpmmShareValue(contract, bet.shares, bet.outcome) - ) + ? calculateCpmmSale(contract, bet) : calculateDpmSaleAmount(contract, bet) } export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) { return contract.mechanism === 'cpmm-1' - ? deductFixedFees(bet.amount, bet.shares) + ? bet.shares : calculateDpmPayoutAfterCorrectBet(contract, bet) } diff --git a/common/contract.ts b/common/contract.ts index 7bdaa14b..954c23fb 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -1,4 +1,5 @@ import { Answer } from './answer' +import { Fees } from './fees' export type FullContract< M extends DPM | CPMM, @@ -30,6 +31,8 @@ export type FullContract< volume24Hours: number volume7Days: number + + collectedFees: Fees } & M & T diff --git a/common/fees.ts b/common/fees.ts index a17a4f06..2f6436ad 100644 --- a/common/fees.ts +++ b/common/fees.ts @@ -1,4 +1,19 @@ -export const PLATFORM_FEE = 0.01 -export const CREATOR_FEE = 0.04 +export const PLATFORM_FEE = 0.005 +export const CREATOR_FEE = 0.02 +export const LIQUIDITY_FEE = 0.02 -export const FEES = PLATFORM_FEE + CREATOR_FEE +export const DPM_PLATFORM_FEE = 2 * PLATFORM_FEE +export const DPM_CREATOR_FEE = 2 * CREATOR_FEE +export const DPM_FEES = DPM_PLATFORM_FEE + DPM_CREATOR_FEE + +export type Fees = { + creatorFee: number + platformFee: number + liquidityFee: number +} + +export const noFees: Fees = { + creatorFee: 0, + platformFee: 0, + liquidityFee: 0, +} diff --git a/common/new-bet.ts b/common/new-bet.ts index b9d362cd..6f3126b5 100644 --- a/common/new-bet.ts +++ b/common/new-bet.ts @@ -16,6 +16,7 @@ import { Multi, } from './contract' import { User } from './user' +import { noFees } from './fees' export const getNewBinaryCpmmBetInfo = ( user: User, @@ -25,7 +26,11 @@ export const getNewBinaryCpmmBetInfo = ( loanAmount: number, newBetId: string ) => { - const { shares, newPool } = calculateCpmmPurchase(contract, amount, outcome) + const { shares, newPool, fees } = calculateCpmmPurchase( + contract, + amount, + outcome + ) const newBalance = user.balance - (amount - loanAmount) @@ -39,13 +44,14 @@ export const getNewBinaryCpmmBetInfo = ( amount, shares, outcome, + fees, loanAmount, probBefore, probAfter, createdTime: Date.now(), } - return { newBet, newPool, newBalance } + return { newBet, newPool, newBalance, fees } } export const getNewBinaryDpmBetInfo = ( @@ -93,6 +99,7 @@ export const getNewBinaryDpmBetInfo = ( probBefore, probAfter, createdTime: Date.now(), + fees: noFees, } const newBalance = user.balance - (amount - loanAmount) @@ -135,6 +142,7 @@ export const getNewMultiBetInfo = ( probBefore, probAfter, createdTime: Date.now(), + fees: noFees, } const newBalance = user.balance - (amount - loanAmount) diff --git a/common/new-contract.ts b/common/new-contract.ts index 16c55a47..2a57a5af 100644 --- a/common/new-contract.ts +++ b/common/new-contract.ts @@ -35,7 +35,7 @@ export function getNewContract( ? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante) : getFreeAnswerProps(ante) - const contract = removeUndefinedProps({ + const contract: Contract = removeUndefinedProps({ id, slug, ...propsByOutcomeType, @@ -57,6 +57,12 @@ export function getNewContract( volume24Hours: 0, volume7Days: 0, + + collectedFees: { + creatorFee: 0, + liquidityFee: 0, + platformFee: 0, + }, }) return contract as Contract diff --git a/common/payouts-dpm.ts b/common/payouts-dpm.ts index adca3408..f740f9a6 100644 --- a/common/payouts-dpm.ts +++ b/common/payouts-dpm.ts @@ -3,7 +3,14 @@ import * as _ from 'lodash' import { Bet } from './bet' import { deductDpmFees, getDpmProbability } from './calculate-dpm' import { DPM, FreeResponse, FullContract, Multi } from './contract' -import { CREATOR_FEE, FEES } from './fees' +import { + DPM_CREATOR_FEE, + DPM_FEES, + DPM_PLATFORM_FEE, + Fees, + noFees, +} from './fees' +import { addObjects } from './util/object' export const getDpmCancelPayouts = ( contract: FullContract, @@ -15,10 +22,12 @@ export const getDpmCancelPayouts = ( const betSum = _.sumBy(bets, (b) => b.amount) - return bets.map((bet) => ({ + const payouts = bets.map((bet) => ({ userId: bet.userId, payout: (bet.amount / betSum) * poolTotal, })) + + return [payouts, contract.collectedFees ?? noFees] } export const getDpmStandardPayouts = ( @@ -36,12 +45,21 @@ export const getDpmStandardPayouts = ( const profit = winnings - amount // profit can be negative if using phantom shares - const payout = amount + (1 - FEES) * Math.max(0, profit) + const payout = amount + (1 - DPM_FEES) * Math.max(0, profit) return { userId, profit, payout } }) const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) - const creatorPayout = CREATOR_FEE * profits + const creatorFee = DPM_CREATOR_FEE * profits + const platformFee = DPM_PLATFORM_FEE * profits + + const finalFees: Fees = { + creatorFee, + platformFee, + liquidityFee: 0, + } + + const fees = addObjects(finalFees, contract.collectedFees ?? {}) console.log( 'resolved', @@ -51,12 +69,14 @@ export const getDpmStandardPayouts = ( 'profits', profits, 'creator fee', - creatorPayout + creatorFee ) - return payouts + const totalPayouts = payouts .map(({ userId, payout }) => ({ userId, payout })) - .concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee + .concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee + + return [totalPayouts, fees] } export const getDpmMktPayouts = ( @@ -84,7 +104,17 @@ export const getDpmMktPayouts = ( }) const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) - const creatorPayout = CREATOR_FEE * profits + + const creatorFee = DPM_CREATOR_FEE * profits + const platformFee = DPM_PLATFORM_FEE * profits + + const finalFees: Fees = { + creatorFee, + platformFee, + liquidityFee: 0, + } + + const fees = addObjects(finalFees, contract.collectedFees ?? {}) console.log( 'resolved MKT', @@ -93,13 +123,14 @@ export const getDpmMktPayouts = ( pool, 'profits', profits, - 'creator fee', - creatorPayout + 'creator fee' ) - return payouts + const totalPayouts = payouts .map(({ userId, payout }) => ({ userId, payout })) - .concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee + .concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee + + return [totalPayouts, fees] } export const getPayoutsMultiOutcome = ( @@ -122,12 +153,22 @@ export const getPayoutsMultiOutcome = ( const winnings = (shares / sharesByOutcome[outcome]) * prob * poolTotal const profit = winnings - amount - const payout = amount + (1 - FEES) * Math.max(0, profit) + const payout = amount + (1 - DPM_FEES) * Math.max(0, profit) return { userId, profit, payout } }) const profits = _.sumBy(payouts, (po) => po.profit) - const creatorPayout = CREATOR_FEE * profits + + const creatorFee = DPM_CREATOR_FEE * profits + const platformFee = DPM_PLATFORM_FEE * profits + + const finalFees: Fees = { + creatorFee, + platformFee, + liquidityFee: 0, + } + + const fees = addObjects(finalFees, contract.collectedFees ?? {}) console.log( 'resolved', @@ -137,10 +178,12 @@ export const getPayoutsMultiOutcome = ( 'profits', profits, 'creator fee', - creatorPayout + creatorFee ) - return payouts + const totalPayouts = payouts .map(({ userId, payout }) => ({ userId, payout })) - .concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee + .concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee + + return [totalPayouts, fees] } diff --git a/common/payouts-fixed.ts b/common/payouts-fixed.ts index ee6eb318..3965c352 100644 --- a/common/payouts-fixed.ts +++ b/common/payouts-fixed.ts @@ -2,13 +2,10 @@ import * as _ from 'lodash' import { Bet } from './bet' import { getProbability } from './calculate' -import { deductFixedFees } from './calculate-fixed-payouts' import { Binary, CPMM, FixedPayouts, FullContract } from './contract' -import { CREATOR_FEE } from './fees' import { LiquidityProvision } from './liquidity-provision' export const getFixedCancelPayouts = ( - contract: FullContract, bets: Bet[], liquidities: LiquidityProvision[] ) => { @@ -34,23 +31,20 @@ export const getStandardFixedPayouts = ( ) => { const winningBets = bets.filter((bet) => bet.outcome === outcome) - const payouts = winningBets.map(({ userId, amount, shares }) => { - const winnings = shares - const profit = winnings - amount - const payout = deductFixedFees(amount, winnings) - return { userId, profit, payout } - }) + const payouts = winningBets.map(({ userId, shares }) => ({ + userId, + payout: shares, + })) - const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) - const creatorPayout = 0 // CREATOR_FEE * profits + const creatorPayout = contract.collectedFees.creatorFee console.log( 'resolved', outcome, 'pool', - contract.pool, - 'profits', - profits, + contract.pool[outcome], + 'payouts', + _.sum(payouts), 'creator fee', creatorPayout ) @@ -88,24 +82,20 @@ export const getMktFixedPayouts = ( ? getProbability(contract) : resolutionProbability - const payouts = bets.map(({ userId, outcome, amount, shares }) => { + const payouts = bets.map(({ userId, outcome, shares }) => { const betP = outcome === 'YES' ? p : 1 - p - const winnings = betP * shares - const profit = winnings - amount - const payout = deductFixedFees(amount, winnings) - return { userId, profit, payout } + return { userId, payout: betP * shares } }) - const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) - const creatorPayout = 0 // CREATOR_FEE * profits + const creatorPayout = contract.collectedFees.creatorFee console.log( - 'resolved MKT', + 'resolved PROB', p, 'pool', - contract.pool, - 'profits', - profits, + p * contract.pool.YES + (1 - p) * contract.pool.NO, + 'payouts', + _.sum(payouts), 'creator fee', creatorPayout ) diff --git a/common/payouts.ts b/common/payouts.ts index 77a88e80..ebc06bfe 100644 --- a/common/payouts.ts +++ b/common/payouts.ts @@ -1,7 +1,16 @@ import * as _ from 'lodash' import { Bet } from './bet' -import { Contract, DPM, FreeResponse, FullContract, Multi } from './contract' +import { + Binary, + Contract, + DPM, + FixedPayouts, + FreeResponse, + FullContract, + Multi, +} from './contract' +import { Fees } from './fees' import { LiquidityProvision } from './liquidity-provision' import { getDpmCancelPayouts, @@ -15,57 +24,12 @@ import { getStandardFixedPayouts, } from './payouts-fixed' -export const getPayouts = ( - outcome: - | string - | { - [outcome: string]: number - }, - contract: Contract, - allBets: Bet[], - liquidities: LiquidityProvision[], - resolutionProbability?: number -) => { - const bets = allBets.filter((b) => !b.isSold && !b.sale) - - if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') { - switch (outcome) { - case 'YES': - case 'NO': - return getStandardFixedPayouts(outcome, contract, bets, liquidities) - case 'MKT': - return getMktFixedPayouts( - contract, - bets, - liquidities, - resolutionProbability - ) - case 'CANCEL': - return getFixedCancelPayouts(contract, allBets, liquidities) - } - } - - switch (outcome) { - case 'YES': - case 'NO': - return getDpmStandardPayouts(outcome, contract, bets) - case 'MKT': - return getDpmMktPayouts(contract, bets, resolutionProbability) - case 'CANCEL': - return getDpmCancelPayouts(contract, bets) - default: - // Multi outcome. - return getPayoutsMultiOutcome( - outcome as { - [outcome: string]: number - }, - contract as FullContract, - bets - ) - } +export type Payout = { + userId: string + payout: number } -export const getLoanPayouts = (bets: Bet[]) => { +export const getLoanPayouts = (bets: Bet[]): Payout[] => { const betsWithLoans = bets.filter((bet) => bet.loanAmount) const betsByUser = _.groupBy(betsWithLoans, (bet) => bet.userId) const loansByUser = _.mapValues(betsByUser, (bets) => @@ -73,3 +37,92 @@ export const getLoanPayouts = (bets: Bet[]) => { ) return _.toPairs(loansByUser).map(([userId, payout]) => ({ userId, payout })) } + +export const getPayouts = ( + outcome: string, + resolutions: { + [outcome: string]: number + }, + contract: Contract, + bets: Bet[], + liquidities: LiquidityProvision[], + resolutionProbability?: number +): [Payout[], Fees] => { + if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') { + const payouts = getFixedPayouts( + outcome, + contract, + bets, + liquidities, + resolutionProbability + ) + return [payouts, contract.collectedFees] + } + + return getDpmPayouts( + outcome, + resolutions, + contract, + bets, + resolutionProbability + ) +} + +export const getFixedPayouts = ( + outcome: string, + contract: FullContract, + bets: Bet[], + liquidities: LiquidityProvision[], + resolutionProbability?: number +): Payout[] => { + switch (outcome) { + case 'YES': + case 'NO': + return getStandardFixedPayouts(outcome, contract, bets, liquidities) + case 'MKT': + return getMktFixedPayouts( + contract, + bets, + liquidities, + resolutionProbability + ) + default: + case 'CANCEL': + return getFixedCancelPayouts(bets, liquidities) + } +} + +export const getDpmPayouts = ( + outcome: string, + resolutions: { + [outcome: string]: number + }, + contract: Contract, + bets: Bet[], + resolutionProbability?: number +) => { + const openBets = bets.filter((b) => !b.isSold && !b.sale) + + switch (outcome) { + case 'YES': + case 'NO': + return getDpmStandardPayouts(outcome, contract, openBets) as [ + Payout[], + Fees + ] + case 'MKT': + return getDpmMktPayouts(contract, openBets, resolutionProbability) as [ + Payout[], + Fees + ] + case 'CANCEL': + return getDpmCancelPayouts(contract, openBets) as [Payout[], Fees] + default: + // Multi outcome. + return getPayoutsMultiOutcome( + resolutions, + contract as FullContract, + openBets + ) as [Payout[], Fees] + } +} diff --git a/common/sell-bet.ts b/common/sell-bet.ts index 58ec8855..8f8a74f1 100644 --- a/common/sell-bet.ts +++ b/common/sell-bet.ts @@ -6,7 +6,7 @@ import { } from './calculate-dpm' import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm' import { Binary, DPM, CPMM, FullContract } from './contract' -import { CREATOR_FEE } from './fees' +import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees' import { User } from './user' export const getSellBetInfo = ( @@ -33,7 +33,15 @@ export const getSellBetInfo = ( const probAfter = getDpmProbability(newTotalShares) const profit = adjShareValue - amount - const creatorFee = CREATOR_FEE * Math.max(0, profit) + + const creatorFee = DPM_CREATOR_FEE * Math.max(0, profit) + const platformFee = DPM_PLATFORM_FEE * Math.max(0, profit) + const fees: Fees = { + creatorFee, + platformFee, + liquidityFee: 0, + } + const saleAmount = deductDpmFees(amount, adjShareValue) console.log( @@ -60,6 +68,7 @@ export const getSellBetInfo = ( amount: saleAmount, betId, }, + fees, } const newBalance = user.balance + saleAmount - (loanAmount ?? 0) @@ -70,7 +79,7 @@ export const getSellBetInfo = ( newTotalShares, newTotalBets, newBalance, - creatorFee, + fees, } } @@ -83,10 +92,7 @@ export const getCpmmSellBetInfo = ( const { pool } = contract const { id: betId, amount, shares, outcome } = bet - const { saleValue, newPool, creatorFee, saleAmount } = calculateCpmmSale( - contract, - bet - ) + const { saleValue, newPool, fees } = calculateCpmmSale(contract, bet) const probBefore = getCpmmProbability(pool) const probAfter = getCpmmProbability(newPool) @@ -96,9 +102,9 @@ export const getCpmmSellBetInfo = ( amount, outcome, 'for M$', - saleAmount, + saleValue, 'creator fee: M$', - creatorFee + fees.creatorFee ) const newBet: Bet = { @@ -112,17 +118,18 @@ export const getCpmmSellBetInfo = ( probAfter, createdTime: Date.now(), sale: { - amount: saleAmount, + amount: saleValue, betId, }, + fees, } - const newBalance = user.balance + saleAmount + const newBalance = user.balance + saleValue return { newBet, newPool, newBalance, - creatorFee, + fees, } } diff --git a/common/util/object.ts b/common/util/object.ts index 4148b057..db504cc3 100644 --- a/common/util/object.ts +++ b/common/util/object.ts @@ -1,3 +1,5 @@ +import * as _ from 'lodash' + export const removeUndefinedProps = (obj: T): T => { let newObj: any = {} @@ -7,3 +9,17 @@ export const removeUndefinedProps = (obj: T): T => { return newObj } + +export const addObjects = ( + obj1: T, + obj2: T +) => { + const keys = _.union(Object.keys(obj1), Object.keys(obj2)) + const newObj = {} as any + + for (let key of keys) { + newObj[key] = (obj1[key] ?? 0) + (obj2[key] ?? 0) + } + + return newObj as T +} diff --git a/functions/src/place-bet.ts b/functions/src/place-bet.ts index 524abed1..8fa21ced 100644 --- a/functions/src/place-bet.ts +++ b/functions/src/place-bet.ts @@ -9,9 +9,10 @@ import { getNewMultiBetInfo, getLoanAmount, } from '../../common/new-bet' -import { removeUndefinedProps } from '../../common/util/object' +import { addObjects, removeUndefinedProps } from '../../common/util/object' import { Bet } from '../../common/bet' import { redeemShares } from './redeem-shares' +import { Fees } from '../../common/fees' export const placeBet = functions.runWith({ minInstances: 1 }).https.onCall( async ( @@ -48,7 +49,7 @@ export const placeBet = functions.runWith({ minInstances: 1 }).https.onCall( return { status: 'error', message: 'Invalid contract' } const contract = contractSnap.data() as Contract - const { closeTime, outcomeType, mechanism } = contract + const { closeTime, outcomeType, mechanism, collectedFees } = contract if (closeTime && Date.now() > closeTime) return { status: 'error', message: 'Trading is closed' } @@ -73,7 +74,14 @@ export const placeBet = functions.runWith({ minInstances: 1 }).https.onCall( .collection(`contracts/${contractId}/bets`) .doc() - const { newBet, newPool, newTotalShares, newTotalBets, newBalance } = + const { + newBet, + newPool, + newTotalShares, + newTotalBets, + newBalance, + fees, + } = outcomeType === 'BINARY' ? mechanism === 'dpm-2' ? getNewBinaryDpmBetInfo( @@ -109,6 +117,7 @@ export const placeBet = functions.runWith({ minInstances: 1 }).https.onCall( pool: newPool, totalShares: newTotalShares, totalBets: newTotalBets, + collectedFees: addObjects(fees ?? {}, collectedFees ?? {}), }) ) diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts index b13aff3e..6fe9e46d 100644 --- a/functions/src/resolve-market.ts +++ b/functions/src/resolve-market.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash' import { Contract } from '../../common/contract' import { User } from '../../common/user' import { Bet } from '../../common/bet' -import { getUser, payUser } from './utils' +import { getUser, isProd, payUser } from './utils' import { sendMarketResolutionEmail } from './emails' import { getLoanPayouts, getPayouts } from '../../common/payouts' import { removeUndefinedProps } from '../../common/util/object' @@ -75,19 +75,6 @@ export const resolveMarket = functions ? Math.min(closeTime, resolutionTime) : closeTime - await contractDoc.update( - removeUndefinedProps({ - isResolved: true, - resolution: outcome, - resolutionTime, - closeTime: newCloseTime, - resolutionProbability, - resolutions, - }) - ) - - console.log('contract ', contractId, 'resolved to:', outcome) - const betsSnap = await firestore .collection(`contracts/${contractId}/bets`) .get() @@ -102,18 +89,33 @@ export const resolveMarket = functions (doc) => doc.data() as LiquidityProvision ) - const payouts = getPayouts( - resolutions ?? outcome, + const [payouts, collectedFees] = getPayouts( + outcome, + resolutions ?? {}, contract, bets, liquidities, resolutionProbability ) + await contractDoc.update( + removeUndefinedProps({ + isResolved: true, + resolution: outcome, + resolutionTime, + closeTime: newCloseTime, + resolutionProbability, + resolutions, + collectedFees, + }) + ) + + console.log('contract ', contractId, 'resolved to:', outcome) + const openBets = bets.filter((b) => !b.isSold && !b.sale) const loanPayouts = getLoanPayouts(openBets) - console.log('payouts:', payouts) + if (!isProd) console.log('payouts:', payouts) const groups = _.groupBy( [...payouts, ...loanPayouts], diff --git a/functions/src/sell-bet.ts b/functions/src/sell-bet.ts index 68cb4cd2..14c15dd7 100644 --- a/functions/src/sell-bet.ts +++ b/functions/src/sell-bet.ts @@ -5,7 +5,8 @@ import { Contract } from '../../common/contract' import { User } from '../../common/user' import { Bet } from '../../common/bet' import { getCpmmSellBetInfo, getSellBetInfo } from '../../common/sell-bet' -import { removeUndefinedProps } from '../../common/util/object' +import { addObjects, removeUndefinedProps } from '../../common/util/object' +import { Fees } from '../../common/fees' export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( async ( @@ -34,7 +35,7 @@ export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( return { status: 'error', message: 'Invalid contract' } const contract = contractSnap.data() as Contract - const { closeTime, mechanism } = contract + const { closeTime, mechanism, collectedFees } = contract if (closeTime && Date.now() > closeTime) return { status: 'error', message: 'Trading is closed' } @@ -55,7 +56,7 @@ export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( newTotalShares, newTotalBets, newBalance, - creatorFee, + fees, } = mechanism === 'dpm-2' ? getSellBetInfo(user, bet, contract, newBetDoc.id) @@ -66,20 +67,7 @@ export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( newBetDoc.id ) as any) - if (contract.creatorId === userId) { - transaction.update(userDoc, { balance: newBalance + creatorFee }) - } else { - const creatorDoc = firestore.doc(`users/${contract.creatorId}`) - const creatorSnap = await transaction.get(creatorDoc) - - if (creatorSnap.exists) { - const creator = creatorSnap.data() as User - const creatorNewBalance = creator.balance + creatorFee - transaction.update(creatorDoc, { balance: creatorNewBalance }) - } - - transaction.update(userDoc, { balance: newBalance }) - } + transaction.update(userDoc, { balance: newBalance }) transaction.update(betDoc, { isSold: true }) transaction.create(newBetDoc, newBet) @@ -89,6 +77,7 @@ export const sellBet = functions.runWith({ minInstances: 1 }).https.onCall( pool: newPool, totalShares: newTotalShares, totalBets: newTotalBets, + collectedFees: addObjects(fees ?? {}, collectedFees ?? {}), }) )