From a90e9b7083870d6f9f475641b5a34fcb41df0859 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Thu, 3 Feb 2022 00:19:59 -0600 Subject: [PATCH 01/10] Reorder fold page queries to be more in parallel --- web/pages/fold/[...slugs]/index.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/web/pages/fold/[...slugs]/index.tsx b/web/pages/fold/[...slugs]/index.tsx index 6841cf27..e93bdbbb 100644 --- a/web/pages/fold/[...slugs]/index.tsx +++ b/web/pages/fold/[...slugs]/index.tsx @@ -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: { From d0766fa7c3560e25c47e816c69b79ad2a06fb6e7 Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Thu, 3 Feb 2022 00:56:49 -0800 Subject: [PATCH 02/10] Use the latest bet time for a feed group --- web/components/contract-feed.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/components/contract-feed.tsx b/web/components/contract-feed.tsx index fe1cbaaa..8455744d 100644 --- a/web/components/contract-feed.tsx +++ b/web/components/contract-feed.tsx @@ -540,7 +540,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 ( <> From e7433c2eec57c12d74917ce5ce5956f4d5cb3c4a Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Thu, 3 Feb 2022 01:12:37 -0800 Subject: [PATCH 03/10] Save comment and description on ctrl+enter --- web/components/contract-feed.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/components/contract-feed.tsx b/web/components/contract-feed.tsx index 8455744d..2279e2fb 100644 --- a/web/components/contract-feed.tsx +++ b/web/components/contract-feed.tsx @@ -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() + } + }} /> ) From 193b9ae6bf1a38751f236453a2c4356b285f55e5 Mon Sep 17 00:00:00 2001 From: Austin Chen Date: Thu, 3 Feb 2022 10:47:26 -0800 Subject: [PATCH 06/10] Restyle /folds page --- web/pages/folds.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/web/pages/folds.tsx b/web/pages/folds.tsx index cc3d1dea..f4583bfc 100644 --- a/web/pages/folds.tsx +++ b/web/pages/folds.tsx @@ -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 ( - + @@ -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> ) } From 426d41ba4451e022848dffe56d0f3e55af33aaa6 Mon Sep 17 00:00:00 2001 From: Austin Chen <akrolsmir@gmail.com> Date: Thu, 3 Feb 2022 11:00:49 -0800 Subject: [PATCH 07/10] Surface a list of tags on /home --- web/pages/home.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/web/pages/home.tsx b/web/pages/home.tsx index 840e1746..c100fb42 100644 --- a/web/pages/home.tsx +++ b/web/pages/home.tsx @@ -16,6 +16,8 @@ import { Spacer } from '../components/layout/spacer' import { Col } from '../components/layout/col' import { useUser } from '../hooks/use-user' import { useContracts } from '../hooks/use-contracts' +import { FoldTag, TagsList } from '../components/tags-list' +import { Row } from '../components/layout/row' export async function getStaticProps() { const [contracts, recentComments, recentBets] = await Promise.all([ @@ -77,6 +79,20 @@ const Home = (props: { <Col className="max-w-3xl"> <FeedCreate user={user ?? undefined} /> <Spacer h={4} /> + + <TagsList + className="mx-2" + tags={[ + '#politics', + '#crypto', + '#covid', + '#sports', + '#meta', + '#science', + ]} + /> + <Spacer h={4} /> + <ActivityFeed contracts={updatedContracts} contractBets={activeContractBets} From 6c9f566d3db5d8a4aa3872955f408e4efeff6e39 Mon Sep 17 00:00:00 2001 From: James Grugett <jahooma@gmail.com> Date: Thu, 3 Feb 2022 14:13:51 -0600 Subject: [PATCH 08/10] Add hashtag in tags of fold card --- web/components/edit-fold-button.tsx | 2 +- web/components/tags-input.tsx | 2 +- web/components/tags-list.tsx | 8 ++++++-- web/pages/fold/[...slugs]/index.tsx | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/web/components/edit-fold-button.tsx b/web/components/edit-fold-button.tsx index 3f41b6fe..246c19b6 100644 --- a/web/components/edit-fold-button.tsx +++ b/web/components/edit-fold-button.tsx @@ -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"> diff --git a/web/components/tags-input.tsx b/web/components/tags-input.tsx index 24b4a20c..1e1678dc 100644 --- a/web/components/tags-input.tsx +++ b/web/components/tags-input.tsx @@ -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 diff --git a/web/components/tags-list.tsx b/web/components/tags-list.tsx index def6a34a..4894643b 100644 --- a/web/components/tags-list.tsx +++ b/web/components/tags-list.tsx @@ -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> ) diff --git a/web/pages/fold/[...slugs]/index.tsx b/web/pages/fold/[...slugs]/index.tsx index e93bdbbb..f43f847a 100644 --- a/web/pages/fold/[...slugs]/index.tsx +++ b/web/pages/fold/[...slugs]/index.tsx @@ -341,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> ) From 4294481b5b93993f6f9c0864095060713a3443b4 Mon Sep 17 00:00:00 2001 From: mantikoros <sgrugett@gmail.com> Date: Thu, 3 Feb 2022 16:59:59 -0600 Subject: [PATCH 09/10] change fee structure --- common/calculate.ts | 6 +++--- common/fees.ts | 4 ++-- common/payouts.ts | 10 +++++----- common/sell-bet.ts | 4 ++-- web/pages/about.tsx | 7 ++++--- web/pages/create.tsx | 2 +- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/common/calculate.ts b/common/calculate.ts index 2e69690e..fbbfd66f 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -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 ) diff --git a/common/fees.ts b/common/fees.ts index 718cdbcb..767107ed 100644 --- a/common/fees.ts +++ b/common/fees.ts @@ -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 diff --git a/common/payouts.ts b/common/payouts.ts index c4010ba2..77630502 100644 --- a/common/payouts.ts +++ b/common/payouts.ts @@ -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, diff --git a/common/sell-bet.ts b/common/sell-bet.ts index e3080644..eeeed355 100644 --- a/common/sell-bet.ts +++ b/common/sell-bet.ts @@ -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$', diff --git a/web/pages/about.tsx b/web/pages/about.tsx index a0d7659e..47e796a7 100644 --- a/web/pages/about.tsx +++ b/web/pages/about.tsx @@ -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 diff --git a/web/pages/create.tsx b/web/pages/create.tsx index 0dddd6f1..bc747fc7 100644 --- a/web/pages/create.tsx +++ b/web/pages/create.tsx @@ -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 From 55aa2db553e8469d8384da15ff697c6f2a6f3d34 Mon Sep 17 00:00:00 2001 From: mantikoros <sgrugett@gmail.com> Date: Thu, 3 Feb 2022 17:07:30 -0600 Subject: [PATCH 10/10] resolution panel creator fee --- web/components/resolution-panel.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/web/components/resolution-panel.tsx b/web/components/resolution-panel.tsx index 1a409f7e..a31072c4 100644 --- a/web/components/resolution-panel.tsx +++ b/web/components/resolution-panel.tsx @@ -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.</>