Merge branch 'main' into loans2
This commit is contained in:
commit
ef7763eb63
|
@ -14,19 +14,21 @@ export type Bet = {
|
||||||
probBefore: number
|
probBefore: number
|
||||||
probAfter: number
|
probAfter: number
|
||||||
|
|
||||||
sale?: {
|
|
||||||
amount: number // amount user makes from sale
|
|
||||||
betId: string // id of bet being sold
|
|
||||||
// TODO: add sale time?
|
|
||||||
}
|
|
||||||
|
|
||||||
fees: Fees
|
fees: Fees
|
||||||
|
|
||||||
isSold?: boolean // true if this BUY bet has been sold
|
|
||||||
isAnte?: boolean
|
isAnte?: boolean
|
||||||
isLiquidityProvision?: boolean
|
isLiquidityProvision?: boolean
|
||||||
isRedemption?: boolean
|
isRedemption?: boolean
|
||||||
challengeSlug?: string
|
challengeSlug?: string
|
||||||
|
|
||||||
|
// Props for bets in DPM contract below.
|
||||||
|
// A bet is either a BUY or a SELL that sells all of a previous buy.
|
||||||
|
isSold?: boolean // true if this BUY bet has been sold
|
||||||
|
// This field marks a SELL bet.
|
||||||
|
sale?: {
|
||||||
|
amount: number // amount user makes from sale
|
||||||
|
betId: string // id of BUY bet being sold
|
||||||
|
}
|
||||||
} & Partial<LimitProps>
|
} & Partial<LimitProps>
|
||||||
|
|
||||||
export type NumericBet = Bet & {
|
export type NumericBet = Bet & {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { maxBy } from 'lodash'
|
import { maxBy, sortBy, sum, sumBy } from 'lodash'
|
||||||
import { Bet, LimitBet } from './bet'
|
import { Bet, LimitBet } from './bet'
|
||||||
import {
|
import {
|
||||||
calculateCpmmSale,
|
calculateCpmmSale,
|
||||||
|
@ -133,10 +133,46 @@ export function resolvedPayout(contract: Contract, bet: Bet) {
|
||||||
: calculateDpmPayout(contract, bet, outcome)
|
: calculateDpmPayout(contract, bet, outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCpmmInvested(yourBets: Bet[]) {
|
||||||
|
const totalShares: { [outcome: string]: number } = {}
|
||||||
|
const totalSpent: { [outcome: string]: number } = {}
|
||||||
|
|
||||||
|
const sortedBets = sortBy(yourBets, 'createdTime')
|
||||||
|
for (const bet of sortedBets) {
|
||||||
|
const { outcome, shares, amount } = bet
|
||||||
|
if (amount > 0) {
|
||||||
|
totalShares[outcome] = (totalShares[outcome] ?? 0) + shares
|
||||||
|
totalSpent[outcome] = (totalSpent[outcome] ?? 0) + amount
|
||||||
|
} else if (amount < 0) {
|
||||||
|
const averagePrice = totalSpent[outcome] / totalShares[outcome]
|
||||||
|
totalShares[outcome] = totalShares[outcome] + shares
|
||||||
|
totalSpent[outcome] = totalSpent[outcome] + averagePrice * shares
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum(Object.values(totalSpent))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDpmInvested(yourBets: Bet[]) {
|
||||||
|
const sortedBets = sortBy(yourBets, 'createdTime')
|
||||||
|
|
||||||
|
return sumBy(sortedBets, (bet) => {
|
||||||
|
const { amount, sale } = bet
|
||||||
|
|
||||||
|
if (sale) {
|
||||||
|
const originalBet = sortedBets.find((b) => b.id === sale.betId)
|
||||||
|
if (originalBet) return -originalBet.amount
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
||||||
const { resolution } = contract
|
const { resolution } = contract
|
||||||
|
const isCpmm = contract.mechanism === 'cpmm-1'
|
||||||
|
|
||||||
let currentInvested = 0
|
|
||||||
let totalInvested = 0
|
let totalInvested = 0
|
||||||
let payout = 0
|
let payout = 0
|
||||||
let loan = 0
|
let loan = 0
|
||||||
|
@ -162,7 +198,6 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
||||||
saleValue -= amount
|
saleValue -= amount
|
||||||
}
|
}
|
||||||
|
|
||||||
currentInvested += amount
|
|
||||||
loan += loanAmount ?? 0
|
loan += loanAmount ?? 0
|
||||||
payout += resolution
|
payout += resolution
|
||||||
? calculatePayout(contract, bet, resolution)
|
? calculatePayout(contract, bet, resolution)
|
||||||
|
@ -174,12 +209,13 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
||||||
const profit = payout + saleValue + redeemed - totalInvested
|
const profit = payout + saleValue + redeemed - totalInvested
|
||||||
const profitPercent = (profit / totalInvested) * 100
|
const profitPercent = (profit / totalInvested) * 100
|
||||||
|
|
||||||
|
const invested = isCpmm ? getCpmmInvested(yourBets) : getDpmInvested(yourBets)
|
||||||
const hasShares = Object.values(totalShares).some(
|
const hasShares = Object.values(totalShares).some(
|
||||||
(shares) => !floatingEqual(shares, 0)
|
(shares) => !floatingEqual(shares, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
invested: Math.max(0, currentInvested),
|
invested,
|
||||||
payout,
|
payout,
|
||||||
netPayout,
|
netPayout,
|
||||||
profit,
|
profit,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { difference, uniq, mapValues, groupBy, sumBy } from 'lodash'
|
import { difference, mapValues, groupBy, sumBy } from 'lodash'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Contract,
|
Contract,
|
||||||
|
@ -22,6 +22,8 @@ import { isManifoldId } from '../../common/envs/constants'
|
||||||
import { removeUndefinedProps } from '../../common/util/object'
|
import { removeUndefinedProps } from '../../common/util/object'
|
||||||
import { LiquidityProvision } from '../../common/liquidity-provision'
|
import { LiquidityProvision } from '../../common/liquidity-provision'
|
||||||
import { APIError, newEndpoint, validate } from './api'
|
import { APIError, newEndpoint, validate } from './api'
|
||||||
|
import { getContractBetMetrics } from '../../common/calculate'
|
||||||
|
import { floatingEqual } from '../../common/util/math'
|
||||||
|
|
||||||
const bodySchema = z.object({
|
const bodySchema = z.object({
|
||||||
contractId: z.string(),
|
contractId: z.string(),
|
||||||
|
@ -162,7 +164,7 @@ export const resolvemarket = newEndpoint(opts, async (req, auth) => {
|
||||||
const userPayoutsWithoutLoans = groupPayoutsByUser(payouts)
|
const userPayoutsWithoutLoans = groupPayoutsByUser(payouts)
|
||||||
|
|
||||||
await sendResolutionEmails(
|
await sendResolutionEmails(
|
||||||
openBets,
|
bets,
|
||||||
userPayoutsWithoutLoans,
|
userPayoutsWithoutLoans,
|
||||||
creator,
|
creator,
|
||||||
creatorPayout,
|
creatorPayout,
|
||||||
|
@ -188,7 +190,7 @@ const processPayouts = async (payouts: Payout[], isDeposit = false) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendResolutionEmails = async (
|
const sendResolutionEmails = async (
|
||||||
openBets: Bet[],
|
bets: Bet[],
|
||||||
userPayouts: { [userId: string]: number },
|
userPayouts: { [userId: string]: number },
|
||||||
creator: User,
|
creator: User,
|
||||||
creatorPayout: number,
|
creatorPayout: number,
|
||||||
|
@ -197,14 +199,15 @@ const sendResolutionEmails = async (
|
||||||
resolutionProbability?: number,
|
resolutionProbability?: number,
|
||||||
resolutions?: { [outcome: string]: number }
|
resolutions?: { [outcome: string]: number }
|
||||||
) => {
|
) => {
|
||||||
const nonWinners = difference(
|
|
||||||
uniq(openBets.map(({ userId }) => userId)),
|
|
||||||
Object.keys(userPayouts)
|
|
||||||
)
|
|
||||||
const investedByUser = mapValues(
|
const investedByUser = mapValues(
|
||||||
groupBy(openBets, (bet) => bet.userId),
|
groupBy(bets, (bet) => bet.userId),
|
||||||
(bets) => sumBy(bets, (bet) => bet.amount)
|
(bets) => getContractBetMetrics(contract, bets).invested
|
||||||
)
|
)
|
||||||
|
const investedUsers = Object.keys(investedByUser).filter(
|
||||||
|
(userId) => !floatingEqual(investedByUser[userId], 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
const nonWinners = difference(investedUsers, Object.keys(userPayouts))
|
||||||
const emailPayouts = [
|
const emailPayouts = [
|
||||||
...Object.entries(userPayouts),
|
...Object.entries(userPayouts),
|
||||||
...nonWinners.map((userId) => [userId, 0] as const),
|
...nonWinners.map((userId) => [userId, 0] as const),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Router from 'next/router'
|
import Router from 'next/router'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { MouseEvent } from 'react'
|
import { MouseEvent, useState } from 'react'
|
||||||
import { UserCircleIcon, UserIcon, UsersIcon } from '@heroicons/react/solid'
|
import { UserCircleIcon, UserIcon, UsersIcon } from '@heroicons/react/solid'
|
||||||
|
|
||||||
export function Avatar(props: {
|
export function Avatar(props: {
|
||||||
|
@ -10,7 +10,8 @@ export function Avatar(props: {
|
||||||
size?: number | 'xs' | 'sm'
|
size?: number | 'xs' | 'sm'
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { username, avatarUrl, noLink, size, className } = props
|
const { username, noLink, size, className } = props
|
||||||
|
const [avatarUrl, setAvatarUrl] = useState(props.avatarUrl)
|
||||||
const s = size == 'xs' ? 6 : size === 'sm' ? 8 : size || 10
|
const s = size == 'xs' ? 6 : size === 'sm' ? 8 : size || 10
|
||||||
|
|
||||||
const onClick =
|
const onClick =
|
||||||
|
@ -35,6 +36,11 @@ export function Avatar(props: {
|
||||||
src={avatarUrl}
|
src={avatarUrl}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
alt={username}
|
alt={username}
|
||||||
|
onError={() => {
|
||||||
|
// If the image doesn't load, clear the avatarUrl to show the default
|
||||||
|
// Mostly for localhost, when getting a 403 from googleusercontent
|
||||||
|
setAvatarUrl('')
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<UserCircleIcon
|
<UserCircleIcon
|
||||||
|
|
|
@ -428,76 +428,35 @@ export function BetsSummary(props: {
|
||||||
: 'NO'
|
: 'NO'
|
||||||
: 'YES'
|
: 'YES'
|
||||||
|
|
||||||
return (
|
const canSell =
|
||||||
<Row className={clsx('flex-wrap gap-4 sm:flex-nowrap sm:gap-6', className)}>
|
isYourBets &&
|
||||||
{!isCpmm && (
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Invested
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(invested)}</div>
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
{resolution ? (
|
|
||||||
<Col>
|
|
||||||
<div className="text-sm text-gray-500">Payout</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(payout)} <ProfitBadge profitPercent={profitPercent} />
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
) : isBinary ? (
|
|
||||||
<>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if <YesLabel />
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(yesWinnings)}</div>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if <NoLabel />
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(noWinnings)}</div>
|
|
||||||
</Col>
|
|
||||||
</>
|
|
||||||
) : isPseudoNumeric ? (
|
|
||||||
<>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if {'>='} {formatLargeNumber(contract.max)}
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(yesWinnings)}</div>
|
|
||||||
</Col>
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Payout if {'<='} {formatLargeNumber(contract.min)}
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(noWinnings)}</div>
|
|
||||||
</Col>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Current value
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
|
|
||||||
</Col>
|
|
||||||
)}
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">Profit</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
|
|
||||||
{isYourBets &&
|
|
||||||
isCpmm &&
|
isCpmm &&
|
||||||
(isBinary || isPseudoNumeric) &&
|
(isBinary || isPseudoNumeric) &&
|
||||||
!isClosed &&
|
!isClosed &&
|
||||||
!resolution &&
|
!resolution &&
|
||||||
hasShares &&
|
hasShares &&
|
||||||
sharesOutcome &&
|
sharesOutcome &&
|
||||||
user && (
|
user
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col className={clsx(className, 'gap-4')}>
|
||||||
|
<Row className="flex-wrap gap-4 sm:flex-nowrap sm:gap-6">
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Invested
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(invested)}</div>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">Profit</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
{canSell && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm ml-2"
|
className="btn btn-sm self-end"
|
||||||
onClick={() => setShowSellModal(true)}
|
onClick={() => setShowSellModal(true)}
|
||||||
>
|
>
|
||||||
Sell
|
Sell
|
||||||
|
@ -514,9 +473,60 @@ export function BetsSummary(props: {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</Row>
|
||||||
|
<Row className="flex-wrap-none gap-4">
|
||||||
|
{resolution ? (
|
||||||
|
<Col>
|
||||||
|
<div className="text-sm text-gray-500">Payout</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(payout)}{' '}
|
||||||
|
<ProfitBadge profitPercent={profitPercent} />
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
) : isBinary ? (
|
||||||
|
<>
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Payout if <YesLabel />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(yesWinnings)}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Payout if <NoLabel />
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(noWinnings)}</div>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
) : isPseudoNumeric ? (
|
||||||
|
<>
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Payout if {'>='} {formatLargeNumber(contract.max)}
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{formatMoney(yesWinnings)}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Payout if {'<='} {formatLargeNumber(contract.min)}
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(noWinnings)}</div>
|
||||||
|
</Col>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Col>
|
||||||
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
Current value
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
|
||||||
|
</Col>
|
||||||
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ export function ContractSearch(props: {
|
||||||
highlightOptions?: ContractHighlightOptions
|
highlightOptions?: ContractHighlightOptions
|
||||||
onContractClick?: (contract: Contract) => void
|
onContractClick?: (contract: Contract) => void
|
||||||
hideOrderSelector?: boolean
|
hideOrderSelector?: boolean
|
||||||
overrideGridClassName?: string
|
gridClassName?: string
|
||||||
cardHideOptions?: {
|
cardHideOptions?: {
|
||||||
hideGroupLink?: boolean
|
hideGroupLink?: boolean
|
||||||
hideQuickBet?: boolean
|
hideQuickBet?: boolean
|
||||||
|
@ -98,7 +98,7 @@ export function ContractSearch(props: {
|
||||||
defaultFilter,
|
defaultFilter,
|
||||||
additionalFilter,
|
additionalFilter,
|
||||||
onContractClick,
|
onContractClick,
|
||||||
overrideGridClassName,
|
gridClassName,
|
||||||
hideOrderSelector,
|
hideOrderSelector,
|
||||||
cardHideOptions,
|
cardHideOptions,
|
||||||
highlightOptions,
|
highlightOptions,
|
||||||
|
@ -181,7 +181,7 @@ export function ContractSearch(props: {
|
||||||
loadMore={performQuery}
|
loadMore={performQuery}
|
||||||
showTime={showTime}
|
showTime={showTime}
|
||||||
onContractClick={onContractClick}
|
onContractClick={onContractClick}
|
||||||
overrideGridClassName={overrideGridClassName}
|
gridClassName={gridClassName}
|
||||||
highlightOptions={highlightOptions}
|
highlightOptions={highlightOptions}
|
||||||
cardHideOptions={cardHideOptions}
|
cardHideOptions={cardHideOptions}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function ContractsGrid(props: {
|
||||||
loadMore?: () => void
|
loadMore?: () => void
|
||||||
showTime?: ShowTime
|
showTime?: ShowTime
|
||||||
onContractClick?: (contract: Contract) => void
|
onContractClick?: (contract: Contract) => void
|
||||||
overrideGridClassName?: string
|
gridClassName?: string
|
||||||
cardHideOptions?: {
|
cardHideOptions?: {
|
||||||
hideQuickBet?: boolean
|
hideQuickBet?: boolean
|
||||||
hideGroupLink?: boolean
|
hideGroupLink?: boolean
|
||||||
|
@ -32,7 +32,7 @@ export function ContractsGrid(props: {
|
||||||
showTime,
|
showTime,
|
||||||
loadMore,
|
loadMore,
|
||||||
onContractClick,
|
onContractClick,
|
||||||
overrideGridClassName,
|
gridClassName,
|
||||||
cardHideOptions,
|
cardHideOptions,
|
||||||
highlightOptions,
|
highlightOptions,
|
||||||
} = props
|
} = props
|
||||||
|
@ -66,9 +66,8 @@ export function ContractsGrid(props: {
|
||||||
<Col className="gap-8">
|
<Col className="gap-8">
|
||||||
<ul
|
<ul
|
||||||
className={clsx(
|
className={clsx(
|
||||||
overrideGridClassName
|
'w-full columns-1 gap-4 space-y-4 md:columns-2',
|
||||||
? overrideGridClassName
|
gridClassName
|
||||||
: 'grid w-full grid-cols-1 gap-4 md:grid-cols-2'
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{contracts.map((contract) => (
|
{contracts.map((contract) => (
|
||||||
|
@ -81,11 +80,10 @@ export function ContractsGrid(props: {
|
||||||
}
|
}
|
||||||
hideQuickBet={hideQuickBet}
|
hideQuickBet={hideQuickBet}
|
||||||
hideGroupLink={hideGroupLink}
|
hideGroupLink={hideGroupLink}
|
||||||
className={
|
className={clsx(
|
||||||
contractIds?.includes(contract.id)
|
'break-inside-avoid-column',
|
||||||
? highlightClassName
|
contractIds?.includes(contract.id) && highlightClassName
|
||||||
: undefined
|
)}
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -65,9 +65,7 @@ export function MarketModal(props: {
|
||||||
<ContractSearch
|
<ContractSearch
|
||||||
hideOrderSelector
|
hideOrderSelector
|
||||||
onContractClick={addContract}
|
onContractClick={addContract}
|
||||||
overrideGridClassName={
|
gridClassName="gap-3 space-y-3"
|
||||||
'flex grid grid-cols-1 sm:grid-cols-2 flex-col gap-3 p-1'
|
|
||||||
}
|
|
||||||
cardHideOptions={{ hideGroupLink: true, hideQuickBet: true }}
|
cardHideOptions={{ hideGroupLink: true, hideQuickBet: true }}
|
||||||
highlightOptions={{
|
highlightOptions={{
|
||||||
contractIds: contracts.map((c) => c.id),
|
contractIds: contracts.map((c) => c.id),
|
||||||
|
|
|
@ -266,12 +266,16 @@ export function listenForHotContracts(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getHotContracts() {
|
const trendingContractsQuery = query(
|
||||||
const data = await getValues<Contract>(hotContractsQuery)
|
contracts,
|
||||||
return sortBy(
|
where('isResolved', '==', false),
|
||||||
chooseRandomSubset(data, 10),
|
where('visibility', '==', 'public'),
|
||||||
(contract) => -1 * contract.volume24Hours
|
orderBy('popularityScore', 'desc'),
|
||||||
)
|
limit(10)
|
||||||
|
)
|
||||||
|
|
||||||
|
export async function getTrendingContracts() {
|
||||||
|
return await getValues<Contract>(trendingContractsQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getContractsBySlugs(slugs: string[]) {
|
export async function getContractsBySlugs(slugs: string[]) {
|
||||||
|
|
|
@ -607,9 +607,7 @@ function AddContractButton(props: { group: Group; user: User }) {
|
||||||
user={user}
|
user={user}
|
||||||
hideOrderSelector={true}
|
hideOrderSelector={true}
|
||||||
onContractClick={addContractToCurrentGroup}
|
onContractClick={addContractToCurrentGroup}
|
||||||
overrideGridClassName={
|
gridClassName="gap-3 space-y-3"
|
||||||
'flex grid grid-cols-1 sm:grid-cols-2 flex-col gap-3 p-1'
|
|
||||||
}
|
|
||||||
cardHideOptions={{ hideGroupLink: true, hideQuickBet: true }}
|
cardHideOptions={{ hideGroupLink: true, hideQuickBet: true }}
|
||||||
additionalFilter={{ excludeContractIds: group.contractIds }}
|
additionalFilter={{ excludeContractIds: group.contractIds }}
|
||||||
highlightOptions={{
|
highlightOptions={{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Contract, getContractsBySlugs } from 'web/lib/firebase/contracts'
|
import { Contract, getTrendingContracts } from 'web/lib/firebase/contracts'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
import { LandingPagePanel } from 'web/components/landing-page-panel'
|
import { LandingPagePanel } from 'web/components/landing-page-panel'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
|
@ -8,19 +8,7 @@ import { useSaveReferral } from 'web/hooks/use-save-referral'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
|
|
||||||
export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => {
|
export const getServerSideProps = redirectIfLoggedIn('/home', async (_) => {
|
||||||
// These hardcoded markets will be shown in the frontpage for signed-out users:
|
const hotContracts = await getTrendingContracts()
|
||||||
const hotContracts = await getContractsBySlugs([
|
|
||||||
'will-max-go-to-prom-with-a-girl',
|
|
||||||
'will-ethereum-switch-to-proof-of-st',
|
|
||||||
'will-russia-control-the-majority-of',
|
|
||||||
'will-elon-musk-buy-twitter-this-yea',
|
|
||||||
'will-trump-be-charged-by-the-grand',
|
|
||||||
'will-spacex-launch-a-starship-into',
|
|
||||||
'who-will-win-the-nba-finals-champio',
|
|
||||||
'who-will-be-time-magazine-person-of',
|
|
||||||
'will-congress-hold-any-hearings-abo-e21f987033b3',
|
|
||||||
'will-at-least-10-world-cities-have',
|
|
||||||
])
|
|
||||||
return { props: { hotContracts } }
|
return { props: { hotContracts } }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user