Merge branch 'main' into custom-feed

This commit is contained in:
James Grugett 2022-02-03 17:33:12 -06:00
commit e5f553fa1a
16 changed files with 82 additions and 43 deletions

View File

@ -104,7 +104,7 @@ export function calculateShareValue(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(
@ -145,7 +145,7 @@ export function calculateStandardPayout(
totalShares[outcome] - phantomShares[outcome] - 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) {
@ -204,7 +204,7 @@ function calculateMktPayout(contract: Contract, bet: Bet) {
return (
betP * bet.amount +
(1 - 2 * FEES) *
(1 - FEES) *
((betP * (bet.shares - bet.amount)) / weightedShareTotal) *
winningsPool
)

View File

@ -1,4 +1,4 @@
export const PLATFORM_FEE = 0.01 // == 1%
export const CREATOR_FEE = 0.01
export const PLATFORM_FEE = 0.01
export const CREATOR_FEE = 0.09
export const FEES = PLATFORM_FEE + CREATOR_FEE

View File

@ -38,12 +38,12 @@ export const getStandardPayouts = (
userId: bet.userId,
payout:
bet.amount +
(1 - 2 * FEES) *
(1 - FEES) *
((bet.shares - bet.amount) / shareDifferenceSum) *
winningsPool,
}))
const creatorPayout = 2 * CREATOR_FEE * winningsPool
const creatorPayout = CREATOR_FEE * winningsPool
console.log(
'resolved',
@ -98,7 +98,7 @@ export const getMktPayouts = (
userId: bet.userId,
payout:
p * bet.amount +
(1 - 2 * FEES) *
(1 - FEES) *
((p * (bet.shares - bet.amount)) / weightedShareTotal) *
winningsPool,
}))
@ -107,12 +107,12 @@ export const getMktPayouts = (
userId: bet.userId,
payout:
(1 - p) * bet.amount +
(1 - 2 * FEES) *
(1 - FEES) *
(((1 - p) * (bet.shares - bet.amount)) / weightedShareTotal) *
winningsPool,
}))
const creatorPayout = 2 * CREATOR_FEE * winningsPool
const creatorPayout = CREATOR_FEE * winningsPool
return [
...yesPayouts,

View File

@ -36,8 +36,8 @@ export const getSellBetInfo = (
const probBefore = getProbability(contract.totalShares)
const probAfter = getProbability(newTotalShares)
const creatorFee = 2 * CREATOR_FEE * adjShareValue
const saleAmount = (1 - 2 * FEES) * adjShareValue
const creatorFee = CREATOR_FEE * adjShareValue
const saleAmount = (1 - FEES) * adjShareValue
console.log(
'SELL M$',

View File

@ -130,6 +130,11 @@ function FeedBet(props: { activityItem: any }) {
className="textarea textarea-bordered w-full"
placeholder="Add a comment..."
rows={3}
onKeyDown={(e) => {
if (e.key === 'Enter' && e.ctrlKey) {
submitComment()
}
}}
/>
<button
className="btn btn-outline btn-sm mt-1"
@ -195,6 +200,11 @@ export function ContractDescription(props: {
description.length
)
}
onKeyDown={(e) => {
if (e.key === 'Enter' && e.ctrlKey) {
saveDescription(e)
}
}}
/>
<Row className="gap-2">
<button
@ -540,7 +550,8 @@ function FeedBetGroup(props: { activityItem: any }) {
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 (
<>

View File

@ -262,7 +262,7 @@ export function SearchableGrid(props: {
onChange={(e) => setSort(e.target.value as Sort)}
>
<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="newest">Newest</option>

View File

@ -105,7 +105,7 @@ export function EditFoldButton(props: { fold: Fold; className?: string }) {
</div>
<Spacer h={4} />
<TagsList tags={tags.map((tag) => `#${tag}`)} noLink noLabel />
<TagsList tags={tags} noLink noLabel />
<Spacer h={4} />
<div className="modal-action">

View File

@ -37,10 +37,7 @@ export function FollowFoldButton(props: { fold: Fold; className?: string }) {
}
return (
<button
className={clsx('btn btn-secondary bg-indigo-500 btn-sm', className)}
onClick={onFollow}
>
<button className={clsx('btn btn-sm', className)} onClick={onFollow}>
Follow
</button>
)

View File

@ -11,6 +11,7 @@ import { ConfirmationButton as ConfirmationButton } from './confirmation-button'
import { resolveMarket } from '../lib/firebase/api-call'
import { ProbabilitySelector } from './probability-selector'
import { getProbability } from '../../common/calculate'
import { CREATOR_FEE } from '../../common/fees'
export function ResolutionPanel(props: {
creator: User
@ -79,16 +80,20 @@ export function ResolutionPanel(props: {
<div>
{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' ? (
<>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' ? (
<>The pool will be returned to traders with no fees.</>
) : outcome === 'MKT' ? (
<>
Traders will be paid out at the probability you specify. You earn 1%
of the pool.
Traders will be paid out at the probability you specify. You earn{' '}
{CREATOR_FEE * 100}%.
</>
) : (
<>Resolving this market will immediately pay out traders.</>

View File

@ -27,7 +27,7 @@ export function TagsInput(props: { contract: Contract; className?: string }) {
return (
<Col className={clsx('gap-4', className)}>
<TagsList tags={newTags.map((tag) => `#${tag}`)} />
<TagsList tags={newTags} />
<Row className="items-center gap-4">
<input

View File

@ -3,7 +3,7 @@ import { Row } from './layout/row'
import { SiteLink } from './site-link'
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 body = (
<div
@ -35,7 +35,11 @@ export function TagsList(props: {
<Row className={clsx('items-center flex-wrap gap-2', className)}>
{!noLabel && <div className="text-gray-500 mr-1">Tags</div>}
{tags.map((tag) => (
<Hashtag key={tag} tag={tag} noLink={noLink} />
<Hashtag
key={tag}
tag={tag.startsWith('#') ? tag : `#${tag}`}
noLink={noLink}
/>
))}
</Row>
)

View File

@ -1,7 +1,7 @@
import { cloneElement } from 'react'
import { CREATOR_FEE } from '../../common/fees'
import { Page } from '../components/page'
import { SEO } from '../components/SEO'
import { useContracts } from '../hooks/use-contracts'
import styles from './about.module.css'
export default function About() {
@ -132,8 +132,9 @@ function Contents() {
</p>
<h3 id="how-are-markets-resolved-">How are markets resolved?</h3>
<p>
The creator of the prediction market decides the outcome and earns 1% of
the betting pool for their effort.
The creator of the prediction market decides the outcome and earns{' '}
{CREATOR_FEE * 100}% of the winnings as a commission for creating and
resolving the market.
</p>
<p>
This simple resolution mechanism has surprising benefits in allowing a

View File

@ -177,7 +177,7 @@ export function NewContract(props: { question: string; tag?: string }) {
<span>Market ante</span>
<InfoTooltip
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>
<AmountInput

View File

@ -48,6 +48,10 @@ export async function getStaticProps(props: { params: { slugs: string[] } }) {
const contracts = fold ? await getFoldContracts(fold).catch((_) => []) : []
const betsPromise = Promise.all(
contracts.map((contract) => listAllBets(contract.id))
)
const [contractComments, contractRecentBets] = await Promise.all([
Promise.all(
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)]
)
const curator = await curatorPromise
const bets = await Promise.all(
contracts.map((contract) => listAllBets(contract.id))
)
const bets = await betsPromise
const creatorScores = scoreCreators(contracts, bets)
const topCreators = await toTopUsers(creatorScores)
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 {
props: {
@ -338,7 +341,7 @@ function FoldOverview(props: { fold: Fold; curator: User }) {
Includes markets matching any of these tags:
</div>
<TagsList tags={tags.map((tag) => `#${tag}`)} noLabel />
<TagsList tags={tags} noLabel />
</Col>
</Col>
)

View File

@ -8,6 +8,7 @@ import { Col } from '../components/layout/col'
import { Row } from '../components/layout/row'
import { Page } from '../components/page'
import { SiteLink } from '../components/site-link'
import { TagsList } from '../components/tags-list'
import { Title } from '../components/title'
import { UserLink } from '../components/user-page'
import { useFolds } from '../hooks/use-fold'
@ -63,7 +64,7 @@ export default function Folds(props: {
return (
<Page>
<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">
<Row className="justify-between items-center">
<Title text="Explore communities" />
@ -76,7 +77,7 @@ export default function Folds(props: {
</div>
</Col>
<Col className="gap-2">
<Col className="gap-4">
{folds.map((fold) => (
<FoldCard
key={fold.id}
@ -93,16 +94,17 @@ export default function Folds(props: {
function FoldCard(props: { fold: Fold; curator: User | undefined }) {
const { fold, curator } = props
const tags = fold.tags.slice(1)
return (
<Col
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)}>
<a className="absolute left-0 right-0 top-0 bottom-0" />
</Link>
<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} />
</Row>
<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>
<div className="text-gray-500 text-sm">{fold.about}</div>
{tags.length > 0 && (
<TagsList className="mt-4" tags={tags} noLink noLabel />
)}
</Col>
)
}

View File

@ -17,6 +17,7 @@ import { Fold } from '../../common/fold'
import { filterDefined } from '../../common/util/array'
import { useUserBets } from '../hooks/use-user-bets'
import { LoadingIndicator } from '../components/loading-indicator'
import { TagsList } from '../components/tags-list'
export async function getStaticProps() {
const [contracts, folds] = await Promise.all([
@ -131,6 +132,18 @@ const Home = (props: { contracts: Contract[]; folds: Fold[] }) => {
<Col className="max-w-3xl w-full">
<FeedCreate user={user ?? undefined} />
<Spacer h={4} />
<TagsList
className="mx-2"
tags={[
'#politics',
'#crypto',
'#covid',
'#sports',
'#meta',
'#science',
]}
/>
<Spacer h={4} />
{activeContracts ? (
<ActivityFeed
contracts={activeContracts}