Merge branch 'main' into custom-feed
This commit is contained in:
commit
e5f553fa1a
|
@ -104,7 +104,7 @@ export function calculateShareValue(contract: Contract, bet: Bet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculateSaleAmount(contract: Contract, bet: Bet) {
|
export function calculateSaleAmount(contract: Contract, bet: Bet) {
|
||||||
return (1 - 2 * FEES) * calculateShareValue(contract, bet)
|
return (1 - FEES) * calculateShareValue(contract, bet)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculatePayout(
|
export function calculatePayout(
|
||||||
|
@ -145,7 +145,7 @@ export function calculateStandardPayout(
|
||||||
totalShares[outcome] - phantomShares[outcome] - totalBets[outcome]
|
totalShares[outcome] - phantomShares[outcome] - totalBets[outcome]
|
||||||
const winningsPool = truePool - totalBets[outcome]
|
const winningsPool = truePool - totalBets[outcome]
|
||||||
|
|
||||||
return amount + (1 - 2 * FEES) * ((shares - amount) / total) * winningsPool
|
return amount + (1 - FEES) * ((shares - amount) / total) * winningsPool
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) {
|
export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) {
|
||||||
|
@ -204,7 +204,7 @@ function calculateMktPayout(contract: Contract, bet: Bet) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
betP * bet.amount +
|
betP * bet.amount +
|
||||||
(1 - 2 * FEES) *
|
(1 - FEES) *
|
||||||
((betP * (bet.shares - bet.amount)) / weightedShareTotal) *
|
((betP * (bet.shares - bet.amount)) / weightedShareTotal) *
|
||||||
winningsPool
|
winningsPool
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const PLATFORM_FEE = 0.01 // == 1%
|
export const PLATFORM_FEE = 0.01
|
||||||
export const CREATOR_FEE = 0.01
|
export const CREATOR_FEE = 0.09
|
||||||
|
|
||||||
export const FEES = PLATFORM_FEE + CREATOR_FEE
|
export const FEES = PLATFORM_FEE + CREATOR_FEE
|
||||||
|
|
|
@ -38,12 +38,12 @@ export const getStandardPayouts = (
|
||||||
userId: bet.userId,
|
userId: bet.userId,
|
||||||
payout:
|
payout:
|
||||||
bet.amount +
|
bet.amount +
|
||||||
(1 - 2 * FEES) *
|
(1 - FEES) *
|
||||||
((bet.shares - bet.amount) / shareDifferenceSum) *
|
((bet.shares - bet.amount) / shareDifferenceSum) *
|
||||||
winningsPool,
|
winningsPool,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const creatorPayout = 2 * CREATOR_FEE * winningsPool
|
const creatorPayout = CREATOR_FEE * winningsPool
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'resolved',
|
'resolved',
|
||||||
|
@ -98,7 +98,7 @@ export const getMktPayouts = (
|
||||||
userId: bet.userId,
|
userId: bet.userId,
|
||||||
payout:
|
payout:
|
||||||
p * bet.amount +
|
p * bet.amount +
|
||||||
(1 - 2 * FEES) *
|
(1 - FEES) *
|
||||||
((p * (bet.shares - bet.amount)) / weightedShareTotal) *
|
((p * (bet.shares - bet.amount)) / weightedShareTotal) *
|
||||||
winningsPool,
|
winningsPool,
|
||||||
}))
|
}))
|
||||||
|
@ -107,12 +107,12 @@ export const getMktPayouts = (
|
||||||
userId: bet.userId,
|
userId: bet.userId,
|
||||||
payout:
|
payout:
|
||||||
(1 - p) * bet.amount +
|
(1 - p) * bet.amount +
|
||||||
(1 - 2 * FEES) *
|
(1 - FEES) *
|
||||||
(((1 - p) * (bet.shares - bet.amount)) / weightedShareTotal) *
|
(((1 - p) * (bet.shares - bet.amount)) / weightedShareTotal) *
|
||||||
winningsPool,
|
winningsPool,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const creatorPayout = 2 * CREATOR_FEE * winningsPool
|
const creatorPayout = CREATOR_FEE * winningsPool
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...yesPayouts,
|
...yesPayouts,
|
||||||
|
|
|
@ -36,8 +36,8 @@ export const getSellBetInfo = (
|
||||||
const probBefore = getProbability(contract.totalShares)
|
const probBefore = getProbability(contract.totalShares)
|
||||||
const probAfter = getProbability(newTotalShares)
|
const probAfter = getProbability(newTotalShares)
|
||||||
|
|
||||||
const creatorFee = 2 * CREATOR_FEE * adjShareValue
|
const creatorFee = CREATOR_FEE * adjShareValue
|
||||||
const saleAmount = (1 - 2 * FEES) * adjShareValue
|
const saleAmount = (1 - FEES) * adjShareValue
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'SELL M$',
|
'SELL M$',
|
||||||
|
|
|
@ -130,6 +130,11 @@ function FeedBet(props: { activityItem: any }) {
|
||||||
className="textarea textarea-bordered w-full"
|
className="textarea textarea-bordered w-full"
|
||||||
placeholder="Add a comment..."
|
placeholder="Add a comment..."
|
||||||
rows={3}
|
rows={3}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' && e.ctrlKey) {
|
||||||
|
submitComment()
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
className="btn btn-outline btn-sm mt-1"
|
className="btn btn-outline btn-sm mt-1"
|
||||||
|
@ -195,6 +200,11 @@ export function ContractDescription(props: {
|
||||||
description.length
|
description.length
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' && e.ctrlKey) {
|
||||||
|
saveDescription(e)
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Row className="gap-2">
|
<Row className="gap-2">
|
||||||
<button
|
<button
|
||||||
|
@ -540,7 +550,8 @@ function FeedBetGroup(props: { activityItem: any }) {
|
||||||
|
|
||||||
const [yesBets, noBets] = _.partition(bets, (bet) => bet.outcome === 'YES')
|
const [yesBets, noBets] = _.partition(bets, (bet) => bet.outcome === 'YES')
|
||||||
|
|
||||||
const createdTime = bets[0].createdTime
|
// Use the time of the last bet for the entire group
|
||||||
|
const createdTime = bets[bets.length - 1].createdTime
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -262,7 +262,7 @@ export function SearchableGrid(props: {
|
||||||
onChange={(e) => setSort(e.target.value as Sort)}
|
onChange={(e) => setSort(e.target.value as Sort)}
|
||||||
>
|
>
|
||||||
<option value="most-traded">Most traded</option>
|
<option value="most-traded">Most traded</option>
|
||||||
<option value="24-hour-vol">24 hour volume</option>
|
<option value="24-hour-vol">24h volume</option>
|
||||||
<option value="close-date">Closing soon</option>
|
<option value="close-date">Closing soon</option>
|
||||||
<option value="newest">Newest</option>
|
<option value="newest">Newest</option>
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ export function EditFoldButton(props: { fold: Fold; className?: string }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Spacer h={4} />
|
<Spacer h={4} />
|
||||||
<TagsList tags={tags.map((tag) => `#${tag}`)} noLink noLabel />
|
<TagsList tags={tags} noLink noLabel />
|
||||||
<Spacer h={4} />
|
<Spacer h={4} />
|
||||||
|
|
||||||
<div className="modal-action">
|
<div className="modal-action">
|
||||||
|
|
|
@ -37,10 +37,7 @@ export function FollowFoldButton(props: { fold: Fold; className?: string }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button className={clsx('btn btn-sm', className)} onClick={onFollow}>
|
||||||
className={clsx('btn btn-secondary bg-indigo-500 btn-sm', className)}
|
|
||||||
onClick={onFollow}
|
|
||||||
>
|
|
||||||
Follow
|
Follow
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { ConfirmationButton as ConfirmationButton } from './confirmation-button'
|
||||||
import { resolveMarket } from '../lib/firebase/api-call'
|
import { resolveMarket } from '../lib/firebase/api-call'
|
||||||
import { ProbabilitySelector } from './probability-selector'
|
import { ProbabilitySelector } from './probability-selector'
|
||||||
import { getProbability } from '../../common/calculate'
|
import { getProbability } from '../../common/calculate'
|
||||||
|
import { CREATOR_FEE } from '../../common/fees'
|
||||||
|
|
||||||
export function ResolutionPanel(props: {
|
export function ResolutionPanel(props: {
|
||||||
creator: User
|
creator: User
|
||||||
|
@ -79,16 +80,20 @@ export function ResolutionPanel(props: {
|
||||||
<div>
|
<div>
|
||||||
{outcome === 'YES' ? (
|
{outcome === 'YES' ? (
|
||||||
<>
|
<>
|
||||||
Winnings will be paid out to YES bettors. You earn 1% of the pool.
|
Winnings will be paid out to YES bettors. You earn{' '}
|
||||||
|
{CREATOR_FEE * 100}%.
|
||||||
</>
|
</>
|
||||||
) : outcome === 'NO' ? (
|
) : outcome === 'NO' ? (
|
||||||
<>Winnings will be paid out to NO bettors. You earn 1% of the pool.</>
|
<>
|
||||||
|
Winnings will be paid out to NO bettors. You earn{' '}
|
||||||
|
{CREATOR_FEE * 100}%.
|
||||||
|
</>
|
||||||
) : outcome === 'CANCEL' ? (
|
) : outcome === 'CANCEL' ? (
|
||||||
<>The pool will be returned to traders with no fees.</>
|
<>The pool will be returned to traders with no fees.</>
|
||||||
) : outcome === 'MKT' ? (
|
) : outcome === 'MKT' ? (
|
||||||
<>
|
<>
|
||||||
Traders will be paid out at the probability you specify. You earn 1%
|
Traders will be paid out at the probability you specify. You earn{' '}
|
||||||
of the pool.
|
{CREATOR_FEE * 100}%.
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>Resolving this market will immediately pay out traders.</>
|
<>Resolving this market will immediately pay out traders.</>
|
||||||
|
|
|
@ -27,7 +27,7 @@ export function TagsInput(props: { contract: Contract; className?: string }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className={clsx('gap-4', className)}>
|
<Col className={clsx('gap-4', className)}>
|
||||||
<TagsList tags={newTags.map((tag) => `#${tag}`)} />
|
<TagsList tags={newTags} />
|
||||||
|
|
||||||
<Row className="items-center gap-4">
|
<Row className="items-center gap-4">
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Row } from './layout/row'
|
||||||
import { SiteLink } from './site-link'
|
import { SiteLink } from './site-link'
|
||||||
import { Fold } from '../../common/fold'
|
import { Fold } from '../../common/fold'
|
||||||
|
|
||||||
export function Hashtag(props: { tag: string; noLink?: boolean }) {
|
function Hashtag(props: { tag: string; noLink?: boolean }) {
|
||||||
const { tag, noLink } = props
|
const { tag, noLink } = props
|
||||||
const body = (
|
const body = (
|
||||||
<div
|
<div
|
||||||
|
@ -35,7 +35,11 @@ export function TagsList(props: {
|
||||||
<Row className={clsx('items-center flex-wrap gap-2', className)}>
|
<Row className={clsx('items-center flex-wrap gap-2', className)}>
|
||||||
{!noLabel && <div className="text-gray-500 mr-1">Tags</div>}
|
{!noLabel && <div className="text-gray-500 mr-1">Tags</div>}
|
||||||
{tags.map((tag) => (
|
{tags.map((tag) => (
|
||||||
<Hashtag key={tag} tag={tag} noLink={noLink} />
|
<Hashtag
|
||||||
|
key={tag}
|
||||||
|
tag={tag.startsWith('#') ? tag : `#${tag}`}
|
||||||
|
noLink={noLink}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { cloneElement } from 'react'
|
import { cloneElement } from 'react'
|
||||||
|
import { CREATOR_FEE } from '../../common/fees'
|
||||||
import { Page } from '../components/page'
|
import { Page } from '../components/page'
|
||||||
import { SEO } from '../components/SEO'
|
import { SEO } from '../components/SEO'
|
||||||
import { useContracts } from '../hooks/use-contracts'
|
|
||||||
import styles from './about.module.css'
|
import styles from './about.module.css'
|
||||||
|
|
||||||
export default function About() {
|
export default function About() {
|
||||||
|
@ -132,8 +132,9 @@ function Contents() {
|
||||||
</p>
|
</p>
|
||||||
<h3 id="how-are-markets-resolved-">How are markets resolved?</h3>
|
<h3 id="how-are-markets-resolved-">How are markets resolved?</h3>
|
||||||
<p>
|
<p>
|
||||||
The creator of the prediction market decides the outcome and earns 1% of
|
The creator of the prediction market decides the outcome and earns{' '}
|
||||||
the betting pool for their effort.
|
{CREATOR_FEE * 100}% of the winnings as a commission for creating and
|
||||||
|
resolving the market.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
This simple resolution mechanism has surprising benefits in allowing a
|
This simple resolution mechanism has surprising benefits in allowing a
|
||||||
|
|
|
@ -177,7 +177,7 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
<span>Market ante</span>
|
<span>Market ante</span>
|
||||||
<InfoTooltip
|
<InfoTooltip
|
||||||
text={`Subsidize your market to encourage trading. Ante bets are set to match your initial probability.
|
text={`Subsidize your market to encourage trading. Ante bets are set to match your initial probability.
|
||||||
You earn ${CREATOR_FEE * 100}% of trading volume.`}
|
You earn ${CREATOR_FEE * 100}% of the winnings.`}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<AmountInput
|
<AmountInput
|
||||||
|
|
|
@ -48,6 +48,10 @@ export async function getStaticProps(props: { params: { slugs: string[] } }) {
|
||||||
|
|
||||||
const contracts = fold ? await getFoldContracts(fold).catch((_) => []) : []
|
const contracts = fold ? await getFoldContracts(fold).catch((_) => []) : []
|
||||||
|
|
||||||
|
const betsPromise = Promise.all(
|
||||||
|
contracts.map((contract) => listAllBets(contract.id))
|
||||||
|
)
|
||||||
|
|
||||||
const [contractComments, contractRecentBets] = await Promise.all([
|
const [contractComments, contractRecentBets] = await Promise.all([
|
||||||
Promise.all(
|
Promise.all(
|
||||||
contracts.map((contract) => listAllComments(contract.id).catch((_) => []))
|
contracts.map((contract) => listAllComments(contract.id).catch((_) => []))
|
||||||
|
@ -79,17 +83,16 @@ export async function getStaticProps(props: { params: { slugs: string[] } }) {
|
||||||
contractComments[contracts.findIndex((c) => c.id === contract.id)]
|
contractComments[contracts.findIndex((c) => c.id === contract.id)]
|
||||||
)
|
)
|
||||||
|
|
||||||
const curator = await curatorPromise
|
const bets = await betsPromise
|
||||||
|
|
||||||
const bets = await Promise.all(
|
|
||||||
contracts.map((contract) => listAllBets(contract.id))
|
|
||||||
)
|
|
||||||
|
|
||||||
const creatorScores = scoreCreators(contracts, bets)
|
const creatorScores = scoreCreators(contracts, bets)
|
||||||
const topCreators = await toTopUsers(creatorScores)
|
|
||||||
|
|
||||||
const traderScores = scoreTraders(contracts, bets)
|
const traderScores = scoreTraders(contracts, bets)
|
||||||
const topTraders = await toTopUsers(traderScores)
|
const [topCreators, topTraders] = await Promise.all([
|
||||||
|
toTopUsers(creatorScores),
|
||||||
|
toTopUsers(traderScores),
|
||||||
|
])
|
||||||
|
|
||||||
|
const curator = await curatorPromise
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
@ -338,7 +341,7 @@ function FoldOverview(props: { fold: Fold; curator: User }) {
|
||||||
Includes markets matching any of these tags:
|
Includes markets matching any of these tags:
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TagsList tags={tags.map((tag) => `#${tag}`)} noLabel />
|
<TagsList tags={tags} noLabel />
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Col } from '../components/layout/col'
|
||||||
import { Row } from '../components/layout/row'
|
import { Row } from '../components/layout/row'
|
||||||
import { Page } from '../components/page'
|
import { Page } from '../components/page'
|
||||||
import { SiteLink } from '../components/site-link'
|
import { SiteLink } from '../components/site-link'
|
||||||
|
import { TagsList } from '../components/tags-list'
|
||||||
import { Title } from '../components/title'
|
import { Title } from '../components/title'
|
||||||
import { UserLink } from '../components/user-page'
|
import { UserLink } from '../components/user-page'
|
||||||
import { useFolds } from '../hooks/use-fold'
|
import { useFolds } from '../hooks/use-fold'
|
||||||
|
@ -63,7 +64,7 @@ export default function Folds(props: {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<Col className="items-center">
|
<Col className="items-center">
|
||||||
<Col className="max-w-lg w-full">
|
<Col className="max-w-xl w-full">
|
||||||
<Col className="px-4 sm:px-0">
|
<Col className="px-4 sm:px-0">
|
||||||
<Row className="justify-between items-center">
|
<Row className="justify-between items-center">
|
||||||
<Title text="Explore communities" />
|
<Title text="Explore communities" />
|
||||||
|
@ -76,7 +77,7 @@ export default function Folds(props: {
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col className="gap-2">
|
<Col className="gap-4">
|
||||||
{folds.map((fold) => (
|
{folds.map((fold) => (
|
||||||
<FoldCard
|
<FoldCard
|
||||||
key={fold.id}
|
key={fold.id}
|
||||||
|
@ -93,16 +94,17 @@ export default function Folds(props: {
|
||||||
|
|
||||||
function FoldCard(props: { fold: Fold; curator: User | undefined }) {
|
function FoldCard(props: { fold: Fold; curator: User | undefined }) {
|
||||||
const { fold, curator } = props
|
const { fold, curator } = props
|
||||||
|
const tags = fold.tags.slice(1)
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
key={fold.id}
|
key={fold.id}
|
||||||
className="bg-white hover:bg-gray-100 p-4 rounded-xl gap-1 shadow-md relative"
|
className="bg-white hover:bg-gray-100 p-8 rounded-xl gap-1 shadow-md relative"
|
||||||
>
|
>
|
||||||
<Link href={foldPath(fold)}>
|
<Link href={foldPath(fold)}>
|
||||||
<a className="absolute left-0 right-0 top-0 bottom-0" />
|
<a className="absolute left-0 right-0 top-0 bottom-0" />
|
||||||
</Link>
|
</Link>
|
||||||
<Row className="justify-between items-center gap-2">
|
<Row className="justify-between items-center gap-2">
|
||||||
<SiteLink href={foldPath(fold)}>{fold.name}</SiteLink>
|
<span className="text-xl">{fold.name}</span>
|
||||||
<FollowFoldButton className="z-10 mb-1" fold={fold} />
|
<FollowFoldButton className="z-10 mb-1" fold={fold} />
|
||||||
</Row>
|
</Row>
|
||||||
<Row className="items-center gap-2 text-gray-500 text-sm">
|
<Row className="items-center gap-2 text-gray-500 text-sm">
|
||||||
|
@ -118,6 +120,9 @@ function FoldCard(props: { fold: Fold; curator: User | undefined }) {
|
||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
<div className="text-gray-500 text-sm">{fold.about}</div>
|
<div className="text-gray-500 text-sm">{fold.about}</div>
|
||||||
|
{tags.length > 0 && (
|
||||||
|
<TagsList className="mt-4" tags={tags} noLink noLabel />
|
||||||
|
)}
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { Fold } from '../../common/fold'
|
||||||
import { filterDefined } from '../../common/util/array'
|
import { filterDefined } from '../../common/util/array'
|
||||||
import { useUserBets } from '../hooks/use-user-bets'
|
import { useUserBets } from '../hooks/use-user-bets'
|
||||||
import { LoadingIndicator } from '../components/loading-indicator'
|
import { LoadingIndicator } from '../components/loading-indicator'
|
||||||
|
import { TagsList } from '../components/tags-list'
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getStaticProps() {
|
||||||
const [contracts, folds] = await Promise.all([
|
const [contracts, folds] = await Promise.all([
|
||||||
|
@ -131,6 +132,18 @@ const Home = (props: { contracts: Contract[]; folds: Fold[] }) => {
|
||||||
<Col className="max-w-3xl w-full">
|
<Col className="max-w-3xl w-full">
|
||||||
<FeedCreate user={user ?? undefined} />
|
<FeedCreate user={user ?? undefined} />
|
||||||
<Spacer h={4} />
|
<Spacer h={4} />
|
||||||
|
<TagsList
|
||||||
|
className="mx-2"
|
||||||
|
tags={[
|
||||||
|
'#politics',
|
||||||
|
'#crypto',
|
||||||
|
'#covid',
|
||||||
|
'#sports',
|
||||||
|
'#meta',
|
||||||
|
'#science',
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Spacer h={4} />
|
||||||
{activeContracts ? (
|
{activeContracts ? (
|
||||||
<ActivityFeed
|
<ActivityFeed
|
||||||
contracts={activeContracts}
|
contracts={activeContracts}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user