track fees on contract and bets; change fee schedule for cpmm markets; only pay out creator fees at resolution
This commit is contained in:
parent
a0833b4764
commit
6b30ed4513
|
@ -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
|
||||
|
|
|
@ -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<CPMM, Binary>,
|
||||
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(
|
||||
|
|
|
@ -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<DPM, any>, bet: Bet) {
|
|||
|
||||
export const deductDpmFees = (betAmount: number, winnings: number) => {
|
||||
return winnings > betAmount
|
||||
? betAmount + (1 - FEES) * (winnings - betAmount)
|
||||
? betAmount + (1 - DPM_FEES) * (winnings - betAmount)
|
||||
: winnings
|
||||
}
|
||||
|
||||
|
|
|
@ -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<FixedPayouts, Binary>,
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<DPM | CPMM, Binary>) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<DPM, any>,
|
||||
|
@ -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<Fees>(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<Fees>(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<Fees>(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]
|
||||
}
|
||||
|
|
|
@ -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<FixedPayouts, Binary>,
|
||||
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
|
||||
)
|
||||
|
|
|
@ -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<DPM, Multi | FreeResponse>,
|
||||
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<FixedPayouts, Binary>,
|
||||
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<DPM, Multi | FreeResponse>,
|
||||
openBets
|
||||
) as [Payout[], Fees]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import * as _ from 'lodash'
|
||||
|
||||
export const removeUndefinedProps = <T>(obj: T): T => {
|
||||
let newObj: any = {}
|
||||
|
||||
|
@ -7,3 +9,17 @@ export const removeUndefinedProps = <T>(obj: T): T => {
|
|||
|
||||
return newObj
|
||||
}
|
||||
|
||||
export const addObjects = <T extends { [key: string]: number }>(
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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>(fees ?? {}, collectedFees ?? {}),
|
||||
})
|
||||
)
|
||||
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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>(fees ?? {}, collectedFees ?? {}),
|
||||
})
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user