Merge branch 'main' into show-comments-position
This commit is contained in:
commit
8c05eec479
10
README.md
10
README.md
|
@ -25,8 +25,9 @@ Operations with complicated contracts (e.g. buying shares) are provided in a sep
|
||||||
- `functions/`: Firebase cloud functions, for secure work (e.g. balances, Stripe payments, emails). Also contains in
|
- `functions/`: Firebase cloud functions, for secure work (e.g. balances, Stripe payments, emails). Also contains in
|
||||||
`functions/src/scripts/` some Typescript scripts that do ad hoc CLI interaction with Firebase.
|
`functions/src/scripts/` some Typescript scripts that do ad hoc CLI interaction with Firebase.
|
||||||
|
|
||||||
- `common/`: Typescript library code shared between `web/` & `functions/`. Also contains in `common/envs` configuration for
|
- `common/`: Typescript library code shared between `web/` & `functions/`. If you want to look at how the market math
|
||||||
the different environments (i.e. prod, dev, Manifold for Teams instances.)
|
works, most of that's in here (it gets called from the `placeBet` and `sellBet` endpoints in `functions/`.) Also
|
||||||
|
contains in `common/envs` configuration for the different environments (i.e. prod, dev, Manifold for Teams instances.)
|
||||||
|
|
||||||
- `og-image/`: The OpenGraph image generator; creates the preview images shown on Twitter/social media.
|
- `og-image/`: The OpenGraph image generator; creates the preview images shown on Twitter/social media.
|
||||||
|
|
||||||
|
@ -34,7 +35,9 @@ Also: Our docs are currently in [a separate repo](https://github.com/manifoldmar
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Since we are just now open-sourcing things, we will see how things go. Feel free to open issues, submit PRs, and chat about the process on Discord. We would prefer [small PRs][small-prs] that we can effectively evaluate and review -- maybe check in with us first if you are thinking to work on a big change.
|
Since we are just now open-sourcing things, we will see how things go. Feel free to open issues, submit PRs, and chat about the process on [Discord][discord]. We would prefer [small PRs][small-prs] that we can effectively evaluate and review -- maybe check in with us first if you are thinking to work on a big change.
|
||||||
|
|
||||||
|
If you need additional access to any infrastructure in order to work on something (e.g. Vercel, Firebase) let us know about that on Discord as well.
|
||||||
|
|
||||||
[vercel]: https://vercel.com/
|
[vercel]: https://vercel.com/
|
||||||
[jamstack]: https://jamstack.org/
|
[jamstack]: https://jamstack.org/
|
||||||
|
@ -45,3 +48,4 @@ Since we are just now open-sourcing things, we will see how things go. Feel free
|
||||||
[cloud-firestore]: https://firebase.google.com/docs/firestore
|
[cloud-firestore]: https://firebase.google.com/docs/firestore
|
||||||
[cloud-functions]: https://firebase.google.com/docs/functions
|
[cloud-functions]: https://firebase.google.com/docs/functions
|
||||||
[small-prs]: https://google.github.io/eng-practices/review/developer/small-cls.html
|
[small-prs]: https://google.github.io/eng-practices/review/developer/small-cls.html
|
||||||
|
[discord]: https://discord.gg/eHQBNBqXuh
|
||||||
|
|
|
@ -34,8 +34,9 @@ Adapted from https://firebase.google.com/docs/functions/get-started
|
||||||
|
|
||||||
## Developing locally
|
## Developing locally
|
||||||
|
|
||||||
|
0. `$ firebase use dev` if you haven't already
|
||||||
1. `$ yarn serve` to spin up the emulators
|
1. `$ yarn serve` to spin up the emulators
|
||||||
The Emulator UI is at http://localhost:4000; the functions are hosted on :5001.
|
1. The Emulator UI is at http://localhost:4000; the functions are hosted on :5001.
|
||||||
Note: You have to kill and restart emulators when you change code; no hot reload =(
|
Note: You have to kill and restart emulators when you change code; no hot reload =(
|
||||||
2. `$ yarn dev:emulate` in `/web` to connect to emulators with the frontend
|
2. `$ yarn dev:emulate` in `/web` to connect to emulators with the frontend
|
||||||
1. Note: emulated database is cleared after every shutdown
|
1. Note: emulated database is cleared after every shutdown
|
||||||
|
|
|
@ -48,8 +48,8 @@ export const sendMarketResolutionEmail = async (
|
||||||
creatorName: creator.name,
|
creatorName: creator.name,
|
||||||
question: contract.question,
|
question: contract.question,
|
||||||
outcome,
|
outcome,
|
||||||
investment: `${Math.round(investment)}`,
|
investment: `${Math.floor(investment)}`,
|
||||||
payout: `${Math.round(payout)}`,
|
payout: `${Math.floor(payout)}`,
|
||||||
url: `https://${DOMAIN}/${creator.username}/${contract.slug}`,
|
url: `https://${DOMAIN}/${creator.username}/${contract.slug}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +189,9 @@ export const sendNewCommentEmail = async (
|
||||||
let betDescription = ''
|
let betDescription = ''
|
||||||
if (bet) {
|
if (bet) {
|
||||||
const { amount, sale } = bet
|
const { amount, sale } = bet
|
||||||
betDescription = `${sale ? 'sold' : 'bought'} M$ ${Math.round(amount)}`
|
betDescription = `${sale || amount < 0 ? 'sold' : 'bought'} ${formatMoney(
|
||||||
|
Math.abs(amount)
|
||||||
|
)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const subject = `Comment on ${question}`
|
const subject = `Comment on ${question}`
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
To run the development server, install [Yarn][yarn], and then in this directory:
|
To run the development server, install [Yarn 1.x][yarn], and then in this directory:
|
||||||
|
|
||||||
1. `yarn` to install all dependencies
|
1. `yarn` to install all dependencies
|
||||||
2. `yarn dev:dev` starts a development web server, pointing at the development database
|
2. `yarn dev:dev` starts a development web server, pointing at the development database
|
||||||
3. Your site will be available on http://localhost:3000
|
3. Your site will be available on http://localhost:3000
|
||||||
|
|
||||||
Check package.json for other command-line tasks. (e.g. `yarn dev` will point the development server at the prod database. `yarn emulate` will run against a local emulated database, if you are serving it via `yarn serve` from the [`functions/` package][functions-readme].)
|
Check package.json for other command-line tasks. (e.g. `yarn dev` will point the development server at the prod
|
||||||
|
database. `yarn emulate` will run against a local emulated database, if you are serving it via `yarn serve` from the
|
||||||
|
[`functions/` package][functions-readme].)
|
||||||
|
|
||||||
## Tech stack
|
## Tech stack
|
||||||
|
|
||||||
|
@ -24,16 +26,17 @@ branch (to production) and PR branches (to ephemeral staging servers that can be
|
||||||
|
|
||||||
Parts of the file structure that directly map to HTTP endpoints are organized specially per Next.js's prescriptions:
|
Parts of the file structure that directly map to HTTP endpoints are organized specially per Next.js's prescriptions:
|
||||||
|
|
||||||
### /public
|
### public/
|
||||||
|
|
||||||
These are static files that will be [served by Next verbatim][next-static-files].
|
These are static files that will be [served by Next verbatim][next-static-files].
|
||||||
|
|
||||||
### /pages
|
### pages/
|
||||||
|
|
||||||
These are components that [Next's router][next-pages] is aware of and interprets as page roots per their filename,
|
These are components that [Next's router][next-pages] is aware of and interprets as page roots per their filename,
|
||||||
e.g. the React component in pages/portfolio.tsx is rendered on the user portfolio page at /portfolio.
|
e.g. the React component in pages/portfolio.tsx is rendered on the user portfolio page at /portfolio. You should
|
||||||
|
look in here or in `components/` to find any specific piece of UI you are interested in working on.
|
||||||
|
|
||||||
### /pages/api
|
### pages/api/
|
||||||
|
|
||||||
Modules under this route are specially interpreted by Next/Vercel as [functions that will be hosted by
|
Modules under this route are specially interpreted by Next/Vercel as [functions that will be hosted by
|
||||||
Vercel][vercel-functions]. This is where the public Manifold HTTP API lives.
|
Vercel][vercel-functions]. This is where the public Manifold HTTP API lives.
|
||||||
|
@ -52,7 +55,7 @@ integration][prettier-integrations] to format it in your editor.
|
||||||
[nextjs]: https://nextjs.org
|
[nextjs]: https://nextjs.org
|
||||||
[vercel]: https://vercel.com
|
[vercel]: https://vercel.com
|
||||||
[tailwind]: https://tailwindcss.com
|
[tailwind]: https://tailwindcss.com
|
||||||
[yarn]: https://yarnpkg.com
|
[yarn]: https://classic.yarnpkg.com/lang/en/docs/install/
|
||||||
[prettier]: https://prettier.io
|
[prettier]: https://prettier.io
|
||||||
[prettier-integrations]: https://prettier.io/docs/en/editors.html
|
[prettier-integrations]: https://prettier.io/docs/en/editors.html
|
||||||
[next-static-files]: https://nextjs.org/docs/basic-features/static-file-serving
|
[next-static-files]: https://nextjs.org/docs/basic-features/static-file-serving
|
||||||
|
|
|
@ -4,8 +4,11 @@ export function ProbabilitySelector(props: {
|
||||||
probabilityInt: number
|
probabilityInt: number
|
||||||
setProbabilityInt: (p: number) => void
|
setProbabilityInt: (p: number) => void
|
||||||
isSubmitting?: boolean
|
isSubmitting?: boolean
|
||||||
|
minProb?: number
|
||||||
|
maxProb?: number
|
||||||
}) {
|
}) {
|
||||||
const { probabilityInt, setProbabilityInt, isSubmitting } = props
|
const { probabilityInt, setProbabilityInt, isSubmitting, minProb, maxProb } =
|
||||||
|
props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row className="items-center gap-2">
|
<Row className="items-center gap-2">
|
||||||
|
@ -15,19 +18,28 @@ export function ProbabilitySelector(props: {
|
||||||
value={probabilityInt}
|
value={probabilityInt}
|
||||||
className="input input-bordered input-md text-lg"
|
className="input input-bordered input-md text-lg"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
min={1}
|
min={minProb ?? 1}
|
||||||
max={99}
|
max={maxProb ?? 99}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setProbabilityInt(parseInt(e.target.value.substring(0, 2)))
|
setProbabilityInt(parseInt(e.target.value.substring(0, 2)))
|
||||||
}
|
}
|
||||||
|
onBlur={() =>
|
||||||
|
setProbabilityInt(
|
||||||
|
maxProb && probabilityInt > maxProb
|
||||||
|
? maxProb
|
||||||
|
: minProb && probabilityInt < minProb
|
||||||
|
? minProb
|
||||||
|
: probabilityInt
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<span>%</span>
|
<span>%</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
className="range range-primary"
|
className="range range-primary"
|
||||||
min={1}
|
min={minProb ?? 1}
|
||||||
max={99}
|
max={maxProb ?? 99}
|
||||||
value={probabilityInt}
|
value={probabilityInt}
|
||||||
onChange={(e) => setProbabilityInt(parseInt(e.target.value))}
|
onChange={(e) => setProbabilityInt(parseInt(e.target.value))}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -151,6 +151,10 @@ function getContractsActivityScores(
|
||||||
const activityCountScore =
|
const activityCountScore =
|
||||||
0.5 + 0.5 * logInterpolation(0, 200, activtyCount)
|
0.5 + 0.5 * logInterpolation(0, 200, activtyCount)
|
||||||
|
|
||||||
|
const { volume7Days, volume } = contract
|
||||||
|
const combinedVolume = Math.log(volume7Days + 1) + Math.log(volume + 1)
|
||||||
|
const volumeScore = 0.5 + 0.5 * logInterpolation(4, 25, combinedVolume)
|
||||||
|
|
||||||
const lastBetTime =
|
const lastBetTime =
|
||||||
contractMostRecentBet[contract.id]?.createdTime ?? contract.createdTime
|
contractMostRecentBet[contract.id]?.createdTime ?? contract.createdTime
|
||||||
const timeSinceLastBet = Date.now() - lastBetTime
|
const timeSinceLastBet = Date.now() - lastBetTime
|
||||||
|
@ -169,7 +173,11 @@ function getContractsActivityScores(
|
||||||
const probScore = 0.5 + frac * 0.5
|
const probScore = 0.5 + frac * 0.5
|
||||||
|
|
||||||
const score =
|
const score =
|
||||||
newCommentScore * activityCountScore * timeAgoScore * probScore
|
newCommentScore *
|
||||||
|
activityCountScore *
|
||||||
|
volumeScore *
|
||||||
|
timeAgoScore *
|
||||||
|
probScore
|
||||||
|
|
||||||
// Map score to [0.5, 1] since no recent activty is not a deal breaker.
|
// Map score to [0.5, 1] since no recent activty is not a deal breaker.
|
||||||
const mappedScore = 0.5 + score / 2
|
const mappedScore = 0.5 + score / 2
|
||||||
|
|
|
@ -106,11 +106,14 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
|
|
||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
|
|
||||||
|
const boundedProb =
|
||||||
|
initialProb > 90 ? 90 : initialProb < 10 ? 10 : initialProb
|
||||||
|
|
||||||
const result: any = await createContract({
|
const result: any = await createContract({
|
||||||
question,
|
question,
|
||||||
outcomeType,
|
outcomeType,
|
||||||
description,
|
description,
|
||||||
initialProb,
|
initialProb: boundedProb,
|
||||||
ante,
|
ante,
|
||||||
closeTime,
|
closeTime,
|
||||||
tags,
|
tags,
|
||||||
|
@ -172,6 +175,8 @@ export function NewContract(props: { question: string; tag?: string }) {
|
||||||
<ProbabilitySelector
|
<ProbabilitySelector
|
||||||
probabilityInt={initialProb}
|
probabilityInt={initialProb}
|
||||||
setProbabilityInt={setInitialProb}
|
setProbabilityInt={setInitialProb}
|
||||||
|
minProb={10}
|
||||||
|
maxProb={90}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,20 +1,26 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import { GetServerSideProps } from 'next'
|
import { GetServerSideProps } from 'next'
|
||||||
import { getServerSideSitemap } from 'next-sitemap'
|
import { getServerSideSitemap, ISitemapField } from 'next-sitemap'
|
||||||
|
|
||||||
import { DOMAIN } from '../../common/envs/constants'
|
import { DOMAIN } from '../../common/envs/constants'
|
||||||
|
import { LiteMarket } from './api/v0/_types'
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
// Fetching data from https://docs.manifold.markets/api
|
// Fetching data from https://manifold.markets/api
|
||||||
const response = await fetch(`https://${DOMAIN}/api/v0/markets`)
|
const response = await fetch(`https://${DOMAIN}/api/v0/markets`)
|
||||||
|
|
||||||
const liteMarkets = await response.json()
|
const liteMarkets = (await response.json()) as LiteMarket[]
|
||||||
const fields = liteMarkets.map((liteMarket: any) => ({
|
const sortedMarkets = _.sortBy(liteMarkets, (m) => -m.volume24Hours)
|
||||||
|
|
||||||
|
const fields = sortedMarkets.map((market) => ({
|
||||||
// See https://www.sitemaps.org/protocol.html
|
// See https://www.sitemaps.org/protocol.html
|
||||||
loc: liteMarket.url,
|
loc: market.url,
|
||||||
changefreq: 'hourly',
|
changefreq: market.volume24Hours > 10 ? 'hourly' : 'daily',
|
||||||
priority: 0.2, // Individual markets aren't that important
|
priority: market.volume24Hours + market.volume7Days > 100 ? 0.7 : 0.1,
|
||||||
// TODO: Add `lastmod` aka last modified time
|
// TODO: Add `lastmod` aka last modified time
|
||||||
}))
|
})) as ISitemapField[]
|
||||||
return getServerSideSitemap(ctx, fields)
|
|
||||||
|
return await getServerSideSitemap(ctx, fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default export to prevent next.js errors
|
// Default export to prevent next.js errors
|
||||||
|
|
|
@ -6,5 +6,5 @@ Allow: /
|
||||||
Host: https://manifold.markets
|
Host: https://manifold.markets
|
||||||
|
|
||||||
# Sitemaps
|
# Sitemaps
|
||||||
Sitemap: https://manifold.markets/sitemap.xml
|
|
||||||
Sitemap: https://manifold.markets/server-sitemap.xml
|
Sitemap: https://manifold.markets/server-sitemap.xml
|
||||||
|
Sitemap: https://manifold.markets/sitemap.xml
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
||||||
<url><loc>https://manifold.markets</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
<url><loc>https://manifold.markets</loc><changefreq>hourly</changefreq><priority>1.0</priority></url>
|
||||||
<url><loc>https://manifold.markets/about</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
<url><loc>https://manifold.markets/markets</loc><changefreq>hourly</changefreq><priority>0.2</priority></url>
|
||||||
<url><loc>https://manifold.markets/account</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
<url><loc>https://manifold.markets/leaderboards</loc><changefreq>daily</changefreq><priority>0.2</priority></url>
|
||||||
<url><loc>https://manifold.markets/add-funds</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/analytics</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/create</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/embed/analytics</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/folds</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/home</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/landing-page</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/leaderboards</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/make-predictions</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/markets</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/profile</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/simulator</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
<url><loc>https://manifold.markets/portfolio</loc><changefreq>hourly</changefreq><priority>0.7</priority><lastmod>2022-03-24T16:51:19.526Z</lastmod></url>
|
|
||||||
</urlset>
|
</urlset>
|
Loading…
Reference in New Issue
Block a user