Compare commits

..

2351 Commits

Author SHA1 Message Date
Phil
c762869b83
Migrate prod bot (#1052)
* Updated dev config to point to new Twitch dev bot hosting location.

* Updated prod config to point to new Twitch bot hosting location.
2022-10-14 16:41:06 +01:00
FRC
aaa09f49c0
New component to add arbitrary (but static) react embeds to posts programatically + midterms component (#1051) 2022-10-14 16:21:54 +01:00
FRC
4d214c01b4
Tipping in posts (#1045)
* Tipping in posts

* Rm itemType field
2022-10-14 13:05:07 +01:00
ingawei
0a70652667
fixed tip button bug (#1049)
* fixed tip button bug
2022-10-14 02:20:32 -05:00
mqp
b4162a0896 Auto-prettification 2022-10-14 06:49:23 +00:00
ingawei
2ecece02c3 Auto-remove unused imports 2022-10-14 06:48:40 +00:00
ingawei
4359ad0530
added cancelling tipping and lil coin (#1047)
* added cancelling tipping and lil coin
* forced timeout to fix weird toast bug
2022-10-14 01:47:51 -05:00
ingawei
3f8988bf27
Inga/colorful answer comments (#1040)
* changing answers in comments to match colors, no longer indenting those responses
2022-10-14 00:07:54 -05:00
Austin Chen
41a46aad9b Update stability-client to fix /dream 2022-10-13 21:44:22 -07:00
James Grugett
4097082c75
Dynamically choose outcome in Position tooltip (#1048) 2022-10-13 23:29:57 -05:00
mantikoros
58cd0e57bd track tournaments 2022-10-13 21:22:32 -05:00
Austin Chen
3a63503161 Fix @/% suggestion regression
See https://github.com/ueberdosis/tiptap/pull/3239
2022-10-13 19:06:03 -07:00
mantikoros
abd06f272b contract card: show traders instead of volume 2022-10-13 21:05:21 -05:00
Austin Chen
ab1c3020da Warn users about Dream breakage 2022-10-13 18:45:09 -07:00
Sinclair Chen
c044460a91
Don't break build on unused import (#1046)
* Fix lint warnings

* Let vercel build when unused import
2022-10-13 18:16:22 -07:00
mantikoros
7a6725ee77 contract card: less busy design 2022-10-13 19:56:16 -05:00
Austin Chen
823d1ddd4c Update stability-client 2022-10-13 17:01:05 -07:00
Austin Chen
7d490e0de1 Expand image on hover 2022-10-13 16:23:25 -07:00
Sinclair Chen
96d2255cb1 fix find and replace mistake 2022-10-13 15:15:45 -07:00
Sinclair Chen
4a139c5cc2 Fix limit prob styling 2022-10-13 14:55:48 -07:00
James Grugett
47eb8abed0 Don't include loans in email payout message 2022-10-13 16:05:40 -05:00
Sinclair Chen
903fcc83b3 Fix form-control styling 2022-10-13 13:20:04 -07:00
Austin Chen
0615bb2d4b
List ManifoldDream as a bot 2022-10-13 13:12:22 -07:00
mantikoros
3508c94634 tip button: fix tip color 2022-10-13 14:33:41 -05:00
mantikoros
29c0dfe3fe tip button: remove fill color 2022-10-13 14:27:47 -05:00
mantikoros
18a3b66164 dream label 2022-10-13 14:16:30 -05:00
James Grugett
9e4f41253f Filter out resolved markets from daily movers 2022-10-13 13:49:48 -05:00
Sinclair Chen
546b0231e7
Die Daisy (#1042)
* un-daisy labels

* un-daisy inputs

* un-daisy input groups

* fixup input

* un-daisy selects

* un-daisy slider

* Uninstall daisy. Migrate colors

* un-daisy tables

* fix input error styling

* lint
2022-10-13 11:23:42 -07:00
Phil
8bb44593f3
Updated dev config to point to new Twitch dev bot hosting location. (#1044) 2022-10-13 17:19:50 +01:00
Pico2x
2c2bc61788 Fix bug with parsing in abritrary react components 2022-10-13 16:56:35 +01:00
Pico2x
34c9dbb3e7 Revert "Revert "New implementation of market card embeddings (#1025)""
This reverts commit d6525bae9f.
2022-10-13 16:25:15 +01:00
Ian Philips
d6525bae9f Revert "New implementation of market card embeddings (#1025)"
This reverts commit 3fc53112b9.
2022-10-13 09:19:28 -06:00
Ian Philips
da32a756a8 Need not follow contract for tag notification 2022-10-13 08:41:33 -06:00
Ian Philips
fa476c78dd Handle numeric outcomes in movers 2022-10-13 08:18:16 -06:00
Ian Philips
e7ba7e715f default cursor on open answer 2022-10-13 08:04:01 -06:00
Ian Philips
9bf82c6082 Match colors on portfolio 2022-10-13 08:00:04 -06:00
Ian Philips
3e1876f0dc Add flaggedByUsernames to firestore rules 2022-10-13 07:58:14 -06:00
ingawei
5ba4a9dce7
Inga/tip button (#1043)
* added tip jar
* made market actions/comments and manalink buttons IconButtons
2022-10-13 01:53:26 -05:00
James Grugett
4e5b78f4ee Use in-memory store for home featured section data 2022-10-12 21:15:40 -05:00
James Grugett
bc6fab399e Make text grayer 2022-10-12 20:51:48 -05:00
James Grugett
c2d112e516 Position => shares 2022-10-12 20:51:20 -05:00
ingawei
3cbe8ad8bb
Inga/comment bounty fix (#1041)
* fixed bounty button in comments
2022-10-12 20:34:07 -05:00
ingawei
6226291e02 made comments smaller 2022-10-12 17:53:37 -07:00
Austin Chen
fa4dba4da3 Document the new /comment endpoint 2022-10-12 17:26:00 -07:00
Austin Chen
9eff69be75
Add /createcomment API endpoint (#946)
* /dream api: Upload StableDiffusion image to Firestore

* Minor tweaks

* Set content type on uploaded image

This makes it so the image doesn't auto-download when opened in a new tab

* Allow users to dream directly from within Manifold

* Remove unused import

* Implement a /comment endpoint which supports html and markdown

* Upgrade @tiptap/core to latest

* Update all tiptap deps to beta.199

* Add @tiptap/suggestion

* Import @tiptap/html in the right place

* ... add deps everywhere

So I have no idea how common deps work apparently

* Add tiptap/suggestion too

* Clean up dream

* More cleanups

* Rework /comment endpoint

* Move API to /comment

* Change imports in case that matters

* Add a couple todos

* Dynamically import micromark

* Parallellize gsutil with -m option

* Adding comments via api working, editor.tsx erroring out

* Unused import

* Remove disabled state from useTextEditor

Co-authored-by: Ian Philips <iansphilips@gmail.com>
2022-10-12 17:25:17 -07:00
James Grugett
789bec2a4f Expand replies by default (quick fix for comment links not working) 2022-10-12 17:49:04 -05:00
James Grugett
18042cd4d1 Return listener 2022-10-12 17:39:13 -05:00
mantikoros
04a126707b fix welcome dialog page freezing bug 2022-10-12 17:33:18 -05:00
mantikoros
7a412fdb0d add key props 2022-10-12 17:33:18 -05:00
James Grugett
e2dc4c6b8f Resolve markets again script 2022-10-12 17:30:21 -05:00
mantikoros
204d302d87 portfolio copy: "Open" => "Active" 2022-10-12 16:57:21 -05:00
James Grugett
ae39c1175b
Better resolve market payouts (#1038)
* Check payout preconditions first. Try to pay out market in 1 transaction.

* Format

* toBatch => lodash's chunk
2022-10-12 16:21:37 -05:00
Marshall Polaris
c44f223064
Fix some hydration issues (#1033)
* Extract `useIsClient` hook

* Fix a bunch of hydration bugs relevant to the contract page
2022-10-12 14:07:07 -07:00
mantikoros
aa717a767d backfill subsidyPool 2022-10-12 16:05:15 -05:00
Sinclair Chen
d9f57b7daa
Remove all daisy buttons (#1036)
* Tweak limit order UI and fix button

* Style all follow/unfollow buttons blue

also get rid of highlight-blue button

* remove all other uses of 'btn'

* Style group follow button like user follow

* lint and format
2022-10-12 12:27:17 -07:00
mantikoros
93ceaa52c4 payouts: fix subsidyPool undefined bug 2022-10-12 14:24:54 -05:00
James Grugett
de76557326 Show shares as contract card position. Fix bug on outcome 2022-10-12 13:55:58 -05:00
James Grugett
da1fcb646f Fix infinite re-render on home page 2022-10-12 13:42:54 -05:00
ingawei
1d618ba337
Inga/fr remove double comments (#1019)
incorporating answer comments into general comments section
2022-10-12 13:05:58 -05:00
Pico2x
2cda3a4d4f Don't show spinner if pinned items is undefined or zero 2022-10-12 18:12:32 +01:00
Sinclair Chen
e44fc8ae13
Simplify rich text to string parsing logic (#1022)
* Simplify rich text to string parsing logic

* lint
2022-10-12 10:09:59 -07:00
James Grugett
e6a90e18e4 Add more padding and improve layout of post card 2022-10-12 11:59:29 -05:00
Austin Chen
cee8caa3e8
Generate images from StableDiffusion (#1035)
* Generate images from StableDiffusion

* Update yarn.lock

* Log an error, remove extra comment

* Code cleanup

* Note about the API
2022-10-12 09:53:04 -07:00
Pico2x
b49264ddfa Show featured to all users 2022-10-12 17:44:43 +01:00
Pico2x
12ed569ff6 Update post card subtitle color 2022-10-12 17:33:02 +01:00
Pico2x
00acc262a0 Update post-cards to show profile pic correctly 2022-10-12 17:31:00 +01:00
Pico2x
fd7d4eb5e2 Make post-cards consistent with contract cards 2022-10-12 17:22:58 +01:00
Pico2x
8ae1166c49 Posts now have denormalized creator username and name 2022-10-12 16:42:28 +01:00
FRC
84e2b63c49
Entire homepage now loads simultaneously including featured (#1034) 2022-10-12 16:02:57 +01:00
FRCassarino
f19ef83ac2 Auto-remove unused imports 2022-10-12 14:38:59 +00:00
Pico2x
0c11f3b450 Fix is admin in featured 2022-10-12 15:38:06 +01:00
Pico2x
de9ffa2b52 Fix bug in which Featured section shows for everyone 2022-10-12 15:29:32 +01:00
Ian Philips
decb3213f6 Ignore cancel resolutions for proven correct 2022-10-12 08:05:55 -06:00
FRC
ff6278b147
Featured items to homepage (#1024)
* Featured items to homepage

* Fix nits
2022-10-12 15:04:39 +01:00
FRC
3fc53112b9
New implementation of market card embeddings (#1025)
* Grids of cards now implemented by rendering component instead of iframe

* Sinclair's nit
2022-10-12 13:24:22 +01:00
Marshall Polaris
59cdc9f776
Update FR colors, consolidate non-top answers into "Other" (#1031)
* Update FR colors, consolidate non-top answers into "Other"

* Fix answer panel coloration to not be weird and work on Firefox
2022-10-11 23:59:11 -07:00
ingawei
f587e0256d
standardizing red and green colors (#1032) 2022-10-12 01:58:20 -05:00
ingawei
1c209f68f6
de daisy sell button (#1030)
* de daisy sell button
2022-10-12 01:31:32 -05:00
ingawei
b4e7d88ed8
de daisied cancel limit bet button (#1029) 2022-10-12 01:13:43 -05:00
ingawei
b2cd6bbe03
Inga/de daisy follow button (#1028)
* de daisy follow button
2022-10-12 01:00:52 -05:00
ingawei
a6d5d5ad15
made create a post button not daisy (#1027)
yay no daisy
2022-10-12 00:24:59 -05:00
ingawei
beeca57d4e
getting rid of daisy for limit order button (#1026)
* getting rid of daisy for limit order button, got rid of betChoice in limit order panel
2022-10-12 00:09:45 -05:00
Ian Philips
fb8bd1acfb Handle slugs with and without leading / 2022-10-11 17:22:58 -06:00
Ian Philips
4215821f35 Fix firebase query for market creators 2022-10-11 16:59:47 -06:00
Ian Philips
a71c3d6a4a Fix notification link 2022-10-11 16:59:20 -06:00
James Grugett
cdcce421a8 Include today's bets in daily profit 2022-10-11 17:32:35 -05:00
Ian Philips
8beff6eb1f Format money 2022-10-11 15:36:35 -06:00
Ian Philips
f714918b88 Update functions readme with local dev details 2022-10-11 15:32:53 -06:00
James Grugett
946d74489f Gray scale position and profit 2022-10-11 16:29:15 -05:00
mantikoros
220d0841bd move liquidity to info dialog 2022-10-11 16:22:35 -05:00
James Grugett
9d44190b9a Fix group url nav to correct tabs 2022-10-11 16:04:40 -05:00
Sinclair Chen
3cdd790ae9
Autosave post, market, comment rich text (#1015)
* Fix freezing when typing big docs

* Make rich text fields autosave to localstorage

* Add autosave for comments

* delete vestigial text editor from challenges

* Clear autosave on submit post/market/comment

* lint
2022-10-11 12:52:27 -07:00
mantikoros
6c1ac89cbe typo 2022-10-11 14:28:10 -05:00
mantikoros
0d8a84ef06 re-order tournaments 2022-10-11 14:26:52 -05:00
James Grugett
d528566ffa People's=>Party #2 2022-10-11 13:46:58 -05:00
James Grugett
b0f8369d9c People's=>Party 2022-10-11 13:41:30 -05:00
James Grugett
721c18cf6c Add tournament for CCP 20th Congress 2022-10-11 13:37:40 -05:00
mantikoros
43b06ae6fa tip button: show number of tips 2022-10-11 13:30:30 -05:00
Ian Philips
bfdb5ae595 Cleanup comments 2022-10-11 10:26:37 -06:00
Ian Philips
274f7fa849 Send market closed notifications every 5 days 2022-10-11 10:22:38 -06:00
Ian Philips
d507c4092e Code for removing erroneous badges 2022-10-11 08:51:51 -06:00
Ian Philips
e970a908c6 Switch logic to includes 2022-10-11 08:35:59 -06:00
Pico2x
4fd0e5caad Fix bug in which new users are flagged as unreliable 2022-10-11 13:39:40 +01:00
James Grugett
70b2b14f80
Daily profit 💰 (#1023)
* Daily profit client side

* Filter out those where profit rounds to 0

* Tabs to spaces
2022-10-11 00:32:55 -05:00
mantikoros
0ec15ff2f8
Make liquidity great again (#1020)
* add subsidy

* drizzle liquidity

* update liquidity panel

* remove addliquidity

* update cloud functions index

* remove json endpoints

* imports

* drizzle liquidity: add velocity; dev script; run every minute

* adjust speed

* logging

* liquidity button, dialog

* modal size

* modal

* info table

* pay back excess liquidity

* remove client withdrawal

* house liquidity subsidy

* disable liquidity button if market resolved or closed

* format tip amount
2022-10-10 21:56:16 -05:00
Sinclair Chen
8bb9885aee
Fix @mention 500 error and can't close market bug (#1021)
* Fix @mention 500 error. Refactor text concat exts

* lint
2022-10-10 18:47:02 -07:00
Ian Philips
c46c384d1d Add more bot tags, better creator name scaling 2022-10-10 15:38:27 -06:00
Ian Philips
4f5c93be96 Badge notifications ui changes 2022-10-10 15:01:18 -06:00
James Grugett
f03e5d7af0
Refactor portfolio query (#1018)
* Fetch less data for portfolio query

* Rename var
2022-10-10 15:51:51 -05:00
Sinclair Chen
fb0a09664e delete bannerUrl from user type 2022-10-10 13:51:27 -07:00
Ian Philips
17d0fb7da6 Change badge award notif setting group 2022-10-10 14:41:24 -06:00
Ian Philips
867cdf2496 Only backfill unfilled users' badges 2022-10-10 14:34:06 -06:00
Ian Philips
f26ba1c4a2
Award badges for market creation, betting streaks, proven correct (#891)
* Award badges for market creation, betting streaks, proven correct

* Styling

* Add minimum unique bettors for proven correct

* Add name, refactor

* Add notifications for badge awards

* Correct styling

* Need at least 3 unique bettors for market maker badge

* Lint

* Switch to badges_awarded

* Don't include n/a resolutions in market creator badge

* Add badges by rarities to profile

* Show badges on profile, soon on market page

* Add achievements to new user

* Backfill all users badges
2022-10-10 14:32:29 -06:00
James Grugett
cdc64c6475 Correctly configure env var for firebase functions 2022-10-10 14:55:33 -05:00
James Grugett
5d561acdf8 Fix NaN 2022-10-10 14:23:16 -05:00
James Grugett
84f79ffe7c Remove undefined props 2022-10-10 13:34:02 -05:00
James Grugett
f6fd703005
Store each user's contract bet metrics (#1017)
* Implement most of caching metrics per user per contract

* Small group updates refactor

* Write contract-metrics subcollection

* Fix type error
2022-10-10 13:05:17 -05:00
mantikoros
b8ef272784 withdrawal warning 2022-10-10 13:01:57 -05:00
Ian Philips
a4699b79ed If unlisted during creation, fill in creator id 2022-10-10 10:48:01 -06:00
Ian Philips
66071e16fa Try without timeout seconds on pubsub 2022-10-10 09:53:42 -06:00
Ian Philips
b3136ebcac Update update-metrics timeout sends 2022-10-10 09:48:46 -06:00
Ian Philips
a143a96919 Fix unable to close contract 2022-10-10 09:24:37 -06:00
Ian Philips
dea65a4ba0 Better error handling for notification destinations 2022-10-10 07:41:41 -06:00
Ian Philips
a310963952 update prefs safely 2022-10-10 07:01:44 -06:00
Sinclair Chen
8d06e4b4d2
refactor text input into one component (#1016)
* Add responsive text input component

* Add styled expanding textarea component
2022-10-09 19:37:24 -07:00
James Grugett
dc51e2cf46 Rename updateMetrics to scheduleUpdateMetrics 2022-10-09 19:11:44 -05:00
sipec
4831c25ce0 Auto-prettification 2022-10-09 23:10:02 +00:00
Sinclair Chen
60f2552139 copy: Referrals -> Refer a friend 2022-10-09 16:09:21 -07:00
mantikoros
4b8d381da5 hide comment bounty when market closed or resolved 2022-10-09 17:14:20 -05:00
mantikoros
565177b76f track midterms, date docs 2022-10-09 17:02:34 -05:00
Sinclair Chen
8bd21c6693 hotfix %mention, add load-fail state 2022-10-08 22:52:36 -07:00
James Grugett
310a41d63e Make loan and bet streak links hoverable in notifications 2022-10-08 12:51:58 -05:00
mantikoros
e1636d0f13 update metrics: fix divide by zero, elasticity NaN bug 2022-10-08 12:16:45 -05:00
James Grugett
d00ea65279 Add MattP to winners for AMM liquidity exploit 2022-10-07 17:45:42 -05:00
James Grugett
60bb5379cb Update bounties doc 2022-10-07 17:30:20 -05:00
James Grugett
f3dedfb27a
Call updatemetrics v2 cloud function from scheduled function (#1014)
* Call updatemetrics v2 cloud function from scheduled function

* Set limits on bets and contracts loaded for portfolio page. Show warning if limit is hit

* mqp review: Use console.error if !response.ok
2022-10-07 17:10:12 -05:00
mantikoros
efa2e44937 comment bounty dialog, styling 2022-10-07 16:26:30 -05:00
mantikoros
84bc490ed3 comment sort: move below input, newest as default 2022-10-07 16:26:30 -05:00
James Grugett
443397b7dc
Action to merge main into main2 automatically 2022-10-07 15:13:57 -05:00
James Grugett
b57ff68654 Fix highlight & scroll to comment 2022-10-07 14:40:38 -05:00
Sinclair Chen
f0b35993c9 fix hydration error (button inside button) 2022-10-07 10:56:27 -07:00
James Grugett
8f56ccad22 Set limits on bets and contracts loaded for portfolio page. Show warning if limit is hit 2022-10-07 11:55:47 -05:00
mantikoros
9e289146af flat trade fee of M$0.1 aka bot tax 2022-10-06 23:04:48 -05:00
James Grugett
4285198f09 Merge branch 'main' into main2 2022-10-06 22:19:39 -05:00
James Grugett
f533d9bfcb
Verify balance of limit order "makers" (#1007)
* Fetch balance of users with open limit orders & cancel orders with insufficient balance

* Fix imports

* Fix bugs

* Fix a bug

* Remove redundant cast

* buttons overlaying content fix (#1005)

* buttons overlaying content fix

* stats: round DAU number

* made set width for portfolio/profit fields (#1006)

* tournaments: included resolved markets

* made delete red, moved button for regular posts (#1008)

* Fix localstorage saved user being overwritten on every page load

* Market page: Show no right panel while user loading

* Don't flash sign in button if user is loading

* election map coloring

* market group modal scroll fix (#1009)

* midterms: posititoning, make mobile friendly

* Un-daisy share buttons (#1010)

* Make embed and challenge buttons non-daisyui

* Allow link Buttons. Change tweet, dupe buttons.

* lint

* don't insert extra lines when upload photos

* Map fixes (#1011)

* usa map: fix sizing

* useSetIframeBackbroundColor

* preload contracts

* seo

* remove hook

* turn off sprig on dev

* Render timestamp only on client to prevent error of server not matching client

* Make sized container have default height so graph doesn't jump

* midterms: use null in static props

* Create common card component (#1012)

* Create common card component

* lint

* add key prop to pills

* redirect to /home after login

* create market: use transaction

* card: reduce border size

* Update groupContracts in db trigger

* Default sort to best

* Save comment sort per user rather than per contract

* Refactor Pinned Items into a reusable component

* Revert "create market: use transaction"

This reverts commit e1f24f24a9.

* Mark @v with a (Bot) label

* fix padding on daily movers

* fix type errors

* Wrap sprig init in check for window

* unindex date-docs from search engines

* Auto-prettification

* compute elasticity

* change dpm elasticity

* Fix google lighthouse issues (#1013)

* don't hide free response panel on open resolve

* liquidity sort

* Limit order trade log: '/' to 'of'. Remove 'of' in 'of YES'.

* Date doc: Toggle to disable creating a prediction market

* Listen for date doc changes

* Fix merge error

* Don't cancel all a users limit orders if they go negative

Co-authored-by: ingawei <46611122+ingawei@users.noreply.github.com>
Co-authored-by: mantikoros <sgrugett@gmail.com>
Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>
Co-authored-by: mantikoros <95266179+mantikoros@users.noreply.github.com>
Co-authored-by: Ian Philips <iansphilips@gmail.com>
Co-authored-by: Pico2x <pico2x@gmail.com>
Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: sipec <sipec@users.noreply.github.com>
2022-10-06 22:16:49 -05:00
Austin Chen
71b0c71729 Tag ArbitrageBot with bot badge 2022-10-06 21:52:55 -05:00
mantikoros
25333317b0 Show elasticity; volume tooltip 2022-10-06 21:52:55 -05:00
Austin Chen
42a7d04b4d Tag ArbitrageBot with bot badge 2022-10-06 20:17:34 -04:00
James Grugett
b1d386ca5a Listen for date doc changes 2022-10-06 18:54:22 -05:00
James Grugett
0dc8753a92 Listen for date doc changes 2022-10-06 18:50:53 -05:00
mantikoros
454f2d1417 Merge branch 'main' into main2 2022-10-06 18:48:56 -05:00
James Grugett
d846b9fb30 Date doc: Toggle to disable creating a prediction market 2022-10-06 18:42:56 -05:00
James Grugett
77e0631ea4 Limit order trade log: '/' to 'of'. Remove 'of' in 'of YES'. 2022-10-06 18:42:56 -05:00
James Grugett
badd67c278 Date doc: Toggle to disable creating a prediction market 2022-10-06 18:36:27 -05:00
mantikoros
80622dc7ee liquidity sort 2022-10-06 18:23:27 -05:00
James Grugett
9d12fa3af0 Limit order trade log: '/' to 'of'. Remove 'of' in 'of YES'. 2022-10-06 18:03:44 -05:00
Sinclair Chen
d9c8925ea0 don't hide free response panel on open resolve 2022-10-06 15:20:46 -07:00
Sinclair Chen
adb809f973
Fix google lighthouse issues (#1013) 2022-10-06 15:19:37 -07:00
mantikoros
a63405ca7c change dpm elasticity 2022-10-06 16:47:52 -05:00
mantikoros
7ca0fb72fc compute elasticity 2022-10-06 16:36:32 -05:00
sipec
ac37f94cf7 Auto-prettification 2022-10-06 20:50:29 +00:00
Sinclair Chen
bc5af50b0c unindex date-docs from search engines 2022-10-06 13:49:39 -07:00
James Grugett
4162cca3ff Wrap sprig init in check for window 2022-10-06 15:23:51 -05:00
mantikoros
91da39370f fix type errors 2022-10-06 14:54:22 -05:00
Sinclair Chen
2f2c586d5d fix padding on daily movers 2022-10-06 12:01:00 -07:00
Austin Chen
853e3e4896 Mark @v with a (Bot) label 2022-10-06 14:20:35 -04:00
Pico2x
edbd0feb37 Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-10-06 17:04:32 +01:00
Pico2x
59de979949 Refactor Pinned Items into a reusable component 2022-10-06 17:04:00 +01:00
mantikoros
b8d65acc3f Revert "create market: use transaction"
This reverts commit e1f24f24a9.
2022-10-06 10:54:42 -05:00
Ian Philips
26f04fb04a Save comment sort per user rather than per contract 2022-10-06 10:16:29 -04:00
Ian Philips
e127f9646a Default sort to best 2022-10-06 09:53:55 -04:00
Ian Philips
25ef17498a Update groupContracts in db trigger 2022-10-06 09:26:35 -04:00
mantikoros
68075db3da card: reduce border size 2022-10-05 22:20:51 -05:00
mantikoros
e1f24f24a9 create market: use transaction 2022-10-05 22:18:19 -05:00
mantikoros
cd8245fbee redirect to /home after login 2022-10-05 21:38:13 -05:00
mantikoros
f1e400765a add key prop to pills 2022-10-05 21:31:34 -05:00
Sinclair Chen
94624c5387
Create common card component (#1012)
* Create common card component

* lint
2022-10-05 18:02:24 -07:00
mantikoros
7ce09ae39d midterms: use null in static props 2022-10-05 19:24:03 -05:00
James Grugett
935bdd12a7 Make sized container have default height so graph doesn't jump 2022-10-05 18:44:30 -05:00
James Grugett
5d7721e041 Render timestamp only on client to prevent error of server not matching client 2022-10-05 18:44:30 -05:00
mantikoros
a149777c0e turn off sprig on dev 2022-10-05 18:36:32 -05:00
mantikoros
81fb2456bd remove hook 2022-10-05 18:29:08 -05:00
mantikoros
f8ec306ee9
Map fixes (#1011)
* usa map: fix sizing

* useSetIframeBackbroundColor

* preload contracts

* seo
2022-10-05 18:20:40 -05:00
Sinclair Chen
a53fb49ec3 don't insert extra lines when upload photos 2022-10-05 16:08:01 -07:00
Sinclair Chen
7863a4232d
Un-daisy share buttons (#1010)
* Make embed and challenge buttons non-daisyui

* Allow link Buttons. Change tweet, dupe buttons.

* lint
2022-10-05 15:51:10 -07:00
mantikoros
a3b841423f midterms: posititoning, make mobile friendly 2022-10-05 17:12:50 -05:00
ingawei
b8911cafe8
market group modal scroll fix (#1009) 2022-10-05 17:07:41 -05:00
mantikoros
60aa294131 election map coloring 2022-10-05 16:58:47 -05:00
James Grugett
0818a94307 Don't flash sign in button if user is loading 2022-10-05 16:56:47 -05:00
James Grugett
a3acd3fa3c Market page: Show no right panel while user loading 2022-10-05 16:52:16 -05:00
James Grugett
1ef1af8234 Fix localstorage saved user being overwritten on every page load 2022-10-05 16:49:28 -05:00
ingawei
189da4a0cf
made delete red, moved button for regular posts (#1008) 2022-10-05 16:21:03 -05:00
mantikoros
10f0bbc63d tournaments: included resolved markets 2022-10-05 15:46:20 -05:00
ingawei
2d56525d65
made set width for portfolio/profit fields (#1006) 2022-10-05 15:40:26 -05:00
mantikoros
f1f8082600 stats: round DAU number 2022-10-05 15:27:20 -05:00
ingawei
ec006f25c4
buttons overlaying content fix (#1005)
* buttons overlaying content fix
2022-10-05 15:19:10 -05:00
James Grugett
b40a114168 Print error from usePagination 2022-10-05 15:13:40 -05:00
Ian Philips
4bbadeb27c Parse images and iframes from tiptap to string descriptions 2022-10-05 14:08:51 -06:00
mantikoros
2596d54831 update tournaments page 2022-10-05 14:46:15 -05:00
Austin Chen
0df5497ffb Fix regression of unable to purchase mana 2022-10-05 15:35:02 -04:00
Ian Philips
27dabc193c Comment out logs 2022-10-05 13:09:40 -06:00
Ian Philips
6ec1b38a21 Add more stringent duplication req and popularity score 2022-10-05 13:07:23 -06:00
James Grugett
f35eb42d7b Make search query params work on page load 2022-10-05 13:27:46 -05:00
James Grugett
18f8ad433d Hide sort and query options when searching 2022-10-05 13:12:30 -05:00
Sinclair Chen
37e8f2ff5a
Make %mentions actually look like mentions (#993)
* Make contract mentions inline text

* Add tooltip for author, close time, volume

* Make pill wider
2022-10-05 11:11:05 -07:00
Ian Philips
328aa1457d Send interesting markets based on groups, follows, similar bettors 2022-10-05 10:41:57 -06:00
Austin Chen
b9ba3e75fa Show top 5k markets in sitemap 2022-10-05 11:49:47 -04:00
Austin Chen
70bfec2742 Improve sitemaps 2022-10-05 11:41:32 -04:00
Pico2x
26281556f7 Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-10-05 15:54:13 +01:00
Austin Chen
730abf584a Revert "Warn whenever rich text editor has unsaved changes"
This reverts commit 419219c703.
2022-10-05 10:54:04 -04:00
Pico2x
34d09316e0 Yscale now updates when zooming in on chart 2022-10-05 15:46:18 +01:00
Austin Chen
6f41ab8efd Keep unlisted state on duplicate 2022-10-05 10:38:23 -04:00
Pico2x
f1207e87ec Update share-modal.tsx 2022-10-05 15:18:53 +01:00
Pico2x
4e22b8e332 Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-10-05 15:01:44 +01:00
Pico2x
07de8cc86a Slightly less horrible color palette 2022-10-05 15:01:41 +01:00
akrolsmir
f07a022d63 Auto-remove unused imports 2022-10-05 13:58:54 +00:00
Austin Chen
d42ec42b0e Standardize on CopyLinkButton 2022-10-05 09:55:46 -04:00
Austin Chen
6fa4e17a58 Remove "Add my comment" button for signed out users 2022-10-05 09:35:21 -04:00
Austin Chen
af3a3a3934 Clean up style on Get M$ 2022-10-05 09:30:51 -04:00
Pico2x
9e3477970d Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-10-05 14:19:38 +01:00
Pico2x
3390c34d0a Mobile tooltip isn't occluded by finger anymore 2022-10-05 14:19:26 +01:00
Austin Chen
419219c703 Warn whenever rich text editor has unsaved changes 2022-10-05 09:16:05 -04:00
Pico2x
8aaca848b2 Updating group name works again. 2022-10-05 14:02:17 +01:00
Pico2x
e4d7d0a232 Mobile graphs no longer show the zoom function. 2022-10-05 13:57:45 +01:00
Pico2x
e9050973e1 Update post-comments.tsx 2022-10-05 11:53:59 +01:00
FRC
83d9a1f3e2
Posts changes (#988)
* Add post subtitle

* Add "Post" badge to post card

* Move post tab to overview tab, refactor components

* Fix styling nits.
2022-10-05 11:37:23 +01:00
Marshall Polaris
49e97ddac1
Small add-on React 18 fixes (#1004)
* Bump React types for main code to 18

* Replace react-beautiful-dnd with maintained fork

* Add a type annotation
2022-10-05 00:23:23 -07:00
Marshall Polaris
a9d5dd7fc8
Memoize FeedBet component (#999) 2022-10-04 23:17:09 -07:00
Marshall Polaris
ddb186dd98
Change Tipper interface, memoize FeedComment (#1000)
* Change `Tipper` interface, memoize `FeedComment`

* Fixup per James feedback

* More fixup per James feedback
2022-10-04 23:16:56 -07:00
Marshall Polaris
d2273087cf
React 17.0.2 -> 18.2.0 (#1003)
* React 17.0.2 -> 18.2.0

* Adjust title tag to only have one text node (no internal spaces)

* react-expanding-textarea 2.3.5 -> 2.3.6
2022-10-04 23:16:39 -07:00
James Grugett
6a0b577aeb Small home refactor 2022-10-05 00:54:38 -05:00
James Grugett
ca6197c7bb Show total loan amount on portfolio 2022-10-05 00:33:05 -05:00
Sinclair Chen
ed6ea011c2 copy: "Dating docs" -> "Dating" 2022-10-04 18:33:26 -07:00
James Grugett
83d33792aa Loan repaid => Loan payment 2022-10-04 20:10:26 -05:00
sipec
583c5b225e Auto-remove unused imports 2022-10-05 00:49:36 +00:00
Sinclair Chen
9f256aa7a8 refactor: require label on buy/confirm buttons 2022-10-04 17:48:24 -07:00
mantikoros
7a271fce29 fix button 2022-10-04 19:35:53 -05:00
James Grugett
d8ef363f06 Add profit amount to sell dialog. 2022-10-04 19:35:29 -05:00
James Grugett
8043fa515a Show loan repaid in sell dialog 2022-10-04 19:10:45 -05:00
mantikoros
f551e6c469 market close styling 2022-10-04 19:07:06 -05:00
Sinclair Chen
3f0b665753 Add Mriya to charities list 2022-10-04 16:57:05 -07:00
Sinclair Chen
40b07329bd Make follow & unfollow buttons same size 2022-10-04 16:42:17 -07:00
Ian Philips
7b9aeea0bd Ignore similar bettor's followed user's markets 2022-10-04 17:12:07 -06:00
Ian Philips
935ff7b97a
Personalized interesting markets emails [WIP] (#1001)
* Test new personalized emails in prod - logs only

* fix import
2022-10-04 16:47:06 -06:00
ingawei
c115b5cca7
Inga/multiple colors (#994)
* making FR bars smoller
2022-10-04 17:36:03 -05:00
Marshall Polaris
d6bb27f97c
Fix graph area to invert at 0 (#998) 2022-10-04 14:13:53 -07:00
Marshall Polaris
bbce3e873e
Tooltip follows marker on charts with marker (#997)
* Renaming of some tooltip stuff

* Tooltip follows marker on single value history charts
2022-10-04 14:02:44 -07:00
mantikoros
26f5e506b7 sell button warning 2022-10-04 15:53:00 -05:00
ingawei
5adaa7253f
made slice skinnier (#996) 2022-10-04 15:41:48 -05:00
Marshall Polaris
a55d85d4b6
Implement basic graph tooltip slice marker thingy (#995) 2022-10-04 12:55:51 -07:00
Ian Philips
f085df96e3 Allow wider group pills on mobile 2022-10-04 09:23:07 -06:00
Ian Philips
1d2af2900b Remove spaces in hashtags & line clamp metadata 2022-10-04 09:14:27 -06:00
Ian Philips
a48cec63fc Use line-clamp in sharing card 2022-10-04 09:06:23 -06:00
Ian Philips
e6374c4994 Fix title, send out the remaining emails today 2022-10-04 08:55:44 -06:00
Ian Philips
6ac467764d Fix unsubscribe all update 2022-10-04 08:38:20 -06:00
Ian Philips
79af4b2be0 Compare to the const ya sillyhead 2022-10-04 08:11:04 -06:00
Ian Philips
094bcaea17 Convert confirmation daisy buttons to tailwind 2022-10-04 08:03:21 -06:00
Ian Philips
c6e5e04e65 Convert confirmation daisy buttons to tailwind 2022-10-04 08:02:20 -06:00
Pico2x
ee4d3947b8 moar contracts in mentions 2022-10-04 14:38:07 +01:00
Marshall Polaris
45b281fac5 Kill a console log 2022-10-04 01:34:17 -07:00
Marshall Polaris
31c6cb7739
Rewrite portfolio graphs with new machinery (#986)
* Fix chart `onMouseOver` propagation

* Make generic charts support money on y-axis

* Fix somewhat ridiculous `formatMoney` to work with negative amounts

* Make margins on charts configurable

* Implement color as function of point on SingleValueHistoryChart

* Rewrite portfolio history graphs with new graph machinery

* Toast Nivo
2022-10-04 01:18:22 -07:00
mantikoros
23ca3ff56a order book label 2022-10-03 23:56:39 -05:00
Sinclair Chen
c3ffac34a1
Remove tag parsing (#956)
* Remove #tag parsing

* Remove #tag linkifying

* lint
2022-10-03 18:28:21 -07:00
Sinclair Chen
375a4e089f
Hide spoiler content in emails / notifs (#957) 2022-10-03 18:25:44 -07:00
James Grugett
efd83eaad4 Home: Don't show duplicate contracts across groups 2022-10-03 18:28:41 -05:00
James Grugett
8d70dc4800 Increase trending group count again. (It's uglier, but it's way more useful!) 2022-10-03 18:22:18 -05:00
Ian Philips
a1dcf8d168 Add best sort to FR contracts 2022-10-03 16:38:12 -06:00
James Grugett
84aaeece9f Filter out unlisted from trending, new, and daily trending on home page 2022-10-03 17:33:51 -05:00
James Grugett
27d765a4a1 Add most popular sort 2022-10-03 17:28:31 -05:00
James Grugett
5214f27be3 Fix daily prob showing global daily prob 2022-10-03 17:23:22 -05:00
mantikoros
d0d223f7ad Auto-prettification 2022-10-03 22:21:56 +00:00
mantikoros
0c9226de41 EditableCloseDate: add pencil icon 2022-10-03 17:20:53 -05:00
ingawei
ce48016f80
added mana cannot be traded for real money disclaimer in welcome modal (#990) 2022-10-03 16:38:10 -05:00
Marshall Polaris
1515d8cab2 Fix lint on Austin's script 2022-10-03 14:30:34 -07:00
Marshall Polaris
28cad9caf8
Bump heroicons yarn.lock version to 1.0.6 (#992) 2022-10-03 14:22:31 -07:00
Marshall Polaris
9a950dc080
Mark scripts as modules to avoid global name collision (#991) 2022-10-03 14:21:21 -07:00
Ian Philips
42cc07e4a6 Hide market title in notifs if grouped 2022-10-03 14:59:27 -06:00
Ian Philips
a5490c903f Reduce streak and max to 5, 25 respectively 2022-10-03 14:52:21 -06:00
Ian Philips
71975f307c Show resolve loading indicator 2022-10-03 14:33:00 -06:00
Ian Philips
ae4136348d Unique bettors email default on 2022-10-03 14:12:55 -06:00
Sinclair Chen
67de983aac Fix links in spoilers 2022-10-03 11:55:10 -07:00
Sinclair Chen
59b128dbe7
Redo add funds modal without daisy and actually use it (#963) 2022-10-03 10:15:58 -07:00
Ian Philips
074a1fdde2 Hide sprig on non-prod envs 2022-10-03 10:59:18 -06:00
Austin Chen
7c34805eeb Upload script to bulk-resolve markets from API 2022-10-03 12:52:48 -04:00
Pico2x
77a5f8b9dd Revert the merge revert (double revert) 2022-10-03 17:31:07 +01:00
Ian Philips
5ae9049295 Show resolution panel above recommented markets 2022-10-03 10:11:24 -06:00
Ian Philips
d5d1284306 Properly handle null id 2022-10-03 09:57:27 -06:00
Ian Philips
adb8bc476f Show whether market is unlisted 2022-10-03 09:36:49 -06:00
Ian Philips
f92f098f82 Allo creators to unlist markets 2022-10-03 09:26:39 -06:00
Ian Philips
370edec890 Remove unsubscribe options for market closure 2022-10-03 08:30:21 -06:00
FRC
f5a3abf0bc
Add spinner (#987) 2022-10-03 15:27:15 +01:00
Ian Philips
27e6534d94 Persist preferred comment sort order by contract 2022-10-03 08:15:27 -06:00
Ian Philips
1caf75d3b5 Do not refund comment bounties 2022-10-03 07:49:26 -06:00
Ian Philips
051c2905e1
Allow user to opt out of all unnecessary notifications (#974)
* Allow user to opt out of all unnecessary notifications

* Unsubscribe from all response ux

* Only send one response
2022-10-03 07:41:39 -06:00
Pico2x
1f7b9174b3 Update index.tsx 2022-10-03 13:45:38 +01:00
FRC
06571a3657
Flag incorrectly resolved markets, warn about unreliable creators (#945)
* Flag incorrectly resolved markets, warn about unreliable creators

* Address James' review nits

* Added a loading state and some copy-changes

* Fix missing refactor

* Fix vercel error

* Fix merging issues
2022-10-03 10:49:19 +01:00
Pico2x
3fb43c16c4 Revert "Merge branch 'main' of https://github.com/manifoldmarkets/manifold"
This reverts commit 603201a00f, reversing
changes made to b517817ee3.
2022-10-03 10:02:38 +01:00
Pico2x
603201a00f Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-10-03 08:47:23 +01:00
Pico2x
b517817ee3 Fix indentation iphone 2022-10-03 08:47:21 +01:00
James Grugett
80693620f0 Make alt contract card listen for updates 2022-10-02 22:46:04 -05:00
mantikoros
f1ae54355d cowp: pointer cursor 2022-10-02 20:44:24 -05:00
Marshall Polaris
503038d2a2 Fix a dumb bug on pseudo-numeric charts 2022-10-02 16:59:49 -07:00
Marshall Polaris
bf8dca25b2
Rewrite stats graphs using new machinery (#985)
* Make curve configurable on generic charts

* Extract SizedContainer helper component

* Use new charts for stats page

* Move analytics charts component

* Fix up start date logic for graphs excluding data
2022-10-02 16:56:29 -07:00
James Grugett
a82f447965 Fix free response comment threading 2022-10-02 18:20:37 -05:00
FRC
1f8c72b4c9
Overview page on groups (#961)
* Frontpage on groups

wip

* Fix James's nits
2022-10-03 00:02:31 +01:00
James Grugett
40c51c3d59 Add emojis to /labs 2022-10-02 17:14:11 -05:00
James Grugett
86ceea831b Add stats to /labs 2022-10-02 17:10:18 -05:00
mantikoros
efb9ef7602 add padding to embeds 2022-10-02 17:04:28 -05:00
James Grugett
8c1131ebab Tweak home search bar spacing on mobile 2022-10-02 17:04:04 -05:00
mantikoros
2c223160ed comment button styling 2022-10-02 16:58:04 -05:00
mantikoros
11bd658c68 hide comment sort, trade tab if no items 2022-10-02 16:58:04 -05:00
James Grugett
39638a3888 Update mtg link 2022-10-02 15:27:29 -05:00
James Grugett
234820ecd4 Add /labs SEO 2022-10-02 15:24:02 -05:00
James Grugett
4d996c2476 Margin tweak 2022-10-02 15:23:16 -05:00
mantikoros
9ecf10496c Auto-remove unused imports 2022-10-02 20:17:27 +00:00
mantikoros
42b27fcedd update midterms dashboard 2022-10-02 15:15:37 -05:00
jahooma
7bf59bcdd0 Auto-prettification 2022-10-02 20:15:07 +00:00
James Grugett
043b18da0e Add referral link to your user page 2022-10-02 15:13:03 -05:00
mantikoros
64951e691e update midterms dashboard 2022-10-02 15:11:40 -05:00
James Grugett
9a90cc3835 Move manalinks into labs 2022-10-02 15:03:29 -05:00
James Grugett
10e361bcac Load daily movers at top level as well 2022-10-02 14:59:02 -05:00
James Grugett
a7f6cb7cfa Fix labs layout 2022-10-02 14:51:28 -05:00
James Grugett
359a768e14 Move challenges into /labs 2022-10-02 14:49:08 -05:00
James Grugett
42aea03415 Add search bar to home 2022-10-02 14:41:44 -05:00
James Grugett
0fb263efa4 Revert "Test loading user from localstorage on first render"
This reverts commit 701d0a06cd.
2022-10-02 14:16:54 -05:00
James Grugett
747977556b Add /labs to More menu 2022-10-02 14:13:19 -05:00
James Grugett
37e8cfbbed Tweak padding 2022-10-02 14:12:33 -05:00
James Grugett
701d0a06cd Test loading user from localstorage on first render 2022-10-02 14:08:05 -05:00
Sinclair Chen
0ffd6c129a
Make small embeds into cards (#976)
* Fix embed style (adjust input, strikethrough)

* Turn small embeds into contract cards

* Use media query instead of conditional render

* Open embed card clicks in new tab
2022-10-02 11:55:47 -07:00
James Grugett
758dbfe398 Add labs cards 2022-10-02 13:51:42 -05:00
James Grugett
33dfce3e16 Remove dating docs from More menu 2022-10-02 13:43:14 -05:00
James Grugett
af66d94c84 Manifold labs 2022-10-02 13:42:44 -05:00
mantikoros
290a34bc64 useTracking 2022-10-02 13:39:29 -05:00
mantikoros
4c2f9011d0 track embed hostname 2022-10-02 13:39:19 -05:00
mantikoros
57b592b5aa show toast after comment tips 2022-10-02 12:55:58 -05:00
mantikoros
fd31b7eaca set comment sort default to newest 2022-10-02 12:50:49 -05:00
Sinclair Chen
1d645e5ff8 trim copy on sort & bounty tooltips 2022-10-02 08:52:53 -07:00
mantikoros
0b0b84a6ad show tips on own comments again 2022-10-01 16:22:19 -05:00
mantikoros
2baae33a77 show market tip total 2022-10-01 16:16:34 -05:00
mantikoros
fac87f8e0c tips: display total 2022-10-01 16:10:17 -05:00
mantikoros
670c6faea8 tip button: remove border color 2022-10-01 16:00:39 -05:00
mantikoros
09e4864b32 consistent tip amount (M$10) 2022-10-01 15:57:47 -05:00
mantikoros
a445d9b7fa make tip button green 2022-10-01 15:54:14 -05:00
mantikoros
cb613705e9
Consistent tips (#984)
* consistent tip button

* hide tips for self

* prettier
2022-10-01 15:51:08 -05:00
mantikoros
aeeb47bdbe don't block on tipping 2022-10-01 15:06:09 -05:00
mantikoros
0844e5620a create: remove visilbity section 2022-10-01 14:30:31 -05:00
mantikoros
2d6fe308b8 better group sort 2022-10-01 14:14:03 -05:00
James Grugett
759685258a Turn off autofocus for amount input. (Fixes FR answer bug; IMO better UX) 2022-10-01 13:48:13 -05:00
James Grugett
b53e4acea6 API: Cache markets for 15 seconds at least 2022-10-01 13:37:56 -05:00
Marshall Polaris
2f1221f094
Size-aware chart tooltip positioning (#980) 2022-10-01 00:10:17 -07:00
mantikoros
2f3ae5192e embed: disable clicking contract details 2022-09-30 20:30:45 -05:00
James Grugett
b0b1d72ba6 Cleaner home page loading! 2022-09-30 20:07:50 -05:00
Marshall Polaris
dc0b6dc6a6
Don't render stuff whenever window size changes (#978) 2022-09-30 18:01:48 -07:00
Marshall Polaris
89e26d077e
Clean up chart sizing code (#977)
* Clean up chart sizing code

* Do all the chart sizing work in same batch
2022-09-30 16:57:48 -07:00
Marshall Polaris
38b7c898f6
More refactoring to make chart tooltips more flexible (#975) 2022-09-30 16:16:04 -07:00
Austin Chen
1fc2f15dae Try extending /stats to 180 days 2022-09-30 18:46:54 -04:00
Sinclair Chen
3d146dd57d decrease trending group count 2022-09-30 14:52:51 -07:00
ingawei
a219680701
Inga/scroll to top (#965)
- adding scroll to top button for markets, removing predict button at the bottom of comments
2022-09-30 15:16:27 -05:00
James Grugett
1e2df99054 Change format money to round up if within epsilon 2022-09-30 15:05:49 -05:00
Sinclair Chen
37beb584ef fix comment bounty overflow style 2022-09-30 12:54:48 -07:00
IanPhilips
9815e7301f Auto-prettification 2022-09-30 19:48:04 +00:00
Ian Philips
ac97e62f2e Add portfolio updates to notification settings 2022-09-30 13:45:57 -06:00
mantikoros
17d1b8575c comment bounty styling 2022-09-30 14:45:23 -05:00
Ian Philips
a25acbe1db Parse ian's email prefs on dev 2022-09-30 13:36:34 -06:00
Phil
b2f81c1149
Twitch minor fix (#973)
* Made Twitch copy link buttons links so right-click -> copy URL works.

* Added Twitch OBS screenshot to public folder.
2022-09-30 20:01:51 +01:00
James Grugett
9d81e3b6d1 Fix import 2022-09-30 13:22:10 -05:00
James Grugett
ab883ea777 Order home group sections by daily score. 2022-09-30 12:00:16 -05:00
Ian Philips
3677de58c3 Add tooltip and badge on contract for bounties 2022-09-30 10:00:55 -06:00
Ian Philips
31de3636fd Fix comment tab title 2022-09-30 09:34:58 -06:00
Ian Philips
a90b765670
Bounty comments (#944)
* Adding, awarding, and sorting by bounties

* Add notification for bounty award as tip

* Fix merge

* Wording

* Allow adding in batches of m250

* import

* imports

* Style tabs

* Refund unused bounties

* Show curreantly available, reset open to 0

* Refactor

* Rerun check prs

* reset yarn.lock

* Revert "reset yarn.lock"

This reverts commit 4606984276.

* undo yarn.lock changes

* Track comment bounties
2022-09-30 09:27:42 -06:00
Ian Philips
55f854115c Remove green circle from resolution prob input 2022-09-30 08:48:33 -06:00
Ian Philips
138f34fc66 Add close now button to contract edit time 2022-09-30 08:40:46 -06:00
Ian Philips
c16e5189f7 Don't send portfolio email to user less than 5 days old 2022-09-30 07:53:47 -06:00
Marshall Polaris
1bc1debbe8 Fix default sizes on charts to make more sense 2022-09-30 00:05:36 -07:00
Marshall Polaris
608ee7b865
Chart visual style adjustment (#971)
* Adjust area fill opacity on line charts

* Light gray border on tooltips
2022-09-30 00:03:31 -07:00
mantikoros
95c47aba1a midterms: add CO, additional markets 2022-09-30 01:30:45 -05:00
James Grugett
f892c92e26 Save portfolio sort and filter to local storage! 2022-09-30 01:11:04 -05:00
Marshall Polaris
7e91133229
Change styles on contract tooltips to be more like portfolio graph (#966) 2022-09-29 22:45:51 -07:00
Marshall Polaris
523689b525
Keep tooltip within bounds of chart (well, for non-FR charts) (#970) 2022-09-29 22:45:31 -07:00
ingawei
b83e5db563
getting rid of daisy buttons (#969)
* getting rid of daisy buttons so bet button does not turn black on mobile
2022-09-30 00:41:22 -05:00
James Grugett
13b3613460 Show number of limit orders 2022-09-29 23:57:45 -05:00
Marshall Polaris
715bae57e0
Fix date memoization in charts (#972)
* Memoize on numbers, not dates

* Use numbers instead of dates to calculate visible range
2022-09-29 21:35:20 -07:00
Marshall Polaris
5b5a919ed7
Expose onMouseOver chart event to hook into from outside (#967) 2022-09-29 20:18:33 -07:00
Ian Philips
2625ab1549 Portfolio email ux 2022-09-29 18:13:33 -06:00
ingawei
262183e0e6
Inga/quick toggle fix (#964)
getting rid of unused component
2022-09-29 18:53:36 -05:00
Sinclair Chen
b7df1a7043
Add ||spoilers|| (#942)
* Add ||spoilers||
* Add spoiler button to format menu
2022-09-29 14:28:04 -07:00
Marshall Polaris
8929b2e6ba
Improve typing for chart tooltip stuff (#962) 2022-09-29 12:51:38 -07:00
mantikoros
9fc1e855ff portfolio graph: put profit first 2022-09-29 13:53:43 -05:00
Pico2x
1755fb15d4 SEO for posts 2022-09-29 19:38:36 +01:00
Olivia Appleton
1e6b72059e
Expose multiple choice answer probabilities (#939)
* Expose multiple choice answer probabilities

* Run prettier

* Update api.md

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-29 14:17:52 -04:00
Olivia Appleton
2d1fd07834
Add documentation for newer market types (#934) 2022-09-29 13:27:07 -04:00
Ian Philips
ec1a9fab77 Show change in M$ 2022-09-29 13:06:12 -04:00
James Grugett
2cc08ba9e7 Daily movers cleanup 2022-09-29 11:42:17 -05:00
Ian Philips
35aa6c0429 Test sample of users' portfolios 2022-09-29 12:32:47 -04:00
Ian Philips
cd7ddae133 Add profit of bets made within last week 2022-09-29 12:30:58 -04:00
Sinclair Chen
46fab105d9 Fix tipper icon progression 2022-09-29 07:26:19 -07:00
Sinclair Chen
4cc985634a Put slider z-index under bottom menu 2022-09-29 07:04:11 -07:00
Marshall Polaris
15cd8b1f94
Fix a couple small chart bugs (#960)
* Fix time clamping causing little visual glitch

* Fix tick formatting glitch
2022-09-28 23:27:42 -07:00
Marshall Polaris
8862425120
Clean up chart tooltip handling (#959) 2022-09-28 21:43:04 -07:00
Marshall Polaris
be010da9f5
Refactor chart tooltip stuff, add bet avatar to tooltips (#958)
* Use objects instead of tuples for chart data

* Carry bet data down into charts

* Refactor to invert control of chart tooltip display

* Jazz up the chart tooltips with avatars

* Tidying
2022-09-28 21:14:34 -07:00
Marshall Polaris
7f7e7acd61
Make binary and pseudonumeric charts re-render less on contract diff (#955) 2022-09-28 18:03:30 -07:00
Sinclair Chen
1f2c7271b7 put edit profile z-index below side menu 2022-09-28 14:30:00 -07:00
Marshall Polaris
83de206e9e
Simply don't print zero (#954) 2022-09-28 14:20:28 -07:00
James Grugett
d55cedb36c Load comments via static props 2022-09-28 13:11:26 -04:00
James Grugett
eb762d9b9e Make loading more sequential for updateMetrics to prevent firebase error. 2022-09-28 12:28:40 -04:00
James Grugett
dba938032f Listen for updates on daily mover contract 2022-09-28 12:28:40 -04:00
ingawei
7c8e977d60
order book things (#953)
Adding order book to limit orders in mobile modal. This is pretty ugly and just a quick fix because people are complaining.
2022-09-28 09:04:47 -05:00
ingawei
e0e6838711 Auto-remove unused imports 2022-09-28 13:46:41 +00:00
ingawei
513cf7b290 added order book 2022-09-28 06:45:32 -07:00
Marshall Polaris
89c3ea559c
Clamp time range in history chart scales (#952) 2022-09-28 01:18:11 -07:00
Marshall Polaris
9238b20242
Modularize d3 imports (#951) 2022-09-28 01:00:39 -07:00
Marshall Polaris
925a9e850f
Hack up brush rendering to fix possible Chrome bug (#950) 2022-09-28 00:58:51 -07:00
Marshall Polaris
8f88af4e2a
Fix an edge case with chart mouseover tooltips (#949) 2022-09-28 00:56:43 -07:00
Marshall Polaris
5b54e7d468
Limit max width of FR legend tooltip labels (#948) 2022-09-27 22:25:37 -07:00
Pico2x
f52127237e COWP for cows 2022-09-28 01:21:38 -04:00
Pico2x
95f2604479 Cowp SEO friendly 2022-09-28 01:04:38 -04:00
Pico2x
a5b943965c Create cowp.tsx 2022-09-28 00:59:24 -04:00
Marshall Polaris
c16adb9ec9
Fix potential clock sync issues with graph updating (#947) 2022-09-27 21:18:22 -07:00
Marshall Polaris
e0d9b4d335
Rewrite contract graphs (#935)
* Fiddle around with everything, WIP FR charts

* Implement numeric chart

* Reorganize everything into neat little files

* Add `AreaWithTopStroke` helper

* Tidying, don't gratuitously use d3.format

* Remove duplicate code

* Better tooltip bisection

* `NumericPoint` -> `DistributionPoint`

* Add numeric market tooltip

* Make numeric chart bucket points less wrong

* Clean up numeric bucket computation

* Clean up a bunch of tooltip stuff, add FR legend tooltips

* Fix a dumb bug

* Implement basic time selection

* Fix fishy Date.now inconsistency bugs

* Might as well show all the FR outcomes now

* Make tooltips accurate on curveStepAfter charts

* Make log scale PN charts work properly

* Adjust x-axis tick count

* Display tooltip on charts only for mouse

* Fix up deps

* Tighter chart tooltips

* Adjustments to chart time range management

* Better date formatting

* Continue tweaking time selection handling to be perfect

* Make FR charts taller by default
2022-09-27 20:24:42 -07:00
James Grugett
9dc0d1696e Fix bug 2022-09-27 19:36:32 -04:00
James Grugett
a7abdbb1db Add to dating group 2022-09-27 19:10:35 -04:00
James Grugett
13dad9a10c Date doc: Remove photo as first-class feature 2022-09-27 19:03:14 -04:00
Austin Chen
14c008234a Script: Add liquidity to all markets in a group 2022-09-27 18:55:30 -04:00
Austin Chen
b87e29d7c0 Rename script 2022-09-27 18:55:30 -04:00
James Grugett
3ed29877ce Add dating docs to menu bar 2022-09-27 18:55:08 -04:00
mantikoros
80d4bffc95
US Elections map (#943)
* usa map

* state election map

* senate midterms

* iframe

* fix

* /midterms

* listen for updates
2022-09-27 17:50:43 -05:00
James Grugett
b21daa1248
Date docs on Manifold (#941)
* Date docs

* Create date doc

* Create and show a date market as well

* Move url to date-docs

* Date doc individual page

* Add share button

* Edit date docs

* Layout

* Add comments for create-post

* Add comments and back nav

* Fix urls

* Tweaks
2022-09-27 17:30:07 -05:00
Austin Chen
419c7ab636 Navigate to ?tab=portfolio 2022-09-27 17:16:48 -04:00
Barak Gila
e2047210b7
add to queue rather than invoking sprig object directly, as it's still being setup (#940) 2022-09-27 15:13:11 -04:00
mantikoros
5e34b5a911 greyscale bet button if outcome is undefined 2022-09-27 13:15:13 -04:00
mantikoros
723d9dbece
Better bet summary (#936)
* show position, expected value, profit instead of "invested"

* move bet summary outside trades on market page

* refactor

* pass in userbets

* hide only if no bets; show invested on desktop

* various
2022-09-27 12:09:54 -05:00
Barak Gila
7ba19c274b
basic sprig integration with possible page URL events (#932)
* basic sprig integration with possible page URL events

* iteration 0

* iteration 1

* run prettier; attempt to remove expect error

* readd expect error messages

* typescript comment fixes

* add identify

* remove package-lock.json

* extract to separate file

* fix linting

* fix lint

* fix lint

* fix missing config
2022-09-27 12:02:03 -05:00
ingawei
a12ed78813
Getting rid of console log, fixing multiple choice markets (#938) 2022-09-26 23:48:00 -05:00
mantikoros
aa93ec060d user page: put markets first 2022-09-27 00:46:30 -04:00
ingawei
fd90bc353b multiple choice betting fix 2022-09-26 21:40:56 -07:00
ingawei
e17a59ae23
Inga/mobilebetting (#911)
* mobile binary betting
2022-09-26 19:28:54 -05:00
ingawei
2fe9fe593d
Inga/profile (#937)
- Changed edit profile button
- got rid of banner
- merged stats and trades tab on profile
- made multicolored profit graph
2022-09-26 18:01:13 -05:00
Ian Philips
d612192109 Send market close notifs for each close time 2022-09-26 18:13:15 -04:00
SirSaltyy
13cffcdaf1 Merge branch 'main' of https://github.com/manifoldmarkets/manifold 2022-09-26 18:12:28 -04:00
SirSaltyy
1b9811ce28 Update twitch page copy 2022-09-26 18:12:24 -04:00
Ian Philips
3ed3b6fb42 Set email sent flag if skipped over 2022-09-26 18:05:50 -04:00
Ian Philips
f7bf42d2e0 Rename & correct spelling 2022-09-26 17:54:48 -04:00
Ian Philips
df316fc4da
Portfolio update emails (#928)
* Stats computing correctly

* Styles propagating - testing in prod now

* Formatting html

* Reset portfolio flag on mondays at 12am

* Add profit, styling

* More styling, less reports

* Cleanup

* Comments

* comment

* Try to send higher signal emails

* Send emails to proper email address
2022-09-26 17:49:06 -04:00
James Grugett
2ef025a151 Only set daily score on contracts that are at least day old 2022-09-26 17:43:27 -04:00
James Grugett
90eaf83775 Redirect from '/home' to '/' if not logged in 2022-09-26 17:04:08 -04:00
Ian Philips
94ffac287e Payout resolution notifications styling 2022-09-26 15:57:38 -04:00
Ian Philips
a10e4c115e Fix dpm MULTI resolution payouts bug 2022-09-26 15:57:21 -04:00
Ian Philips
cc3b44891b Add user to market followers in create answer 2022-09-26 15:56:47 -04:00
Ian Philips
d9292f7a95 Switch order of my groups and all tabs 2022-09-26 11:30:41 -04:00
Ian Philips
bf92c4fb06 Fix 500 on non-existant group page 2022-09-26 11:28:54 -04:00
James Grugett
68120ec2b2 Revert "Clean up and fix stuff on answers panel (#914)"
This reverts commit 721448f408.
2022-09-25 23:29:13 -04:00
Marshall Polaris
be2c60d3f3
Fix some rendering issues on contract page (#933)
* Memoize calculating sale amount on your bets list

* Don't re-render more than necessary with `useIsMobile` hook

* Use `useIsMobile` hook in `AmountInput`
2022-09-25 16:43:53 -07:00
Austin Chen
c1c3a360fd Add CART contest to /tournaments 2022-09-25 13:08:34 -04:00
Austin Chen
ae4d49d960 Generate markets for the Criticism and Red Teaming contest 2022-09-25 11:29:59 -04:00
James Grugett
21c7130d3b Filter out markets with undefined probChanges in dev 2022-09-23 18:58:05 -04:00
Marshall Polaris
d990bc2f07
Remove images config from next.config.js (#931) 2022-09-23 14:55:27 -07:00
Marshall Polaris
e2a8df6c3a
Nivo 0.74.0 -> 0.80.0 (#929) 2022-09-23 14:55:17 -07:00
Marshall Polaris
96dc060a0a
Move react-masonry-css dependency to web package.json (#930) 2022-09-23 14:55:06 -07:00
Austin Chen
d04304bdac Fix blank page on nav to groups 2022-09-23 17:04:32 -04:00
Austin Chen
2891a47d8c Support navigating to /about pages 2022-09-23 16:49:14 -04:00
James Grugett
490734db00 If no user, show loading on home 2022-09-23 16:43:23 -04:00
James Grugett
77ddc456a2 Add new home section to top. 2022-09-23 16:39:17 -04:00
James Grugett
1a5dcdedcc Delay prefetch by 1000ms. Don't prefetch portfolio history. 2022-09-23 16:30:44 -04:00
James Grugett
0ab82a7bd4 Delete some unused code 2022-09-23 15:40:48 -04:00
James Grugett
deb8397ee9 Add Daily Trending section (daily-score for you.) Remove recently updated 2022-09-23 15:33:50 -04:00
James Grugett
57190e7876 Daily trending sort option 2022-09-23 15:33:50 -04:00
FRC
5a10132e2b
Add a "Posts" tab to groups (#926)
* Add a "Posts" sidebar item to groups

* Fix James's nits

* Show "Add Post" button only to users
2022-09-23 15:11:50 -04:00
Sinclair Chen
ebcecd4fe9 remove unused files 2022-09-23 15:01:48 -04:00
Austin Chen
61a9224a7d Move Civid Dashboard and Research.Bet to Alumni 2022-09-23 12:10:48 -04:00
Austin Chen
47c97c36db Add Alignment Markets to Awesome Manifold 2022-09-23 12:01:33 -04:00
Ian Philips
5483955590 Remove contractId from required JSON for /close 2022-09-23 10:21:11 -04:00
Ian Philips
91f89ccb3d Add docs fo /close, allow to pass closeTime 2022-09-23 10:14:41 -04:00
Ian Philips
08202c3ede Add close market endpoint 2022-09-23 10:02:40 -04:00
jahooma
70bc5b2c4a Auto-prettification 2022-09-22 21:58:40 +00:00
James Grugett
c6d034545a
Home: Prob change cards. Sort by daily score. (#925)
* Add dailyScore: product of unique bettors (3 days) and probChanges.day

* Increase memory and duration of scoreContracts

* Home: Smaller prob change card for groups. Use dailyScore for sort order (algolia)

* Add back hover
2022-09-22 16:57:48 -05:00
Sinclair Chen
eaaa46294a fix empty comment send button style 2022-09-22 17:07:51 -04:00
Sinclair Chen
2240db9baa fix profile tab styling 2022-09-22 16:24:57 -04:00
Marshall Polaris
a1c3d0a2dd
Fix up comment permalink stuff (#915)
* Eliminate needless state/effects to highlight comments

* Scroll to comment on render if highlighted
2022-09-22 12:58:40 -07:00
Marshall Polaris
7704de6904
Next.js 12.2.5 -> 12.3.1 (#922) 2022-09-22 12:46:48 -07:00
Marshall Polaris
721448f408
Clean up and fix stuff on answers panel (#914) 2022-09-22 12:40:55 -07:00
Marshall Polaris
6ee8d90bdb
Eliminate redundant showReply/replyTo state (#917) 2022-09-22 12:40:44 -07:00
Marshall Polaris
6fe0a22a48
Improve contract leaderboard computation (#918)
* Fix and clean up top comment stuff

* Make leaderboard code generic on entry type

* No need to look up users on contract leaderboard
2022-09-22 12:40:27 -07:00
mantikoros
b9fffcfa30 sort: add back 24h volume, remove most traded 2022-09-22 14:20:44 -04:00
mantikoros
0c0e7b5582 Auto-prettification 2022-09-22 18:02:17 +00:00
mantikoros
06db5515f6 add qr code to share dialog 2022-09-22 14:01:37 -04:00
FRC
a5e293c010
Bring back tabs in groups (#923) 2022-09-22 12:12:53 -04:00
Sinclair Chen
4412d0195c
Add tooltips to market header icons (#924) 2022-09-22 11:53:55 -04:00
mantikoros
c15285aa64 pare down sorts; only show high/low prob on groups 2022-09-22 00:32:20 -04:00
Austin Chen
9ff2b62740 Remove console log 2022-09-21 23:10:25 -04:00
Sinclair Chen
e9ab234d61 copy: manifold dollars -> mana 2022-09-21 17:49:32 -07:00
ingawei
7988fdde60
simplify binary graphs (#921) 2022-09-21 18:49:20 -05:00
Pico2x
b875ac563d Revert "Bring back tabs in groups (#919)"
This reverts commit b4a59cfb21.
2022-09-21 19:14:05 -04:00
FRC
b4a59cfb21
Bring back tabs in groups (#919) 2022-09-21 18:27:49 -04:00
Austin Chen
d922900bda Increase tip size to M$10 2022-09-21 18:25:56 -04:00
ingawei
24766740c5
cleaning up search bar for mobile (#916)
* cleaning up search bar for mobile
2022-09-21 16:48:32 -05:00
Austin Chen
73fad2e34b Remove F2P Tournament 2022-09-21 15:31:45 -04:00
Marshall Polaris
a10605e74c
More work on contract page and tabs (#912)
* Consolidate comment thread component code

* Move `visibleBets` work down into bets tab

* Remove unnecessary cruft from contract page props

* Don't load all comments in contract page static props anymore

* Tidy up props a bit

* Memoize bets tab

* Memoize recommended contracts widget
2022-09-21 00:02:10 -07:00
Marshall Polaris
c7f29af2ee
Clean up some stuff in AnswersPanel (#902)
* Tidy up messy markup on FR answers panel

* Clean up obsolete feed-related answer stuff

* Slight fixup per James feedback
2022-09-20 22:07:40 -07:00
James Grugett
ea1579975c Increase memory of update functions 2022-09-20 23:56:14 -05:00
Marshall Polaris
6e2aa622ab
Refactor, improve efficiency of contract tabs stuff (#909)
* Move comments and tips fetching down into comments tab rendering

* Consolidate `contract-activity.tsx` into `contract-tabs.tsx`

* Move LP fetching into bets tab
2022-09-20 21:02:17 -07:00
mantikoros
54778ec1b1
Fix twitch onboarding (#910)
* don't show welcome dialog for twitch users

* handle sign up race conditions with more hooks

* content organization and copy tweaks

* lint

* fix import
2022-09-20 19:23:18 -05:00
Marshall Polaris
8870f0d356
Don't always require tips to render comments (#898) 2022-09-20 15:58:47 -07:00
Marshall Polaris
be4def49a2
Kill counts of comments and trades on contract page (#900) 2022-09-20 15:53:35 -07:00
James Grugett
589bf9651d Track viewing full daily movers 2022-09-20 17:40:31 -05:00
Marshall Polaris
60c79141aa
Move comment-bet association code into comment creation trigger (#899)
* Move comment-bet association code into comment creation trigger

* Add index for new comments query
2022-09-20 15:25:58 -07:00
Marshall Polaris
faaf502114
Remove old check in free answer comment rendering (#906) 2022-09-20 14:11:26 -07:00
Marshall Polaris
30ce80d0c9
Extract signup UI from contract tabs component (#901) 2022-09-20 14:04:07 -07:00
Marshall Polaris
8145b128ad
Move recommended contracts to own widget (#896) 2022-09-20 14:03:52 -07:00
Marshall Polaris
a2d9e8e3d2
Cleanup free answer comment stuff (#897)
* Remove unused most-recent-bet-time stuff

* Remove strange reply box hiding behavior

* Tidying markup
2022-09-20 14:03:33 -07:00
mantikoros
106dc232b8 send market guide onboarding email after 96 hrs 2022-09-20 16:03:17 -05:00
mantikoros
379e736e51
hide liquidity panel (#904) 2022-09-20 15:57:27 -05:00
mantikoros
8920241c39 space out onboarding emails 2022-09-20 15:56:28 -05:00
mantikoros
ac952f1164 Revert "Don't send creator guide email & interesting markets on create user"
This reverts commit a4399aaee9.
2022-09-20 15:49:46 -05:00
Ian Philips
6d7fbd69c7 Lint 2022-09-20 12:17:37 -04:00
Ian Philips
a4399aaee9 Don't send creator guide email & interesting markets on create user 2022-09-20 12:15:23 -04:00
Ian Philips
6c3338f5d7 Remove unused unsubscribe attributes 2022-09-20 09:59:48 -04:00
Ian Philips
272ba921a0 Add memory to weekly email functions 2022-09-20 09:45:14 -04:00
Pico2x
fdd7dcc0ab Rm group about/short description 2022-09-20 14:42:41 +01:00
Ian Philips
5ab86c8362 Check new weekly email notification preferences 2022-09-20 09:36:44 -04:00
Ian Philips
c6a60a6678 Streak & uniques bonus in transaction 2022-09-20 08:42:09 -04:00
James Grugett
62f20694bf Exclude resolved from daily movers 2022-09-20 00:49:25 -05:00
James Grugett
c338dce3ce Add daily activation rate. Remove top tenth actions. Cleanup 2022-09-20 00:10:05 -05:00
marsteralex
44deaf7b0a
WIP: add artist category (#866)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

* update cards for categories

update for dominaria united

* added commander category

commander category

* update basic land art

* can use double feature

* removing racist cards upstream

this way we don't have to store the cards in the json

* remove generated cards from digital commanders

* fix counterspell setting default

* added difficulty rating

* updated padding

* add dfc support for commanders

* add artists

* use latest non-digital if possible

* change vsCode settings for python

* update with latest non-digital printing

* update artist list

* update algo to select k samples

* cleanup code

* equally weight artists

* weight everything equally

* updated for all artists

* update artists

* add allowlist

* update artists to min 50 art

* allow promo to be replaced

* update jsons

* update with min 100 arts

* update code to be smaller jsons

* updated to 18 artists per game

* update ui

* update importing artists

* update to 21

* move num artists to top of js file

* update artistList to not include artist sigs

* update to 50 artists

* update for ub

* update artist list

* update ub defaults

* update jsons

* allow non-english cards to be replaced

* update allowlist

* update jsons

* add watermark

* update jsons

* update jsons

* make jsons slightly smaller

* add checkmarks and x's

* remove python

* add no answer and checkbox and x

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-19 18:10:14 -07:00
James Grugett
4dc3eada1f Add more stats. Fix timezone. Group retention and new user retention 2022-09-19 18:41:24 -05:00
James Grugett
d0973de2b4 Use percent chart, save unrounded stats 2022-09-19 17:45:52 -05:00
James Grugett
b4244ea75d Change stats date to be by Pacific time zone 2022-09-19 17:10:12 -05:00
mantikoros
a2b01e28c9 daily movers: filter out numeric markets 2022-09-19 16:29:21 -05:00
James Grugett
935c550733 Revalidate static props on market resolve 2022-09-19 16:26:34 -05:00
jahooma
c101337c38 Auto-remove unused imports 2022-09-19 21:25:50 +00:00
James Grugett
f3ff6d99c8 Remove colored background for daily movers to make it not feel like your own profit / loss 2022-09-19 16:24:36 -05:00
James Grugett
de8e4df04c Revalidate static props for new comments 2022-09-19 16:07:27 -05:00
James Grugett
1a82ce193d Add node-fetch, add secret to function config 2022-09-19 15:25:16 -05:00
James Grugett
fb27fac524 Revalidate getStaticProps after each bet 2022-09-19 14:55:37 -05:00
James Grugett
6f5d69ec9c Increase memory of updateMetrics function 2022-09-19 14:34:48 -05:00
mantikoros
55a68d4fec email wording 2022-09-19 14:22:04 -05:00
mantikoros
24cf42284f replace "predictor" => "trader" 2022-09-19 14:03:52 -05:00
FRC
6aa45a2d12
Move group navbar to top (#895) 2022-09-19 17:29:17 +01:00
Ian Philips
5d65bb5bb1 Add message about unique bonuses withdrawn on n/a 2022-09-19 07:31:04 -06:00
Marshall Polaris
bfe00595e7
Make comments with bet outcome but no answer outcome appear (#894) 2022-09-19 00:53:10 -07:00
James Grugett
b93af31d2f Add D1 and W1 (new users) to stats 2022-09-19 01:28:18 -05:00
Marshall Polaris
a9e5020904
Render free response comment threads more simply without bets (#893) 2022-09-18 19:16:48 -07:00
Marshall Polaris
58dcbaaf6e
Precalculate and store current positions for users who make comments (#878) 2022-09-18 15:57:50 -07:00
mantikoros
e37b805b49 disable liquidity bonus (for now) 2022-09-18 17:49:29 -05:00
James Grugett
8ebf829169 Change groups default sort to Trending 2022-09-18 17:16:13 -05:00
James Grugett
56b4889b94 Add user.homeSections to firestore rules 2022-09-18 17:00:35 -05:00
James Grugett
17453e5618 Improve hook that was spamming in dev 2022-09-18 16:57:20 -05:00
James Grugett
ae6437442b Fix unsaved changes warning erronously appearing 2022-09-18 16:36:45 -05:00
James Grugett
373cfc5d10 Format firestore /group rules 2022-09-18 16:23:09 -05:00
mantikoros
540915eb65 homepage: fix betting streaks error 2022-09-18 16:05:22 -05:00
James Grugett
f111d6e24f Fix console errors from svg non-camelcase attributes 2022-09-18 15:55:39 -05:00
James Grugett
676bcc159d Fix missing key 2022-09-18 15:53:04 -05:00
James Grugett
1da4373335 Creating a group from create market adds it immediately 2022-09-18 14:24:29 -05:00
James Grugett
c9e782faa7 Simplify create group dialog 2022-09-18 13:49:28 -05:00
Marshall Polaris
39119a3419
Twitch bot deployment work (#892)
* Point at production Twitch bot endpoint

* Move Twitch endpoints into env config
2022-09-18 01:13:10 -07:00
James Grugett
65166f2fcb Fix import 2022-09-18 01:10:34 -05:00
James Grugett
eb021f30f5 Fix loans (user without a portfolio throws error) 2022-09-18 01:05:55 -05:00
James Grugett
4aea3b96d7 Save initial home sections for new users 2022-09-17 23:58:18 -05:00
James Grugett
987274ad2d Fix bug part 2 2022-09-17 20:01:00 -05:00
James Grugett
2166169608 Fix bug 2022-09-17 19:56:20 -05:00
James Grugett
d8e9e7812a Don't show Daily movers if there are none. Threshold is 1% 2022-09-17 19:47:04 -05:00
James Grugett
3bddda37d2 Add plus to trending group button 2022-09-17 19:25:19 -05:00
jahooma
42f66b11f4 Auto-prettification 2022-09-18 00:20:50 +00:00
James Grugett
436646cc47 Use algolia to fetch daily movers so it's faster. 2022-09-17 19:18:48 -05:00
James Grugett
a14e7d3947 Move Algolia bits to own file in web/lib/service 2022-09-17 19:18:48 -05:00
mantikoros
47cc313aef add back leaderboards link 2022-09-17 19:15:26 -05:00
mantikoros
44f9a1faa2 fix labels 2022-09-17 19:12:44 -05:00
mantikoros
f71791bdd5 fix labels 2022-09-17 19:10:34 -05:00
James Grugett
350ab35856 Tweak padding 2022-09-17 18:52:14 -05:00
mantikoros
37cff04e39 share dialog styling 2022-09-17 18:49:24 -05:00
James Grugett
e7ed893b78 Round prob in Daily movers 2022-09-17 18:45:40 -05:00
mantikoros
8f30ef38d9 fix imports 2022-09-17 18:40:45 -05:00
James Grugett
1fbadf8181 Improve Customize UI 2022-09-17 18:30:29 -05:00
mantikoros
438c12da57 refactor sidebar; add to mobile navbar 2022-09-17 18:26:02 -05:00
James Grugett
191ec9535c Show more rows on daily movers all 2022-09-17 18:00:24 -05:00
James Grugett
b74fd57912 Show absolute prob in daily movers as well 2022-09-17 17:58:08 -05:00
James Grugett
a54f060ccb New for you => New 2022-09-17 15:15:37 -05:00
mantikoros
fdde57e334 'predictor' => 'trader' 2022-09-17 15:10:16 -05:00
mantikoros
fde90be5a2 fix resolve prob notification text 2022-09-17 15:01:45 -05:00
mantikoros
d2471e2a02 group selector dialog: loading indicator, tracking 2022-09-17 14:59:02 -05:00
James Grugett
f35799c129 Only autofocus search if no query params set 2022-09-17 14:54:55 -05:00
mantikoros
6a21067440 update /about redirect 2022-09-17 14:46:59 -05:00
mantikoros
340b21c53e halve referral bonus 2022-09-17 14:38:52 -05:00
mantikoros
fc5807ebbe halve MAX_QUESTION_LENGTH 2022-09-17 14:38:52 -05:00
Austin Chen
e0806cf0e0 Fix links to group /about and /leaderboards 2022-09-16 20:36:52 -07:00
James Grugett
94c448ee8b Most predictions => Most traded 2022-09-16 17:43:27 -05:00
Sinclair Chen
3e9f046a29 Always focus search bar on search page 2022-09-16 15:37:52 -07:00
James Grugett
9340d827d9 Replace new with recently updated 2022-09-16 17:29:46 -05:00
James Grugett
612066d96c Tweak margin 2022-09-16 17:26:00 -05:00
James Grugett
015e86afcb Add search to bottom nav bar. Add back Home title to home page. 2022-09-16 17:24:31 -05:00
James Grugett
7c710ba598 Rename file nav-bar to bottom-nav-bar 2022-09-16 17:24:31 -05:00
Sinclair Chen
5b8fc12163
Make add group button same height as group names (#890) 2022-09-16 15:21:07 -07:00
James Grugett
ab3ed3fbf1 Save last sort to local storage 2022-09-16 17:09:12 -05:00
James Grugett
22d5c74818 Add search nav items 2022-09-16 16:46:24 -05:00
James Grugett
1321139e7f Prefetch daily movers 2022-09-16 16:35:04 -05:00
mantikoros
70ef9e1836 group sidebar, navbar tweaks 2022-09-16 16:30:06 -05:00
James Grugett
e4cfd92bb2 Track home actions 2022-09-16 16:21:13 -05:00
James Grugett
25ee793208
🏠 New home (#889)
* Factor out section header

* Remove daily balance change

* Remove dead code

* Layout, add streak

* Fix visibility observer to work on server

* Tweak

* Search perserved by url

* Add pill query param

* Add search page

* Extract component for ProbChangeRow

* Explore groups page

* Add search row

* Add trending groups section

* Add unfollow option for group

* Experimental home: accommodate old saved sections.

* Tweaks to search layout

* Rearrange layout

* Daily movers page

* Add streak grayed out indicator

* Use firebase query instead of algolia search for groups

* Replace trending group card with pills

* Hide streak if you turned off that notification

* Listen for group updates

* Better UI for adding / removing groups

* Toast feedback for join/leave group. Customize button moved to bottom.

* Remove Home title

* Refactor arrange home

* Add new for you section

* Add prefetch

* Move home out of experimental!

* Remove unused import

* Show non-public markets from group
2022-09-16 16:12:24 -05:00
mantikoros
f7164ddd7d
group selector dialog (#888)
* group selector dialog

* cache groups to prevent ui jumping

* welcome dialog display logic

* show fewer groups, more filtering
2022-09-16 14:58:36 -05:00
Sinclair Chen
dd2b09830e update Clearer Thinking end date to Sep 30 2022-09-16 12:13:45 -07:00
Phil
52ecd79736
Twitch prerelease (#887)
* Bot linking button functional.

* Implemented initial prototype of new Twitch signup page.

* Removed old Twitch signup page.

* Moved new Twitch page to correct URL.

* Twitch account linking functional.

* Fixed charity link.

* Changed to point to live bot server.

* Slightly improve spacing and alignment on Twitch page

* Tidy up, handle some errors when talking to bot

* Seriously do the thing where Twitch link is hidden by default

* Fixed secondary Get Started button. Copy overlay and dock link now functional.

* Add/remove bot from channel working.

* Removed legacy Twitch controls from user profile.

* Links provided by dock/overlay buttons are now correct.

* Minor profile cleanup post merge.

* Fixed unnecessary relinking Twitch account when logging in on Twitch page.

* Added confirmation popup to refresh API key. Refreshing API key now requires a user to relink their Twitch account.

* Removed legacy twitch-panel.tsx

Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-09-16 08:43:49 -07:00
IanPhilips
c316d49957 Auto-prettification 2022-09-16 15:30:09 +00:00
Ian Philips
68f2277def Just put 'you' on mobile 2022-09-16 09:28:39 -06:00
Ian Philips
a2d912bb5a Add more info to limit order notif 2022-09-16 09:02:58 -06:00
Ian Philips
c183315d52 Don't notify of updated close time when resolving market 2022-09-16 08:15:16 -06:00
Ian Philips
6a5873f8d4 Try more restrictive detault notification settings 2022-09-16 07:43:27 -06:00
FRC
456aed467c
Move tabs to sidebar (#873)
* Move tabs to sidebar

* Address all feedback

Fix icon names
Extract navbar component into a separate function
Rm arrow and indentation
Move group name under logo
Fix visual sidebar stretchy thing
Fix visual bug

* Extra nits
2022-09-16 14:32:15 +01:00
ingawei
256fd89fd2
market close fix oopsies (#886)
* market close fix
2022-09-16 02:38:09 -05:00
Phil
833ec518b4
Twitch prerelease (#882)
* Bot linking button functional.

* Implemented initial prototype of new Twitch signup page.

* Removed old Twitch signup page.

* Moved new Twitch page to correct URL.

* Twitch account linking functional.

* Fixed charity link.

* Changed to point to live bot server.

* Slightly improve spacing and alignment on Twitch page

* Tidy up, handle some errors when talking to bot

* Seriously do the thing where Twitch link is hidden by default

Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-09-16 00:22:13 -07:00
Austin Chen
1321b95eb1
%mentions for embedding contract card, take 2 (#884)
* Revert "Revert "Use %mention to embed a contract card in rich text editor (#869)""

This reverts commit e0634cea6d.

* Overwrite name to prevent breakages

* Fix '%' mentioning if you escape out

* Cleanup: merge render functions
2022-09-15 23:37:17 -07:00
Austin Chen
ca4a2bc7db Remove console log 2022-09-15 23:00:58 -07:00
mantikoros
430ad1acb0 "unique bettors"; "Unknown" => "0" 2022-09-15 23:18:27 -05:00
ingawei
1ce989f3d6
Inga/bettingfix embedfix (#885)
* Revert "Revert "Inga/bettingfix (#879)""
This reverts commit 176acf959f.
* added embed fix
2022-09-15 19:41:25 -05:00
mantikoros
5a1cc4c19d getCpmmInvested: fix NaN issue 2022-09-15 18:32:38 -05:00
James Grugett
e0634cea6d Revert "Use %mention to embed a contract card in rich text editor (#869)"
This reverts commit 140628692f.
2022-09-15 18:19:22 -05:00
Sinclair Chen
ebbb8905e2
Add clearer thinking Regrant to tournaments (#883) 2022-09-15 16:05:56 -07:00
Austin Chen
140628692f
Use %mention to embed a contract card in rich text editor (#869)
* Bring up a list of contracts with @

* Fix hot reload for RichContent

* Render contracts as half-size cards

* Use % as the prompt; allow for spaces

* WIP: When there's no matching question, create a new contract

* Revert "WIP: When there's no matching question, create a new contract"

This reverts commit efae1bf715.

* Rename to contract-mention

* WIP: Try to merge in @ and % side by side

* Add a different pluginKey

* Track the prosemirror-state dep
2022-09-15 15:12:26 -07:00
Ian Philips
e9fcf5a352 Space 2022-09-15 16:12:05 -06:00
Ian Philips
3362b2f953 Capitalize 2022-09-15 15:51:39 -06:00
Ian Philips
61c672ce4c Show negative payouts 2022-09-15 15:50:26 -06:00
Ian Philips
7628713c4b Enrich contract resolved notification 2022-09-15 15:25:19 -06:00
Marshall Polaris
b903183fff
Paginate contract bets tab (#881)
* Apply pagination to bets list on contract

* Make contract trades tab number actually match number of entries
2022-09-15 13:47:07 -07:00
Austin Chen
1476f669d3 Fix capitalization 2022-09-15 13:45:51 -07:00
Ian Philips
8c6a40bab7 Enrich limit order notification 2022-09-15 13:39:46 -06:00
Sinclair Chen
69c2570ff9 fix copy to make clear referrals aren't limited 2022-09-15 12:29:57 -07:00
Ian Philips
b3e6dce31e Capitalize 2022-09-15 09:57:14 -06:00
Ian Philips
be91d5d5e0 Avatars don't link during contract selection 2022-09-15 09:51:52 -06:00
Ian Philips
e9f136a653 Single source of truth for predict 2022-09-15 09:12:56 -06:00
Ian Philips
4c10c8499b Take back unique bettor bonuses on N/A 2022-09-15 09:12:44 -06:00
Pico2x
718218c717 Update bet-inline.tsx 2022-09-15 15:51:14 +01:00
Pico2x
772eeb5c93 Update [contractSlug].tsx 2022-09-15 15:45:49 +01:00
Ian Philips
ada9fac343 Add logs to on-create-bet 2022-09-15 08:07:42 -06:00
Ian Philips
733d206517 Add txn types 2022-09-15 07:50:35 -06:00
Ian Philips
4a5c6a42f6 Store bonus txn data in data field 2022-09-15 07:45:11 -06:00
Ian Philips
e5428ce525 Watch market modal copy 2022-09-15 07:14:59 -06:00
Pico2x
176acf959f Revert "Inga/bettingfix (#879)"
This reverts commit 8aaaf5e9e0.
2022-09-15 13:55:57 +01:00
ingawei
8aaaf5e9e0
Inga/bettingfix (#879)
* making betting action panels much more minimal, particularly for mobile
* added tiny follow button
2022-09-15 01:46:58 -05:00
ingawei
ccf02bdba8
Inga/admin rules resolve (#880)
* Giving admin permission to resolve all markets that have closed after 7 days.
2022-09-14 22:28:40 -05:00
Ian Philips
9aa56dd193 Only show prev opened notif setting section 2022-09-14 17:25:17 -06:00
Ian Philips
3efd968058 Allow one-click unsubscribe, slight refactor 2022-09-14 17:17:32 -06:00
Sinclair Chen
68b0539fc1 Enable search exclusion and exact searches
like `-musk` to remove Elon results or `"eth"` for Ethereum results
2022-09-14 15:06:11 -07:00
Sinclair Chen
7aaacf4d50 Center tweets 2022-09-14 13:19:12 -07:00
Ian Philips
050bd14e46 Update script 2022-09-14 10:29:48 -06:00
Ian Philips
7ba2eab65e Rename user notification preferences 2022-09-14 10:26:08 -06:00
Ian Philips
edbae16c8e Betting streak reset indicator 2022-09-14 08:56:05 -06:00
Ian Philips
d6b0a1edc0 Betting streak reset to 7am UTC and store streak data on notif 2022-09-14 07:27:20 -06:00
mantikoros
a2d61a1daa
Twitch integration (#815)
* twitch account linking; profile page twitch panel; twitch landing page

* fix import

* twitch logo

* save twitch credentials cloud function

* use user id instead of bot id, add manifold api endpoint

* properly add function to index

* Added support for new redirect Twitch auth.

* Added clean error handling in case of Twitch link fail.

* remove simulator

* Removed legacy non-redirect Twitch auth code. Added "add bot to channel" button in user profile and relevant data to user type.

* Removed unnecessary imports.

* Fixed line endings.

* Allow users to modify private user twitchInfo firestore object

* Local dev on savetwitchcredentials function

Co-authored-by: Phil <phil.bladen@gmail.com>
Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-09-14 01:52:31 -07:00
Marshall Polaris
7144e57c93
Denormalize user display fields onto bets (#853)
* Denormalize user display fields onto bets

* Make bet denormalization script fast enough to run it on prod

* Make `placeBet`/`sellShares` immediately post denormalized info
2022-09-14 01:33:59 -07:00
Marshall Polaris
1ebb505752
Fix liquidity feed display to look right (#877) 2022-09-14 01:13:53 -07:00
mantikoros
273b815e54 hide house liquidity on feed 2022-09-14 00:51:43 -05:00
mantikoros
e7d8cfe7e0
House liquidity (#876)
* add house liquidity for unique bettors

* hide notifications from house liquidity

* up bonus liquidity to  M$20
2022-09-14 00:26:47 -05:00
mantikoros
be851b8382 fix typo 2022-09-13 21:23:36 -05:00
mantikoros
58ef43a8ec intro panel: use gradient image 2022-09-13 21:12:01 -05:00
Ian Philips
f6feacfbc9 Fix lint and persistent storage key 2022-09-13 17:18:16 -06:00
Sinclair Chen
74335f2b01
Adjust market modal styles (#875)
* Refactor add market modals into one component
* Adjust style: stickier search, scroll auto
2022-09-13 16:16:07 -07:00
Ian Philips
df3d7b591d Componentize notification line setting, don't use useEffect 2022-09-13 17:00:34 -06:00
James Grugett
c9d323c83f
Small updates to experimental/home (#874)
* Factor out section header

* Remove daily balance change

* Remove dead code

* Layout, add streak

* Fix visibility observer to work on server

* Tweak

* Formatting
2022-09-13 17:47:29 -05:00
Ian Philips
34bad35cb8 Betting=>predicting 2022-09-13 16:19:52 -06:00
Ian Philips
c423326270 Send users emails when they hit 1 and 6 unique bettors 2022-09-13 16:12:53 -06:00
Ian Philips
4398fa9bda Add new market from followed user email notification 2022-09-13 09:54:51 -06:00
Ian Philips
2c922cbae6 Send no-bet resolution emails to those without bets 2022-09-13 08:16:23 -06:00
Ian Philips
55b895146b Find multiple choice resolution texts as well 2022-09-13 07:54:37 -06:00
Ian Philips
8b1776fe3b Remove contracts number badge from groups tab 2022-09-13 07:53:01 -06:00
Ian Philips
de8c27c970 Filter None answer earlier 2022-09-13 07:48:41 -06:00
James Grugett
483838c1b2 Revert "Make parse.richTextToString more efficient (#848)"
This reverts commit cb143117e5.
2022-09-12 19:06:37 -05:00
Marshall Polaris
cb143117e5
Make parse.richTextToString more efficient (#848) 2022-09-12 16:11:03 -07:00
Sinclair Chen
22d2248951
Add floating menu (bold, italic, link) (#867)
* Add floating menu (bold, italic, link)
* Sanitize and href-ify user input
2022-09-12 16:10:32 -07:00
Ian Philips
2351403674 Replies to answers are comments 2022-09-12 17:04:06 -06:00
Ian Philips
018eb8fbfc Send notif to all users in reply chain as reply 2022-09-12 17:01:59 -06:00
James Grugett
f49cb9b399 Only show 'Show more' for free response answers if there are more answers to show 2022-09-12 17:40:19 -05:00
mantikoros
d66a81bc6b Auto-prettification 2022-09-12 22:35:32 +00:00
mantikoros
8e41b39936 landing page: use next image for logo 2022-09-12 17:34:13 -05:00
mantikoros
0e5b1a7742 market intro panel 2022-09-12 17:30:51 -05:00
mantikoros
3d3caa7a42 remove comment bet area 2022-09-12 16:50:38 -05:00
Pico2x
a3da8a7c3c Make update-metrics actually write cached group leaderboards 2022-09-12 22:01:37 +01:00
Pico2x
2a96ee98f4 Fix type error in update metrics pt.3 2022-09-12 21:49:15 +01:00
Ian Philips
5c6fe08bdb Website => Web 2022-09-12 14:48:42 -06:00
Ian Philips
747d5d7c7c In app => website 2022-09-12 14:48:16 -06:00
Ian Philips
3a814a5b5d Detect just settings tab w/o section 2022-09-12 14:41:30 -06:00
Ian Philips
e35c0b3b52 Only notify followers of new public markets 2022-09-12 14:36:54 -06:00
Ian Philips
0af1ff112b Allow users to see 0% FR answers via show more button 2022-09-12 14:30:15 -06:00
Pico2x
4456a771fd fix type error in update-metrics pt.2 2022-09-12 21:25:45 +01:00
Ian Philips
86422f90ea Set all overflow notifs to seen 2022-09-12 14:17:39 -06:00
Pico2x
7d9908dbd0 Fix type error in update-metrics 2022-09-12 20:58:12 +01:00
FRC
ff81b859d1
"Fix "500 internal error" in large groups (#872)
* Fix "500 internal error" in large groups (#856)

This reverts commit 28f0c6b1f8.

* Ship without touching prod and with some logs.
2022-09-12 20:54:11 +01:00
Ian Philips
3cb36a36ec Separate email and browser ids list 2022-09-12 11:00:24 -06:00
James Grugett
4f19220778 Experimental home: accommodate old saved sections. 2022-09-12 11:56:20 -05:00
Ian Philips
5c6328ffc2
[WIP] Fully customizable notifications (#860)
* Notifications Settings page working

* Update import

* Linked notification settings to notification rules

* Add more subscribe types

* It's alive... It's alive, it's moving, it's alive, it's alive, it's alive, it's alive, IT'S ALIVE'

* UI Tweaks

* Clean up comments

* Direct & highlight sections for notif mgmt from emails

* Comment cleanup

* Comment cleanup, lint

* More comment cleanup

* Update email templates to predict

* Move private user out of getDestinationsForUser

* Fix resolution messages

* Remove magic

* Extract switch to switch-setting

* Change tab in url

* Show 0 as invested or payout

* All emails use unsubscribeUrl
2022-09-12 10:34:56 -06:00
FRC
28f0c6b1f8
Revert "Fix "500 internal error" in large groups (#856)" (#871)
This reverts commit a6ed8c9228.
2022-09-12 17:26:46 +01:00
FRC
a6ed8c9228
Fix "500 internal error" in large groups (#856)
* Members to memberIds

* Moved to update-metrics
2022-09-12 16:44:24 +01:00
James Grugett
c1287a4a25
Small updates to experimental home (#870)
* Line clamp question in prob change table

* Tweaks

* Expand option for daily movers

* Snap scrolling for carousel

* Add arrows to section headers

* Remove carousel from experimental/home

* React querify fetching your groups

* Edit home is its own page

* Add daily profit and balance

* Merge branch 'main' into new-home

* Make experimental search by your followed groups/creators

* Just submit, allow xs on pills

* Weigh in

* Use next/future/image component to optimize avatar images

* Inga/challenge icon (#857)

* changed challenge icon to custom icon
* fixed tip button alignment

* weighing in and trading "weigh in" for "trade"

* Delete closing soon, mark new as New for you, trending is site-wide

* Delete your trades. Factor out section item

* Don't allow hiding of home sections

* Convert daily movers into a section

* Tweaks for loading daily movers

* Prob change table shows variable number of rows

* Fix double negative

Co-authored-by: Ian Philips <iansphilips@gmail.com>
Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: ingawei <46611122+ingawei@users.noreply.github.com>
Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-09-12 00:39:04 -05:00
Sinclair Chen
f8d346a404 Clean up charity styles
- center cards on mobile
- make notes more professional
2022-09-11 11:42:05 -07:00
mantikoros
93033b5b24 Revert "Yes and no buttons on contract page (#868)"
This reverts commit b39e0f304f.
2022-09-10 21:57:50 -05:00
mantikoros
18815caed4 Revert "Put sale value above quick bet button"
This reverts commit 9ee7173305.
2022-09-10 21:57:35 -05:00
Ian Philips
9ee7173305 Put sale value above quick bet button 2022-09-10 17:48:35 -06:00
Ian Philips
b39e0f304f
Yes and no buttons on contract page (#868)
* Yes and no buttons on contract page

* Cheating by adding 0.05 to max shares but gives better quickbet UX
2022-09-10 17:07:23 -06:00
mantikoros
e17234ecce typo 2022-09-10 17:43:52 -05:00
Austin Chen
33bcc1a65e Clean up /tournaments styling 2022-09-10 12:00:01 -07:00
Austin Chen
e61591622e Feature a few other semi-tournaments 2022-09-10 11:50:03 -07:00
jahooma
11ba65ec4a Auto-remove unused imports 2022-09-09 22:43:33 +00:00
FRC
26f83ac4f6
Adds investmentValue to group leaderboard calculation (#855)
* Adds investmentValue to group leaderboard calculation

* Initial investment is no longer counted, only the profit

* Simplify scoring calculation

* Remove console.log

* Group bets by user first

Co-authored-by: James Grugett <jahooma@gmail.com>
2022-09-09 17:42:51 -05:00
akrolsmir
cca870ced5 Auto-remove unused imports 2022-09-09 21:27:16 +00:00
Austin Chen
fdf123b875 Remove console.logs from common code
This makes it easier to debug in local; we shouldn't be checking in console.log into the codebase, as a general rule
2022-09-09 14:26:23 -07:00
Austin Chen
a737ae9f46 Link to tournament /about pages 2022-09-09 14:14:22 -07:00
mantikoros
43660387fa modal positioning 2022-09-09 16:08:42 -05:00
mantikoros
7729bdd2dc bet panel: higher threshold for warning; no autofocus on mobile 2022-09-09 15:58:26 -05:00
marsteralex
1ae0f0e273
add dfc support to commanders (#865)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

* update cards for categories

update for dominaria united

* added commander category

commander category

* update basic land art

* can use double feature

* removing racist cards upstream

this way we don't have to store the cards in the json

* remove generated cards from digital commanders

* fix counterspell setting default

* added difficulty rating

* updated padding

* add dfc support for commanders

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-09 13:54:54 -07:00
Austin Chen
18466afc78 Fix API URLs from non "manifold.markets" domains 2022-09-09 11:43:23 -07:00
Austin Chen
4c801f76b4
Submit comments on ctrl/cmd-enter (#830)
* Submit comments on ctrl/cmd-enter

* Remove unused import

* Tweak padding on /tournaments

* Always submit on ctrl+enter

Since we took out group chats, this should be fine for all comments
2022-09-09 11:09:31 -07:00
Austin Chen
6a69f44f07 Tweak padding on /tournaments 2022-09-09 10:16:25 -07:00
marsteralex
aa5876fe0d
added difficulty rating (#864)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

* update cards for categories

update for dominaria united

* added commander category

commander category

* update basic land art

* can use double feature

* removing racist cards upstream

this way we don't have to store the cards in the json

* remove generated cards from digital commanders

* fix counterspell setting default

* added difficulty rating

* updated padding

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-09 09:51:20 -07:00
Ian Philips
e639cb654e Add group endpoint note in api docs 2022-09-09 07:22:31 -06:00
Austin Chen
1408908959 List the Manifold F2P tournament 2022-09-09 01:46:25 -07:00
mantikoros
cd1d8ecd8a WarningConfirmationButton for bets 2022-09-09 01:02:30 -05:00
mantikoros
0dbb42aa69 Auto-prettification 2022-09-09 05:03:05 +00:00
mantikoros
2ebb83418c bet panel: disable input focus on mobile 2022-09-09 00:02:14 -05:00
mantikoros
eac56b1f4f slider: smarter step increments; disable clicking on track 2022-09-09 00:02:14 -05:00
James Grugett
987ebccdfd Contract tabs: used passed in bets 2022-09-08 23:45:26 -05:00
James Grugett
cf74a195b2 Redeem shares: pay back a smaller frac of your loan 2022-09-08 22:59:25 -05:00
marsteralex
677b20a7ba
fix brawl commanders for digital (#862)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

* update cards for categories

update for dominaria united

* added commander category

commander category

* update basic land art

* can use double feature

* removing racist cards upstream

this way we don't have to store the cards in the json

* remove generated cards from digital commanders

* fix counterspell setting default

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-08 20:23:31 -07:00
Austin Chen
e3e80a5fd0 Change user info using bulkWriter 2022-09-08 20:21:32 -07:00
marsteralex
8aeb544f7e
add commander category (#861)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

* update cards for categories

update for dominaria united

* added commander category

commander category

* update basic land art

* can use double feature

* removing racist cards upstream

this way we don't have to store the cards in the json

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-08 18:38:48 -07:00
James Grugett
b9ae919fda Add staleTime option for prefetching 2022-09-08 16:59:05 -05:00
mantikoros
f25460a647 smaller font for "Predict" 2022-09-08 15:10:36 -05:00
Sinclair Chen
28f3694e8f
Fix editor rerendering when you load it (#831)
* Don't rerender entire editor for user list

also fixes bug where you are the only mention

* Cache with react query instead of memoize
2022-09-08 13:02:50 -07:00
Pico2x
caa3fc06e6 Minor tailwind/indent fixes to posts 2022-09-08 17:32:42 +01:00
Pico2x
1e645f911a Add Fede to admin pt2 2022-09-08 17:14:40 +01:00
Pico2x
adf2086141 Add Fede to admins 2022-09-08 16:51:58 +01:00
FRC
d9bb7d1926
Edit posts (#859) 2022-09-08 16:23:19 +01:00
Ian Philips
5547b30364 Add david to admins 2022-09-08 09:16:54 -06:00
Ian Philips
3932a3dbd4 I predict this will do better than trade 2022-09-08 07:40:16 -06:00
James Grugett
bff4eff719 Persist user page markets on back (Marshall's machinery) 2022-09-08 01:39:01 -05:00
James Grugett
54c227cf6c
Updates to experimental home (#858)
* Line clamp question in prob change table

* Tweaks

* Expand option for daily movers

* Snap scrolling for carousel

* Add arrows to section headers

* Remove carousel from experimental/home

* React querify fetching your groups

* Edit home is its own page

* Add daily profit and balance

* Merge branch 'main' into new-home

* Make experimental search by your followed groups/creators

* Just submit, allow xs on pills

* Weigh in

* Use next/future/image component to optimize avatar images

* Inga/challenge icon (#857)

* changed challenge icon to custom icon
* fixed tip button alignment

* weighing in and trading "weigh in" for "trade"

Co-authored-by: Ian Philips <iansphilips@gmail.com>
Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: ingawei <46611122+ingawei@users.noreply.github.com>
Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-09-08 01:36:34 -05:00
mantikoros
edbebb7e67 weighing in and trading "weigh in" for "trade" 2022-09-08 00:16:48 -05:00
ingawei
004671f032
Inga/challenge icon (#857)
* changed challenge icon to custom icon
* fixed tip button alignment
2022-09-07 23:51:52 -05:00
Austin Chen
45a965476e Use next/future/image component to optimize avatar images 2022-09-07 20:59:00 -07:00
Ian Philips
bcee49878b Weigh in 2022-09-07 21:39:21 -06:00
Ian Philips
35de4c485a Just submit, allow xs on pills 2022-09-07 21:39:14 -06:00
James Grugett
4439447a6d Persist group markets and scroll position on back (Marshall's machinery) 2022-09-07 21:33:36 -05:00
mantikoros
e6c6f64077 fix mobile nav for trades tab 2022-09-07 21:16:58 -05:00
FRC
0acdec787d
Adds comments to posts (#844)
* Adds comments to posts

* Uncoupled CommentInput from Contracts

* Fix nits
2022-09-07 23:09:20 +01:00
mantikoros
ce52f21ce9 fix sidebar profile link to your trades 2022-09-07 15:13:17 -05:00
mantikoros
b3343c210a more "bet" => "trade" 2022-09-07 15:04:34 -05:00
mantikoros
b4e0e9ebc0 "A market for every question" 2022-09-07 15:01:02 -05:00
mantikoros
28af2063c3 "bet" => "trade" 2022-09-07 14:45:04 -05:00
FRC
cce14cbe1f
Toggle monthly leaderboards (#790)
* Toggle monthly leaderboards

I didn't get to enabling monthly leaderboards after my work trial was over (I enabled daily/weekly/alltime). The cache has been filled out for a while now, this toggles it on.

* Fix nits
2022-09-07 17:04:30 +01:00
Ian Philips
87060488f5 Convert market to lite market for Phil 2022-09-07 07:13:34 -06:00
James Grugett
ad18987e65 Update Daily movers UI 2022-09-07 01:18:11 -05:00
James Grugett
a40bdc28be Remove some excess spacing on user page 2022-09-06 23:39:50 -05:00
James Grugett
082125bd2f Remove some margin 2022-09-06 23:31:02 -05:00
James Grugett
21870d7edb User page: Move portfolio graph and social stats to new tab 2022-09-06 23:24:58 -05:00
mantikoros
85be84071a track embedded markets separtely 2022-09-06 22:43:28 -05:00
mantikoros
a9627bb2b6 market page: regenerate static props after 5 seconds 2022-09-06 22:12:18 -05:00
Sinclair Chen
537962a7dc Stop links from opening twice 2022-09-06 16:55:33 -07:00
Ian Philips
f7d027ccc9 Create button=>Site link 2022-09-06 16:38:01 -06:00
Ian Philips
8759064ccb new bettors 2022-09-06 16:30:58 -06:00
Ian Philips
c16e7c6cfd Add membership indicators and link to see group 2022-09-06 16:20:43 -06:00
James Grugett
668f30dd55 Free market creation shows cost striked through 2022-09-06 16:55:52 -05:00
Ian Philips
45e54789b7 Groups search shares query, sorted by contract & members 2022-09-06 15:51:36 -06:00
mantikoros
c59de1be2e bet slider: decrease step size 2022-09-06 11:53:09 -05:00
Ian Philips
a038ef91eb Show num contracts in group selector 2022-09-06 09:58:24 -06:00
Ian Philips
74af54f3c0 Remove chance from FR og-images 2022-09-06 09:36:41 -06:00
Ian Philips
7c44abdcd7 Comment out unused script functions 2022-09-06 09:27:50 -06:00
Ian Philips
5af92a7d81 Update groups API 2022-09-06 09:24:26 -06:00
Ian Philips
2ee067c072 Remove member and contract ids from group doc 2022-09-06 08:14:13 -06:00
Ian Philips
39d7f1055b Fix spacing on challenge modal 2022-09-06 07:58:00 -06:00
Ian Philips
a3b18e5bea Add challenge back to share modal 2022-09-06 07:57:52 -06:00
FRC
59f3936dad
Fix bug (#854) 2022-09-06 14:17:21 +01:00
mantikoros
450b140f5f show challenge button on mobile 2022-09-05 18:19:13 -05:00
James Grugett
f21711f3dc Fix type error 2022-09-05 18:13:01 -05:00
James Grugett
cd8bb72f94 Daily movers table in experimental/home 2022-09-05 18:09:03 -05:00
mantikoros
837a4d8949 Revert "Show challenge on desktop, simplify modal"
This reverts commit 8922b370cc.
2022-09-05 18:07:44 -05:00
mantikoros
8952b100ad add answer panel mobile formatting, slider 2022-09-05 17:59:19 -05:00
mantikoros
2d724bf2c8 make slider black 2022-09-05 17:44:21 -05:00
mantikoros
374c25ffb3 Auto-prettification 2022-09-05 22:40:48 +00:00
mantikoros
96cf1a5f7f mobile slider styling 2022-09-05 17:39:59 -05:00
mantikoros
ae40999700 mobile bet slider 2022-09-05 17:11:32 -05:00
mantikoros
30d73d6362 remove parantheses from balance text 2022-09-05 16:59:35 -05:00
mantikoros
97e0a78806 "join group" => "follow" 2022-09-05 16:51:09 -05:00
James Grugett
d812776357 Remove show hot volume param 2022-09-05 16:25:48 -05:00
mantikoros
9a49c0b8fe remove numeric, multiple choice markets from create market page 2022-09-05 13:33:58 -05:00
Austin Chen
70eec63533 Adding in "Highest %" and "Lowest %" sort options
Quick alternative to https://github.com/manifoldmarkets/manifold/pull/850/files courtesy of James.

One downside of this approach is that the % only update every 15 minutes; but maybe users won't notice?
2022-09-05 10:07:33 -07:00
Marshall Polaris
6ef2beed8f
Denormalize betAmount and betOutcome fields on comments (#838)
* Create and use `betAmount` and `betOutcome` fields on comments

* Be robust to ridiculous bet IDs on dev
2022-09-04 14:28:45 -07:00
James Grugett
a15230e7ab Smartest money => Best bet. Don't show amount made for comment. 2022-09-04 14:06:29 -05:00
James Grugett
a21466d877 Add sort for 24 hour change in probability 2022-09-03 16:20:57 -05:00
Marshall Polaris
89b30fc50d Fix tournaments page loading indicator and turn page size back down 2022-09-03 14:07:34 -07:00
James Grugett
9060abde8e Cache prob and prob changes on cpmm contracts 2022-09-03 15:06:42 -05:00
mantikoros
085b9aeb2a remove simulator 2022-09-03 14:58:42 -05:00
Marshall Polaris
c0383bcf26
Make tournament page efficient (#832)
* Make tournament page efficient

* Fix URL to Salem contract

* Use totalMembers instead of deprecated field

* Increase page size to 12

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-09-03 09:55:10 -07:00
Ian Philips
0938368e30 Capitalize yes/no resolution outcomes 2022-09-03 07:29:35 -06:00
Ian Philips
272658e5dc Use most up-to-date user on groups page 2022-09-03 06:52:51 -06:00
Marshall Polaris
861fb7abbd
Use the magic auth prop for groups SSR (#851) 2022-09-03 06:51:55 -06:00
Ian Philips
2d88675f42 Move & more out of the loop 2022-09-03 06:33:33 -06:00
James Grugett
bfa88c3406 Turn off react-query notification subscription because it's buggy 2022-09-02 22:57:23 -05:00
Marshall Polaris
784c081663
Enable source maps in production (#852) 2022-09-02 19:43:22 -07:00
Marshall Polaris
8318621d51
Some changes to make auth better (#846)
* Handle the case where a user is surprisingly not in the DB

* Only set referral info on user after creation

* More reliably cache current user info in local storage

* Don't jam username stuff into user listener hook
2022-09-02 19:39:27 -07:00
Marshall Polaris
e924061c54
Don't re-create visibility observer for no reason (#849)
* Don't re-create visibility observer for no reason

* `IntersectionObserver.unobserve` instead of `.disconnect`
2022-09-02 19:39:07 -07:00
Ian Philips
25a0276bf7 Auth user server-side on groups page 2022-09-02 19:52:38 -06:00
Ian Philips
c74d972caf Pass user and members via props 2022-09-02 19:36:49 -06:00
Ian Philips
57b74a5d09 Use cached values 2022-09-02 18:12:55 -06:00
Ian Philips
9577955d2d Remove null check 2022-09-02 18:08:53 -06:00
Ian Philips
cf508fd8b6
Members and contracts now subcollections of groups (#847)
* Members and contracts now documents

* undo loans change?

* Handle closed group

* Slight refactoring

* Don't allow modification of private groups contracts

* Add back in numMembers

* Update group field names

* Update firestore rules

* Update firestore rules

* Handle updated groups

* update start numbers

* Lint

* Lint
2022-09-02 18:06:48 -06:00
James Grugett
2f53cef36f Move metrics calculation to common 2022-09-02 18:45:42 -05:00
James Grugett
af68fa6c42 Fix typo in email followup 2022-09-02 16:20:04 -05:00
James Grugett
231d3e65c4 Fix incorrect error message for no bets 2022-09-02 16:19:10 -05:00
James Grugett
00de66cd79
Leaderboard calc: update profit even when portfolio didn't change (#845)
* Leaderboard calc: remove didProfitChange optimization that was incorrect

* Put back didPortfolioChange for deciding whether to create new history doc.
2022-09-02 15:59:32 -05:00
mantikoros
b6449ad296 fix bet panel warnings 2022-09-02 15:32:54 -05:00
Marshall Polaris
d1e1937195
Remove custom token generation machinery (#840) 2022-09-02 13:04:00 -07:00
Marshall Polaris
245627a347 Temporarily patch groups loading to make dev deploy work 2022-09-02 13:00:38 -07:00
Marshall Polaris
a429a98a29
Tidy up some dead code and markup in sidebar (#842) 2022-09-02 12:52:27 -07:00
Marshall Polaris
b1bb6fab5b
Disable SSR on /home (#839) 2022-09-02 12:51:41 -07:00
Marshall Polaris
21b9d0efab
Clean up some old pre-Amplitude tracking code (#841) 2022-09-02 12:51:27 -07:00
Marshall Polaris
4c429cd519
Remove some old code related to the old feed (#843) 2022-09-02 12:51:14 -07:00
mantikoros
0cb20d89ed numeric market labels: LOW/HIGH instead of MIN/MAX; eliminate payout <= MIN, etc. 2022-09-02 10:35:41 -05:00
James Grugett
8029ee49a4 Fix loans bug 2022-09-01 23:06:57 -05:00
Marshall Polaris
4406e53121
Make prefetching correctly use context cache (#835) 2022-09-01 19:38:09 -07:00
Marshall Polaris
dca7205a47
Disable group prefetching from contract links (#836)
* Kill dead code

* Stop prefetching groups when viewing contract

* Tidy markup
2022-09-01 19:37:41 -07:00
Sinclair Chen
04e8bb248b Fix Salem Center market url 2022-09-01 18:15:10 -07:00
Austin Chen
51fe44f877 Show the number of open markets on each groups page 2022-09-01 16:12:09 -07:00
Ian Philips
00ba3b0c48
Show avatars of tippers and unique bettors (#837)
* Show avatars of tippers and unique bettors

* Make transparent the avatar bg

* fix import
2022-09-01 16:23:12 -06:00
Marshall Polaris
7508d86c73
Clean up contract overview code (#823)
* Don't call Date.now a million times in answers graph

* Refactor contract overview code so that it's easier to understand
2022-09-01 14:42:50 -07:00
Ian Philips
8d853815d6
Show resolution on og card image (#834)
* Handle resolved markets

* Add in group names as hashtags
2022-09-01 13:55:24 -06:00
FRC
1208694d2d
http to https to avoid blocked requests (#833) 2022-09-01 17:54:46 +01:00
FRC
96be4e8992
Add embedded ContractGrid to Posts (#822)
* Add embedded market grids

* Hacky way to set height

I haven't figured out a way yet to get the height of the actual iframe's content, so I did some bad estimate for now to unblock shipping the feature, while I continue investigating.
2022-09-01 17:47:45 +01:00
Ian Philips
7310cf3d4a fix import 2022-09-01 10:11:08 -06:00
Ian Philips
8922b370cc Show challenge on desktop, simplify modal 2022-09-01 10:02:41 -06:00
Ian Philips
fecf976ab9 Show all group contracts if less than 5 open 2022-09-01 09:11:14 -06:00
Ian Philips
0823414360 Adjust group name padding on mobile 2022-09-01 08:52:49 -06:00
Ian Philips
c6eac97b64 Show group based on most recent creator added group 2022-09-01 08:29:56 -06:00
Ian Philips
6706fe7350 Show user balance on bet panels 2022-09-01 08:12:46 -06:00
Ian Philips
a7c8b8aec4 Hide bet panel when signed out 2022-09-01 07:34:02 -06:00
Ian Philips
5dec6b4a22 Medium includes 10 bettors 2022-09-01 07:23:43 -06:00
Ian Philips
a8d7e91a02 Clean comments 2022-09-01 07:01:49 -06:00
Ian Philips
fec4e19c1d Selectively force long polling for ios only 2022-09-01 07:01:02 -06:00
Marshall Polaris
0568322c82
Dramatically improve server auth stuff (#826) 2022-08-31 22:13:26 -07:00
Marshall Polaris
42548cea2a
Fix prefetching to not populate useless state (#827) 2022-08-31 21:59:58 -07:00
mantikoros
879d6fb2dd bury profile stats in Comments until we find a better place for them 2022-08-31 23:20:20 -05:00
mantikoros
2a17bcb8b2 eslint 2022-08-31 23:00:39 -05:00
Sinclair Chen
7c1e663b26
Editor tweaks (#829)
* Show border around selected embeds

* Make editor tooltips not animate
2022-08-31 20:52:12 -07:00
mantikoros
2c3cd34444 Auto-remove unused imports 2022-09-01 03:34:22 +00:00
mantikoros
e0ebdc644d market close email: remove mention of creator fee 2022-08-31 22:33:37 -05:00
James Grugett
ee76f4188b For you: remove contracts bet on by anyone you follow. 2022-08-31 21:57:11 -05:00
Sinclair Chen
58e671e640 Upload dropped images 2022-08-31 17:18:35 -07:00
Austin Chen
bc1ec414de
Update awesome-manifold.md 2022-08-31 16:29:42 -07:00
Austin Chen
5514eeff2d
Update awesome-manifold.md 2022-08-31 16:18:53 -07:00
Austin Chen
7a9b159909
Update awesome-manifold.md 2022-08-31 15:40:23 -07:00
Ian Philips
74b6df2e44 Unwatch applies to email comment notifs too 2022-08-31 16:18:48 -06:00
Ian Philips
26aba26da5
force long polling (#824) 2022-08-31 15:38:55 -06:00
Austin Chen
7c8b33597a Add "Duplicate Contract" into "..." menu 2022-08-31 14:33:24 -07:00
James Grugett
3660830ec1 Don't server side render Notifications page for improved perf 2022-08-31 15:41:34 -05:00
James Grugett
83696cca21 Fix dayjs fromNow bug (it requires plugin, so use our util instead) 2022-08-31 15:35:47 -05:00
Sinclair Chen
d06b725f52
Let admins add and edit posts to any group (#820)
- show add post UI to admins
- change firebase permissions
2022-08-31 11:29:49 -07:00
Marshall Polaris
149204f6ca Fix my other foolish bug 2022-08-31 11:17:36 -07:00
Ian Philips
5a9d8e3f5d Show how much you've tipped a market 2022-08-31 09:27:37 -06:00
Ian Philips
37d2be9384 Show only relative time if same day on close date 2022-08-31 08:49:35 -06:00
Ian Philips
5df594e46a Make details fit on one line, make group a link 2022-08-31 08:29:35 -06:00
Ian Philips
91e5abe76a Add query to help avoid timeout 2022-08-31 08:03:51 -06:00
Marshall Polaris
27b46f4306 Fix order of comments in threads and under answers 2022-08-31 01:16:57 -07:00
Marshall Polaris
d336383a93 Fix my foolish bug 2022-08-31 01:02:10 -07:00
James Grugett
a3569280a4 Add your bets section to /experimental/home 2022-08-31 00:30:31 -05:00
James Grugett
ccb6fd291e Move components out of /pages into /components 2022-08-30 23:53:12 -05:00
James Grugett
849402ed70 Rearrange home sections. Load more in carousel. 2022-08-30 23:47:09 -05:00
Marshall Polaris
7dddff52b8
Tidying some feed code up (#818)
* Clean up some markup & dead code

* Order comments in Firestore instead of on client

* Order bets in Firestore instead of on client

* Make indexes file up to date with production
2022-08-30 20:28:30 -07:00
mantikoros
40f1c09002 Auto-remove unused imports 2022-08-31 01:56:03 +00:00
mantikoros
ec90b041ee upgrade firebase, nextjs versions 2022-08-30 20:54:32 -05:00
Sinclair Chen
c202c5de68 clarify closed/open group copy 2022-08-30 16:28:49 -07:00
Ian Philips
aad5f6528b
new market view (#819)
* Show old details on lg, don't unfill heart

* Hide tip market if creator

* Small ui tweaks

* Remove contract. calls

* Update high-medium-low

* Remove unused bets prop

* Show uniques

* Remove unused bets prop
2022-08-30 17:13:25 -06:00
James Grugett
3e1e84ee5e Experimental Home: Add links. Single layer carousel for < 6 cards 2022-08-30 17:14:22 -05:00
James Grugett
f83b62cf50 Implement double carousel for /experimental/home 2022-08-30 16:18:42 -05:00
Austin Chen
d658a48b66 Revert "hide quick bet on mobile"
This reverts commit 3d073da97e.
2022-08-30 10:31:35 -07:00
Ian Philips
876abef040 Only send dev weekly trending emails to ian 2022-08-30 10:02:51 -06:00
Ian Philips
74c9406191 Use cached user ids while likes is loading 2022-08-30 09:52:14 -06:00
Ian Philips
a0402830c5
liking markets with tip/heart (#798)
* WIP liking markets with tip

* Refactor Userlink, add MultiUserLink

* Lint

* Lint

* Fix merge

* Fix imports

* wip liked contracts list

* Cache likes and liked by user ids on contract

* Refactor tip amount, reduce to M

* Move back to M

* Change positioning for large screens
2022-08-30 09:38:59 -06:00
FRC
e1f19c52ab
Post in a group about page. (#803)
* Dashboards in Group about page

* Rename group dashboard to 'About Post'

* Fixed James nits
2022-08-30 13:39:10 +01:00
Marshall Polaris
7debc4925e
De-feedify contract tab contents (#808)
* De-feedify contract bets list

* De-feedify contract comments lists

* Clean up a bunch of duplicated work in the comments list stuff

* Remove wrapper markup from comment replies list

* Fix sort order on comments I broke

* Kill now unhelpful `CommentRepliesList` wrapper component

* More random cleanup

* More cleanup and fix some styling I had broken

* Make bet calculations less wrong

* Keep up to date with master

* Make copy link component copy better URL

* Make highlighted comments align properly

* Make user header left align with content on comments

* Fix some free response UI stuff up
2022-08-30 02:41:47 -07:00
Marshall Polaris
1e3a0ca3d9
Upgrade Typescript, ESLint, Prettier (#817)
* Bump Typescript to 4.8.2, eslint, prettier

* Fix some loose typing

* Fix prettier complaint
2022-08-30 01:44:45 -07:00
James Grugett
c7452796f0 Recommend contracts you haven't bet on 2022-08-30 00:22:12 -05:00
Marshall Polaris
1369f3b967
WIP persistence work (#762)
* WIP persistence work

* Fix up close date filter, kill custom scroll restoration

* Use built-in Next.js scroll restoration machinery

* Tweaking stuff

* Implement 'history state' idea

* Clean up and unify persistent state stores

* Respect options for persisting contract search

* Fix typing in common lib

* Clean up console logging
2022-08-29 21:56:11 -07:00
James Grugett
1d948821ca Turn off react query subscription for user bets and portfolio history 2022-08-29 16:47:21 -05:00
Ian Philips
0318f7a12b Add missing parentheses 2022-08-29 13:47:24 -06:00
James Grugett
84432e5ac4 Add creatorId to lite market 2022-08-29 14:25:58 -05:00
FRC
851cffd73e
Dashboards (#791)
* Create backend for Dashboards

* Rm lastupdatetime for now

* Added a create-dashboard and sharable view dashboard page

* Various nit fixes.

* Renamed Dashboards to Posts

* Fix nits
2022-08-29 16:06:17 +01:00
Austin Chen
1d1b09c938 Append question changed text to end of description (instead of start) 2022-08-28 23:23:40 -07:00
Austin Chen
8f338a8d88
Prevent embeds from breaking in Chrome incognito (#814) 2022-08-28 22:40:57 -07:00
James Grugett
7ea6777d6b Add margin bottom to tournament cards to reveal shadow 2022-08-29 00:29:59 -05:00
James Grugett
ecacce0796 Remove console.log. Log onIdTokenChanged error. 2022-08-29 00:26:12 -05:00
James Grugett
71dfcc4dd9 Add tracking for clicking recommended card & tournament card 2022-08-29 00:23:31 -05:00
Austin Chen
6c64c9f1cd Remove hot volume from /tournaments 2022-08-28 22:21:28 -07:00
mantikoros
6facf3b7a7 sidebar ordering 2022-08-29 00:01:04 -05:00
James Grugett
62e72b2091 Loan dialog wording tweak 2022-08-28 23:51:43 -05:00
James Grugett
4dad954820 Change limit order label "at" => "up to" or "down to" 2022-08-28 23:47:11 -05:00
Austin Chen
f0727a65fc Add SF 2022 Ballot to /tournaments 2022-08-28 21:33:13 -07:00
akrolsmir
c7be227865 Auto-remove unused imports 2022-08-29 04:00:14 +00:00
Austin Chen
cf58fc9fd4 Remove Groups from sidebar 2022-08-28 20:59:14 -07:00
James Grugett
996b4795ea
Cache user bets tab with react query!! (#813)
* Convert useUserBets to react query

* Fix duplicate key warnings

* Fix react-query workaround to use refetchOnMount: always'

* Use react query for portfolio history

* Fix useUserBet workaround

* Script to back fill unique bettors in all contracts

* React query for user bet contracts, using uniqueBettorsId!

* Prefetch user bets / portfolio data
2022-08-28 18:03:00 -05:00
mantikoros
7e00f29189 back to "sign up to bet" 2022-08-28 16:55:29 -05:00
mantikoros
1ff453d64c eslint 2022-08-28 16:38:59 -05:00
mantikoros
e4c66e08f5 clean up add markets dialog 2022-08-28 16:26:36 -05:00
mqp
3fd07da1b0 Auto-prettification 2022-08-28 21:15:31 +00:00
mantikoros
eb070f0b07 Auto-remove unused imports 2022-08-28 21:14:44 +00:00
mantikoros
c88621de19 hide group edit dialog when signed out 2022-08-28 16:14:02 -05:00
mantikoros
2e96721a5c "sign in" => "add my answer" 2022-08-28 16:14:02 -05:00
mantikoros
0a5fb4752a CreateQuestionButton: use Button component 2022-08-28 16:14:02 -05:00
mantikoros
cae2154893 sign in button 2022-08-28 16:14:02 -05:00
mantikoros
926929880a "Sign up to bet" => "Place my bet"; "Sign in to comment" => "Add my comment"; rename button to BetSignUpPrompt 2022-08-28 16:14:02 -05:00
James Grugett
9c15d5b96c
React-query-ify notifications (#812)
* Use single react query to subscribe to notifications

* Remove 'preferred' in variable names
2022-08-28 15:20:21 -05:00
mantikoros
3d073da97e hide quick bet on mobile 2022-08-28 14:07:19 -05:00
mantikoros
d63dd12056 admin unlisted toggle 2022-08-28 13:37:34 -05:00
mantikoros
133e7a9c3f change label to admin 2022-08-28 13:37:34 -05:00
mantikoros
98861ccc19 remove typo 2022-08-28 13:37:34 -05:00
Marshall Polaris
1e11491369 Tidy up rendering of info tooltips 2022-08-28 01:43:13 -07:00
James Grugett
7c798a063c Improve edit close date UI 2022-08-28 00:35:24 -05:00
mantikoros
03e07037ea
ban users from posting (#810) 2022-08-28 00:23:25 -05:00
James Grugett
2acc1a8433 Double daily loans rate to 2% 💰💰 2022-08-28 00:11:28 -05:00
James Grugett
9dd23b4a08 Fix weird new crash in updateMetrics: contract.id missing? 2022-08-28 00:11:13 -05:00
James Grugett
e4f46c48f1 Fix recommended markets not updating when navigating 2022-08-27 22:35:46 -05:00
James Grugett
cb08a114ae Better recommended contracts. Include from first group. 2022-08-27 22:26:39 -05:00
James Grugett
e7f369e2b4 Load recommended markets even when navigating from home 2022-08-27 22:26:39 -05:00
mantikoros
f31db2f9ed emails: make banner a link 2022-08-27 22:15:14 -05:00
Marshall Polaris
b21051ced5 Fix up copy link toast styling 2022-08-27 19:15:55 -07:00
Marshall Polaris
ef77c7c9a3
Clean up markup in CopyLinkDateTimeComponent (#809) 2022-08-27 19:05:46 -07:00
James Grugett
36fa9078f5 Fix absolute import within functions 2022-08-27 17:18:39 -05:00
James Grugett
a80d1f194c Don't redeem shares if there's only epsilon shares to redeem 2022-08-27 17:14:41 -05:00
James Grugett
d7793841d1 Fix NaN invested (floating point error) 2022-08-27 17:13:29 -05:00
Marshall Polaris
4b513a894d
Make tooltip rendering more efficient (#807)
* Don't use very slow dayjs formatter on timestamp tooltips

* Kill dead code in feed-bets.tsx

* Clean up tooltip markup
2022-08-27 13:46:35 -07:00
mantikoros
eeed9eef10 market resolution email: link in header image, show only non-negative investment amount 2022-08-27 14:38:09 -05:00
mantikoros
305acbb18f "current value" => "expected value" 2022-08-27 14:17:19 -05:00
Marshall Polaris
5d8f5d41fc
Fix some efficiency problems with ContractProbGraph (#806)
* Memoize bets input to ContractOverview

* Optimize a bunch of nonsense in `ContractProbGraph`
2022-08-27 01:09:17 -07:00
Marshall Polaris
3e976eadac
Make portfolio graph loading more efficient (#805)
* Make portfolio graph on profile not load extra data

* Clean up unused props

* Tidy up markup

* Enable "daily" option again on portfolio history picker
2022-08-27 01:09:01 -07:00
James Grugett
51ceb62871 Fix console error on create page 2022-08-27 01:14:24 -05:00
James Grugett
a9ea335cd1 Fix create page serverside vs clientside render discrepancy console error 2022-08-27 01:07:39 -05:00
James Grugett
a040df2732 Fix console error from non-react-style attributes on trophy icon 2022-08-27 01:02:59 -05:00
James Grugett
2e3c2d4dcb Tweak to add market to group UI 2022-08-27 00:59:00 -05:00
mqp
5ff847fba3 Auto-prettification 2022-08-27 05:01:29 +00:00
jahooma
f641569bcc Auto-remove unused imports 2022-08-27 05:00:37 +00:00
James Grugett
8e4dd407f6 Test with unused import 2022-08-26 23:59:56 -05:00
James Grugett
b88f9a4fc1
Set up github action to remove unused imports 2022-08-26 23:56:38 -05:00
James Grugett
86cf956894 Add eslint plugin to remove unused imports 2022-08-26 23:49:03 -05:00
James Grugett
e4d6bb35b5 Fix floating button to be on top of quick bet arrows. Switch icon. 2022-08-26 23:10:10 -05:00
Marshall Polaris
902d9e140c
Create and use new usePagination hook for paginating loading (#769)
* Create and use new `usePagination` hook for paginating loading

* Fix index for new comment list code
2022-08-26 20:18:08 -07:00
Sinclair Chen
9698895c22 Update fr chart colors 2022-08-26 17:39:46 -07:00
Austin Chen
a2da319e7c Remove unused import 2022-08-26 17:35:59 -07:00
Austin Chen
1dbef921b0 Sort markets on /tournaments by % 2022-08-26 17:13:49 -07:00
Sinclair Chen
59aa76a474
Add Salem Center tournaments (card screenshots) (#804) 2022-08-26 16:23:44 -07:00
James Grugett
99bff6b794 Improve group market selection UI 2022-08-26 18:17:15 -05:00
James Grugett
5735864fd1 Add pencil to edit group on contract page 2022-08-26 17:25:05 -05:00
Sinclair Chen
8903b1ef95
Replace style props with tailwind classes (#800)
* add utility class for `word-break: break-word`

* refactor visuallyHidden style out of Page

* refactor out ref sizing hack in sidebar

* replace style props with tailwind classes
2022-08-26 14:23:06 -07:00
Austin Chen
3255806891 Support Figma embeds 2022-08-26 12:41:39 -07:00
Austin Chen
ba7d0f45db Close Add Market modal on Cancel 2022-08-26 12:41:29 -07:00
Sinclair Chen
490115d890
Add tournaments to sidebar (#802)
* Add tournaments to sidebar

* Remove unused import

* Reposition tournaments tab

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-26 10:45:01 -07:00
Sinclair Chen
803091db06
Add tournament home page (#797)
* Add tournament home page

* Preload markets, follow count

* organize imports

* Fix card width

* Make entire title clickable

* plural /tournament -> /tournaments

* prettier

* Fix /tournaments when groupIds are invalid

* Restyle /tournaments page

* Reintroduce Salem, tweak styles

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-26 10:31:25 -07:00
James Grugett
31db330319 Show "(max)" on streak payout if it is the max payout 2022-08-26 11:38:08 -05:00
James Grugett
b1ccee73fd If there is a group for a market on market page, clicking it goes to group 2022-08-26 01:23:50 -05:00
James Grugett
74ce98913c Make graph start from left side for new markets 2022-08-26 01:08:16 -05:00
Austin Chen
26a2eb2391 Switch to a different color scheme 2022-08-25 22:31:05 -07:00
James Grugett
6f2e2a3bbb Render graph for multiple choice embeds 2022-08-26 00:23:50 -05:00
James Grugett
539bfba70c Decrease starting time window for free response graph 2022-08-26 00:21:06 -05:00
James Grugett
e5777f02d8 Expand notifications by default if <= 3 items 2022-08-26 00:00:44 -05:00
Sinclair Chen
1b029ce8dd
bump tiptap version to fix multi-italic bug (#801) 2022-08-25 20:56:38 -07:00
Austin Chen
4faab4fcdc Clean up Featured code 2022-08-25 19:42:40 -07:00
Austin Chen
0f49effade Tweak Featured badge design 2022-08-25 19:17:50 -07:00
James Grugett
7773234138 Add debug console.log 2022-08-25 17:21:51 -05:00
Ian Philips
91bb4dfab2 With play money on numeric & center text 2022-08-25 12:06:42 -06:00
James Grugett
97b648a51e Move recommended markets below market white bg onto gray bg 2022-08-25 12:59:26 -05:00
Ian Philips
b785d4b047 With play money 2022-08-25 10:02:22 -06:00
Ian Philips
90e1fdb586 Add david to admins 2022-08-25 07:54:50 -06:00
Ian Philips
dc89d5d4d0 Feature markets on trending 2022-08-25 07:05:26 -06:00
Ian Philips
b9f0da9d3b Give all users 5 free markets 2022-08-25 05:51:56 -06:00
Ian Philips
6e3b8fdd4d Show old streak for old streak notifs 2022-08-25 05:10:38 -06:00
Ian Philips
465e219bfc Show old streak for old streak notifs 2022-08-25 05:10:24 -06:00
mantikoros
18f2550e4d resolution email: show n/a for canceled numeric 2022-08-24 23:44:30 -05:00
James Grugett
93739e7990 Fix betting streak number 2022-08-24 23:40:27 -05:00
James Grugett
51380febd4 Increase memory for updateStats function 2022-08-24 22:50:52 -05:00
James Grugett
cffd5dcd31 Weekly => daily loans 2022-08-24 22:03:07 -05:00
James Grugett
0caa5e24e8 Some other follow to watch changes 2022-08-24 21:23:12 -05:00
James Grugett
25eca71846 Convert heart to eye and follow to watch 2022-08-24 21:16:38 -05:00
Ian Philips
535e50eeac Betting streak bonus per day:10, max:50 2022-08-24 17:40:03 -06:00
Ian Philips
bca34dad60 Set max betting bonus to M 2022-08-24 17:31:35 -06:00
James Grugett
a8da5719fe Create experimental home page 2022-08-24 18:30:31 -05:00
Ian Philips
7a38d67c5b Reduce share row top margin on mobile 2022-08-24 17:11:48 -06:00
Ian Philips
7a22c7d76a Gap adjustment 2022-08-24 17:09:07 -06:00
Ian Philips
8d1cebf4db Move share button back down, small spacing tweaks 2022-08-24 17:07:22 -06:00
Ian Philips
b6e636cbc0 Small ux tweaks for signed out market page 2022-08-24 16:41:46 -06:00
mantikoros
5bf135760e fix sidebar tracking 2022-08-24 17:23:34 -05:00
SirSaltyy
74a0479cbd
Change about button (#796)
About button name change and now directs to "Help and About Center" super.so
2022-08-25 06:51:33 +09:00
Ian Philips
52a89d0783 Remove bolded More from navbar 2022-08-24 15:42:09 -06:00
Ian Philips
d553aae71e Shrink icon 2022-08-24 15:11:38 -06:00
Ian Philips
5365fa6175 💔💔💔 2022-08-24 15:09:28 -06:00
mantikoros
d5ac560f0c eslint 2022-08-24 15:36:57 -05:00
mantikoros
de74b2987a eslint 2022-08-24 15:34:00 -05:00
mantikoros
d390b39e0a eliminate fees 2022-08-24 15:29:48 -05:00
James Grugett
d6d1e8e86f Fix types 2022-08-24 13:29:35 -05:00
James Grugett
1c323c5a7f Simple recommended contracts based on contract creator 2022-08-24 12:59:23 -05:00
Ian Philips
3eb1b66e9a Lint 2022-08-24 11:58:32 -06:00
Ian Philips
c72bf506c3 Heart button on xl style 2022-08-24 11:53:29 -06:00
Ian Philips
432ee387ec Show all groups on sidebar 2022-08-24 11:23:07 -06:00
Ian Philips
a5812a5a73 Remove group chat display 2022-08-24 11:15:38 -06:00
Ian Philips
5dcaae7af6 Fix import 2022-08-24 10:51:21 -06:00
Ian Philips
480371cf9f Fix import 2022-08-24 10:50:55 -06:00
Ian Philips
f50b4775a1
Allow to follow/unfollow markets, backfill as well (#794)
* Allow to follow/unfollow markets, backfill as well

* remove yarn script edit

* add decrement comment

* Lint

* Decrement follow count on unfollow

* Follow/unfollow button logic

* Unfollow/follow => heart

* Add user to followers in place-bet and sell-shares

* Add tracking

* Show contract follow modal for first time following

* Increment follower count as well

* Remove add follow from bet trigger

* restore on-create-bet

* Add pubsub to dev.sh, show heart on FR, remove from answer trigger
2022-08-24 10:49:53 -06:00
James Grugett
78780a9219 Dedup contract leaderboards code from contract slug (merge error?) 2022-08-23 19:25:57 -05:00
Sinclair Chen
7da4eb8fe9
Fix bet modal probability sticking (#793)
* Fix button group styles
* Reset prob strike-out when bet modal closed
2022-08-23 14:31:52 -07:00
Ian Philips
bea94d58c5 Add extra text-sm 2022-08-23 07:55:26 -06:00
mantikoros
1c73d21925 weeklyMarketsEmails: send different markets to different users 2022-08-23 00:27:07 -05:00
Sinclair Chen
b476a7e3f8
Take descriptions out of LiteMarket (#789) 2022-08-22 18:18:51 -07:00
Sinclair Chen
baa27a3c85 Make Sinclair an admin 2022-08-22 17:50:59 -07:00
Sinclair Chen
20fd286756
Fix link classes duplicating on paste (#788) 2022-08-22 17:45:23 -07:00
Austin Chen
552f9add70 Reduce min time on contract graph to 1h
Allows more resolution on real-time markets, where a lot of trading happens within minutes
2022-08-22 17:23:59 -07:00
Ian Philips
3bea983662 Be more explicit after unsubscribing from weekly trending 2022-08-22 16:56:28 -06:00
Ian Philips
6929076740 Be more specific about unsubscribe 2022-08-22 16:43:08 -06:00
Ian Philips
e1775681aa Add weekly email sent flag, filter out manifold grouped markets 2022-08-22 16:36:39 -06:00
Sinclair Chen
ec4d0f6b4a
Fix notification for updated questions (#782)
* Fix update notification for question, description

* Don't notify on updated description
2022-08-22 15:26:54 -07:00
Ian Philips
b9a667b126 Add logs to weekly emails 2022-08-22 14:59:11 -06:00
mantikoros
571cf80e13 markets api: only load 500 markets by default 2022-08-22 14:42:23 -05:00
mantikoros
3313b55853 listAllContracts: sort by createdTime by default 2022-08-22 14:23:52 -05:00
Austin Chen
650aa68bcd Fix imports 2022-08-22 11:31:33 -07:00
Austin Chen
7736f1e3c1 Make duplicating better: description, closetime, logscale
Known issue: some markets like https://manifold.markets/FFSX/rojo-ronald-jones don't duplicate because too much stuff in JSON...?
2022-08-22 10:49:54 -07:00
Austin Chen
0cd61eb214 DX: Link to Firestore console from "..." 2022-08-22 10:48:21 -07:00
mantikoros
2530171721 don't run next-sitemap post build 2022-08-22 12:09:16 -05:00
mantikoros
009c85b61a listAllContracts: order by popularity score 2022-08-22 12:07:05 -05:00
mantikoros
7a0d64e72f host sitemap manually (delete nextjs sitemap) 2022-08-22 12:02:08 -05:00
mantikoros
40a22b31f3 fix sitemap 2022-08-22 11:52:05 -05:00
mantikoros
8ea9a79760 loan emoji 2022-08-22 10:31:23 -05:00
James Grugett
e6db99e810 Check loans calc for isFinite 2022-08-22 10:20:22 -05:00
Ian Philips
571d3e71b5 Only show most recent streak notif, relative econ imports, pubsub emulator 2022-08-22 06:31:30 -06:00
Austin Chen
b7790a9678 Show Referrals count for each user 2022-08-21 22:53:02 -07:00
Austin Chen
88bf678ce3
Allow custom environments to override any economic aspect (#787)
* Extract monetary constants to a single file economy.ts

* Add missing import

* Allow environments to override any econ variable

* Update imports

* Update more imports

* Fix import
2022-08-21 22:37:26 -07:00
James Grugett
8b7cd20b6f
Loans2: Bets return you some capital every week (#588)
* Remove some old loan code

* Almost complete implementation of updateLoans cloud function

* Merge fixes

* Use invested instead of sale value, check if eligible, perform payouts

* Run monday 12am

* Implement loan income notification

* Fix imports

* Loan update fixes / debug

* Handle NaN and negative loan calcs

* Working loan notification

* Loan modal!

* Move loans calculation to /common

* Better layout

* Pay back loan on sell shares

* Pay back fraction of loan on redeem

* Sell bet loan: negate buy bet's loan

* Modal tweaks

* Compute and store nextLoanCached for all users

* lint

* Update loans with newest portfolio

* Filter loans to only unresolved contracts

* Tweak spacing

* Increase memory
2022-08-22 00:22:49 -05:00
Austin Chen
3158740ea3 Minor tweaks for custom instances 2022-08-21 22:13:42 -07:00
Austin Chen
258b2a318f Default to showing weekly bet graph 2022-08-21 21:02:56 -07:00
Austin Chen
aa3647e0f3 Handle case when no charity txns 2022-08-21 20:55:04 -07:00
James Grugett
d18dd5b8fb Fix a case of limit order sorting 2022-08-21 15:58:49 -05:00
James Grugett
645cfc65f4 Update sort order of limit orders (older bets first b/c they are filled first) 2022-08-21 12:57:00 -05:00
mantikoros
97b38c156f Revert "create contract: ante no longer user liquidity provision"
This reverts commit 56e9b5fa2f.
2022-08-20 15:34:52 -05:00
mantikoros
c6dc852cd8 send creator guide on D1 2022-08-20 15:34:34 -05:00
mantikoros
ef127ea335 update welcome email 2022-08-20 15:34:34 -05:00
mantikoros
43bbc9ec24 send followup email on D2 2022-08-20 15:34:34 -05:00
Marshall Polaris
2439317408
Convert tags to groups on demand (#784)
* Fix stuff to not prematurely initialize Firebase when imported

* Add script to convert a tag to a group with the same contracts
2022-08-20 13:32:12 -07:00
James Grugett
f4ebb2b504 Fix wrapping on "Add market" button 2022-08-20 15:19:05 -05:00
Sinclair Chen
a9f846e8fc
Fix tooltip styles in your bets (#783)
* Only show answer tooltips if truncated

* Always wrap in tooltip

* refactor. make title in dialog less big
2022-08-20 13:05:33 -07:00
James Grugett
099764a931 Show unlisted markets in groups and under your markets 2022-08-20 14:38:15 -05:00
James Grugett
09e8993cd4 Implement visibility option for new markets 2022-08-20 14:31:32 -05:00
mantikoros
dd6c5dc97a betting streaks copy 2022-08-20 13:47:26 -05:00
James Grugett
2fef413d88 Don't show fantasy football in newest sort 2022-08-20 13:46:14 -05:00
James Grugett
474304d284 Revert "🔥🔥🔥🔥🔥🔥🔥🔥"
This reverts commit fc8487dca0.
2022-08-20 11:45:13 -05:00
Austin Chen
6791da0fc8 Fix "Most Recent Donor" on /charity 2022-08-19 17:28:08 -07:00
Sinclair Chen
c850cfe97f Revert "Revert "fix firefox visual glitch - single card wrapping""
This reverts commit 63a5241b2e.
2022-08-19 16:59:42 -07:00
Austin Chen
51c843d765
Use masonry on contract cards, sorted correctly (#773)
* Revert "Revert "Tile contract cards in masonry layout (#761)""

This reverts commit 62728e52b7.

* Sort the contracts in the correct masonry order

* Fix ordering on single columns

* Use react-masonry-css to accomplish masonry view

* Improve comment

* Remove gridClassName

Everything is spaced with m-4, too bad
2022-08-19 16:57:23 -07:00
Ian Philips
03d98a7ad7 Reset hour to 12am utc 2022-08-19 17:16:17 -06:00
mantikoros
0cbc0010c1 schedule emails from onCreateUser; send interesting markets on D1 2022-08-19 17:03:00 -05:00
Ian Philips
fc8487dca0 🔥🔥🔥🔥🔥🔥🔥🔥 2022-08-19 16:00:40 -06:00
James Grugett
b67a26ad61 Don't show bets streak modal on navigate each tab 2022-08-19 16:51:52 -05:00
Ian Philips
39c312cf9f Explicitly pass utc timezone 2022-08-19 15:19:52 -06:00
Ian Philips
1196ec4375 Send 6 trending emails to all users monday 12pm PT 2022-08-19 15:01:53 -06:00
Ian Philips
634196d8f1 Slice the popular emails to the top 20 2022-08-19 14:45:04 -06:00
Ian Philips
36bfbe8f42 Change betting streak modal, tweak trending email query 2022-08-19 14:37:16 -06:00
Ian Philips
a0f62ba172
Markets emails (#764)
* Send out email template for 3 trending markets

* Rich text to plaintext descriptions, other ui changes

* Lint

* Filter for closed markets

* Change sign

* First order must be closeTime

* Send 6 emails, check flag twice

* Exclude contracts with trump and president in the name

* interesting markets email

* sendInterestingMarketsEmail

* Change subject line back

Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-08-19 11:43:57 -06:00
Ian Philips
ba5dabd613 Increase gap between profit and streak 2022-08-19 11:24:28 -06:00
Ian Philips
00c9fa61c3
betting streaks (#777)
* Parse notif, show betting streaks modal, schedule function

* Ignore streaks of 0

* Pass notifyFills the contract

* Turn 9am into a constant

* Lint

* Up streak reward, fix timing logic

* Change wording
2022-08-19 11:10:32 -06:00
Sinclair Chen
4f3202f90b
Simple bet interface in embeds (#775)
* rename BetRow -> BetButton

* Replace bet modal in embed with inline betting

- Also simplifies graph height calculation

* Move bet row above graph, in "mini modal"

* Show signup button if not signed up

* Show probability change

* Show error after modal

- Show balance if insufficient funds
- Clear error from amount input if amount deleted entirely

* Fix error state conditions

- Reset amount input on success
- Reset success state on user input

* Make input smaller (80px)
2022-08-19 10:07:48 -07:00
Sinclair Chen
98a0ed99c9 Fix (i) alignment 2022-08-19 09:53:16 -07:00
Sinclair Chen
4d7df00a68
Make Avatar component update when avatarUrl updates (#780) 2022-08-19 09:47:00 -07:00
Barak Gila
f51ad2224b
add YIMBY Law and CaRLA as charities (#781) 2022-08-19 10:52:01 -05:00
Marshall Polaris
0972de9025
Make typing for comments more fancy (#776) 2022-08-19 01:06:40 -07:00
Sinclair Chen
f2764e9258
Remove keyboard accessibility for tooltips (#779)
Headless UI's Modal component autofocuses the first focusable item
inside it when opened. This is by design for accessibility reasons.
See https://headlessui.com/react/dialog#managing-initial-focus

Ironically this means we'll have to remove keyboard focus for tooltips
because this causes the tooltips to pop up unnecessarily for all users
whenever the dialog is opened. The alternative is managing focus
manually for several dialogs, which may not be possible as some of our
modals lack a sensible element to focus by default.
2022-08-18 18:54:09 -07:00
Sinclair Chen
2537663a57
Fix user avatar in mention list not updating (#778) 2022-08-18 17:20:40 -07:00
Marshall Polaris
0cf9a90cfb
Remove some dead code related to tags, categories, and old feed stuff (#765)
* Remove dead image storage code

* Kill tag page

* Kill tag and categories related component UI

* Kill some old algo feed kind of code
2022-08-18 15:46:11 -07:00
Keri Warr
4f6d478211
List manifold-sdk on the Awesome Manifold page (#774) 2022-08-18 16:59:18 -05:00
Marshall Polaris
06ced7042d Fix a typo in my script 2022-08-18 12:49:01 -07:00
Marshall Polaris
c37997bcb7
Add comment type field to comments (#772) 2022-08-18 12:47:35 -07:00
Austin Chen
c2db558b85 Describe why subsidizing is good 2022-08-18 10:12:38 -07:00
James Grugett
097000c9da Don't scroll to top on search change except on home 2022-08-18 11:23:16 -05:00
mantikoros
d216b298ba "create-contract.ts" => "create-market.ts" 2022-08-18 11:14:16 -05:00
mantikoros
56e9b5fa2f create contract: ante no longer user liquidity provision 2022-08-18 11:14:16 -05:00
Ian Philips
c9c3a95d2a Condense user profile bits 2022-08-18 09:54:30 -06:00
mantikoros
87561503c1 accept challenge: redeem shares 2022-08-18 10:39:48 -05:00
Ian Philips
68a949de35 Change Challenge page wording 2022-08-18 08:22:37 -06:00
Ian Philips
33edd3c0fb
Create challenge without previous market (#718)
* Create challenge without previous market

* Check if they've balance to create both on fe

* Change wording slightly

* Finish merge
2022-08-18 08:15:20 -06:00
Ian Philips
c3d09e5323 Add links to challenge page 2022-08-18 07:53:19 -06:00
Marshall Polaris
97fa5fa636
Replace /markets with /home (#766)
* Make /home not kick out logged out users

* Point people at /home instead of /markets
2022-08-17 23:15:25 -07:00
marsteralex
fb67010c0e
include draft innovation basics (#771)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

* include battlebond

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-17 18:17:29 -07:00
marsteralex
5bf39a7a92
sort by set name (#770)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

* sort names by set instead of by set symbol

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-17 18:01:59 -07:00
marsteralex
2c97be815b
remove check from original (#768)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

* removed check from original

* remove check from original

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-17 16:03:02 -07:00
mantikoros
159723ed0c market creation email 2022-08-17 17:36:52 -05:00
marsteralex
ce3d092497
Add Basic Lands to MTG Guesser (#716)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

* add basic land guesser

also added fetcher to filter all cards instead of only unique art

* default to original

makes basic better

* added set symbol to basics

added set symbol to the basics game mode. Changed name to "How Basic"

* cleanup

* changed some pixels

* only load set data if needed

* hacked fix for removing image from name

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-08-17 12:40:59 -07:00
Sinclair Chen
99009f841b Make text of old chats same size as current 2022-08-17 10:45:30 -07:00
mantikoros
bf64f5b3a9 redirect /about; more content in welcome email 2022-08-17 11:21:29 -05:00
James Grugett
770a8d049c Check limit prob with floating equals 2022-08-17 10:55:47 -05:00
Ian Philips
a00857cb45 Fix wrapping close date and truncate group name 2022-08-16 16:03:55 -06:00
Sinclair Chen
59565416b6
Api fixes (#704)
* Add min, max, isLogScale to numeric market API return

* Add lastUpdatedTime to market API

* Return a string description in market API

* Accept string descriptions in market POST api

* install prettier eslint config. fix import

* fix another import
2022-08-16 15:01:03 -07:00
James Grugett
8c2f3c56d3 Limit orders: Subtract fees from "profit if both filled" 2022-08-16 15:51:04 -05:00
James Grugett
814c4aa01d Change limit prob validation to be only on Binary markets (not numeric) 2022-08-16 15:44:58 -05:00
James Grugett
62728e52b7 Revert "Tile contract cards in masonry layout (#761)"
This reverts commit 4002c23bee.
2022-08-16 13:03:04 -05:00
James Grugett
63a5241b2e Revert "fix firefox visual glitch - single card wrapping"
This reverts commit ec7263da18.
2022-08-16 13:02:55 -05:00
mantikoros
c58ed8bd2c personal followup email 2022-08-16 11:45:58 -05:00
mantikoros
c3eaf0351b one week email changes 2022-08-16 11:44:01 -05:00
Marshall Polaris
59ca1f7640
Denormalize some contract comment fields (#760)
* Make `groupConsecutive` more capable

* Put denormalized `contractQuestion` and `contractSlug` on comments

* Update user profile UI to use new denormalized fields

* `/Austin` -> `/market`
2022-08-15 22:43:46 -07:00
Marshall Polaris
d00fe7bcd2
Backend robustness to email sending or analytics tracking failures (#728)
* Make `sendEmail` functions await email send success

* Make tracking and email sending not throw on failure
2022-08-15 22:13:38 -07:00
Marshall Polaris
186befd0ac
Bail out earlier if createmarket is called with invalid group ID (#745)
* Bail out earlier if `createmarket` is called with invalid group ID

* Fix typing in `createmarket`
2022-08-15 22:12:43 -07:00
Sinclair Chen
ec7263da18 fix firefox visual glitch - single card wrapping 2022-08-15 22:08:09 -07:00
James Grugett
f2f77cb51e Resolve market emails: fix negative amount bug with better invested calculation 2022-08-15 21:48:01 -05:00
James Grugett
e5aef763cd Calculate invested properly for DPM 2022-08-15 21:48:01 -05:00
James Grugett
aef14e49bb Update bet type to explain dpm props 2022-08-15 21:48:01 -05:00
Ian Philips
cd520e6cfe lint 2022-08-15 19:47:58 -06:00
Ian Philips
d56435b9cd Signed out home page shows dynamic trending markets 2022-08-15 19:34:45 -06:00
Sinclair Chen
4002c23bee
Tile contract cards in masonry layout (#761) 2022-08-15 17:41:53 -07:00
James Grugett
997d68a574 Compute invested & display in your bets 2022-08-15 19:04:37 -05:00
Austin Chen
34e8138e50 Show placeholder when avatarUrl errors 2022-08-15 16:33:02 -07:00
Ian Philips
428d9a3692 Move avatar to below card on mobile 2022-08-15 13:49:33 -06:00
James Grugett
2ff2d6c1fc Scroll to top for fresh query 2022-08-15 14:26:18 -05:00
mantikoros
5c49461449 new welcome email 2022-08-15 11:12:33 -05:00
James Grugett
c80f82a3f7 Home page hack: discard NextJS router state 2022-08-15 11:06:42 -05:00
Marshall Polaris
972f215f0c
Rewrite useQueryAndSortParams machinery to be faster/simpler/better (#758)
* Rewrite useQueryAndSortParams machinery to be faster/simpler/better

* Politely debounce Algolia querying

* Tidy some stuff up

* Style changes suggested by James
2022-08-14 22:09:25 -07:00
mantikoros
5d14d79e6e share dialog: remove native sharer; use toast for embed 2022-08-15 00:03:05 -05:00
mantikoros
b57c84bbd9 notifications title/seo 2022-08-14 23:55:11 -05:00
James Grugett
4e1fae5b5f Require a whole percentage for limitProb in back end 2022-08-14 20:51:10 -05:00
Marshall Polaris
0b711be480
Clean up a bunch of markup and CSS on contract cards (#753)
* Remove random unnecessary top-level divs

* Remove wrapper in MiscDetails

* Remove another wrapper in ContractCard

* Fix a bunch of weird CSS stuff
2022-08-14 01:05:17 -07:00
Marshall Polaris
69c49679f1
Move search controls into separate component (#757)
* Move search controls into separate component

* Fix up typing on pill groups thingy

* More precise comparison per James

* Make sure `additionalFilter` is passed into controls
2022-08-13 16:34:03 -07:00
Marshall Polaris
0085ffcb0b
Simplify and fix inefficiencies in contract search component (#756)
* Simplify and fix inefficiencies in contract search component

* Add react-dom types

* Add a clarifying comment

* Improve search per some feedback
2022-08-13 13:15:11 -07:00
James Grugett
0a9df3ac6b Group horizontal margin on tabs 2022-08-13 13:50:26 -05:00
James Grugett
aeea66491a Group question => market 2022-08-13 13:49:25 -05:00
Marshall Polaris
456d9398a1
Revamp a lot of stuff on the user page to make it usably efficient (#751)
* Load bets and comments tabs data on user page independently

* Implement basic pagination on profile comments list

* Tweak server auth to return `null` instead of `undefined`

* Switch to SSR for user page

* Fix lint

* Fix broken contract fetching in user bets list

* Tidying
2022-08-12 20:42:58 -07:00
Sinclair Chen
dcc3c61f52
Only calculate position when tooltip is shown (#755) 2022-08-12 20:35:08 -07:00
Marshall Polaris
0f7f55ec0a Fix embarrassing bug in server auth 2022-08-12 20:14:24 -07:00
Marshall Polaris
e4239d0122
Tweak Firestore user rules to be more robust (#750) 2022-08-12 20:13:09 -07:00
Sinclair Chen
facb19a347
fix dependency peer-dep warnings, mostly (#752) 2022-08-12 17:49:08 -07:00
Marshall Polaris
96a378ec4b
Make RelativeTimestamp a little more efficient (#754)
* Don't do extra dayjs work in timestamp components

* Remove extra wrapper from `RelativeTimestamp`
2022-08-12 17:48:41 -07:00
Marshall Polaris
79be0c555b Fix tiny bug in auth context code 2022-08-12 13:45:38 -07:00
Marshall Polaris
3cb28cdecb
Teach AuthContext to manage the private user doc (#738)
* Return both user and privateUser from `createuser`

* Make `useStateCheckEquality` more flexible

* Make `AuthContext` track the private user doc

* Change `usePrivateUser` hook to use the auth context data

* Pass both user and private user through SSR to auth context

* Fix bug in create user flow
2022-08-12 13:41:00 -07:00
James Grugett
3cbf5a6f7d Always show search placeholder 2022-08-12 14:35:27 -05:00
Sinclair Chen
20ab313c6c Improve profile comments vis d 2022-08-12 12:10:45 -07:00
Sinclair Chen
88535e5512 fix lint error 2022-08-12 12:10:07 -07:00
Sinclair Chen
df858f916b
Migrate daisy tooltips to our own to fix cutoffs (#748)
* Make all tooltips use our component

* Stop mobile tooltip crop (daisy -> floating-ui)

* Show tooltip on tap for touch devices

Except tooltips on buttons

* migrate another daisy tooltip to ours

* Prevent hidden tooltip from covering click/hover
2022-08-12 12:04:23 -07:00
mantikoros
d2b634c775 template email tracking 2022-08-12 11:33:02 -05:00
mantikoros
8ebccd05ec market movement warning; add bankroll warning to FR markets 2022-08-12 11:24:08 -05:00
Marshall Polaris
80fd38990f Experimentally do not optimizeCss 2022-08-11 21:07:54 -07:00
Austin Chen
7ad8af848a Replace DaisyUI buttons with TailwindUI buttons
Maybe this should use the button component...? But that's styled differently, the rest of /create uses standard TailwindUI
2022-08-11 20:54:12 -07:00
Marshall Polaris
e2eae01ad8
Add a shitload of logging to the server auth code (#749) 2022-08-11 20:46:18 -07:00
Marshall Polaris
38d9e8190c
Only load portfolio history inside user page bets tab (#747) 2022-08-11 20:44:51 -07:00
Austin Chen
af4c442105 Support Twitch video and channel embeds 2022-08-11 20:23:33 -07:00
Austin Chen
9311652bed
Support Youtube, Tweet, and Metaculus embeds in editor (#744)
* Embed a tweet by URL

* Clean up imports

* Prevent tweetId from getting interpreted as a number

* Use a single place to embed iframe, Youtube, and Tweets

* Support Manifold and Metaculus embeds

* Remove unused import

* Simplify Manifold paste logic

* Clean up embed rewrite code

* Add back comment

* Rejigger config so tsx is only in web/

* Clean up comment

* Revert unnecessary tsconfig change

* Fix placeholder

* Lighten placeholder
2022-08-11 20:18:01 -07:00
Sinclair Chen
daba28423a
Improve create page UI (#746)
* Adjust create page styles

* Keep answers when switching market type
2022-08-11 14:41:21 -07:00
Austin Chen
dc95587cca
Add editor toolbar to choose and embed markets (#702)
* Embed markets using the "add markets" template

* Override dev domain

* Improve market modal style

- contract searchbar now sticky
- entire card clickable to select (if quickbet is hidden)
- adjust selected card styles

* remove extra export

* Hide pills

* Fix browser redirect warning

* Insert all markets instead of just one

* fix type error

* fixup "Insert all markets instead of just one"

Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>
2022-08-11 14:32:02 -07:00
Marshall Polaris
4e8b94a28c
Componentize confetti to isolate re-renders due to window size (#740)
* Componentize confetti to isolate re-renders due to window size

* Clean up debug logging
2022-08-11 12:55:25 -07:00
Marshall Polaris
b9f347b7f4
Use UserFollowButton instead of FollowButton in UserPage (#742) 2022-08-11 12:54:09 -07:00
Marshall Polaris
ad75ecdc87
Move liquidity provision fetch down into ContractTabs (#741) 2022-08-11 12:53:54 -07:00
Marshall Polaris
daa86fa330
Change tabs to keep all individual tab components in the DOM (#743) 2022-08-11 12:53:42 -07:00
Sinclair Chen
99326eb65a fix spacing of long group names on markets 2022-08-11 12:30:34 -07:00
James Grugett
3f6ca6c8ed Make Manifold account able to resolve markets 2022-08-11 00:38:15 -05:00
James Grugett
6e93f11a59 Fix bolded group chat not getting unbolded 2022-08-11 00:00:40 -05:00
James Grugett
61ae481a03 Document cancel bet 2022-08-10 18:43:11 -05:00
Marshall Polaris
8c537537a1
Add cache headers to avatars (#737)
* Set cache headers on newly uploaded avatars

* Go fix up all the old avatars to have cache headers
2022-08-10 11:03:55 -07:00
Austin Chen
b5b77be188 Accept URLs in the iframe editor
TODO: Update placeholder text to mention this
2022-08-10 11:03:39 -07:00
James Grugett
d7b021b79f Clear entered limit probs on submit limit order 2022-08-10 12:37:51 -05:00
James Grugett
654790315c Fix missing key console error 2022-08-10 12:33:29 -05:00
mantikoros
35df201e2e prob bar for multiple choice 2022-08-10 12:32:30 -05:00
SirSaltyy
e591de8b29
Increase description max length (#739) 2022-08-11 02:31:28 +09:00
James Grugett
4d953d58a1 Move group chat back into a tab 2022-08-10 12:28:02 -05:00
mantikoros
dc26db2864 add salem to sidebar; clean up code 2022-08-10 12:17:40 -05:00
jahooma
3d30a1adbc Auto-prettification 2022-08-10 17:06:34 +00:00
James Grugett
05c9d3513a Don't reference window outside useEffect or click event. 2022-08-10 12:05:56 -05:00
mantikoros
52a2a3d842 track search 2022-08-10 11:50:21 -05:00
Marshall Polaris
521c479abf Fix an embarrassing bug in getPrivateUser 2022-08-09 23:55:41 -07:00
Marshall Polaris
818c90a95e
Refactor tipper (#734)
* Clean up tipping components

* Pass comment into tip callback
2022-08-09 23:05:56 -07:00
mantikoros
5f77a026aa fix modal 2022-08-09 21:59:40 -05:00
mantikoros
63538ae925 referral link on user page, manalinks, market share dialog; native sharer on mobile 2022-08-09 21:51:08 -05:00
Sinclair Chen
0b9ca6b7ee
Editor improvements (#735)
* Allow focus on all parts of editor

* Fix background and text colors

* Restrict height of image in comment

* Remove "Type *markdown*" placeholder

it's a little misleading (can't do markdown links) and messes with focus

to be replaced with a highlight menu in the future
2022-08-09 19:04:55 -07:00
Marshall Polaris
c07daafb8d
Make homepage load user via SSR, pass it to contract stuff (#729) 2022-08-09 15:28:52 -07:00
Marshall Polaris
847d3d0f27
Fix efficiency problems with visibility checking code (#730)
* Fix problems with visibility checking code

* Tear out old contract tracking stuff per James

* Use `useEvent` in VisibilityObserver per James suggestion
2022-08-09 15:28:27 -07:00
Marshall Polaris
5715b0e44a
Random contract page fixups (#712)
* Remove some divs and so on

* Correctly align bet avatars and text in feed

* Extract sidebar component on contract page
2022-08-09 13:25:42 -07:00
mantikoros
1e3c5cb936 add qr code to referrals 2022-08-09 12:27:52 -05:00
Sinclair Chen
914fc476ce
Remove top/bottom margin from indented list items (#733) 2022-08-09 10:17:44 -07:00
Austin Chen
49541d3eec Stop interpolating on portfolio value graph 2022-08-09 10:08:14 -07:00
Marshall Polaris
592125b5e7
Fix broken useBets filters (#731) 2022-08-09 10:50:11 -05:00
Marshall Polaris
e7f1d3924b
Fix up several pages to load user data on the server (#722)
* Fix up several pages to load user data on the server

* Add key prop to `EditUserField`
2022-08-08 22:43:04 -07:00
Marshall Polaris
5649161348
Pass page props user to auth provider if present (#724)
* Pass page props user to auth provider if present

* Rename `user` -> `serverUser`

* Don't load from local storage if server told us a user
2022-08-08 22:42:52 -07:00
Austin Chen
fd308151b3 Disable bouncing Challenge 2022-08-08 15:24:30 -07:00
Marshall Polaris
85e55312ca
What will be removed, is removed (#721) 2022-08-08 15:05:25 -07:00
James Grugett
98806a806f Fix query params on emulator/private instance 2022-08-07 18:07:36 -07:00
James Grugett
8fb3b42ea1 Default to trending. Fix close date being opposite 2022-08-07 17:48:43 -07:00
Austin Chen
a910e5dc17 Revert "Revert "Fix a bug with expiration of refresh and custom tokens""
This reverts commit 012b67e3c5.
2022-08-07 09:57:18 -07:00
Austin Chen
012b67e3c5 Revert "Fix a bug with expiration of refresh and custom tokens"
This reverts commit abd344b951.
2022-08-07 09:56:42 -07:00
Marshall Polaris
abd344b951 Fix a bug with expiration of refresh and custom tokens 2022-08-06 19:24:50 -07:00
James Grugett
1f8aef2891 Disable challenges for private instances 2022-08-06 17:45:21 -07:00
Sinclair Chen
da977f62a9
Make added text go after img instead of replacing (#725) 2022-08-06 15:43:41 -07:00
Sinclair Chen
5892ccee97
Rich text in comments, fixed (#719)
* Revert "Revert "Switch comments/chat to rich text editor (#703)""

This reverts commit 33906adfe4.

* Fix typing after being weird on Android

Issue: character from mention gets deleted. Most weird on Swiftkey:
mention gets duplicated instead of deleting.

See https://github.com/ProseMirror/prosemirror/issues/565
https://bugs.chromium.org/p/chromium/issues/detail?id=612446

The keyboard still closes unexpectedly when you delete :(

* On reply, put space instead of \n after mention

* Remove image upload from placeholder text

- Redundant with image upload button
- Too long on mobile

* fix dependency
2022-08-06 13:39:52 -07:00
Marshall Polaris
d43b9e1836
Vercel auth phase 2 (#714)
* Add cloud function to get custom token from API auth

* Use custom token to authenticate Firebase SDK on server

* Make sure getcustomtoken cloud function is fast

* Make server auth code maximally robust

* Refactor cookie code, make set and delete more flexible
2022-08-05 20:49:29 -07:00
James Grugett
e0196f7107 Rename file contracts-list to contracts-group 2022-08-05 17:46:32 -07:00
James Grugett
b3b06896be Add loading indicator when algolia search is initially loading 2022-08-05 17:44:55 -07:00
Marshall Polaris
48ac21ffad Add comment explaining fishy token 2022-08-05 16:08:30 -07:00
Marshall Polaris
bf3ba8ac3f Remove test file 2022-08-05 16:07:02 -07:00
mqp
bba9f9a555 Auto-prettification 2022-08-05 23:03:25 +00:00
Marshall Polaris
7e0634aee0 Try using a personal access token for formatter job 2022-08-05 16:02:46 -07:00
Marshall Polaris
f05db0ef0f Give formatting workflow even more permissions... 2022-08-05 15:56:10 -07:00
Marshall Polaris
db3b0c2cf5 Give repo write permission to formatting workflow 2022-08-05 15:38:22 -07:00
Marshall Polaris
d9ddabcfd4 Commit some un-pretty code 2022-08-05 15:35:57 -07:00
Marshall Polaris
67139b99f9
Add workflow to automate prettier running on main (#720) 2022-08-05 15:33:34 -07:00
mantikoros
5e89628593 challenge bet tracking 2022-08-05 13:42:09 -07:00
mantikoros
f11c9a3341 bouncing challenge button (temporary gimmick) 2022-08-05 13:42:09 -07:00
James Grugett
ced404eb74 Local search filters on groups, exclude contractIds 2022-08-05 12:01:16 -07:00
Ian Philips
60ebadbbe5 Add not about donating winnings to charity 2022-08-05 09:58:02 -06:00
Ian Philips
f47b70dd3c Darken numeric preview text 2022-08-05 07:08:41 -06:00
Ian Philips
de6d5b388a Lint 2022-08-05 06:58:39 -06:00
Ian Philips
1c80bf1faf Chat icon => users icon 2022-08-05 06:58:29 -06:00
Ian Philips
97e3de4e0f Show numeric values in card preview 2022-08-05 06:56:10 -06:00
Ian Philips
d90901b4e3 Check creator balance again upon acceptance 2022-08-05 05:03:47 -06:00
mantikoros
f3704633ee liquidity panel styling 2022-08-05 00:03:38 -07:00
mantikoros
5988dd1e48 improved create challenge modal; 2xs button 2022-08-04 23:42:35 -07:00
mantikoros
16f4fb9490 disable clicking on group in embed 2022-08-04 22:47:59 -07:00
mantikoros
4d153755c1 delete challenge button 2022-08-04 22:33:56 -07:00
mantikoros
1e66f4d140
Share row (#715)
* Challenge bets

* Store avatar url

* Fix before and after probs

* Check balance before creation

* Calculate winning shares

* pretty

* Change winning value

* Set shares to equal each other

* Fix share challenge link

* pretty

* remove lib refs

* Probability of bet is set to market

* Remove peer pill

* Cleanup

* Button on contract page

* don't show challenge if not binary or if resolved

* challenge button (WIP)

* fix accept challenge: don't change pool/probability

* Opengraph preview [WIP]

* elim lib

* Edit og card props

* Change challenge text

* New card gen attempt

* Get challenge on server

* challenge button styling

* Use env domain

* Remove other window ref

* Use challenge creator as avatar

* Remove user name

* Remove s from property, replace prob with outcome

* challenge form

* share text

* Add in challenge parts to template and url

* Challenge url params optional

* Add challenge params to parse request

* Parse please

* Don't remove prob

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Add to readme about how to dev og-image

* Add emojis

* button: gradient background, 2xl size

* beautify accept bet screen

* update question button

* Add separate challenge template

* Accepted challenge sharing card, fix accept bet call

* accept challenge button

* challenge winner page

* create challenge screen

* Your outcome/cost=> acceptorOutcome/cost

* New create challenge panel

* Fix main merge

* Add challenge slug to bet and filter by it

* Center title

* Add helper text

* Add FAQ section

* Lint

* Columnize the user areas in preview link too

* Absolutely position

* Spacing

* Orientation

* Restyle challenges list, cache contract name

* Make copying easy on mobile

* Link spacing

* Fix spacing

* qr codes!

* put your challenges first

* eslint

* Changes to contract buttons and create challenge modal

* Change titles around for current bet

* Add back in contract title after winning

* Cleanup

* Add challenge enabled flag

* Spacing of switch button

* market share row

* Add lite market endpoint

* 500 mana email (#687)

* Create 500-mana.html

* Update 500-mana.html

Fixed typos and links not working

* Added "create a good market" guide

added page creating-market.html
For Stephen to set up condition (email 3 days after signing up)

* Update 500-mana.html

updated 500 Mana email (still need to make changes to create market guide)

* email changes

* sendOneWeekBonusEmail logic

* add dayjs as dependency

* don't use mailgun scheduling

Co-authored-by: mantikoros <sgrugett@gmail.com>

* Challenge Bets (#679)

* Challenge bets

* Store avatar url

* Fix before and after probs

* Check balance before creation

* Calculate winning shares

* pretty

* Change winning value

* Set shares to equal each other

* Fix share challenge link

* pretty

* remove lib refs

* Probability of bet is set to market

* Remove peer pill

* Cleanup

* Button on contract page

* don't show challenge if not binary or if resolved

* challenge button (WIP)

* fix accept challenge: don't change pool/probability

* Opengraph preview [WIP]

* elim lib

* Edit og card props

* Change challenge text

* New card gen attempt

* Get challenge on server

* challenge button styling

* Use env domain

* Remove other window ref

* Use challenge creator as avatar

* Remove user name

* Remove s from property, replace prob with outcome

* challenge form

* share text

* Add in challenge parts to template and url

* Challenge url params optional

* Add challenge params to parse request

* Parse please

* Don't remove prob

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Add to readme about how to dev og-image

* Add emojis

* button: gradient background, 2xl size

* beautify accept bet screen

* update question button

* Add separate challenge template

* Accepted challenge sharing card, fix accept bet call

* accept challenge button

* challenge winner page

* create challenge screen

* Your outcome/cost=> acceptorOutcome/cost

* New create challenge panel

* Fix main merge

* Add challenge slug to bet and filter by it

* Center title

* Add helper text

* Add FAQ section

* Lint

* Columnize the user areas in preview link too

* Absolutely position

* Spacing

* Orientation

* Restyle challenges list, cache contract name

* Make copying easy on mobile

* Link spacing

* Fix spacing

* qr codes!

* put your challenges first

* eslint

* Changes to contract buttons and create challenge modal

* Change titles around for current bet

* Add back in contract title after winning

* Cleanup

* Add challenge enabled flag

* Spacing of switch button

* Put sharing qr code  in modal

Co-authored-by: mantikoros <sgrugett@gmail.com>

* See challenges you've accepted too

* Remove max height

* Notify mentioned users on market publish (#683)

* Add function to parse at mentions

* Notify mentioned users on market create

- refactor createNotification to accept list of recipients' ids

* Switch comments/chat to rich text editor (#703)

* Switch comments/chat to rich text editor

* Remove TruncatedComment

* Re-add submit on enter

* Insert at mention on reply

* Update editor style for send button

* only submit on enter in chat

* code review: refactor

* use more specific type for upload

* fix ESlint and errors from merge

* fix trigger on every render eslint warning

* Notify people mentioned in comment

* fix type errors

* Revert "Switch comments/chat to rich text editor (#703)"

This reverts commit f52da72115.

* merge conflict

* share modal

* merge issue

* eslint

* bigger link icion

Co-authored-by: Ian Philips <iansphilips@gmail.com>
Co-authored-by: James Grugett <jahooma@gmail.com>
Co-authored-by: SirSaltyy <104849031+SirSaltyy@users.noreply.github.com>
Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>
2022-08-05 00:22:45 -05:00
James Grugett
33906adfe4 Revert "Switch comments/chat to rich text editor (#703)"
This reverts commit f52da72115.
2022-08-04 16:49:59 -07:00
Sinclair Chen
f52da72115
Switch comments/chat to rich text editor (#703)
* Switch comments/chat to rich text editor

* Remove TruncatedComment

* Re-add submit on enter

* Insert at mention on reply

* Update editor style for send button

* only submit on enter in chat

* code review: refactor

* use more specific type for upload

* fix ESlint and errors from merge

* fix trigger on every render eslint warning

* Notify people mentioned in comment

* fix type errors
2022-08-04 16:34:04 -07:00
Sinclair Chen
edae709f5f
Notify mentioned users on market publish (#683)
* Add function to parse at mentions

* Notify mentioned users on market create

- refactor createNotification to accept list of recipients' ids
2022-08-04 15:35:55 -07:00
Ian Philips
912ccad530 Remove max height 2022-08-04 16:09:33 -06:00
Ian Philips
c93f9c5483 See challenges you've accepted too 2022-08-04 15:58:48 -06:00
Ian Philips
798253f887
Challenge Bets (#679)
* Challenge bets

* Store avatar url

* Fix before and after probs

* Check balance before creation

* Calculate winning shares

* pretty

* Change winning value

* Set shares to equal each other

* Fix share challenge link

* pretty

* remove lib refs

* Probability of bet is set to market

* Remove peer pill

* Cleanup

* Button on contract page

* don't show challenge if not binary or if resolved

* challenge button (WIP)

* fix accept challenge: don't change pool/probability

* Opengraph preview [WIP]

* elim lib

* Edit og card props

* Change challenge text

* New card gen attempt

* Get challenge on server

* challenge button styling

* Use env domain

* Remove other window ref

* Use challenge creator as avatar

* Remove user name

* Remove s from property, replace prob with outcome

* challenge form

* share text

* Add in challenge parts to template and url

* Challenge url params optional

* Add challenge params to parse request

* Parse please

* Don't remove prob

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Challenge card styling

* Add to readme about how to dev og-image

* Add emojis

* button: gradient background, 2xl size

* beautify accept bet screen

* update question button

* Add separate challenge template

* Accepted challenge sharing card, fix accept bet call

* accept challenge button

* challenge winner page

* create challenge screen

* Your outcome/cost=> acceptorOutcome/cost

* New create challenge panel

* Fix main merge

* Add challenge slug to bet and filter by it

* Center title

* Add helper text

* Add FAQ section

* Lint

* Columnize the user areas in preview link too

* Absolutely position

* Spacing

* Orientation

* Restyle challenges list, cache contract name

* Make copying easy on mobile

* Link spacing

* Fix spacing

* qr codes!

* put your challenges first

* eslint

* Changes to contract buttons and create challenge modal

* Change titles around for current bet

* Add back in contract title after winning

* Cleanup

* Add challenge enabled flag

* Spacing of switch button

* Put sharing qr code  in modal

Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-08-04 15:27:02 -06:00
SirSaltyy
2d3ca47b52
500 mana email (#687)
* Create 500-mana.html

* Update 500-mana.html

Fixed typos and links not working

* Added "create a good market" guide

added page creating-market.html
For Stephen to set up condition (email 3 days after signing up)

* Update 500-mana.html

updated 500 Mana email (still need to make changes to create market guide)

* email changes

* sendOneWeekBonusEmail logic

* add dayjs as dependency

* don't use mailgun scheduling

Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-08-04 13:03:02 -05:00
James Grugett
7e46188107 Add lite market endpoint 2022-08-03 22:21:22 -07:00
Ian Philips
d83e103fab Ignore clicks when hidden 2022-08-03 18:42:40 -06:00
Ian Philips
5bc905b358 Bottom padding works on mobile, broken on desktop :( 2022-08-03 16:42:51 -06:00
Ian Philips
b4c6b99e09 Remove bottom bar height correction 2022-08-03 16:38:00 -06:00
Ian Philips
756115ba54 Link tags aren't hiding sidebar on click 2022-08-03 16:30:05 -06:00
Ian Philips
fab83cfc33 Don't auotfocus on mobile 2022-08-03 16:16:46 -06:00
Ian Philips
aa3101baa9 Fix group chat padding 2022-08-03 16:10:02 -06:00
Ian Philips
82419d0b92
Groups chat ux (#713)
* Add in group chat bubble

* Show chat bubble on nav with unseen notifs

* Spacing

* More spacing

* Remove chat tab

* Show chat on help/welcome/updates/features groups

* Cleanup

* Scroll with updated height
2022-08-03 15:38:35 -06:00
James Grugett
a7d80d62cb Don't show cancel button for other people's limit orders 2022-08-03 14:30:59 -07:00
James Grugett
a761f8c65e Hide pills while searching 2022-08-03 14:17:45 -07:00
James Grugett
280308b625 Show # of bets equal to visible bets 2022-08-02 17:40:34 -07:00
James Grugett
b5d8acfef3 Switch profit in bets tab to match user page 2022-08-02 17:31:49 -07:00
James Grugett
3c9108de0d Document creating a limit order in API 2022-08-02 17:01:31 -07:00
Ian Philips
c24b4e77a8 Lint 2022-08-02 17:24:59 -06:00
James Grugett
d45edb7887 Add WagerWith.me and James' Bot to Awesome Manifold 2022-08-02 16:21:07 -07:00
Ian Philips
e700697423 Fix group referrals not working 2022-08-02 17:18:08 -06:00
Marshall Polaris
f8a74aa438
Allow admins to resolve any market (#711) 2022-08-02 15:34:20 -07:00
mantikoros
6563082746 move claim button 2022-08-02 15:22:53 -07:00
mantikoros
5e8b9711dc hide pagination if only one page 2022-08-02 15:22:53 -07:00
mantikoros
96c0876053 manalinks: fix focus 2022-08-02 15:22:53 -07:00
mantikoros
164d9ef079 manalinks: mention referral bonus 2022-08-02 15:22:53 -07:00
Austin Chen
53d89fa4ac Show the value to 2 decimal places on hover 2022-08-02 14:59:47 -07:00
Marshall Polaris
b83caf4dd9 Just make me endpoint forward the backend response 2022-08-02 00:21:51 -07:00
Keri Warr
cfeb50826c
Add endpoint for reading currently authenticated user (#710) 2022-08-02 00:06:23 -07:00
Marshall Polaris
6901507461
Allow unspecfied outcome as input to sellshares (#706)
* Allow unspecfied outcome as input to `sellshares`

* Fix small details
2022-08-01 23:53:12 -07:00
Ian Philips
0b06ded5e5
Groups contracts (#709)
* Update group links in trigger and api

* Remove extra call during creation

* Remove grouplinks on frontend

* Deserialize

* Consolidate logic

* Move function locally
2022-08-01 21:15:09 -06:00
Marshall Polaris
b4e8c5d602
Backfill missing group IDs (#708) 2022-08-01 16:40:04 -06:00
Marshall Polaris
ec84245dd4
Add some API endpoints for reading group info (#707) 2022-08-01 14:59:45 -07:00
Ian Philips
0819c3918f Most popular=> Trending 2022-08-01 09:03:46 -06:00
ingawei
ae2e7dfe30
noobify welcome demo (#699)
* noobify welcome
* also beginning to add greyscale color scheme
2022-07-30 02:50:03 -05:00
James Grugett
87f6949d80 Use custom search index for search results. Hide sort options while there's a query. 2022-07-29 18:13:53 -07:00
James Grugett
003301762c Ignore filter on contract status when searching 2022-07-29 17:37:53 -07:00
James Grugett
d6cf4332da Delete query param when empty 2022-07-29 17:37:34 -07:00
James Grugett
be01a15230
Refactor search to not use Algolia components (#705)
* In progress refactor to not use Algolia components

* Cleanup

* Only query when necessary

* Read and update url params for query and sort

* Don't push router

* Don't update url if using default sort

* Add popstate listener to improve home navigation

* Remove console.logs
2022-07-29 19:08:51 -05:00
mantikoros
079a2a3936 eslint 2022-07-29 16:06:22 -07:00
mantikoros
5812d8ed2e manalink qr code 2022-07-29 16:02:18 -07:00
mantikoros
779b6dfc0c manalink referrals 2022-07-29 15:09:48 -07:00
mantikoros
bdea739c55 multiple choice contract card 2022-07-29 09:20:49 -07:00
Austin Chen
693eb96d23 Include groupId when duplicating markets 2022-07-29 09:20:01 -07:00
mantikoros
ada3f0787c create: add bottom margin 2022-07-28 11:44:07 -07:00
Ian Philips
d3da6de5dd Groups default open 2022-07-28 11:37:26 -07:00
marsteralex
aa6d0d1750
add beasts (#700)
* fix https

* add beasts

* Remove extra file

* Prettier-ify code

* Prettier-ify

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-07-28 11:31:58 -07:00
Ian Philips
05b0ca5cdb I want to see others' referrals 2022-07-28 11:16:48 -07:00
mantikoros
b6a70641a0 fix modal 2022-07-27 19:51:34 -07:00
mantikoros
1aaae93113
Multiple choice markets (#698)
* multipe choice answers

* create multiple choice cloud function

* multi choice market page

* show outcome '0'

* stats: multi choice type

* update place bet

* answer doc id = outcome

* update resolve market

* prettier

* fix

* fix resolution
2022-07-27 21:40:33 -05:00
James Grugett
b1c4f018f9 Expose cancel bet api 2022-07-27 17:38:25 -07:00
Ian Philips
013ff1d941 Show api error on create contract 2022-07-26 16:44:51 -07:00
Ian Philips
f32e995baa Show referrals banner on user-page 2022-07-26 15:24:16 -07:00
Marshall Polaris
b506e96548
Implement "sell all shares" functionality in sellshares and expose API (#696)
* Change `sellshares` to be able to sell all shares

* Sell all shares properly on bet panel UI

* Add API route for selling shares, document
2022-07-26 12:47:19 -07:00
Marshall Polaris
ad46a60c4f
Clean up rendering of user bets list (#694)
* Clean up crufty markup in bets list

* Don't render bet tables in bets list until expanded

* Don't look up unfilled bets for every sell button
2022-07-26 00:10:22 -07:00
Marshall Polaris
7e4f4b9a87
Clean up a bunch of crufty stuff on user comments list (#693) 2022-07-26 00:10:11 -07:00
SirSaltyy
0c2bcceae2
Update charity.ts (#695)
Added Founder's Pledge Global Health and Development Fund.

Made new logos for all the Founder's Pledge charities to help distinguish between them.
2022-07-26 11:10:22 +09:00
Ian Philips
af25a6c795 Allow adding multiple contracts to group in modal 2022-07-25 18:27:43 -07:00
mantikoros
ec0e25e5ed create user: remove ip check 2022-07-25 18:25:23 -07:00
mantikoros
3107c8fe30 large bet warning 2022-07-25 18:11:29 -07:00
mantikoros
24124ac86a show sign up button on mobile on market page 2022-07-25 17:45:42 -07:00
Marshall Polaris
06948bb98b
Make setNotificationsAsSeen return a promise (#692) 2022-07-25 16:37:23 -07:00
Marshall Polaris
64462d6ab4
Make tabs components better (#691)
* Make better tabs components, apply to user page

* Remove fishy unused href property from tabs

* Remove tab ID property

* Clean up crufty markup in tabs component

* Fix naming to be right (thanks James!)
2022-07-25 13:27:09 -07:00
TrueMilli
e4f8c14fab
Image compression (#689)
* added image compression

* removed TODO
2022-07-25 12:51:51 -07:00
mantikoros
d8f96876a0 PlayMoneyDisclaimer copy; hide order book for signed out users 2022-07-25 12:29:29 -07:00
mantikoros
d82c7d7f3e “added liquidity” ⇒ “added a subsidy” 2022-07-25 12:22:38 -07:00
mantikoros
d982d0332c play money wording 2022-07-24 23:38:57 -07:00
mantikoros
df91310d0f PlayMoneyDisclaimer; hide limit orders for signed out users; infobox styling 2022-07-24 23:28:05 -07:00
mantikoros
e389f4cc3b referrals text 2022-07-24 22:50:33 -07:00
Marshall Polaris
9840742927 Fix overaggressive emulator running in dev.sh 2022-07-24 02:30:28 -07:00
Marshall Polaris
312b244e2a
Small backend cleanups (#643)
* Reuse DAY_MS in update-metrics job

* More concise transaction in cancelbet

* Remove some meaningless awaits

* Do less work in onCreateLiquidityProvision

* Do less work in onCreateAnswer
2022-07-24 00:45:45 -07:00
Marshall Polaris
a1d5d161dd
Revamp backend code to support good local function development (#657)
* Move concurrently dep upwards

* Add express as explicit dependency

* Accept just one HTTP method per endpoint

* Fix endpoint option coalescing

* Expressification of cloud functions

* Nicer logging of API requests

* Refactor web package.json

* Add ts-node and nodemon to dev dependencies, bring back cors

* Add scaffolding to point dev server at local functions

* Enable emulator in dev server scaffolding

* Fix up a little stuff I broke
2022-07-24 00:26:38 -07:00
Austin Chen
6ad43b02c7 Show the number of comments and bets 2022-07-24 00:11:35 -07:00
Olivia Appleton
1f655acddb
Add my market manager tool (#690) 2022-07-23 23:33:19 -07:00
Sinclair Chen
6c89e5f18f
Add @ mentions to editor (#670)
* Add @ mentions to editor

* Fix mention list not loading

* Sort mention list by prefix, follow count

* Render at mention with Linkify component

- mentions are now Next <Link> rather than <a>
- fix bug where editor.getText() returns [object Object] for mentions
- fix mention rendering for posted markets
2022-07-23 20:37:34 -07:00
Ian Philips
f4e4582913 Add group slug during create 2022-07-23 15:04:11 -06:00
Marshall Polaris
6c8c068327
Write script to fix old comments without IDs and user IDs (#680) 2022-07-23 13:48:28 -07:00
James Grugett
64f2dbbe71 Fix unused var 2022-07-23 15:26:08 -05:00
James Grugett
f43df42449 Change card to show volume instead of pool 2022-07-23 15:23:47 -05:00
James Grugett
71b20eb61a Tweak visually hidden style 2022-07-23 15:10:54 -05:00
James Grugett
7f42796724 Update algolia filters to use groupLinks.slug isntead of deprecated groupSlugs field. 2022-07-23 15:02:08 -05:00
Austin Chen
71880dfc98
Add a toolbar for images and iframes (#688)
* Add a toolbar for images and iframes

* Insert embed code via modal
2022-07-23 09:19:49 -07:00
James Grugett
408027dd6a Fix bug 2022-07-22 22:44:21 -05:00
James Grugett
2116b86aec Fix infinite loop in numeric limit bet 2022-07-22 21:03:08 -05:00
Ian Philips
56a579ff91 Don't filter for group contract ids 2022-07-22 16:44:03 -06:00
Ian Philips
abde013ab6 Re-get contracts to get updated links 2022-07-22 16:40:37 -06:00
Ian Philips
5f074206de
Backfill and forward fill contracts with group info (#686)
* Backfill and forward fill contracts with group info

* No nested queries :(

* Fix filter

* Pass empty arrays instead of undefined
2022-07-22 16:28:53 -06:00
James Grugett
5899c1f3c0 Fix lints 2022-07-22 16:30:07 -05:00
James Grugett
135160dd92 Remove custom placeholders. Just show '0' for limit inputs 2022-07-22 16:18:36 -05:00
James Grugett
a1d51e3778 Update labels for numeric market outcomes 2022-07-22 16:07:59 -05:00
James Grugett
f800570845 Improve range limit order UI 2022-07-22 16:03:55 -05:00
Ian Philips
d319b654ce Add creator id to unique bettor ids 2022-07-22 14:15:42 -06:00
Ian Philips
63d8e6739b Add title, mobile flex 2022-07-22 13:53:19 -06:00
James Grugett
d3d472f5d2 Hide "Your bets" when signed out. "For you" becomes "Featured" when signed out. 2022-07-22 14:50:29 -05:00
Ian Philips
6fb9849007
Allow to add/remove from groups on market page (#685)
* Allow to add/remove from groups on market page

* remove lib

* Fix Sinclair's relative import from May

* Clean
2022-07-22 11:34:10 -06:00
mantikoros
163c990e9d "bettors" => "traders" 2022-07-22 12:03:33 -05:00
mantikoros
c3a0326b1e homepage seo 2022-07-22 12:01:52 -05:00
mantikoros
e13f4d3d4d charity description 2022-07-22 11:59:25 -05:00
mantikoros
2c80133856 add SEO tags to everything 2022-07-22 11:56:03 -05:00
mantikoros
de53a13c84 fix referrals seo 2022-07-22 11:25:48 -05:00
mantikoros
624df76393 search: sort by liquidity; remove oldest 2022-07-22 11:24:25 -05:00
Austin Chen
7cace82b83
Render iframes inside the rich text editor (#682)
* Try embedding iframes in tiptap

* When iframe code is pasted, inject it into the editor

* Code cleanups and comments

* Remove clsx dependency

Cuz it doesn't exist in `common` anyways

* Rename to tiptap-iframe
2022-07-22 09:12:23 -07:00
Austin Chen
87170894e2 Suppress eslint warning for script 2022-07-22 09:12:01 -07:00
Ian Philips
83cb0a6130 Allow clickable username in welcome message 2022-07-22 08:19:06 -06:00
Ian Philips
bfb11339ca Convert world and culture categories 2022-07-22 08:12:40 -06:00
Marshall Polaris
08fd27cb26
Make main login/logout buttons reload server side props (#677)
* Set cookies in auth handler before looking up user

* Make sidebar logout button trigger SSR reload

* Make sidebar login button trigger SSR reload
2022-07-22 00:03:16 -07:00
James Grugett
3b953a7c21
Range limit orders (#655)
* Prototype range limit order UI

* Conditionally show YES or NO max payout

* Range bet executes both bets immediately.

* Validate lowLimitProb < highLimitProb

* Show error if low limit is higher than high limit

* Update range order UI

* Revert "Validate lowLimitProb < highLimitProb"

This reverts commit c261fc2743.

* Revert "Range bet executes both bets immediately."

This reverts commit 30b95d75d9.

* Buy panel only non-limit orders

* Bet choice => outcome

* More iterating on range UI

* betChoice => outcome

* Lighten placeholder text
2022-07-22 00:57:56 -05:00
James Grugett
23b704ffe0 Fix excessive bottom margin on chart 2022-07-21 21:51:20 -05:00
James Grugett
ca5ca9b2b8 Refactor: Move ContractLeaderboard to its own file 2022-07-21 21:39:06 -05:00
ingawei
7474c0a0fd
Inga/manalinks pagination bug (#678)
* manalink pagination fix
* also fixed new manalink timing out bug
2022-07-21 20:22:17 -05:00
Sinclair Chen
4b4734531f
refactor createNotif - put ?: args in object (#681) 2022-07-21 17:08:09 -07:00
mantikoros
cded3f50ff "question" => "market" (controversial!) 2022-07-21 18:17:02 -05:00
mantikoros
80b27fdf6e Put leadeboards back in sidebar 2022-07-21 17:23:55 -05:00
Ian Philips
daca6ef482 Bonus=>10 2022-07-21 15:33:21 -06:00
mantikoros
91bec9c996
Referrals page (#676)
* Referrals page added to sidebar; useSaveReferral; InfoBox

* text color

* eslint

* migrate useUsers hook to react-query (#674)

* Remove bet button from free response comments

* Make answer replies more closely spaced together

* Host Ida and Alex's MTG Guesser game (#656)

* Copy over code from Mtg Guesser

* Run Prettier

* CSS Tweaks: Hover feedback, button positioning

* Hide all but counterspell & burn, for now

* Move to /mtg directory

* Fix prettierignore

* smaller jsons (#673)

limited burn to only red cards and also added limited json files to only have fields needed to play

* Add Ida's tweak to card position

Co-authored-by: marsteralex <bob.masteralex@gmail.com>

* Adjust card positioning

* Make the select screen index.html

* Remove other guessing games

* Remove alternate versions; add Alex's email

* Remove unused jsons

* Small FR comments tweaks

* Spacing tweak

* Changing manalinks table UI (#665)

From table to card view

* Fix comment spacing on non-FR

* Move "Send M$" lower in sidebar More list.

* Move leaderboards up in mobile nav

* eslint

* prettier

Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>
Co-authored-by: James Grugett <jahooma@gmail.com>
Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: marsteralex <bob.masteralex@gmail.com>
Co-authored-by: ingawei <46611122+ingawei@users.noreply.github.com>
2022-07-21 14:43:10 -05:00
mantikoros
96e9f749d2 track search categories 2022-07-21 12:45:47 -05:00
Austin Chen
6603effd1b
Use https for hotlinked image
Editing main from my phone, fingers crossed
2022-07-21 01:16:21 -07:00
Marshall Polaris
03858e4a8c
Make a React context to be the source of truth for authenticated user (#675)
* Make a React context to manage the logged in user events

* Remove unnecessary new user creation promise machinery

* Slight refactoring to auth context code

* Improvements in response to James feedback
2022-07-21 00:38:26 -07:00
James Grugett
8aa360c853 Move leaderboards up in mobile nav 2022-07-21 00:52:11 -05:00
James Grugett
21c08aed30 Move "Send M$" lower in sidebar More list. 2022-07-21 00:50:28 -05:00
James Grugett
2ad7266283 Fix comment spacing on non-FR 2022-07-21 00:46:57 -05:00
ingawei
7a041fd753
Changing manalinks table UI (#665)
From table to card view
2022-07-21 00:45:53 -05:00
James Grugett
f7151f131d Spacing tweak 2022-07-20 22:37:43 -05:00
James Grugett
8f5e51a304 Small FR comments tweaks 2022-07-20 22:13:37 -05:00
Austin Chen
aba818a9de Remove unused jsons 2022-07-20 18:05:41 -07:00
Austin Chen
260f4641dd Remove alternate versions; add Alex's email 2022-07-20 18:04:54 -07:00
Austin Chen
edee910e2d Remove other guessing games 2022-07-20 18:00:18 -07:00
Austin Chen
6b5b9b42f5 Make the select screen index.html 2022-07-20 17:14:49 -07:00
Austin Chen
c3b825cc44 Adjust card positioning 2022-07-20 16:59:40 -07:00
Austin Chen
a3f150b1d9
Host Ida and Alex's MTG Guesser game (#656)
* Copy over code from Mtg Guesser

* Run Prettier

* CSS Tweaks: Hover feedback, button positioning

* Hide all but counterspell & burn, for now

* Move to /mtg directory

* Fix prettierignore

* smaller jsons (#673)

limited burn to only red cards and also added limited json files to only have fields needed to play

* Add Ida's tweak to card position

Co-authored-by: marsteralex <bob.masteralex@gmail.com>
2022-07-20 16:57:51 -07:00
James Grugett
528dd2b28a Make answer replies more closely spaced together 2022-07-20 18:35:09 -05:00
James Grugett
5ddf496dae Remove bet button from free response comments 2022-07-20 18:35:09 -05:00
Sinclair Chen
aa554ca9f6
migrate useUsers hook to react-query (#674) 2022-07-20 16:31:18 -07:00
Marshall Polaris
ace39ef73d
Update Next.js 12.1.2 -> 12.2.0 (#669)
* Update Next.js 12.1.2 -> 12.2.0

* Further bump Next to 12.2.2
2022-07-20 15:42:31 -07:00
mantikoros
49dcd97d70 feed bets: better prob display 2022-07-20 17:04:11 -05:00
mantikoros
75a1d606cb feed bets: show change in prob 2022-07-20 16:28:25 -05:00
mantikoros
302a635542 group page max width 2022-07-20 16:06:31 -05:00
James Grugett
c35d0a8bc6 Split out "Your bets" from "For you" 2022-07-20 15:30:07 -05:00
James Grugett
44afa92b58 Turn off for you by default 2022-07-20 15:05:48 -05:00
James Grugett
e45d81513c Don't filter by personal unless pills enabled 2022-07-20 14:49:16 -05:00
Sinclair Chen
0870397fea Show line in menu on mobile 2022-07-20 12:36:23 -07:00
Sinclair Chen
202132868f lint and prettier 2022-07-20 12:35:04 -07:00
mantikoros
d65a60984d make group invite link more prominent 2022-07-20 11:45:53 -05:00
mantikoros
45b883477d generic copy link button 2022-07-20 11:42:49 -05:00
mantikoros
b60892fada group 'rankings' => 'leaderboards' (friendlier, more consistent terminology) 2022-07-20 11:15:55 -05:00
Marshall Polaris
c8361f1748
Make it so that if you sign in on / you get redirected to /home (#672) 2022-07-20 01:59:14 -07:00
mantikoros
b517f7cfa7 eslint; remove unused tags import 2022-07-20 00:35:27 -05:00
mantikoros
2b13085dff search: use default categories for non-authed users 2022-07-20 00:23:00 -05:00
mantikoros
0013f76873 search defaults to 'for you'; hide pills for additional filters 2022-07-20 00:03:03 -05:00
mantikoros
83e9408d69 remove tags from info panel 2022-07-19 23:48:09 -05:00
Marshall Polaris
bacd546e5d Fix unused import from Ian's code 2022-07-19 20:10:54 -07:00
Marshall Polaris
61094ea17d
Properly handle expired ID token cookie, be robust to errors (#671) 2022-07-19 20:08:33 -07:00
James Grugett
b2c89d36cf Home: Show pills that are groups (in addition to All, For you) 2022-07-19 18:23:52 -05:00
Ian Philips
921ac4b2a9 Fix last 3 days number 2022-07-19 17:22:23 -06:00
mantikoros
b48e910f70 fix areaBaselineValue 2022-07-19 18:20:03 -05:00
mantikoros
bab828412b group: add question button 2022-07-19 18:16:03 -05:00
Ian Philips
1f0983a145 Find old contracts to decrement score on 2022-07-19 17:08:51 -06:00
Ian Philips
4aface583d Remove pesky loading spinner 2022-07-19 16:41:11 -06:00
Ian Philips
2152e5286a Score & sort by unique bettors in last 3 days 2022-07-19 16:29:41 -06:00
James Grugett
58d6286361 Fix chart area extending into labels below 2022-07-19 17:22:58 -05:00
James Grugett
6124ea01f6 Fix a DOM error in console 2022-07-19 16:57:32 -05:00
Marshall Polaris
6d3490cd68
Turn off Next.js font inlining (#668) 2022-07-19 14:20:23 -07:00
James Grugett
af6552958f Show all-time profit on UserPage 2022-07-19 16:05:50 -05:00
Marshall Polaris
f9aab39039
Clean up font loading and see if it fixes our problem (#667) 2022-07-19 13:57:32 -07:00
James Grugett
fc9e261601 Fix build 2022-07-19 15:45:47 -05:00
James Grugett
e9ad30cc74 Increase number of contracts shown in bets list 2022-07-19 15:43:37 -05:00
James Grugett
2684c8bcca Default to weekly leaderboard 2022-07-19 15:39:40 -05:00
James Grugett
6c070464dd Use static props to load leaderboard fast 2022-07-19 15:39:17 -05:00
Ian Philips
b5ef7490c3 NotificationSettings to its own file 2022-07-19 14:24:36 -06:00
Ian Philips
6dcad6225b Next/Previous => Older/Newer 2022-07-19 14:16:20 -06:00
James Grugett
b6c8390a46 Show order book button even on Quick bet 2022-07-19 15:01:15 -05:00
Ian Philips
a1e03c3a25 Allow opening notifs in new tabs, return newest notifs 2022-07-19 13:58:51 -06:00
James Grugett
93b9ace477 Comment email: Subject no longer varies between questions so emails are threaded in gmail 2022-07-19 14:54:42 -05:00
James Grugett
74760b1062 Reorder orderbook columns 2022-07-19 14:53:33 -05:00
Marshall Polaris
61cbb07bd5
Fix some broken stuff on the homepage contract search routing (#664)
* Use Next router more appropriately

* Replace instead of push when modifying search query params
2022-07-19 12:33:53 -07:00
mantikoros
12567074cc fix log scale graph 2022-07-19 12:31:26 -05:00
mantikoros
4b3370e374 fix formatting 2022-07-19 12:31:26 -05:00
Marshall Polaris
0d282a962c
Don't setQuery on group selector component during initial render (#660) 2022-07-19 09:35:43 -06:00
Ian Philips
a203f43142 Cache all notifs 2022-07-19 09:29:12 -06:00
Ian Philips
c236eb15b1
Cache notifs in local, gives instant load of old notifs (#662)
* Cache notifs in local, gives instant load of old notifs

* Small refactor, add ss auth

* unused vars

* Add back in replaceAll

* Save all notifs

* Memoize paginated notifs

* Replace all => replace with regexp
2022-07-19 09:04:47 -06:00
Austin Chen
2bae7dc200 Fix error on no portfolio history 2022-07-19 02:54:05 -07:00
Marshall Polaris
55775d9d37 Also handle case where there are no cookies yet 2022-07-19 01:35:34 -07:00
Marshall Polaris
c256e9c0cc Attempt to fix up overly sensitive cookie parsing 2022-07-19 01:33:00 -07:00
Austin Chen
f6d2c56e43 Fix /create 2022-07-19 01:23:36 -07:00
Marshall Polaris
a103a2ee2c
Initial draft of Vercel Firebase auth (#593)
* Set a cookie with an up-to-date Firebase ID token

* Implement server-side authentication cookie reading logic

* Change index page to redirect for authed users

* No branch necessary for logged in users on index page

* Add helpers for creating server-side redirects

* Add some common sense redirects
2022-07-19 00:50:11 -07:00
Austin Chen
d1ad0716c8 Fix import 2022-07-19 00:34:53 -07:00
Austin Chen
b501776e33 Remove quadratic matching from /charity 2022-07-19 00:20:18 -07:00
Austin Chen
dcd2ccae1b Allow environments to override the referral bonus 2022-07-18 23:29:32 -07:00
mantikoros
8793288dc8 contract description: less prominent edit buttons 2022-07-18 19:17:45 -05:00
Marshall Polaris
f2a7a145e4
Add React key prop to homepage filter widget (#661) 2022-07-18 18:37:46 -05:00
James Grugett
61a21d34b2 Order limit bets in sorted order on mobile 2022-07-18 18:19:30 -05:00
Austin Chen
47a27bf3fe
Label "Since June" for users who had an account prior to 2022-06-20 (#659) 2022-07-18 15:55:17 -07:00
Sinclair Chen
781de79b97 Make description text style more consistent
- links and blockquotes have light font weight, like other text
- font size in editor matches font size in description
- old descriptions have same style as new
- placeholder text matches editor style
- decrease line-height a bit
2022-07-18 14:03:05 -07:00
Ian Philips
e2a72dd0a2 Fix /create date input 2022-07-18 15:01:50 -06:00
Ian Philips
f2a16afc90 Update firestore rules for question editing 2022-07-18 14:52:28 -06:00
mantikoros
65e4f24531 groups: only change layout if sidebar chat, smaller leave button 2022-07-18 12:55:49 -05:00
Ian Philips
39c38a669e Referrals bug fix and attribute group 2022-07-18 10:40:44 -06:00
Ian Philips
db537a97ba Allow click on group card avatar 2022-07-18 08:35:59 -06:00
Ian Philips
229d270d25 Set max width on avatars 2022-07-18 08:34:20 -06:00
Ian Philips
eb4906cb97 Remove query from notif isSeen logic 2022-07-18 08:18:40 -06:00
SirSaltyy
d012561c50
Create 500-mana.html (#658) 2022-07-18 15:13:16 +01:00
Ian Philips
906cfc29c8 Endswith=>includes to handle sort query in group chat 2022-07-18 07:59:21 -06:00
Ian Philips
a247e6d0de Default to created time if no chat activity 2022-07-18 07:38:02 -06:00
Austin Chen
f393246e4f
Let users edit descriptions and questions (#654)
* Use rich text editor on the description

* Write a new line to description when the question is changed

* Stop showing categories

* Allow anyone to edit their own question
2022-07-17 22:22:44 -07:00
mantikoros
281b712258 move group chat to sidebar on desktop 2022-07-17 15:56:39 -05:00
mantikoros
b5f0b58898 usePing 2022-07-17 15:17:31 -05:00
mantikoros
07bfdadd25 remove OnlineUserList b/c of responsiveness issues 2022-07-17 14:40:21 -05:00
James Grugett
c1d77f48e3 Fix tag filter 2022-07-16 18:56:21 -05:00
James Grugett
1edc1993e1 Cache follows in localstorage 2022-07-16 14:58:25 -05:00
Austin Chen
bae55828a1 Simplify Firestore isAdmin rule 2022-07-16 12:52:59 -07:00
Austin Chen
60f4e43cf3 Prettier fix 2022-07-16 12:51:22 -07:00
mantikoros
a3975080a1 adjust sig figs 2022-07-16 14:50:16 -05:00
James Grugett
7feacbd961 Tweak wording 2022-07-16 14:37:03 -05:00
James Grugett
7b6344d976 Order book button opens full table of limit orders in dialog 2022-07-16 14:21:25 -05:00
Austin Chen
32cb19d01f Randomize image upload path to avoid collisions 2022-07-16 11:39:58 -07:00
Austin Chen
1bc49dc0a2 Tweak placeholder copy 2022-07-16 11:39:58 -07:00
Austin Chen
349772a2f9 Description typography: font-light, text-base 2022-07-16 11:39:58 -07:00
Austin Chen
916618be31 Disable quotation marks in quotes 2022-07-16 11:39:58 -07:00
James Grugett
6d8ad74b4d Redeem shares of makers after sellshares 2022-07-16 13:11:13 -05:00
ingawei
7d24a3e4a2
Inga/manalink bug fixes (#653)
* fixed manalinks bug of claiming own manalink, and also rerouting to home upon claiming if not logged in
* no more multiple hardcoded manalink messages
2022-07-15 20:42:37 -05:00
Ian Philips
eed7990c3c Lighten unseen notifs 2022-07-15 16:57:58 -06:00
Sinclair Chen
2543bdcdfc
refactor string matching (#649) 2022-07-15 14:16:00 -07:00
Marshall Polaris
38c26f8b5c
Add API endpoints for fetching user info by username and ID (#652)
* Add an API endpoint for fetching user info by username

* Add endpoint for querying users by ID, too

* Add very simple docs about user APIs
2022-07-15 14:03:34 -07:00
James Grugett
feba0b58ee Turn search filters into pills 2022-07-15 15:06:33 -05:00
Ian Philips
a6cbb6b759 Small notifications ux improvements 2022-07-15 11:53:30 -06:00
James Grugett
1ca73ecd4d Add size prop to button 2022-07-15 12:24:07 -05:00
James Grugett
ec682788e0 Put back old Yes/No bet buttons 2022-07-15 11:03:42 -05:00
Ian Philips
0be38c4e09 Online users list ui, remove from followers list 2022-07-15 09:32:03 -06:00
Ian Philips
50447cf8d3 Unused vars 2022-07-15 08:48:35 -06:00
Ian Philips
d54a72c431 Remove extra comment 2022-07-15 08:47:19 -06:00
Ian Philips
dd9d24e657 Show online users on desktop 2022-07-15 08:45:52 -06:00
Ian Philips
2610f32521 Correct my username 2022-07-15 07:35:17 -06:00
Ian Philips
47579e8509 Fix network spam with modified deps array 2022-07-15 07:28:04 -06:00
Ian Philips
9c49f2e2d7 Revert "Revert "Order groups by most recent chat activity (#650)""
This reverts commit 17c9beca28.
2022-07-15 06:52:08 -06:00
James Grugett
36851ae9f9 Exclude more mobile options from private instances 2022-07-15 00:45:50 -05:00
James Grugett
64c83c4ef0 Don't show portfolio no history message 2022-07-14 23:56:30 -05:00
James Grugett
590c63e911 Small fixes for limit order table 2022-07-14 21:27:00 -05:00
James Grugett
17c9beca28 Revert "Order groups by most recent chat activity (#650)"
This reverts commit 6e1aa4b0f4.
2022-07-14 20:51:38 -05:00
ingawei
2f02e4d3e0
minor tweaks of manalink form (#647)
* minor tweaks of manalink form, adding M$ in front of amount and changing expire time to dropdown instead of calendar selection
* made minimum for uses and amount 1, it seems otherwise it does not generate a link at all
2022-07-14 19:43:06 -05:00
Ian Philips
44d993a588 Bold group for old chat notif 2022-07-14 17:03:08 -06:00
James Grugett
a9018d77c7 If a limit bet doesn't match any orders, don't update the contract, don't redeem shares. Perf win! 2022-07-14 18:01:35 -05:00
Ian Philips
6e1aa4b0f4
Order groups by most recent chat activity (#650)
* Order groups by most recent chat activity

* Use group chat slug constant

* Match source slug and isSeenOnHref

* Listen for group member changes
2022-07-14 16:46:45 -06:00
James Grugett
be64bf71a7 Limit the amount of bets and comments sent to the client through getStaticProps 2022-07-14 14:57:17 -05:00
Ian Philips
d9279e42cc Don't collapse/expand notifs with ctrl/cmd click 2022-07-14 11:56:40 -06:00
Ian Philips
6a28643215 Notifications ux 2022-07-14 11:48:04 -06:00
Ian Philips
27a544205f Optimistically join groups 2022-07-14 11:09:28 -06:00
James Grugett
8daf1b2ba8 Return undefined instead of null for useUserById(undefined) 2022-07-14 12:03:29 -05:00
Sinclair Chen
a93e64c830
fix: let useUserById accept undefined userId (#648) 2022-07-14 12:02:46 -05:00
James Grugett
0c328bc398 Move getStorage() into init.ts after initializeApp() is called. 2022-07-14 11:44:52 -05:00
Ian Philips
deaa595f07 Exclude contract creator in both places 2022-07-14 09:32:50 -06:00
Ian Philips
4eba3c8124 Try new way of calculating rankings for large groups 2022-07-14 09:09:12 -06:00
Ian Philips
eb6b1b9f89 Rename on-delete-group 2022-07-14 08:02:54 -06:00
Ian Philips
709ce5377a Remove extra key assignment 2022-07-14 07:57:33 -06:00
Ian Philips
ee01328553 Remove group slugs from contracts on delete group 2022-07-14 07:53:41 -06:00
Sinclair Chen
5ebd4498a0
Remove deprecated useUserById implementation (#571)
* Remove duplicate useUserById implementation

* fix bug: firebase doesn't accept empty paths
2022-07-13 17:43:20 -07:00
Sinclair Chen
095af10d4f replace raw checkbox w/ Checkbox component
also run prettier
2022-07-13 16:50:08 -07:00
James Grugett
f4b7b9efd0 Only show probabilty update with arrow if probability changes 2022-07-13 18:39:32 -05:00
James Grugett
67b3450924 Use quick vs limit bet in mobile dialog 2022-07-13 18:28:33 -05:00
James Grugett
9240cd3d1c Bet panel: Quick vs Limit pill buttons. Also, pill buttons for Yes vs No. 2022-07-13 18:23:36 -05:00
Sinclair Chen
98192ee580 simplify rich text link styles 2022-07-13 16:14:44 -07:00
Sinclair Chen
664e55a40b
Add typing, pasting links (#646) 2022-07-13 15:56:15 -07:00
Ian Philips
45fb3803c1 Limit member search to 100 2022-07-13 16:24:35 -06:00
mantikoros
e1b6619e9c embeds: don't show bet button after resolution 2022-07-13 17:22:50 -05:00
Ian Philips
7a49549389 Ignore rankings/members for huge groups for now 2022-07-13 16:20:56 -06:00
Sinclair Chen
f08d6bda93
when adding package, don't put ^ before version (#645) 2022-07-13 15:14:06 -07:00
ingawei
a4e2cce4aa
initial commit for manalinks UI improvements (#642)
* manalinks UI improvements

* made manalink look more like card

* changed new link to pulsing indigo instead of green
2022-07-13 16:57:34 -05:00
Ian Philips
55c91dfcdd
Categories to groups (#641)
* start on script

* Revert "Remove category filters"

This reverts commit d6e808e1a3.

* Convert categories to official default groups

* Add new users to default groups

* Rework group cards

* Cleanup

* Add unique bettors to contract and sort by them

* Most bettors to most popular

* Unused vars

* Track unique bettor ids on contracts

* Add followed users' bets to personal markets

* Add new users to welcome, bugs, and updates groups

* Add users to fewer default cats
2022-07-13 15:11:22 -06:00
James Grugett
e868f0a15a Fix pagination component going one page too far + tweaks 2022-07-13 15:15:03 -05:00
James Grugett
9075a6f33a Add headers to limit orders table 2022-07-13 14:59:51 -05:00
Austin Chen
87b669e358 Add FYXX Foundation (h/t Holly Elmore) 2022-07-13 12:44:32 -07:00
Sinclair Chen
a92eda3af2 fix bug where descriptions not showing 2022-07-13 12:36:01 -07:00
Sinclair Chen
9a11f55762
Rich content (#620)
* Add TipTap editor and renderer components

* Change market description editor to rich text

* Type description as JSON, fix string-based logic

- Delete make-predictions.tsx
- Delete feed logic that showed descriptions

* wip Fix API validation

* fix type error

* fix extension import (backend)

In firebase, typescript compiles imports into common js imports
like `const StarterKit = require("@tiptap/starter-kit")`

Even though StarterKit is exported from the cjs file, it gets imported
as undefined. But it magically works if we import *

If you're reading this in the future, consider replacing StarterKit with
the entire list of extensions.

* Stop load on fail create market, improve warning

* Refactor editor as hook / fix infinite submit bug

Move state of editor back up to parent
We have to do this later anyways to allow parent to edit

* Add images - display, paste + uploading

* add uploading state of image

* Fix placeholder, misc styling

min height, quote

* Fix appending to description

* code review fixes: rename, refactor, chop carets

* Add hint & upload button on new lines

- bump to Tailwind 3.1 for arbitrary variants

* clean up, run prettier

* rename FileButton to FileUploadButton

* add image extension as functions dependency
2022-07-13 11:58:22 -07:00
mantikoros
83d8f18bd7 fix bet summary selling 2022-07-13 13:20:53 -05:00
James Grugett
50eee33a6e Redeem shares of makers after matching with limit bets 2022-07-13 12:51:19 -05:00
James Grugett
f1eea66588 Show all limit orders in a tab 2022-07-13 12:15:00 -05:00
mantikoros
737d803903 bet row: default to YES 2022-07-13 11:20:29 -05:00
Ian Philips
18abad38b6 Unused var 2022-07-13 09:13:34 -06:00
Ian Philips
cc1431da60 Disable enter submit on mobile on group chat 2022-07-13 09:12:43 -06:00
Ian Philips
490eabf977 Revert "Revert "Disable enter to submit on mobile group chat""
This reverts commit e3f7f0efda.
2022-07-13 09:08:32 -06:00
Ian Philips
e3f7f0efda Revert "Disable enter to submit on mobile group chat"
This reverts commit b3f4c2f009.
2022-07-13 08:44:27 -06:00
Ian Philips
b3f4c2f009 Disable enter to submit on mobile group chat 2022-07-13 08:34:14 -06:00
Ian Philips
9e90f849a8 Show group scrollbars only when needed 2022-07-13 07:57:51 -06:00
Ian Philips
96a378f25f Handle free response resolution 2022-07-13 07:41:58 -06:00
mantikoros
1f2bdf40d0 bet row: fix labels 2022-07-13 00:07:12 -05:00
Austin Chen
10c510fc6b Feature Wild Animal Initiative 2022-07-12 18:27:22 -07:00
mantikoros
68343701ca answer bet panel: scroll up on ios 2022-07-12 17:47:48 -05:00
mantikoros
5c166b9dd5 bet row: 'higher' 'lower' labels 2022-07-12 17:47:28 -05:00
mantikoros
38aad40569
Simplify bet buttons (#644)
* mono-button bet row

* "bet yes" => "yes"

* prettier
2022-07-12 17:34:10 -05:00
mantikoros
dd9fdc381f track limit orders 2022-07-12 16:55:00 -05:00
mantikoros
24896e44b4 "limit bet" => "limit order" 2022-07-12 16:46:03 -05:00
Marshall Polaris
5fd42df1ed
Don't run share redemption after adding liquidity (#631) 2022-07-12 12:36:31 -07:00
Marshall Polaris
43b30e6d04
Don't "warm up" resolveMarket anymore (#638) 2022-07-12 12:36:10 -07:00
James Grugett
0882f1c0d6 Remove top Pagepadding on small screens 2022-07-11 19:07:37 -05:00
James Grugett
b8d7c2ee17 Size group chat window & nav bar list of groups precisely. Update Page margin/padding. 2022-07-11 18:40:25 -05:00
James Grugett
24fac1fc0b Fix erronous 0 prob shown in table 2022-07-11 15:53:13 -05:00
Ian Philips
ed9a2c0d35 Set min height for group chat 2022-07-11 14:52:16 -06:00
James Grugett
90a75985dd In market bets tab, show limit orders' total order amount 2022-07-11 11:46:09 -05:00
mantikoros
61300e93a4 more validation for creating numeric markets 2022-07-11 11:38:51 -05:00
Ian Philips
7b60cc63ce Fix annoying create description scrolling on firefox 2022-07-11 09:56:10 -06:00
James Grugett
9b252b93ab Fix fee calculation in bet panel tooltip 2022-07-11 10:54:37 -05:00
James Grugett
dd6f5e5ef4 Show better limit order stats in bets table 2022-07-11 10:49:36 -05:00
Ian Philips
52d688885d Group income notifs by source title 2022-07-11 08:11:52 -06:00
Ian Philips
86c256cbf7 Unused var 2022-07-11 08:01:26 -06:00
Ian Philips
a2a08b90ff Show numeric resolution contract value 2022-07-11 07:51:48 -06:00
James Grugett
1e68267e8e Use relative import 2022-07-10 23:09:46 -05:00
James Grugett
098f20ccad Fix limit bet filter to exclude cancelled and filled bets 2022-07-10 22:28:29 -05:00
James Grugett
89d48d6c34 Use hook to fetch user bets 2022-07-10 22:28:04 -05:00
James Grugett
99fcfa6be7 Add portfolio filter for limit bets. 2022-07-10 22:15:07 -05:00
James Grugett
9586e81e95 Show limit bets in bets table 2022-07-10 22:07:42 -05:00
James Grugett
fd7384a099 Hide referrals button on user page 2022-07-10 19:59:23 -05:00
James Grugett
67edc7b639 UserPage: Load user with getStatic props 2022-07-10 19:42:34 -05:00
James Grugett
5e1ed17cdf Load contracts at UserPage top level instead of in BetsList 2022-07-10 19:19:35 -05:00
James Grugett
f294189e20 Refactor notifications to use Pagination component 2022-07-10 18:50:59 -05:00
James Grugett
162e73912e Paginate bets list 2022-07-10 18:41:33 -05:00
James Grugett
5c6a143614 Change portfolio graph option labels 2022-07-10 18:26:06 -05:00
James Grugett
78ceac0659 Don't load user bets twice 👀 2022-07-10 18:22:21 -05:00
Marshall Polaris
4700ceb14c
Refactor some backend-related stuff (#639)
* web/lib/firebase/api-call -> common/api, web/lib/firebase/api

* Reuse `APIError` type in server code

* Reuse `getFunctionUrl` in server code
2022-07-10 15:03:15 -07:00
Marshall Polaris
6462d4a2ed
Migrate createUser function to v2 (#633) 2022-07-10 14:02:32 -07:00
Marshall Polaris
eb9b14d6d5
Migrate unsubscribe function to v2 (#637)
* Migrate unsubscribe function to v2

* Move Stripe import because I forgot to do it before
2022-07-10 13:46:00 -07:00
James Grugett
83c5f9b323 Fix unused var 2022-07-10 14:55:10 -05:00
James Grugett
f2df32e710 PseudoNumeric markets store resolveValue in resolved notification and render it 2022-07-10 14:52:31 -05:00
James Grugett
900fc75506 Add sourceContractId to bet_fill notification 2022-07-10 13:45:32 -05:00
James Grugett
4de22acb3e Tweak check for matching with pool 2022-07-10 13:24:54 -05:00
James Grugett
80ae551ca9
🧾 Limit orders! (#495)
* Simple limit order UI

* Update bet schema

* Restrict bet panel / bet row to only CPMMBinaryContracts (all binary DPM are resolved)

* Limit orders partway implemented

* Update follow leaderboard copy

* Change cpmm code to take some state instead of whole contract

* Write more of matching algorithm

* Fill in more of placebet

* Use client side contract search for emulator

* More correct matching

* Merge branch 'main' into limit-orders

* Some cleanup

* Listen for unfilled bets in bet panel. Calculate how the probability moves based on open limit orders.

* Simpler switching between bet & limit bet.

* Render your open bets (unfilled limit orders)

* Cancel bet endpoint.

* Fix build error

* Rename open bets to limit bets. Tweak payout calculation

* Limit probability selector to 1-99

* Deduct user balance only on each fill. Store orderAmount of bet. Timestamp of fills.

* Use floating equal to check if have shares

* Add limit order switcher to mobile bet dialog

* Support limit orders on numeric markets

* Allow CORS exception for Vercel deployments

* Remove console.logs

* Update user balance by new bet amount

* Tweak vercel cors

* Try another regexp for vercel cors

* Test another vercel regex

* Slight notifications refactor

* Fix docs edit link (#624)

* Fix docs edit link

* Update github links

* Small groups UX changes

* Groups UX on mobile

* Leaderboards => Rankings on groups

* Unused vars

* create: remove automatic setting of log scale

* Use react-query to cache notifications (#625)

* Use react-query to cache notifications

* Fix imports

* Cleanup

* Limit unseen notifs query

* Catch the bounced query

* Don't use interval

* Unused var

* Avoid flash of page nav

* Give notification question priority & 2 lines

* Right justify timestamps

* Rewording

* Margin

* Simplify error msg

* Be explicit about limit for unseen notifs

* Pass limit > 0

* Remove category filters

* Remove category selector references

* Track notification clicks

* Analyze tab usage

* Bold more on new group chats

* Add API route for listing a bets by user (#567)

* Add API route for getting a user's bets

* Refactor bets API to use /bets

* Update /markets to use zod validation

* Update docs

* Clone missing indexes from firestore

* Minor notif spacing adjustments

* Enable tipping on group chats w/ notif (#629)

* Tweak cors regex for vercel

* Your limit bets

* Implement selling shares

* Merge branch 'main' into limit-orders

* Fix lint

* Move binary search to util file

* Add note that there might be closed form

* Add tooltip to explain limit probability

* Tweak

* Cancel your limit orders if you run out of money

* Don't show amount error in probability input

* Require limit prob to be >= .1% and <= 99.9%

* Fix focus input bug

* Simplify mobile betting dialog

* Move mobile limit bets list into bet dialog.

* Small fixes to existing sell shares client

* Lint

* Refactor useSaveShares to actually read from localStorage, use less bug-prone interface.

* Fix NaN error

* Remove TODO

* Simple bet fill notification

* Tweak wording

* Sort limit bets by limit prob

* Padding on limit bets

* Match header size

Co-authored-by: Ian Philips <iansphilips@gmail.com>
Co-authored-by: ahalekelly <ahalekelly@gmail.com>
Co-authored-by: mantikoros <sgrugett@gmail.com>
Co-authored-by: Ben Congdon <ben@congdon.dev>
Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-07-10 13:05:44 -05:00
mantikoros
fc06b03af8 fix getCpmmLiquidityPoolWeights 2022-07-09 22:39:26 -04:00
mantikoros
d063e209dd Revert "expand search bar when typing on mobile"
This reverts commit 43b1096313.
2022-07-09 22:04:50 -04:00
Marshall Polaris
480b3e7c54
Make referral stuff not busted (#632) 2022-07-09 14:38:23 -07:00
mantikoros
43b1096313 expand search bar when typing on mobile 2022-07-09 17:27:39 -04:00
Marshall Polaris
67a05c2f1b
Migrate transact function to v2 (#635) 2022-07-09 13:54:15 -07:00
Marshall Polaris
581a42f288
Migrate stripeWebhook and createCheckoutSession to v2 (#636) 2022-07-09 13:43:18 -07:00
mantikoros
e7e686d579 return creator liquidity after resolution 2022-07-09 13:53:50 -04:00
Marshall Polaris
c1ca1471a1
Migrate createAnswer function to v2 (#634)
* Migrate createAnswer function to v2

* Remove unhelpful toString on APIError
2022-07-09 00:26:56 -07:00
Marshall Polaris
fdde73710e
Migrate claimManalink function to v2 (#628)
* Implement helpful `toString` on client `APIError`

* Migrate claimManalink function to v2
2022-07-08 15:28:04 -07:00
Marshall Polaris
d9f42caa6a
Migrate addLiquidity and withdrawLiquidity functions to v2 (#627) 2022-07-08 15:08:17 -07:00
Marshall Polaris
ed0544212d
Migrate changeUserInfo function to v2 (#626) 2022-07-08 15:00:03 -07:00
mantikoros
93b293ca0e remove quick betting for FR markets 2022-07-08 12:50:46 -04:00
mantikoros
50c5f8b6eb reenable fees on share sales; rename getCpmmFees() 2022-07-08 12:34:16 -04:00
Ian Philips
b1b016f9e0
Enable tipping on group chats w/ notif (#629) 2022-07-07 17:23:13 -06:00
Ian Philips
d6136a9937 Minor notif spacing adjustments 2022-07-07 17:17:10 -06:00
Austin Chen
53ddb1243b Clone missing indexes from firestore 2022-07-07 15:41:44 -07:00
Ben Congdon
c3bc25a4b9
Add API route for listing a bets by user (#567)
* Add API route for getting a user's bets

* Refactor bets API to use /bets

* Update /markets to use zod validation

* Update docs
2022-07-07 15:36:02 -07:00
Ian Philips
999c1cd8e3 Bold more on new group chats 2022-07-07 15:52:28 -06:00
Ian Philips
e456b9a855 Analyze tab usage 2022-07-07 15:24:13 -06:00
Ian Philips
3eee4a4103 Track notification clicks 2022-07-07 15:06:29 -06:00
Ian Philips
3ff8b26312 Remove category selector references 2022-07-07 14:55:28 -06:00
Ian Philips
d6e808e1a3 Remove category filters 2022-07-07 14:45:26 -06:00
Ian Philips
cfbb78af48
Use react-query to cache notifications (#625)
* Use react-query to cache notifications

* Fix imports

* Cleanup

* Limit unseen notifs query

* Catch the bounced query

* Don't use interval

* Unused var

* Avoid flash of page nav

* Give notification question priority & 2 lines

* Right justify timestamps

* Rewording

* Margin

* Simplify error msg

* Be explicit about limit for unseen notifs

* Pass limit > 0
2022-07-07 14:41:50 -06:00
mantikoros
a22b29ad6d create: remove automatic setting of log scale 2022-07-07 12:36:34 -04:00
Ian Philips
7f8617832f Unused vars 2022-07-07 07:05:12 -06:00
Ian Philips
b8748fd49a Leaderboards => Rankings on groups 2022-07-07 06:54:00 -06:00
Ian Philips
93b2900015 Groups UX on mobile 2022-07-07 06:53:14 -06:00
Ian Philips
a23c744c3e Small groups UX changes 2022-07-06 17:24:53 -06:00
ahalekelly
2591655269
Fix docs edit link (#624)
* Fix docs edit link

* Update github links
2022-07-06 15:41:13 -06:00
Ian Philips
e969540c72 Slight notifications refactor 2022-07-06 15:06:41 -06:00
Ian Philips
54b4f97a84 Move timestamp to same line 2022-07-06 13:45:31 -06:00
Ian Philips
de20ee9fb9
Show tip notifications (#623)
* Show tip notifications

* Optimizing notifications for mobile

* Unused vars

* Move income reason logic to income notif

* Remove unnecessary icons

* Unused vars
2022-07-06 13:30:51 -06:00
Austin Chen
2d1e76eae8 When duplicating, add the original link in description 2022-07-06 10:39:19 -07:00
Ian Philips
434b8b9dbe Just show first names to save space 2022-07-06 07:51:32 -06:00
Ian Philips
83a02c4b20 Small notifications ux improvements 2022-07-06 07:45:47 -06:00
Ian Philips
a6143c1abb Always group income 2022-07-06 07:27:21 -06:00
Austin Chen
029021b351 Remove Categories from /create 2022-07-05 17:20:37 -07:00
Austin Chen
6cd8b04bd0 Nit: Fix spacing 2022-07-05 16:53:00 -07:00
Austin Chen
b71944607b Simplify Tweet text 2022-07-05 16:48:59 -07:00
Austin Chen
cb25a7752d
Duplicate a question from '...' screen (#622)
* Duplicate a question from '...' screen

* Remove unused code
2022-07-05 16:26:58 -07:00
Ian Philips
3a6d28e2c2
Bold groups with recent chat activity (#621)
* Bold groups with recent chat activity

* Cleanup

* Cleanup
2022-07-05 17:18:37 -06:00
Sinclair Chen
270a5fc139 also filter by username when adding people 2022-07-05 14:34:16 -07:00
Marshall Polaris
5eca9def9d
Don't accidentally make meaningless zero bets (#619) 2022-07-05 14:01:57 -07:00
Marshall Polaris
4d1c50a6cc
Redemption refactoring (#614)
* Refactor share redemption code into a few sensible functions

* Put very general share redemption code into common
2022-07-05 12:35:39 -07:00
Marshall Polaris
7f2bbdcb87
Allow people to sell all their shares (#599) 2022-07-05 12:26:51 -07:00
Marshall Polaris
f0fbdf1b42
Add a missing index (#606) 2022-07-05 12:26:13 -07:00
Marshall Polaris
a9e74e7111
Add functions framework as explicit dependency (#613) 2022-07-05 12:25:58 -07:00
Marshall Polaris
9bff858696
Fix up lint configuration, lint line endings (#615)
* Make sure we ignore all built code in common and functions

* Add lint for Unix line endings

* Fix line endings in withdraw-liquidity.ts
2022-07-05 12:25:44 -07:00
Ian Philips
b26648c1ce
Daily trading bonuses (#618)
* first commit, WIP

* Give trading bonuses & paginate notifications

* Move read & update into transaction

* Move request bonus logic to notifs icon
2022-07-05 11:29:26 -06:00
Austin Chen
53b4a28944 Check in .env to git 2022-07-04 16:21:59 -07:00
Austin Chen
c39e3aedfa Also send .env file when deploy functions 2022-07-04 16:04:05 -07:00
Sinclair Chen
af2b148b34 show names on admin user table 2022-07-04 13:25:44 -07:00
Ian Philips
790fdad1e3 Display refered by publicly 2022-07-04 09:18:01 -06:00
Ian Philips
22f917e250 Avatar sizes to 24, size 20 is broken 2022-07-04 08:32:51 -06:00
Ian Philips
e712ad8289
Allow users to choose who referred them (#611)
* Allow users to choose who referred them

* Refactor

* Rewording

* Match list styles

* Match empty text styles
2022-07-04 07:49:41 -06:00
mantikoros
d78bbcb3df fix navbar tracking 2022-07-03 23:43:18 -04:00
Austin Chen
579dcd81dc Update env config template 2022-07-03 16:46:45 -07:00
Austin Chen
9839b7b5a4 Allow customizing starting balance & antes 2022-07-03 16:46:15 -07:00
James Grugett
8fdc44f7f3 Switch to firebase dev before serving firebase emulators 2022-07-03 15:37:24 -04:00
Pico2x
960f8a1b3d
Toggle weekly leaderboard and daily/weekly/alltime portfolio graph (#616)
* Toggle weekly leaderboard and daily/weekly/alltime portfolio graph

* Formatmoney for tooltip value
2022-07-03 14:18:12 -05:00
Marshall Polaris
7dea9cbfa8
Use getAll Firestore technology to improve some code (#612) 2022-07-02 16:24:03 -07:00
Marshall Polaris
90d7f55c6d
Fix backup DB job to actually backup most things, refactor (#605)
* Make backup manually invokable and thereby testable

* Add a shitload of missing stuff to our backups

* Also backup follows as per James
2022-07-02 13:27:06 -07:00
Marshall Polaris
18b8758191
Remove code for obsolete feed updater backend jobs (#607)
* Remove code for obsolete feed updater backend jobs

* Kill two more obsolete guys
2022-07-02 13:26:42 -07:00
mantikoros
218b18254c add liquidity: support pseudo numeric markets 2022-07-02 15:46:32 -04:00
mantikoros
1a6afaf44f
Pseudo numeric market (#609)
* create pseudo-numeric contracts

* graph and bet panel for pseudo numeric

* pseudo numeric market layout, quick betting

* Estimated value

* sell panel

* fix graph

* pseudo numeric resolution

* bets tab

* redemption for pseudo numeric markets

* create log scale market, validation

* log scale

* create: initial value can't be min or max

* don't allow log scale for ranges with negative values (b/c of problem with graph library)

* prettier delenda est

* graph: handle min value of zero

* bet labeling

* validation

* prettier

* pseudo numeric embeds

* update disclaimer

* validation

* validation
2022-07-02 14:37:59 -05:00
Sinclair Chen
cc52bff05e
fix functions/README formatting 2022-07-01 16:45:05 -07:00
Ian Philips
2dce3e15a1 Correct margin on tabs 2022-07-01 17:03:26 -06:00
Ian Philips
b9931e65da Allow adding anyone's contract to a group 2022-07-01 16:37:30 -06:00
James Grugett
cb68530e2a Use client side contract search for emulator 2022-07-01 12:26:45 -04:00
Ben Congdon
d29115b05a
Nitpick on Manalinks claim page (#608) 2022-07-01 08:40:43 -07:00
Ian Philips
5034a43c3c Filter for ian's deleted users 2022-07-01 08:29:12 -06:00
Ian Philips
3165e42119
Referrals (#592)
* add trigger for updated user

* Add referral bonuses and notifications for them

* Cleanup

* Add share group button, cleanup

* Cleanup

* Add referrals list to user profile

* Remove unused

* Referral bonus => constant

* Refactor

* Add referral txn to helper fn

* Move reads into firebase transaction

* Use effects to write referral info

* Flex-wrap profile objects

* Small ui changes

* Restrict referral user to one update

* Remove rogue semicolon

* Note about group referral query details

* Track referrals, add them to settings list
2022-07-01 07:47:19 -06:00
Marshall Polaris
b0b8c6e98b
Make the resolve API docs not obviously wrong (#604) 2022-06-30 15:25:32 -07:00
Ben Congdon
7fc1ec6bd2
Clear suggested FR answer after submission (#603) 2022-06-30 15:13:59 -07:00
Ben Congdon
c5efd5b7d0
Market Resolution API (#600)
* Add market resolution API

* Add additional free market resolution validation

* Address review comments

* Refactor resolution validation code somewhat

Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-06-30 15:11:45 -07:00
Ben Congdon
a5a0a1370a
Remove daily free market text from docs (#601) 2022-06-30 10:07:28 -05:00
Marshall Polaris
fc7f19e785
Finalize v2 resolvemarket migration (#598)
* Update resolve-market to be a v2 function

* Cleanup API error responses

* Update frontend to use v2 version of resolvemarket

* Appease ESLint

* Address review comments

* Appease ESLint

* Remove unnecessary auth check

* Fix logic bug in FR market validation

* Make it so you can specify runtime opts for v2 functions

* Cleanup to resolve market API resolutions input, fixes

* Fix up tiny lint

* Last minute cleanup to resolvemarket FR API input validation

Co-authored-by: Benjamin <ben@congdon.dev>
2022-06-29 16:47:06 -07:00
Marshall Polaris
2fbbc66029
Point v2 functions @ localhost during emulation (#597) 2022-06-29 16:31:53 -07:00
Austin Chen
7bbc425690 Only show "My Groups" when there is at least 1 group 2022-06-29 17:54:08 -05:00
Austin Chen
19d12c949a Add a line spacer on the sidebar 2022-06-29 17:51:11 -05:00
Marshall Polaris
3b4666ba3e
Add Firebase schema collection helpers (kind of an RFC) (#583)
* Add Firebase schema collection helpers

* Decentralize definitions from schema file (James feedback)

* Add lint comment
2022-06-29 12:21:40 -07:00
Austin Chen
8132fa595b Don't add space when there are 0 groups 2022-06-29 13:08:01 -05:00
Austin Chen
2d79d7f8db
Rework nav to show list of groups (#596)
* Rework nav to show list of groups

* Fix lint

* Replace Portfolio with Profile link

* Lint: remove unused vars
2022-06-29 12:33:20 -05:00
Ian Philips
8c3c30c707
Show groups on user page, allow to join/leave (#594)
* Show groups on user page, allow to join/leave

* Link to groups

* Unused var
2022-06-29 11:00:43 -05:00
SirSaltyy
63528aa0f3
Add CES charity (#591)
Added CES charity to the charity page.
2022-06-28 17:19:58 -05:00
Forrest Wolf
7f9b0557c4
Reorganize verify scripts (#589)
* Update verify to match check for functions

* Give each subdirectory a verify:dir script
2022-06-28 12:46:25 -07:00
Austin Chen
c18a0378e9 Tweak nav items around 2022-06-28 11:18:55 -05:00
Austin Chen
2f434c849d Remove portfolio link; user icon links to portfolio 2022-06-28 11:03:14 -05:00
Marshall Polaris
0b585d1c98
Typescript project references take 2 (#586)
* More liberal .gitignores on TS output directories

* Use project references for Typescript functions project

* Use /dist dir for Cloud Functions deployment payload

* Fix Github actions functions tsc job
2022-06-27 13:32:24 -07:00
James Grugett
4107d5fedb Fix weird layout on refreshing create page 2022-06-27 14:40:40 -05:00
James Grugett
1e904f567a Revert "Use Typescript project references, improve functions build/deploy (#575)"
This reverts commit 4edad9f19b.
2022-06-27 12:30:22 -05:00
Sinclair Chen
54356b8d2f
Remove undo. Show full tip amount. Linear scale. (#573) 2022-06-27 11:18:15 -05:00
Austin Chen
c1765ca0cb Use green for FR (and numeric) cards 2022-06-26 19:44:10 -05:00
Ben Congdon
3b6ba76db6
Add market liquidity addition events to bets feed (#578)
* Add liquidity events to bets feed

* Use larger avatar for liquidity feed items
2022-06-26 19:00:02 -05:00
Marshall Polaris
0067bee94b
Compute stats in Firebase instead of Vercel (#584)
* Add stats updating cloud function

* Read stats from database on client instead of computing them

* Improve logging for stats updater

* Tidying up
2022-06-26 14:42:42 -07:00
Marshall Polaris
2e5d852a77 Fix lint 2022-06-25 18:20:54 -07:00
Marshall Polaris
11f6a57c54 Fix types on API LiteUser 2022-06-25 18:20:10 -07:00
Justin
fa86f5e89a
Add Users API endpoint (#547)
* add users endpoint to API

* docs, url

* tweak docs
2022-06-25 16:28:01 -07:00
Ben Congdon
5e768aa57c
Prevent duplicate Free Response answers (#581)
* Prevent duplicate Free Response answers

* Address review comments
2022-06-25 16:18:49 -07:00
Marshall Polaris
b7cbd2a431 More robust functions deploy script 2022-06-24 22:44:41 -07:00
Marshall Polaris
4edad9f19b
Use Typescript project references, improve functions build/deploy (#575)
* More liberal .gitignores on TS output directories

* Use project references for Typescript projects

* Use /dist dir for Cloud Functions deployment payload

* Disable `next build` typechecking

* Fiddle with GitHub tsc jobs
2022-06-24 22:41:30 -07:00
Austin Chen
3123021d94 Rename "Details" to "About" 2022-06-24 18:41:02 -05:00
Austin Chen
da81035e58 Group leaderboards show members only by default 2022-06-24 18:38:39 -05:00
Austin Chen
8357361038 Remove unused function 2022-06-24 18:06:20 -05:00
Austin Chen
f224fc2e28 Clean up Group Sidebar by moving into Details tab 2022-06-24 16:02:05 -05:00
Ian Philips
969cdcaa16 Search group contracts 2022-06-24 12:32:59 -05:00
James Grugett
b7dbcaaadf Run prettier 2022-06-24 12:27:03 -05:00
Pico2x
e7abe709b3
[Leaderboard] Show daily 'topBettor' leaderboard (#579) 2022-06-24 12:24:20 -05:00
James Grugett
8d7bf6fb64 Apply tag and creatorId filters to contract firestore search 2022-06-24 12:19:04 -05:00
Ian Philips
8ced159d9a Various group & mobile ux improvements 2022-06-24 12:16:37 -05:00
Pico2x
ebc4bd6bcf
[PortfolioGraph] Shows a graph of the portfolio value over time (#570)
* [Portfolio Graph] Shows a graph of the portfolio value over time

* [PortfolioGraph] Fix some nits.

* [PortfolioGraph] Comment out portfolio-value-section

Hides the component completely for now, so we can land today. My plan would be to land today, wait for the history to build up, and then revert this commit. As opposed to leaving the PR idle for a while, and then have to deal with conflicts.

* [PortfolioGraph] Rm duplicate firestore rule
2022-06-24 12:14:20 -05:00
Marshall Polaris
b4e09e37b1
Bump memory on all v2 functions to 2GB (#577)
* Bump memory on all v2 functions to 2GB

* Also give the v2 functions 1 vCPU

* Also explicitly specify concurrency
2022-06-24 00:18:08 -07:00
Marshall Polaris
db3c65a974
Bump Cloud Functions Node version from 12 -> 16 (#563) 2022-06-23 22:09:54 -07:00
Marshall Polaris
d8a0cc281d
Greatly extend static page generation timeout (#576) 2022-06-23 21:51:46 -07:00
Marshall Polaris
603bec9e88
Ameliorate homepage search spam (#564)
* Don't recompute search filters when follows loaded unnecessarily

* Don't wait for router to get saved search sort
2022-06-23 16:47:03 -07:00
Marshall Polaris
4f9e303daa
Clean up definition of v2 cloud function URLs (#562) 2022-06-23 16:46:49 -07:00
Ian Philips
f0d4e9940c Improve group user search 2022-06-23 16:49:14 -05:00
Ian Philips
17ac6c58b2 Don't prompt to comment on FR bets, arrow spacing 2022-06-23 16:09:26 -05:00
Ian Philips
b5810481d0 Restore category in tags to /create 2022-06-23 15:55:05 -05:00
Sinclair Chen
8cc0cf160a lint 2022-06-23 14:41:42 -05:00
Sinclair Chen
970800bd31 add tailwind to recommended vscode extensions 2022-06-23 14:25:55 -05:00
Sinclair Chen
00c2012ccf Refactor empty avatar component 2022-06-23 14:23:40 -05:00
Ian Philips
9757ed1d8b lint 2022-06-23 13:02:52 -05:00
Ian Philips
211905c27f Free daily markets on hiatus 2022-06-23 13:00:14 -05:00
Ian Philips
75907f6c18 Join group button 2022-06-23 12:40:32 -05:00
Ian Philips
b569f67fc1 Group discussion ux improvements 2022-06-23 12:36:09 -05:00
ahalekelly
28c8cc6863
Fix line off the right of closed markets (#569) 2022-06-23 12:13:13 -05:00
Ben Congdon
cdd8af241b
Show resolution time in market cards when appropriate (#565)
* Show resolution time in market cards when appropriate

* Rebase and fix contract-search-firestore
2022-06-23 12:12:57 -05:00
Ben Congdon
4a1a907b37
Fix API docs formatting (#568) 2022-06-23 10:51:41 -05:00
Austin Chen
6cc2d8af58
Manalink: Send mana to anyone via link (#114)
* Set up Firestore structure for mana bounty links

* Split up manalinks into successes and failures

* Allow clients to create manalinks

* Track txnId and successful users

* Store custom amounts in the link

* List all manalinks you've created

* Support backend for claiming manalinks

* Add some more error handling

* Tweak readme

* Fix typescript breakage

* Revert "Convert common imports in functions to be absolute"

This reverts commit c03518e906.

* Scaffolding so `claimManalink` works

* Clean up imports

* Barebones endpoint to claim mana

* Fix rules to only allow link creators to query

* Design out claim giftcard

* List all claimed transactions

* Style in a more awesome card

* Fix import

* Padding tweak

* Fix useManalinkTxns hook

* /send -> /link

* Tidy up some details

* Do a bunch of random manalinks work

* Fix up LinksTable to build

* Clean up LinksTable an absurd amount

* Basic details functionality on manalinks table

* Work on manalink claim stuff

* Fix up some merge mess

* Not-signed-in flow implemented

* Better manalinks table

* Only show outstanding links in table

* Use new `ManalinkTxn` type

* /link -> /links

* Change manalinks page UI to use nice looking tabs

* Many fixes to manalinks UI

* Default to 1 use

* Tidying up

* Some copy changes based on feedback

* Add required index

Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-06-23 01:07:52 -07:00
ahalekelly
4eedf65b21
API update pool (#553)
* Update api pool and totalLiquidity

* fix pool type

* reverting totalLiquidity changes

* pool and totalLiquidity docs description

* Changed pool type to match elsewhere
2022-06-23 00:37:04 -07:00
Austin Chen
6dcbc92a66 Update firestore.indexes.json 2022-06-22 19:04:25 -05:00
Austin Chen
ba39eb114c Hide "Get M$" on private instances 2022-06-22 19:02:50 -05:00
Austin Chen
6f789c9363 Fix build 2022-06-22 18:45:48 -05:00
Austin Chen
fee36a378c Use firestore-based search for private instances 2022-06-22 18:44:23 -05:00
James Grugett
16ac25bb77 Move more nav options to More menu on mobile 2022-06-22 17:34:43 -05:00
Justin
4ea7b6692a
add resolutionProbability to LiteMarket (#546) 2022-06-22 15:28:11 -07:00
Ian Philips
237a6c4d2a Unused vars 2022-06-22 17:21:31 -05:00
Ian Philips
6a35d3bf2d Remove group details cache, update group directly 2022-06-22 17:19:17 -05:00
James Grugett
e5e13cc598 Update follow leaderboard copy 2022-06-22 16:22:45 -05:00
James Grugett
8276046f8e Number of followers => Total followers 2022-06-22 16:17:21 -05:00
James Grugett
a785fe4f83 Fix follow leaderboard layout on mobile 2022-06-22 15:52:23 -05:00
James Grugett
8b1d132e17
Daily/Weekly/Monthly Leaderboards by Fede (#557)
* [Leaderboards] Added period toggle for leaderboards

* [Leaderboards] TopBettors now calculates by period correctly

* [Leaderboard] Use a subcollection for the portfolio caching

* [Leaderboard] Switches to a tab view, temporarily hides the missing topBettors periods

* [Leaderboard] Reverts random yarn.lock changes

* Fix type error from merge

* Increase timeout on update metrics

* Update firebase rules to allow reading user portfolioHistory

Co-authored-by: Pico2x <pico2x@gmail.com>
2022-06-22 15:29:40 -05:00
mantikoros
c58e75f49a comment email: include username in subject; 'on Manifold' in from label 2022-06-22 15:01:03 -05:00
Ian Philips
4902321cd3 Update firstore rules 2022-06-22 13:17:56 -05:00
Ian Philips
495cbef995 portfolio fix 2022-06-22 12:54:41 -05:00
Ian Philips
eb02207c56 portfolio fix 2022-06-22 12:51:20 -05:00
Ian Philips
a1ea864799 Debounce 2022-06-22 11:43:47 -05:00
Ian Philips
3b3717d307
Groups (#510)
* Folds=>groups

* Show groups on user profile

* Allow group creation from /create

* Refactoring to groups

* Convert folds to groups

* Add new add to group notification

* Fix user profile tab bug

* Add groups nav and tab for my groups

* Remove bad profile pages

* remove comments

* Add group list dropdown to sidebar

* remove unused

* group cards ui

* Messages=>Comments, v2, groupDetails

* Discussion time

* Cleaning up some code

* Remove follow count

* Fix pool scoring for cpmm

* Fix imports

* Simplify rules, add GroupUser collection

* Fix group cards

* Refactor

* Refactor

* Small fixes

* Remove string

* Add api error detail handling

* Clear name field

* Componentize

* Spacing

* Undo userpage memo

* Member groups are already in my tab

* Remove active contracts reference for now

* Remove unused

* Refactoring

* Allow adding old questions to a group

* Rename

* Wording

* Throw standard v2 APIError

* Hide input for non-members, add about under title

* Multiple names to & # more

* Move comments firestore rules to appropriate subpaths

* Group membership, pool=>volume

* Cleanup, useEvent

* Raise state to parent

* Eliminate unused

* Cleaning up

* Clean code

* Revert tags input deletion

* Cleaning code

* Stylling

* Limit members to display

* Array cleanup

* Add categories back in

* Private=>closed

* Unused vars
2022-06-22 11:35:50 -05:00
Ben Congdon
67d0a6c0c2
Create Top Followed Users leaderboard (#531)
* Create Top Followed Users leaderboard

* Switch to increment/decrement approach for caching user follower counts

* Backfill script for user follow counts

* Appease ESLint

* Address review comment

Co-authored-by: James Grugett <jahooma@gmail.com>
2022-06-22 11:05:54 -05:00
ahalekelly
7a09365f00
New tooltip for binary market chart, with date and time (#529)
* New tooltip for binary market chart

* Tooltip on one line and rewrote date formatter

* Interpolate graph points and updated date format

* Reduced point count to 300 desktop, 50 mobile
2022-06-22 10:45:15 -05:00
Marshall Polaris
0df4ca8f1e
Make type definitions for Txn more precise (#551)
* Make type definitions for Txn more precise

* Changes in response to feedback
2022-06-22 01:13:25 -07:00
Justin
402f80b420
Updating docs (#549)
* update docs pass 1

* styling

* urls

* merge binary/fr market docs

* styling

* cpmm liquidity details

* liquidity disclaimer

* ahalekelly suggestions

* pool note
2022-06-22 01:12:52 -07:00
TrueMilli
5b02485f4b
added recommended extensions (#550)
* prettier
* eslint
* firestore security rules
2022-06-21 17:20:28 -05:00
Sinclair Chen
7e999881c5
Check toUser balance sufficient in negative txns (#555) 2022-06-21 11:52:02 -05:00
mantikoros
a8b874a32f track comment tips 2022-06-21 10:36:44 -05:00
James Grugett
9a97da72d8 Don't show sell button if you have sold all your shares 2022-06-21 10:14:55 -05:00
mantikoros
d4f2a81735 exclude certain categories for new users 2022-06-21 10:01:51 -05:00
James Grugett
1b3b4eb6d8 Don't show sell button for closed markets 2022-06-21 09:59:12 -05:00
Marshall Polaris
5add7eea49
Remove notification box about public/hidden bets (#544) 2022-06-20 18:29:46 -05:00
Justin
d572efad1a
Add Statistics link to sidebar (#545)
* add stats link to sidebar & mobile nav

* prettier
2022-06-20 18:28:18 -05:00
Ian Philips
9004c694ca Fix comments & bets mixing in tabs ui 2022-06-20 08:58:18 -05:00
Marshall Polaris
dca957708c
http -> https in documentation (#552) 2022-06-19 14:48:16 -07:00
Marshall Polaris
1075fec53f
Clean up unclean user names (#543)
* Clean the user's display name on update.

The user's display name should always be clean (see for example
functions/src/create-user.ts). However, change-user-info.ts does not
enforce this, thus potentially allowing a malicious user to change their
name to something that doesn't satisfy the rules for clean display
names.

Note: this cannot happen currently because all callers (in profile.tsx)
clean the name. However, doing it here is good defense in depth
(similar to how the userName is cleaned).

* Update display name max length to 30

* Add a script to hunt down too-long display names

* Make util.isProd a function

* Don't access admin.firestore() on top level of utils.ts

Co-authored-by: Jonas Wagner <ltlygwayh@gmail.com>
2022-06-18 14:31:39 -07:00
Marshall Polaris
08632a3a07
Correctly proxy GET and HEAD requests instead of dying (#542) 2022-06-18 02:09:44 -07:00
James Grugett
24176c864d Yarn lock change 2022-06-17 23:39:44 -05:00
Sinclair Chen
833dd37469
Comment tips (attempt 2) (#539)
* Add tip arrows UI (visual)

* move tipper into its own component

* simplify score calculation

* Add tip txns

- more specific txn types
- fix transact cloud function to be able to create tip txns
- insert tips into comments via a context

* Refactor tipper to send tip txns

* Stop tipping yourself. Disable anons.

* Style tipper (smaller)

* remove default exports

* capitalize tooltips

* rename stuff

* add exhausting hook dependencies

* replace context with prop threading

* fix eslint unused vars

* fix: thread tips correctly into fr comments
2022-06-17 22:28:16 -05:00
Marshall Polaris
bb934d8390
Remove surprising 2 minute edge caching for API market data (#541) 2022-06-17 17:43:02 -07:00
James Grugett
83ded17625 Revert "Comment tips (#469)"
This reverts commit e567782a7d.
2022-06-17 16:31:21 -05:00
mantikoros
7679849c7d show 2 decimal places for fees 2022-06-17 16:28:25 -05:00
Sinclair Chen
e567782a7d
Comment tips (#469)
* Add tip arrows UI (visual)

* move tipper into its own component

* simplify score calculation

* Add tip txns

- more specific txn types
- fix transact cloud function to be able to create tip txns
- insert tips into comments via a context

* Refactor tipper to send tip txns

* Stop tipping yourself. Disable anons.

* Style tipper (smaller)

* remove default exports

* capitalize tooltips

* rename stuff

* add exhausting hook dependencies

* replace context with prop threading

* fix eslint unused vars
2022-06-17 14:19:42 -07:00
Sinclair Chen
e0a40d4d24 Replace leaderboard cake icon with line go up 2022-06-17 10:38:47 -07:00
Marshall Polaris
732a474cc9
Add userId to bets in API (#530)
* add userid to bets

* allBets -> bets

* revert one

Co-authored-by: wasabipesto <21313833+wasabipesto@users.noreply.github.com>
2022-06-17 00:35:38 -07:00
Ben Congdon
172f14c16f
Include answer probabilities in FreeResponse API results (#526)
* Include answer probabilities in FreeResponse API results

* Appease ESLint
2022-06-17 00:20:43 -07:00
Marshall Polaris
61a2bcb609
Don't unnecessarily query contract in onCreateBet (#528) 2022-06-17 00:15:37 -07:00
Marshall Polaris
0820cc8f4d
Clean some API stuff up and instrument placeBet with a bunch of logging (#521)
* Hoist some variables out of functions

* Use built in CORS processing machinery

* Instrument `placebet` with a bunch of logging
2022-06-16 20:57:03 -07:00
Ben Congdon
a8ae724159
Don't show the 'New' chip on resolved markets (#523)
* Don't show the 'New' chip on resolved markets

* Add createdTime check to contract-details
2022-06-16 22:52:53 -05:00
ahalekelly
f063d5cd24
Step charts (#520)
* Use step charts

* Fixed formatting

* Fixed flat line on right side of chart

* Tweaked chart margins

* Restored right margin
2022-06-16 22:51:48 -05:00
Ben Congdon
2d3088bfc3
Create initial 'Awesome Manifold' docs page (#522)
* Create initial 'Awesome Manifold' docs page

* Add awesome-manifold entry for Mastodon bot
2022-06-16 16:46:41 -07:00
Marshall Polaris
5d6bdff988
Kill updateRecommendations job (#525) 2022-06-16 17:38:32 -05:00
ahalekelly
73b048a5dc
Update binary-markets.md (#512) 2022-06-16 15:25:21 -07:00
Ben Congdon
60bb892601
Include free response answers in FullMarket API response (#519) 2022-06-16 15:14:59 -07:00
ahalekelly
6bb1a1f9ea
Update edit link to point to new repo (#514)
Resolves #498
2022-06-16 14:58:05 -07:00
Sinclair Chen
b99f9dd193 make leaderboard margin same as other pages 2022-06-16 14:43:34 -07:00
Sinclair Chen
8219929227 fix: sidebar wider on medium screens 2022-06-16 14:14:08 -07:00
James Grugett
ecbfed049d Reveal email in admin table 2022-06-16 14:13:08 -05:00
mantikoros
67abb942a0 eliminate platform, liquidty fees 2022-06-16 12:46:34 -05:00
mantikoros
d2a1af7c15 track search, track charity donation 2022-06-16 11:49:57 -05:00
mantikoros
6c6f03e622 fix subtle tabs bug 2022-06-16 11:29:04 -05:00
mantikoros
c2945f11b7 track create user 2022-06-16 10:45:44 -05:00
mantikoros
a15d39110b bug fix: tracking messing with alogolia 2022-06-15 22:17:28 -05:00
mantikoros
f295e0a537 track landing page 2022-06-15 21:42:11 -05:00
mantikoros
fb10e9cddc server side tracking; track M$ purchases 2022-06-15 21:29:53 -05:00
James Grugett
b1597c0f24 Figure out a better hack to fake the url on home page. (And fix bug navigating back to home.) 2022-06-15 21:00:30 -05:00
SirSaltyy
6a73dc042c
Update Trending Markets (#513) 2022-06-16 00:05:25 +01:00
James Grugett
0c1ca11304 Don't show unsavedChanges warning if you are submitting new market. 2022-06-15 17:19:16 -05:00
mantikoros
781506129c sign in, sign up button styling 2022-06-15 16:58:52 -05:00
mantikoros
c45da8c334
Tracking (#511)
* tracking helper functions

* track everything

* remove extraneous code
2022-06-15 16:34:34 -05:00
James Grugett
730b7272ce Warn about unsaved changes on create page. 2022-06-15 15:12:16 -05:00
James Grugett
e4f1d7cae1 Change eslint unused var to 'warn' instead of 'error' 2022-06-15 14:48:12 -05:00
mantikoros
c3b15ac5dc prevent total liquidity stat from going negative 2022-06-15 11:33:58 -05:00
James Grugett
cc4b9abd9f Revert "Automated market resolution (#404)"
This reverts commit a3663d03e8.
2022-06-14 23:31:20 -05:00
James Grugett
26921451a8 Revert "Fix auto resolve markets query"
This reverts commit b405bd453a.
2022-06-14 23:30:58 -05:00
James Grugett
b405bd453a Fix auto resolve markets query 2022-06-14 23:16:02 -05:00
mantikoros
38c63fb3ee
Amplitude (#505)
* basic amplitude setup

* delete heap

* track referrers

* basic tracking

* delete unused import

* prettier
2022-06-14 22:00:36 -05:00
Marshall Polaris
4f96b9ef63
Finish optimizing updateFooMetrics functions (#489)
* Consolidate metrics updates into one batch job

* Try batching updates of metrics

* Don't look up all bets again for all contracts

* Tidying up

* Make computeTotalPool less needlessly inefficient looking
2022-06-14 17:38:38 -07:00
Ben Congdon
cb70ab3675
Typo fix in market creation API example (#497) 2022-06-14 17:27:48 -07:00
TrueMilli
a3663d03e8
Automated market resolution (#404)
* Added radio buttons to market creation (non functional)

* Ignoring vs code files

Should this be done in the repo or should everyone using VS Code do that himself globally on his machine(s)?

* Removed 'automatic' resolution

* added union type for resolution

* revert: resolution could be anything here (non binary markets)

* Expanded ChoicesToggleGroup for string choices

* Added combined resolution and required buttons to market creation

* restricted automatic resolution to binary markets

* added automatic resolution to contract

* added automatic resolution to contract overview

* string or number array to mixed array

* created const for resolutions

* Added comments for leading semicolons

* configuration of auto resolution on market creation

* v1.22.19

* v1.0.0

* v0.0.0

* v1.0.0

* v1.22.19

* Mock display automatic resolution

* Revert changes to market creation

* Revert "v1.22.19"

This reverts commit 22f59adc9c.

* Removed resolutiontype from contract creation

* Added auto resolution time to contract

* Auto resolution date editable

* refactoring

* Editable interface for auto resolution

* New edit interface for auto resolution

* Setting of auto resolve date when changing close date

* prohibited changing other peoples markets

* removed unnecessary export

* refactoring

(cherry picked from commit 4de86d5b08)

* Added comments for leading semicolons

(cherry picked from commit 60739c7853)

* Ignoring vs code files

Should this be done in the repo or should everyone using VS Code do that himself globally on his machine(s)?

(cherry picked from commit 944de9398a)

* removed unused imports and variables

* added type for binary resolution

* Prettier

* const for binary resolutions

* using the type "resolution"

* Prettier

* Re-added comment

* Update functions/src/create-contract.ts

* Revert "Ignoring vs code files"

This reverts commit 09aea5c207.

* launch config for debugging with vs code WIP

* "Launch Chrome" does not work since login via google is not possible in debugger-chrome
* Breakpoints are unbound when attached to chrome

* Revert "Added comments for leading semicolons"

* prettier

* linebreak crlf

* vscode settings

* correct linebreaks
* search exclusion
* automatic prettifier

* vscode settings

* correct linebreaks
* search exclusion
* automatic prettifier

* Working debugger config

* fix merge

* Removed comments, default resolution MKT

* removed vscode from gitignore

* refactoring description update

* Added auto resolution to LiteMarket

* fix date, setDate mutates object

* fixed firestore.rules

* script to add auto resolution to all markets

* regularely auto resolve markets

* fix description error

* moved calculate ts for access in firebase

* Revert "moved calculate ts for access in firebase"

This reverts commit 8380bf4f72.

* fix reference to calculate for firebase

* fixed references to time

* renamed function

* added description

* added auto resolution to description

* direct bool check instead of != null

* direct bool check instead of != undefined

* remove explicit type

* Fix free response markets

* removed contract from functionname

* interval set to 1h

* query instead of filter

* folds ~> contracts

* query instead of filter

* promise.all instead of foreach

* removed contractDoc from function header

* removed autoResolution from function header

* batchedWaitAll instead of promise.all

* removed unused parameter

* replaced auto resolution with constant

* suggestions from PR

* fix comment

* removed unused imports

* added scripts to add close dates on prod

* optimization

* removed test script

* security: only auto resolve markets which are closed

* consistency checks

* re-added type check for binary markets
* moved check of probability into switch case block

* removed unused import

* auto resolution every minute

* auto resolution time optional

* pr fixes
2022-06-14 15:01:32 -05:00
SirSaltyy
cb64703905
Fix bounties (#502)
fix formatting error
2022-06-14 20:22:20 +01:00
mantikoros
b08cdd495f Revert "Refinement of LiteMarket (#479)"
This reverts commit 9945738811.
2022-06-14 12:06:22 -05:00
SirSaltyy
be777ba122
about button directs to "how to" (#501)
* Create how to manifold.md

* Updates to docs

Updated bounties, added new "How to Manifold" page, edited "About MM" page.

* Update sidebar.tsx
2022-06-14 18:02:21 +01:00
TrueMilli
9945738811
Refinement of LiteMarket (#479)
* split into two function to access the pool value as number

* rename function

* changes to the exported object

* removal of totalLiquidity: this value was only set for binary markets and it's value is identical to getPoolvalue(contract)
* pool: set in the same way as in the "Market Overview" from contract-info-dialog.tsx now
* totalShares: total shares of the contract. It's value is equal to the old "pool" value in case of binary markets

* update docs

* removal of totalShares
2022-06-14 12:01:12 -05:00
SirSaltyy
da2c6ae037
Update docs.md (#499)
* Create how to manifold.md

* Updates to docs

Updated bounties, added new "How to Manifold" page, edited "About Manifold Markets" page.
2022-06-14 17:57:16 +01:00
mantikoros
0ecff1b581
Heap analytics (#500)
* add heap

* remove hotjar

* prettier formatting conflicting with es lint

* stop weird prettier/eslint conflict
2022-06-14 11:54:58 -05:00
James Grugett
92b368f11e Fix sorting for Your bets table 2022-06-14 11:27:11 -05:00
James Grugett
be094ef8e5 Tweaks to stats page 2022-06-14 10:27:52 -05:00
Ian Philips
e49f614acb
Remove unused acitivity items-related code (#492)
* Remove unused acitivity items

* Remove activity page
2022-06-14 07:13:24 -06:00
Austin Chen
716e00374c Tweak /charity page style 2022-06-13 21:27:20 -07:00
Austin Chen
af3895de79
Add quadratic matching to Manifold for Charity (#486)
* Calculate quadratic funding match

* Tweak copy

* More concise quadratic funding calculation

Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>

* Fix imports and calculations

* Remove unused var for now

* Clean up styling

Co-authored-by: Sinclair Chen <abc.sinclair@gmail.com>
2022-06-13 20:53:29 -07:00
mantikoros
c4e3376313 check if shares below min pool qty for sales 2022-06-13 21:14:52 -05:00
mantikoros
dd4444caf9 deprecation warning for numeric markets 2022-06-13 21:09:09 -05:00
mantikoros
d9f0428e41 alert box component 2022-06-13 21:08:56 -05:00
mantikoros
64542f775a non-cash-dropping logo gif 2022-06-13 20:32:47 -05:00
James Grugett
dcaddd6a1a Move contract hook back into ContractPageContent. (Fixes going back to home.) 2022-06-13 16:05:46 -05:00
James Grugett
05f1da430c Stats: Use action count of the user who is tenth percentile 2022-06-13 15:51:17 -05:00
mantikoros
e2d7e94e4c prettier 2022-06-13 11:52:29 -05:00
mantikoros
e6a1046039 landing page branding, UI tweaks 2022-06-13 11:22:50 -05:00
mantikoros
a34a32d790 delete old landing page 2022-06-13 11:20:34 -05:00
James Grugett
f3d4827115
Listen for contract updates when navigating from home to contract page. (#494) 2022-06-13 11:04:56 -05:00
Ian Philips
68da02ec00 Fix href is missing query values 2022-06-13 08:42:47 -06:00
Marshall Polaris
72f4a2f603 Fix prettier errors 2022-06-12 21:49:02 -07:00
Marshall Polaris
aaef9842d4 Fix lint error 2022-06-12 21:44:35 -07:00
Marshall Polaris
816fc5d64c Revert "Revert "Turn on no unused variables linting, kill dead code (#484)""
This reverts commit 5beda1ded7.
2022-06-12 21:42:41 -07:00
mantikoros
0d2204bb02 Put "Get M$" in sidebar 2022-06-12 23:34:11 -05:00
mantikoros
246aa5c214 "add funds" => "get M$" 2022-06-12 23:26:05 -05:00
Austin Chen
5beda1ded7 Revert "Turn on no unused variables linting, kill dead code (#484)"
This reverts commit 515928a69a.
2022-06-12 20:55:48 -07:00
Daniel Reeves
4ad04869a1
Typo fix (feedback #697) (#490) 2022-06-12 19:40:02 -07:00
Marshall Polaris
515928a69a
Turn on no unused variables linting, kill dead code (#484)
* Slightly fix up ChoicesToggleGroup

* Kill a bunch of dead code and unused variables

* Turn on no-unused-vars lint

* Un-kill some dead code that James likes
2022-06-12 19:04:55 -07:00
mantikoros
3d39b705ae stats: show market type, payout mechanism 2022-06-12 18:37:01 -05:00
mantikoros
e34d34080d Show liquidity pool; change tab to "Subsidize" 2022-06-12 18:34:18 -05:00
Marshall Polaris
8fce8d5f23
Improve visibility of updateFooMetrics functions behavior (#485)
* Make updateFooMetrics functions manually testable

* Add logging, test script to metrics update functions

* Improve on `batchedWaitAll` for update functions
2022-06-12 16:22:29 -07:00
Marshall Polaris
6ac129a0b8
Validate that user can pay for market (#487) 2022-06-12 14:23:23 -07:00
Austin Chen
a7dca6a163 Add ACLU charity 2022-06-11 18:06:44 -07:00
Austin Chen
49bd954945 Rename to "Manifold for Charity" 2022-06-11 09:15:59 -07:00
Marshall Polaris
1ab17bbbf0 Bump metrics updater functions to 1GB memory 2022-06-10 22:25:54 -07:00
James Grugett
789c9aa32a Stats: Add chart of the amount of mana bet 2022-06-10 23:44:19 -05:00
James Grugett
457db07da4 Show all answers in answer panel 2022-06-10 23:28:09 -05:00
James Grugett
f4a66263dd Stats: Compute charts for total actions of top tenth of users 2022-06-10 23:22:48 -05:00
Marshall Polaris
00d7e622df
Query bets all at once in updateUserMetrics (#477) 2022-06-10 21:00:19 -07:00
Marshall Polaris
d8dc91d4b7
Optimize updateContractMetrics (#476)
* Don't query bets repeatedly

* Don't read entire database of contracts for no reason

* Fix lint
2022-06-10 21:00:09 -07:00
Marshall Polaris
3a6960c71b
Simply do not update the feed (#474) 2022-06-10 20:55:51 -07:00
Marshall Polaris
6956f0d730
Implement really obvious optimizations on placebet, sellbet, sellshares (#452)
* Change authed endpoints to not look up users unnecessarily

* Parallelize some extremely parallelizable DB requests

* Clean up overcomplicated sellshares logic
2022-06-10 17:51:55 -07:00
Ian Philips
ee816d6552
Must listen to contract updates for probs to update (#482)
* Must listen to contract updates

* remove unused import

* Rename liveContract
2022-06-10 17:36:18 -06:00
Ian Philips
789bbced5f Ignore manifold as lp for now 2022-06-10 17:23:17 -06:00
Ian Philips
89784bf5eb
Notifications for liquidity proiders/provisions (#478)
* Notifications for liquidity proiders/provisions

* typo

* Rename

* Return default text

* Marke needs resolution notifications

* remove todo
2022-06-10 16:48:28 -06:00
Ian Philips
8bdc33f683 Revert using bet probs, must make totalShares work 2022-06-10 15:58:44 -06:00
Marshall Polaris
9cccc08021
Fix busted comment permalink copying code (#481)
* Fix busted comment permalink copying code

* Fix busted comment permalink href
2022-06-10 14:31:53 -07:00
Ian Philips
f1c3914807
Properly fill probs on DPMM bets (#480)
* Properly fill probs on DPMM bets

* Remove unused import
2022-06-10 15:15:52 -06:00
James Grugett
15882904eb Disallow following yourself via market page 2022-06-10 12:55:55 -05:00
James Grugett
ad1a40ba3e Switch select all to outline button 2022-06-10 12:45:51 -05:00
James Grugett
fba3905724 Creator leaderboard 'Market volume' => 'Total bet' 2022-06-10 12:35:18 -05:00
James Grugett
f2c6c8c483 Change Similar tab to exclude people you follow 2022-06-10 12:28:02 -05:00
James Grugett
a68e943d0b Refactor to load bets once on market page 2022-06-10 12:23:35 -05:00
mantikoros
e28dfaaa80 fix ios scrolling in bet panel 2022-06-10 11:36:07 -05:00
James Grugett
86581a421a Home: Add floating plus button that takes you to /create 2022-06-10 11:22:36 -05:00
James Grugett
25c1e9c3a3 Tweak layout of user page buttons 2022-06-10 11:11:31 -05:00
mantikoros
077bd946ff fix ContractLeaderboard 2022-06-10 11:01:32 -05:00
Ian Philips
ead13b4e08 Time flies when you floor it 2022-06-10 09:15:55 -06:00
Austin Chen
05c6575ebb Extend wasabicharts iframe height 2022-06-09 18:35:38 -07:00
mantikoros
c59d93979c
Fix overflow (#472)
* fix pool overflow bug by enforcing minimum share quantity in pool

* CPMM_MIN_POOL_QTY = 0.01

* use newPool
2022-06-09 20:25:05 -05:00
Marshall Polaris
6fac56d2c9
Make feed updating do much less work (#455) 2022-06-09 15:13:06 -07:00
Ian Philips
30eac1bd96 Initial probs = 50 2022-06-09 14:26:46 -06:00
James Grugett
2564eb9c26 Linkify bettor username in Bets tab 2022-06-09 15:00:31 -05:00
mantikoros
3858f8fbea withdraw liquidity: redeem surplus shares, adjust total depostits, adjust amount for current prob, set isLiquidityProvision for surplus bets, error handling 2022-06-09 12:47:02 -05:00
Ian Philips
699bddcb2a
Notif ux improvements (#471)
* Save resolved values in notifs

* Clean up

* Various ux improvements
2022-06-09 11:39:23 -06:00
Ian Philips
8634af702a
Save resolved values in notifs (#470)
* Save resolved values in notifs

* Clean up
2022-06-09 10:30:26 -06:00
Ian Philips
64f04185d4 Fix comment inputs on mobile 2022-06-09 10:15:34 -06:00
Ian Philips
bdb3bbd960 Don't open comment w/o new bet 2022-06-09 08:53:01 -06:00
Ian Philips
65bc0f0f86 Disable always autofocus 2022-06-09 07:36:36 -06:00
Ian Philips
65a879d5ab Save resolution with notif 2022-06-09 07:31:53 -06:00
Ian Philips
3d18b12ede bet.probAfter => getDpmOutcomeProb 2022-06-09 07:22:03 -06:00
James Grugett
f52b756163 Use lighter gray in small follow button to deemphasize 2022-06-08 23:06:25 -05:00
James Grugett
a2eece5f5c Reveal all bets in Bets tab from June 1st onward 2022-06-08 23:03:07 -05:00
Marshall Polaris
5a2ff18859
Add paging to /markets API endpoint (#468)
* Add really simple paging to markets endpoint

* Document changes to markets endpoint

* n -> limit
2022-06-08 18:08:06 -07:00
Ian Philips
01adf50ae1
Update FR comment prob and input after bet (#467)
* Update FR comment prob and input after bet

* Remove comment

* eslint

* Use proper deps and useEvent hook
2022-06-08 17:09:49 -06:00
mantikoros
52dd68303a dpm: don't use weightedShareTotal 2022-06-08 16:18:00 -05:00
TrueMilli
b231d0968f
dpm fix multi resolution (#461)
* fix dpm prob

* refactoring

* remove newline

* refactoring
2022-06-08 16:06:14 -05:00
James Grugett
88bf22622d Fix follow dialog staying open on navigate 2022-06-08 15:51:53 -05:00
SirSaltyy
ad820913f7
Update welcome.html (#465) 2022-06-08 21:28:29 +01:00
James Grugett
c9a4fa8679 Add small follow button after creator name in market page 2022-06-08 15:27:53 -05:00
mantikoros
902bdc96b2 thank you email template 2022-06-08 14:56:55 -05:00
James Grugett
4e83aa1431 Put back edit following button 2022-06-08 14:42:23 -05:00
James Grugett
f809acd6fd 'Discover' => 'Similar' and shows recs based on user you have open 2022-06-08 14:38:04 -05:00
James Grugett
d7e52c1969 Extract TextButton component with blue underline. 2022-06-08 14:23:10 -05:00
James Grugett
21d6815de1 Don't show follow button for yourself 2022-06-08 14:22:10 -05:00
mantikoros
0cd9943e0d
Liquidity withdrawal (#457)
* withdrawLiquidity cloud function

* update rules

* exclude antes from getCpmmLiquidityPoolWeights

* update correct lp shares

* liquidity panel

* don't create bet if less than 1 surplus share

* withdrawLiquidity return type

* static analysis fix

* hook dependency

* prettier

* renaming

* typo

* getCpmmLiquidityPoolWeights: always exclude antes

* delete unused function

* casting
2022-06-08 13:00:49 -05:00
Ian Philips
45eb5a3e63
Always show notif settings details (#460) 2022-06-08 11:24:07 -06:00
Austin Chen
470e36a5a3 Add New Science 2022-06-08 10:08:02 -07:00
James Grugett
a31766b020 Run prettier... 2022-06-08 11:38:09 -05:00
James Grugett
bc752b853a Prevent comment text overlap with submit button 2022-06-08 11:34:37 -05:00
James Grugett
323a62f041 Put back numbers in home tab titles for categories / following 2022-06-08 11:24:35 -05:00
Ian Philips
61d7f0eca0 No more flying graph points 2022-06-08 09:12:33 -06:00
Ian Philips
936cabe353
Speed up notification loading by prepopulating relevant info (#453)
* Populate notification with relevant info

* eslint

* Remove duplicated code

* Unused ?

* Add new q notification, other small fixes
2022-06-08 08:43:24 -06:00
Ian Philips
7e37fc776c
Fr comment ux improvements (#451)
* Extend comment input box, only use airplane

* Only 1 commentable bet, shrink input, fix feed lines

* Pad sign in to comment button

* Small changes
2022-06-08 07:24:12 -06:00
James Grugett
ad6594f0bc Add discover tab of users based on markets you have bet on 2022-06-07 23:42:42 -05:00
James Grugett
ac763de16b Run prettier! 2022-06-07 23:16:05 -05:00
James Grugett
00cbec2309 Show edit following button on home 2022-06-07 23:07:05 -05:00
James Grugett
66cf69e425 Fix react query console error 2022-06-07 23:06:14 -05:00
James Grugett
879ab272e0
Following and follower list (#456)
* Create following button that opens follow list in modal.

* Move react query deps to web package.json

* UseFollowers hook

* Following and followers button, dialog with tabs.

* Fix line endings

* Remove carriage return from default vscode eol

* Add placeholder message if no users followed / no followers

* Tweak spacing
2022-06-07 22:24:18 -05:00
Marshall Polaris
18044e7302 api/v0/bets -> api/v0/bet 2022-06-07 15:44:07 -07:00
Marshall Polaris
43be03617a
Documentation for new write API endpoints (#430) 2022-06-07 15:39:05 -07:00
James Grugett
82b189aa48 Don't show numeric bet panel if market closed 2022-06-07 17:03:22 -05:00
Marshall Polaris
244bbc51b2
Migrate sellBet cloud function to v2 sellbet (#438)
* Migrate sellBet to v2

* Kill sellBet warmup requests

* Point client at new v2 sellbet function

* Clean up `getSellBetInfo`

* Fix up functions index.ts
2022-06-07 14:08:56 -07:00
Marshall Polaris
60e830974e
Migrate sellShares cloud function to v2 sellshares (#440)
* Migrate `sellShares` to v2 `sellshares`

* Point client at new v2 sellshares function

* Clean up `getCpmmSellBetInfo`
2022-06-07 13:54:58 -07:00
Marshall Polaris
0f0390cb6a
Fix secret access for some email sending functions (#449) 2022-06-07 13:31:08 -07:00
Austin Chen
2ac7caaf24
Rename /analytics to /stats to work around adblockers (#437)
* Rename /analytics to /stats to work around adblockers

* Fix prettier lint
2022-06-07 08:37:23 -07:00
Ian Philips
4aec46f880 Follow notif groups => Other activity 2022-06-06 16:36:55 -06:00
Ian Philips
85ad343d5d Follow notifications => user profile 2022-06-06 16:30:31 -06:00
Ian Philips
a7a482eecd
Various notifications bugfixes/improvements (#442)
* Various notifications bugfixes/improvements

* eslint
2022-06-06 16:15:36 -06:00
James Grugett
849e7d03a8 Switch to tabs for categories vs following 2022-06-06 14:53:05 -05:00
Marshall Polaris
d9eb9798e5
Point client at new v2 versions of createmarket and placebet (#433)
* Kill 'warmup spam' for createContract and placeBet

* Point v2 function calls at v2 endpoints

* Add real prod placebet and createmarket endpoints
2022-06-06 12:46:06 -07:00
Marshall Polaris
13826b5759
Migrate placeBet and createContract to v2 functions (#432) 2022-06-06 12:34:58 -07:00
Ian Philips
44b3579cc7 Add tip to see more 2022-06-06 12:01:12 -06:00
James Grugett
caa43856af Run prettier :P 2022-06-06 12:55:13 -05:00
James Grugett
587357c13f Description for categories / users you are following with edit button. 2022-06-06 12:51:45 -05:00
Ian Philips
6aa639591b Minor settings line wording tweaks 2022-06-06 11:46:26 -06:00
Ian Philips
1976bc755e Revert "Revert "Notification detail, grouping, and settings control [wip] (#403)""
This reverts commit 07f2d390e5.
2022-06-06 11:36:59 -06:00
Ian Philips
07f2d390e5 Revert "Notification detail, grouping, and settings control [wip] (#403)"
This reverts commit 37c7f909a3.
2022-06-06 10:54:25 -06:00
Ian Philips
37c7f909a3
Notification detail, grouping, and settings control [wip] (#403)
* Revert "Revert "Notifications ux fixes - wip (#383)""

This reverts commit 699b03eb42.

* Group & provide more control over notification display

* UI/UX improvements

* Remove unused text key

* Refactor

* Refactor

* Show answer resolution in notification

* Disable eslint on single linefor exhaustive deps

* Handle arbritrary notifications

* Refactor

* Remove unused vars

* Add follow user

* Various UX improvements, add follow notif

* Various small ui changes

* Show notification settings breakdown

* Improve notification status lines
2022-06-06 10:52:11 -06:00
James Grugett
773465c6c5 Move select all button to top of category selector modal 2022-06-06 10:44:37 -05:00
mantikoros
43b0fe6749
Contract card ui tweaks: consistent market type colors, no underline,… (#402)
* contract card ui tweaks: consistent market type colors, no underline, adjust font/border size

* bigger probabiity numbers in contract card

* revert non-color changes; change prob bar width to 1.5
2022-06-06 09:54:43 -05:00
Marshall Polaris
9e66daa861
Ratchet up linting for functions package a little bit (#431) 2022-06-05 22:50:27 -07:00
Marshall Polaris
e712a054ae Fix line endings 2022-06-05 18:57:49 -07:00
Jack
004dd7168e
Fix free response answers with 0 bets disappearing (#427)
Before this PR, free response answers and their associated comments disappeared when all shares in the answer were sold. If it's just an answer, this is a surprising UX but not that bad. But this can also disappear an entire comment thread with great discussion, as I noticed on one of my markets recently.

I suppose the downside is that these answers take up space and are more likely to be undesired - but I think answers with M$1 of bets are much more common anyway.
2022-06-05 17:20:06 -07:00
James Grugett
a39b1e502c Fix navigate to home not working 2022-06-05 15:45:33 -05:00
James Grugett
96db414ca1
Category checklist (#426)
* Use ChoicesToggleGroup for categories vs following

* Edit categories modal

* Filter closed and resolved using Configure. Set page to 0.

* Add useEvent hook, incase we want to use it before React releases it.

* useMemo on filters computation

* Try to fix prettier

* Use check box! Add select all/none button
2022-06-05 14:06:08 -05:00
James Grugett
3d31641050 Fix clicking card from user profile has no effect 2022-06-04 23:21:24 -05:00
James Grugett
a42a0f086c Remove Row and run prettier 2022-06-04 21:53:55 -05:00
James Grugett
cfd6af7c72 Tweak create page layout 2022-06-04 21:28:27 -05:00
James Grugett
11974f68fc Run prettier 2022-06-04 19:14:14 -05:00
James Grugett
93bb1dd519 Support ctrl or command click to open link in new tab 2022-06-04 19:13:07 -05:00
James Grugett
5152be57ba Quick back from clicking contract card on home! Preserves search state. 2022-06-04 19:00:13 -05:00
James Grugett
cd12628565 Tag page: load sort option from storage 2022-06-04 18:17:32 -05:00
Marshall Polaris
069b88d6fd
Fix API calls to hit emulated Firebase (#424) 2022-06-04 15:19:46 -07:00
Marshall Polaris
33ddd86add
Switch to Google Secret Manager for function secrets (#418)
* Upgrade firebase-functions 3.16.0 -> 3.21.2

* Use Secret Manager instead of config

* Small refactoring on new stripe/mailgun initialization

* Teach README about new secrets workflow
2022-06-04 14:39:25 -07:00
Justin
e3eb43a14b
add volume to api response (#413) 2022-06-04 13:33:07 -07:00
Marshall Polaris
f8c44efeba
Small cleanup to some common code (#422)
* Remove unnecessary type helpers

* Turn on unused code lint
2022-06-04 13:30:54 -07:00
Forrest Wolf
b89753e1fe
Add yarn verify script (#378)
* Add yarn check script

* Rename

* Add verify script to subdirectories for convenience
2022-06-03 15:10:14 -07:00
Marshall Polaris
7572e07489
Add a /health function for ease of benchmarking (#412)
* Slightly more defensive API endpoints

* Add health endpoint for benchmarking trivial Firebase fns
2022-06-03 14:49:09 -07:00
TrueMilli
00f142ec7e
vscode settings (#409)
* vscode settings
* Working debugger config
2022-06-03 12:17:31 -07:00
Marshall Polaris
49859e6f00
Call cloud functions directly from web client instead of proxy (#405) 2022-06-03 00:50:24 -07:00
James Grugett
c1bda8a775
Follow other users. Filter markets by followed (#387)
* Add follow button to user page

* Update follows in the database using follow button.

* Add toggle for followed market creators to home

* Hide follow toggle from user's markets page

* Check that sold bet is by auth'd user

* Change follow toggle to category pill

* Remove unused imports

* Remove console.logs
2022-06-02 23:52:14 -05:00
Sinclair Chen
749f7aad40 copyedit: username in bet list empty state 2022-06-02 18:07:47 -07:00
Forrest Wolf
397d90c6b7
Replace some more uses of any with more specific types (#389)
* Give confirmation button icon a type

* Give setBids a type

* Make StripeSession type more specific

* Give MenuButton buttonContent a type
2022-06-02 17:40:41 -07:00
TrueMilli
0f2a311b74
Refactoring (#401)
* refactoring

(cherry picked from commit 4de86d5b08)

* removed unused imports and variables

* added type for binary resolution

* Prettier

* const for binary resolutions

* using the type "resolution"

* Prettier

* Update functions/src/create-contract.ts

* launch config for debugging with vs code
* "Launch Chrome" does not work since login via google is not possible in debugger-chrome
* Breakpoints are unbound when attached to chrome
2022-06-02 17:30:34 -07:00
James Grugett
bbb9a2c1fa Quick bet: Opposite arrow sells position 2022-06-02 18:24:10 -05:00
mantikoros
06c5c97a03 down betting arrow disabled for free response markets 2022-06-02 16:25:41 -05:00
mantikoros
5a4bb65d80 remove extra space (to fix prettier static check) 2022-06-02 16:00:38 -05:00
mantikoros
5af2e1fef0 up arrows green, down arrows red 2022-06-02 15:54:03 -05:00
Austin Chen
5b142baa98 Revert "Demote charity to more menu"
This reverts commit 311b39ffe9.
2022-06-02 13:35:31 -07:00
mantikoros
4fbc7703ce create page: remove numeric markets 2022-06-02 14:27:01 -05:00
Sinclair Chen
e1b1f30c55 make sell button show on mobile (bet list) 2022-06-02 10:56:56 -07:00
Ian Philips
9b4974a3da Auth for description/close time, unauth for tags 2022-06-02 11:23:25 -06:00
Ian Philips
31a0a378ef Sync firestore rules from firebase console 2022-06-02 11:14:01 -06:00
James Grugett
c40f2904f0 Revert "check id on update (#393)"
This reverts commit c2f993ddf2.
2022-06-02 10:42:05 -05:00
James Grugett
311b39ffe9 Demote charity to more menu 2022-06-01 23:31:38 -05:00
James Grugett
f14bb63393 Check that sold bet is by auth'd user 2022-06-01 18:30:42 -05:00
TrueMilli
c2f993ddf2
check id on update (#393) 2022-06-01 16:30:36 -07:00
Ian Philips
78a359407d
Notifications reverted ux (#386)
* Revert "Revert "Notifications ux fixes - wip (#383)""

This reverts commit 699b03eb42.

* <p>=><div>
2022-06-01 12:26:41 -06:00
Ian Philips
699b03eb42 Revert "Notifications ux fixes - wip (#383)"
This reverts commit b5057f4028.
2022-06-01 11:52:27 -06:00
Ian Philips
b5057f4028
Notifications ux fixes - wip (#383)
* Notifications generating on comment,answer,contract update

* Notifications MVP

* Submitted an answer => answered

* Listen for unseen notifications

* Fix userlink formatting, move page

* Fix links

* Remove redundant code

* Cleanup

* Cleanup

* Refactor name

* Comments

* Cleanup & update notif only after data retrieval

* Find initial new notifs on user change

* Enforce auth rules in db

* eslint update

* Code review changes

* Refactor reason

* Add todos

* Show question in notifiation title

* Allow larger width on md

* Condense on mobile

* Decrease padding, hide title on mobile

* Line clamp notifications

* Shrink text
2022-06-01 11:31:46 -06:00
Jack
536ef180ce
Add charities: EA Funds and Founders Pledge funds (#382)
Co-authored-by: jackc271 <jackc2718@gmail.com>
2022-06-01 10:25:37 -07:00
TrueMilli
29a09ad276
Made an actual comment (#384) 2022-06-01 10:20:45 -07:00
James Grugett
2c4aa6152e Hide sell buttons in other people's profiles 2022-06-01 11:28:47 -05:00
Ian Philips
59830579a9
Set timeout to reset the free market banner (#367)
* wip - trigger recheck when past time

* wip - trigger recheck when past time

* Reset logic

* Interval=>timeout
2022-06-01 07:54:48 -06:00
Ian Philips
1c980ba678
Notifications (#354)
* Notifications generating on comment,answer,contract update

* Notifications MVP

* Submitted an answer => answered

* Listen for unseen notifications

* Fix userlink formatting, move page

* Fix links

* Remove redundant code

* Cleanup

* Cleanup

* Refactor name

* Comments

* Cleanup & update notif only after data retrieval

* Find initial new notifs on user change

* Enforce auth rules in db

* eslint update

* Code review changes

* Refactor reason
2022-06-01 07:11:25 -06:00
Marshall Polaris
35c373f6ce
MULTI contracts are not a thing (#381) 2022-05-31 20:40:08 -07:00
James Grugett
dec286583a Show bets loading indictor instead of wrong data 2022-05-31 22:21:45 -05:00
Marshall Polaris
9f1fe3ee17
Fix busted avatar links on charity pages (#380) 2022-05-31 20:02:50 -07:00
Marshall Polaris
7c4ec2a8e3
Refactor contract typing to be more concise and more correct (#355)
* Refactor contract types slightly

* Refactor contract types greatly

* Kill dead binary DPM contract creation code

* Use BinaryContract, DPMContract, etc. type aliases
2022-05-31 19:42:35 -07:00
Forrest Wolf
30adb5e1f8
Fix dependency of useEffect in BetsList (#379)
* Fix dependency of useEffect in BetsList

* Revert "Fix dependency of useEffect in BetsList"

This reverts commit 077b211f22.

* Disable linter for BetsList useEffect deps

* Add hideBetsBefore to useEffect dependencies

* Fix formatting
2022-05-31 21:36:00 -05:00
James Grugett
9cb24c9f3c Make categories work when signed out 2022-05-31 21:23:36 -05:00
James Grugett
b236ebdbd9 Fix janky horizontal scrollbar on home on mobile 2022-05-31 20:07:28 -05:00
Austin Chen
a840143990 Publicly display bets without infinite looping
React why
2022-05-31 18:06:29 -07:00
Sinclair Chen
c8bf71d40d
Allow selling CPMM binary position from bet table (#372)
* Allow selling CPMM binary position from bet table

* Only click to collapse on bet header, not body
2022-05-31 17:36:58 -07:00
Sinclair Chen
3d9d60e8fe refactor Row, Col to accept all div props 2022-05-31 17:13:54 -07:00
Marshall Polaris
d8af8accbb Mention /docs package in README 2022-05-31 15:16:18 -07:00
Marshall Polaris
cee8e8b892
Fix up formatting in the docs package to use Prettier correctly (#370)
* Add .prettierignore to docs package

* Fix up prettier errors
2022-05-31 14:50:21 -07:00
Marshall Polaris
c640b4c830
Bring docs repo into main repo as new workspace (#369)
* Bring docs repo into main repo as new workspace

* Pin @types/react to 17.x
2022-05-31 14:30:58 -07:00
Sinclair Chen
6106eeee95 copyedit: donations available after May 2022-05-31 13:23:01 -07:00
James Grugett
5bcff4171b Revert "Publicly display bets placed after 06-01-2022"
This reverts commit a4330cfdf2.
2022-05-31 14:15:26 -05:00
Austin Chen
a4330cfdf2 Publicly display bets placed after 06-01-2022 2022-05-31 10:49:07 -07:00
Ian Philips
2fb1c4bb11 Free markets: Generate new date for setUTCHours 2022-05-31 09:51:44 -06:00
Austin Chen
3bc4e7b336 Embed @wasabipesto's charts on /analytics 2022-05-31 08:32:11 -07:00
Austin Chen
5978f4b1cb Show blog link in sidebar 2022-05-29 17:33:41 -07:00
James Grugett
23944bf47a Filter redemption bets out of market 'Bets' tab 2022-05-28 15:57:12 -05:00
James Grugett
90a8de09d7 Ask a question => Create a question 2022-05-28 15:48:08 -05:00
James Grugett
4c81106612 Don't show quick bet when signed out, or for numeric markets 2022-05-28 15:36:16 -05:00
James Grugett
29a05ffff2 Disable quick bet for resolved markets 2022-05-28 15:27:01 -05:00
Marshall Polaris
c3a5205d8e
Use cross-env for things that set environment variables (#359) 2022-05-28 13:21:05 -07:00
mantikoros
9076b625e3 numeric bet ante provided by house 2022-05-28 13:43:57 -05:00
James Grugett
45f92379bc Portfolio: Fix calculation of whether you sold all your shares 2022-05-28 00:18:09 -05:00
Austin Chen
2ea7ddc1aa Hide mobile bottom bar if Manifold is iframed 2022-05-27 21:27:37 -07:00
Marshall Polaris
80d5607984 Revert fishy change regarding payouts 2022-05-27 14:24:49 -07:00
Marshall Polaris
86625798cd
Clean up some mess related to nullable collectedFees (#352)
* contract.collectedFees is no longer sometimes nonexistent

* Fix typing issues around payouts code
2022-05-27 14:02:02 -07:00
James Grugett
279b139556 Add 'sold' filter option in portfolio page 2022-05-27 15:51:55 -05:00
James Grugett
15d203977a Portfolio: Consistently filter out contracts you have sold out of. 2022-05-27 15:40:52 -05:00
Marshall Polaris
e5ce17c2ad Fix up backfill script (and I ran it) 2022-05-27 13:11:32 -07:00
Marshall Polaris
3e25faf74c Add script to backfill contract collectedFees 2022-05-26 22:07:56 -07:00
Forrest Wolf
1e0845f4b9
Replace some uses of any with more specific types (#344)
* Add tsconfig.json for common

* Prefer `const` over `let` over `var`

* Kill dead code

* Fix some trivial Typescript issues

* Turn on Typescript linting in common except for no-explicit-any

* Correctly specify tsconfig dir name in functions eslintrc

* Give react children explicit types

* Add explicit types to removeUndefinedProps

* Create StripeSession type

* Give event in Dropdown an explicit type

* Revert "Give event in Dropdown an explicit type"

This reverts commit 80604310f2.

* Give bids in NewBidTable an explicit type

* Cast results of removeUndefinedProps when neccessary

* Fix type of JoinSpans

* Revert "Cast results of removeUndefinedProps when neccessary"

This reverts commit 5541617bc8.

* Revert "Add explicit types to removeUndefinedProps"

This reverts commit ccf8ffb0b5.

* Give React children types everywhere

* Give event a type

* Give event correct type

* Lint

* Standardize React import

Co-authored-by: Marshall Polaris <marshall@pol.rs>
2022-05-26 15:22:44 -07:00
Marshall Polaris
420ea9e90e
Add more linting to web package (#343)
* Import React a lot

* Fix misc. linting concerns

* Turn on many recommended lints for `web` package
2022-05-26 14:41:24 -07:00
Marshall Polaris
5217270073
Serious business API validation & big cleanup of createContract, placeBet (#302)
* Add the great Zod as a dependency to help us

* Tweak eslint

* Rewrite a ton of stuff in createContract and placeBet

* Clean up error reporting in API

* Make sure the UI is enforcing validated limits on lengths

* Remove unnecessary Math.abs

* Better type on `BetInfo`

* Kill `manaLimitPerUser`

* Clean up hacky parameters on bet info functions

* Validate `closeTime` as a valid timestamp in the future
2022-05-26 14:37:51 -07:00
Ian Philips
09e93779fb Use today's 4pm utc if past already 2022-05-26 10:29:46 -06:00
mantikoros
7b0be014eb show resolved n/a for numeric markets 2022-05-26 09:39:14 -05:00
Ian Philips
6f23c16df7 Show up arrow if shares in numeric markets 2022-05-26 06:23:22 -06:00
Sinclair Chen
38fa58406d Fix embeds on incognito (blocked localStorage) 2022-05-25 22:30:03 -07:00
Austin Chen
eb68d059e2 Extend analytics to 90 days 2022-05-25 19:22:50 -07:00
Marshall Polaris
b8b1c0d056
Add tsconfig, more linting to common package (#324)
* Add tsconfig.json for common

* Prefer `const` over `let` over `var`

* Kill dead code

* Fix some trivial Typescript issues

* Turn on Typescript linting in common except for no-explicit-any

* Correctly specify tsconfig dir name in functions eslintrc
2022-05-25 17:12:36 -07:00
Ian Philips
0c3fa9f065
Handle free response outcomes for yes/no shares (#338) 2022-05-25 16:51:33 -06:00
Ian Philips
1334840ee0
Fix comment links from /activity & add to emails (#320) 2022-05-25 16:47:08 -06:00
Ian Philips
f0d44be2f4 Clean up time diff code 2022-05-25 16:04:14 -06:00
Ian Philips
f3be3b1f3c Maybe free market countdown will work now, beats me 2022-05-25 15:55:50 -06:00
Ian Philips
d117ff600a Adjust spacing on % input 2022-05-25 12:47:44 -06:00
Ian Philips
959ee5f8d1
Improve create market UI (#336)
* Move to tailwindui

* Remove commented code

* Prettier

* Show custom prob toggle, limit to 5-95%

* match left margin

* Show prob, date, time, other ui changes

* fix for firefox
2022-05-25 12:13:51 -06:00
Ian Philips
d69f4f9a0a
Move to tailwindui for create market page (#332)
* Move to tailwindui

* Remove commented code

* Prettier

* Show custom prob toggle, limit to 5-95%

* match left margin

* Show prob, date, time, other ui changes
2022-05-25 11:54:28 -06:00
James Grugett
92df092ad3 Fix DOM error in console 2022-05-25 11:48:37 -05:00
Forrest Wolf
c5763e6ec3
Extract signup prompt (#333)
* Extract SignUpPrompt component

* Return null instead of false when not showing SignUpPrompt

* Add trailing newline

* Lint
2022-05-25 09:25:39 -07:00
Ian Philips
fb237d502d Adjust emulator install instructions 2022-05-25 10:11:45 -06:00
Ian Philips
1942e1c47a
Show just first 3 letters and chosen answer on fr cards (#318)
* Show just first 3 letters and chosen answer

* 3 dots

* Just show resolved and the chosen answer

* Remove unused truncate & hide resolved except on xs
2022-05-25 07:13:33 -06:00
Austin Chen
eef5dda0f3 Shrink card margin between question & % 2022-05-24 17:25:23 -07:00
Austin Chen
d5a362502a Display liquidity "pool" instead of "bet" volume 2022-05-24 16:57:34 -07:00
Austin Chen
8715ff2740 Semibold the question on cards 2022-05-24 16:39:50 -07:00
Austin Chen
f9336c00be
Preview the quick bet result on hover (#319)
* Switch from triangle to a circle arrow

WIP

* Revert "Switch from triangle to a circle arrow"

This reverts commit 370f8eefe4.

* Show amount moved in probability

* Animate the prob bar change too

* Pull out quick bet display component

* Minor cleanups

* Clean up comments

* Close empty divs

* Feedback from Ian

* Pull out constant

* Get rid of quick bet separators

* Fix typescript change

* Invert colors so gray indicates placed bets

* Update comment on useSaveShares re: Ian's comments
2022-05-24 16:38:43 -07:00
Marshall Polaris
2d8ad40e70
Add outcome type to API market descriptors (#325) 2022-05-24 14:31:49 -07:00
mantikoros
5f385c2f9d Revert "use geometric mean probability to calculate fees for cfmm (a lot easier than solving the integral)"
This reverts commit a1c3a5e569.
2022-05-24 15:46:41 -05:00
mantikoros
c3be126337
Revert "Update leaderboards.tsx (#314)" (#317)
This reverts commit d19d60eb6a.
2022-05-24 09:31:42 -05:00
Ian Philips
811a7cece7 Market => question 2022-05-24 08:28:09 -06:00
Ian Philips
49bfb67daa Revert to showing FR answer 2022-05-24 08:24:01 -06:00
Ian Philips
13bf5ac253 Free daily market bugfix 2022-05-24 07:31:44 -06:00
SirSaltyy
d19d60eb6a
Update leaderboards.tsx (#314)
Renamed top creators header from "market volume" to "market liquidity".
2022-05-24 14:05:12 +01:00
Austin Chen
8cedf93901
Implement quick betting: directly from the market card (#291)
* Play with using 3 icons for 1-click usage

* Align bet icons with the percentages

* Hide liquidity injection star, for now

* Fix Free Response card layouts

* Use triangles instead of planes

* Set correct hover states the arrows

* Fix down triangle & padding

* Default large nums to 2 sigfigs

* Clean up hover areas

* Fix bet width, remove "chance/expected"

* Show "M$20" on hover, hide arrows when closed

* Improve click targets

* FR: "MULTI" => "MANY", single => "TOP"

* Install react-hot-toaster

* Implement quick betting on binary questions

* Handle different kinds of markets

* Extract out QuickBet into its own component

* Minor tweaks

* Visually separate out quick bet pane

* Hide quick bet for FR markets with no answers

* Fill in which bets the user has already placed

* Animate movements, fix binary direction

* Hover arrows are now always gray

* Pull out code into quick-bet.tsx

* Minor comments

* Fix import

ts-ignore is scary

* Fixes from James's feedback

* Hide text only on quickbet
2022-05-23 23:44:16 -07:00
Austin Chen
a8e47d4fc7 Update "No markets found" to point to /create
Fixes #308
2022-05-23 17:49:07 -07:00
Ian Philips
fc320ca1e4
Shrink avatar, paddding, gaps for mobile screens (#275)
* Shrink avatar, paddding, gaps for mobile screens

* Adjust padding, add arrow to input area

* Padding and reply ui adjustments
2022-05-23 16:09:40 -06:00
mantikoros
854daaebb7 nav bar: gray background for selected page (indigo color was too prominent) 2022-05-23 16:49:30 -05:00
mantikoros
244fd7c981 changed sidebar button color — violet doesn't match color scheme 2022-05-23 16:38:42 -05:00
Marshall Polaris
3987baa11b
Add Vercel routes for public write API (#280)
* Improve typing of client CORS helper

* Take node-fetch as a dependency

* Add explicit Firebase region into config

* Add new Vercel proxy API routes that talk to backend

* Call Vercel proxy routes from `api-call` module

* Tweak import to try to get Vercel happy

* Tidy up a tiny bit
2022-05-23 14:16:56 -07:00
Ian Philips
fb33f829cc Clean up free market countdown bugfix 2022-05-23 15:14:50 -06:00
James Grugett
2b13b1adb7 Update activity feed every hour to decrease cost 2022-05-23 15:29:18 -05:00
Ian Philips
0f01753795 Daily free market countdown fix 2022-05-23 11:26:17 -06:00
Ian Philips
d0347ff5c2
Add countdown timer for daily free market (#276)
* Add countdown timer for daily free market

* Reset example numbers

* Remove daily

* Free market reset => 4pm UTC
2022-05-23 08:43:11 -06:00
Ian Philips
355b2261a7 Show numeric bet panel between sm-xl 2022-05-23 08:04:55 -06:00
Ian Philips
e94d204a87
Simplify market creation page (#268)
* Simplify market creation page

* Minor improvements

* Prettier
2022-05-23 06:35:50 -06:00
James Grugett
fc6b36b690 Adjust numeric graph axis to use large number formatting 2022-05-22 22:05:52 -05:00
James Grugett
21527275d1 Fix numeric market embeds 2022-05-22 22:03:05 -05:00
James Grugett
1c73bba908 Show x value in numeric graph's tooltip 2022-05-22 17:48:02 -05:00
James Grugett
f85b10e517 Exclude partial day from analytics 2022-05-22 17:35:10 -05:00
Jonas Wagner
2a5b68977b
Require a minimum amount of 1 Mana per bet. (#273)
This is a hacky patch for a problem that runs more deeply: the use of
floating-point for Mana calculations, along with the associated rounding
errors.

Consider the following example:

```typescript
const balance = 1000
const bet = 5.6e-14
const newBalance = balance - bet
if (newBalance == balance) {
  alert(`I placed a bet of ${bet} without changing my balance.`)
}
```

This will actually print a message, because floating-point numbers can
only represent so many digits, and thus we get 1000.0 rather than
999.99999999999994.

This is a purely theoretical attack at this point; nobody could create
enough pico-bets to actually affect their balance using this technique.
However, I believe is worth ensuring a minimum bet amount, and might
prevent other problems such as the UI showing messages like "User Foo
bought M0 of YES", which could confuse users.

For a more definite solution, we would probably need to change some
computation to integers, where addition is always commutative and
value-preserving. This is similar to what most financial software does
(e.g., Bitcoin uses 1 Satoshi = 0.00000001 BTC as their unit).
2022-05-22 16:34:18 -05:00
Marshall Polaris
73d538c7cf
Modularize imports of hero icons in compiler (#292) 2022-05-22 12:40:34 -07:00
Marshall Polaris
d072216d2d
Kill /about page (#293) 2022-05-22 12:40:16 -07:00
Marshall Polaris
47f10301c8
Change lodash stuff so that it can be tree-shaken out of build (#233)
* Set common package.json sideEffects: false

* Configure SWC to modularize lodash imports

* Import specific lodash functions instead of _

* Add an eslint rule to avoid full lodash import
2022-05-22 01:36:05 -07:00
Marshall Polaris
0803a15902
Set up eslint for common, functions packages (#290)
* Move common dev dependencies to workspace top level

* Add .eslintrc.js for functions and common packages

* Add more linting to check workflow
2022-05-22 00:35:43 -07:00
Marshall Polaris
3061af3837
Kill Husky, lint-staged, and pretty-quick (#288) 2022-05-21 14:50:59 -07:00
Marshall Polaris
3db1de6b66
Add some useEffect dependencies (#289) 2022-05-21 14:48:15 -07:00
Marshall Polaris
cd602755be Try just killing setup-node action in check workflow 2022-05-21 14:43:43 -07:00
Marshall Polaris
f25b0d66f5 Disable Next.js telemetry in check workflow 2022-05-21 14:04:14 -07:00
Marshall Polaris
55fb59a54c
Rename static analysis job (#287)
* Rename static analysis job

* Try to colorize check workflow output
2022-05-21 14:01:29 -07:00
Marshall Polaris
7fdd8bd539 Try to make check job continue but report failure accurately 2022-05-21 13:38:21 -07:00
Marshall Polaris
1c041d74f0 Remove check workflow security events permission 2022-05-21 13:25:44 -07:00
Marshall Polaris
32284791e2 Cache subpackage node_modules in check workflow 2022-05-21 13:23:29 -07:00
Marshall Polaris
8321bc94c6 ESLint should barf on warnings in check workflow 2022-05-21 13:19:00 -07:00
Marshall Polaris
00e14ac8b2 Check workflow should continue on error 2022-05-21 13:17:28 -07:00
Marshall Polaris
9a25fd4b73 Adjust prettier check, directory switching in check workflow 2022-05-21 13:11:28 -07:00
Marshall Polaris
1be7685741 Try implementing node_modules cache in check workflow 2022-05-21 13:08:16 -07:00
Marshall Polaris
190f013a15 Yarn --prefer-offline in check workflow 2022-05-21 12:59:19 -07:00
Marshall Polaris
767ec81205 Add per-branch concurrency to check workflow 2022-05-21 12:55:20 -07:00
Marshall Polaris
1008c8a9fc Run prettier --check in check workflow 2022-05-21 12:53:24 -07:00
Marshall Polaris
d118551405 Switch check workflow to use single job 2022-05-21 12:51:31 -07:00
Marshall Polaris
c0cc9cdb7a Add runs-on field to GitHub static analysis workflow 2022-05-21 12:44:35 -07:00
Marshall Polaris
1ddfd41a18
Add GitHub 'check' workflow to do static analysis (#286) 2022-05-21 12:42:59 -07:00
James Grugett
c5660ab762 Prevent flash of "No markets found" on search load 2022-05-21 13:51:41 -04:00
James Grugett
688ebb797a Add "Last updated" sort option 2022-05-21 13:44:55 -04:00
Marshall Polaris
98c5329e03
Fix up API CORS header processing (#277)
* Fix ultra embarrassing bug not restricting CORS origins

* Put CORS origin regexps in common

* Static types so I don't muck it up again

* Fixup CORS regex to be more strict

* Fix sloppy imports to actually work
2022-05-20 19:34:26 -07:00
Marshall Polaris
540476f65a
Public API improvements (#266)
* Remove needless wrappers from API requests and responses

* Return real HTTP status codes instead of status field

* More robustly handle API errors

* Fix broken error handling in NumericResolutionPanel warmup
2022-05-20 14:58:14 -07:00
Austin Chen
03a13248a4 Limit large numbers to 3 sig figs 2022-05-20 15:25:32 -04:00
Austin Chen
52ad4c42b0 Persist homepage sort 2022-05-20 15:08:00 -04:00
Austin Chen
4c93da3cc0 Use a blue bar for Numeric types 2022-05-20 14:00:22 -04:00
Austin Chen
5d96fdafd6 Add padding to homepage 2022-05-20 13:53:12 -04:00
mantikoros
6f1a569797 back to M$ — the worst option except for all the others 2022-05-19 22:28:38 -04:00
James Grugett
1616ffc817 Make filter for isResolved work in all cases 2022-05-19 22:13:58 -04:00
mantikoros
e9fee04761 ante for free FR market now come from house 2022-05-19 22:06:50 -04:00
mantikoros
99c41b6112 bug fix: use new placeBet api in numeric bet panel 2022-05-19 20:39:23 -04:00
Sinclair Chen
4c7836abc7
Show embed view of normal market links in iframes (#254) 2022-05-19 17:34:08 -07:00
SirSaltyy
a0db1b806a
Add trevor project charity (#258) 2022-05-19 17:08:21 -07:00
Marshall Polaris
20f4b97d8b
Rewrite client for new public APIs to use fetch instead of callables (#241)
* Rename `lib/firebase/api-call` -> `lib/firebase/fn-call`

This relieves ambiguity now that we will be using our actual
public API in the client.

* Rewrite client API calls to createContract, placeBet

* Tiny fixup for client market creation code
2022-05-19 15:04:34 -07:00
James Grugett
d4a49789d1 Add daily signups 2022-05-19 17:13:30 -04:00
James Grugett
a275b3a9c2 Fix analytics 2022-05-19 16:53:49 -04:00
James Grugett
4396456ed1 Add tooltip to analytics charts 2022-05-19 16:14:27 -04:00
mantikoros
be3d4d7735 exclude antes from bets list 2022-05-19 15:26:44 -04:00
James Grugett
76f27d1a93
Numeric range markets!! (#146)
* Numeric contract type

* Create market numeric type

* Add numeric graph (coded without testing)

* Outline of numeric bet panel

* Update bet panel logic

* create numeric contracts

* remove batching for antes for numeric markets

* Remove focus

* numeric market range [1, 100]

* Zoom graph

* Hide bet panels

* getNumericBets

* Add numeric resolution panel

* Use getNumericBets in bet panel calc

* Switch bucket count to 100

* Parallelize ante creation

* placeBet for numeric markets

* halve std of numeric bets

* Update resolveMarket with numeric type

* Set min and max for contract

* lower std for numeric bets

* calculateNumericDpmShares: use sorted order

* Use min and max to map the input

* Fix probability calc

* normpdf variance mislabeled

* range input

* merge

* change numeric graph color

* fix getNewContract params

* bet panel labels

* validation

* number input

* fix bucketing

* bucket input, numeric resolution panel

* outcome label

* merge

* numeric bet panel on mobile

* Make text underneath filled green answer bar selectable

* Default to 'all' feed category when loading page.

* fix numeric resolution panel

* fix numeric bet panel calculations

* display numeric resolution

* don't render NumericBetPanel for non numeric markets

* numeric bets: store shares, bet amounts across buckets in each bet object

* restore your bets for numeric markets

* numeric pnl calculations

* remove hasUserHitManaLimit

* contrain contract type

* handle undefined allOutcomeShares

* numeric ante bet amount

* use correct amount for numeric dpm payouts

* change numeric graph/outcome color

* numeric constants

* hack to show correct numeric payout in calculateDpmPayoutAfterCorrectBet

* remove comment

* fix ante display in bet list

* halve bucket count

* cast to NumericContract

* fix merge imports

* OUTCOME_TYPES

* typo

* lower bucket count to 200

* store raw numeric value with bet

* store raw numeric resolution value

* number input max length

* create page: min, max to undefined if not numeric market

* numeric resolution formatting

* expected value for numeric markets

* expected value for numeric markets

* rearrange lines for readability

* move normalpdf to util/math

* show bets tab

* check if outcomeMode is undefined

* remove extraneous auto-merge cruft

* hide comment status for numeric markets

* import

Co-authored-by: mantikoros <sgrugett@gmail.com>
2022-05-19 12:42:03 -05:00
Ian Philips
7d8ccb78a4
Remove unused bets (redemptions, antes) and sort by desc in query (#264)
* Remove unused bets and sort in query

* remove console

* Explicitly ignore or include redemptions

* Pass options from parent function

* Fix let=>const
2022-05-19 10:03:37 -06:00
Ian Philips
8013862f15 Center question button on lg 2022-05-19 09:20:38 -06:00
Ian Philips
6935f8865d Move comment btn to its own row on xs 2022-05-18 21:34:59 -06:00
Ian Philips
e6dabf97d9
Show prob at time of comment on binary markets (#255)
* Show prob at time of comment on binary markets

* unused import
2022-05-18 19:38:02 -06:00
James Grugett
6a30a41b10 Make analytics show data up to current hour 2022-05-18 18:53:53 -04:00
James Grugett
e9ee7e2355 Truncate user link 2022-05-18 17:08:46 -04:00
James Grugett
1f3f800a57 Show close time in cards on close date sort 2022-05-18 17:08:46 -04:00
Ian Philips
6d3aaf71d4 Any question => anything 2022-05-18 13:47:53 -06:00
Sinclair Chen
e2b03f31e9 Style current page on mobile nav bar 2022-05-18 15:45:08 -04:00
Sinclair Chen
f7c1d98221 Add Sinclair to admins 2022-05-18 15:04:06 -04:00
Ian Philips
daf6cadea9 Remove border from sidebar buttons 2022-05-18 10:52:00 -06:00
Austin Chen
7b3c8ea046 Revert "back to M$ — playtime is over"
This reverts commit ed41d99484.
2022-05-18 12:09:24 -04:00
Ian Philips
ca9e93fe47 Show loading indicator on comment submit 2022-05-18 10:08:42 -06:00
Austin Chen
f17675bbcf Fix typo 2022-05-18 12:07:36 -04:00
Austin Chen
44c53ae96d Kill the ugly "Sign in" button 2022-05-18 12:06:41 -04:00
Austin Chen
89b05be5ec Tweak "Ask a question" button VisD 2022-05-18 11:57:22 -04:00
Austin Chen
39870dd933
Bets on profile, again (#251)
* Revert "Revert "Show every user's bets on their profile (#170)""

This reverts commit 142206b79a.

* Fix typo

* Delete portfolio page
2022-05-18 11:52:12 -04:00
Ian Philips
eb3ac802c0 Fix #208 - check for comments without ids 2022-05-18 09:35:32 -06:00
Ian Philips
974d5a8d89 hide free daily market when signed out 2022-05-18 09:25:56 -06:00
James Grugett
142206b79a Revert "Show every user's bets on their profile (#170)"
This reverts commit d50cc39c27.
2022-05-18 10:59:08 -04:00
James Grugett
d51a20e2e0 Add back useState to import 2022-05-18 10:57:45 -04:00
James Grugett
acd59767e4 Move search and sort/filter options in one line on mobile 2022-05-18 10:42:56 -04:00
Boa
7b3c21cf98
Comments ux improvements and bugfixes (#246)
* Show majority stake on comments

* Darken comment input text

* Fix old FR comments displayed in general section

* Refactor feed comments and bets into files

* Only allow user to comment on most recent bet

* Fix overlapping sign in to comment

* Only calculate current users bets once

* Minor tweaks & is betting @ prob
2022-05-18 08:42:13 -06:00
Austin Chen
d50cc39c27
Show every user's bets on their profile (#170)
* Show user bets on their profile

* Add an alert for current users

* Replace `/portfolio` with `/Austin?tab=Bets`

* Replace `/Austin?tab=Bets` with `/Austin/bets`

* Use replaceState for better browser history

* Remove two console.logs

* Note a bug

* Fix path

* Write in description of why we're doing this
2022-05-18 10:36:17 -04:00
mantikoros
42c981a54d remove border on ask question button 2022-05-18 10:21:03 -04:00
Boa
84c86552d5
Fix fr comment emails (#247)
* Get answer outcome from comment

* Avoid db call if possible

* Include general comments note in comment emails

* Send market-comment on general comment
2022-05-18 07:25:38 -06:00
James Grugett
5adf430fad Revert "Fix up money moniker to be more elegant"
This reverts commit 4bba070423.
2022-05-17 23:54:51 -04:00
Marshall Polaris
4bba070423 Fix up money moniker to be more elegant 2022-05-17 20:04:39 -07:00
James Grugett
76ffe8eea1 Infinite scroll: automatically load new page of results 2022-05-17 19:29:46 -04:00
James Grugett
be9df7bcd8 Fix links beginning with https 2022-05-17 17:11:24 -04:00
mantikoros
a1c3a5e569 use geometric mean probability to calculate fees for cfmm (a lot easier than solving the integral) 2022-05-17 17:05:00 -04:00
James Grugett
85a182b7f2 Decrease feed update interval 2022-05-17 16:53:38 -04:00
mantikoros
ed41d99484 back to M$ — playtime is over 2022-05-17 15:34:58 -04:00
James Grugett
7da46050e5
Swap home and explore (#244)
* Add activity page. Copy explore page into home

* Update navbar with activity. Show explore instead if signed out.

* Move category selector into contract search

* Make algolia filter by category

* Default tag page to all filter
2022-05-17 12:56:10 -05:00
Sinclair Chen
1bf2073e61
refactor SiteLink to not repeat (#125) 2022-05-17 10:36:36 -07:00
Sinclair Chen
f8601af45c
Bet embed (#204)
* Add bet buttons to embed

- Make only title link to market
- Prevent avatar / username from being clicked on

* refactor: remove extra elem, de-indent

* adjust embed info row styles

* make bet panel smaller

* make sell panel smaller
2022-05-17 10:31:19 -07:00
Ian Philips
d458d8a299 Includes=>endsWith for multi digit answers 2022-05-17 10:13:29 -06:00
Ian Philips
ef98c16c26 Created contrac today => true to avoid free market flash 2022-05-17 10:06:00 -06:00
mantikoros
b98468c7ea show none option in categories 2022-05-17 12:02:46 -04:00
Ian Philips
1afda07525 Handle linking from feed 2022-05-17 10:00:09 -06:00
Boa
8337cb251f
Enable url linking to comments and a copy to clipboard function [wip] (#239)
* Link to comments & highlight comment

* Copy link, show toast and fade bg

* Remove unused imports

* Standardize link copied toast

* Add linking to answer comment threads

* Refactor open answers component, use indigo highlight

* Distinguish chosen answer a bit more
2022-05-17 09:55:26 -06:00
mantikoros
5310da05e2 fix double hashtag 2022-05-17 11:49:10 -04:00
mantikoros
2858fd090c "create market" => "ask question" 2022-05-17 11:32:20 -04:00
mantikoros
14ce820dcb cache deservesDailyFreeMarket locally 2022-05-17 11:32:10 -04:00
mantikoros
f47d75c120 landing page tweaks 2022-05-17 10:25:51 -04:00
mantikoros
4860150334 change slogan 2022-05-17 10:02:29 -04:00
Austin Chen
a3c1cd2cc1 Unhardcode M$ in more locations 2022-05-17 08:17:22 -04:00
Austin Chen
8982dcae10 Replace "m1234" with "ϻ1234" 2022-05-17 08:13:12 -04:00
Marshall Polaris
cd7efb03ca
Implement onRequest versions of createContract, placeBet functions (#227)
* Reimplement createContract and placeBet cloud functions

* Fix broken warmup function error handling
2022-05-16 21:43:40 -07:00
Marshall Polaris
aafd2a226f
Clean up some stuff with SellPanel and AmountInput (#232)
* Hoist SellAmountInput logic into SellPanel

* Ditch now-unnecessary SellAmountInput

* Clean up sale proceeds markup

* Clean unused imports

* BuyPanel doesn't need userBets
2022-05-16 20:27:37 -07:00
James Grugett
d5cc6d5067 Revert "Better random (#213)"
This reverts commit c9f3644988.
2022-05-16 20:26:51 -04:00
Austin Chen
6c6cbdc1a5
Redesign the contract card (#235)
* Redesign the card

* Limit to 1 category on a card

* Make card tags a lighter gray

* Righbar always starts from the bottom
2022-05-16 19:15:22 -04:00
Austin Chen
05c94374c9 Switch from 'M$' to 'ϻ'
Pros:
- Better emphasizes "mana"
- Visually pleasing
- Visually distinct (acts as an icon)
- Single character
Cons
- Harder to correctly type (but users can just do m1234 for shorthand)
2022-05-16 09:02:17 -04:00
Marshall Polaris
6f1c20571b
Upgrade HeadlessUI to 1.6.1 (#234)
https://github.com/tailwindlabs/headlessui/releases
2022-05-16 07:38:16 -04:00
James Grugett
695f243a93 Update categories: Add Culture, health => Covid, rearrange 2022-05-15 23:45:02 -04:00
Marshall Polaris
72b21925e5
Allow users to generate an API key in their profile (#182)
* Add /private-users/apiKey to DB

* Add field to edit API key on profile

* Move API key to bottom of profile page

Austin thinks this is better since most people don't care about it.
2022-05-15 20:41:07 -07:00
James Grugett
19da0c6c82 Default to 'all' feed category when loading page. 2022-05-15 23:09:49 -04:00
James Grugett
727d85ddac Make text underneath filled green answer bar selectable 2022-05-15 23:07:38 -04:00
Marshall Polaris
ee91a94466
Clean up some bet panel markup and CSS (#231)
* Tidy up probability change markup

* Tidy up payout markup
2022-05-15 14:10:26 -07:00
Jonas Wagner
c9f3644988
Better random (#213)
* randomString: generate a securely random string.

Also, support lengths > 12 in case that's ever needed.

This is used in at least one case (creating device tokens for users)
where it seems important that the output is unpredictable.

* Try harder to create unique usernames.

The previous version added 16 bits of entropy to the username, which
isn't all that much. Due to the birthday paradox, it would be enough to
create ~256 users with the same prefix to get a collision.

Trying that would probably fail later on, and not create security
issues... but it just seems better to be on the safe side here.
2022-05-15 13:13:07 -07:00
James Grugett
c85b806bc1 Change analytics to 45 days instead of 90 to fix timeout 2022-05-15 15:50:28 -04:00
James Grugett
75a1dfd0e4 Change feed update strategy to update a fixed subset of users every 5 minutes 2022-05-15 15:47:29 -04:00
James Grugett
ab8d541f8d Revert "Make absolute imports work with functions project (#168)"
This reverts commit c82a56af09.
2022-05-15 13:39:42 -04:00
Jonas Wagner
92aa56ba20
Swap values with structured assignment rather than temporary (#210)
Saves two lines and a variable.

Also, a desperate attempt to save my Mana in https://manifold.markets/ManifoldMarkets/if-we-open-source-our-frontend-code
2022-05-14 22:06:35 -07:00
Marshall Polaris
07ded756d9
Remove unused imports from random files (#224) 2022-05-13 18:30:52 -07:00
Marshall Polaris
bc5cd5be45
Clean up some markup in the sidebar (#220)
* Remove wrapper div around logo

* Small refactor, remove wrapper around sidebar profile summary

* Remove random unused imports

* Replace random styles with Tailwind classes
2022-05-13 18:07:44 -07:00
Marshall Polaris
33c0471c29
Clean up some markup around the top of the page (#189)
* Remove unnecessary wrapper div around sidebar

* Remove extra column used for alignment on homepage

* Remove extra wrapper div around whole page
2022-05-13 16:47:50 -07:00
Marshall Polaris
babca140f1
Fix random errors (#205)
* Fix warning in ShareMarket component

* Fix NewContract component to use keys on category list

* Refactor NewContract component to assign `value` to `select`
2022-05-13 16:42:48 -07:00
Daniel Reeves
0e64e0f9f9
Typo fix; fixes #216 (#218) 2022-05-13 18:22:10 -04:00
James Grugett
8be6b79732 Remove 'Category' label 2022-05-13 16:19:15 -04:00
James Grugett
8ce3a09471 Shrink vertical padding on feed answer group 2022-05-13 16:16:46 -04:00
James Grugett
e660acab56 Put back hotjar code, but deactivate from hotjar site 2022-05-13 15:07:32 -04:00
James Grugett
67717bbde7 Prevent having to go back twice from profile / tag search page 2022-05-13 15:01:38 -04:00
James Grugett
b195dcdfd2 Print build info only once 2022-05-13 14:33:02 -04:00
James Grugett
5f59623be8 Disable hotjar 2022-05-13 14:31:03 -04:00
James Grugett
d62156b8c0 Hide scrollbar on categories 2022-05-13 14:27:47 -04:00
Austin Chen
805a997ba0 Fix hotjar typo
Copy+pasting is hard >.>
2022-05-13 10:59:47 -04:00
Austin Chen
bf07b45467 Add id to hotjar script
Apparently needed by NextJS
2022-05-13 10:49:01 -04:00
Austin Chen
c99cf7579b Try out Hotjar 2022-05-13 10:11:57 -04:00
James Grugett
c2320a07be Free response markets initialize with 0 volume, instead of 100 2022-05-13 09:43:12 -04:00
Jonas Wagner
a373f08b4f
Update mantic.markets -> manifold.markets (#212)
The other references to "mantic" that I could find look legit, but the
referrer in stripe.ts should probably be updated.
2022-05-13 07:07:51 -04:00
James Grugett
06cdf2a84a
Show category on market card (#197)
* Show category on market card

* Show multiple categories in contract description

* Tweak layout of contract card and show multiple categories
2022-05-12 18:28:21 -05:00
Austin Chen
e0d266887c
Show prob of FR answer as a bar chart (#200) 2022-05-12 14:04:51 -04:00
Austin Chen
25d3fbcc5f Add a hover to the category selector 2022-05-12 13:47:07 -04:00
Ian Philips
c2f8aee89e Touch up reply buttons 2022-05-12 10:27:52 -06:00
James Grugett
aafc08f24c Prevent android chrome from defining categories on tap 2022-05-12 12:07:46 -04:00
Ian Philips
fed49d3040 Hide ante in FR bets tab 2022-05-12 09:48:19 -06:00
James Grugett
31f57f8ff2 Condense category names to just the tag name 2022-05-12 11:18:44 -04:00
James Grugett
49f900b298 Tags input: save on enter 2022-05-12 11:13:35 -04:00
mantikoros
9a4e5763f5
Categories (#132)
* basic market categories

* use tags to store market category

* display category in market

* display full category

* category selector component on feed

* Move feed data fetching to new file

* Decrease batch size for updating feed to prevent out-of-memory error

* Compute and update category feeds!

* Show feeds based on category tabs

* Add react-query package!

* Use react query to cache contracts

* Remove 'other' category

* Add back personal / friends to feed categories

* Show scrollbar temporarily for categories

* Remove 5 categories, change geopolitics to world

* finance => economics

* Show categories on two lines on larger screens

Co-authored-by: James Grugett <jahooma@gmail.com>
2022-05-12 10:07:10 -05:00
Boa
403156ed1a
FR ux changes, restore submit button for comments (#195) 2022-05-12 08:59:05 -06:00
James Grugett
696e6a7882 Revert "Fix hyperlinks when starting with an open paren"
This reverts commit 2fd9759bc1.
2022-05-11 21:42:33 -04:00
Marshall Polaris
1063897c7c
Clean up extra wrapper and CSS on BetRow (#178) 2022-05-11 14:35:50 -07:00
Boa
02ed9bf7e1
Single threaded comments (#175)
* Remove unused hideOutcome in comments

* Remove unused hideOutcome in comments

* Add replyToComment fields to Comment

* Add 1 threaded replies to comments & answers

* Allow smooth scrolling within pages via #

* remove yarn-error log

* correct spelling

* Remove smooth-scroll-to-hashtag component

* Cleanup & show user position/bets in replies
2022-05-11 15:11:46 -06:00
Sinclair Chen
aa433e309c
restyle home page (separation between markets) (#186) 2022-05-11 12:51:45 -07:00
Austin Chen
2fd9759bc1 Fix hyperlinks when starting with an open paren 2022-05-11 15:28:28 -04:00
mantikoros
b00814a8c4
Template emails (#185)
* unsubscribe from generic emails

* new welcome email

* fix from address

* thank you email

* one week bonus email
2022-05-11 10:51:58 -05:00
Marshall Polaris
1ca634be3b Move @types/module-alias to a dev dependency
My bad, I accidentally did `yarn add` and it went to the wrong spot.
2022-05-10 21:43:57 -07:00
James Grugett
2b9e639281 Default to 'all' search filter for markets on user page 2022-05-11 00:19:29 -04:00
Marshall Polaris
b55e807c62
Truncate multi-line commit messages in console build info (#180) 2022-05-10 21:12:00 -07:00
Marshall Polaris
22c594eb79
Print out build info in console (#179)
* Print out build info in console

* Fix up build info printing per feedback
2022-05-10 20:57:09 -07:00
James Grugett
948d878222 Prevent 500 error on markets with no collected fees 2022-05-10 23:01:07 -04:00
Marshall Polaris
9b376fb11a
Reinstate eslint warning cleanup (#174)
* Reinstate eslint upgrade and related fixes

* Another shot at improving ContractLeaderboard
2022-05-10 14:49:24 -07:00
Marshall Polaris
4565430db5 Revert "Upgrade eslint, fix eslint warnings (#149)"
It's not 100% clear, but this could have caused some production
problems on the contract page, so let's revert it for now.
2022-05-10 14:22:57 -07:00
Marshall Polaris
c82a56af09
Make absolute imports work with functions project (#168)
* Use a silly hack to make absolute imports work with Firebase tools

* Use absolute import for common module in functions code
2022-05-10 13:59:38 -07:00
Marshall Polaris
6c9df223d8
Feed container markup cleanup (#173)
* Move FeedContainer stuff into ActivityFeed

* Greatly clean up ActivityFeed container markup
2022-05-10 13:59:19 -07:00
Marshall Polaris
167cf20bfc
Upgrade eslint, fix eslint warnings (#149)
* Add a couple missing dependencies for hooks

* Upgrade eslint

This newer eslint and typescript-eslint fixes some spurious warnings
that were bugs and supports our version of Typescript.

* Use Next Script component the way it wants us to

* Rephrase ContractLeaderboard component to avoid useEffect woes

* Use perhaps more idiomatic type for ContractLeaderboard props

* Make Folds data fetching more correct and more clear
2022-05-10 13:58:38 -07:00
Sinclair Chen
a297f6492d
move manaToUSD to format.ts (#172) 2022-05-10 11:14:24 -07:00
Austin Chen
b97301c6fa Hardcode two new frontpage markets 2022-05-10 13:03:53 -04:00
mantikoros
8313930c70 show correct commission in market close emails 2022-05-10 11:24:06 -04:00
James Grugett
bbfdc923fb Fix word scores, broken by an extra long word 2022-05-10 10:28:07 -04:00
mantikoros
264e5058ea
Separate out fees (#169)
* deduct market ante from profits

* display creator fees in stats

* show creator earnings in stats

* separate out creator, liquidity fees in payouts and deduct from profits

* include creator payout in resolution emails

* deduct liquidity from profits

* hide cost tooltip if daily free market
2022-05-10 08:49:14 -05:00
James Grugett
067fb34973 Switch vscode imports to "shortest" 2022-05-10 09:43:04 -04:00
James Grugett
235c9f6b21 Add .vscode workspace settings: Use non-relative imports 2022-05-09 19:18:10 -04:00
Marshall Polaris
d34907681a
Remove some random unnecessary wrapper divs (#163)
* Remove wrapper div in user comment feed item

* Remove wrapper div around feed market card title link

* Remove wrapper div around BetRow
2022-05-09 15:50:20 -07:00
mantikoros
ef43e46153 include fees in tooltip 2022-05-09 17:33:24 -04:00
Marshall Polaris
8a99f3772a
Cleanup avatar again (#161)
* Reinstate avatar component cleanup

This was reverted due to a bug, fixed in the subsequent commit.

* Kill additional wrapper divs around avatars

This also fixes a bug where the `w-8` answer row wrapper div was
constraining the width of the `w-10` avatar, leading it to be `w-8`
and `h-10` and appear as an oval.
2022-05-09 14:32:59 -07:00
mantikoros
9fc9fc0dd6 fix import errors 2022-05-09 17:14:00 -04:00
mantikoros
5135135e79
Separate out fees (#159)
* deduct market ante from profits

* display creator fees in stats

* show creator earnings in stats

* separate out creator, liquidity fees in payouts and deduct from profits
2022-05-09 16:04:40 -05:00
Boa
a5b0372a6e
Free response markets with investment cap (#157)
* Show error message for FR bet

* Allow M$ limit for markets

* Allow M$ limit for markets

* Apply M$ limit to FR answer bets

* Improve error message

* Improve error message

* Only check stats if mana limit set

* Consolidate logic

* Remove unused variable

* absolute import

* absolute imports
2022-05-09 14:09:07 -06:00
James Grugett
d55990d5d4 Fix contract search not adjusting to new user 2022-05-09 15:47:18 -04:00
James Grugett
e8ab863557
🔍 Algolia search (#136)
* Add algolia and instantsearch packages

* Switch to hooks-web package

* Implement algolia search!

* Fix types

* Fix tags page

* Closed sort option

* Implement select for filtering on open, closed, resolved, all.

* Support search in dev environment

* Fix runtime error in landing page
2022-05-09 12:38:33 -05:00
Austin Chen
3fc159f10b Revert "Convert common imports in functions to be absolute"
This reverts commit c03518e906.
2022-05-09 12:06:16 -04:00
Ian Philips
8fbbeffd38 Revert one more avatar component change 2022-05-09 10:33:52 -04:00
Ian Philips
5c1bc78408 Revert avatar component changes 2022-05-09 10:18:05 -04:00
Marshall Polaris
acc9c84e2e
More absolute imports (#156)
* Configure functions module to allow absolute imports

* Convert common imports in functions to be absolute

* Convert common imports in web to be absolute

* Convert lib imports in web to be absolute

* Convert hooks imports in web to be absolute

* Convert components imports in web to be absolute
2022-05-09 09:04:36 -04:00
Marshall Polaris
dd2366458d
Fix up target in web tsconfig (#155) 2022-05-08 23:57:29 -07:00
Austin Chen
b47e4364ad Remove circle around markets/comments count 2022-05-08 22:53:02 -04:00
Marshall Polaris
5efcf61289
Configure compiler to allow absolute imports from root (#147) 2022-05-08 13:33:58 -07:00
Austin Chen
2eed1c432a Hardcode in 8 frontpage markets 2022-05-07 19:44:01 -04:00
Austin Chen
cea9422802 Update site copy 2022-05-07 10:10:25 -04:00
Austin Chen
22a9901148 Feature Nonlinear as a charity 2022-05-07 08:15:40 -04:00
James Grugett
4de0fcd198 Revert "Represent DB avatar URLs as non-null (#128)"
This reverts commit bf8e09b6c1.
2022-05-06 14:29:15 -04:00
SirSaltyy
87b43e6bdb
Add link to twitter with svg icon to the sidebar and profile menu. Remove Discord hero icon and replace it with discord svg. (#135) 2022-05-05 22:20:18 -04:00
SirSaltyy
e41f646b42
Remove all spaces between M$ and the associated number (#134)
* Remove all spaces between M$ and the associated number

* Update index.tsx
2022-05-05 22:19:47 -04:00
Boa
bbf419953e
Show comments on profile (#137)
* WIP - got comments on the user page

* Remove number from chosen FR answer

* Distinguish wining and losing FR answers

* Show no answers text

* Simplify get answer items logic

* Show answer number

* Show answer # when resolving

* Fix import path

* Add user's collated comments onto profile

* Allow linking to comments/markets in profile

* Allow preload of users contracts in profile

* Remove unused check

* Small code improvements
2022-05-05 16:30:30 -06:00
Marshall Polaris
2e214cab7a
Kill warmup spam (#133)
* Only warm up sell bet function once

* Only warm up create user function once
2022-05-05 09:51:09 -07:00
Marshall Polaris
adefd3259c
Cleanup avatar component rendering (#130)
* Remove unnecessary classes on avatar img

I don't believe these have any visible effect.

* Don't apply Tailwind 'avatar' class in menu

We don't use this class elsewhere when displaying avatars (instead our
avatar has manual styles that do the stuff Tailwind is trying to do)
and it just assigns a weird size that we don't want.

If we want to use the Tailwind avatar styles we should refactor further.

* Remove unnecessary avatar wrapper div

* Remove old prop from avatar
2022-05-05 09:50:58 -07:00
Austin Chen
1caed44552 Show /charity in signed-out sidebar 2022-05-05 10:15:12 -04:00
Austin Chen
b42e77858f Search through charity preview, description, and tags too 2022-05-05 10:12:16 -04:00
Boa
9480f9f34c
Improve free response answer ux (#131)
* Remove number from chosen FR answer

* Distinguish wining and losing FR answers

* Show no answers text

* Simplify get answer items logic

* Show answer number

* Show answer # when resolving
2022-05-04 16:03:06 -06:00
Marshall Polaris
bf8e09b6c1
Represent DB avatar URLs as non-null (#128) 2022-05-04 11:07:22 -07:00
Marshall Polaris
899c6ab0e0
Add script to denormalize avatars into other docs (#127)
* Add script to denormalize avatars into contracts/comments

* Also handle denormalizing answer avatar URLs

* Small fixups
2022-05-04 11:07:00 -07:00
mantikoros
95b67c05e2 another sell shares rounding bug 2022-05-04 11:47:45 -04:00
mantikoros
5c18820d96 go back to $100 fixed ante 2022-05-04 11:11:06 -04:00
Ian Philips
a004d3a4bf Always show answer panel 2022-05-03 17:54:00 -04:00
Ian Philips
278bcb9724 Show resolved FR answers 2022-05-03 16:57:39 -04:00
Ian Philips
0d63e471be Only show cursor if focused 2022-05-03 16:45:21 -04:00
Boa
3a33efa8db
Threaded free response comments & general comments sections (#121)
* Allow comments to reference answers

* Add comment inputs for free response answers

* condense comment logic in one component

* Add General Comments section to FR answers

* Prompt signin even if no comment

* Remove unused & refactor

* Simplify general comments logic, toggle comment boxes

* Clarify rendering logic
2022-05-03 14:38:40 -06:00
Sinclair Chen
100821e34c
Ask user to buy more mana when insufficient funds (#124) 2022-05-03 13:36:54 -07:00
Sinclair Chen
f2217d1d8b
Improve donate box UI in mid-size window (#126) 2022-05-03 13:36:00 -07:00
Sinclair Chen
abf23a1462
Pre-load charity order to prevent "jump" (#122) 2022-05-03 10:25:14 -07:00
mantikoros
a982b86cfe $300 ante for free daily markets 2022-05-03 12:18:37 -04:00
mantikoros
14544d064a charity page: remove centering 2022-05-03 11:26:02 -04:00
Sinclair Chen
2da5423f36
Add padding too charity images (#123) 2022-05-03 08:12:42 -07:00
Sinclair Chen
3434b3de3f
Move all charity images to imgur + Next (#120) 2022-05-03 07:00:33 -07:00
Boa
8da36298e5
condense comment logic in one component (#119) 2022-05-03 07:51:25 -06:00
James Grugett
ab4dbc798c Change analytics to 90 days. Default to DAU / MAU chart instead of DAU / WAU. 2022-05-02 18:18:42 -04:00
Sinclair Chen
2f6a3c4e00
Replace email with free response market (#118) 2022-05-02 12:48:15 -07:00
mantikoros
a516122f61 round shares to avoid negative shares error message 2022-05-02 13:59:09 -04:00
Sinclair Chen
db695875c4
CPM: sort charities by amount raised (#117)
* Sort charities by amount raised (after Featured)

* Sort donations chronologically

* refactor charities query to remove parens
2022-05-02 10:55:40 -07:00
James Grugett
beece64ae5 Tweak contract info dialog 2022-05-02 12:18:55 -04:00
mantikoros
43d3662db1 fixed negative shares bug 2022-05-02 12:16:36 -04:00
James Grugett
6232284e92 Larger font on amount input 2022-05-02 12:15:00 -04:00
James Grugett
bec8cdb3e8 Add share market widget shown if you are the creator 2022-05-02 11:23:54 -04:00
Sinclair Chen
fdbcffcfbc
CPM simple feed (#116)
* Add minimal feed

* Display full cent amount for raised < $1
2022-05-02 08:23:12 -07:00
James Grugett
0b5b0bb9d3 Clear comment after submitting, for multiple bets 2022-05-02 10:43:17 -04:00
James Grugett
d6a9b89c43 Fix answers not wrapping in cards 2022-05-02 10:35:49 -04:00
Austin Chen
0a63a0ae1f
Update README link to Discord 2022-05-02 10:14:40 -04:00
Austin Chen
b63cc17630 Hide charity names on cards 2022-05-02 08:45:05 -04:00
Austin Chen
1fa214ed48 Add Givewell MIF 2022-05-02 08:42:50 -04:00
James Grugett
06b7e49e98
[In progress] Server-side feed computation (#106)
* Store view counts & last viewed time

* Schedule updating user recommendations. Compute using tf-idf.

* Update contract's lastBetTime and lastCommentTime on new bets and comments.

* Remove contract's lastUpdatedTime

* Remove folds activity feed

* Implement getFeed cloud function

* Hook up client to use getFeed

* Script to cache viewCounts and lastViewTime

* Batched wait all userRecommendations

* Cache view script runs on all users

* Update user feed each hour and get feed from cache doc.

* Delete view cache script

* Update feed script

* Tweak feed algorithm

* Compute recommendation scores from updateUserFeed

* Disable lastViewedScore factor

* Update lastCommentTime script

* Comment out console.log

* Fix timeout issue by calling new cloud functions with part of the work.

* Listen for contract updates to feed.

* Handle new user: use default feed of top markets this week

* Track lastUpdatedTime

* Tweak logic of calling cloud functions in batches

* Tweak cloud function batching
2022-05-01 11:36:54 -05:00
Austin Chen
80d594bd5f Rename QURI back 2022-05-01 08:30:55 -04:00
Austin Chen
ade8eb7aae Add CSPI 2022-04-30 18:00:51 -04:00
Austin Chen
bbc8915f79 Clean up prediction market on charities 2022-04-30 17:42:08 -04:00
Austin Chen
53a584f37d Add in LTFF, ARC, RC 2022-04-30 17:35:10 -04:00
Marshall Polaris
731e5d5b7c
Apply permissive CORS headers to API (#115)
* Take cors package as dependency

* Apply permissive CORS headers to all API routes
2022-04-30 13:30:49 -07:00
mantikoros
a3311bd5aa embed total donations market on charity page 2022-04-30 16:27:19 -04:00
Austin Chen
46bf09f182 Feature 1Day Sooner & QURI 2022-04-30 15:47:47 -04:00
Austin Chen
1c7232f31e Add 1Day Sooner & QURI as supported charities 2022-04-30 15:26:07 -04:00
Austin Chen
f5e5af0b7a Revert "bound initial probability to [0.1, 0.9]"
This reverts commit da153ceea9.
2022-04-30 13:05:43 -04:00
mantikoros
f6d4409899 return liquidity pool after resolution according to true pool weight 2022-04-30 12:24:33 -04:00
Austin Chen
ccd0e42734 Revert "give@ isn't set up, use info@"
This reverts commit d2218b5b8b.
2022-04-30 11:19:20 -04:00
Austin Chen
04c42e7835 Tweak copy ("You with" => "You have") 2022-04-30 10:20:03 -04:00
James Grugett
a4c722550a Don't truncate comments for contract page 2022-04-30 10:07:39 -04:00
Austin Chen
d2218b5b8b give@ isn't set up, use info@ 2022-04-30 09:45:24 -04:00
Austin Chen
cc300c84e1 Adjust totalDeposits on charity donation 2022-04-30 08:52:24 -04:00
Austin Chen
5c03f1581a Fix lowercase search, tweak copy 2022-04-30 08:42:25 -04:00
Austin Chen
774ba6fba6 Show confetti on donate 2022-04-30 08:18:25 -04:00
Austin Chen
c51aa0b6b4 Simpify Donate panel 2022-04-30 08:13:38 -04:00
Austin Chen
bd98e8810e Disable "Read more..." for now 2022-04-30 08:09:57 -04:00
Austin Chen
a6de0ec695 Add missing charity logos 2022-04-30 08:09:57 -04:00
Marshall Polaris
17b5345d82
Fix HTTP charity logo links (#113) 2022-04-30 07:44:11 -04:00
James Grugett
3bb4111445 Make charity cards extend same length in row. Tweak image padding 2022-04-29 23:55:32 -04:00
Austin Chen
78e8927de4
Image preview: truncate to 100 chars, show avatar url (#111)
* Truncate image preview to 120 chars

* Try 100 chars instead

* Pass along creatorAvatarUrl

Hoping nothing breaks if the avatarUrl is empty

* Thread through avatarUrl all the way

* Fix typescript
2022-04-29 19:38:31 -04:00
Sinclair Chen
73fc67955d
Send M$ to Charity & txns (#81)
* Add components for CPM landing and charity pages

* Remove misc.ts to fix build

* Set up cloud function for writing txns

* More plumbing for txns

* Fix up API call

* Use Date.now() to keep timestamps simple

* Some styles for charity list page

* Hard code charities data

* Pass charity data to charity page

* Update txn type

* Listen for charity txns

* Handle txn to non-user by burning it

* Read txns for charity card and charity page.

* Set images to object contain

* Clean up txn types

* Move pic to top of card. Other misc styling.

* Update charity short & long descriptions

* Add `token` and `category` to Txn

* Fix breakages

* Show Charity link in the sidebar

* Fix typing issues

* Fix not reading from the right type

* Switch out icon

* Also show Charity icon on mobile

* Update copy

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
Co-authored-by: James Grugett <jahooma@gmail.com>
2022-04-29 19:35:56 -04:00
Boa
78997c1e45
Show comments position (#110)
* Add betting activity back to feed

* Show position in bin. markets, no comments on bets

* Degroup bets on Bets tab

* Show users position or recent bet with comments

* Add tooltip on answer to FR comments

* Style improvements

* Only use bets by current user for comment input
2022-04-29 15:11:04 -06:00
mantikoros
5cb6ee3bca daily free markets' liquidity provided by @ManifoldMarkets 2022-04-29 15:58:01 -04:00
mantikoros
5f86637ca5 raise ante to M$100 2022-04-29 15:53:13 -04:00
Austin Chen
c2832039b8
Link to beeminder & discourse 2022-04-29 13:45:10 -04:00
mantikoros
f078ce4fa9 CLA: fix link 2022-04-29 13:40:44 -04:00
mantikoros
be7e4a5c03 Manifold CLA: readme message, move to top level 2022-04-29 13:39:52 -04:00
mantikoros
c59e444d0a Manifold CLA 2022-04-29 13:30:27 -04:00
James Grugett
fa8ebe36bd Move loading indicator into SearchableGrid. 2022-04-29 10:05:32 -04:00
James Grugett
7e9007aad1 Fetch markets client side on explore page (to avoid vercel 5MB limit for static props) 2022-04-29 09:53:51 -04:00
Ian Philips
ca8420d61b Allow free daily market with M-zsh 2022-04-29 07:38:46 -06:00
Marshall Polaris
2ddd95e904
Make tags page filter on server side (#108) 2022-04-28 22:39:39 -07:00
Ian Philips
760681f958 Default has created one to prevent flash 2022-04-28 19:39:55 -06:00
Ian Philips
9e275a18f1 use Date instead of dayjs 2022-04-28 19:31:43 -06:00
Boa
4ec59be46f
Free daily market (#107)
* Allow users a free daily market

* Show confetti on recent created market

* remove unused import

* remove comment

* Did create market -> hook, capitalize buttons

* Check for confetti with interval

* Just check once

* Capitalize create market button on feed
2022-04-28 17:01:50 -06:00
Austin Chen
2e17f9f917 Add a "Show more..." button when there are more contracts 2022-04-28 18:45:26 -04:00
Ian Philips
625308c19d Readme: must be in dev for emulators to work 2022-04-28 12:29:02 -06:00
mantikoros
da153ceea9 bound initial probability to [0.1, 0.9] 2022-04-28 10:47:18 -04:00
mantikoros
45aa6646fa sendNewCommentEmail: forgot abs value 2022-04-27 23:34:50 -04:00
mantikoros
d9ee03a96f sendNewCommentEmail: handle sold shares for cfmm 2022-04-27 22:24:32 -04:00
James Grugett
8e514de28f Add volume factor to feed 2022-04-27 16:26:01 -04:00
Marshall Polaris
d33913a111
A couple additions to help newbies out (#104) 2022-04-27 13:15:14 -07:00
mantikoros
5a5069a419 sitemap: market changefreq; homepage priority = 1 2022-04-27 13:47:51 -04:00
mantikoros
e926741177 sitemap: remove non-public pages; sort by 24hr volume; make market page priority depend on volume 2022-04-27 13:40:02 -04:00
Marshall Polaris
53c79f41a3
Hardening Firestore rules (#101)
* Harden Firestore fold update rule

This prevents editing fields on the fold that would lead to
strange and disruptive results, for example, changing the
curatorId to another user, or manually changing followCount.

* Harden Firestore follower update rule

This prevents users from creating follower entries with
the userId of someone else, which would effectively
subscribe that person to the fold.

* Harden Firestore comment posting rule

This prevents people from posting comments with inauthentic
user information.

* Fix silly bugs in comment rules I made
2022-04-27 01:17:29 -07:00
Marshall Polaris
c0d6e17060
Disallow users deleting contracts (#99) 2022-04-27 00:34:23 -07:00
Boa
1db1a739cf
Misc comment ux improvements (#103)
* Separate comments and bets via tabs

* Normalcase comment button

* Note about abbreviated and all mode

* Revese,abbreviate,limit comments in feed
2022-04-26 15:08:50 -06:00
Marshall Polaris
f9f226aceb
Write some useful stuff in the /web README (#94)
* Write some useful stuff in the /web README

* Fix one busted link in new README

* Reorder some README stuff per feedback
2022-04-26 13:08:29 -07:00
Boa
d6c7230d6a
Moved answers (#102)
* Moved answers

* Remove unnecessary code
2022-04-26 09:53:12 -06:00
Ian Philips
9fbd521732 Top traders -> Top bettors 2022-04-26 08:42:45 -06:00
Ian Philips
8ba2a55180 Your trades -> Your bets 2022-04-26 08:38:28 -06:00
Boa
7722c723c4
Separate free response answers & comments (#100)
* Just for playing with, bad code

* Whiten answer cards

* Separate answers and comments in FR markets

* Highlight FR answer in bet w/ comment

* Darken answer text and move classname

* Normalcase Comment
2022-04-26 07:24:57 -06:00
Marshall Polaris
9270d48e12
Flesh out main README (#96)
* Flesh out main README

* Shorten "Getting Started" to an numbered list

Co-authored-by: Austin Chen <akrolsmir@gmail.com>
2022-04-26 01:35:22 -05:00
Marshall Polaris
5df0b014c1
Bump web typescript to 4.5.3 (#98) 2022-04-25 10:47:00 -05:00
Boa
1e2f9fc099
Support local firestore emulation (#91)
* Support local firestore emulation

* Update readme

* Update readme
2022-04-25 09:46:35 -06:00
Marshall Polaris
44107ccbd3
Take 2 on getting tsc to behave (#97) 2022-04-25 10:46:20 -05:00
Marshall Polaris
4d48c98975
Fix generation of create market placeholder text (#92)
* Fix generation of create market placeholder text

* Capitalize comment
2022-04-24 17:14:20 -05:00
Marshall Polaris
8ac95b363d
Remove workspace-specific lockfiles (#95) 2022-04-24 16:36:42 -05:00
Marshall Polaris
06f9f53951
Link to /home, not / (#93) 2022-04-22 21:39:29 -05:00
mantikoros
bfbcde4dfc remove fees for sold shares 2022-04-22 12:26:47 -05:00
Ian Philips
4e4ffe8e20 Sell either type of share for dpm market 2022-04-22 04:43:34 -06:00
Ian Philips
3f471e7e8e Sell either type of share for dpm market 2022-04-22 04:30:13 -06:00
Ian Philips
8109f3d9a9 Show free comments in feed 2022-04-22 04:16:54 -06:00
James Grugett
a525ad82ec Filter out markets you sold out of from most of the views on portfolio page. 2022-04-21 18:32:46 -05:00
mantikoros
a7c422e69e change ante to M$ 50 2022-04-21 12:59:12 -05:00
mantikoros
004969aa66
user-added liquidity provision panel (#90)
* user-added liquidity provision panel

* AddLiquidityPanel: handle loading, errors

* ContractInfoDialog: don't show add liquidity when market is closed

* ContractInfoDialog: hide add liquidity for FR
2022-04-21 12:58:12 -05:00
James Grugett
1af0740f10 Fix explore on dev 2022-04-21 12:43:57 -05:00
Boa
7b70b9b3bd
Free comments (#88)
* Allow free comments with optional bets

* Send emails for comments without bets

* Refactor to share logic

* No free comments on free response questions

* Minor fixes

* Condense line
2022-04-21 11:09:06 -06:00
Austin Chen
9ce82b1b6f Show "New" badge on contract cards too 2022-04-20 23:03:16 -07:00
James Grugett
7847a9e781 Track latency of feed and portfolio page. 2022-04-21 01:00:08 -05:00
James Grugett
8c8a9be6a1 Show new label instead of 0 volume 2022-04-20 17:25:29 -05:00
James Grugett
e1b5b595e7 Also get new contracts for feed with 0 volume. 2022-04-20 16:59:34 -05:00
James Grugett
01e43abd17 Add factors to weight new contracts higher, contracts with unseen comments, and contracts with prob closer to 50% 2022-04-20 16:37:01 -05:00
Ian Philips
0390ec0f47 Prepare user website urls for external link 2022-04-20 10:13:17 -06:00
Ian Philips
d6d72aa619 Clean twitter and website user urls 2022-04-20 09:47:14 -06:00
Boa
9ba7c04524
Sell shares mobile (#86)
* Abstract sell shares row to component

* Allow sell row to show just a button

This is nice for the feed and on a bet's mobile interface.

* Add and use floor shares

* Allow sell button on the same line as bet button

* Move use save shares to own file

* Make sure to sell non-integer shares

* Create SellButon & sell non-integer shares

* Remove props prefixes

* Break out sell modal and button
2022-04-20 08:13:39 -06:00
James Grugett
c592a3ab59 Fix type error take 2 2022-04-20 01:04:53 -05:00
James Grugett
d8a24e1abd Rename script environment variable to GOOGLE_APPLICATION_CREDENTIALS_DEV (and _PROD) 2022-04-20 00:47:13 -05:00
Marshall Polaris
5dbe16ec8d
Change how Firebase scripts auth (#87)
Now the admin key path is loaded from enviroment variables instead
of being hardcoded in script-init.ts.
2022-04-19 23:31:46 -05:00
James Grugett
987e36c357 Fix type error 2022-04-19 22:36:19 -05:00
James Grugett
3c1e16b815 Move contract details components to their own file. 2022-04-19 22:34:41 -05:00
James Grugett
3b413e52e5 Clean up FR answer on card 2022-04-19 22:31:45 -05:00
Marshall Polaris
30d92becd6
Kill allowTransparency on 404 Discord widget (#84) 2022-04-19 21:42:04 -05:00
Marshall Polaris
0f3b0f242d
Fix bugs in Firebase README (#83) 2022-04-19 21:41:43 -05:00
Marshall Polaris
bf408d9bf0
Fix tiny Tailwind config details (#82)
* Fix Tailwind darkMode CSS pref

See https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration.

* Fix Tailwind 'purge' -> 'content'

See https://tailwindcss.com/docs/upgrade-guide#configure-content-sources.

* Remove additional cruft from Tailwind config

These literally weren't hurting anything but the upgrade guide
suggests toasting them, so might as well do it while I'm in here.
2022-04-19 21:41:24 -05:00
Marshall Polaris
abf9d30ad2
Fix up dev tsc typechecking (#85)
* Don't typecheck Next-generated JS

* Add comment to package.json explaining explicit tsc
2022-04-19 21:40:54 -05:00
James Grugett
d19debfd19 Close time label: show relative time iff same day. Show year iff not same year. 2022-04-19 12:55:22 -05:00
James Grugett
24a9a79f16 Decrease padding so sell shares label fits in one line 2022-04-19 12:53:04 -05:00
James Grugett
190a39b2d6 Autofocus question input on /create page 2022-04-19 11:04:07 -05:00
Ian Philips
7ccdc6d683 Hide bet panel on FR after successful submit 2022-04-19 09:01:40 -06:00
Boa
7c7d6815f8
Add create market button (#79) 2022-04-19 08:15:05 -06:00
James Grugett
e8e19e0e5d Add monthly retention (code suggested by co-pilot) 2022-04-19 01:57:27 -05:00
James Grugett
3c6369726e Add weekly activiation rate 2022-04-19 01:47:58 -05:00
James Grugett
280ae97070 Track clicks from feed 2022-04-19 00:16:08 -05:00
James Grugett
a6e6e2f52f Include top answer in answer card. 2022-04-18 21:44:31 -05:00
James Grugett
88b8d39c7c Remove filterToOutcome 2022-04-18 20:54:31 -05:00
James Grugett
9c74f88b4a
Improve Free response UI (#78)
* Add legend to free response graph

* Hide answers panel unless resolving. Correctly order answers

* No gray background for add answer & resolve panel. Tweak spacing

* Max answer length 240 chars

* Show answer text in resolution for market page, card instead of number.

* Remove remaining answer #'s. Refactor outcome/resolution labels.

* Move answer panel back up

* Tweak spacing

* Update placement of bet button on mobile for FR answer feed item

* Fix reversed feed for binary markets

* Show multi resolve options

* Clean up unused parts of answer item

* Lighten resolve buttons

* Show answer text in market resolve email
2022-04-18 18:02:40 -05:00
mantikoros
7abc11c146 getInitialProbability: use totalShares to calculate prob for ported contracts 2022-04-18 17:15:25 -05:00
mantikoros
790aec9baf
Cfmm migration (#80)
* cfmm migration script

* save dpm shares, pool

* formatMoney: use floor instead of round

* migrate cfmm: use sale amount as shares for sold bets

* migration: make up fake share qty for sold shares

* migration: also convert resolved markets

* migration: don't migrate resolved markets after all

* migration: error checking
2022-04-18 17:02:53 -05:00
James Grugett
b637ab1536 Analytics: Add ratios of daily, weekly, monthly active users 2022-04-15 20:56:07 -05:00
James Grugett
102e22e111 Recent trade => recent 2022-04-15 17:55:26 -05:00
James Grugett
87b54ff92e Fix getBinaryProb when resolutionProbability is 0. 2022-04-14 23:17:55 -05:00
James Grugett
72a56b46fd Switch portfolio default sort to recent trade 2022-04-14 00:16:08 -05:00
James Grugett
43b8c16fcc Compact-ify cards in portfolio page 2022-04-14 00:06:13 -05:00
James Grugett
8a59ab3180 Tweak current investment calculation 2022-04-13 22:28:29 -05:00
James Grugett
2a3593c269 Fix profit calculation for dpm sales 2022-04-13 20:42:47 -05:00
James Grugett
e58ec54546 Clean up portfolio sorts 2022-04-13 18:56:28 -05:00
James Grugett
ec63464461 Remove Communities: Not shown in navigation or market info 2022-04-13 18:14:03 -05:00
James Grugett
a09af526a0 Switch summary stat from payout at current % to profit 2022-04-13 17:57:46 -05:00
James Grugett
bfb708d477 Fix profit calculation for cpmm sales. Change totalValue to investment value excluding sales 2022-04-13 17:25:32 -05:00
James Grugett
a9028b0a94 Remove invested metric for cfmm markets 2022-04-13 15:47:12 -05:00
Austin Chen
624d99fc1d Show number of traders in "More" panel 2022-04-13 12:11:49 -07:00
James Grugett
75b39cbf6f Remove loans: no new loans 2022-04-13 12:52:12 -05:00
James Grugett
6ac361fdb9 Fix lodash import for cloud functions 2022-04-13 12:44:47 -05:00
James Grugett
4268e03293 Redirect to contract page if home loads initially with query params 2022-04-12 23:11:10 -05:00
James Grugett
ff1cb68077 Copy contract url doesn't use current url 2022-04-11 17:06:51 -05:00
James Grugett
e53cde5b34 Quick back navigation into feed 2022-04-11 16:13:26 -05:00
James Grugett
9f2ac17ffb Add copy link button in market share options 2022-04-10 19:59:01 -05:00
James Grugett
8c9da833bf Don't match parenthses in link url 2022-04-10 19:47:26 -05:00
James Grugett
4a9fc7bc9b Don't show portfolio when signed out. Home navigates to '/' when signed out. 2022-04-10 00:26:28 -05:00
mantikoros
642fedbbb4 fix error 2022-04-09 19:09:06 -05:00
mantikoros
ea07edcbd3 add funds: remove M$500 purchase option 2022-04-09 18:56:05 -05:00
mantikoros
5bf36d1963 market stats: pool 2022-04-09 18:23:24 -05:00
mantikoros
2d8e85ab5e ContractInfoDialog: volume 2022-04-09 18:13:15 -05:00
James Grugett
ec49a73c74
Algo feed (#77)
* Implement algo feed

* Remove 'See more...' from feed items

* Fix problem with useUpdatedContracts.

* Tweak some params
2022-04-09 18:10:58 -05:00
mantikoros
7c11df6147 market info stats 2022-04-09 18:04:13 -05:00
mantikoros
fe5fba184f FeedQuestion: hide close time 2022-04-09 17:15:05 -05:00
mantikoros
6e38540d58 contract tabs: always show timeline tab 2022-04-09 16:34:43 -05:00
mantikoros
13300326fd bet panel styling 2022-04-09 16:26:43 -05:00
mantikoros
b624ec045e "buy" => "bet" 2022-04-09 16:13:36 -05:00
mantikoros
2fe71731e4 create: fixed ante of $100 2022-04-09 13:51:22 -05:00
mantikoros
ab41404d07
contract tabs: separate market timeline from your trades (#76)
* contract tabs: separate market timeline from your trades

* contract tabs: use pre-existing tab component

* tab styling
2022-04-08 16:13:10 -05:00
mantikoros
108f0afb95 contract card: remove created time; inline info dialog button 2022-04-08 14:27:17 -05:00
James Grugett
238af975e3 Reverse FR acitivty as well 2022-04-07 18:00:44 -05:00
James Grugett
7dfc3fb966 Reverse contract feed on contract page! 2022-04-07 16:29:36 -05:00
James Grugett
f773a59676 FR: Move description above answers 2022-04-07 16:19:27 -05:00
James Grugett
aff6acc8f4 Refactor contract components into contract directory 2022-04-07 16:15:51 -05:00
James Grugett
14cef9ffc3 Move contract description out of activity feed 2022-04-07 16:09:19 -05:00
James Grugett
5e8decfa4e Move tweet, embed buttons plus communities and tags into a market info dialog 2022-04-07 15:52:54 -05:00
mantikoros
7e6545a669 app: viewport meta tag to prevent Safari from automatically zooming in 2022-04-06 22:54:44 -05:00
mantikoros
20052b61ec AmountInput: filter non-numeric, show numeric keyboard on ios 2022-04-06 22:48:06 -05:00
718 changed files with 71357 additions and 23235 deletions

27
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,27 @@
# Manifold CLA
**Manifold Markets Contributor License Agreement**
(Thanks to [Beeminder](http://bmndr.co/cla) and [Discourse.org](https://cla-assistant.io/discourse/discourse) whose CLAs we modeled this on!)
## Unofficial Summary
- Manifold can use your contributions
- Manifold can sell things involving your contributions
- Youre legally able to agree to the above
- Youre the one who created these contributions
- Manifold decides what gets included in Manifold
- Manifold does not promise any support
## Official Agreement
The document below clarifies the terms under which You (the copyright owner or legal entity authorized by the copyright owner), may make "The Contributions" (software, bug fixes, configuration changes, documentation, or any other materials) to "The Work" (Manifold Markets). This license protects You, "The Company" (Manifold Markets, Inc.) and licensees; it does not change your rights to use your own contributions for any other purpose.
You and "The Company" (Manifold Markets, Inc.) agree:
- You grant to "The Company" (Manifold Markets, Inc.) a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, relicenseable, transferable license under all of Your relevant intellectual property rights, to use, copy, prepare derivative works of, distribute and publicly perform and display "The Contributions" on any licensing terms, including without limitation: (a) open source licenses like the GNU General Public (v2.0) license; and (b) binary, proprietary, or commercial licenses. Except for the licenses granted herein, You reserve all right, title, and interest in and to "The Contributions".
- You grant to "The Company" a non-exclusive, irrevocable (except as stated in this section), worldwide, royalty-free, sublicenseable, transferable patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer "The Work", where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with "The Work" to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or "The Work" to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
- You are able to grant us these rights. You represent that You are legally entitled to grant the above license(s). If Your employer has rights to intellectual property that You create, You represent that You have received permission to make "The Contributions" on behalf of that employer, or that Your employer has waived such rights for "The Contributions".
- "The Contributions" are your original work. You represent that "The Contributions" are Your original works of authorship, and to Your knowledge, no other person claims, or has the right to claim, any right in any invention or patent related to "The Contributions". You also represent that You are not legally obligated, whether by entering into an agreement or otherwise, in any way that conflicts with the terms of this license. For example, if you have signed an agreement requiring you to assign the intellectual property rights in "The Contributions" to an employer or customer, that would conflict with the terms of this license.
- We, as authoritative representatives of "The Company" determine the code that is in "The Work". You understand that the decision to include "The Contribution(s)" in any project or source repository is entirely that of "The Company", and this agreement does not guarantee that "The Contributions" will be included in any product.
- No Implied Warranties. "The Company" acknowledges that, except as explicitly described in this Agreement, the Contribution is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.

55
.github/workflows/check.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Check PRs
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
FORCE_COLOR: 3
NEXT_TELEMETRY_DISABLED: 1
jobs:
check:
name: Static analysis
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Restore cached node_modules
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
- name: Install missing dependencies
run: yarn install --prefer-offline --frozen-lockfile
- name: Run Prettier on web client
working-directory: web
run: npx prettier --check .
- name: Run ESLint on common
if: ${{ success() || failure() }}
working-directory: common
run: npx eslint . --max-warnings 0
- name: Run ESLint on web client
if: ${{ success() || failure() }}
working-directory: web
run: yarn lint --max-warnings 0
- name: Run ESLint on cloud functions
if: ${{ success() || failure() }}
working-directory: functions
run: npx eslint . --max-warnings 0
- name: Run Typescript checker on web client
if: ${{ success() || failure() }}
working-directory: web
run: tsc --pretty --project tsconfig.json --noEmit
- name: Run Typescript checker on cloud functions
if: ${{ success() || failure() }}
working-directory: functions
run: tsc -b -v --pretty

43
.github/workflows/format.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Reformat main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
env:
FORCE_COLOR: 3
NEXT_TELEMETRY_DISABLED: 1
# mqp - i generated a personal token to use for these writes -- it's unclear
# why, but the default token didn't work, even when i gave it max permissions
jobs:
prettify:
name: Auto-prettify
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
token: ${{ secrets.FORMATTER_ACCESS_TOKEN }}
- name: Restore cached node_modules
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
- name: Install missing dependencies
run: yarn install --prefer-offline --frozen-lockfile
- name: Run Prettier on web client
working-directory: web
run: yarn format
- name: Commit any Prettier changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Auto-prettification
branch: ${{ github.head_ref }}

43
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Run linter (remove unused imports)
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main]
env:
FORCE_COLOR: 3
NEXT_TELEMETRY_DISABLED: 1
# mqp - i generated a personal token to use for these writes -- it's unclear
# why, but the default token didn't work, even when i gave it max permissions
jobs:
lint:
name: Auto-lint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
token: ${{ secrets.FORMATTER_ACCESS_TOKEN }}
- name: Restore cached node_modules
uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }}
- name: Install missing dependencies
run: yarn install --prefer-offline --frozen-lockfile
- name: Run lint script
run: yarn lint
- name: Commit any lint changes
if: always()
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Auto-remove unused imports
branch: ${{ github.head_ref }}

View File

@ -0,0 +1,17 @@
name: Merge main into main2 on every commit
on:
push:
branches:
- 'main'
jobs:
merge-branch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Merge main -> main2
uses: devmasx/merge-branch@master
with:
type: now
target_branch: main2
github_token: ${{ github.token }}

5
.gitignore vendored
View File

@ -1,4 +1,7 @@
.DS_Store .DS_Store
.idea/
.vercel .vercel
node_modules node_modules
yarn-error.log
firebase-debug.log

14
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"toba.vsfire",
"bradlc.vscode-tailwindcss"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Chrome",
"type": "chrome",
"request": "attach",
"port": 9222, // chrome needs to be started with the parameter "--remote-debugging-port=9222"
"urlFilter": "http://localhost:3000/*",
"webRoot": "${workspaceFolder}/web"
}
]
}

13
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"javascript.preferences.importModuleSpecifier": "shortest",
"typescript.preferences.importModuleSpecifier": "shortest",
"files.eol": "\n",
"search.exclude": {
"**/node_modules": true,
"**/package-lock.json": true,
"**/yarn.lock": true
},
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

View File

@ -1,14 +1,53 @@
# Manifold Markets # Manifold Markets
This [monorepo][] has basically everything involved in running and operating Manifold.
## Getting started
0. Make sure you have [Yarn 1.x][yarn]
1. `$ cd web`
2. `$ yarn`
3. `$ yarn dev:dev`
4. Your site will be available on http://localhost:3000
See [`web/README.md`][web-readme] for more details on hacking on the web client.
## General architecture
Manifold's public API and web app are hosted by [Vercel][vercel]. In general, the web app runs as much as possible on the client; we follow a [JAMStack][jamstack] architecture. All data is stored in Firebase's database, [Cloud Firestore][cloud-firestore]. This is directly accessed by the client for most data access operations.
Operations with complicated contracts (e.g. buying shares) are provided in a separate HTTP API using Firebase's [Cloud Functions][cloud-functions]. Those are deployed separately from the Vercel website; see [`functions/README.md`][functions-readme] for more details.
## Directory overview ## Directory overview
- `web/`: UI and biz logic. Where most of the site lives - `web/`: UI and business logic for the client. Where most of the site lives. The public API endpoints are also in here.
- `functions/`: Firebase cloud functions, for secure work (balances, Stripe payments, emails)
- `common/`: shared between web & functions
- `og-image/`: The OpenGraph image generator; creates the preview images shown on Twitter/social media
## Philosophies - `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.
- [JAMStack](https://jamstack.org/): Keep things simple, no servers - `common/`: Typescript library code shared between `web/` & `functions/`. If you want to look at how the market math
- [Monorepo](https://semaphoreci.com/blog/what-is-monorepo): Good fit for our current size works, most of that's in here (it gets called from the `placeBet` and `sellBet` endpoints in `functions/`.) Also
- [Small PRs](https://google.github.io/eng-practices/review/developer/small-cls.html): Lots of little changes > one big diff 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.
- `docs/`: Manifold's public documentation that lives at https://docs.manifold.markets.
## 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][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.
By contributing to this codebase, you are agreeing to the terms of the [Manifold CLA](https://github.com/manifoldmarkets/manifold/blob/main/.github/CONTRIBUTING.md).
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][discord] as well.
[vercel]: https://vercel.com/
[jamstack]: https://jamstack.org/
[monorepo]: https://semaphoreci.com/blog/what-is-monorepo
[yarn]: https://classic.yarnpkg.com/lang/en/docs/install/
[web-readme]: https://github.com/manifoldmarkets/manifold/blob/main/web/README.md
[functions-readme]: https://github.com/manifoldmarkets/manifold/blob/main/functions/README.md
[cloud-firestore]: https://firebase.google.com/docs/firestore
[cloud-functions]: https://firebase.google.com/docs/functions
[small-prs]: https://google.github.io/eng-practices/review/developer/small-cls.html
[discord]: https://discord.gg/3Zuth9792G

39
common/.eslintrc.js Normal file
View File

@ -0,0 +1,39 @@
module.exports = {
plugins: ['lodash', 'unused-imports'],
extends: ['eslint:recommended'],
ignorePatterns: ['lib'],
env: {
browser: true,
node: true,
},
overrides: [
{
files: ['**/*.ts'],
plugins: ['@typescript-eslint'],
extends: ['plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'unused-imports/no-unused-imports': 'warn',
},
},
],
rules: {
'no-extra-semi': 'off',
'no-constant-condition': ['error', { checkLoops: false }],
'linebreak-style': ['error', 'unix'],
'lodash/import-scope': [2, 'member'],
},
}

3
common/.gitignore vendored
View File

@ -1,6 +1,5 @@
# Compiled JavaScript files # Compiled JavaScript files
lib/**/*.js lib/
lib/**/*.js.map
# TypeScript v1 declaration files # TypeScript v1 declaration files
typings/ typings/

1
common/.yarnrc Normal file
View File

@ -0,0 +1 @@
save-prefix ""

30
common/add-liquidity.ts Normal file
View File

@ -0,0 +1,30 @@
import { getCpmmLiquidity } from './calculate-cpmm'
import { CPMMContract } from './contract'
import { LiquidityProvision } from './liquidity-provision'
export const getNewLiquidityProvision = (
userId: string,
amount: number,
contract: CPMMContract,
newLiquidityProvisionId: string
) => {
const { pool, p, totalLiquidity, subsidyPool } = contract
const liquidity = getCpmmLiquidity(pool, p)
const newLiquidityProvision: LiquidityProvision = {
id: newLiquidityProvisionId,
userId: userId,
contractId: contract.id,
amount,
pool,
p,
liquidity,
createdTime: Date.now(),
}
const newTotalLiquidity = (totalLiquidity ?? 0) + amount
const newSubsidyPool = (subsidyPool ?? 0) + amount
return { newLiquidityProvision, newTotalLiquidity, newSubsidyPool }
}

View File

@ -29,3 +29,5 @@ export const getNoneAnswer = (contractId: string, creator: User) => {
text: 'None', text: 'None',
} }
} }
export const MAX_ANSWER_LENGTH = 240

View File

@ -1,16 +1,30 @@
import { Bet } from './bet' import { range } from 'lodash'
import { getDpmProbability } from './calculate-dpm' import { Bet, NumericBet } from './bet'
import { Binary, CPMM, DPM, FreeResponse, FullContract } from './contract' import { getDpmProbability, getValueFromBucket } from './calculate-dpm'
import {
CPMMBinaryContract,
DPMBinaryContract,
FreeResponseContract,
MultipleChoiceContract,
NumericContract,
} from './contract'
import { User } from './user' import { User } from './user'
import { LiquidityProvision } from './liquidity-provision' import { LiquidityProvision } from './liquidity-provision'
import { noFees } from './fees' import { noFees } from './fees'
import { Answer } from './answer'
export const PHANTOM_ANTE = 0.001 export const HOUSE_LIQUIDITY_PROVIDER_ID = 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // @ManifoldMarkets' id
export const MINIMUM_ANTE = 50 export const DEV_HOUSE_LIQUIDITY_PROVIDER_ID = '94YYTk1AFWfbWMpfYcvnnwI1veP2' // @ManifoldMarkets' id
export const UNIQUE_BETTOR_LIQUIDITY_AMOUNT = 20
type NormalizedBet<T extends Bet = Bet> = Omit<
T,
'userAvatarUrl' | 'userName' | 'userUsername'
>
export function getCpmmInitialLiquidity( export function getCpmmInitialLiquidity(
creator: User, providerId: string,
contract: FullContract<CPMM, Binary>, contract: CPMMBinaryContract,
anteId: string, anteId: string,
amount: number amount: number
) { ) {
@ -18,7 +32,7 @@ export function getCpmmInitialLiquidity(
const lp: LiquidityProvision = { const lp: LiquidityProvision = {
id: anteId, id: anteId,
userId: creator.id, userId: providerId,
contractId: contract.id, contractId: contract.id,
createdTime, createdTime,
isAnte: true, isAnte: true,
@ -34,7 +48,7 @@ export function getCpmmInitialLiquidity(
export function getAnteBets( export function getAnteBets(
creator: User, creator: User,
contract: FullContract<DPM, Binary>, contract: DPMBinaryContract,
yesAnteId: string, yesAnteId: string,
noAnteId: string noAnteId: string
) { ) {
@ -43,7 +57,7 @@ export function getAnteBets(
const { createdTime } = contract const { createdTime } = contract
const yesBet: Bet = { const yesBet: NormalizedBet = {
id: yesAnteId, id: yesAnteId,
userId: creator.id, userId: creator.id,
contractId: contract.id, contractId: contract.id,
@ -57,7 +71,7 @@ export function getAnteBets(
fees: noFees, fees: noFees,
} }
const noBet: Bet = { const noBet: NormalizedBet = {
id: noAnteId, id: noAnteId,
userId: creator.id, userId: creator.id,
contractId: contract.id, contractId: contract.id,
@ -75,8 +89,8 @@ export function getAnteBets(
} }
export function getFreeAnswerAnte( export function getFreeAnswerAnte(
creator: User, anteBettorId: string,
contract: FullContract<DPM, FreeResponse>, contract: FreeResponseContract,
anteBetId: string anteBetId: string
) { ) {
const { totalBets, totalShares } = contract const { totalBets, totalShares } = contract
@ -85,9 +99,9 @@ export function getFreeAnswerAnte(
const { createdTime } = contract const { createdTime } = contract
const anteBet: Bet = { const anteBet: NormalizedBet = {
id: anteBetId, id: anteBetId,
userId: creator.id, userId: anteBettorId,
contractId: contract.id, contractId: contract.id,
amount, amount,
shares, shares,
@ -101,3 +115,86 @@ export function getFreeAnswerAnte(
return anteBet return anteBet
} }
export function getMultipleChoiceAntes(
creator: User,
contract: MultipleChoiceContract,
answers: string[],
betDocIds: string[]
) {
const { totalBets, totalShares } = contract
const amount = totalBets['0']
const shares = totalShares['0']
const p = 1 / answers.length
const { createdTime } = contract
const bets: NormalizedBet[] = answers.map((answer, i) => ({
id: betDocIds[i],
userId: creator.id,
contractId: contract.id,
amount,
shares,
outcome: i.toString(),
probBefore: p,
probAfter: p,
createdTime,
isAnte: true,
fees: noFees,
}))
const { username, name, avatarUrl } = creator
const answerObjects: Answer[] = answers.map((answer, i) => ({
id: i.toString(),
number: i,
contractId: contract.id,
createdTime,
userId: creator.id,
username,
name,
avatarUrl,
text: answer,
}))
return { bets, answerObjects }
}
export function getNumericAnte(
anteBettorId: string,
contract: NumericContract,
ante: number,
newBetId: string
) {
const { bucketCount, createdTime } = contract
const betAnte = ante / bucketCount
const betShares = Math.sqrt(ante ** 2 / bucketCount)
const allOutcomeShares = Object.fromEntries(
range(0, bucketCount).map((_, i) => [i, betShares])
)
const allBetAmounts = Object.fromEntries(
range(0, bucketCount).map((_, i) => [i, betAnte])
)
const anteBet: NormalizedBet<NumericBet> = {
id: newBetId,
userId: anteBettorId,
contractId: contract.id,
amount: ante,
allBetAmounts,
outcome: '0',
value: getValueFromBucket('0', contract),
shares: betShares,
allOutcomeShares,
probBefore: 0,
probAfter: 1 / bucketCount,
createdTime,
isAnte: true,
fees: noFees,
}
return anteBet
}

24
common/api.ts Normal file
View File

@ -0,0 +1,24 @@
import { ENV_CONFIG } from './envs/constants'
export class APIError extends Error {
code: number
details?: unknown
constructor(code: number, message: string, details?: unknown) {
super(message)
this.code = code
this.name = 'APIError'
this.details = details
}
}
export function getFunctionUrl(name: string) {
if (process.env.NEXT_PUBLIC_FUNCTIONS_URL) {
return `${process.env.NEXT_PUBLIC_FUNCTIONS_URL}/${name}`
} else if (process.env.NEXT_PUBLIC_FIREBASE_EMULATE) {
const { projectId, region } = ENV_CONFIG.firebaseConfig
return `http://localhost:5001/${projectId}/${region}/${name}`
} else {
const { cloudRunId, cloudRunRegion } = ENV_CONFIG
return `https://${name}-${cloudRunId}-${cloudRunRegion}.a.run.app`
}
}

123
common/badge.ts Normal file
View File

@ -0,0 +1,123 @@
import { User } from './user'
export type Badge = {
type: BadgeTypes
createdTime: number
data: { [key: string]: any }
name: 'Proven Correct' | 'Streaker' | 'Market Creator'
}
export type BadgeTypes = 'PROVEN_CORRECT' | 'STREAKER' | 'MARKET_CREATOR'
export type ProvenCorrectBadgeData = {
type: 'PROVEN_CORRECT'
data: {
contractSlug: string
contractCreatorUsername: string
contractTitle: string
commentId: string
betAmount: number
}
}
export type MarketCreatorBadgeData = {
type: 'MARKET_CREATOR'
data: {
totalContractsCreated: number
}
}
export type StreakerBadgeData = {
type: 'STREAKER'
data: {
totalBettingStreak: number
}
}
export type ProvenCorrectBadge = Badge & ProvenCorrectBadgeData
export type StreakerBadge = Badge & StreakerBadgeData
export type MarketCreatorBadge = Badge & MarketCreatorBadgeData
export const MINIMUM_UNIQUE_BETTORS_FOR_PROVEN_CORRECT_BADGE = 5
export const provenCorrectRarityThresholds = [1, 1000, 10000]
const calculateProvenCorrectBadgeRarity = (badge: ProvenCorrectBadge) => {
const { betAmount } = badge.data
const thresholdArray = provenCorrectRarityThresholds
let i = thresholdArray.length - 1
while (i >= 0) {
if (betAmount >= thresholdArray[i]) {
return i + 1
}
i--
}
return 1
}
export const streakerBadgeRarityThresholds = [1, 50, 250]
const calculateStreakerBadgeRarity = (badge: StreakerBadge) => {
const { totalBettingStreak } = badge.data
const thresholdArray = streakerBadgeRarityThresholds
let i = thresholdArray.length - 1
while (i >= 0) {
if (totalBettingStreak == thresholdArray[i]) {
return i + 1
}
i--
}
return 1
}
export const marketCreatorBadgeRarityThresholds = [1, 75, 300]
const calculateMarketCreatorBadgeRarity = (badge: MarketCreatorBadge) => {
const { totalContractsCreated } = badge.data
const thresholdArray = marketCreatorBadgeRarityThresholds
let i = thresholdArray.length - 1
while (i >= 0) {
if (totalContractsCreated == thresholdArray[i]) {
return i + 1
}
i--
}
return 1
}
export type rarities = 'bronze' | 'silver' | 'gold'
const rarityRanks: { [key: number]: rarities } = {
1: 'bronze',
2: 'silver',
3: 'gold',
}
export const calculateBadgeRarity = (badge: Badge) => {
switch (badge.type) {
case 'PROVEN_CORRECT':
return rarityRanks[
calculateProvenCorrectBadgeRarity(badge as ProvenCorrectBadge)
]
case 'MARKET_CREATOR':
return rarityRanks[
calculateMarketCreatorBadgeRarity(badge as MarketCreatorBadge)
]
case 'STREAKER':
return rarityRanks[calculateStreakerBadgeRarity(badge as StreakerBadge)]
default:
return rarityRanks[0]
}
}
export const getBadgesByRarity = (user: User | null | undefined) => {
const rarities: { [key in rarities]: number } = {
bronze: 0,
silver: 0,
gold: 0,
}
if (!user) return rarities
Object.values(user.achievements).map((value) => {
value.badges.map((badge) => {
rarities[calculateBadgeRarity(badge)] =
(rarities[calculateBadgeRarity(badge)] ?? 0) + 1
})
})
return rarities
}

View File

@ -3,7 +3,14 @@ import { Fees } from './fees'
export type Bet = { export type Bet = {
id: string id: string
userId: string userId: string
// denormalized for bet lists
userAvatarUrl?: string
userUsername: string
userName: string
contractId: string contractId: string
createdTime: number
amount: number // bet size; negative if SELL bet amount: number // bet size; negative if SELL bet
loanAmount?: number loanAmount?: number
@ -13,20 +20,50 @@ 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
createdTime: number // 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>
export type NumericBet = Bet & {
value: number
allOutcomeShares: { [outcome: string]: number }
allBetAmounts: { [outcome: string]: number }
} }
export const MAX_LOAN_PER_CONTRACT = 20 // Binary market limit order.
export type LimitBet = Bet & LimitProps
type LimitProps = {
orderAmount: number // Amount of limit order.
limitProb: number // [0, 1]. Bet to this probability.
isFilled: boolean // Whether all of the bet amount has been filled.
isCancelled: boolean // Whether to prevent any further fills.
// A record of each transaction that partially (or fully) fills the orderAmount.
// I.e. A limit order could be filled by partially matching with several bets.
// Non-limit orders can also be filled by matching with multiple limit orders.
fills: fill[]
}
export type fill = {
// The id the bet matched against, or null if the bet was matched by the pool.
matchedBetId: string | null
amount: number
shares: number
timestamp: number
// If the fill is a sale, it means the matching bet has shares of the same outcome.
// I.e. -fill.shares === matchedBet.shares
isSale?: boolean
}

View File

@ -1,7 +1,15 @@
import * as _ from 'lodash' import { groupBy, mapValues, sumBy } from 'lodash'
import { LimitBet } from './bet'
import { Binary, CPMM, FullContract } from './contract' import { CREATOR_FEE, Fees, LIQUIDITY_FEE, PLATFORM_FEE } from './fees'
import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees' import { LiquidityProvision } from './liquidity-provision'
import { computeFills } from './new-bet'
import { binarySearch } from './util/algos'
export type CpmmState = {
pool: { [outcome: string]: number }
p: number
}
export function getCpmmProbability( export function getCpmmProbability(
pool: { [outcome: string]: number }, pool: { [outcome: string]: number },
@ -12,11 +20,11 @@ export function getCpmmProbability(
} }
export function getCpmmProbabilityAfterBetBeforeFees( export function getCpmmProbabilityAfterBetBeforeFees(
contract: FullContract<CPMM, Binary>, state: CpmmState,
outcome: string, outcome: string,
bet: number bet: number
) { ) {
const { pool, p } = contract const { pool, p } = state
const shares = calculateCpmmShares(pool, p, bet, outcome) const shares = calculateCpmmShares(pool, p, bet, outcome)
const { YES: y, NO: n } = pool const { YES: y, NO: n } = pool
@ -29,12 +37,12 @@ export function getCpmmProbabilityAfterBetBeforeFees(
} }
export function getCpmmOutcomeProbabilityAfterBet( export function getCpmmOutcomeProbabilityAfterBet(
contract: FullContract<CPMM, Binary>, state: CpmmState,
outcome: string, outcome: string,
bet: number bet: number
) { ) {
const { newPool } = calculateCpmmPurchase(contract, bet, outcome) const { newPool } = calculateCpmmPurchase(state, bet, outcome)
const p = getCpmmProbability(newPool, contract.p) const p = getCpmmProbability(newPool, state.p)
return outcome === 'NO' ? 1 - p : p return outcome === 'NO' ? 1 - p : p
} }
@ -56,12 +64,8 @@ function calculateCpmmShares(
: n + bet - (k * (bet + y) ** -p) ** (1 / (1 - p)) : n + bet - (k * (bet + y) ** -p) ** (1 / (1 - p))
} }
export function getCpmmLiquidityFee( export function getCpmmFees(state: CpmmState, bet: number, outcome: string) {
contract: FullContract<CPMM, Binary>, const prob = getCpmmProbabilityAfterBetBeforeFees(state, outcome, bet)
bet: number,
outcome: string
) {
const prob = getCpmmProbabilityAfterBetBeforeFees(contract, outcome, bet)
const betP = outcome === 'YES' ? 1 - prob : prob const betP = outcome === 'YES' ? 1 - prob : prob
const liquidityFee = LIQUIDITY_FEE * betP * bet const liquidityFee = LIQUIDITY_FEE * betP * bet
@ -72,29 +76,27 @@ export function getCpmmLiquidityFee(
const totalFees = liquidityFee + platformFee + creatorFee const totalFees = liquidityFee + platformFee + creatorFee
const remainingBet = bet - totalFees const remainingBet = bet - totalFees
return { remainingBet, fees } return { remainingBet, totalFees, fees }
} }
export function calculateCpmmSharesAfterFee( export function calculateCpmmSharesAfterFee(
contract: FullContract<CPMM, Binary>, state: CpmmState,
bet: number, bet: number,
outcome: string outcome: string
) { ) {
const { pool, p } = contract const { pool, p } = state
const { remainingBet } = getCpmmLiquidityFee(contract, bet, outcome) const { remainingBet } = getCpmmFees(state, bet, outcome)
return calculateCpmmShares(pool, p, remainingBet, outcome) return calculateCpmmShares(pool, p, remainingBet, outcome)
} }
export function calculateCpmmPurchase( export function calculateCpmmPurchase(
contract: FullContract<CPMM, Binary>, state: CpmmState,
bet: number, bet: number,
outcome: string outcome: string
) { ) {
const { pool, p } = contract const { pool, p } = state
const { remainingBet, fees } = getCpmmLiquidityFee(contract, bet, outcome) const { remainingBet, fees } = getCpmmFees(state, bet, outcome)
// const remainingBet = bet
// const fees = noFees
const shares = calculateCpmmShares(pool, p, remainingBet, outcome) const shares = calculateCpmmShares(pool, p, remainingBet, outcome)
const { YES: y, NO: n } = pool const { YES: y, NO: n } = pool
@ -113,117 +115,125 @@ export function calculateCpmmPurchase(
return { shares, newPool, newP, fees } return { shares, newPool, newP, fees }
} }
function computeK(y: number, n: number, p: number) { // Note: there might be a closed form solution for this.
return y ** p * n ** (1 - p) // If so, feel free to switch out this implementation.
} export function calculateCpmmAmountToProb(
state: CpmmState,
function sellSharesK( prob: number,
y: number,
n: number,
p: number,
s: number,
outcome: 'YES' | 'NO',
b: number
) {
return outcome === 'YES'
? computeK(y - b + s, n - b, p)
: computeK(y - b, n - b + s, p)
}
function calculateCpmmShareValue(
contract: FullContract<CPMM, Binary>,
shares: number,
outcome: 'YES' | 'NO' outcome: 'YES' | 'NO'
) { ) {
const { pool, p } = contract if (prob <= 0 || prob >= 1 || isNaN(prob)) return Infinity
if (outcome === 'NO') prob = 1 - prob
// Find bet amount that preserves k after selling shares. // First, find an upper bound that leads to a more extreme probability than prob.
const k = computeK(pool.YES, pool.NO, p) let maxGuess = 10
const otherPool = outcome === 'YES' ? pool.NO : pool.YES let newProb = 0
do {
maxGuess *= 10
newProb = getCpmmOutcomeProbabilityAfterBet(state, outcome, maxGuess)
} while (newProb < prob)
// Constrain the max sale value to the lessor of 1. shares and 2. the other pool. // Then, binary search for the amount that gets closest to prob.
// This is because 1. the max value per share is M$ 1, const amount = binarySearch(0, maxGuess, (amount) => {
// and 2. The other pool cannot go negative and the sale value is subtracted from it. const newProb = getCpmmOutcomeProbabilityAfterBet(state, outcome, amount)
// (Without this, there are multiple solutions for the same k.) return newProb - prob
let highAmount = Math.min(shares, otherPool) })
let lowAmount = 0
let mid = 0
let kGuess = 0
while (true) {
mid = lowAmount + (highAmount - lowAmount) / 2
// Break once we've reached max precision. return amount
if (mid === lowAmount || mid === highAmount) break
kGuess = sellSharesK(pool.YES, pool.NO, p, shares, outcome, mid)
if (kGuess < k) {
highAmount = mid
} else {
lowAmount = mid
} }
}
return mid function calculateAmountToBuyShares(
state: CpmmState,
shares: number,
outcome: 'YES' | 'NO',
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) {
// Search for amount between bounds (0, shares).
// Min share price is M$0, and max is M$1 each.
return binarySearch(0, shares, (amount) => {
const { takers } = computeFills(
outcome,
amount,
state,
undefined,
unfilledBets,
balanceByUserId
)
const totalShares = sumBy(takers, (taker) => taker.shares)
return totalShares - shares
})
} }
export function calculateCpmmSale( export function calculateCpmmSale(
contract: FullContract<CPMM, Binary>, state: CpmmState,
shares: number, shares: number,
outcome: string outcome: 'YES' | 'NO',
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) { ) {
if (shares < 0) { if (Math.round(shares) < 0) {
throw new Error('Cannot sell non-positive shares') throw new Error('Cannot sell non-positive shares')
} }
const rawSaleValue = calculateCpmmShareValue( const oppositeOutcome = outcome === 'YES' ? 'NO' : 'YES'
contract, const buyAmount = calculateAmountToBuyShares(
state,
shares, shares,
outcome as 'YES' | 'NO' oppositeOutcome,
unfilledBets,
balanceByUserId
) )
const { fees, remainingBet: saleValue } = getCpmmLiquidityFee( const { cpmmState, makers, takers, totalFees, ordersToCancel } = computeFills(
contract, oppositeOutcome,
rawSaleValue, buyAmount,
outcome === 'YES' ? 'NO' : 'YES' state,
undefined,
unfilledBets,
balanceByUserId
) )
const { pool } = contract // Transform buys of opposite outcome into sells.
const { YES: y, NO: n } = pool const saleTakers = takers.map((taker) => ({
...taker,
// You bought opposite shares, which combine with existing shares, removing them.
shares: -taker.shares,
// Opposite shares combine with shares you are selling for M$ of shares.
// You paid taker.amount for the opposite shares.
// Take the negative because this is money you gain.
amount: -(taker.shares - taker.amount),
isSale: true,
}))
const { liquidityFee: fee } = fees const saleValue = -sumBy(saleTakers, (taker) => taker.amount)
const [newY, newN] = return {
outcome === 'YES'
? [y + shares - saleValue + fee, n - saleValue + fee]
: [y - saleValue + fee, n + shares - saleValue + fee]
if (newY < 0 || newN < 0) {
console.log('calculateCpmmSale', {
newY,
newN,
y,
n,
shares,
saleValue, saleValue,
fee, cpmmState,
outcome, fees: totalFees,
}) makers,
throw new Error('Cannot sell more than in pool') takers: saleTakers,
ordersToCancel,
} }
const postBetPool = { YES: newY, NO: newN }
const { newPool, newP } = addCpmmLiquidity(postBetPool, contract.p, fee)
return { saleValue, newPool, newP, fees }
} }
export function getCpmmProbabilityAfterSale( export function getCpmmProbabilityAfterSale(
contract: FullContract<CPMM, Binary>, state: CpmmState,
shares: number, shares: number,
outcome: 'YES' | 'NO' outcome: 'YES' | 'NO',
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) { ) {
const { newPool } = calculateCpmmSale(contract, shares, outcome) const { cpmmState } = calculateCpmmSale(
return getCpmmProbability(newPool, contract.p) state,
shares,
outcome,
unfilledBets,
balanceByUserId
)
return getCpmmProbability(cpmmState.pool, cpmmState.p)
} }
export function getCpmmLiquidity( export function getCpmmLiquidity(
@ -256,22 +266,23 @@ export function addCpmmLiquidity(
return { newPool, liquidity, newP } return { newPool, liquidity, newP }
} }
// export function removeCpmmLiquidity( export function getCpmmLiquidityPoolWeights(liquidities: LiquidityProvision[]) {
// contract: FullContract<CPMM, Binary>, const userAmounts = groupBy(liquidities, (w) => w.userId)
// liquidity: number const totalAmount = sumBy(liquidities, (w) => w.amount)
// ) {
// const { YES, NO } = contract.pool
// const poolLiquidity = getCpmmLiquidity({ YES, NO })
// const p = getCpmmProbability({ YES, NO }, contract.p)
// const f = liquidity / poolLiquidity return mapValues(
// const [payoutYes, payoutNo] = [f * YES, f * NO] userAmounts,
(amounts) => sumBy(amounts, (w) => w.amount) / totalAmount
)
}
// const betAmount = Math.abs(payoutYes - payoutNo) export function getUserLiquidityShares(
// const betOutcome = p >= 0.5 ? 'NO' : 'YES' // opposite side as adding liquidity userId: string,
// const payout = Math.min(payoutYes, payoutNo) state: CpmmState,
liquidities: LiquidityProvision[]
) {
const weights = getCpmmLiquidityPoolWeights(liquidities)
const userWeight = weights[userId] ?? 0
// const newPool = { YES: YES - payoutYes, NO: NO - payoutNo } return mapValues(state.pool, (shares) => userWeight * shares)
}
// return { newPool, payout, betAmount, betOutcome }
// }

View File

@ -1,7 +1,9 @@
import * as _ from 'lodash' import { cloneDeep, range, sum, sumBy, sortBy, mapValues } from 'lodash'
import { Bet } from './bet' import { Bet, NumericBet } from './bet'
import { Binary, DPM, FreeResponse, FullContract } from './contract' import { DPMContract, DPMBinaryContract, NumericContract } from './contract'
import { DPM_FEES } from './fees' import { DPM_FEES } from './fees'
import { normpdf } from './util/math'
import { addObjects } from './util/object'
export function getDpmProbability(totalShares: { [outcome: string]: number }) { export function getDpmProbability(totalShares: { [outcome: string]: number }) {
// For binary contracts only. // For binary contracts only.
@ -14,11 +16,94 @@ export function getDpmOutcomeProbability(
}, },
outcome: string outcome: string
) { ) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
const shares = totalShares[outcome] ?? 0 const shares = totalShares[outcome] ?? 0
return shares ** 2 / squareSum return shares ** 2 / squareSum
} }
export function getDpmOutcomeProbabilities(totalShares: {
[outcome: string]: number
}) {
const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
return mapValues(totalShares, (shares) => shares ** 2 / squareSum)
}
export function getNumericBets(
contract: NumericContract,
bucket: string,
betAmount: number,
variance: number
) {
const { bucketCount } = contract
const bucketNumber = parseInt(bucket)
const buckets = range(0, bucketCount)
const mean = bucketNumber / bucketCount
const allDensities = buckets.map((i) =>
normpdf(i / bucketCount, mean, variance)
)
const densitySum = sum(allDensities)
const rawBetAmounts = allDensities
.map((d) => (d / densitySum) * betAmount)
.map((x) => (x >= 1 / bucketCount ? x : 0))
const rawSum = sum(rawBetAmounts)
const scaledBetAmounts = rawBetAmounts.map((x) => (x / rawSum) * betAmount)
const bets = scaledBetAmounts
.map((x, i) => (x > 0 ? [i.toString(), x] : undefined))
.filter((x) => x != undefined) as [string, number][]
return bets
}
export const getMappedBucket = (value: number, contract: NumericContract) => {
const { bucketCount, min, max } = contract
const index = Math.floor(((value - min) / (max - min)) * bucketCount)
const bucket = Math.max(Math.min(index, bucketCount - 1), 0)
return `${bucket}`
}
export const getValueFromBucket = (
bucket: string,
contract: NumericContract
) => {
const { bucketCount, min, max } = contract
const index = parseInt(bucket)
const value = min + (index / bucketCount) * (max - min)
const rounded = Math.round(value * 1e4) / 1e4
return rounded
}
export const getExpectedValue = (contract: NumericContract) => {
const { bucketCount, min, max, totalShares } = contract
const totalShareSum = sumBy(
Object.values(totalShares),
(shares) => shares ** 2
)
const probs = range(0, bucketCount).map(
(i) => totalShares[i] ** 2 / totalShareSum
)
const values = range(0, bucketCount).map(
(i) =>
// use mid point within bucket
0.5 * (min + (i / bucketCount) * (max - min)) +
0.5 * (min + ((i + 1) / bucketCount) * (max - min))
)
const weightedValues = range(0, bucketCount).map((i) => probs[i] * values[i])
const expectation = sum(weightedValues)
const rounded = Math.round(expectation * 1e2) / 1e2
return rounded
}
export function getDpmOutcomeProbabilityAfterBet( export function getDpmOutcomeProbabilityAfterBet(
totalShares: { totalShares: {
[outcome: string]: number [outcome: string]: number
@ -55,7 +140,7 @@ export function calculateDpmShares(
bet: number, bet: number,
betChoice: string betChoice: string
) { ) {
const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) const squareSum = sumBy(Object.values(totalShares), (shares) => shares ** 2)
const shares = totalShares[betChoice] ?? 0 const shares = totalShares[betChoice] ?? 0
const c = 2 * bet * Math.sqrt(squareSum) const c = 2 * bet * Math.sqrt(squareSum)
@ -63,6 +148,30 @@ export function calculateDpmShares(
return Math.sqrt(bet ** 2 + shares ** 2 + c) - shares return Math.sqrt(bet ** 2 + shares ** 2 + c) - shares
} }
export function calculateNumericDpmShares(
totalShares: {
[outcome: string]: number
},
bets: [string, number][]
) {
const shares: number[] = []
totalShares = cloneDeep(totalShares)
const order = sortBy(
bets.map(([, amount], i) => [amount, i]),
([amount]) => amount
).map(([, i]) => i)
for (const i of order) {
const [bucket, bet] = bets[i]
shares[i] = calculateDpmShares(totalShares, bet, bucket)
totalShares = addObjects(totalShares, { [bucket]: shares[i] })
}
return { shares, totalShares }
}
export function calculateDpmRawShareValue( export function calculateDpmRawShareValue(
totalShares: { totalShares: {
[outcome: string]: number [outcome: string]: number
@ -71,11 +180,11 @@ export function calculateDpmRawShareValue(
betChoice: string betChoice: string
) { ) {
const currentValue = Math.sqrt( const currentValue = Math.sqrt(
_.sumBy(Object.values(totalShares), (shares) => shares ** 2) sumBy(Object.values(totalShares), (shares) => shares ** 2)
) )
const postSaleValue = Math.sqrt( const postSaleValue = Math.sqrt(
_.sumBy(Object.keys(totalShares), (outcome) => sumBy(Object.keys(totalShares), (outcome) =>
outcome === betChoice outcome === betChoice
? Math.max(0, totalShares[outcome] - shares) ** 2 ? Math.max(0, totalShares[outcome] - shares) ** 2
: totalShares[outcome] ** 2 : totalShares[outcome] ** 2
@ -86,7 +195,7 @@ export function calculateDpmRawShareValue(
} }
export function calculateDpmMoneyRatio( export function calculateDpmMoneyRatio(
contract: FullContract<DPM, any>, contract: DPMContract,
bet: Bet, bet: Bet,
shareValue: number shareValue: number
) { ) {
@ -95,12 +204,12 @@ export function calculateDpmMoneyRatio(
const p = getDpmOutcomeProbability(totalShares, outcome) const p = getDpmOutcomeProbability(totalShares, outcome)
const actual = _.sum(Object.values(pool)) - shareValue const actual = sum(Object.values(pool)) - shareValue
const betAmount = p * amount const betAmount = p * amount
const expected = const expected =
_.sumBy( sumBy(
Object.keys(totalBets), Object.keys(totalBets),
(outcome) => (outcome) =>
getDpmOutcomeProbability(totalShares, outcome) * getDpmOutcomeProbability(totalShares, outcome) *
@ -112,10 +221,7 @@ export function calculateDpmMoneyRatio(
return actual / expected return actual / expected
} }
export function calculateDpmShareValue( export function calculateDpmShareValue(contract: DPMContract, bet: Bet) {
contract: FullContract<DPM, any>,
bet: Bet
) {
const { pool, totalShares } = contract const { pool, totalShares } = contract
const { shares, outcome } = bet const { shares, outcome } = bet
@ -127,17 +233,14 @@ export function calculateDpmShareValue(
return adjShareValue return adjShareValue
} }
export function calculateDpmSaleAmount( export function calculateDpmSaleAmount(contract: DPMContract, bet: Bet) {
contract: FullContract<DPM, any>,
bet: Bet
) {
const { amount } = bet const { amount } = bet
const winnings = calculateDpmShareValue(contract, bet) const winnings = calculateDpmShareValue(contract, bet)
return deductDpmFees(amount, winnings) return deductDpmFees(amount, winnings)
} }
export function calculateDpmPayout( export function calculateDpmPayout(
contract: FullContract<DPM, any>, contract: DPMContract,
bet: Bet, bet: Bet,
outcome: string outcome: string
) { ) {
@ -147,43 +250,52 @@ export function calculateDpmPayout(
return calculateStandardDpmPayout(contract, bet, outcome) return calculateStandardDpmPayout(contract, bet, outcome)
} }
export function calculateDpmCancelPayout( export function calculateDpmCancelPayout(contract: DPMContract, bet: Bet) {
contract: FullContract<DPM, any>,
bet: Bet
) {
const { totalBets, pool } = contract const { totalBets, pool } = contract
const betTotal = _.sum(Object.values(totalBets)) const betTotal = sum(Object.values(totalBets))
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
return (bet.amount / betTotal) * poolTotal return (bet.amount / betTotal) * poolTotal
} }
export function calculateStandardDpmPayout( export function calculateStandardDpmPayout(
contract: FullContract<DPM, any>, contract: DPMContract,
bet: Bet, bet: Bet,
outcome: string outcome: string
) { ) {
const { amount, outcome: betOutcome, shares } = bet const { outcome: betOutcome } = bet
if (betOutcome !== outcome) return 0 const isNumeric = contract.outcomeType === 'NUMERIC'
if (!isNumeric && betOutcome !== outcome) return 0
const shares = isNumeric
? ((bet as NumericBet).allOutcomeShares ?? {})[outcome]
: bet.shares
if (!shares) return 0
const { totalShares, phantomShares, pool } = contract const { totalShares, phantomShares, pool } = contract
if (!totalShares[outcome]) return 0 if (!totalShares[outcome]) return 0
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
const total = const total =
totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0) totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0)
const winnings = (shares / total) * poolTotal const winnings = (shares / total) * poolTotal
// profit can be negative if using phantom shares
return amount + (1 - DPM_FEES) * Math.max(0, winnings - amount) const amount = isNumeric
? (bet as NumericBet).allBetAmounts[outcome]
: bet.amount
const payout = amount + (1 - DPM_FEES) * Math.max(0, winnings - amount)
return payout
} }
export function calculateDpmPayoutAfterCorrectBet( export function calculateDpmPayoutAfterCorrectBet(
contract: FullContract<DPM, any>, contract: DPMContract,
bet: Bet bet: Bet
) { ) {
const { totalShares, pool, totalBets } = contract const { totalShares, pool, totalBets, outcomeType } = contract
const { shares, amount, outcome } = bet const { shares, amount, outcome } = bet
const prevShares = totalShares[outcome] ?? 0 const prevShares = totalShares[outcome] ?? 0
@ -204,52 +316,57 @@ export function calculateDpmPayoutAfterCorrectBet(
...totalBets, ...totalBets,
[outcome]: prevTotalBet + amount, [outcome]: prevTotalBet + amount,
}, },
outcomeType:
outcomeType === 'NUMERIC'
? 'FREE_RESPONSE' // hack to show payout at particular bet point estimate
: outcomeType,
} }
return calculateStandardDpmPayout(newContract, bet, outcome) return calculateStandardDpmPayout(newContract as any, bet, outcome)
} }
function calculateMktDpmPayout(contract: FullContract<DPM, any>, bet: Bet) { function calculateMktDpmPayout(contract: DPMContract, bet: Bet) {
if (contract.outcomeType === 'BINARY') if (contract.outcomeType === 'BINARY')
return calculateBinaryMktDpmPayout(contract, bet) return calculateBinaryMktDpmPayout(contract, bet)
const { totalShares, pool, resolutions } = contract as FullContract< const { totalShares, pool, resolutions, outcomeType } = contract
DPM,
FreeResponse
>
let probs: { [outcome: string]: number } let probs: { [outcome: string]: number }
if (resolutions) { if (resolutions) {
const probTotal = _.sum(Object.values(resolutions)) const probTotal = sum(Object.values(resolutions))
probs = _.mapValues( probs = mapValues(
totalShares, totalShares,
(_, outcome) => (resolutions[outcome] ?? 0) / probTotal (_, outcome) => (resolutions[outcome] ?? 0) / probTotal
) )
} else { } else {
const squareSum = _.sum( const squareSum = sum(
Object.values(totalShares).map((shares) => shares ** 2) Object.values(totalShares).map((shares) => shares ** 2)
) )
probs = _.mapValues(totalShares, (shares) => shares ** 2 / squareSum) probs = mapValues(totalShares, (shares) => shares ** 2 / squareSum)
} }
const weightedShareTotal = _.sumBy(Object.keys(totalShares), (outcome) => {
return probs[outcome] * totalShares[outcome]
})
const { outcome, amount, shares } = bet const { outcome, amount, shares } = bet
const totalPool = _.sum(Object.values(pool)) const poolFrac =
const poolFrac = (probs[outcome] * shares) / weightedShareTotal outcomeType === 'NUMERIC'
const winnings = poolFrac * totalPool ? sumBy(
Object.keys((bet as NumericBet).allOutcomeShares ?? {}),
(outcome) => {
return (
(probs[outcome] * (bet as NumericBet).allOutcomeShares[outcome]) /
totalShares[outcome]
)
}
)
: (probs[outcome] * shares) / totalShares[outcome]
const totalPool = sum(Object.values(pool))
const winnings = poolFrac * totalPool
return deductDpmFees(amount, winnings) return deductDpmFees(amount, winnings)
} }
function calculateBinaryMktDpmPayout( function calculateBinaryMktDpmPayout(contract: DPMBinaryContract, bet: Bet) {
contract: FullContract<DPM, Binary>,
bet: Bet
) {
const { resolutionProbability, totalShares, phantomShares } = contract const { resolutionProbability, totalShares, phantomShares } = contract
const p = const p =
resolutionProbability !== undefined resolutionProbability !== undefined
@ -270,7 +387,7 @@ function calculateBinaryMktDpmPayout(
return deductDpmFees(amount, winnings) return deductDpmFees(amount, winnings)
} }
export function resolvedDpmPayout(contract: FullContract<DPM, any>, bet: Bet) { export function resolvedDpmPayout(contract: DPMContract, bet: Bet) {
if (contract.resolution) if (contract.resolution)
return calculateDpmPayout(contract, bet, contract.resolution) return calculateDpmPayout(contract, bet, contract.resolution)
throw new Error('Contract was not resolved') throw new Error('Contract was not resolved')

View File

@ -1,9 +1,9 @@
import { Bet } from './bet' import { Bet } from './bet'
import { getProbability } from './calculate' import { getProbability } from './calculate'
import { Binary, FixedPayouts, FullContract } from './contract' import { CPMMContract } from './contract'
export function calculateFixedPayout( export function calculateFixedPayout(
contract: FullContract<FixedPayouts, Binary>, contract: CPMMContract,
bet: Bet, bet: Bet,
outcome: string outcome: string
) { ) {
@ -23,10 +23,7 @@ export function calculateStandardFixedPayout(bet: Bet, outcome: string) {
return shares return shares
} }
function calculateFixedMktPayout( function calculateFixedMktPayout(contract: CPMMContract, bet: Bet) {
contract: FullContract<FixedPayouts, Binary>,
bet: Bet
) {
const { resolutionProbability } = contract const { resolutionProbability } = contract
const p = const p =
resolutionProbability !== undefined resolutionProbability !== undefined

315
common/calculate-metrics.ts Normal file
View File

@ -0,0 +1,315 @@
import { Dictionary, groupBy, last, partition, sum, sumBy, uniq } from 'lodash'
import { calculatePayout, getContractBetMetrics } from './calculate'
import { Bet, LimitBet } from './bet'
import {
Contract,
CPMMBinaryContract,
CPMMContract,
DPMContract,
} from './contract'
import { PortfolioMetrics, User } from './user'
import { DAY_MS } from './util/time'
import { getBinaryCpmmBetInfo, getNewMultiBetInfo } from './new-bet'
import { getCpmmProbability } from './calculate-cpmm'
import { removeUndefinedProps } from './util/object'
const computeInvestmentValue = (
bets: Bet[],
contractsDict: { [k: string]: Contract }
) => {
return sumBy(bets, (bet) => {
const contract = contractsDict[bet.contractId]
if (!contract || contract.isResolved) return 0
if (bet.sale || bet.isSold) return 0
const payout = calculatePayout(contract, bet, 'MKT')
const value = payout - (bet.loanAmount ?? 0)
if (isNaN(value)) return 0
return value
})
}
export const computeInvestmentValueCustomProb = (
bets: Bet[],
contract: Contract,
p: number
) => {
return sumBy(bets, (bet) => {
if (!contract || contract.isResolved) return 0
if (bet.sale || bet.isSold) return 0
const { outcome, shares } = bet
const betP = outcome === 'YES' ? p : 1 - p
const value = betP * shares
if (isNaN(value)) return 0
return value
})
}
export const computeElasticity = (
bets: Bet[],
contract: Contract,
betAmount = 50
) => {
const { mechanism, outcomeType } = contract
return mechanism === 'cpmm-1' &&
(outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC')
? computeBinaryCpmmElasticity(bets, contract, betAmount)
: computeDpmElasticity(contract, betAmount)
}
export const computeBinaryCpmmElasticity = (
bets: Bet[],
contract: CPMMContract,
betAmount: number
) => {
const limitBets = bets
.filter(
(b) =>
!b.isFilled &&
!b.isSold &&
!b.isRedemption &&
!b.sale &&
!b.isCancelled &&
b.limitProb !== undefined
)
.sort((a, b) => a.createdTime - b.createdTime) as LimitBet[]
const userIds = uniq(limitBets.map((b) => b.userId))
// Assume all limit orders are good.
const userBalances = Object.fromEntries(
userIds.map((id) => [id, Number.MAX_SAFE_INTEGER])
)
const { newPool: poolY, newP: pY } = getBinaryCpmmBetInfo(
'YES',
betAmount,
contract,
undefined,
limitBets,
userBalances
)
const resultYes = getCpmmProbability(poolY, pY)
const { newPool: poolN, newP: pN } = getBinaryCpmmBetInfo(
'NO',
betAmount,
contract,
undefined,
limitBets,
userBalances
)
const resultNo = getCpmmProbability(poolN, pN)
// handle AMM overflow
const safeYes = Number.isFinite(resultYes) ? resultYes : 1
const safeNo = Number.isFinite(resultNo) ? resultNo : 0
return safeYes - safeNo
}
export const computeDpmElasticity = (
contract: DPMContract,
betAmount: number
) => {
return getNewMultiBetInfo('', 2 * betAmount, contract).newBet.probAfter
}
const computeTotalPool = (userContracts: Contract[], startTime = 0) => {
const periodFilteredContracts = userContracts.filter(
(contract) => contract.createdTime >= startTime
)
return sum(
periodFilteredContracts.map((contract) => sum(Object.values(contract.pool)))
)
}
export const computeVolume = (contractBets: Bet[], since: number) => {
return sumBy(contractBets, (b) =>
b.createdTime > since && !b.isRedemption ? Math.abs(b.amount) : 0
)
}
const calculateProbChangeSince = (descendingBets: Bet[], since: number) => {
const newestBet = descendingBets[0]
if (!newestBet) return 0
const betBeforeSince = descendingBets.find((b) => b.createdTime < since)
if (!betBeforeSince) {
const oldestBet = last(descendingBets) ?? newestBet
return newestBet.probAfter - oldestBet.probBefore
}
return newestBet.probAfter - betBeforeSince.probAfter
}
export const calculateProbChanges = (descendingBets: Bet[]) => {
const now = Date.now()
const yesterday = now - DAY_MS
const weekAgo = now - 7 * DAY_MS
const monthAgo = now - 30 * DAY_MS
return {
day: calculateProbChangeSince(descendingBets, yesterday),
week: calculateProbChangeSince(descendingBets, weekAgo),
month: calculateProbChangeSince(descendingBets, monthAgo),
}
}
export const calculateCreatorVolume = (userContracts: Contract[]) => {
const allTimeCreatorVolume = computeTotalPool(userContracts, 0)
const monthlyCreatorVolume = computeTotalPool(
userContracts,
Date.now() - 30 * DAY_MS
)
const weeklyCreatorVolume = computeTotalPool(
userContracts,
Date.now() - 7 * DAY_MS
)
const dailyCreatorVolume = computeTotalPool(
userContracts,
Date.now() - 1 * DAY_MS
)
return {
daily: dailyCreatorVolume,
weekly: weeklyCreatorVolume,
monthly: monthlyCreatorVolume,
allTime: allTimeCreatorVolume,
}
}
export const calculateNewPortfolioMetrics = (
user: User,
contractsById: { [k: string]: Contract },
currentBets: Bet[]
) => {
const investmentValue = computeInvestmentValue(currentBets, contractsById)
const newPortfolio = {
investmentValue: investmentValue,
balance: user.balance,
totalDeposits: user.totalDeposits,
timestamp: Date.now(),
userId: user.id,
}
return newPortfolio
}
const calculateProfitForPeriod = (
startingPortfolio: PortfolioMetrics | undefined,
currentProfit: number
) => {
if (startingPortfolio === undefined) {
return currentProfit
}
const startingProfit = calculatePortfolioProfit(startingPortfolio)
return currentProfit - startingProfit
}
export const calculatePortfolioProfit = (portfolio: PortfolioMetrics) => {
return portfolio.investmentValue + portfolio.balance - portfolio.totalDeposits
}
export const calculateNewProfit = (
portfolioHistory: Record<
'current' | 'day' | 'week' | 'month',
PortfolioMetrics | undefined
>,
newPortfolio: PortfolioMetrics
) => {
const allTimeProfit = calculatePortfolioProfit(newPortfolio)
const newProfit = {
daily: calculateProfitForPeriod(portfolioHistory.day, allTimeProfit),
weekly: calculateProfitForPeriod(portfolioHistory.week, allTimeProfit),
monthly: calculateProfitForPeriod(portfolioHistory.month, allTimeProfit),
allTime: allTimeProfit,
}
return newProfit
}
export const calculateMetricsByContract = (
bets: Bet[],
contractsById: Dictionary<Contract>
) => {
const betsByContract = groupBy(bets, (bet) => bet.contractId)
const unresolvedContracts = Object.keys(betsByContract)
.map((cid) => contractsById[cid])
.filter((c) => c && !c.isResolved)
return unresolvedContracts.map((c) => {
const bets = betsByContract[c.id] ?? []
const current = getContractBetMetrics(c, bets)
let periodMetrics
if (c.mechanism === 'cpmm-1' && c.outcomeType === 'BINARY') {
const periods = ['day', 'week', 'month'] as const
periodMetrics = Object.fromEntries(
periods.map((period) => [
period,
calculatePeriodProfit(c, bets, period),
])
)
}
return removeUndefinedProps({
contractId: c.id,
...current,
from: periodMetrics,
})
})
}
export type ContractMetrics = ReturnType<
typeof calculateMetricsByContract
>[number]
const calculatePeriodProfit = (
contract: CPMMBinaryContract,
bets: Bet[],
period: 'day' | 'week' | 'month'
) => {
const days = period === 'day' ? 1 : period === 'week' ? 7 : 30
const fromTime = Date.now() - days * DAY_MS
const [previousBets, recentBets] = partition(
bets,
(b) => b.createdTime < fromTime
)
const prevProb = contract.prob - contract.probChanges[period]
const prob = contract.resolutionProbability
? contract.resolutionProbability
: contract.prob
const previousBetsValue = computeInvestmentValueCustomProb(
previousBets,
contract,
prevProb
)
const currentBetsValue = computeInvestmentValueCustomProb(
previousBets,
contract,
prob
)
const { profit: recentProfit, invested: recentInvested } =
getContractBetMetrics(contract, recentBets)
const profit = currentBetsValue - previousBetsValue + recentProfit
const invested = previousBetsValue + recentInvested
const profitPercent = invested === 0 ? 0 : 100 * (profit / invested)
return {
profit,
profitPercent,
invested,
prevValue: previousBetsValue,
value: currentBetsValue,
}
}

View File

@ -1,5 +1,5 @@
import _ from 'lodash' import { maxBy, partition, sortBy, sum, sumBy } from 'lodash'
import { Bet } from './bet' import { Bet, LimitBet } from './bet'
import { import {
calculateCpmmSale, calculateCpmmSale,
getCpmmProbability, getCpmmProbability,
@ -18,23 +18,35 @@ import {
getDpmProbabilityAfterSale, getDpmProbabilityAfterSale,
} from './calculate-dpm' } from './calculate-dpm'
import { calculateFixedPayout } from './calculate-fixed-payouts' import { calculateFixedPayout } from './calculate-fixed-payouts'
import { Binary, Contract, CPMM, DPM, FullContract } from './contract' import {
Contract,
BinaryContract,
FreeResponseContract,
PseudoNumericContract,
MultipleChoiceContract,
} from './contract'
import { floatingEqual } from './util/math'
export function getProbability(contract: FullContract<DPM | CPMM, Binary>) { export function getProbability(
contract: BinaryContract | PseudoNumericContract
) {
return contract.mechanism === 'cpmm-1' return contract.mechanism === 'cpmm-1'
? getCpmmProbability(contract.pool, contract.p) ? getCpmmProbability(contract.pool, contract.p)
: getDpmProbability(contract.totalShares) : getDpmProbability(contract.totalShares)
} }
export function getInitialProbability( export function getInitialProbability(
contract: FullContract<DPM | CPMM, Binary> contract: BinaryContract | PseudoNumericContract
) { ) {
return ( if (contract.initialProbability) return contract.initialProbability
contract.initialProbability ??
(contract.mechanism === 'cpmm-1' if (contract.mechanism === 'dpm-2' || (contract as any).totalShares)
? getCpmmProbability(contract.pool, contract.p) // use totalShares to calculate prob for ported contracts
: getDpmProbability(contract.phantomShares ?? contract.totalShares)) return getDpmProbability(
(contract as any).phantomShares ?? (contract as any).totalShares
) )
return getCpmmProbability(contract.pool, contract.p)
} }
export function getOutcomeProbability(contract: Contract, outcome: string) { export function getOutcomeProbability(contract: Contract, outcome: string) {
@ -49,11 +61,7 @@ export function getOutcomeProbabilityAfterBet(
bet: number bet: number
) { ) {
return contract.mechanism === 'cpmm-1' return contract.mechanism === 'cpmm-1'
? getCpmmOutcomeProbabilityAfterBet( ? getCpmmOutcomeProbabilityAfterBet(contract, outcome, bet)
contract as FullContract<CPMM, Binary>,
outcome,
bet
)
: getDpmOutcomeProbabilityAfterBet(contract.totalShares, outcome, bet) : getDpmOutcomeProbabilityAfterBet(contract.totalShares, outcome, bet)
} }
@ -63,17 +71,26 @@ export function calculateShares(
betChoice: string betChoice: string
) { ) {
return contract.mechanism === 'cpmm-1' return contract.mechanism === 'cpmm-1'
? calculateCpmmSharesAfterFee( ? calculateCpmmSharesAfterFee(contract, bet, betChoice)
contract as FullContract<CPMM, Binary>,
bet,
betChoice
)
: calculateDpmShares(contract.totalShares, bet, betChoice) : calculateDpmShares(contract.totalShares, bet, betChoice)
} }
export function calculateSaleAmount(contract: Contract, bet: Bet) { export function calculateSaleAmount(
return contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY' contract: Contract,
? calculateCpmmSale(contract, Math.abs(bet.shares), bet.outcome).saleValue bet: Bet,
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) {
return contract.mechanism === 'cpmm-1' &&
(contract.outcomeType === 'BINARY' ||
contract.outcomeType === 'PSEUDO_NUMERIC')
? calculateCpmmSale(
contract,
Math.abs(bet.shares),
bet.outcome as 'YES' | 'NO',
unfilledBets,
balanceByUserId
).saleValue
: calculateDpmSaleAmount(contract, bet) : calculateDpmSaleAmount(contract, bet)
} }
@ -86,19 +103,25 @@ export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) {
export function getProbabilityAfterSale( export function getProbabilityAfterSale(
contract: Contract, contract: Contract,
outcome: string, outcome: string,
shares: number shares: number,
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) { ) {
return contract.mechanism === 'cpmm-1' return contract.mechanism === 'cpmm-1'
? getCpmmProbabilityAfterSale( ? getCpmmProbabilityAfterSale(
contract as FullContract<CPMM, Binary>, contract,
shares, shares,
outcome as 'YES' | 'NO' outcome as 'YES' | 'NO',
unfilledBets,
balanceByUserId
) )
: getDpmProbabilityAfterSale(contract.totalShares, outcome, shares) : getDpmProbabilityAfterSale(contract.totalShares, outcome, shares)
} }
export function calculatePayout(contract: Contract, bet: Bet, outcome: string) { export function calculatePayout(contract: Contract, bet: Bet, outcome: string) {
return contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY' return contract.mechanism === 'cpmm-1' &&
(contract.outcomeType === 'BINARY' ||
contract.outcomeType === 'PSEUDO_NUMERIC')
? calculateFixedPayout(contract, bet, outcome) ? calculateFixedPayout(contract, bet, outcome)
: calculateDpmPayout(contract, bet, outcome) : calculateDpmPayout(contract, bet, outcome)
} }
@ -107,33 +130,83 @@ export function resolvedPayout(contract: Contract, bet: Bet) {
const outcome = contract.resolution const outcome = contract.resolution
if (!outcome) throw new Error('Contract not resolved') if (!outcome) throw new Error('Contract not resolved')
return contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY' return contract.mechanism === 'cpmm-1' &&
(contract.outcomeType === 'BINARY' ||
contract.outcomeType === 'PSEUDO_NUMERIC')
? calculateFixedPayout(contract, bet, outcome) ? calculateFixedPayout(contract, bet, outcome)
: 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 (floatingEqual(shares, 0)) continue
const spent = totalSpent[outcome] ?? 0
const position = totalShares[outcome] ?? 0
if (amount > 0) {
totalShares[outcome] = position + shares
totalSpent[outcome] = spent + amount
} else if (amount < 0) {
const averagePrice = position === 0 ? 0 : spent / position
totalShares[outcome] = position + shares
totalSpent[outcome] = spent + averagePrice * shares
}
}
return sum([0, ...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 type ContractBetMetrics = ReturnType<typeof getContractBetMetrics>
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 invested = 0 let totalInvested = 0
let salesInvested = 0
let payout = 0 let payout = 0
let loan = 0 let loan = 0
let sellProfit = 0 let saleValue = 0
let redeemed = 0 let redeemed = 0
const totalShares: { [outcome: string]: number } = {}
for (const bet of yourBets) { for (const bet of yourBets) {
const { isSold, sale, amount, loanAmount, isRedemption } = bet const { isSold, sale, amount, loanAmount, isRedemption, shares, outcome } =
bet
totalShares[outcome] = (totalShares[outcome] ?? 0) + shares
if (isSold) { if (isSold) {
sellProfit -= amount totalInvested += amount
salesInvested += amount
} else if (sale) { } else if (sale) {
sellProfit += sale.amount saleValue += sale.amount
} else { } else {
if (isRedemption) { if (isRedemption) {
redeemed += -1 * amount redeemed += -1 * amount
} else if (amount > 0) { } else if (amount > 0) {
invested += amount totalInvested += amount
} else {
saleValue -= amount
} }
loan += loanAmount ?? 0 loan += loanAmount ?? 0
@ -143,30 +216,87 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
} }
} }
const investedIncludingSales = invested + salesInvested const profit = payout + saleValue + redeemed - totalInvested
const profitPercent = totalInvested === 0 ? 0 : (profit / totalInvested) * 100
const totalValue = payout + sellProfit + redeemed const invested = isCpmm ? getCpmmInvested(yourBets) : getDpmInvested(yourBets)
const profit = totalValue - invested const hasShares = Object.values(totalShares).some(
const profitPercent = (profit / investedIncludingSales) * 100 (shares) => !floatingEqual(shares, 0)
const netInvestment = payout - loan )
return { return {
invested, invested,
loan,
payout, payout,
totalValue,
profit, profit,
profitPercent, profitPercent,
netInvestment, totalShares,
hasShares,
} }
} }
export function getContractBetNullMetrics() { export function getContractBetNullMetrics() {
return { return {
invested: 0, invested: 0,
loan: 0,
payout: 0, payout: 0,
totalValue: 0,
profit: 0, profit: 0,
profitPercent: 0, profitPercent: 0,
netInvestment: 0, totalShares: {} as { [outcome: string]: number },
hasShares: false,
} }
} }
export function getTopAnswer(
contract: FreeResponseContract | MultipleChoiceContract
) {
const { answers } = contract
const top = maxBy(
answers?.map((answer) => ({
answer,
prob: getOutcomeProbability(contract, answer.id),
})),
({ prob }) => prob
)
return top?.answer
}
export function getLargestPosition(contract: Contract, userBets: Bet[]) {
let yesFloorShares = 0,
yesShares = 0,
noShares = 0,
noFloorShares = 0
if (userBets.length === 0) {
return null
}
if (contract.outcomeType === 'FREE_RESPONSE') {
const answerCounts: { [outcome: string]: number } = {}
for (const bet of userBets) {
if (bet.outcome) {
if (!answerCounts[bet.outcome]) {
answerCounts[bet.outcome] = bet.amount
} else {
answerCounts[bet.outcome] += bet.amount
}
}
}
const majorityAnswer =
maxBy(Object.keys(answerCounts), (outcome) => answerCounts[outcome]) ?? ''
return {
prob: undefined,
shares: answerCounts[majorityAnswer] || 0,
outcome: majorityAnswer,
}
}
const [yesBets, noBets] = partition(userBets, (bet) => bet.outcome === 'YES')
yesShares = sumBy(yesBets, (bet) => bet.shares)
noShares = sumBy(noBets, (bet) => bet.shares)
yesFloorShares = Math.floor(yesShares)
noFloorShares = Math.floor(noShares)
const shares = yesFloorShares || noFloorShares
const outcome = yesFloorShares > noFloorShares ? 'YES' : 'NO'
return { shares, outcome }
}

43
common/categories.ts Normal file
View File

@ -0,0 +1,43 @@
import { difference } from 'lodash'
export const CATEGORIES_GROUP_SLUG_POSTFIX = '-default'
export const CATEGORIES = {
politics: 'Politics',
technology: 'Technology',
science: 'Science',
world: 'World',
sports: 'Sports',
economics: 'Economics',
personal: 'Personal',
culture: 'Culture',
manifold: 'Manifold',
covid: 'Covid',
crypto: 'Crypto',
gaming: 'Gaming',
fun: 'Fun',
}
export type category = keyof typeof CATEGORIES
export const TO_CATEGORY = Object.fromEntries(
Object.entries(CATEGORIES).map(([k, v]) => [v, k])
)
export const CATEGORY_LIST = Object.keys(CATEGORIES)
export const EXCLUDED_CATEGORIES: category[] = [
'fun',
'manifold',
'personal',
'covid',
'gaming',
'crypto',
]
export const DEFAULT_CATEGORIES = difference(CATEGORY_LIST, EXCLUDED_CATEGORIES)
export const DEFAULT_CATEGORY_GROUPS = DEFAULT_CATEGORIES.map((c) => ({
slug: c.toLowerCase() + CATEGORIES_GROUP_SLUG_POSTFIX,
name: CATEGORIES[c as category],
}))

65
common/challenge.ts Normal file
View File

@ -0,0 +1,65 @@
import { IS_PRIVATE_MANIFOLD } from './envs/constants'
export type Challenge = {
// The link to send: https://manifold.markets/challenges/username/market-slug/{slug}
// Also functions as the unique id for the link.
slug: string
// The user that created the challenge.
creatorId: string
creatorUsername: string
creatorName: string
creatorAvatarUrl?: string
// Displayed to people claiming the challenge
message: string
// How much to put up
creatorAmount: number
// YES or NO for now
creatorOutcome: string
// Different than the creator
acceptorOutcome: string
acceptorAmount: number
// The probability the challenger thinks
creatorOutcomeProb: number
contractId: string
contractSlug: string
contractQuestion: string
contractCreatorUsername: string
createdTime: number
// If null, the link is valid forever
expiresTime: number | null
// How many times the challenge can be used
maxUses: number
// Used for simpler caching
acceptedByUserIds: string[]
// Successful redemptions of the link
acceptances: Acceptance[]
// TODO: will have to fill this on resolve contract
isResolved: boolean
resolutionOutcome?: string
}
export type Acceptance = {
// User that accepted the challenge
userId: string
userUsername: string
userName: string
userAvatarUrl: string
// The ID of the successful bet that tracks the money moved
betId: string
createdTime: number
}
export const CHALLENGES_ENABLED = !IS_PRIVATE_MANIFOLD

608
common/charity.ts Normal file
View File

@ -0,0 +1,608 @@
export interface Charity {
id: string
slug: string
name: string
website: string
ein?: string
photo?: string
preview: string
description: string
tags?: CharityTag[]
}
type CharityTag = 'Featured' // | 'Health' | 'Poverty' | 'X-Risk' | 'Animal Welfare' | 'Policy'
// Warning: 'name' is currently used as the slug and the txn toId for the charity.
export const charities: Charity[] = [
{
name: '1Day Sooner',
website: 'https://www.1daysooner.org/',
preview:
'Accelerating the development of each vaccine by even a couple of days via COVID-19 human challenge trials could save thousands of lives.',
photo: 'https://i.imgur.com/bUDdzUE.png',
description: `1Day Sooner is a non-profit that advocates on behalf of COVID-19 challenge trial volunteers.
After a vaccine candidate is created in a lab, it is developed through a combination of pre-clinical evaluation and three phases of clinical trials that test its safety and efficacy. In traditional Phase III trials, participants receive the vaccine candidate or a placebo/active comparator, and efficacy is judged by comparing the prevalence of infection in the vaccine group and the placebo/comparator group, to test the hypothesis that significantly fewer participants in the vaccine group get infected. In these traditional trials, after receiving the treatment, participants return to their homes and their normal daily lives so as to test the treatment under real world conditions. Since only a small proportion of these participants may encounter the disease, it may take a large number of participants and a good deal of time for these trials to reveal differences between the vaccine and placebo groups.
In a human challenge trial (HCT), willing participants would receive the vaccine candidate or placebo and, after some time for the vaccine to take effect, be deliberately exposed to live coronavirus. Since exposure to the virus is guaranteed in HCTs, it may be possible to judge a vaccine candidates efficacy more quickly and with far fewer participants than a standard Phase III trial. While HCT efficacy results do not traditionally provide sufficient basis for licensure on their own, they could allow us to (1) more quickly weed out disappointing vaccine candidates or (2) promote the development of promising candidates in conjunction with traditional Phase III studies.
In addition, by gathering detailed data on the process of infection and vaccine protection in a clinical setting, researchers could learn information that proves extremely useful for broader vaccine and therapeutic development efforts. Altogether, there are scenarios in which the speed of HCTs and the richness of the data they provide accelerate the development of an effective and broadly accessible COVID-19 vaccine, with thousands of lives spared (depending on the pandemics long-term trajectory).`,
tags: ['Featured'] as CharityTag[],
},
{
name: 'QURI',
website: 'https://quantifieduncertainty.org/',
preview:
'The Quantified Uncertainty Research Institute advances forecasting and epistemics to improve the long-term future of humanity.',
photo: 'https://i.imgur.com/ZsSXPjH.png',
description: `QURI researches systematic practices to specify and estimate the most important parameters for the most important or scalable decisions. Research areas include forecasting, epistemics, evaluations, ontology, and estimation.
We emphasize technological solutions that can heavily scale in the next 5 to 30 years.
We believe that humanitys success in the next few hundred years will lie intensely on its ability to coordinate and make good decisions. If important governmental and philanthropic bodies become significantly more effective, this will make society far more resilient to many kinds of challenges ahead.`,
tags: ['Featured'] as CharityTag[],
},
{
name: 'Long-Term Future Fund',
website: 'https://funds.effectivealtruism.org/funds/far-future',
photo: 'https://i.imgur.com/C2qka9g.png',
preview:
'The Long-Term Future Fund aims to improve the long-term trajectory of civilization by making grants that address global catastrophic risks.',
description: `The Long-Term Future Fund aims to positively influence the long-term trajectory of civilization by making grants that address global catastrophic risks, especially potential risks from advanced artificial intelligence and pandemics. In addition, we seek to promote, implement, and advocate for longtermist ideas, and to otherwise increase the likelihood that future generations will flourish.
The Fund has a broad remit to make grants that promote, implement and advocate for longtermist ideas. Many of our grants aim to address potential risks from advanced artificial intelligence and to build infrastructure and advocate for longtermist projects. However, we welcome applications related to long-term institutional reform or other global catastrophic risks (e.g., pandemics or nuclear conflict).
We intend to support:
- Projects that directly contribute to reducing existential risks through technical research, policy analysis, advocacy, and/or demonstration projects
- Training for researchers or practitioners who work to mitigate existential risks, or help with relevant recruitment efforts, or infrastructure for people working on longtermist projects
- Promoting long-term thinking`,
tags: ['Featured'] as CharityTag[],
},
{
name: 'New Science',
website: 'https://newscience.org/',
photo: 'https://i.imgur.com/C7PoR4q.png',
preview:
'Facilitating scientific breakthroughs by empowering the next generation of scientists and building the 21st century institutions of basic science.',
description: `As its first major project, in the summer of 2022, New Science will run an in-person research fellowship in Boston for young life scientists, during which they will independently explore an ambitious high-risk scientific idea they couldnt work on otherwise and start building the foundations for a bigger research project, while having much more freedom than they could expect in their normal research environment but also much more support from us. This is inspired by Cold Spring Harbor Laboratory, which started as a place where leading molecular biologists came for the summer to hang out and work on random projects together, and which eventually housed 8 Nobel Prize winners.
As its second major project, in the fall of 2022, New Science will run an in-person 12-month-long fellowship for young scientists starting to directly attack the biggest structural issues of the established institutions of science. We will double down on things that worked well during the summer fellowship, while extending the fellowship to one year, thus allowing researchers to make much more progress and will strive to provide them as much scientific leverage as possible.
In several years, New Science will start funding entire labs outside of academia and then will be creating an entire network of scientific organizations, while supporting the broader scientific ecosystem that will constitute the 21st century institutions of basic science.`,
tags: ['Featured'] as CharityTag[],
},
{
name: 'Global Health and Development Fund',
website: 'https://funds.effectivealtruism.org/funds/global-development',
photo: 'https://i.imgur.com/C2qka9g.png',
preview:
"The Global Health and Development Fund aims to improve people's lives, typically in the poorest regions of the world where the need for healthcare and economic empowerment is greatest.",
description: `The Global Health and Development Fund recommends grants with the aim of improving people's lives, typically in the poorest regions of the world where the need for healthcare and economic empowerment is greatest. This will be achieved primarily by supporting projects that:
- Directly provide healthcare, or preventive measures that will improve health, well-being, or life expectancy
- Directly provide services that raise incomes or otherwise improve economic conditions
- Provide assistance to governments in the design and implementation of effective policies
In addition, the Global Health and Development Fund has a broad remit, and may fund other activities whose ultimate purpose is to serve people living in the poorest regions of the world, for example by raising additional funds (e.g. One for the World) or by exploring novel financing arrangements (e.g. Instiglio).
The Fund manager recommends grants to GiveWell top charities as a baseline, but will recommend higher-risk grants they believe to be more effective (in expectation) than GiveWell top charities. As such, the fund makes grants with a variety of different risk profiles.`,
},
{
name: 'Animal Welfare Fund',
website: 'https://funds.effectivealtruism.org/funds/animal-welfare',
photo: 'https://i.imgur.com/C2qka9g.png',
preview:
'The Animal Welfare Fund aims to effectively improve the well-being of nonhuman animals.',
description: `The Animal Welfare Fund aims to effectively improve the well-being of nonhuman animals, by making grants that focus on one or more of the following:
- Relatively neglected geographic regions or groups of animals
- Promising research into animal advocacy or animal well-being
- Activities that could make it easier to help animals in the future
- Otherwise best-in-class opportunities
The Fund focuses on projects that primarily address farmed animals, as well as projects that could affect other large populations of nonhuman animals. Some examples of projects that the Fund could support:
- Supporting farmed animal advocacy in Asia
- Researching ways to improve the welfare of farmed fish
- Promoting alternative proteins in order to reduce demand for animal products
- Advocating against the use of some cruel practice within the industrial agriculture system
- Growing the field of welfare biology in order to improve our understanding of different ways to address wild animal suffering`,
},
{
name: 'Effective Altruism Infrastructure Fund',
website: 'https://funds.effectivealtruism.org/funds/ea-community',
photo: 'https://i.imgur.com/C2qka9g.png',
preview:
'The Effective Altruism Infrastructure Fund aims to increase the impact of projects that use the principles of effective altruism.',
description: `The Effective Altruism Infrastructure Fund (EA Infrastructure Fund) recommends grants that aim to improve the work of projects using principles of effective altruism, by increasing their access to talent, capital, and knowledge.
The EA Infrastructure Fund has historically attempted to make strategic grants to incubate and grow projects that attempt to use reason and evidence to do as much good as possible. These include meta-charities that fundraise for highly effective charities doing direct work on important problems, research organizations that improve our understanding of how to do good more effectively, and projects that promote principles of effective altruism in contexts like academia.`,
},
{
name: 'Nonlinear',
website: 'https://www.nonlinear.org/',
photo: 'https://i.imgur.com/Muifc1l.png',
preview:
'Incubate longtermist nonprofits by connecting founders with ideas, funding, and mentorship.',
description: `Problem: There are tens of thousands of people working full time to make AI powerful, but around one hundred working to make AI safe. This needs to change.
Longtermism is held back by two bottlenecks:
1. Lots of funding, but few charities to deploy it.
2. Lots of talent, but few charities creating jobs.
Solution: Longtermism needs more charities to deploy funding and create jobs. Our goal is to 10x the number of talented people working on longtermism by launching dozens of high impact charities.
This helps solve the bottlenecks because entrepreneurs unlock latent EA talent - if one person starts an organization that employs 100 people who werent previously working on AI safety, that doubles the number of people working on the problem.
Our process:
1. Research the highest leverage ideas
2. Find the right founders
3. Connect them with mentors and funding
We will be announcing more details about our incubation program soon.
A few of the ideas weve incubated so far:
- The Nonlinear Library: Listen to top EA content on your podcast player. We use text-to-speech software to create an automatically updating repository of audio content from the EA Forum, Alignment Forum, and LessWrong. You can find it on all major podcast players here.
- EA Hiring Agency: Helping EA orgs scalably hire talent.
- EA Houses: EA's Airbnb - Connecting EAs who have extra space with EAs who need space here.`,
tags: ['Featured'] as CharityTag[],
},
{
name: 'GiveWell Maximum Impact Fund',
website: 'https://www.givewell.org/maximum-impact-fund',
photo: 'https://i.imgur.com/xikuDMZ.png',
preview:
'We search for the charities that save or improve lives the most per dollar.',
description: `
GiveWell is a nonprofit dedicated to finding outstanding giving opportunities and publishing the full details of our analysis to help donors decide where to give.
We don't focus solely on financials, such as assessing administrative or fundraising costs. Instead, we conduct in-depth research to determine how much good a given program accomplishes (in terms of lives saved, lives improved, etc.) per dollar spent. Rather than rating as many charities as possible, we focus on the few charities that stand out most (by our criteria) in order to find and confidently recommend high-impact giving opportunities (our list of top charities).
Our top recommendation to GiveWell donors seeking to do the most good possible is to donate to the Maximum Impact Fund. Donations to the Maximum Impact Fund are granted each quarter. We use our latest research to grant the funds to the recommended charity (or charities) where we believe theyll do the most good.
We grant funds from the Maximum Impact Fund to the recipient charity (or charities) at the end of each fiscal quarter. Our research team decides which charities have the highest priority funding needs at that time. This decision takes into consideration factors such as:
- Which funding gaps we expect to be filled and unfilled
- Each charitys plans for additional funding
- The cost-effectiveness of each funding gap`,
},
{
name: "Founder's Pledge Climate Change Fund",
website: 'https://founderspledge.com/funds/climate-change-fund',
photo: 'https://i.imgur.com/9turaJW.png',
preview:
'The Climate Change Fund aims to sustainably reach net-zero emissions globally, while still allowing growth to free millions from energy poverty.',
description: `The Climate Change Fund aims to sustainably reach net-zero emissions globally.
Current levels of emissions are contributing to millions of deaths annually from air pollution and causing irrevocable damage to our planet. In addition, millions worldwide do not have access to modern energy technology, severely hampering development goals.
This Fund is committed to finding and funding sustainable solutions to the emissions crisis that still allow growth, freeing millions from the prison of energy poverty.
The Fund is a philanthropic co-funding vehicle that does not provide investment returns.`,
},
{
name: "Founder's Pledge Patient Philanthropy Fund",
website: 'https://founderspledge.com/funds/patient-philanthropy-fund',
photo: 'https://i.imgur.com/LLR6CI6.png',
preview:
'The Patient Philanthropy Project aims to safeguard and benefit the long-term future of humanity',
description: `The Patient Philanthropy Project focuses on how we can collectively grow our resources to support the long-term flourishing of humanity. It addresses a crucial gap: as a society, we spend much too little on safeguarding and benefiting future generations. In fact, we spend more money on ice cream each year than we do on preventing our own extinction. However, people in the future - who do not have a voice in their future survival or environment - matter. Lots of them may yet come into existence and we have the ability to positively affect their lives now, if only by making sure we avoid major catastrophes that could destroy our common future.
Housed within the Project is the Patient Philanthropy Fund, a philanthropic co-funding vehicle which invests to give and ensures capital is at the ready when extraordinary opportunities to safeguard and improve the long-term future arise.
The Funds patient approach means that we aim to identify the point in time when the highest-impact opportunities are available, which may be years, decades, or even centuries ahead.`,
},
{
name: 'ARC',
website: 'https://alignment.org/',
photo: 'https://i.imgur.com/Hwg8OMP.png',
preview: 'Align future machine learning systems with human interests.',
description: `ARC is a non-profit research organization whose mission is to align future machine learning systems with human interests. Its current work focuses on developing an alignment strategy that could be adopted in industry today while scaling gracefully to future ML systems. Right now Paul Christiano and Mark Xu are researchers and Kyle Scott handles operations.
What is alignment? ML systems can exhibit goal-directed behavior, but it is difficult to understand or control what they are trying to do. Powerful models could cause harm if they were trying to manipulate and deceive humans. The goal of intent alignment is to instead train these models to be helpful and honest.
Motivation: We believe that modern ML techniques would lead to severe misalignment if scaled up to large enough computers and datasets. Practitioners may be able to adapt before these failures have catastrophic consequences, but we could reduce the risk by adopting scalable methods further in advance.
What were working on: The best way to understand our research priorities and methodology is probably to read our report on Eliciting Latent Knowledge. At a high level, were trying to figure out how to train ML systems to answer questions by straightforwardly translating their beliefs into natural language rather than by reasoning about what a human wants to hear.
Methodology: Were unsatisfied with an algorithm if we can see any plausible story about how it eventually breaks down, which means that we can rule out most algorithms on paper without ever implementing them. The cost of this approach is that it may completely miss strategies that exploit important structure in realistic ML models; the benefit is that you can consider lots of ideas quickly. (More)
Future plans: We expect to focus on similar theoretical problems in alignment until we either become more pessimistic about tractability or ARC grows enough to branch out into other areas. Over the long term we are likely to work on a combination of theoretical and empirical alignment research, collaborations with industry labs, alignment forecasting, and ML deployment policy.`,
},
{
name: 'Give Directly',
website: 'https://www.givedirectly.org/',
ein: '27-1661997',
photo: 'https://i.imgur.com/lrdxSyd.jpg',
preview: 'Send money directly to people living in poverty.',
description:
'GiveDirectly is a nonprofit that lets donors like you send money directly to the worlds poorest households. We believe people living in poverty deserve the dignity to choose for themselves how best to improve their lives — cash enables that choice. Since 2009, weve delivered $500M+ in cash directly into the hands of over 1 million families living in poverty. We currently have operations in Kenya, Rwanda, Liberia, Malawi, Morocco, Mozambique, DRC, Uganda, and the United States.',
},
{
name: 'Hellen Keller International',
website: 'https://www.hki.org/',
ein: '13-5562162',
photo: 'https://i.imgur.com/Dl97Abk.jpg',
preview:
'We envision a world where no one is deprived of the opportunity to live a healthy life and reach their true potential.',
description:
'Right now, 36 million people worldwide — most of them in developing countries — are blind.\n 90 percent of them didnt have to lose their sight. Helen Keller International is dedicated to combating the causes and consequences of vision loss and making clear vision a reality for those most vulnerable to disease and who lack access to quality eye care.\n Last year alone, we helped provide many tens of millions of people with treatment to prevent diseases of poverty including blinding trachoma and river blindness.\n Surgeons trained by our staff also performed tens of thousands of cataract surgeries in the developing world.  And in the United States, we screened the vision of nearly 66,000 students living in some of our countrys poorest neighborhoods and provided free eyeglasses to just over 16,000 of them. ',
},
{
name: 'Against Malaria Foundation',
website: 'https://www.againstmalaria.com/',
ein: '20-3069841',
photo: 'https://i.imgur.com/F3JoZi9.png',
preview: 'We help protect people from malaria.',
description:
'AMF (againstmalaria.com) provides funding for long-lasting insecticide-treated net (LLIN) distributions (for protection against malaria) in developing countries. There is strong evidence that distributing LLINs reduces child mortality and malaria cases. AMF conducts post-distribution surveys of completed distributions to determine whether LLINs have reached their intended destinations and how long they remain in good condition.',
},
{
name: 'Rethink Charity',
website: 'https://rethink.charity/',
photo: 'https://i.imgur.com/Go7N7As.png',
preview:
'Providing vital support to high-impact charities and charitable projects.',
description: `At Rethink Charity, were excited about improving the world by providing vital support to high-impact charities and charitable projects. We equip them with tools to boost their impact, through our projects that empower their donors with tax-efficient giving options and strategically coordinated matching opportunities.
What we do:
- Rethink Charity Forward is a cause-neutral donation routing fund for high-impact charities around the world. Canadians have used RC Forward to donate $10 million to high-impact charities since the project was launched in late 2017.
- EA Giving Tuesday supports both donors and highly effective nonprofits participating in Facebooks annual Giving Tuesday match. In addition to setting up systems and processes, the team provides analysis-based recommendations, detailed instructions, and responsive support. The teams goal is to make it as easy as possible for donors to direct matching dollars to highly effective nonprofits.`,
},
{
name: 'Malaria Consortium',
website: 'https://www.malariaconsortium.org/',
ein: '98-0627052',
photo: 'https://i.imgur.com/LGwy9d8.png ',
preview:
'We specialise in the prevention, control and treatment of malaria and other communicable diseases.',
description:
'We are dedicated to ensuring our work is supported by strong evidence and remains grounded in the lessons we learn through implementation. We explore beyond current practice, to try out innovative ways through research, implementation and policy development to achieve effective and sustainable disease management and control.',
},
{
name: 'The Center for the Study of Partisanship and Ideology',
website: 'https://cspicenter.org/',
photo: 'https://i.imgur.com/O88tkOW.png',
preview:
'Support and fund research on how ideology and government policy contribute to scientific, technological, and social progress.',
description: `Over the last few decades, scientific and technological progress have stagnated. Scientists conduct more research than ever before, but groundbreaking innovation is scarce. At the same time, identity politics and political polarization have reached new extremes, and social trends such as family stability and crime are worse than in previous decades and in some cases moving in the wrong direction. What explains these trends, and how can we reverse them?
Much of the blame lies with the institutions we rely on for administration, innovation, and leadership. Instead of forward-looking governments, we have short-sighted politicians and bloated bureaucracies. Instead of real experts with proven track records, we have so-called experts who appeal to the authority of their credentials. Instead of political leaders willing to face facts and make tough tradeoffs, we have politicians who appeal to ignorance and defer responsibility.
To fix our institutions, we need to rethink them from the ground up. That is why CSPI supports and funds research into the administrative systems, organizational structures, and political ideologies of modern governance. Only by understanding what makes these systems so often dysfunctional can we change them for the better.
CSPI believes that governments should be accountable to the populace as a whole, not special interest groups. We think experts should have greater say in public policy, but that there should be different standards for what qualifies as expertise. We want to end scientific and technological stagnation and usher in a new era of growth and innovation.
We are interested in funding and supporting research that can speak to these issues in the social sciences through grants and fellowships. CSPI particularly seek outs work that is unlikely to receive support elsewhere. See our home page for more about the kinds of research we are particularly interested in funding.`,
},
{
name: 'Faunalytics',
website: 'https://faunalytics.org/',
ein: '01-0686889',
photo: 'https://i.imgur.com/3JXhuXl.jpg',
preview:
'Faunalytics conducts research and shares knowledge to help advocates help animals effectively.',
description:
"Faunalytics' mission is to empower animal advocates with access to research, analysis, strategies, and messages that maximize their effectiveness to reduce animal suffering.\n Animals need you, and you need data. We conduct essential research, maintain an online research library, and directly support advocates and organizations in their work to save lives. The range of data we offer helps our movement understand how people think about and respond to advocacy, providing advocates with the best strategies to inspire change for animals. ",
},
{
name: 'The Humane League',
website: 'https://thehumaneleague.org/',
ein: '04-3817491',
photo: 'https://i.imgur.com/za9Rwon.jpg',
preview:
'We exist to end the abuse of animals raised for food by influencing the policies of the worlds biggest companies, demanding legislation, and empowering others to take action and leave animals off their plates',
description:
'The Humane League (THL) currently operates in the U.S., Mexico, the U.K., and Japan, where they work to improve animal welfare standards through grassroots campaigns, movement building, veg*n advocacy, research, and advocacy training, as well as through corporate, media, and community outreach. They work to build the animal advocacy movement internationally through the Open Wing Alliance (OWA), a coalition founded by THL whose mission is to end the use of battery cages globally.',
},
{
name: 'Wild Animal Initiative',
website: 'https://www.wildanimalinitiative.org/',
ein: '82-2281466',
tags: ['Featured'] as CharityTag[],
photo: 'https://i.imgur.com/bOVUnDm.png',
preview:
'Our mission is to understand and improve the lives of wild animals.',
description: `Although the natural world is a source of great beauty and happiness, vast numbers of animals routinely face serious challenges such as disease, hunger, or natural disasters. There is no “one-size-fits-all” solution to these threats. However, even as we recognize that improving the welfare of free-ranging wild animals is difficult, we believe that humans have a responsibility to help whenever we can.
Our staff explores how humans can beneficially coexist with animals through the lens of wild animal welfare.
We respect wild animals as individuals with their own needs and preferences, rather than seeing them as mere parts of ecosystems. But this approach demands a richer understanding of wild animals lives.
We want to take a proactive approach to managing the welfare benefits, threats, and uncertainties that are inherent to complex natural and urban environments. Yet, to take action safely, we must conduct research to understand the impacts of our actions. The transdisciplinary perspective of wild animal welfare draws upon ethics, ecology, and animal welfare science to gather the knowledge we need, facilitating evidence-based improvements to wild animals quality of life.
Without sufficient public interest or research activity, solutions to the problems wild animals face will go undiscovered.
Wild Animal Initiative currently focuses on helping scientists, grantors, and decision-makers investigate important and understudied questions about wild animal welfare. Our work catalyzes research and applied projects that will open the door to a clearer picture of wild animals needs and how to enhance their well-being. Ultimately, we envision a world in which people actively choose to help wild animals and have the knowledge they need to do so responsibly.`,
},
{
name: 'FYXX Foundation',
website: 'https://www.fyxxfoundation.org/',
photo: 'https://i.imgur.com/ROmWO7m.png',
preview:
'FYXX Foundation: wildlife population management, without killing.',
description: `The future of our planet depends on the innovations of today, and the health of our wildlife are the first indication of our successful stewardship, which we believe can be improved by safe population management utilizing fertility control instead of poison and culling.`,
},
{
name: 'New Incentives',
website: 'https://www.newincentives.org/',
ein: '45-2368993',
photo: 'https://i.imgur.com/bYl4tk3.png',
preview: 'Cash incentives to boost vaccination rates and save lives.',
description:
'New Incentives (newincentives.org) runs a conditional cash transfer (CCT) program in North West Nigeria which seeks to increase uptake of routine immunizations through cash transfers, raising public awareness of the benefits of vaccination and reducing the frequency of vaccine stockouts.',
},
{
name: 'SCI foundation',
website: 'https://schistosomiasiscontrolinitiative.org/',
ein: '',
photo: 'https://i.imgur.com/sWD8zM5.png',
preview:
'SCI works with governments in sub-Saharan Africa to create or scale up programs that treat schistosomiasis and soil-transmitted helminthiasis ("deworming").',
description:
'Were a non-profit initiative supporting governments in sub-Saharan African countries. We support them to develop sustainable, cost-effective programmes against parasitic worm infections such as schistosomiasis and intestinal worms.Since our foundation in 2002, weve contributed to the delivery of over 200 million treatments against these diseases. The programmes are highly effective; parasitic worm infections can be reduced by up to 60% after just one round of treatment.',
},
{
name: 'Wikimedia Foundation',
website: 'https://wikimediafoundation.org/',
ein: '20-0049703',
photo: 'https://i.imgur.com/klEzUbR.png',
preview: 'We help everyone share in the sum of all knowledge.',
description:
'We are the people who keep knowledge free. There is an amazing community of people around the world that makes great projects like Wikipedia. We help them do that work. We take care of the technical infrastructure, the legal challenges, and the growing pains.',
},
{
name: 'Rainforest Trust',
website: 'https://www.rainforesttrust.org/',
ein: '13-3500609',
photo: 'https://i.imgur.com/6MzS530.png',
preview:
'Rainforest Trust saves endangered wildlife and protects our planet by creating rainforest reserves through partnerships, community engagement and donor support.',
description:
'Our unique, cost-effective conservation model for protecting endangered species has been implemented successfully for over 30 years. Thanks to the generosity of our donors, the expertise of our partners and the participation of local communities across the tropics, our reserves are exemplary models of international conservation.',
},
{
name: 'The Nature Conservancy',
website: 'https://www.nature.org/en-us/',
ein: '53-0242652',
photo: 'https://i.imgur.com/vjxkoGo.jpg',
preview: 'A Future Where People and Nature Thrive',
description:
'The Nature Conservancy is a global environmental nonprofit working to create a world where people and nature can thrive. Founded in the U.S. through grassroots action in 1951, The Nature Conservancy has grown to become one of the most effective and wide-reaching environmental organizations in the world. Thanks to more than a million members and the dedicated efforts of our diverse staff and over 400 scientists, we impact conservation in 76 countries and territories: 37 by direct conservation impact and 39 through partners.',
},
{
name: 'Doctors Without Borders',
website: 'https://www.doctorswithoutborders.org/',
ein: '13-3433452',
photo: 'https://i.imgur.com/xqhH9FE.png',
preview:
'We provide independent, impartial medical humanitarian assistance to the people who need it most.',
description:
'Doctors Without Borders/Médecins Sans Frontières (MSF) cares for people affected by conflict, disease outbreaks, natural and human-made disasters, and exclusion from health care in more than 70 countries.',
},
{
name: 'World Wildlife Fund',
website: 'https://www.worldwildlife.org/',
ein: '52-1693387',
photo: 'https://i.imgur.com/hDADuqW.png',
preview:
'WWF works to sustain the natural world for the benefit of people and wildlife, collaborating with partners from local to global levels in nearly 100 countries.',
description:
'As the worlds leading conservation organization, WWF works in nearly 100 countries to tackle the most pressing issues at the intersection of nature, people, and climate. We collaborate with local communities to conserve the natural resources we all depend on and build a future in which people and nature thrive. Together with partners at all levels, we transform markets and policies toward sustainability, tackle the threats driving the climate crisis, and protect and restore wildlife and their habitats.',
},
{
name: 'UNICEF USA',
website: 'https://www.unicefusa.org/',
photo: 'https://i.imgur.com/9cxuvZi.png',
ein: '13-1760110',
preview:
"UNICEF USA helps save and protect the world's most vulnerable children.",
description:
'Over eight decades, the United Nations Childrens Fund (UNICEF) has built an unprecedented global support system for the worlds children. UNICEF relentlessly works day in and day out to deliver the essentials that give every child an equitable chance in life: health care and immunizations, safe water and sanitation, nutrition, education, emergency relief and more. UNICEF USA advances the global mission of UNICEF by rallying the American public to support the worlds most vulnerable children. Together, we have helped save more childrens lives than any other humanitarian organization.',
},
{
name: 'Vitamin Angels',
website: 'https://www.vitaminangels.org/',
ein: '77-0485881',
photo: 'https://i.imgur.com/Mf35IOu.jpg',
preview:
'By improving access to vital nutrition, everyone gets an equal chance to grow, thrive, and prosper.',
description:
'Our team of program experts collaborates with thousands of local organizations and national governments around the world, focusing efforts on reaching communities who are underserved. Vitamin Angels program partners are a local presence in these communities. As trusted organizations already hard at work, they connect millions of pregnant women and young children with our evidence-based nutrition interventions in addition to the health services they already provide.',
},
{
name: 'Free Software Foundation',
website: 'https://www.fsf.org/',
ein: '04-2888848',
photo: 'https://i.imgur.com/z87sFDE.png',
preview:
'The Free Software Foundation (FSF) is a nonprofit with a worldwide mission to promote computer user freedom.',
description:
'As our society grows more dependent on computers, the software we run is of critical importance to securing the future of a free society. Free software is about having control over the technology we use in our homes, schools and businesses, where computers work for our individual and communal benefit, not for proprietary software companies or governments who might seek to restrict and monitor us. The Free Software Foundation exclusively uses free software to perform its work.The Free Software Foundation is working to secure freedom for computerusers by promoting the development and use of free (as in freedom) software and documentation—particularly the GNU operating system—and by campaigning against threats to computer user freedom like Digital Restrictions Management (DRM) and software patents.',
},
{
name: 'Direct Relief',
website: 'https://www.directrelief.org/',
ein: '95-1831116',
photo: 'https://i.imgur.com/QS7kHAU.png',
preview:
'Direct Relief is a humanitarian aid organization, active in all 50 states and more than 80 countries, with a mission to improve the health and lives of people affected by poverty or emergencies without regard to politics, religion, or ability to pay.',
description:
'Nongovernmental, nonsectarian, and not-for-profit, Direct Relief relies entirely on private contributions to advance its mission and perform a wide range of functions.\n Included among them are identifying key local providers of health services; working to identify the unmet needs of people in the low-resource areas; mobilizing essential medicines, supplies, and equipment that are requested and appropriate for the circumstances; and managing the many details inherent in storing, transporting, and distributing such resources to organizations in the most efficient manner possible.',
},
{
name: 'World Resources Institute',
website: 'https://www.wri.org/',
ein: '52-1257057',
photo: 'https://i.imgur.com/Bi6MgYI.png',
preview:
'WRI is a global nonprofit organization that works with leaders in government, business and civil society to research, design, and carry out practical solutions that simultaneously improve peoples lives and ensure nature can thrive.',
description:
"Since its founding in 1982, WRI has been guided by its mission and core values which are integrated into all that we do. Our mission: To move human society to live in ways that protect Earths environment and its capacity to provide for the needs and aspirations of current and future generations. WRI relies on the generosity of our donors to drive outcomes that help the world to be a fairer, healthier and more sustainable place for people and the planet. We publish our financials annually to highlight our continued fiscal accountability. That's why WRI consistently receives top ratings from charity evaluators for our strong financial stewardship and commitment to transparency and accountability.",
},
{
name: 'ProPublica',
website: 'https://www.propublica.org/',
ein: '14-2007220',
photo: 'https://i.imgur.com/R5Vt3Pb.png',
preview:
'The mission: to expose abuses of power and betrayals of the public trust by government, business, and other institutions, using the moral force of investigative journalism to spur reform through the sustained spotlighting of wrongdoing.',
description:
'ProPublica is an independent, nonprofit newsroom that produces investigative journalism with moral force. We dig deep into important issues, shining a light on abuses of power and betrayals of public trust — and we stick with those issues as long as it takes to hold power to account. With a team of more than 100 dedicated journalists, ProPublica covers a range of topics including government and politics, business, criminal justice, the environment, education, health care, immigration, and technology. We focus on stories with the potential to spur real-world impact. Among other positive changes, our reporting has contributed to the passage of new laws; reversals of harmful policies and practices; and accountability for leaders at local, state and national levels.',
},
{
name: 'Dana-Farber Cancer Institute',
website: 'https://www.dana-farber.org/',
ein: '04-2263040',
photo: 'https://i.imgur.com/SQNn97p.png',
preview:
"For over 70 years, we've led the world by making life-changing breakthroughs in cancer research and patient care, providing the most advanced treatments available.",
description:
"Since its founding in 1947, Dana-Farber Cancer Institute in Boston, Massachusetts has been committed to providing adults and children with cancer with the best treatment available today while developing tomorrow's cures through cutting-edge research. Today, the Institute employs more than 5,000 staff, faculty, and clinicians supporting more than 640,000 annual outpatient visits, more than 1,000 hospital discharges per year, and has over 1,100 open clinical trials. Dana-Farber is internationally renowned for its equal commitment to cutting edge research and provision of excellent patient care. The deep expertise in these two areas uniquely positions Dana-Farber to develop, test, and gain FDA approval for new cancer therapies in its laboratories and clinical settings. Dana-Farber researchers have contributed to the development of 35 of 75 cancer drugs recently approved by the FDA for use in cancer patients.",
},
{
name: 'Save The Children',
website: 'https://www.savethechildren.org/',
ein: '06-0726487',
photo: 'https://i.imgur.com/GngYPBI.png',
preview:
'Through the decades, Save the Children has continued to work to save childrens lives, and thats still what we do today.',
description:
"Our pioneering programs address children's unique needs, giving them a healthy start in life, the opportunity to learn and protection from harm. In the United States and around the world, our work creates lasting change for children, their families and communities ultimately, transforming the future we all share.\nThis work is only made possible by the ongoing generosity of our donors, whose valuable support is used in the most cost-effective ways. It's important to note that all our work intersects helping a boy or girl go to school also protects them from dangers such as child trafficking and early marriage. Keeping children healthy from disease or malnutrition means their parents are more likely to avoid costly treatment and be better able to provide for their family.\nWe dont go into communities, carry out a project and then move on. We consult with children, their families, community leaders and local councils to understand all the issues or barriers, and then we develop programs that address these. We build trust so that our programs are successful and bring about real change.",
},
{
name: 'World Central Kitchen Incorporated',
website: 'https://wck.org/',
ein: '27-3521132',
photo: 'https://i.imgur.com/te93MaY.png',
preview:
'WCK is first to the frontlines, providing meals in response to humanitarian, climate, and community crises. We build resilient food systems with locally led solutions.',
description:
"WCK responds to natural disasters, man-made crises, and humanitarian emergencies around the world. We're a team of food first responders, mobilizing with the urgency of now to get meals to the people who need them most. Deploying our model of quick action, leveraging local resources, and adapting in real time, we know that a nourishing meal in a time of crisis is so much more than a plate of food—it's hope, it's dignity, and it's a sign that someone cares.",
},
{
name: 'The Johns Hopkins Center for Health Security',
website: 'https://www.centerforhealthsecurity.org/',
ein: '',
photo: 'https://i.imgur.com/gKZE2Xs.png',
preview:
'Our mission: to protect peoples health from epidemics and disasters and ensure that communities are resilient to major challenges.',
description:
'The Center for Health Security undertakes a series of projects, collaborations, and initiatives to push forward progress on global health security, emerging infectious diseases and epidemics, medical and public health preparedness and response, deliberate biological threats, and opportunities and risks in the life sciences. We:\n- Conduct research and analysis on major domestic and international health security issues.\n- Engage with researchers, the policymaking community, and the private sector to make progress in the field.\n- Convene expert working groups, congressional seminars, scientific meetings, conferences, and tabletop exercises to stimulate new thinking and provoke action.\n- Educate a rising generation of scholars, practitioners, and policymakers.',
},
{
name: 'ALLFED',
website: 'https://allfed.info/',
photo: 'https://i.imgur.com/p235vwF.jpg',
ein: '27-6601178',
preview: 'Feeding everyone no matter what.',
description:
'The mission of the Alliance to Feed the Earth in Disasters is to help create resilience to global food shocks. We seek to identify various resilient food solutions and to help governments implement these solutions, to increase the chances that people have enough to eat in the event of a global catastrophe. We focus on events that could deplete food supplies or access to 5% of the global population or more.Our ultimate goal is to feed everyone, no matter what. An important aspect of this goal is that we need to establish equitable solutions so that all people can access the nutrition they need, regardless of wealth or location.ALLFED is inspired by effective altruism, using reason and evidence to identify how to do the most good. Our solutions are backed by science and research, and we also identify the most cost-effective solutions, to be able to provide more nutrition in catastrophes.',
},
{
name: 'The Trevor Project',
website: 'https://www.thetrevorproject.org/',
photo: 'https://i.imgur.com/QN4mVNn.jpeg',
preview:
'The Trevor Project is the worlds largest suicide prevention and crisis intervention organization for LGBTQ (lesbian, gay, bisexual, transgender, queer, and questioning) young people.',
description: `Two decades ago, we responded to a health crisis. Now were building a safer, more-inclusive world. LGBTQ young people are four times more likely to attempt suicide, and suicide remains the second leading cause of death among all young people in the U.S.
Our Mission
To end suicide among lesbian, gay, bisexual, transgender, queer & questioning young people.
Our Vision
A world where all LGBTQ young people see a bright future for themselves.
Our Goal
To serve 1.8 million crisis contacts annually, by the end of our 25th year, while continuing to innovate on our core services.`,
},
{
name: 'ACLU',
website: 'https://www.aclu.org/',
photo: 'https://i.imgur.com/nbSYuDC.png',
preview:
'The ACLU works in the courts, legislatures, and communities to defend and preserve the individual rights and liberties guaranteed to all people in this country by the Constitution and laws of the United States.',
description: `
THREE THINGS TO KNOW ABOUT THE ACLU
We protect American values. In many ways, the ACLU is the nation's most conservative organization. Our job is to conserve America's original civic values - the Constitution and the Bill of Rights - and defend the rights of every man, woman and child in this country.
We're not anti-anything. The only things we fight are attempts to take away or limit your civil liberties, like your right to practice any religion you want (or none at all); or to decide in private whether or not to have a child; or to speak out - for or against - anything at all; or to be treated with equality and fairness, no matter who you are.
We're there for you. Rich or poor, straight or gay, black or white or brown, urban or rural, pious or atheist, American-born or foreign-born, able-bodied or living with a disability. Every person in this country should have the same basic rights. And since our founding in 1920, we've been working hard to make sure no one takes them away.
The American Civil Liberties Union is our nation's guardian of liberty, working daily in courts, legislatures and communities to defend and preserve the individual rights and liberties that the Constitution and laws of the United States guarantee everyone in this country.
"So long as we have enough people in this country willing to fight for their rights, we'll be called a democracy," ACLU Founder Roger Baldwin said.
The U.S. Constitution and the Bill of Rights trumpet our aspirations for the kind of society that we want to be. But for much of our history, our nation failed to fulfill the promise of liberty for whole groups of people.`,
},
{
name: 'The Center for Election Science',
website: 'https://electionscience.org/',
photo: 'https://i.imgur.com/WvdHHZa.png',
preview:
'The Center for Election Science is a nonpartisan nonprofit dedicated to empowering voters with voting methods that strengthen democracy. We believe you deserve a vote that empowers you to impact the world you live in.',
description: `Founded in 2011, The Center for Election Science is a national, nonpartisan nonprofit focused on voting reform.
Our Mission To empower people with voting methods that strengthen democracy.
Our Vision A world where democracies thrive because voters voices are heard.
With an emphasis on approval voting, we bring better elections to people across the country through both advocacy and research.
The movement for a better way to vote is rapidly gaining momentum as voters grow tired of election results that dont represent the will of the people. In 2018, we worked with locals in Fargo, ND to help them become the first city in the U.S. to adopt approval voting. And in 2020, we helped grassroots activists empower the 300k people of St. Louis, MO with stronger democracy through approval voting.`,
},
{
name: 'Founders Pledge Global Health and Development Fund',
website: 'https://founderspledge.com/funds/global-health-and-development',
photo: 'https://i.imgur.com/EXbxH7T.png',
preview:
'Tackling the vast global inequalities in health, wealth and opportunity',
description: `Nearly half the world lives on less than $2.50 a day, yet giving by the worlds richest often overlooks the worlds poorest and most vulnerable. Despite the average American household being richer than 90% of the rest of the world, only 6% of US charitable giving goes to charities which work internationally.
This Fund is focused on helping those who need it most, wherever that help can make the biggest difference. By building a mixed portfolio of direct and indirect interventions, such as policy work, we aim to:
Improve the lives of the world's most vulnerable people.
Reduce the number of easily preventable deaths worldwide.
Work towards sustainable, systemic change.`,
},
{
name: 'YIMBY Law',
website: 'https://www.yimbylaw.org/',
photo: 'https://i.imgur.com/zlzp21Z.png',
preview:
'YIMBY Law works to make housing in California more accessible and affordable, by enforcing state housing laws.',
description: `
YIMBY Law works to make housing in California more accessible and affordable. Our method is to enforce state housing laws, and some examples are outlined below. We send letters to cities considering zoning or general plan compliant housing developments informing them of their duties under state law, and sue them when they do not comply.
If you would like to support our work, you can do so by getting involved or by donating.`,
},
{
name: 'CaRLA',
website: 'https://carlaef.org/',
photo: 'https://i.imgur.com/IsNVTOY.png',
preview:
'The California Renters Legal Advocacy and Education Funds core mission is to make lasting impacts to improve the affordability and accessibility of housing to current and future Californians, especially low- and moderate-income people and communities of color.',
description: `
The California Renters Legal Advocacy and Education Funds core mission is to make lasting impacts to improve the affordability and accessibility of housing to current and future Californians, especially low- and moderate-income people and communities of color.
CaRLA uses legal advocacy and education to ensure all cities comply with their own zoning and state housing laws and do their part to help solve the states housing shortage.
In addition to housing impact litigation, we provide free legal aid, education and workshops, counseling and advocacy to advocates, homeowners, small developers, and city and state government officials.`,
},
{
name: 'Mriya',
website: 'https://mriya-ua.org/',
photo:
'https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2Fdefault%2Fci2h3hStFM.47?alt=media&token=0d2cdc3d-e4d8-4f5e-8f23-4a586b6ff637',
preview: 'Donate supplies to soldiers in Ukraine',
description:
'Donate supplies to soldiers in Ukraine, including tourniquets and plate carriers.',
},
].map((charity) => {
const slug = charity.name.toLowerCase().replace(/\s/g, '-')
return {
...charity,
id: slug,
slug,
}
})

View File

@ -1,16 +1,56 @@
import type { JSONContent } from '@tiptap/core'
export type AnyCommentType = OnContract | OnGroup | OnPost
// Currently, comments are created after the bet, not atomically with the bet. // Currently, comments are created after the bet, not atomically with the bet.
// They're uniquely identified by the pair contractId/betId. // They're uniquely identified by the pair contractId/betId.
export type Comment = { export type Comment<T extends AnyCommentType = AnyCommentType> = {
id: string id: string
contractId: string replyToCommentId?: string
betId: string
userId: string userId: string
text: string /** @deprecated - content now stored as JSON in content*/
text?: string
content: JSONContent
createdTime: number createdTime: number
// Denormalized, for rendering comments // Denormalized, for rendering comments
userName: string userName: string
userUsername: string userUsername: string
userAvatarUrl?: string userAvatarUrl?: string
bountiesAwarded?: number
} & T
export type OnContract = {
commentType: 'contract'
contractId: string
answerOutcome?: string
betId?: string
// denormalized from contract
contractSlug: string
contractQuestion: string
// denormalized from bet
betAmount?: number
betOutcome?: string
// denormalized based on betting history
commenterPositionProb?: number // binary only
commenterPositionShares?: number
commenterPositionOutcome?: string
} }
export type OnGroup = {
commentType: 'group'
groupId: string
}
export type OnPost = {
commentType: 'post'
postId: string
}
export type ContractComment = Comment<OnContract>
export type GroupComment = Comment<OnGroup>
export type PostComment = Comment<OnPost>

168
common/contract-details.ts Normal file
View File

@ -0,0 +1,168 @@
import { Challenge } from './challenge'
import { BinaryContract, Contract } from './contract'
import { getFormattedMappedValue } from './pseudo-numeric'
import { getProbability } from './calculate'
import { richTextToString } from './util/parse'
import { getCpmmProbability } from './calculate-cpmm'
import { getDpmProbability } from './calculate-dpm'
import { formatMoney, formatPercent } from './util/format'
export function contractMetrics(contract: Contract) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const dayjs = require('dayjs')
const { createdTime, resolutionTime, isResolved } = contract
const createdDate = dayjs(createdTime).format('MMM D')
const resolvedDate = isResolved
? dayjs(resolutionTime).format('MMM D')
: undefined
const volumeLabel = `${formatMoney(contract.volume)} bet`
return { volumeLabel, createdDate, resolvedDate }
}
// String version of the above, to send to the OpenGraph image generator
export function contractTextDetails(contract: Contract) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const dayjs = require('dayjs')
const { closeTime, groupLinks } = contract
const { createdDate, resolvedDate, volumeLabel } = contractMetrics(contract)
const groupHashtags = groupLinks?.map((g) => `#${g.name.replace(/ /g, '')}`)
return (
`${resolvedDate ? `${createdDate} - ${resolvedDate}` : createdDate}` +
(closeTime
? `${closeTime > Date.now() ? 'Closes' : 'Closed'} ${dayjs(
closeTime
).format('MMM D, h:mma')}`
: '') +
`${volumeLabel}` +
(groupHashtags ? `${groupHashtags.join(' ')}` : '')
)
}
export function getBinaryProb(contract: BinaryContract) {
const { pool, resolutionProbability, mechanism } = contract
return (
resolutionProbability ??
(mechanism === 'cpmm-1'
? getCpmmProbability(pool, contract.p)
: getDpmProbability(contract.totalShares))
)
}
export const getOpenGraphProps = (contract: Contract) => {
const {
resolution,
question,
creatorName,
creatorUsername,
outcomeType,
creatorAvatarUrl,
description: desc,
} = contract
const probPercent =
outcomeType === 'BINARY'
? formatPercent(getBinaryProb(contract))
: undefined
const numericValue =
outcomeType === 'PSEUDO_NUMERIC'
? getFormattedMappedValue(contract)(getProbability(contract))
: undefined
const stringDesc = typeof desc === 'string' ? desc : richTextToString(desc)
const description = resolution
? `Resolved ${resolution}. ${stringDesc}`
: probPercent
? `${probPercent} chance. ${stringDesc}`
: stringDesc
return {
question,
probability: probPercent,
metadata: contractTextDetails(contract),
creatorName,
creatorUsername,
creatorAvatarUrl,
description,
numericValue,
resolution,
}
}
export type OgCardProps = {
question: string
probability?: string
metadata: string
creatorName: string
creatorUsername: string
creatorAvatarUrl?: string
numericValue?: string
resolution?: string
}
export function buildCardUrl(props: OgCardProps, challenge?: Challenge) {
const {
creatorAmount,
acceptances,
acceptorAmount,
creatorOutcome,
acceptorOutcome,
} = challenge || {}
const {
probability,
numericValue,
resolution,
creatorAvatarUrl,
question,
metadata,
creatorUsername,
creatorName,
} = props
const { userName, userAvatarUrl } = acceptances?.[0] ?? {}
const probabilityParam =
probability === undefined
? ''
: `&probability=${encodeURIComponent(probability ?? '')}`
const numericValueParam =
numericValue === undefined
? ''
: `&numericValue=${encodeURIComponent(numericValue ?? '')}`
const creatorAvatarUrlParam =
creatorAvatarUrl === undefined
? ''
: `&creatorAvatarUrl=${encodeURIComponent(creatorAvatarUrl ?? '')}`
const challengeUrlParams = challenge
? `&creatorAmount=${creatorAmount}&creatorOutcome=${creatorOutcome}` +
`&challengerAmount=${acceptorAmount}&challengerOutcome=${acceptorOutcome}` +
`&acceptedName=${userName ?? ''}&acceptedAvatarUrl=${userAvatarUrl ?? ''}`
: ''
const resolutionUrlParam = resolution
? `&resolution=${encodeURIComponent(resolution)}`
: ''
// URL encode each of the props, then add them as query params
return (
`https://manifold-og-image.vercel.app/m.png` +
`?question=${encodeURIComponent(question)}` +
probabilityParam +
numericValueParam +
`&metadata=${encodeURIComponent(metadata)}` +
`&creatorName=${encodeURIComponent(creatorName)}` +
creatorAvatarUrlParam +
`&creatorUsername=${encodeURIComponent(creatorUsername)}` +
challengeUrlParams +
resolutionUrlParam
)
}

View File

@ -1,43 +1,83 @@
import { Answer } from './answer' import { Answer } from './answer'
import { Fees } from './fees' import { Fees } from './fees'
import { JSONContent } from '@tiptap/core'
import { GroupLink } from 'common/group'
export type FullContract< export type AnyMechanism = DPM | CPMM
M extends DPM | CPMM, export type AnyOutcomeType =
T extends Binary | Multi | FreeResponse | Binary
> = { | MultipleChoice
| PseudoNumeric
| FreeResponse
| Numeric
export type AnyContractType =
| (CPMM & Binary)
| (CPMM & PseudoNumeric)
| (DPM & Binary)
| (DPM & FreeResponse)
| (DPM & Numeric)
| (DPM & MultipleChoice)
export type Contract<T extends AnyContractType = AnyContractType> = {
id: string id: string
slug: string // auto-generated; must be unique slug: string // auto-generated; must be unique
creatorId: string creatorId: string
creatorName: string creatorName: string
creatorUsername: string creatorUsername: string
creatorAvatarUrl?: string // Start requiring after 2022-03-01 creatorAvatarUrl?: string
question: string question: string
description: string // More info about what the contract is about description: string | JSONContent // More info about what the contract is about
tags: string[] tags: string[]
lowercaseTags: string[] lowercaseTags: string[]
visibility: 'public' | 'unlisted' visibility: visibility
createdTime: number // Milliseconds since epoch createdTime: number // Milliseconds since epoch
lastUpdatedTime: number // If the question or description was changed lastUpdatedTime?: number // Updated on new bet or comment
lastBetTime?: number
lastCommentTime?: number
closeTime?: number // When no more trading is allowed closeTime?: number // When no more trading is allowed
isResolved: boolean isResolved: boolean
resolutionTime?: number // When the contract creator resolved the market resolutionTime?: number // When the contract creator resolved the market
resolution?: string resolution?: string
resolutionProbability?: number
closeEmailsSent?: number closeEmailsSent?: number
volume: number volume: number
volume24Hours: number volume24Hours: number
volume7Days: number volume7Days: number
elasticity: number
collectedFees: Fees collectedFees: Fees
} & M &
T
export type Contract = FullContract<DPM | CPMM, Binary | Multi | FreeResponse> groupSlugs?: string[]
groupLinks?: GroupLink[]
uniqueBettorIds?: string[]
uniqueBettorCount?: number
popularityScore?: number
dailyScore?: number
followerCount?: number
featuredOnHomeRank?: number
likedByUserIds?: string[]
likedByUserCount?: number
flaggedByUsernames?: string[]
openCommentBounties?: number
unlistedById?: string
} & T
export type BinaryContract = Contract & Binary
export type PseudoNumericContract = Contract & PseudoNumeric
export type NumericContract = Contract & Numeric
export type FreeResponseContract = Contract & FreeResponse
export type MultipleChoiceContract = Contract & MultipleChoice
export type DPMContract = Contract & DPM
export type CPMMContract = Contract & CPMM
export type DPMBinaryContract = BinaryContract & DPM
export type CPMMBinaryContract = BinaryContract & CPMM
export type DPM = { export type DPM = {
mechanism: 'dpm-2' mechanism: 'dpm-2'
@ -52,31 +92,74 @@ export type CPMM = {
mechanism: 'cpmm-1' mechanism: 'cpmm-1'
pool: { [outcome: string]: number } pool: { [outcome: string]: number }
p: number // probability constant in y^p * n^(1-p) = k p: number // probability constant in y^p * n^(1-p) = k
totalLiquidity: number // in M$ totalLiquidity: number // for historical reasons, this the total subsidy amount added in M$
subsidyPool: number // current value of subsidy pool in M$
prob: number
probChanges: {
day: number
week: number
month: number
}
} }
export type FixedPayouts = CPMM
export type Binary = { export type Binary = {
outcomeType: 'BINARY' outcomeType: 'BINARY'
initialProbability: number initialProbability: number
resolutionProbability?: number // Used for BINARY markets resolved to MKT resolutionProbability?: number // Used for BINARY markets resolved to MKT
resolution?: resolution
} }
export type Multi = { export type PseudoNumeric = {
outcomeType: 'MULTI' outcomeType: 'PSEUDO_NUMERIC'
multiOutcomes: string[] // Used for outcomeType 'MULTI'. min: number
resolutions?: { [outcome: string]: number } // Used for PROB max: number
isLogScale: boolean
resolutionValue?: number
// same as binary market; map everything to probability
initialProbability: number
resolutionProbability?: number
} }
export type FreeResponse = { export type FreeResponse = {
outcomeType: 'FREE_RESPONSE' outcomeType: 'FREE_RESPONSE'
answers: Answer[] // Used for outcomeType 'FREE_RESPONSE'. answers: Answer[] // Used for outcomeType 'FREE_RESPONSE'.
resolutions?: { [outcome: string]: number } // Used for PROB resolution?: string | 'MKT' | 'CANCEL'
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
} }
export type outcomeType = 'BINARY' | 'MULTI' | 'FREE_RESPONSE' export type MultipleChoice = {
outcomeType: 'MULTIPLE_CHOICE'
answers: Answer[]
resolution?: string | 'MKT' | 'CANCEL'
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
}
export const MAX_QUESTION_LENGTH = 480 export type Numeric = {
export const MAX_DESCRIPTION_LENGTH = 10000 outcomeType: 'NUMERIC'
bucketCount: number
min: number
max: number
resolutions?: { [outcome: string]: number } // Used for MKT resolution.
resolutionValue?: number
}
export type outcomeType = AnyOutcomeType['outcomeType']
export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL'
export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const
export const OUTCOME_TYPES = [
'BINARY',
'MULTIPLE_CHOICE',
'FREE_RESPONSE',
'PSEUDO_NUMERIC',
'NUMERIC',
] as const
export const MAX_QUESTION_LENGTH = 240
export const MAX_DESCRIPTION_LENGTH = 16000
export const MAX_TAG_LENGTH = 60 export const MAX_TAG_LENGTH = 60
export const CPMM_MIN_POOL_QTY = 0.01
export type visibility = 'public' | 'unlisted'
export const VISIBILITIES = ['public', 'unlisted'] as const

20
common/economy.ts Normal file
View File

@ -0,0 +1,20 @@
import { ENV_CONFIG } from './envs/constants'
const econ = ENV_CONFIG.economy
export const FIXED_ANTE = econ?.FIXED_ANTE ?? 100
export const STARTING_BALANCE = econ?.STARTING_BALANCE ?? 1000
// for sus users, i.e. multiple sign ups for same person
export const SUS_STARTING_BALANCE = econ?.SUS_STARTING_BALANCE ?? 10
export const REFERRAL_AMOUNT = econ?.REFERRAL_AMOUNT ?? 250
export const UNIQUE_BETTOR_BONUS_AMOUNT = econ?.UNIQUE_BETTOR_BONUS_AMOUNT ?? 10
export const BETTING_STREAK_BONUS_AMOUNT =
econ?.BETTING_STREAK_BONUS_AMOUNT ?? 5
export const BETTING_STREAK_BONUS_MAX = econ?.BETTING_STREAK_BONUS_MAX ?? 25
export const BETTING_STREAK_RESET_HOUR = econ?.BETTING_STREAK_RESET_HOUR ?? 7
export const FREE_MARKETS_PER_USER_MAX = econ?.FREE_MARKETS_PER_USER_MAX ?? 5
export const COMMENT_BOUNTY_AMOUNT = econ?.COMMENT_BOUNTY_AMOUNT ?? 250
export const UNIQUE_BETTOR_LIQUIDITY = 20

View File

@ -1,16 +1,17 @@
import { escapeRegExp } from 'lodash'
import { DEV_CONFIG } from './dev' import { DEV_CONFIG } from './dev'
import { EnvConfig, PROD_CONFIG } from './prod' import { EnvConfig, PROD_CONFIG } from './prod'
import { THEOREMONE_CONFIG } from './theoremone' import { THEOREMONE_CONFIG } from './theoremone'
const ENV = process.env.NEXT_PUBLIC_FIREBASE_ENV ?? 'PROD' export const ENV = process.env.NEXT_PUBLIC_FIREBASE_ENV ?? 'PROD'
const CONFIGS = { const CONFIGS: { [env: string]: EnvConfig } = {
PROD: PROD_CONFIG, PROD: PROD_CONFIG,
DEV: DEV_CONFIG, DEV: DEV_CONFIG,
THEOREMONE: THEOREMONE_CONFIG, THEOREMONE: THEOREMONE_CONFIG,
} }
// @ts-ignore
export const ENV_CONFIG: EnvConfig = CONFIGS[ENV] export const ENV_CONFIG = CONFIGS[ENV]
export function isWhitelisted(email?: string) { export function isWhitelisted(email?: string) {
if (!ENV_CONFIG.whitelistEmail) { if (!ENV_CONFIG.whitelistEmail) {
@ -20,11 +21,38 @@ export function isWhitelisted(email?: string) {
} }
// TODO: Before open sourcing, we should turn these into env vars // TODO: Before open sourcing, we should turn these into env vars
export function isAdmin(email: string) { export function isAdmin(email?: string) {
if (!email) {
return false
}
return ENV_CONFIG.adminEmails.includes(email) return ENV_CONFIG.adminEmails.includes(email)
} }
export function isManifoldId(userId: string) {
return userId === 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2'
}
export const DOMAIN = ENV_CONFIG.domain export const DOMAIN = ENV_CONFIG.domain
export const FIREBASE_CONFIG = ENV_CONFIG.firebaseConfig export const FIREBASE_CONFIG = ENV_CONFIG.firebaseConfig
export const PROJECT_ID = ENV_CONFIG.firebaseConfig.projectId export const PROJECT_ID = ENV_CONFIG.firebaseConfig.projectId
export const IS_PRIVATE_MANIFOLD = ENV_CONFIG.visibility === 'PRIVATE' export const IS_PRIVATE_MANIFOLD = ENV_CONFIG.visibility === 'PRIVATE'
export const AUTH_COOKIE_NAME = `FBUSER_${PROJECT_ID.toUpperCase().replace(
/-/g,
'_'
)}`
// Manifold's domain or any subdomains thereof
export const CORS_ORIGIN_MANIFOLD = new RegExp(
'^https?://(?:[a-zA-Z0-9\\-]+\\.)*' + escapeRegExp(ENV_CONFIG.domain) + '$'
)
// Vercel deployments, used for testing.
export const CORS_ORIGIN_VERCEL = new RegExp(
'^https?://[a-zA-Z0-9\\-]+' + escapeRegExp('mantic.vercel.app') + '$'
)
// Any localhost server on any port
export const CORS_ORIGIN_LOCALHOST = /^http:\/\/localhost:\d+$/
export function firestoreConsolePath(contractId: string) {
return `https://console.firebase.google.com/project/${PROJECT_ID}/firestore/data/~2Fcontracts~2F${contractId}`
}

View File

@ -2,13 +2,20 @@ import { EnvConfig, PROD_CONFIG } from './prod'
export const DEV_CONFIG: EnvConfig = { export const DEV_CONFIG: EnvConfig = {
...PROD_CONFIG, ...PROD_CONFIG,
domain: 'dev.manifold.markets',
firebaseConfig: { firebaseConfig: {
apiKey: 'AIzaSyBoq3rzUa8Ekyo3ZaTnlycQYPRCA26VpOw', apiKey: 'AIzaSyBoq3rzUa8Ekyo3ZaTnlycQYPRCA26VpOw',
authDomain: 'dev-mantic-markets.firebaseapp.com', authDomain: 'dev-mantic-markets.firebaseapp.com',
projectId: 'dev-mantic-markets', projectId: 'dev-mantic-markets',
region: 'us-central1',
storageBucket: 'dev-mantic-markets.appspot.com', storageBucket: 'dev-mantic-markets.appspot.com',
messagingSenderId: '134303100058', messagingSenderId: '134303100058',
appId: '1:134303100058:web:27f9ea8b83347251f80323', appId: '1:134303100058:web:27f9ea8b83347251f80323',
measurementId: 'G-YJC9E37P37', measurementId: 'G-YJC9E37P37',
}, },
cloudRunId: 'w3txbmd3ba',
cloudRunRegion: 'uc',
amplitudeApiKey: 'fd8cbfd964b9a205b8678a39faae71b3',
twitchBotEndpoint: 'https://dev-twitch-bot.manifold.markets',
sprigEnvironmentId: 'Tu7kRZPm7daP',
} }

View File

@ -1,6 +1,14 @@
export type EnvConfig = { export type EnvConfig = {
domain: string domain: string
firebaseConfig: FirebaseConfig firebaseConfig: FirebaseConfig
amplitudeApiKey?: string
twitchBotEndpoint?: string
sprigEnvironmentId?: string
// IDs for v2 cloud functions -- find these by deploying a cloud function and
// examining the URL, https://[name]-[cloudRunId]-[cloudRunRegion].a.run.app
cloudRunId: string
cloudRunRegion: string
// Access controls // Access controls
adminEmails: string[] adminEmails: string[]
@ -9,15 +17,38 @@ export type EnvConfig = {
// Branding // Branding
moneyMoniker: string // e.g. 'M$' moneyMoniker: string // e.g. 'M$'
bettor?: string // e.g. 'bettor' or 'predictor'
presentBet?: string // e.g. 'bet' or 'predict'
pastBet?: string // e.g. 'bet' or 'prediction'
faviconPath?: string // Should be a file in /public faviconPath?: string // Should be a file in /public
navbarLogoPath?: string navbarLogoPath?: string
newQuestionPlaceholders: string[] newQuestionPlaceholders: string[]
economy?: Economy
}
export type Economy = {
FIXED_ANTE?: number
STARTING_BALANCE?: number
SUS_STARTING_BALANCE?: number
REFERRAL_AMOUNT?: number
UNIQUE_BETTOR_BONUS_AMOUNT?: number
BETTING_STREAK_BONUS_AMOUNT?: number
BETTING_STREAK_BONUS_MAX?: number
BETTING_STREAK_RESET_HOUR?: number
FREE_MARKETS_PER_USER_MAX?: number
COMMENT_BOUNTY_AMOUNT?: number
} }
type FirebaseConfig = { type FirebaseConfig = {
apiKey: string apiKey: string
authDomain: string authDomain: string
projectId: string projectId: string
region?: string
storageBucket: string storageBucket: string
messagingSenderId: string messagingSenderId: string
appId: string appId: string
@ -26,24 +57,39 @@ type FirebaseConfig = {
export const PROD_CONFIG: EnvConfig = { export const PROD_CONFIG: EnvConfig = {
domain: 'manifold.markets', domain: 'manifold.markets',
amplitudeApiKey: '2d6509fd4185ebb8be29709842752a15',
sprigEnvironmentId: 'sQcrq9TDqkib',
firebaseConfig: { firebaseConfig: {
apiKey: 'AIzaSyDp3J57vLeAZCzxLD-vcPaGIkAmBoGOSYw', apiKey: 'AIzaSyDp3J57vLeAZCzxLD-vcPaGIkAmBoGOSYw',
authDomain: 'mantic-markets.firebaseapp.com', authDomain: 'mantic-markets.firebaseapp.com',
projectId: 'mantic-markets', projectId: 'mantic-markets',
region: 'us-central1',
storageBucket: 'mantic-markets.appspot.com', storageBucket: 'mantic-markets.appspot.com',
messagingSenderId: '128925704902', messagingSenderId: '128925704902',
appId: '1:128925704902:web:f61f86944d8ffa2a642dc7', appId: '1:128925704902:web:f61f86944d8ffa2a642dc7',
measurementId: 'G-SSFK1Q138D', measurementId: 'G-SSFK1Q138D',
}, },
twitchBotEndpoint: 'https://twitch-bot.manifold.markets',
cloudRunId: 'nggbo3neva',
cloudRunRegion: 'uc',
adminEmails: [ adminEmails: [
'akrolsmir@gmail.com', // Austin 'akrolsmir@gmail.com', // Austin
'jahooma@gmail.com', // James 'jahooma@gmail.com', // James
'taowell@gmail.com', // Stephen 'taowell@gmail.com', // Stephen
'abc.sinclair@gmail.com', // Sinclair
'manticmarkets@gmail.com', // Manifold 'manticmarkets@gmail.com', // Manifold
'iansphilips@gmail.com', // Ian
'd4vidchee@gmail.com', // D4vid
'federicoruizcassarino@gmail.com', // Fede
'ingawei@gmail.com', //Inga
], ],
visibility: 'PUBLIC', visibility: 'PUBLIC',
moneyMoniker: 'M$', moneyMoniker: 'M$',
bettor: 'trader',
pastBet: 'trade',
presentBet: 'trade',
navbarLogoPath: '', navbarLogoPath: '',
faviconPath: '/favicon.ico', faviconPath: '/favicon.ico',
newQuestionPlaceholders: [ newQuestionPlaceholders: [

View File

@ -6,11 +6,14 @@ export const THEOREMONE_CONFIG: EnvConfig = {
apiKey: 'AIzaSyBSXL6Ys7InNHnCKSy-_E_luhh4Fkj4Z6M', apiKey: 'AIzaSyBSXL6Ys7InNHnCKSy-_E_luhh4Fkj4Z6M',
authDomain: 'theoremone-manifold.firebaseapp.com', authDomain: 'theoremone-manifold.firebaseapp.com',
projectId: 'theoremone-manifold', projectId: 'theoremone-manifold',
region: 'us-central1',
storageBucket: 'theoremone-manifold.appspot.com', storageBucket: 'theoremone-manifold.appspot.com',
messagingSenderId: '698012149198', messagingSenderId: '698012149198',
appId: '1:698012149198:web:b342af75662831aa84b79f', appId: '1:698012149198:web:b342af75662831aa84b79f',
measurementId: 'G-Y3EZ1WNT6E', measurementId: 'G-Y3EZ1WNT6E',
}, },
cloudRunId: 'nggbo3neva', // TODO: fill in real ID for T1
cloudRunRegion: 'uc',
adminEmails: [...PROD_CONFIG.adminEmails, 'david.glidden@theoremone.co'], adminEmails: [...PROD_CONFIG.adminEmails, 'david.glidden@theoremone.co'],
whitelistEmail: '@theoremone.co', whitelistEmail: '@theoremone.co',
moneyMoniker: 'T$', moneyMoniker: 'T$',

9
common/feed.ts Normal file
View File

@ -0,0 +1,9 @@
import { Bet } from './bet'
import { Comment } from './comment'
import { Contract } from './contract'
export type feed = {
contract: Contract
recentBets: Bet[]
recentComments: Comment[]
}[]

View File

@ -1,9 +1,11 @@
export const PLATFORM_FEE = 0.01 export const FLAT_TRADE_FEE = 0.1 // M$0.1
export const CREATOR_FEE = 0.06
export const LIQUIDITY_FEE = 0.06
export const DPM_PLATFORM_FEE = 0.01 export const PLATFORM_FEE = 0
export const DPM_CREATOR_FEE = 0.04 export const CREATOR_FEE = 0
export const LIQUIDITY_FEE = 0
export const DPM_PLATFORM_FEE = 0.0
export const DPM_CREATOR_FEE = 0.0
export const DPM_FEES = DPM_PLATFORM_FEE + DPM_CREATOR_FEE export const DPM_FEES = DPM_PLATFORM_FEE + DPM_CREATOR_FEE
export type Fees = { export type Fees = {

View File

@ -1,23 +0,0 @@
export type Fold = {
id: string
slug: string
name: string
about: string
curatorId: string // User id
createdTime: number
tags: string[]
lowercaseTags: string[]
contractIds: string[]
excludedContractIds: string[]
// Invariant: exactly one of the following is defined.
// Default: creatorIds: undefined, excludedCreatorIds: []
creatorIds?: string[]
excludedCreatorIds?: string[]
followCount: number
disallowMarketCreation?: boolean
}

9
common/follow.ts Normal file
View File

@ -0,0 +1,9 @@
export type Follow = {
userId: string
timestamp: number
}
export type ContractFollow = {
id: string // user id
createdTime: number
}

3
common/globalConfig.ts Normal file
View File

@ -0,0 +1,3 @@
export type GlobalConfig = {
pinnedItems: { itemId: string; type: 'post' | 'contract' }[]
}

42
common/group.ts Normal file
View File

@ -0,0 +1,42 @@
export type Group = {
id: string
slug: string
name: string
about: string
creatorId: string // User id
createdTime: number
mostRecentActivityTime: number
anyoneCanJoin: boolean
totalContracts: number
totalMembers: number
aboutPostId?: string
postIds: string[]
chatDisabled?: boolean
mostRecentContractAddedTime?: number
cachedLeaderboard?: {
topTraders: {
userId: string
score: number
}[]
topCreators: {
userId: string
score: number
}[]
}
pinnedItems: { itemId: string; type: 'post' | 'contract' }[]
}
export const MAX_GROUP_NAME_LENGTH = 75
export const MAX_ABOUT_LENGTH = 140
export const MAX_ID_LENGTH = 60
export const NEW_USER_GROUP_SLUGS = ['updates', 'bugs', 'welcome']
export const GROUP_CHAT_SLUG = 'chat'
export type GroupLink = {
slug: string
name: string
groupId: string
createdTime: number
userId?: string
}
export type GroupContractDoc = { contractId: string; createdTime: number }

9
common/like.ts Normal file
View File

@ -0,0 +1,9 @@
export type Like = {
id: string // will be id of the object liked, i.e. contract.id
userId: string
type: 'contract' | 'post'
createdTime: number
tipTxnId?: string // only holds most recent tip txn id
}
export const LIKE_TIP_AMOUNT = 10
export const TIP_UNDO_DURATION = 2000

138
common/loans.ts Normal file
View File

@ -0,0 +1,138 @@
import { Dictionary, groupBy, sumBy, minBy } from 'lodash'
import { Bet } from './bet'
import { getContractBetMetrics } from './calculate'
import {
Contract,
CPMMContract,
FreeResponseContract,
MultipleChoiceContract,
} from './contract'
import { PortfolioMetrics, User } from './user'
import { filterDefined } from './util/array'
const LOAN_DAILY_RATE = 0.02
const calculateNewLoan = (investedValue: number, loanTotal: number) => {
const netValue = investedValue - loanTotal
return netValue * LOAN_DAILY_RATE
}
export const getLoanUpdates = (
users: User[],
contractsById: { [contractId: string]: Contract },
portfolioByUser: { [userId: string]: PortfolioMetrics | undefined },
betsByUser: { [userId: string]: Bet[] }
) => {
const eligibleUsers = filterDefined(
users.map((user) =>
isUserEligibleForLoan(portfolioByUser[user.id]) ? user : undefined
)
)
const betUpdates = eligibleUsers
.map((user) => {
const updates = calculateLoanBetUpdates(
betsByUser[user.id] ?? [],
contractsById
).betUpdates
return updates.map((update) => ({ ...update, user }))
})
.flat()
const updatesByUser = groupBy(betUpdates, (update) => update.userId)
const userPayouts = Object.values(updatesByUser).map((updates) => {
return {
user: updates[0].user,
payout: sumBy(updates, (update) => update.newLoan),
}
})
return {
betUpdates,
userPayouts,
}
}
const isUserEligibleForLoan = (portfolio: PortfolioMetrics | undefined) => {
if (!portfolio) return true
const { balance, investmentValue } = portfolio
return balance + investmentValue > 0
}
const calculateLoanBetUpdates = (
bets: Bet[],
contractsById: Dictionary<Contract>
) => {
const betsByContract = groupBy(bets, (bet) => bet.contractId)
const contracts = filterDefined(
Object.keys(betsByContract).map((contractId) => contractsById[contractId])
).filter((c) => !c.isResolved)
const betUpdates = filterDefined(
contracts
.map((c) => {
if (c.mechanism === 'cpmm-1') {
return getBinaryContractLoanUpdate(c, betsByContract[c.id])
} else if (
c.outcomeType === 'FREE_RESPONSE' ||
c.outcomeType === 'MULTIPLE_CHOICE'
)
return getFreeResponseContractLoanUpdate(c, betsByContract[c.id])
else {
// Unsupported contract / mechanism for loans.
return []
}
})
.flat()
)
const totalNewLoan = sumBy(betUpdates, (loanUpdate) => loanUpdate.loanTotal)
return {
totalNewLoan,
betUpdates,
}
}
const getBinaryContractLoanUpdate = (contract: CPMMContract, bets: Bet[]) => {
const { invested } = getContractBetMetrics(contract, bets)
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
const oldestBet = minBy(bets, (bet) => bet.createdTime)
const newLoan = calculateNewLoan(invested, loanAmount)
if (!isFinite(newLoan) || newLoan <= 0 || !oldestBet) return undefined
const loanTotal = (oldestBet.loanAmount ?? 0) + newLoan
return {
userId: oldestBet.userId,
contractId: contract.id,
betId: oldestBet.id,
newLoan,
loanTotal,
}
}
const getFreeResponseContractLoanUpdate = (
contract: FreeResponseContract | MultipleChoiceContract,
bets: Bet[]
) => {
const openBets = bets.filter((bet) => !bet.isSold && !bet.sale)
return openBets.map((bet) => {
const loanAmount = bet.loanAmount ?? 0
const newLoan = calculateNewLoan(bet.amount, loanAmount)
const loanTotal = loanAmount + newLoan
if (!isFinite(newLoan) || newLoan <= 0) return undefined
return {
userId: bet.userId,
contractId: contract.id,
betId: bet.id,
newLoan,
loanTotal,
}
})
}

35
common/manalink.ts Normal file
View File

@ -0,0 +1,35 @@
export type Manalink = {
// The link to send: https://manifold.markets/send/{slug}
// Also functions as the unique id for the link.
slug: string
// Note: we assume both fromId and toId are of SourceType 'USER'
fromId: string
// Displayed to people claiming the link
message: string
// How much to send with the link
amount: number
token: 'M$' // TODO: could send eg YES shares too??
createdTime: number
// If null, the link is valid forever
expiresTime: number | null
// If null, the link can be used infinitely
maxUses: number | null
// Used for simpler caching
claimedUserIds: string[]
// Successful redemptions of the link
claims: Claim[]
}
export type Claim = {
toId: string
// The ID of the successful txn that tracks the money moved
txnId: string
claimedTime: number
}

View File

@ -1,70 +1,303 @@
import * as _ from 'lodash' import { sortBy, sum, sumBy } from 'lodash'
import { Bet, MAX_LOAN_PER_CONTRACT } from './bet' import { Bet, fill, LimitBet, NumericBet } from './bet'
import { import {
calculateDpmShares, calculateDpmShares,
getDpmProbability, getDpmProbability,
getDpmOutcomeProbability, getDpmOutcomeProbability,
getNumericBets,
calculateNumericDpmShares,
} from './calculate-dpm' } from './calculate-dpm'
import { calculateCpmmPurchase, getCpmmProbability } from './calculate-cpmm'
import { import {
Binary, calculateCpmmAmountToProb,
CPMM, calculateCpmmPurchase,
DPM, CpmmState,
FreeResponse, getCpmmProbability,
FullContract, } from './calculate-cpmm'
Multi, import {
CPMMBinaryContract,
DPMBinaryContract,
DPMContract,
NumericContract,
PseudoNumericContract,
} from './contract' } from './contract'
import { User } from './user'
import { noFees } from './fees' import { noFees } from './fees'
import { addObjects, removeUndefinedProps } from './util/object'
import { NUMERIC_FIXED_VAR } from './numeric-constants'
import {
floatingEqual,
floatingGreaterEqual,
floatingLesserEqual,
} from './util/math'
export const getNewBinaryCpmmBetInfo = ( export type CandidateBet<T extends Bet = Bet> = Omit<
user: User, T,
outcome: 'YES' | 'NO', 'id' | 'userId' | 'userAvatarUrl' | 'userName' | 'userUsername'
amount: number, >
contract: FullContract<CPMM, Binary>, export type BetInfo = {
loanAmount: number, newBet: CandidateBet
newBetId: string newPool?: { [outcome: string]: number }
) => { newTotalShares?: { [outcome: string]: number }
const { shares, newPool, newP, fees } = calculateCpmmPurchase( newTotalBets?: { [outcome: string]: number }
contract, newTotalLiquidity?: number
amount, newP?: number
outcome
)
const newBalance = user.balance - (amount - loanAmount)
const { pool, p, totalLiquidity } = contract
const probBefore = getCpmmProbability(pool, p)
const probAfter = getCpmmProbability(newPool, newP)
const newBet: Bet = {
id: newBetId,
userId: user.id,
contractId: contract.id,
amount,
shares,
outcome,
fees,
loanAmount,
probBefore,
probAfter,
createdTime: Date.now(),
} }
const { liquidityFee } = fees const computeFill = (
const newTotalLiquidity = (totalLiquidity ?? 0) + liquidityFee amount: number,
outcome: 'YES' | 'NO',
limitProb: number | undefined,
cpmmState: CpmmState,
matchedBet: LimitBet | undefined
) => {
const prob = getCpmmProbability(cpmmState.pool, cpmmState.p)
return { newBet, newPool, newP, newBalance, newTotalLiquidity, fees } if (
limitProb !== undefined &&
(outcome === 'YES'
? floatingGreaterEqual(prob, limitProb) &&
(matchedBet?.limitProb ?? 1) > limitProb
: floatingLesserEqual(prob, limitProb) &&
(matchedBet?.limitProb ?? 0) < limitProb)
) {
// No fill.
return undefined
}
const timestamp = Date.now()
if (
!matchedBet ||
(outcome === 'YES'
? !floatingGreaterEqual(prob, matchedBet.limitProb)
: !floatingLesserEqual(prob, matchedBet.limitProb))
) {
// Fill from pool.
const limit = !matchedBet
? limitProb
: outcome === 'YES'
? Math.min(matchedBet.limitProb, limitProb ?? 1)
: Math.max(matchedBet.limitProb, limitProb ?? 0)
const buyAmount =
limit === undefined
? amount
: Math.min(amount, calculateCpmmAmountToProb(cpmmState, limit, outcome))
const { shares, newPool, newP, fees } = calculateCpmmPurchase(
cpmmState,
buyAmount,
outcome
)
const newState = { pool: newPool, p: newP }
return {
maker: {
matchedBetId: null,
shares,
amount: buyAmount,
state: newState,
fees,
timestamp,
},
taker: {
matchedBetId: null,
shares,
amount: buyAmount,
timestamp,
},
}
}
// Fill from matchedBet.
const matchRemaining = matchedBet.orderAmount - matchedBet.amount
const shares = Math.min(
amount /
(outcome === 'YES' ? matchedBet.limitProb : 1 - matchedBet.limitProb),
matchRemaining /
(outcome === 'YES' ? 1 - matchedBet.limitProb : matchedBet.limitProb)
)
const maker = {
bet: matchedBet,
matchedBetId: 'taker',
amount:
shares *
(outcome === 'YES' ? 1 - matchedBet.limitProb : matchedBet.limitProb),
shares,
timestamp,
}
const taker = {
matchedBetId: matchedBet.id,
amount:
shares *
(outcome === 'YES' ? matchedBet.limitProb : 1 - matchedBet.limitProb),
shares,
timestamp,
}
return { maker, taker }
}
export const computeFills = (
outcome: 'YES' | 'NO',
betAmount: number,
state: CpmmState,
limitProb: number | undefined,
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) => {
if (isNaN(betAmount)) {
throw new Error('Invalid bet amount: ${betAmount}')
}
if (isNaN(limitProb ?? 0)) {
throw new Error('Invalid limitProb: ${limitProb}')
}
const sortedBets = sortBy(
unfilledBets.filter((bet) => bet.outcome !== outcome),
(bet) => (outcome === 'YES' ? bet.limitProb : -bet.limitProb),
(bet) => bet.createdTime
)
const takers: fill[] = []
const makers: {
bet: LimitBet
amount: number
shares: number
timestamp: number
}[] = []
const ordersToCancel: LimitBet[] = []
let amount = betAmount
let cpmmState = { pool: state.pool, p: state.p }
let totalFees = noFees
const currentBalanceByUserId = { ...balanceByUserId }
let i = 0
while (true) {
const matchedBet: LimitBet | undefined = sortedBets[i]
const fill = computeFill(amount, outcome, limitProb, cpmmState, matchedBet)
if (!fill) break
const { taker, maker } = fill
if (maker.matchedBetId === null) {
// Matched against pool.
cpmmState = maker.state
totalFees = addObjects(totalFees, maker.fees)
takers.push(taker)
} else {
// Matched against bet.
i++
const { userId } = maker.bet
const makerBalance = currentBalanceByUserId[userId]
if (floatingGreaterEqual(makerBalance, maker.amount)) {
currentBalanceByUserId[userId] = makerBalance - maker.amount
} else {
// Insufficient balance. Cancel maker bet.
ordersToCancel.push(maker.bet)
continue
}
takers.push(taker)
makers.push(maker)
}
amount -= taker.amount
if (floatingEqual(amount, 0)) break
}
return { takers, makers, totalFees, cpmmState, ordersToCancel }
}
export const getBinaryCpmmBetInfo = (
outcome: 'YES' | 'NO',
betAmount: number,
contract: CPMMBinaryContract | PseudoNumericContract,
limitProb: number | undefined,
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) => {
const { pool, p } = contract
const { takers, makers, cpmmState, totalFees, ordersToCancel } = computeFills(
outcome,
betAmount,
{ pool, p },
limitProb,
unfilledBets,
balanceByUserId
)
const probBefore = getCpmmProbability(contract.pool, contract.p)
const probAfter = getCpmmProbability(cpmmState.pool, cpmmState.p)
const takerAmount = sumBy(takers, 'amount')
const takerShares = sumBy(takers, 'shares')
const isFilled = floatingEqual(betAmount, takerAmount)
const newBet: CandidateBet = removeUndefinedProps({
orderAmount: betAmount,
amount: takerAmount,
shares: takerShares,
limitProb,
isFilled,
isCancelled: false,
fills: takers,
contractId: contract.id,
outcome,
probBefore,
probAfter,
loanAmount: 0,
createdTime: Date.now(),
fees: totalFees,
})
const { liquidityFee } = totalFees
const newTotalLiquidity = (contract.totalLiquidity ?? 0) + liquidityFee
return {
newBet,
newPool: cpmmState.pool,
newP: cpmmState.p,
newTotalLiquidity,
makers,
ordersToCancel,
}
}
export const getBinaryBetStats = (
outcome: 'YES' | 'NO',
betAmount: number,
contract: CPMMBinaryContract | PseudoNumericContract,
limitProb: number,
unfilledBets: LimitBet[],
balanceByUserId: { [userId: string]: number }
) => {
const { newBet } = getBinaryCpmmBetInfo(
outcome,
betAmount ?? 0,
contract,
limitProb,
unfilledBets,
balanceByUserId
)
const remainingMatched =
((newBet.orderAmount ?? 0) - newBet.amount) /
(outcome === 'YES' ? limitProb : 1 - limitProb)
const currentPayout = newBet.shares + remainingMatched
const currentReturn = betAmount ? (currentPayout - betAmount) / betAmount : 0
const totalFees = sum(Object.values(newBet.fees))
return { currentPayout, currentReturn, totalFees, newBet }
} }
export const getNewBinaryDpmBetInfo = ( export const getNewBinaryDpmBetInfo = (
user: User,
outcome: 'YES' | 'NO', outcome: 'YES' | 'NO',
amount: number, amount: number,
contract: FullContract<DPM, Binary>, contract: DPMBinaryContract
loanAmount: number,
newBetId: string
) => { ) => {
const { YES: yesPool, NO: noPool } = contract.pool const { YES: yesPool, NO: noPool } = contract.pool
@ -92,12 +325,10 @@ export const getNewBinaryDpmBetInfo = (
const probBefore = getDpmProbability(contract.totalShares) const probBefore = getDpmProbability(contract.totalShares)
const probAfter = getDpmProbability(newTotalShares) const probAfter = getDpmProbability(newTotalShares)
const newBet: Bet = { const newBet: CandidateBet = {
id: newBetId,
userId: user.id,
contractId: contract.id, contractId: contract.id,
amount, amount,
loanAmount, loanAmount: 0,
shares, shares,
outcome, outcome,
probBefore, probBefore,
@ -106,18 +337,13 @@ export const getNewBinaryDpmBetInfo = (
fees: noFees, fees: noFees,
} }
const newBalance = user.balance - (amount - loanAmount) return { newBet, newPool, newTotalShares, newTotalBets }
return { newBet, newPool, newTotalShares, newTotalBets, newBalance }
} }
export const getNewMultiBetInfo = ( export const getNewMultiBetInfo = (
user: User,
outcome: string, outcome: string,
amount: number, amount: number,
contract: FullContract<DPM, Multi | FreeResponse>, contract: DPMContract
loanAmount: number,
newBetId: string
) => { ) => {
const { pool, totalShares, totalBets } = contract const { pool, totalShares, totalBets } = contract
@ -135,12 +361,10 @@ export const getNewMultiBetInfo = (
const probBefore = getDpmOutcomeProbability(totalShares, outcome) const probBefore = getDpmOutcomeProbability(totalShares, outcome)
const probAfter = getDpmOutcomeProbability(newTotalShares, outcome) const probAfter = getDpmOutcomeProbability(newTotalShares, outcome)
const newBet: Bet = { const newBet: CandidateBet = {
id: newBetId,
userId: user.id,
contractId: contract.id, contractId: contract.id,
amount, amount,
loanAmount, loanAmount: 0,
shares, shares,
outcome, outcome,
probBefore, probBefore,
@ -149,17 +373,48 @@ export const getNewMultiBetInfo = (
fees: noFees, fees: noFees,
} }
const newBalance = user.balance - (amount - loanAmount) return { newBet, newPool, newTotalShares, newTotalBets }
return { newBet, newPool, newTotalShares, newTotalBets, newBalance }
} }
export const getLoanAmount = (yourBets: Bet[], newBetAmount: number) => { export const getNumericBetsInfo = (
const openBets = yourBets.filter((bet) => !bet.isSold && !bet.sale) value: number,
const prevLoanAmount = _.sumBy(openBets, (bet) => bet.loanAmount ?? 0) outcome: string,
const loanAmount = Math.min( amount: number,
newBetAmount, contract: NumericContract
MAX_LOAN_PER_CONTRACT - prevLoanAmount ) => {
const { pool, totalShares, totalBets } = contract
const bets = getNumericBets(contract, outcome, amount, NUMERIC_FIXED_VAR)
const allBetAmounts = Object.fromEntries(bets)
const newTotalBets = addObjects(totalBets, allBetAmounts)
const newPool = addObjects(pool, allBetAmounts)
const { shares, totalShares: newTotalShares } = calculateNumericDpmShares(
contract.totalShares,
bets
) )
return loanAmount
const allOutcomeShares = Object.fromEntries(
bets.map(([outcome], i) => [outcome, shares[i]])
)
const probBefore = getDpmOutcomeProbability(totalShares, outcome)
const probAfter = getDpmOutcomeProbability(newTotalShares, outcome)
const newBet: CandidateBet<NumericBet> = {
contractId: contract.id,
value,
amount,
allBetAmounts,
shares: shares.find((s, i) => bets[i][0] === outcome) ?? 0,
allOutcomeShares,
outcome,
probBefore,
probAfter,
createdTime: Date.now(),
fees: noFees,
}
return { newBet, newPool, newTotalShares, newTotalBets }
} }

View File

@ -1,16 +1,19 @@
import { PHANTOM_ANTE } from './antes' import { range } from 'lodash'
import { import {
Binary, Binary,
Contract, Contract,
CPMM, CPMM,
DPM, DPM,
FreeResponse, FreeResponse,
MultipleChoice,
Numeric,
outcomeType, outcomeType,
PseudoNumeric,
visibility,
} from './contract' } from './contract'
import { User } from './user' import { User } from './user'
import { parseTags } from './util/parse'
import { removeUndefinedProps } from './util/object' import { removeUndefinedProps } from './util/object'
import { calcDpmInitialPool } from './calculate-dpm' import { JSONContent } from '@tiptap/core'
export function getNewContract( export function getNewContract(
id: string, id: string,
@ -18,24 +21,33 @@ export function getNewContract(
creator: User, creator: User,
question: string, question: string,
outcomeType: outcomeType, outcomeType: outcomeType,
description: string, description: JSONContent,
initialProb: number, initialProb: number,
ante: number, ante: number,
closeTime: number, closeTime: number,
extraTags: string[] extraTags: string[],
) {
const tags = parseTags(
`${question} ${description} ${extraTags.map((tag) => `#${tag}`).join(' ')}`
)
const lowercaseTags = tags.map((tag) => tag.toLowerCase())
// used for numeric markets
bucketCount: number,
min: number,
max: number,
isLogScale: boolean,
// for multiple choice
answers: string[],
visibility: visibility
) {
const propsByOutcomeType = const propsByOutcomeType =
outcomeType === 'BINARY' outcomeType === 'BINARY'
? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante) ? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante)
: outcomeType === 'PSEUDO_NUMERIC'
? getPseudoNumericCpmmProps(initialProb, ante, min, max, isLogScale)
: outcomeType === 'NUMERIC'
? getNumericProps(ante, bucketCount, min, max)
: outcomeType === 'MULTIPLE_CHOICE'
? getMultipleChoiceProps(ante, answers)
: getFreeAnswerProps(ante) : getFreeAnswerProps(ante)
const volume = outcomeType === 'BINARY' ? 0 : ante
const contract: Contract = removeUndefinedProps({ const contract: Contract = removeUndefinedProps({
id, id,
slug, slug,
@ -47,18 +59,19 @@ export function getNewContract(
creatorAvatarUrl: creator.avatarUrl, creatorAvatarUrl: creator.avatarUrl,
question: question.trim(), question: question.trim(),
description: description.trim(), description,
tags, tags: [],
lowercaseTags, lowercaseTags: [],
visibility: 'public', visibility,
unlistedById: visibility === 'unlisted' ? creator.id : undefined,
isResolved: false, isResolved: false,
createdTime: Date.now(), createdTime: Date.now(),
lastUpdatedTime: Date.now(),
closeTime, closeTime,
volume, volume: 0,
volume24Hours: 0, volume24Hours: 0,
volume7Days: 0, volume7Days: 0,
elasticity: propsByOutcomeType.mechanism === 'cpmm-1' ? 0.38 : 0.75,
collectedFees: { collectedFees: {
creatorFee: 0, creatorFee: 0,
@ -70,6 +83,9 @@ export function getNewContract(
return contract as Contract return contract as Contract
} }
/*
import { PHANTOM_ANTE } from './antes'
import { calcDpmInitialPool } from './calculate-dpm'
const getBinaryDpmProps = (initialProb: number, ante: number) => { const getBinaryDpmProps = (initialProb: number, ante: number) => {
const { sharesYes, sharesNo, poolYes, poolNo, phantomYes, phantomNo } = const { sharesYes, sharesNo, poolYes, poolNo, phantomYes, phantomNo } =
calcDpmInitialPool(initialProb, ante, PHANTOM_ANTE) calcDpmInitialPool(initialProb, ante, PHANTOM_ANTE)
@ -86,6 +102,7 @@ const getBinaryDpmProps = (initialProb: number, ante: number) => {
return system return system
} }
*/
const getBinaryCpmmProps = (initialProb: number, ante: number) => { const getBinaryCpmmProps = (initialProb: number, ante: number) => {
const pool = { YES: ante, NO: ante } const pool = { YES: ante, NO: ante }
@ -95,9 +112,30 @@ const getBinaryCpmmProps = (initialProb: number, ante: number) => {
mechanism: 'cpmm-1', mechanism: 'cpmm-1',
outcomeType: 'BINARY', outcomeType: 'BINARY',
totalLiquidity: ante, totalLiquidity: ante,
subsidyPool: 0,
initialProbability: p, initialProbability: p,
p, p,
pool: pool, pool: pool,
prob: initialProb,
probChanges: { day: 0, week: 0, month: 0 },
}
return system
}
const getPseudoNumericCpmmProps = (
initialProb: number,
ante: number,
min: number,
max: number,
isLogScale: boolean
) => {
const system: CPMM & PseudoNumeric = {
...getBinaryCpmmProps(initialProb, ante),
outcomeType: 'PSEUDO_NUMERIC',
min,
max,
isLogScale,
} }
return system return system
@ -116,10 +154,53 @@ const getFreeAnswerProps = (ante: number) => {
return system return system
} }
const getMultiProps = ( const getMultipleChoiceProps = (ante: number, answers: string[]) => {
outcomes: string[], const numAnswers = answers.length
initialProbs: number[], const betAnte = ante / numAnswers
ante: number const betShares = Math.sqrt(ante ** 2 / numAnswers)
) => {
// Not implemented. const defaultValues = (x: any) =>
Object.fromEntries(range(0, numAnswers).map((k) => [k, x]))
const system: DPM & MultipleChoice = {
mechanism: 'dpm-2',
outcomeType: 'MULTIPLE_CHOICE',
pool: defaultValues(betAnte),
totalShares: defaultValues(betShares),
totalBets: defaultValues(betAnte),
answers: [],
}
return system
}
const getNumericProps = (
ante: number,
bucketCount: number,
min: number,
max: number
) => {
const buckets = range(0, bucketCount).map((i) => i.toString())
const betAnte = ante / bucketCount
const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte]))
const totalBets = pool
const betShares = Math.sqrt(ante ** 2 / bucketCount)
const totalShares = Object.fromEntries(
buckets.map((answer) => [answer, betShares])
)
const system: DPM & Numeric = {
mechanism: 'dpm-2',
outcomeType: 'NUMERIC',
pool,
totalBets,
totalShares,
bucketCount,
min,
max,
}
return system
} }

270
common/notification.ts Normal file
View File

@ -0,0 +1,270 @@
import { notification_preference } from './user-notification-preferences'
export type Notification = {
id: string
userId: string
reasonText?: string
reason?: notification_reason_types | notification_preference
createdTime: number
viewTime?: number
isSeen: boolean
sourceId?: string
sourceType?: notification_source_types
sourceUpdateType?: notification_source_update_types
sourceContractId?: string
sourceUserName?: string
sourceUserUsername?: string
sourceUserAvatarUrl?: string
sourceText?: string
data?: { [key: string]: any }
sourceContractTitle?: string
sourceContractCreatorUsername?: string
sourceContractSlug?: string
sourceSlug?: string
sourceTitle?: string
isSeenOnHref?: string
}
export type notification_source_types =
| 'contract'
| 'comment'
| 'bet'
| 'answer'
| 'liquidity'
| 'follow'
| 'tip'
| 'admin_message'
| 'group'
| 'user'
| 'bonus'
| 'challenge'
| 'betting_streak_bonus'
| 'loan'
| 'like'
| 'tip_and_like'
| 'badge'
export type notification_source_update_types =
| 'created'
| 'updated'
| 'resolved'
| 'deleted'
| 'closed'
/* Optional - if possible use a notification_preference */
export type notification_reason_types =
| 'tagged_user'
| 'on_new_follow'
| 'contract_from_followed_user'
| 'you_referred_user'
| 'user_joined_to_bet_on_your_market'
| 'unique_bettors_on_your_contract'
| 'tip_received'
| 'bet_fill'
| 'user_joined_from_your_group_invite'
| 'challenge_accepted'
| 'betting_streak_incremented'
| 'loan_income'
| 'liked_and_tipped_your_contract'
| 'comment_on_your_contract'
| 'answer_on_your_contract'
| 'comment_on_contract_you_follow'
| 'answer_on_contract_you_follow'
| 'update_on_contract_you_follow'
| 'resolution_on_contract_you_follow'
| 'comment_on_contract_with_users_shares_in'
| 'answer_on_contract_with_users_shares_in'
| 'update_on_contract_with_users_shares_in'
| 'resolution_on_contract_with_users_shares_in'
| 'comment_on_contract_with_users_answer'
| 'update_on_contract_with_users_answer'
| 'resolution_on_contract_with_users_answer'
| 'answer_on_contract_with_users_answer'
| 'comment_on_contract_with_users_comment'
| 'answer_on_contract_with_users_comment'
| 'update_on_contract_with_users_comment'
| 'resolution_on_contract_with_users_comment'
| 'reply_to_users_answer'
| 'reply_to_users_comment'
| 'your_contract_closed'
| 'subsidized_your_market'
type notification_descriptions = {
[key in notification_preference]: {
simple: string
detailed: string
necessary?: boolean
}
}
export const NOTIFICATION_DESCRIPTIONS: notification_descriptions = {
all_answers_on_my_markets: {
simple: 'Answers on your markets',
detailed: 'Answers on your own markets',
},
all_comments_on_my_markets: {
simple: 'Comments on your markets',
detailed: 'Comments on your own markets',
},
answers_by_followed_users_on_watched_markets: {
simple: 'Only answers by users you follow',
detailed: "Only answers by users you follow on markets you're watching",
},
answers_by_market_creator_on_watched_markets: {
simple: 'Only answers by market creator',
detailed: "Only answers by market creator on markets you're watching",
},
betting_streaks: {
simple: `For prediction streaks`,
detailed: `Bonuses for predictions made over consecutive days (Prediction streaks)})`,
},
comments_by_followed_users_on_watched_markets: {
simple: 'Only comments by users you follow',
detailed:
'Only comments by users that you follow on markets that you watch',
},
contract_from_followed_user: {
simple: 'New markets from users you follow',
detailed: 'New markets from users you follow',
},
limit_order_fills: {
simple: 'Limit order fills',
detailed: 'When your limit order is filled by another user',
},
loan_income: {
simple: 'Automatic loans from your predictions in unresolved markets',
detailed:
'Automatic loans from your predictions that are locked in unresolved markets',
},
market_updates_on_watched_markets: {
simple: 'All creator updates',
detailed: 'All market updates made by the creator',
},
market_updates_on_watched_markets_with_shares_in: {
simple: "Only creator updates on markets that you're invested in",
detailed:
"Only updates made by the creator on markets that you're invested in",
},
on_new_follow: {
simple: 'A user followed you',
detailed: 'A user followed you',
},
onboarding_flow: {
simple: 'Emails to help you get started using Manifold',
detailed: 'Emails to help you learn how to use Manifold',
},
probability_updates_on_watched_markets: {
simple: 'Large changes in probability on markets that you watch',
detailed: 'Large changes in probability on markets that you watch',
},
profit_loss_updates: {
simple: 'Weekly portfolio updates',
detailed: 'Weekly portfolio updates',
},
referral_bonuses: {
simple: 'For referring new users',
detailed: 'Bonuses you receive from referring a new user',
},
resolutions_on_watched_markets: {
simple: 'All market resolutions',
detailed: "All resolutions on markets that you're watching",
},
resolutions_on_watched_markets_with_shares_in: {
simple: "Only market resolutions that you're invested in",
detailed:
"Only resolutions of markets you're watching and that you're invested in",
},
subsidized_your_market: {
simple: 'Your market was subsidized',
detailed: 'When someone subsidizes your market',
},
tagged_user: {
simple: 'A user tagged you',
detailed: 'When another use tags you',
},
thank_you_for_purchases: {
simple: 'Thank you notes for your purchases',
detailed: 'Thank you notes for your purchases',
},
tipped_comments_on_watched_markets: {
simple: 'Only highly tipped comments on markets that you watch',
detailed: 'Only highly tipped comments on markets that you watch',
},
tips_on_your_comments: {
simple: 'Tips on your comments',
detailed: 'Tips on your comments',
},
tips_on_your_markets: {
simple: 'Tips/Likes on your markets',
detailed: 'Tips/Likes on your markets',
},
trending_markets: {
simple: 'Weekly interesting markets',
detailed: 'Weekly interesting markets',
},
unique_bettors_on_your_contract: {
simple: 'For unique predictors on your markets',
detailed: 'Bonuses for unique predictors on your markets',
},
your_contract_closed: {
simple: 'Your market has closed and you need to resolve it (necessary)',
detailed: 'Your market has closed and you need to resolve it (necessary)',
necessary: true,
},
all_comments_on_watched_markets: {
simple: 'All new comments',
detailed: 'All new comments on markets you follow',
},
all_comments_on_contracts_with_shares_in_on_watched_markets: {
simple: `Only on markets you're invested in`,
detailed: `Comments on markets that you're watching and you're invested in`,
},
all_replies_to_my_comments_on_watched_markets: {
simple: 'Only replies to your comments',
detailed: "Only replies to your comments on markets you're watching",
},
all_replies_to_my_answers_on_watched_markets: {
simple: 'Only replies to your answers',
detailed: "Only replies to your answers on markets you're watching",
},
all_answers_on_watched_markets: {
simple: 'All new answers',
detailed: "All new answers on markets you're watching",
},
all_answers_on_contracts_with_shares_in_on_watched_markets: {
simple: `Only on markets you're invested in`,
detailed: `Answers on markets that you're watching and that you're invested in`,
},
badges_awarded: {
simple: 'New badges awarded',
detailed: 'New badges you have earned',
},
opt_out_all: {
simple: 'Opt out of all notifications (excludes when your markets close)',
detailed:
'Opt out of all notifications excluding your own market closure notifications',
},
}
export type BettingStreakData = {
streak: number
bonusAmount: number
}
export type BetFillData = {
betOutcome: string
creatorOutcome: string
probability: number
fillAmount: number
limitOrderTotal?: number
limitOrderRemaining?: number
}
export type ContractResolutionData = {
outcome: string
userPayout: number
userInvestment: number
}

View File

@ -0,0 +1,5 @@
export const NUMERIC_BUCKET_COUNT = 200
export const NUMERIC_FIXED_VAR = 0.005
export const NUMERIC_GRAPH_COLOR = '#5fa5f9'
export const NUMERIC_TEXT_COLOR = 'text-blue-500'

View File

@ -2,8 +2,19 @@
"name": "common", "name": "common",
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"scripts": {}, "scripts": {
"verify": "(cd .. && yarn verify)",
"verify:dir": "npx eslint . --max-warnings 0"
},
"sideEffects": false,
"dependencies": { "dependencies": {
"@tiptap/core": "2.0.0-beta.199",
"@tiptap/extension-image": "2.0.0-beta.199",
"@tiptap/extension-link": "2.0.0-beta.199",
"@tiptap/extension-mention": "2.0.0-beta.199",
"@tiptap/html": "2.0.0-beta.199",
"@tiptap/starter-kit": "2.0.0-beta.199",
"@tiptap/suggestion": "2.0.0-beta.199",
"lodash": "4.17.21" "lodash": "4.17.21"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,44 +1,43 @@
import * as _ from 'lodash' import { sum, groupBy, sumBy, mapValues } from 'lodash'
import { Bet } from './bet' import { Bet, NumericBet } from './bet'
import { deductDpmFees, getDpmProbability } from './calculate-dpm' import { deductDpmFees, getDpmProbability } from './calculate-dpm'
import { DPM, FreeResponse, FullContract, Multi } from './contract'
import { import {
DPM_CREATOR_FEE, DPMContract,
DPM_FEES, FreeResponseContract,
DPM_PLATFORM_FEE, MultipleChoiceContract,
Fees, } from './contract'
noFees, import { DPM_CREATOR_FEE, DPM_FEES, DPM_PLATFORM_FEE } from './fees'
} from './fees'
import { addObjects } from './util/object' import { addObjects } from './util/object'
export const getDpmCancelPayouts = ( export const getDpmCancelPayouts = (contract: DPMContract, bets: Bet[]) => {
contract: FullContract<DPM, any>,
bets: Bet[]
) => {
const { pool } = contract const { pool } = contract
const poolTotal = _.sum(Object.values(pool)) const poolTotal = sum(Object.values(pool))
console.log('resolved N/A, pool M$', poolTotal)
const betSum = _.sumBy(bets, (b) => b.amount) const betSum = sumBy(bets, (b) => b.amount)
const payouts = bets.map((bet) => ({ const payouts = bets.map((bet) => ({
userId: bet.userId, userId: bet.userId,
payout: (bet.amount / betSum) * poolTotal, payout: (bet.amount / betSum) * poolTotal,
})) }))
return [payouts, contract.collectedFees ?? noFees] return {
payouts,
creatorPayout: 0,
liquidityPayouts: [],
collectedFees: contract.collectedFees,
}
} }
export const getDpmStandardPayouts = ( export const getDpmStandardPayouts = (
outcome: string, outcome: string,
contract: FullContract<DPM, any>, contract: DPMContract,
bets: Bet[] bets: Bet[]
) => { ) => {
const winningBets = bets.filter((bet) => bet.outcome === outcome) const winningBets = bets.filter((bet) => bet.outcome === outcome)
const poolTotal = _.sum(Object.values(contract.pool)) const poolTotal = sum(Object.values(contract.pool))
const totalShares = _.sumBy(winningBets, (b) => b.shares) const totalShares = sumBy(winningBets, (b) => b.shares)
const payouts = winningBets.map(({ userId, amount, shares }) => { const payouts = winningBets.map(({ userId, amount, shares }) => {
const winnings = (shares / totalShares) * poolTotal const winnings = (shares / totalShares) * poolTotal
@ -49,38 +48,66 @@ export const getDpmStandardPayouts = (
return { userId, profit, payout } return { userId, profit, payout }
}) })
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
const creatorFee = DPM_CREATOR_FEE * profits const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
const collectedFees = addObjects(contract.collectedFees, {
const finalFees: Fees = {
creatorFee, creatorFee,
platformFee, platformFee,
liquidityFee: 0, liquidityFee: 0,
})
return {
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
creatorPayout: creatorFee,
liquidityPayouts: [],
collectedFees,
}
} }
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? {}) export const getNumericDpmPayouts = (
outcome: string,
contract: DPMContract,
bets: NumericBet[]
) => {
const totalShares = sumBy(bets, (bet) => bet.allOutcomeShares[outcome] ?? 0)
const winningBets = bets.filter((bet) => !!bet.allOutcomeShares[outcome])
console.log( const poolTotal = sum(Object.values(contract.pool))
'resolved',
outcome, const payouts = winningBets.map(
'pool', ({ userId, allBetAmounts, allOutcomeShares }) => {
poolTotal, const shares = allOutcomeShares[outcome] ?? 0
'profits', const winnings = (shares / totalShares) * poolTotal
profits,
'creator fee', const amount = allBetAmounts[outcome] ?? 0
creatorFee const profit = winnings - amount
// profit can be negative if using phantom shares
const payout = amount + (1 - DPM_FEES) * Math.max(0, profit)
return { userId, profit, payout }
}
) )
const totalPayouts = payouts const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
.map(({ userId, payout }) => ({ userId, payout })) const creatorFee = DPM_CREATOR_FEE * profits
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee const platformFee = DPM_PLATFORM_FEE * profits
const collectedFees = addObjects(contract.collectedFees, {
creatorFee,
platformFee,
liquidityFee: 0,
})
return [totalPayouts, fees] return {
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
creatorPayout: creatorFee,
liquidityPayouts: [],
collectedFees,
}
} }
export const getDpmMktPayouts = ( export const getDpmMktPayouts = (
contract: FullContract<DPM, any>, contract: DPMContract,
bets: Bet[], bets: Bet[],
resolutionProbability?: number resolutionProbability?: number
) => { ) => {
@ -89,7 +116,7 @@ export const getDpmMktPayouts = (
? getDpmProbability(contract.totalShares) ? getDpmProbability(contract.totalShares)
: resolutionProbability : resolutionProbability
const weightedShareTotal = _.sumBy(bets, (b) => const weightedShareTotal = sumBy(bets, (b) =>
b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares
) )
@ -103,88 +130,62 @@ export const getDpmMktPayouts = (
return { userId, profit, payout } return { userId, profit, payout }
}) })
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit)) const profits = sumBy(payouts, (po) => Math.max(0, po.profit))
const creatorFee = DPM_CREATOR_FEE * profits const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
const collectedFees = addObjects(contract.collectedFees, {
const finalFees: Fees = {
creatorFee, creatorFee,
platformFee, platformFee,
liquidityFee: 0, liquidityFee: 0,
})
return {
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
creatorPayout: creatorFee,
liquidityPayouts: [],
collectedFees,
} }
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? {})
console.log(
'resolved MKT',
p,
'pool',
pool,
'profits',
profits,
'creator fee',
creatorFee
)
const totalPayouts = payouts
.map(({ userId, payout }) => ({ userId, payout }))
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee
return [totalPayouts, fees]
} }
export const getPayoutsMultiOutcome = ( export const getPayoutsMultiOutcome = (
resolutions: { [outcome: string]: number }, resolutions: { [outcome: string]: number },
contract: FullContract<DPM, Multi | FreeResponse>, contract: FreeResponseContract | MultipleChoiceContract,
bets: Bet[] bets: Bet[]
) => { ) => {
const poolTotal = _.sum(Object.values(contract.pool)) const poolTotal = sum(Object.values(contract.pool))
const winningBets = bets.filter((bet) => resolutions[bet.outcome]) const winningBets = bets.filter((bet) => resolutions[bet.outcome])
const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome) const betsByOutcome = groupBy(winningBets, (bet) => bet.outcome)
const sharesByOutcome = _.mapValues(betsByOutcome, (bets) => const sharesByOutcome = mapValues(betsByOutcome, (bets) =>
_.sumBy(bets, (bet) => bet.shares) sumBy(bets, (bet) => bet.shares)
) )
const probTotal = _.sum(Object.values(resolutions)) const probTotal = sum(Object.values(resolutions))
const payouts = winningBets.map(({ userId, outcome, amount, shares }) => { const payouts = winningBets.map(({ userId, outcome, amount, shares }) => {
const prob = resolutions[outcome] / probTotal const prob = resolutions[outcome] / probTotal
const winnings = (shares / sharesByOutcome[outcome]) * prob * poolTotal const winnings = (shares / sharesByOutcome[outcome]) * prob * poolTotal
const profit = winnings - amount const profit = winnings - amount
const payout = amount + (1 - DPM_FEES) * Math.max(0, profit) const payout = amount + (1 - DPM_FEES) * profit
return { userId, profit, payout } return { userId, profit, payout }
}) })
const profits = _.sumBy(payouts, (po) => po.profit) const profits = sumBy(payouts, (po) => po.profit)
const creatorFee = DPM_CREATOR_FEE * profits const creatorFee = DPM_CREATOR_FEE * profits
const platformFee = DPM_PLATFORM_FEE * profits const platformFee = DPM_PLATFORM_FEE * profits
const collectedFees = addObjects(contract.collectedFees, {
const finalFees: Fees = {
creatorFee, creatorFee,
platformFee, platformFee,
liquidityFee: 0, liquidityFee: 0,
})
return {
payouts: payouts.map(({ userId, payout }) => ({ userId, payout })),
creatorPayout: creatorFee,
liquidityPayouts: [],
collectedFees,
} }
const fees = addObjects<Fees>(finalFees, contract.collectedFees ?? noFees)
console.log(
'resolved',
resolutions,
'pool',
poolTotal,
'profits',
profits,
'creator fee',
creatorFee
)
const totalPayouts = payouts
.map(({ userId, payout }) => ({ userId, payout }))
.concat([{ userId: contract.creatorId, payout: creatorFee }]) // add creator fee
return [totalPayouts, fees]
} }

View File

@ -1,8 +1,8 @@
import * as _ from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { getProbability } from './calculate' import { getProbability } from './calculate'
import { Binary, CPMM, FixedPayouts, FullContract } from './contract' import { getCpmmLiquidityPoolWeights } from './calculate-cpmm'
import { CPMMContract } from './contract'
import { noFees } from './fees'
import { LiquidityProvision } from './liquidity-provision' import { LiquidityProvision } from './liquidity-provision'
export const getFixedCancelPayouts = ( export const getFixedCancelPayouts = (
@ -14,18 +14,21 @@ export const getFixedCancelPayouts = (
payout: lp.amount, payout: lp.amount,
})) }))
return bets const payouts = bets
.filter((b) => !b.isAnte && !b.isLiquidityProvision) .filter((b) => !b.isAnte && !b.isLiquidityProvision)
.map((bet) => ({ .map((bet) => ({
userId: bet.userId, userId: bet.userId,
payout: bet.amount, payout: bet.amount,
})) }))
.concat(liquidityPayouts)
const creatorPayout = 0
return { payouts, creatorPayout, liquidityPayouts, collectedFees: noFees }
} }
export const getStandardFixedPayouts = ( export const getStandardFixedPayouts = (
outcome: string, outcome: string,
contract: FullContract<FixedPayouts, Binary>, contract: CPMMContract,
bets: Bet[], bets: Bet[],
liquidities: LiquidityProvision[] liquidities: LiquidityProvision[]
) => { ) => {
@ -36,43 +39,36 @@ export const getStandardFixedPayouts = (
payout: shares, payout: shares,
})) }))
const creatorPayout = contract.collectedFees.creatorFee const { collectedFees } = contract
const creatorPayout = collectedFees.creatorFee
console.log( const liquidityPayouts = getLiquidityPoolPayouts(
'resolved', contract,
outcome, outcome,
'pool', liquidities
contract.pool[outcome],
'payouts',
_.sum(payouts),
'creator fee',
creatorPayout
) )
return payouts return { payouts, creatorPayout, liquidityPayouts, collectedFees }
.map(({ userId, payout }) => ({ userId, payout }))
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
.concat(getLiquidityPoolPayouts(contract, outcome, liquidities))
} }
export const getLiquidityPoolPayouts = ( export const getLiquidityPoolPayouts = (
contract: FullContract<CPMM, Binary>, contract: CPMMContract,
outcome: string, outcome: string,
liquidities: LiquidityProvision[] liquidities: LiquidityProvision[]
) => { ) => {
const providedLiquidity = _.sumBy(liquidities, (lp) => lp.liquidity) const { pool, subsidyPool } = contract
const finalPool = pool[outcome] + (subsidyPool ?? 0)
if (finalPool < 1e-3) return []
const { pool } = contract const weights = getCpmmLiquidityPoolWeights(liquidities)
const finalPool = pool[outcome]
return liquidities.map((lp) => ({ return Object.entries(weights).map(([providerId, weight]) => ({
userId: lp.userId, userId: providerId,
payout: (lp.liquidity / providedLiquidity) * finalPool, payout: weight * finalPool,
})) }))
} }
export const getMktFixedPayouts = ( export const getMktFixedPayouts = (
contract: FullContract<FixedPayouts, Binary>, contract: CPMMContract,
bets: Bet[], bets: Bet[],
liquidities: LiquidityProvision[], liquidities: LiquidityProvision[],
resolutionProbability?: number resolutionProbability?: number
@ -87,37 +83,26 @@ export const getMktFixedPayouts = (
return { userId, payout: betP * shares } return { userId, payout: betP * shares }
}) })
const creatorPayout = contract.collectedFees.creatorFee const { collectedFees } = contract
const creatorPayout = collectedFees.creatorFee
const liquidityPayouts = getLiquidityPoolProbPayouts(contract, p, liquidities)
console.log( return { payouts, creatorPayout, liquidityPayouts, collectedFees }
'resolved PROB',
p,
'pool',
p * contract.pool.YES + (1 - p) * contract.pool.NO,
'payouts',
_.sum(payouts),
'creator fee',
creatorPayout
)
return payouts
.map(({ userId, payout }) => ({ userId, payout }))
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
.concat(getLiquidityPoolProbPayouts(contract, p, liquidities))
} }
export const getLiquidityPoolProbPayouts = ( export const getLiquidityPoolProbPayouts = (
contract: FullContract<CPMM, Binary>, contract: CPMMContract,
p: number, p: number,
liquidities: LiquidityProvision[] liquidities: LiquidityProvision[]
) => { ) => {
const providedLiquidity = _.sumBy(liquidities, (lp) => lp.liquidity) const { pool, subsidyPool } = contract
const finalPool = p * pool.YES + (1 - p) * pool.NO + (subsidyPool ?? 0)
if (finalPool < 1e-3) return []
const { pool } = contract const weights = getCpmmLiquidityPoolWeights(liquidities)
const finalPool = p * pool.YES + (1 - p) * pool.NO
return liquidities.map((lp) => ({ return Object.entries(weights).map(([providerId, weight]) => ({
userId: lp.userId, userId: providerId,
payout: (lp.liquidity / providedLiquidity) * finalPool, payout: weight * finalPool,
})) }))
} }

View File

@ -1,14 +1,11 @@
import * as _ from 'lodash' import { sumBy, groupBy, mapValues } from 'lodash'
import { Bet } from './bet' import { Bet, NumericBet } from './bet'
import { import {
Binary,
Contract, Contract,
DPM, CPMMBinaryContract,
FixedPayouts, DPMContract,
FreeResponse, PseudoNumericContract,
FullContract,
Multi,
} from './contract' } from './contract'
import { Fees } from './fees' import { Fees } from './fees'
import { LiquidityProvision } from './liquidity-provision' import { LiquidityProvision } from './liquidity-provision'
@ -16,6 +13,7 @@ import {
getDpmCancelPayouts, getDpmCancelPayouts,
getDpmMktPayouts, getDpmMktPayouts,
getDpmStandardPayouts, getDpmStandardPayouts,
getNumericDpmPayouts,
getPayoutsMultiOutcome, getPayoutsMultiOutcome,
} from './payouts-dpm' } from './payouts-dpm'
import { import {
@ -31,50 +29,67 @@ export type Payout = {
export const getLoanPayouts = (bets: Bet[]): Payout[] => { export const getLoanPayouts = (bets: Bet[]): Payout[] => {
const betsWithLoans = bets.filter((bet) => bet.loanAmount) const betsWithLoans = bets.filter((bet) => bet.loanAmount)
const betsByUser = _.groupBy(betsWithLoans, (bet) => bet.userId) const betsByUser = groupBy(betsWithLoans, (bet) => bet.userId)
const loansByUser = _.mapValues(betsByUser, (bets) => const loansByUser = mapValues(betsByUser, (bets) =>
_.sumBy(bets, (bet) => -(bet.loanAmount ?? 0)) sumBy(bets, (bet) => -(bet.loanAmount ?? 0))
) )
return _.toPairs(loansByUser).map(([userId, payout]) => ({ userId, payout })) return Object.entries(loansByUser).map(([userId, payout]) => ({
userId,
payout,
}))
}
export const groupPayoutsByUser = (payouts: Payout[]) => {
const groups = groupBy(payouts, (payout) => payout.userId)
return mapValues(groups, (group) => sumBy(group, (g) => g.payout))
}
export type PayoutInfo = {
payouts: Payout[]
creatorPayout: number
liquidityPayouts: Payout[]
collectedFees: Fees
} }
export const getPayouts = ( export const getPayouts = (
outcome: string, outcome: string | undefined,
resolutions: {
[outcome: string]: number
},
contract: Contract, contract: Contract,
bets: Bet[], bets: Bet[],
liquidities: LiquidityProvision[], liquidities: LiquidityProvision[],
resolutions?: {
[outcome: string]: number
},
resolutionProbability?: number resolutionProbability?: number
): [Payout[], Fees] => { ): PayoutInfo => {
if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') { if (
const payouts = getFixedPayouts( contract.mechanism === 'cpmm-1' &&
(contract.outcomeType === 'BINARY' ||
contract.outcomeType === 'PSEUDO_NUMERIC')
) {
return getFixedPayouts(
outcome, outcome,
contract, contract,
bets, bets,
liquidities, liquidities,
resolutionProbability resolutionProbability
) )
return [payouts, contract.collectedFees]
} }
return getDpmPayouts( return getDpmPayouts(
outcome, outcome,
resolutions,
contract, contract,
bets, bets,
resolutions,
resolutionProbability resolutionProbability
) )
} }
export const getFixedPayouts = ( export const getFixedPayouts = (
outcome: string, outcome: string | undefined,
contract: FullContract<FixedPayouts, Binary>, contract: CPMMBinaryContract | PseudoNumericContract,
bets: Bet[], bets: Bet[],
liquidities: LiquidityProvision[], liquidities: LiquidityProvision[],
resolutionProbability?: number resolutionProbability?: number
): Payout[] => { ) => {
switch (outcome) { switch (outcome) {
case 'YES': case 'YES':
case 'NO': case 'NO':
@ -93,43 +108,36 @@ export const getFixedPayouts = (
} }
export const getDpmPayouts = ( export const getDpmPayouts = (
outcome: string, outcome: string | undefined,
resolutions: { contract: DPMContract,
bets: Bet[],
resolutions?: {
[outcome: string]: number [outcome: string]: number
}, },
contract: Contract,
bets: Bet[],
resolutionProbability?: number resolutionProbability?: number
) => { ): PayoutInfo => {
const openBets = bets.filter((b) => !b.isSold && !b.sale) const openBets = bets.filter((b) => !b.isSold && !b.sale)
const { outcomeType } = contract
switch (outcome) { switch (outcome) {
case 'YES': case 'YES':
case 'NO': case 'NO':
return getDpmStandardPayouts(outcome, contract, openBets) as [ return getDpmStandardPayouts(outcome, contract, openBets)
Payout[],
Fees
]
case 'MKT': case 'MKT':
return contract.outcomeType === 'FREE_RESPONSE' return outcomeType === 'FREE_RESPONSE' ||
? (getPayoutsMultiOutcome( outcomeType === 'MULTIPLE_CHOICE' // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
resolutions, ? getPayoutsMultiOutcome(resolutions!, contract, openBets)
contract as FullContract<DPM, Multi | FreeResponse>, : getDpmMktPayouts(contract, openBets, resolutionProbability)
openBets
) as [Payout[], Fees])
: (getDpmMktPayouts(contract, openBets, resolutionProbability) as [
Payout[],
Fees
])
case 'CANCEL': case 'CANCEL':
return getDpmCancelPayouts(contract, openBets) as [Payout[], Fees] case undefined:
return getDpmCancelPayouts(contract, openBets)
default: default:
if (outcomeType === 'NUMERIC')
return getNumericDpmPayouts(outcome, contract, openBets as NumericBet[])
// Outcome is a free response answer id. // Outcome is a free response answer id.
return getDpmStandardPayouts(outcome, contract, openBets) as [ return getDpmStandardPayouts(outcome, contract, openBets)
Payout[],
Fees
]
} }
} }

29
common/post.ts Normal file
View File

@ -0,0 +1,29 @@
import { JSONContent } from '@tiptap/core'
export type Post = {
id: string
title: string
subtitle: string
content: JSONContent
creatorId: string // User id
createdTime: number
slug: string
// denormalized user fields
creatorName: string
creatorUsername: string
creatorAvatarUrl?: string
likedByUserIds?: string[]
likedByUserCount?: number
}
export type DateDoc = Post & {
bounty: number
birthday: number
type: 'date-doc'
contractSlug: string
}
export const MAX_POST_TITLE_LENGTH = 480
export const MAX_POST_SUBTITLE_LENGTH = 480

48
common/pseudo-numeric.ts Normal file
View File

@ -0,0 +1,48 @@
import { BinaryContract, PseudoNumericContract } from './contract'
import { formatLargeNumber, formatPercent } from './util/format'
export function formatNumericProbability(
p: number,
contract: PseudoNumericContract
) {
const value = getMappedValue(contract)(p)
return formatLargeNumber(value)
}
export const getMappedValue =
(contract: PseudoNumericContract | BinaryContract) => (p: number) => {
if (contract.outcomeType === 'BINARY') return p
const { min, max, isLogScale } = contract
if (isLogScale) {
const logValue = p * Math.log10(max - min + 1)
return 10 ** logValue + min - 1
}
return p * (max - min) + min
}
export const getFormattedMappedValue =
(contract: PseudoNumericContract | BinaryContract) => (p: number) => {
if (contract.outcomeType === 'BINARY') return formatPercent(p)
const value = getMappedValue(contract)(p)
return formatLargeNumber(value)
}
export const getPseudoProbability = (
value: number,
min: number,
max: number,
isLogScale = false
) => {
if (value < min) return 0
if (value > max) return 1
if (isLogScale) {
return Math.log10(value - min + 1) / Math.log10(max - min + 1)
}
return (value - min) / (max - min)
}

View File

@ -0,0 +1,27 @@
import { groupBy, mapValues, sum, sumBy } from 'lodash'
import { Txn } from './txn'
// Returns a map of charity ids to the amount of M$ matched
export function quadraticMatches(
allCharityTxns: Txn[],
matchingPool: number
): Record<string, number> {
// For each charity, group the donations by each individual donor
const donationsByCharity = groupBy(allCharityTxns, 'toId')
const donationsByDonors = mapValues(donationsByCharity, (txns) =>
groupBy(txns, 'fromId')
)
// Weight for each charity = [sum of sqrt(individual donor)] ^ 2
const weights = mapValues(donationsByDonors, (byDonor) => {
const sumByDonor = Object.values(byDonor).map((txns) =>
sumBy(txns, 'amount')
)
const sumOfRoots = sumBy(sumByDonor, Math.sqrt)
return sumOfRoots ** 2
})
// Then distribute the matching pool based on the individual weights
const totalWeight = sum(Object.values(weights))
return mapValues(weights, (weight) => matchingPool * (weight / totalWeight))
}

58
common/redeem.ts Normal file
View File

@ -0,0 +1,58 @@
import { partition, sumBy } from 'lodash'
import { Bet } from './bet'
import { getProbability } from './calculate'
import { CPMMContract } from './contract'
import { noFees } from './fees'
import { CandidateBet } from './new-bet'
type RedeemableBet = Pick<Bet, 'outcome' | 'shares' | 'loanAmount'>
export const getRedeemableAmount = (bets: RedeemableBet[]) => {
const [yesBets, noBets] = partition(bets, (b) => b.outcome === 'YES')
const yesShares = sumBy(yesBets, (b) => b.shares)
const noShares = sumBy(noBets, (b) => b.shares)
const shares = Math.max(Math.min(yesShares, noShares), 0)
const soldFrac =
shares > 0
? Math.min(yesShares, noShares) / Math.max(yesShares, noShares)
: 0
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
const loanPayment = loanAmount * soldFrac
const netAmount = shares - loanPayment
return { shares, loanPayment, netAmount }
}
export const getRedemptionBets = (
shares: number,
loanPayment: number,
contract: CPMMContract
) => {
const p = getProbability(contract)
const createdTime = Date.now()
const yesBet: CandidateBet = {
contractId: contract.id,
amount: p * -shares,
shares: -shares,
loanAmount: loanPayment ? -loanPayment / 2 : 0,
outcome: 'YES',
probBefore: p,
probAfter: p,
createdTime,
isRedemption: true,
fees: noFees,
}
const noBet: CandidateBet = {
contractId: contract.id,
amount: (1 - p) * -shares,
shares: -shares,
loanAmount: loanPayment ? -loanPayment / 2 : 0,
outcome: 'NO',
probBefore: p,
probAfter: p,
createdTime,
isRedemption: true,
fees: noFees,
}
return [yesBet, noBet]
}

View File

@ -1,13 +1,19 @@
import * as _ from 'lodash' import { groupBy, sumBy, mapValues, keyBy, sortBy } from 'lodash'
import { Bet } from './bet' import { Bet } from './bet'
import { Binary, Contract, FullContract } from './contract' import { getContractBetMetrics, resolvedPayout } from './calculate'
import { getPayouts } from './payouts' import { Contract } from './contract'
import { ContractComment } from './comment'
export function scoreCreators(contracts: Contract[], bets: Bet[][]) { export function scoreCreators(contracts: Contract[]) {
const creatorScore = _.mapValues( const creatorScore = mapValues(
_.groupBy(contracts, ({ creatorId }) => creatorId), groupBy(contracts, ({ creatorId }) => creatorId),
(contracts) => _.sumBy(contracts, ({ pool }) => pool.YES + pool.NO) (contracts) =>
sumBy(
contracts.map((contract) => {
return contract.volume
})
)
) )
return creatorScore return creatorScore
@ -24,46 +30,12 @@ export function scoreTraders(contracts: Contract[], bets: Bet[][]) {
return userScores return userScores
} }
export function scoreUsersByContract( export function scoreUsersByContract(contract: Contract, bets: Bet[]) {
contract: FullContract<any, Binary>, const betsByUser = groupBy(bets, (bet) => bet.userId)
bets: Bet[] return mapValues(
) { betsByUser,
const { resolution, resolutionProbability } = contract (bets) => getContractBetMetrics(contract, bets).profit
const [closedBets, openBets] = _.partition(
bets,
(bet) => bet.isSold || bet.sale
) )
const [resolvePayouts] = getPayouts(
resolution,
{},
contract,
openBets,
[],
resolutionProbability
)
const salePayouts = closedBets.map((bet) => {
const { userId, sale } = bet
return { userId, payout: sale ? sale.amount : 0 }
})
const investments = bets
.filter((bet) => !bet.sale)
.map((bet) => {
const { userId, amount, loanAmount } = bet
const payout = -amount - (loanAmount ?? 0)
return { userId, payout }
})
const netPayouts = [...resolvePayouts, ...salePayouts, ...investments]
const userScore = _.mapValues(
_.groupBy(netPayouts, (payout) => payout.userId),
(payouts) => _.sumBy(payouts, ({ payout }) => payout)
)
return userScore
} }
export function addUserScores( export function addUserScores(
@ -75,3 +47,47 @@ export function addUserScores(
dest[userId] += score dest[userId] += score
} }
} }
export function scoreCommentorsAndBettors(
contract: Contract,
bets: Bet[],
comments: ContractComment[]
) {
const commentsById = keyBy(comments, 'id')
const betsById = keyBy(bets, 'id')
// If 'id2' is the sale of 'id1', both are logged with (id2 - id1) of profit
// Otherwise, we record the profit at resolution time
const profitById: Record<string, number> = {}
for (const bet of bets) {
if (bet.sale) {
const originalBet = betsById[bet.sale.betId]
const profit = bet.sale.amount - originalBet.amount
profitById[bet.id] = profit
profitById[originalBet.id] = profit
} else {
profitById[bet.id] = resolvedPayout(contract, bet) - bet.amount
}
}
// Now find the betId with the highest profit
const topBetId = sortBy(bets, (b) => -profitById[b.id])[0]?.id
const topBettor = betsById[topBetId]?.userName
// And also the commentId of the comment with the highest profit
const topCommentId = sortBy(
comments,
(c) => c.betId && -profitById[c.betId]
)[0]?.id
const topCommentBetId = commentsById[topCommentId]?.betId
return {
topCommentId,
topBetId,
topBettor,
profitById,
commentsById,
betsById,
topCommentBetId,
}
}

View File

@ -1,20 +1,20 @@
import { Bet } from './bet' import { Bet, LimitBet } from './bet'
import { import {
getDpmProbability,
calculateDpmShareValue, calculateDpmShareValue,
deductDpmFees, deductDpmFees,
getDpmOutcomeProbability,
} from './calculate-dpm' } from './calculate-dpm'
import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm' import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm'
import { Binary, DPM, CPMM, FullContract } from './contract' import { CPMMContract, DPMContract } from './contract'
import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees' import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees'
import { User } from './user' import { sumBy } from 'lodash'
export const getSellBetInfo = ( export type CandidateBet<T extends Bet> = Omit<
user: User, T,
bet: Bet, 'id' | 'userId' | 'userAvatarUrl' | 'userName' | 'userUsername'
contract: FullContract<DPM, any>, >
newBetId: string
) => { export const getSellBetInfo = (bet: Bet, contract: DPMContract) => {
const { pool, totalShares, totalBets } = contract const { pool, totalShares, totalBets } = contract
const { id: betId, amount, shares, outcome, loanAmount } = bet const { id: betId, amount, shares, outcome, loanAmount } = bet
@ -29,8 +29,8 @@ export const getSellBetInfo = (
const newTotalBets = { ...totalBets, [outcome]: totalBets[outcome] - amount } const newTotalBets = { ...totalBets, [outcome]: totalBets[outcome] - amount }
const probBefore = getDpmProbability(totalShares) const probBefore = getDpmOutcomeProbability(totalShares, outcome)
const probAfter = getDpmProbability(newTotalShares) const probAfter = getDpmOutcomeProbability(newTotalShares, outcome)
const profit = adjShareValue - amount const profit = adjShareValue - amount
@ -54,9 +54,7 @@ export const getSellBetInfo = (
creatorFee creatorFee
) )
const newBet: Bet = { const newBet: CandidateBet<Bet> = {
id: newBetId,
userId: user.id,
contractId: contract.id, contractId: contract.id,
amount: -adjShareValue, amount: -adjShareValue,
shares: -shares, shares: -shares,
@ -69,41 +67,41 @@ export const getSellBetInfo = (
betId, betId,
}, },
fees, fees,
loanAmount: -(loanAmount ?? 0),
} }
const newBalance = user.balance + saleAmount - (loanAmount ?? 0)
return { return {
newBet, newBet,
newPool, newPool,
newTotalShares, newTotalShares,
newTotalBets, newTotalBets,
newBalance,
fees, fees,
} }
} }
export const getCpmmSellBetInfo = ( export const getCpmmSellBetInfo = (
user: User,
shares: number, shares: number,
outcome: 'YES' | 'NO', outcome: 'YES' | 'NO',
contract: FullContract<CPMM, Binary>, contract: CPMMContract,
prevLoanAmount: number, unfilledBets: LimitBet[],
newBetId: string balanceByUserId: { [userId: string]: number },
loanPaid: number
) => { ) => {
const { pool, p } = contract const { pool, p } = contract
const { saleValue, newPool, newP, fees } = calculateCpmmSale( const { saleValue, cpmmState, fees, makers, takers, ordersToCancel } = calculateCpmmSale(
contract, contract,
shares, shares,
outcome outcome,
unfilledBets,
balanceByUserId,
) )
const loanPaid = Math.min(prevLoanAmount, saleValue)
const netAmount = saleValue - loanPaid
const probBefore = getCpmmProbability(pool, p) const probBefore = getCpmmProbability(pool, p)
const probAfter = getCpmmProbability(newPool, p) const probAfter = getCpmmProbability(cpmmState.pool, cpmmState.p)
const takerAmount = sumBy(takers, 'amount')
const takerShares = sumBy(takers, 'shares')
console.log( console.log(
'SELL M$', 'SELL M$',
@ -115,27 +113,29 @@ export const getCpmmSellBetInfo = (
fees.creatorFee fees.creatorFee
) )
const newBet: Bet = { const newBet: CandidateBet<Bet> = {
id: newBetId,
userId: user.id,
contractId: contract.id, contractId: contract.id,
amount: -saleValue, amount: takerAmount,
shares: -shares, shares: takerShares,
outcome, outcome,
probBefore, probBefore,
probAfter, probAfter,
createdTime: Date.now(), createdTime: Date.now(),
loanAmount: -loanPaid, loanAmount: -loanPaid,
fees, fees,
fills: takers,
isFilled: true,
isCancelled: false,
orderAmount: takerAmount,
} }
const newBalance = user.balance + netAmount
return { return {
newBet, newBet,
newPool, newPool: cpmmState.pool,
newP, newP: cpmmState.p,
newBalance,
fees, fees,
makers,
takers,
ordersToCancel
} }
} }

25
common/stats.ts Normal file
View File

@ -0,0 +1,25 @@
export type Stats = {
startDate: number
dailyActiveUsers: number[]
dailyActiveUsersWeeklyAvg: number[]
weeklyActiveUsers: number[]
monthlyActiveUsers: number[]
d1: number[]
d1WeeklyAvg: number[]
nd1: number[]
nd1WeeklyAvg: number[]
nw1: number[]
dailyBetCounts: number[]
dailyContractCounts: number[]
dailyCommentCounts: number[]
dailySignups: number[]
weekOnWeekRetention: number[]
monthlyRetention: number[]
dailyActivationRate: number[]
dailyActivationRateWeeklyAvg: number[]
manaBet: {
daily: number[]
weekly: number[]
monthly: number[]
}
}

18
common/tracking.ts Normal file
View File

@ -0,0 +1,18 @@
export type View = {
contractId: string
timestamp: number
}
export type UserEvent = ClickEvent
export type ClickEvent = {
type: 'click'
contractId: string
timestamp: number
}
export type LatencyEvent = {
type: 'feed' | 'portfolio'
latency: number
timestamp: number
}

14
common/tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"baseUrl": "../",
"composite": true,
"module": "commonjs",
"moduleResolution": "node",
"noImplicitReturns": true,
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017"
},
"include": ["**/*.ts"]
}

140
common/txn.ts Normal file
View File

@ -0,0 +1,140 @@
// A txn (pronounced "texan") respresents a payment between two ids on Manifold
// Shortened from "transaction" to distinguish from Firebase transactions (and save chars)
type AnyTxnType =
| Donation
| Tip
| Manalink
| Referral
| UniqueBettorBonus
| BettingStreakBonus
| CancelUniqueBettorBonus
| CommentBountyRefund
type SourceType = 'USER' | 'CONTRACT' | 'CHARITY' | 'BANK'
export type Txn<T extends AnyTxnType = AnyTxnType> = {
id: string
createdTime: number
fromId: string
fromType: SourceType
toId: string
toType: SourceType
amount: number
token: 'M$' // | 'USD' | MarketOutcome
category:
| 'CHARITY'
| 'MANALINK'
| 'TIP'
| 'REFERRAL'
| 'UNIQUE_BETTOR_BONUS'
| 'BETTING_STREAK_BONUS'
| 'CANCEL_UNIQUE_BETTOR_BONUS'
| 'COMMENT_BOUNTY'
| 'REFUND_COMMENT_BOUNTY'
// Any extra data
data?: { [key: string]: any }
// Human-readable description
description?: string
} & T
type Donation = {
fromType: 'USER'
toType: 'CHARITY'
category: 'CHARITY'
}
type Tip = {
fromType: 'USER'
toType: 'USER'
category: 'TIP'
data: {
commentId: string
contractId?: string
groupId?: string
}
}
type Manalink = {
fromType: 'USER'
toType: 'USER'
category: 'MANALINK'
}
type Referral = {
fromType: 'BANK'
toType: 'USER'
category: 'REFERRAL'
}
type UniqueBettorBonus = {
fromType: 'BANK'
toType: 'USER'
category: 'UNIQUE_BETTOR_BONUS'
data: {
contractId: string
uniqueNewBettorId?: string
// Old unique bettor bonus txns stored all unique bettor ids
uniqueBettorIds?: string[]
}
}
type BettingStreakBonus = {
fromType: 'BANK'
toType: 'USER'
category: 'BETTING_STREAK_BONUS'
data: {
currentBettingStreak?: number
}
}
type CancelUniqueBettorBonus = {
fromType: 'USER'
toType: 'BANK'
category: 'CANCEL_UNIQUE_BETTOR_BONUS'
data: {
contractId: string
}
}
type CommentBountyDeposit = {
fromType: 'USER'
toType: 'BANK'
category: 'COMMENT_BOUNTY'
data: {
contractId: string
}
}
type CommentBountyWithdrawal = {
fromType: 'BANK'
toType: 'USER'
category: 'COMMENT_BOUNTY'
data: {
contractId: string
commentId: string
}
}
type CommentBountyRefund = {
fromType: 'BANK'
toType: 'USER'
category: 'REFUND_COMMENT_BOUNTY'
data: {
contractId: string
}
}
export type DonationTxn = Txn & Donation
export type TipTxn = Txn & Tip
export type ManalinkTxn = Txn & Manalink
export type ReferralTxn = Txn & Referral
export type BettingStreakBonusTxn = Txn & BettingStreakBonus
export type UniqueBettorBonusTxn = Txn & UniqueBettorBonus
export type CancelUniqueBettorBonusTxn = Txn & CancelUniqueBettorBonus
export type CommentBountyDepositTxn = Txn & CommentBountyDeposit
export type CommentBountyWithdrawalTxn = Txn & CommentBountyWithdrawal

View File

@ -0,0 +1,222 @@
import { filterDefined } from './util/array'
import { notification_reason_types } from './notification'
import { getFunctionUrl } from './api'
import { DOMAIN } from './envs/constants'
import { PrivateUser } from './user'
export type notification_destination_types = 'email' | 'browser'
export type notification_preference = keyof notification_preferences
export type notification_preferences = {
// Watched Markets
all_comments_on_watched_markets: notification_destination_types[]
all_answers_on_watched_markets: notification_destination_types[]
// Comments
tipped_comments_on_watched_markets: notification_destination_types[]
comments_by_followed_users_on_watched_markets: notification_destination_types[]
all_replies_to_my_comments_on_watched_markets: notification_destination_types[]
all_replies_to_my_answers_on_watched_markets: notification_destination_types[]
all_comments_on_contracts_with_shares_in_on_watched_markets: notification_destination_types[]
// Answers
answers_by_followed_users_on_watched_markets: notification_destination_types[]
answers_by_market_creator_on_watched_markets: notification_destination_types[]
all_answers_on_contracts_with_shares_in_on_watched_markets: notification_destination_types[]
// On users' markets
your_contract_closed: notification_destination_types[]
all_comments_on_my_markets: notification_destination_types[]
all_answers_on_my_markets: notification_destination_types[]
subsidized_your_market: notification_destination_types[]
// Market updates
resolutions_on_watched_markets: notification_destination_types[]
resolutions_on_watched_markets_with_shares_in: notification_destination_types[]
market_updates_on_watched_markets: notification_destination_types[]
market_updates_on_watched_markets_with_shares_in: notification_destination_types[]
probability_updates_on_watched_markets: notification_destination_types[]
// Balance Changes
loan_income: notification_destination_types[]
betting_streaks: notification_destination_types[]
referral_bonuses: notification_destination_types[]
unique_bettors_on_your_contract: notification_destination_types[]
tips_on_your_comments: notification_destination_types[]
tips_on_your_markets: notification_destination_types[]
limit_order_fills: notification_destination_types[]
// General
tagged_user: notification_destination_types[]
on_new_follow: notification_destination_types[]
contract_from_followed_user: notification_destination_types[]
trending_markets: notification_destination_types[]
profit_loss_updates: notification_destination_types[]
onboarding_flow: notification_destination_types[]
thank_you_for_purchases: notification_destination_types[]
badges_awarded: notification_destination_types[]
opt_out_all: notification_destination_types[]
// When adding a new notification preference, use add-new-notification-preference.ts to existing users
}
export const getDefaultNotificationPreferences = (
userId: string,
privateUser?: PrivateUser,
noEmails?: boolean
) => {
const constructPref = (browserIf: boolean, emailIf: boolean) => {
const browser = browserIf ? 'browser' : undefined
const email = noEmails ? undefined : emailIf ? 'email' : undefined
return filterDefined([browser, email]) as notification_destination_types[]
}
const defaults: notification_preferences = {
// Watched Markets
all_comments_on_watched_markets: constructPref(true, false),
all_answers_on_watched_markets: constructPref(true, false),
// Comments
tips_on_your_comments: constructPref(true, true),
comments_by_followed_users_on_watched_markets: constructPref(true, true),
all_replies_to_my_comments_on_watched_markets: constructPref(true, true),
all_replies_to_my_answers_on_watched_markets: constructPref(true, true),
all_comments_on_contracts_with_shares_in_on_watched_markets: constructPref(
true,
false
),
// Answers
answers_by_followed_users_on_watched_markets: constructPref(true, true),
answers_by_market_creator_on_watched_markets: constructPref(true, true),
all_answers_on_contracts_with_shares_in_on_watched_markets: constructPref(
true,
true
),
// On users' markets
your_contract_closed: constructPref(true, true), // High priority
all_comments_on_my_markets: constructPref(true, true),
all_answers_on_my_markets: constructPref(true, true),
subsidized_your_market: constructPref(true, true),
// Market updates
resolutions_on_watched_markets: constructPref(true, false),
market_updates_on_watched_markets: constructPref(true, false),
market_updates_on_watched_markets_with_shares_in: constructPref(
true,
false
),
resolutions_on_watched_markets_with_shares_in: constructPref(true, true),
//Balance Changes
loan_income: constructPref(true, false),
betting_streaks: constructPref(true, false),
referral_bonuses: constructPref(true, true),
unique_bettors_on_your_contract: constructPref(true, true),
tipped_comments_on_watched_markets: constructPref(true, true),
tips_on_your_markets: constructPref(true, true),
limit_order_fills: constructPref(true, false),
// General
tagged_user: constructPref(true, true),
on_new_follow: constructPref(true, true),
contract_from_followed_user: constructPref(true, true),
trending_markets: constructPref(false, true),
profit_loss_updates: constructPref(false, true),
probability_updates_on_watched_markets: constructPref(true, false),
thank_you_for_purchases: constructPref(false, false),
onboarding_flow: constructPref(false, false),
opt_out_all: [],
badges_awarded: constructPref(true, false),
}
return defaults
}
// Adding a new key:value here is optional, you can just use a key of notification_subscription_types
// You might want to add a key:value here if there will be multiple notification reasons that map to the same
// subscription type, i.e. 'comment_on_contract_you_follow' and 'comment_on_contract_with_users_answer' both map to
// 'all_comments_on_watched_markets' subscription type
// TODO: perhaps better would be to map notification_subscription_types to arrays of notification_reason_types
const notificationReasonToSubscriptionType: Partial<
Record<notification_reason_types, notification_preference>
> = {
you_referred_user: 'referral_bonuses',
user_joined_to_bet_on_your_market: 'referral_bonuses',
tip_received: 'tips_on_your_comments',
bet_fill: 'limit_order_fills',
user_joined_from_your_group_invite: 'referral_bonuses',
challenge_accepted: 'limit_order_fills',
betting_streak_incremented: 'betting_streaks',
liked_and_tipped_your_contract: 'tips_on_your_markets',
comment_on_your_contract: 'all_comments_on_my_markets',
answer_on_your_contract: 'all_answers_on_my_markets',
comment_on_contract_you_follow: 'all_comments_on_watched_markets',
answer_on_contract_you_follow: 'all_answers_on_watched_markets',
update_on_contract_you_follow: 'market_updates_on_watched_markets',
resolution_on_contract_you_follow: 'resolutions_on_watched_markets',
comment_on_contract_with_users_shares_in:
'all_comments_on_contracts_with_shares_in_on_watched_markets',
answer_on_contract_with_users_shares_in:
'all_answers_on_contracts_with_shares_in_on_watched_markets',
update_on_contract_with_users_shares_in:
'market_updates_on_watched_markets_with_shares_in',
resolution_on_contract_with_users_shares_in:
'resolutions_on_watched_markets_with_shares_in',
comment_on_contract_with_users_answer: 'all_comments_on_watched_markets',
update_on_contract_with_users_answer: 'market_updates_on_watched_markets',
resolution_on_contract_with_users_answer: 'resolutions_on_watched_markets',
answer_on_contract_with_users_answer: 'all_answers_on_watched_markets',
comment_on_contract_with_users_comment: 'all_comments_on_watched_markets',
answer_on_contract_with_users_comment: 'all_answers_on_watched_markets',
update_on_contract_with_users_comment: 'market_updates_on_watched_markets',
resolution_on_contract_with_users_comment: 'resolutions_on_watched_markets',
reply_to_users_answer: 'all_replies_to_my_answers_on_watched_markets',
reply_to_users_comment: 'all_replies_to_my_comments_on_watched_markets',
}
export const getNotificationDestinationsForUser = (
privateUser: PrivateUser,
// TODO: accept reasons array from most to least important and work backwards
reason: notification_reason_types | notification_preference
) => {
const notificationSettings = privateUser.notificationPreferences
const unsubscribeEndpoint = getFunctionUrl('unsubscribe')
try {
let destinations
let subscriptionType: notification_preference | undefined
if (Object.keys(notificationSettings).includes(reason)) {
subscriptionType = reason as notification_preference
destinations = notificationSettings[subscriptionType]
} else {
const key = reason as notification_reason_types
subscriptionType = notificationReasonToSubscriptionType[key]
destinations = subscriptionType
? notificationSettings[subscriptionType]
: []
}
const optOutOfAllSettings = notificationSettings['opt_out_all']
// Your market closure notifications are high priority, opt-out doesn't affect their delivery
const optedOutOfEmail =
optOutOfAllSettings.includes('email') &&
subscriptionType !== 'your_contract_closed'
const optedOutOfBrowser =
optOutOfAllSettings.includes('browser') &&
subscriptionType !== 'your_contract_closed'
return {
sendToEmail: destinations.includes('email') && !optedOutOfEmail,
sendToBrowser: destinations.includes('browser') && !optedOutOfBrowser,
unsubscribeUrl: `${unsubscribeEndpoint}?id=${privateUser.id}&type=${subscriptionType}`,
urlToManageThisNotification: `${DOMAIN}/notifications?tab=settings&section=${subscriptionType}`,
}
} catch (e) {
// Fail safely
console.log(
`couldn't get notification destinations for type ${reason} for user ${privateUser.id}`
)
return {
sendToEmail: false,
sendToBrowser: false,
unsubscribeUrl: '',
urlToManageThisNotification: '',
}
}
}

View File

@ -1,3 +1,7 @@
import { notification_preferences } from './user-notification-preferences'
import { ENV_CONFIG } from './envs/constants'
import { MarketCreatorBadge, ProvenCorrectBadge, StreakerBadge } from './badge'
export type User = { export type User = {
id: string id: string
createdTime: number createdTime: number
@ -8,28 +12,96 @@ export type User = {
// For their user page // For their user page
bio?: string bio?: string
bannerUrl?: string
website?: string website?: string
twitterHandle?: string twitterHandle?: string
discordHandle?: string discordHandle?: string
balance: number balance: number
totalDeposits: number totalDeposits: number
totalPnLCached: number
creatorVolumeCached: number profitCached: {
daily: number
weekly: number
monthly: number
allTime: number
} }
export const STARTING_BALANCE = 1000 creatorVolumeCached: {
export const SUS_STARTING_BALANCE = 10 // for sus users, i.e. multiple sign ups for same person daily: number
weekly: number
monthly: number
allTime: number
}
fractionResolvedCorrectly: number
nextLoanCached: number
followerCountCached: number
followedCategories?: string[]
homeSections?: string[]
referredByUserId?: string
referredByContractId?: string
referredByGroupId?: string
lastPingTime?: number
shouldShowWelcome?: boolean
lastBetTime?: number
currentBettingStreak?: number
hasSeenContractFollowModal?: boolean
freeMarketsCreated?: number
isBannedFromPosting?: boolean
achievements: {
provenCorrect?: {
badges: ProvenCorrectBadge[]
}
marketCreator?: {
badges: MarketCreatorBadge[]
}
streaker?: {
badges: StreakerBadge[]
}
}
}
export type PrivateUser = { export type PrivateUser = {
id: string // same as User.id id: string // same as User.id
username: string // denormalized from User username: string // denormalized from User
email?: string email?: string
unsubscribedFromResolutionEmails?: boolean weeklyTrendingEmailSent?: boolean
unsubscribedFromCommentEmails?: boolean weeklyPortfolioUpdateEmailSent?: boolean
unsubscribedFromAnswerEmails?: boolean manaBonusEmailSent?: boolean
initialDeviceToken?: string initialDeviceToken?: string
initialIpAddress?: string initialIpAddress?: string
apiKey?: string
notificationPreferences: notification_preferences
twitchInfo?: {
twitchName: string
controlToken: string
botEnabled?: boolean
needsRelinking?: boolean
} }
}
export type PortfolioMetrics = {
investmentValue: number
balance: number
totalDeposits: number
timestamp: number
userId: string
}
export const MANIFOLD_USER_USERNAME = 'ManifoldMarkets'
export const MANIFOLD_USER_NAME = 'ManifoldMarkets'
export const MANIFOLD_AVATAR_URL = 'https://manifold.markets/logo-bg-white.png'
// TODO: remove. Hardcoding the strings would be better.
// Different views require different language.
export const BETTOR = ENV_CONFIG.bettor ?? 'trader'
export const BETTORS = ENV_CONFIG.bettor + 's' ?? 'traders'
export const PRESENT_BET = ENV_CONFIG.presentBet ?? 'trade'
export const PRESENT_BETS = ENV_CONFIG.presentBet + 's' ?? 'trades'
export const PAST_BET = ENV_CONFIG.pastBet ?? 'trade'
export const PAST_BETS = ENV_CONFIG.pastBet + 's' ?? 'trades'

22
common/util/algos.ts Normal file
View File

@ -0,0 +1,22 @@
export function binarySearch(
min: number,
max: number,
comparator: (x: number) => number
) {
let mid = 0
while (true) {
mid = min + (max - min) / 2
// Break once we've reached max precision.
if (mid === min || mid === max) break
const comparison = comparator(mid)
if (comparison === 0) break
else if (comparison > 0) {
max = mid
} else {
min = mid
}
}
return mid
}

View File

@ -1,3 +1,40 @@
import { isEqual } from 'lodash'
export function filterDefined<T>(array: (T | null | undefined)[]) { export function filterDefined<T>(array: (T | null | undefined)[]) {
return array.filter((item) => item !== null && item !== undefined) as T[] return array.filter((item) => item !== null && item !== undefined) as T[]
} }
export function buildArray<T>(
...params: (T | T[] | false | undefined | null)[]
) {
const array: T[] = []
for (const el of params) {
if (Array.isArray(el)) {
array.push(...el)
} else if (el) {
array.push(el)
}
}
return array
}
export function groupConsecutive<T, U>(xs: T[], key: (x: T) => U) {
if (!xs.length) {
return []
}
const result = []
let curr = { key: key(xs[0]), items: [xs[0]] }
for (const x of xs.slice(1)) {
const k = key(x)
if (!isEqual(key, curr.key)) {
result.push(curr)
curr = { key: k, items: [x] }
} else {
curr.items.push(x)
}
}
result.push(curr)
return result
}

View File

@ -7,6 +7,6 @@ export const cleanUsername = (name: string, maxLength = 25) => {
.substring(0, maxLength) .substring(0, maxLength)
} }
export const cleanDisplayName = (displayName: string, maxLength = 25) => { export const cleanDisplayName = (displayName: string, maxLength = 30) => {
return displayName.replace(/\s+/g, ' ').substring(0, maxLength).trim() return displayName.replace(/\s+/g, ' ').substring(0, maxLength).trim()
} }

24
common/util/color.ts Normal file
View File

@ -0,0 +1,24 @@
export const interpolateColor = (color1: string, color2: string, p: number) => {
const rgb1 = parseInt(color1.replace('#', ''), 16)
const rgb2 = parseInt(color2.replace('#', ''), 16)
const [r1, g1, b1] = toArray(rgb1)
const [r2, g2, b2] = toArray(rgb2)
const q = 1 - p
const rr = Math.round(r1 * q + r2 * p)
const rg = Math.round(g1 * q + g2 * p)
const rb = Math.round(b1 * q + b2 * p)
const hexString = Number((rr << 16) + (rg << 8) + rb).toString(16)
const hex = `#${'0'.repeat(6 - hexString.length)}${hexString}`
return hex
}
function toArray(rgb: number) {
const r = rgb >> 16
const g = (rgb >> 8) % 256
const b = rgb % 256
return [r, g, b]
}

View File

@ -7,30 +7,31 @@ const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0, minimumFractionDigits: 0,
}) })
const formatterCents = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
maximumFractionDigits: 2,
minimumFractionDigits: 2,
})
export function formatMoney(amount: number) { export function formatMoney(amount: number) {
const newAmount = Math.round(amount) === 0 ? 0 : amount // handle -0 case const newAmount =
return ( // handle -0 case
ENV_CONFIG.moneyMoniker + ' ' + formatter.format(newAmount).replace('$', '') Math.round(amount) === 0
? 0
: // Handle 499.9999999999999 case
(amount > 0 ? Math.floor : Math.ceil)(
amount + 0.00000000001 * Math.sign(amount)
) )
return ENV_CONFIG.moneyMoniker + formatter.format(newAmount).replace('$', '')
} }
export function formatCents(probability: number) {
const newAmount = Math.round(probability * 100) === 0 ? 0 : probability // handle -0 case export function formatMoneyWithDecimals(amount: number) {
return ( return ENV_CONFIG.moneyMoniker + amount.toFixed(2)
ENV_CONFIG.moneyMoniker +
' ' +
formatterCents.format(newAmount).replace('$', '')
)
} }
export function formatWithCommas(amount: number) { export function formatWithCommas(amount: number) {
return formatter.format(amount).replace('$', '') return formatter.format(Math.floor(amount)).replace('$', '')
}
export function manaToUSD(mana: number) {
return (mana / 100).toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
})
} }
export function formatPercent(zeroToOne: number) { export function formatPercent(zeroToOne: number) {
@ -39,6 +40,36 @@ export function formatPercent(zeroToOne: number) {
return (zeroToOne * 100).toFixed(decimalPlaces) + '%' return (zeroToOne * 100).toFixed(decimalPlaces) + '%'
} }
const showPrecision = (x: number, sigfigs: number) =>
// convert back to number for weird formatting reason
`${Number(x.toPrecision(sigfigs))}`
// Eg 1234567.89 => 1.23M; 5678 => 5.68K
export function formatLargeNumber(num: number, sigfigs = 2): string {
const absNum = Math.abs(num)
if (absNum < 1) return showPrecision(num, sigfigs)
if (absNum < 100) return showPrecision(num, 2)
if (absNum < 1000) return showPrecision(num, 3)
if (absNum < 10000) return showPrecision(num, 4)
const suffix = ['', 'K', 'M', 'B', 'T', 'Q']
const i = Math.floor(Math.log10(absNum) / 3)
const numStr = showPrecision(num / Math.pow(10, 3 * i), sigfigs)
return `${numStr}${suffix[i] ?? ''}`
}
export function shortFormatNumber(num: number): string {
if (num < 1000) return showPrecision(num, 3)
const suffix = ['', 'K', 'M', 'B', 'T', 'Q']
const i = Math.floor(Math.log10(num) / 3)
const numStr = showPrecision(num / Math.pow(10, 3 * i), 2)
return `${numStr}${suffix[i] ?? ''}`
}
export function toCamelCase(words: string) { export function toCamelCase(words: string) {
const camelCase = words const camelCase = words
.split(' ') .split(' ')

50
common/util/math.ts Normal file
View File

@ -0,0 +1,50 @@
import { sortBy, sum } from 'lodash'
export const logInterpolation = (min: number, max: number, value: number) => {
if (value <= min) return 0
if (value >= max) return 1
return Math.log(value - min + 1) / Math.log(max - min + 1)
}
export function normpdf(x: number, mean = 0, variance = 1) {
if (variance === 0) {
return x === mean ? Infinity : 0
}
return (
Math.exp((-0.5 * Math.pow(x - mean, 2)) / variance) /
Math.sqrt(TAU * variance)
)
}
export const TAU = Math.PI * 2
export function median(xs: number[]) {
if (xs.length === 0) return NaN
const sorted = sortBy(xs, (x) => x)
const mid = Math.floor(sorted.length / 2)
if (sorted.length % 2 === 0) {
return (sorted[mid - 1] + sorted[mid]) / 2
}
return sorted[mid]
}
export function average(xs: number[]) {
return sum(xs) / xs.length
}
const EPSILON = 0.00000001
export function floatingEqual(a: number, b: number, epsilon = EPSILON) {
return Math.abs(a - b) < epsilon
}
export function floatingGreaterEqual(a: number, b: number, epsilon = EPSILON) {
return a + epsilon >= b
}
export function floatingLesserEqual(a: number, b: number, epsilon = EPSILON) {
return a - epsilon <= b
}

View File

@ -1,9 +1,9 @@
import * as _ from 'lodash' import { union } from 'lodash'
export const removeUndefinedProps = <T>(obj: T): T => { export const removeUndefinedProps = <T extends object>(obj: T): T => {
let newObj: any = {} const newObj: any = {}
for (let key of Object.keys(obj)) { for (const key of Object.keys(obj)) {
if ((obj as any)[key] !== undefined) newObj[key] = (obj as any)[key] if ((obj as any)[key] !== undefined) newObj[key] = (obj as any)[key]
} }
@ -14,12 +14,26 @@ export const addObjects = <T extends { [key: string]: number }>(
obj1: T, obj1: T,
obj2: T obj2: T
) => { ) => {
const keys = _.union(Object.keys(obj1), Object.keys(obj2)) const keys = union(Object.keys(obj1), Object.keys(obj2))
const newObj = {} as any const newObj = {} as any
for (let key of keys) { for (const key of keys) {
newObj[key] = (obj1[key] ?? 0) + (obj2[key] ?? 0) newObj[key] = (obj1[key] ?? 0) + (obj2[key] ?? 0)
} }
return newObj as T return newObj as T
} }
export const subtractObjects = <T extends { [key: string]: number }>(
obj1: T,
obj2: T
) => {
const keys = union(Object.keys(obj1), Object.keys(obj2))
const newObj = {} as any
for (const key of keys) {
newObj[key] = (obj1[key] ?? 0) - (obj2[key] ?? 0)
}
return newObj as T
}

View File

@ -1,29 +1,115 @@
import { MAX_TAG_LENGTH } from '../contract' import { generateText, JSONContent, Node } from '@tiptap/core'
import { generateJSON } from '@tiptap/html'
// Tiptap starter extensions
import { Blockquote } from '@tiptap/extension-blockquote'
import { Bold } from '@tiptap/extension-bold'
import { BulletList } from '@tiptap/extension-bullet-list'
import { Code } from '@tiptap/extension-code'
import { CodeBlock } from '@tiptap/extension-code-block'
import { Document } from '@tiptap/extension-document'
import { HardBreak } from '@tiptap/extension-hard-break'
import { Heading } from '@tiptap/extension-heading'
import { History } from '@tiptap/extension-history'
import { HorizontalRule } from '@tiptap/extension-horizontal-rule'
import { Italic } from '@tiptap/extension-italic'
import { ListItem } from '@tiptap/extension-list-item'
import { OrderedList } from '@tiptap/extension-ordered-list'
import { Paragraph } from '@tiptap/extension-paragraph'
import { Strike } from '@tiptap/extension-strike'
import { Text } from '@tiptap/extension-text'
// other tiptap extensions
import { Image } from '@tiptap/extension-image'
import { Link } from '@tiptap/extension-link'
import { Mention } from '@tiptap/extension-mention'
import Iframe from './tiptap-iframe'
import TiptapTweet from './tiptap-tweet-type'
import { find } from 'linkifyjs'
import { uniq } from 'lodash'
import { TiptapSpoiler } from './tiptap-spoiler'
export function parseTags(text: string) { /** get first url in text. like "notion.so " -> "http://notion.so"; "notion" -> null */
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi export function getUrl(text: string) {
const matches = (text.match(regex) || []).map((match) => const results = find(text, 'url')
match.trim().substring(1).substring(0, MAX_TAG_LENGTH) return results.length ? results[0].href : null
)
const tagSet = new Set()
const uniqueTags: string[] = []
// Keep casing of last tag.
matches.reverse()
for (const tag of matches) {
const lowercase = tag.toLowerCase()
if (!tagSet.has(lowercase)) {
tagSet.add(lowercase)
uniqueTags.push(tag)
}
}
uniqueTags.reverse()
return uniqueTags
} }
export function parseWordsAsTags(text: string) { // TODO: fuzzy matching
const taggedText = text export const wordIn = (word: string, corpus: string) =>
.split(/\s+/) corpus.toLocaleLowerCase().includes(word.toLocaleLowerCase())
.map((tag) => (tag.startsWith('#') ? tag : `#${tag}`))
.join(' ') const checkAgainstQuery = (query: string, corpus: string) =>
return parseTags(taggedText) query.split(' ').every((word) => wordIn(word, corpus))
export const searchInAny = (query: string, ...fields: string[]) =>
fields.some((field) => checkAgainstQuery(query, field))
/** @return user ids of all \@mentions */
export function parseMentions(data: JSONContent): string[] {
const mentions = data.content?.flatMap(parseMentions) ?? [] //dfs
if (data.type === 'mention' && data.attrs) {
mentions.push(data.attrs.id as string)
}
return uniq(mentions)
}
// TODO: this is a hack to get around the fact that tiptap doesn't have a
// way to add a node view without bundling in tsx
function skippableComponent(name: string): Node<any, any> {
return Node.create({
name,
group: 'block',
content: 'inline*',
parseHTML() {
return [
{
tag: 'grid-cards-component',
},
]
},
})
}
const stringParseExts = [
// StarterKit extensions
Blockquote,
Bold,
BulletList,
Code,
CodeBlock,
Document,
HardBreak,
Heading,
History,
HorizontalRule,
Italic,
ListItem,
OrderedList,
Paragraph,
Strike,
Text,
// other extensions
Link,
Image.extend({ renderText: () => '[image]' }),
Mention, // user @mention
Mention.extend({ name: 'contract-mention' }), // market %mention
Iframe.extend({
renderText: ({ node }) =>
'[embed]' + node.attrs.src ? `(${node.attrs.src})` : '',
}),
skippableComponent('gridCardsComponent'),
skippableComponent('staticReactEmbedComponent'),
TiptapTweet.extend({ renderText: () => '[tweet]' }),
TiptapSpoiler.extend({ renderHTML: () => ['span', '[spoiler]', 0] }),
]
export function richTextToString(text?: JSONContent) {
if (!text) return ''
return generateText(text, stringParseExts)
}
export function htmlToRichText(html: string) {
return generateJSON(html, stringParseExts)
} }

View File

@ -5,7 +5,8 @@ export const randomString = (length = 12) =>
export function genHash(str: string) { export function genHash(str: string) {
// xmur3 // xmur3
for (var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) { let h: number
for (let i = 0, h = 1779033703 ^ str.length; i < str.length; i++) {
h = Math.imul(h ^ str.charCodeAt(i), 3432918353) h = Math.imul(h ^ str.charCodeAt(i), 3432918353)
h = (h << 13) | (h >>> 19) h = (h << 13) | (h >>> 19)
} }
@ -28,7 +29,7 @@ export function createRNG(seed: string) {
b >>>= 0 b >>>= 0
c >>>= 0 c >>>= 0
d >>>= 0 d >>>= 0
var t = (a + b) | 0 let t = (a + b) | 0
a = b ^ (b >>> 9) a = b ^ (b >>> 9)
b = (c + (c << 3)) | 0 b = (c + (c << 3)) | 0
c = (c << 21) | (c >>> 11) c = (c << 21) | (c >>> 11)
@ -39,11 +40,16 @@ export function createRNG(seed: string) {
} }
} }
export const shuffle = (array: any[], rand: () => number) => { export const shuffle = (array: unknown[], rand: () => number) => {
for (let i = 0; i < array.length; i++) { for (let i = 0; i < array.length; i++) {
const swapIndex = Math.floor(rand() * (array.length - i)) const swapIndex = Math.floor(rand() * (array.length - i))
const temp = array[i] ;[array[i], array[swapIndex]] = [array[swapIndex], array[i]]
array[i] = array[swapIndex]
array[swapIndex] = temp
} }
} }
export function chooseRandomSubset<T>(items: T[], count: number) {
const fiveMinutes = 5 * 60 * 1000
const seed = Math.round(Date.now() / fiveMinutes).toString()
shuffle(items, createRNG(seed))
return items.slice(0, count)
}

6
common/util/time.ts Normal file
View File

@ -0,0 +1,6 @@
export const MINUTE_MS = 60 * 1000
export const HOUR_MS = 60 * MINUTE_MS
export const DAY_MS = 24 * HOUR_MS
export const sleep = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms))

View File

@ -0,0 +1,100 @@
// Adopted from https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/iframe.ts
import { Node } from '@tiptap/core'
export interface IframeOptions {
allowFullscreen: boolean
HTMLAttributes: {
[key: string]: any
}
}
declare module '@tiptap/core' {
interface Commands<ReturnType> {
iframe: {
setIframe: (options: { src: string }) => ReturnType
}
}
}
// These classes style the outer wrapper and the inner iframe;
// Adopted from css in https://github.com/ueberdosis/tiptap/blob/main/demos/src/Experiments/Embeds/Vue/index.vue
const wrapperClasses = 'relative h-auto w-full overflow-hidden'
const iframeClasses = 'absolute top-0 left-0 h-full w-full'
export default Node.create<IframeOptions>({
name: 'iframe',
group: 'block',
atom: true,
addOptions() {
return {
allowFullscreen: true,
HTMLAttributes: {
class: 'iframe-wrapper' + ' ' + wrapperClasses,
// Tailwind JIT doesn't seem to pick up `pb-[20rem]`, so we hack this in:
style: 'padding-bottom: 20rem; ',
},
}
},
addAttributes() {
return {
src: {
default: null,
},
frameborder: {
default: 0,
},
height: {
default: 0,
},
allowfullscreen: {
default: this.options.allowFullscreen,
parseHTML: () => this.options.allowFullscreen,
},
}
},
parseHTML() {
return [{ tag: 'iframe' }]
},
renderHTML({ HTMLAttributes }) {
this.options.HTMLAttributes.style =
this.options.HTMLAttributes.style +
' height: ' +
HTMLAttributes.height +
';'
return [
'div',
this.options.HTMLAttributes,
[
'iframe',
{
...HTMLAttributes,
class: HTMLAttributes.class + ' ' + iframeClasses,
},
],
]
},
addCommands() {
return {
setIframe:
(options: { src: string }) =>
({ tr, dispatch }) => {
const { selection } = tr
const node = this.type.create(options)
if (dispatch) {
tr.replaceRangeWith(selection.from, selection.to, node)
}
return true
},
}
},
})

View File

@ -0,0 +1,116 @@
// adapted from @n8body/tiptap-spoiler
import {
Mark,
markInputRule,
markPasteRule,
mergeAttributes,
} from '@tiptap/core'
import type { ElementType } from 'react'
declare module '@tiptap/core' {
interface Commands<ReturnType> {
spoilerEditor: {
setSpoiler: () => ReturnType
toggleSpoiler: () => ReturnType
unsetSpoiler: () => ReturnType
}
}
}
export type SpoilerOptions = {
HTMLAttributes: Record<string, any>
spoilerOpenClass: string
spoilerCloseClass?: string
inputRegex: RegExp
pasteRegex: RegExp
as: ElementType
}
const spoilerInputRegex = /(?:^|\s)((?:\|\|)((?:[^||]+))(?:\|\|))$/
const spoilerPasteRegex = /(?:^|\s)((?:\|\|)((?:[^||]+))(?:\|\|))/g
export const TiptapSpoiler = Mark.create<SpoilerOptions>({
name: 'spoiler',
inline: true,
group: 'inline',
inclusive: false,
exitable: true,
content: 'inline*',
priority: 1001, // higher priority than other formatting so they go inside
addOptions() {
return {
HTMLAttributes: { 'aria-label': 'spoiler' },
spoilerOpenClass: '',
spoilerCloseClass: undefined,
inputRegex: spoilerInputRegex,
pasteRegex: spoilerPasteRegex,
as: 'span',
editing: false,
}
},
addCommands() {
return {
setSpoiler:
() =>
({ commands }) =>
commands.setMark(this.name),
toggleSpoiler:
() =>
({ commands }) =>
commands.toggleMark(this.name),
unsetSpoiler:
() =>
({ commands }) =>
commands.unsetMark(this.name),
}
},
addInputRules() {
return [
markInputRule({
find: this.options.inputRegex,
type: this.type,
}),
]
},
addPasteRules() {
return [
markPasteRule({
find: this.options.pasteRegex,
type: this.type,
}),
]
},
parseHTML() {
return [
{
tag: 'span',
getAttrs: (node) =>
(node as HTMLElement).ariaLabel?.toLowerCase() === 'spoiler' && null,
},
]
},
renderHTML({ HTMLAttributes }) {
const elem = document.createElement(this.options.as as string)
Object.entries(
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
class: this.options.spoilerCloseClass ?? this.options.spoilerOpenClass,
})
).forEach(([attr, val]) => elem.setAttribute(attr, val))
elem.addEventListener('click', () => {
elem.setAttribute('class', this.options.spoilerOpenClass)
})
return elem
},
})

View File

@ -0,0 +1,37 @@
import { Node, mergeAttributes } from '@tiptap/core'
export interface TweetOptions {
tweetId: string
}
// This is a version of the Tiptap Node config without addNodeView,
// since that would require bundling in tsx
export const TiptapTweetNode = {
name: 'tiptapTweet',
group: 'block',
atom: true,
addAttributes() {
return {
tweetId: {
default: null,
},
}
},
parseHTML() {
return [
{
tag: 'tiptap-tweet',
},
]
},
renderHTML(props: { HTMLAttributes: Record<string, any> }) {
return ['tiptap-tweet', mergeAttributes(props.HTMLAttributes)]
},
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
export default Node.create<TweetOptions>(TiptapTweetNode)

View File

@ -1,5 +0,0 @@
export type FirstArgument<T> = T extends (arg1: infer U, ...args: any[]) => any
? U
: any
export type Truthy<T> = Exclude<T, undefined | null | false | 0 | ''>

View File

@ -1,4 +0,0 @@
export type View = {
contractId: string
timestamp: number
}

43
dev.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
ENV=${1:-dev}
case $ENV in
dev)
FIREBASE_PROJECT=dev
NEXT_ENV=DEV ;;
prod)
FIREBASE_PROJECT=prod
NEXT_ENV=PROD ;;
localdb)
FIREBASE_PROJECT=dev
NEXT_ENV=DEV
EMULATOR=true ;;
*)
echo "Invalid environment; must be dev, prod, or localdb."
exit 1
esac
firebase use $FIREBASE_PROJECT
if [ ! -z $EMULATOR ]
then
npx concurrently \
-n FIRESTORE,FUNCTIONS,NEXT,TS \
-c green,white,magenta,cyan \
"yarn --cwd=functions localDbScript" \
"cross-env FIRESTORE_EMULATOR_HOST=localhost:8080 yarn --cwd=functions dev" \
"cross-env NEXT_PUBLIC_FUNCTIONS_URL=http://localhost:8088 \
NEXT_PUBLIC_FIREBASE_EMULATE=TRUE \
NEXT_PUBLIC_FIREBASE_ENV=${NEXT_ENV} \
yarn --cwd=web serve" \
"cross-env yarn --cwd=web ts-watch"
else
npx concurrently \
-n FUNCTIONS,NEXT,TS \
-c white,magenta,cyan \
"yarn --cwd=functions dev" \
"cross-env NEXT_PUBLIC_FUNCTIONS_URL=http://localhost:8088 \
NEXT_PUBLIC_FIREBASE_ENV=${NEXT_ENV} \
yarn --cwd=web serve" \
"cross-env yarn --cwd=web ts-watch"
fi

2
docs/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

20
docs/.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

2
docs/.prettierignore Normal file
View File

@ -0,0 +1,2 @@
.docusaurus/
build/

7
docs/.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": false,
"trailingComma": "es5",
"singleQuote": true
}

11
docs/README.md Normal file
View File

@ -0,0 +1,11 @@
# docs
Manifold Markets Docs
## Getting started
0. Make sure you have [Yarn 1.x][yarn]
1. `$ cd docs`
2. `$ yarn`
3. `$ yarn start`
4. The docs site will be available on http://localhost:3000

3
docs/babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
}

61
docs/docs/$how-to.md Normal file
View File

@ -0,0 +1,61 @@
# How to Manifold
Manifold Markets is a novel site where users can bet against each other to predict the outcomes of all types of questions. Engage in intense discussion, or joke with friends, whilst putting play-money where your mouth is.
## Mana
Mana (M$) is our virtual play currency that cannot be converted to real money.
- **Its Value**
You can redeem your Mana and we will [donate to a charity](https://manifold.markets/charity) on your behalf. Redeeming and purchasing Mana occurs at a rate of M$100 to $1. You will be able to redeem it for merch and other cool items soon too!
- **It sets us apart**
Using play-money sets us apart from other similar sites as we dont want our users to solely focus on monetary gains. Instead we prioritize providing value in the form of an enjoyable experience and facilitating a more informed world through the power of prediction markets.
## How probabilities work
The probability of a market represents what the collective bets of users predict the chances of an outcome occurring is. How this is calculated depends on the type of market - see below!
## Types of markets
There are currently 3 types of markets: Yes/No (binary), Free response, and Numerical.
- **Yes/No (Binary)**
The creator asks a question where traders can bet yes or no.
Check out [Maniswap](https://www.notion.so/Maniswap-ce406e1e897d417cbd491071ea8a0c39) for more info on its automated market maker.
- **Free Response**
The creator asks an open ended question. Both the creator and users can propose answers which can be bet on. Dont be intimidated to add new answers! The payout system and initial liquidity rewards users who bet on new answers early. The algorithm used to determine the probability and payout is complicated but if you want to learn more check out [DPM](https://www.notion.so/DPM-b9b48a09ea1f45b88d991231171730c5).
- **Numerical**
Retracted whilst we make improvements. You still may see some old ones floating around though. Questions which can be answered by a number within a given range. Betting on a value will cause you to buy shares from buckets surrounding the number you choose.
## Compete and build your portfolio
Generate profits to prove your expertise and shine above your friends.
To the moon 🚀
- **Find inaccurate probabilities**
Use your superior knowledge on topics to identify markets which have inaccurate probabilities. This gives you favorable odds, so bet accordingly to shift the probability to what you think it should be.
- **React to news**
Markets are dynamic and ongoing events can drastically affect what the probability should look like. Be the keenest to react and there is a lot of Mana to be made.
- **Buy low, sell high**
Similar to a stock market, probabilities can be overvalued and undervalued. If you bet (buy shares) at one end of the spectrum and subsequently other users buy even more shares of that same type, the value of your own shares will increase. Sometimes it will be most profitable to wait for the market to resolve but often it can be wise to sell your shares and take the immediate profits. This can also be a great way to free up Mana if you are lacking funds.
- **Create innovative answers**
Certain free response markets provide room for creativity! The answers themselves can often affect the outcome based on how compelling they are.
More questions? Check out **[this community-driven FAQ](https://docs.manifold.markets/faq)**!

71
docs/docs/about.md Normal file
View File

@ -0,0 +1,71 @@
---
id: about
slug: /
---
# About Manifold Markets
Manifold Markets lets anyone create a prediction market on any topic. Win virtual play money betting on what you know, from **[chess tournaments](https://manifold.markets/SG/will-magnus-carlsen-lose-any-regula)** to **[lunar collisions](https://manifold.markets/Duncan/will-the-wayward-falcon-9-booster-h)** to **[newsletter subscriber rates](https://manifold.markets/Nu%C3%B1oSempere/how-many-additional-subscribers-wil)** - or learn about the future by creating your own market!
### **What are prediction markets?**
**Prediction markets are a place where you can bet on the outcome of future events.**
Consider a question like: "Will Democrats win the 2024 US presidential election?"
If I think the Democrats are very likely to win, and you disagree, I might offer $70 to your $30 (with the winner taking home $100 total). This set of bets imply a 70% probability of the Democrats winning.
Now, you or I could be mistaken and overshooting the true probability one way or another. If so, there's an incentive for someone else to bet and correct it! Over time, the implied probability will converge to the **[market's best estimate](https://en.wikipedia.org/wiki/Efficient-market_hypothesis)**. Since these probabilities are public, anyone can use them to make better decisions!
### **Can prediction markets work without real money?**
Yes! There is substantial evidence that play-money prediction markets provide real predictive power. Examples include **[sports betting](http://www.electronicmarkets.org/fileadmin/user_upload/doc/Issues/Volume_16/Issue_01/V16I1_Statistical_Tests_of_Real-Money_versus_Play-Money_Prediction_Markets.pdf)** and internal prediction markets at firms like **[Google](https://www.networkworld.com/article/2284098/google-bets-on-value-of-prediction-markets.html)**.
Our overall design also ensures that good forecasting will come out on top in the long term. In the competitive environment of the marketplace, bettors that are correct more often will gain influence, leading to better-calibrated forecasts over time.
Since our launch, we've seen hundreds of users trade each day, on over a thousand different markets! You can track the popularity of our platform at **[https://manifold.markets/stats](https://manifold.markets/stats)**.
### **Why is this important?**
Prediction markets aggregate and reveal crucial information that would not otherwise be known. They are a bottom-up mechanism that can influence everything from politics, economics, and business, to scientific research and education.
Prediction markets can predict **[which research papers will replicate](https://www.pnas.org/content/112/50/15343)**; which drug is the most effective; which policy would generate the most tax revenue; which charity will be underfunded; or which startup idea is the most promising. By surfacing and quantifying our collective knowledge, we as a society become wiser.
### **How are markets resolved?**
The creator of the prediction market decides the outcome and earns a commission based on the trade volume.
This simple resolution mechanism has surprising benefits in allowing a diversity of views to flourish. Competition between market creators will lead to traders flocking to the creators with good judgment on market resolution.
What's more, when the creator is free to use their judgment, many new kinds of prediction markets can be created that are less objective or even personal. (E.g. "Will I enjoy participating in the Metaverse in 2023?")
<!-- ### **Can I create private markets?**
Soon! We're running a pilot version of Manifold for Teams - private Manifold instances where you can discuss internal topics and predict on outcomes for your organization.
If this sounds like something youd want, **[join the waitlist here](https://docs.google.com/forms/d/e/1FAIpQLSfM_rxRHemCjKE6KPiYXGyP2nBSInZNKn_wc7yS1-rvlLAVnA/viewform?usp=sf_link)**! -->
### **Who are we?**
Manifold Markets is currently a team of three:
- James Grugett
- Stephen Grugett
- Austin Chen
We've previously launched consumer-facing startups (**[Throne](https://throne.live/)**, **[One Word](https://oneword.games/platform)**), and worked at top tech and trading firms (Google, Susquehanna).
## **Talk to us!**
Questions? Comments? Want to create a market? Talk to us!
- Email: **[info@manifold.markets](mailto:info@manifold.markets)**
- Office hours:
- **[Calendly — Austin](https://calendly.com/austinchen/manifold)**
- **[Calendly — James](https://calendly.com/jamesgrugett/manifold)**
- Chat: **[Manifold Markets Discord server](https://discord.gg/eHQBNBqXuh)**
## **Further Reading**
- **[Above the fold](https://manifoldmarkets.substack.com/)**, our newsletter
- **[Scott Alexander on play-money prediction markets](https://astralcodexten.substack.com/p/play-money-and-reputation-systems)**

789
docs/docs/api.md Normal file
View File

@ -0,0 +1,789 @@
# API
:::caution
Our API is still in alpha — things may change or break at any time!
If you have questions, come chat with us on [Discord](https://discord.com/invite/eHQBNBqXuh). Wed love to hear about what you build!
:::
## General notes
Some APIs are not associated with any particular user. Other APIs require authentication.
APIs that require authentication accept an `Authorization` header in one of two formats:
- `Authorization: Key {key}`. A Manifold API key associated with a user
account. Each account may have zero or one API keys. To generate an API key
for your account, visit your user profile, click "edit", and click the
"refresh" button next to the API key field at the bottom. You can click it
again any time to invalidate your existing key and generate a new one.
- `Authorization: Bearer {jwt}`. A signed JWT from Firebase asserting your
identity. This is what our web client uses. It will probably be annoying for
you to generate and we will not document it further here.
API requests that accept parameters should either have the parameters in the
query string if they are GET requests, or have a body with a JSON object with
one property per parameter if they are POST requests.
API responses should always either have a body with a JSON result object (if
the response was a 200) or with a JSON object representing an error (if the
response was a 4xx or 5xx.)
## Endpoints
### `GET /v0/user/[username]`
Gets a user by their username. Remember that usernames may change.
Requires no authorization.
### `GET /v0/user/by-id/[id]`
Gets a user by their unique ID. Many other API endpoints return this as the `userId`.
Requires no authorization.
### GET /v0/me
Returns the authenticated user.
### `GET /v0/groups`
Gets all groups, in no particular order.
Parameters:
- `availableToUserId`: Optional. if specified, only groups that the user can
join and groups they've already joined will be returned.
Requires no authorization.
### `GET /v0/group/[slug]`
Gets a group by its slug.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/group/by-id/[id]`
Gets a group by its unique ID.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/group/by-id/[id]/markets`
Gets a group's markets by its unique ID.
Requires no authorization.
Note: group is singular in the URL.
### `GET /v0/markets`
Lists all markets, ordered by creation date descending.
Parameters:
- `limit`: Optional. How many markets to return. The maximum and the default is 1000.
- `before`: Optional. The ID of the market before which the list will start. For
example, if you ask for the most recent 10 markets, and then perform a second
query for 10 more markets with `before=[the id of the 10th market]`, you will
get markets 11 through 20.
Requires no authorization.
- Example request
```
https://manifold.markets/api/v0/markets?limit=1
```
- Example response
```json
[
{
"id":"EvIhzcJXwhL0HavaszD7",
"creatorUsername":"Austin",
"creatorName":"Austin",
"createdTime":1653850472294,
"creatorAvatarUrl":"https://lh3.googleusercontent.com/a-/AOh14GiZyl1lBehuBMGyJYJhZd-N-mstaUtgE4xdI22lLw=s96-c",
"closeTime":1653893940000,
"question":"Will I write a new blog post today?",
"tags":[
"personal",
"commitments"
],
"url":"https://manifold.markets/Austin/will-i-write-a-new-blog-post-today",
"pool":146.73022894879944,
"probability":0.8958175225896258,
"p":0.08281474972181882,
"totalLiquidity":102.65696071594805,
"outcomeType":"BINARY",
"mechanism":"cpmm-1",
"volume":241,
"volume7Days":0,
"volume24Hours":0,
"isResolved":true,
"resolution":"YES",
"resolutionTime":1653924077078
},
...
```
- Response type: Array of `LiteMarket`
```tsx
// Information about a market, but without bets or comments
type LiteMarket = {
// Unique identifer for this market
id: string
// Attributes about the creator
creatorUsername: string
creatorName: string
createdTime: number // milliseconds since epoch
creatorAvatarUrl?: string
// Market attributes. All times are in milliseconds since epoch
closeTime?: number // Min of creator's chosen date, and resolutionTime
question: string
// A list of tags on each market. Any user can add tags to any market.
// This list also includes the predefined categories shown as filters on the home page.
tags: string[]
// Note: This url always points to https://manifold.markets, regardless of what instance the api is running on.
// This url includes the creator's username, but this doesn't need to be correct when constructing valid URLs.
// i.e. https://manifold.markets/Austin/test-market is the same as https://manifold.markets/foo/test-market
url: string
outcomeType: string // BINARY, FREE_RESPONSE, MULTIPLE_CHOICE, NUMERIC, or PSEUDO_NUMERIC
mechanism: string // dpm-2 or cpmm-1
probability: number
pool: { outcome: number } // For CPMM markets, the number of shares in the liquidity pool. For DPM markets, the amount of mana invested in each answer.
p?: number // CPMM markets only, probability constant in y^p * n^(1-p) = k
totalLiquidity?: number // CPMM markets only, the amount of mana deposited into the liquidity pool
min?: number // PSEUDO_NUMERIC markets only, the minimum resolvable value
max?: number // PSEUDO_NUMERIC markets only, the maximum resolvable value
isLogScale?: bool // PSEUDO_NUMERIC markets only, if true `number = (max - min + 1)^probability + minstart - 1`, otherwise `number = min + (max - min) * probability`
volume: number
volume7Days: number
volume24Hours: number
isResolved: boolean
resolutionTime?: number
resolution?: string
resolutionProbability?: number // Used for BINARY markets resolved to MKT
lastUpdatedTime?: number
}
```
### `GET /v0/market/[marketId]`
Gets information about a single market by ID. Includes comments, bets, and answers.
Requires no authorization.
- Example request
```
https://manifold.markets/api/v0/market/3zspH9sSzMlbFQLn9GKR
```
- <details><summary>Example response</summary><p>
```json
{
"id": "lEoqtnDgJzft6apSKzYK",
"creatorUsername": "Angela",
"creatorName": "Angela",
"createdTime": 1655258914863,
"creatorAvatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
"closeTime": 1655265001448,
"question": "What is good?",
"description": "Resolves proportionally to the answer(s) which I find most compelling. (Obviously Ill refrain from giving my own answers)\n\n(Please have at it with philosophy, ethics, etc etc)\n\n\nContract resolved automatically.",
"tags": [],
"url": "https://manifold.markets/Angela/what-is-good",
"pool": null,
"outcomeType": "FREE_RESPONSE",
"mechanism": "dpm-2",
"volume": 112,
"volume7Days": 212,
"volume24Hours": 0,
"isResolved": true,
"resolution": "MKT",
"resolutionTime": 1655265001448,
"answers": [
{
"createdTime": 1655258941573,
"avatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
"id": "1",
"username": "Angela",
"number": 1,
"name": "Angela",
"contractId": "lEoqtnDgJzft6apSKzYK",
"text": "ANTE",
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2",
"probability": 0.66749733001068
},
{
"name": "Isaac King",
"username": "IsaacKing",
"text": "This answer",
"userId": "y1hb6k7txdZPV5mgyxPFApZ7nQl2",
"id": "2",
"number": 2,
"avatarUrl": "https://lh3.googleusercontent.com/a-/AOh14GhNVriOvxK2VUAmE-jvYZwC-XIymatzVirT0Bqb2g=s96-c",
"contractId": "lEoqtnDgJzft6apSKzYK",
"createdTime": 1655261198074,
"probability": 0.008922214311142757
},
{
"createdTime": 1655263226587,
"userId": "jbgplxty4kUKIa1MmgZk22byJq03",
"id": "3",
"avatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FMartin%2Fgiphy.gif?alt=media&token=422ef610-553f-47e3-bf6f-c0c5cc16c70a",
"text": "Toyota Camry",
"contractId": "lEoqtnDgJzft6apSKzYK",
"name": "Undox",
"username": "Undox",
"number": 3,
"probability": 0.008966714133143469
},
{
"number": 4,
"name": "James Grugett",
"userId": "5LZ4LgYuySdL1huCWe7bti02ghx2",
"text": "Utility (Defined by your personal utility function.)",
"createdTime": 1655264793224,
"contractId": "lEoqtnDgJzft6apSKzYK",
"username": "JamesGrugett",
"id": "4",
"avatarUrl": "https://lh3.googleusercontent.com/a-/AOh14GjC83uMe-fEfzd6QvxiK6ZqZdlMytuHxevgMYIkpAI=s96-c",
"probability": 0.09211463154147384
}
],
"comments": [
{
"id": "ZdHIyfQazHyl8nI0ENS7",
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2",
"createdTime": 1655265807433,
"text": "ok what\ni did not resolve this intentionally",
"contractId": "lEoqtnDgJzft6apSKzYK",
"userName": "Angela",
"userAvatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
"userUsername": "Angela"
},
{
"userName": "James Grugett",
"userUsername": "JamesGrugett",
"id": "F7fvHGhTiFal8uTsUc9P",
"userAvatarUrl": "https://lh3.googleusercontent.com/a-/AOh14GjC83uMe-fEfzd6QvxiK6ZqZdlMytuHxevgMYIkpAI=s96-c",
"replyToCommentId": "ZdHIyfQazHyl8nI0ENS7",
"text": "@Angela Sorry! There was an error that automatically resolved several markets that were created in the last few hours.",
"createdTime": 1655266286514,
"userId": "5LZ4LgYuySdL1huCWe7bti02ghx2",
"contractId": "lEoqtnDgJzft6apSKzYK"
},
{
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2",
"contractId": "lEoqtnDgJzft6apSKzYK",
"id": "PIHhXy5hLHSgW8uoUD0Q",
"userName": "Angela",
"text": "lmk if anyone lost manna from this situation and i'll try to fix it",
"userUsername": "Angela",
"createdTime": 1655277581308,
"userAvatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476"
},
{
"userAvatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
"userName": "Angela",
"text": "from my end it looks like no one did",
"replyToCommentId": "PIHhXy5hLHSgW8uoUD0Q",
"createdTime": 1655287149528,
"userUsername": "Angela",
"id": "5slnWEQWwm6dHjDi6oiH",
"contractId": "lEoqtnDgJzft6apSKzYK",
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2"
}
],
"bets": [
{
"outcome": "0",
"contractId": "lEoqtnDgJzft6apSKzYK",
"fees": {
"liquidityFee": 0,
"creatorFee": 0,
"platformFee": 0
},
"isAnte": true,
"shares": 100,
"probAfter": 1,
"amount": 100,
"userId": "IPTOzEqrpkWmEzh6hwvAyY9PqFb2",
"createdTime": 1655258914863,
"probBefore": 0,
"id": "2jNZqnwoEQL7WDTTAWDP"
},
{
"shares": 173.20508075688772,
"fees": {
"platformFee": 0,
"liquidityFee": 0,
"creatorFee": 0
},
"contractId": "lEoqtnDgJzft6apSKzYK",
"probBefore": 0,
"createdTime": 1655258941573,
"loanAmount": 0,
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2",
"amount": 100,
"outcome": "1",
"probAfter": 0.75,
"id": "xuc3JoiNkE8lXPh15mUb"
},
{
"userId": "y1hb6k7txdZPV5mgyxPFApZ7nQl2",
"contractId": "lEoqtnDgJzft6apSKzYK",
"loanAmount": 0,
"probAfter": 0.009925496893641248,
"id": "8TBlzPtOdO0q5BgSyRbi",
"createdTime": 1655261198074,
"shares": 20.024984394500787,
"amount": 1,
"outcome": "2",
"probBefore": 0,
"fees": {
"liquidityFee": 0,
"creatorFee": 0,
"platformFee": 0
}
},
{
"probAfter": 0.00987648269777473,
"outcome": "3",
"id": "9vdwes6s9QxbYZUBhHs4",
"createdTime": 1655263226587,
"shares": 20.074859899884732,
"amount": 1,
"loanAmount": 0,
"fees": {
"liquidityFee": 0,
"platformFee": 0,
"creatorFee": 0
},
"userId": "jbgplxty4kUKIa1MmgZk22byJq03",
"contractId": "lEoqtnDgJzft6apSKzYK",
"probBefore": 0
},
{
"createdTime": 1655264793224,
"fees": {
"creatorFee": 0,
"liquidityFee": 0,
"platformFee": 0
},
"probAfter": 0.09211463154147384,
"amount": 10,
"id": "BehiSGgk1wAkIWz1a8L4",
"userId": "5LZ4LgYuySdL1huCWe7bti02ghx2",
"contractId": "lEoqtnDgJzft6apSKzYK",
"loanAmount": 0,
"probBefore": 0,
"outcome": "4",
"shares": 64.34283176858165
}
]
}
```
</p>
</details>
- Response type: A `FullMarket`
```tsx
// A complete market, along with bets, comments, and answers (for free response markets)
type FullMarket = LiteMarket & {
bets: Bet[]
comments: Comment[]
answers?: Answer[] // dpm-2 markets only
description: JSONContent // Rich text content. See https://tiptap.dev/guide/output#option-1-json
textDescription: string // string description without formatting, images, or embeds
}
type Bet = {
id: string
contractId: string
amount: number // bet size; negative if SELL bet
outcome: string
shares: number // dynamic parimutuel pool weight; negative if SELL bet
probBefore: number
probAfter: number
sale?: {
amount: number // amount user makes from sale
betId: string // id of bet being sold
}
isSold?: boolean // true if this BUY bet has been sold
isAnte?: boolean
createdTime: number
}
```
### `GET /v0/slug/[marketSlug]`
Gets information about a single market by slug (the portion of the URL path after the username).
Requires no authorization.
- Example request
```
https://manifold.markets/api/v0/slug/will-carrick-flynn-win-the-general
```
- Response type: A `FullMarket` ; same as above.
### `GET /v0/users`
Lists all users.
Requires no authorization.
- Example request
```
https://manifold.markets/api/v0/users
```
- Example response
```json
[
{
"id":"igi2zGXsfxYPgB0DJTXVJVmwCOr2",
"createdTime":1639011767273,
"name":"Austin",
"username":"Austin",
"url":"https://manifold.markets/Austin",
"avatarUrl":"https://lh3.googleusercontent.com/a-/AOh14GiZyl1lBehuBMGyJYJhZd-N-mstaUtgE4xdI22lLw=s96-c",
"bio":"I build Manifold! Always happy to chat; reach out on Discord or find a time on https://calendly.com/austinchen/manifold!",
"bannerUrl":"https://images.unsplash.com/photo-1501523460185-2aa5d2a0f981?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1531&q=80",
"website":"https://blog.austn.io",
"twitterHandle":"akrolsmir",
"discordHandle":"akrolsmir#4125",
"balance":9122.607163564959,
"totalDeposits":10339.004780544328,
"totalPnLCached":9376.601262721899,
"creatorVolumeCached":76078.46984199001
}
```
- Response type: Array of `LiteUser`
```tsx
// Basic information about a user
type LiteUser = {
id: string // user's unique id
createdTime: number
name: string // display name, may contain spaces
username: string // username, used in urls
url: string // link to user's profile
avatarUrl?: string
bio?: string
bannerUrl?: string
website?: string
twitterHandle?: string
discordHandle?: string
// Note: the following are here for convenience only and may be removed in the future.
balance: number
totalDeposits: number
totalPnLCached: number
creatorVolumeCached: number
}
```
### `POST /v0/bet`
Places a new bet on behalf of the authorized user.
Parameters:
- `amount`: Required. The amount to bet, in M$, before fees.
- `contractId`: Required. The ID of the contract (market) to bet on.
- `outcome`: Required. The outcome to bet on. For binary markets, this is `YES`
or `NO`. For free response markets, this is the ID of the free response
answer. For numeric markets, this is a string representing the target bucket,
and an additional `value` parameter is required which is a number representing
the target value. (Bet on numeric markets at your own peril.)
- `limitProb`: Optional. A number between `0.001` and `0.999` inclusive representing
the limit probability for your bet (i.e. 0.1% to 99.9% — multiply by 100 for the
probability percentage).
The bet will execute immediately in the direction of `outcome`, but not beyond this
specified limit. If not all the bet is filled, the bet will remain as an open offer
that can later be matched against an opposite direction bet.
- For example, if the current market probability is `50%`:
- A `M$10` bet on `YES` with `limitProb=0.4` would not be filled until the market
probability moves down to `40%` and someone bets `M$15` of `NO` to match your
bet odds.
- A `M$100` bet on `YES` with `limitProb=0.6` would fill partially or completely
depending on current unfilled limit bets and the AMM's liquidity. Any remaining
portion of the bet not filled would remain to be matched against in the future.
- An unfilled limit order bet can be cancelled using the cancel API.
Example request:
```
$ curl https://manifold.markets/api/v0/bet -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"amount":1, \
"outcome":"YES", \
"contractId":"{...}"}'
```
### `POST /v0/bet/cancel/[id]`
Cancel the limit order of a bet with the specified id. If the bet was unfilled, it will be cancelled so that no other bets will match with it. This is action irreversable.
### `POST /v0/market`
Creates a new market on behalf of the authorized user.
Parameters:
- `outcomeType`: Required. One of `BINARY`, `FREE_RESPONSE`, `MULTIPLE_CHOICE`, or `PSEUDO_NUMERIC`.
- `question`: Required. The headline question for the market.
- `description`: Required. A long description describing the rules for the market.
- Note: string descriptions do **not** turn into links, mentions, formatted text. Instead, rich text descriptions must be in [TipTap json](https://tiptap.dev/guide/output#option-1-json).
- `closeTime`: Required. The time at which the market will close, represented as milliseconds since the epoch.
- `tags`: Optional. An array of string tags for the market.
For binary markets, you must also provide:
- `initialProb`: An initial probability for the market, between 1 and 99.
For numeric markets, you must also provide:
- `min`: The minimum value that the market may resolve to.
- `max`: The maximum value that the market may resolve to.
- `isLogScale`: If true, your numeric market will increase exponentially from min to max.
- `initialValue`: An initial value for the market, between min and max, exclusive.
For multiple choice markets, you must also provide:
- `answers`: An array of strings, each of which will be a valid answer for the market.
Example request:
```
$ curl https://manifold.markets/api/v0/market -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Key {...}'
--data-raw '{"outcomeType":"BINARY", \
"question":"Is there life on Mars?", \
"description":"I'm not going to type some long ass example description.", \
"closeTime":1700000000000, \
"initialProb":25}'
```
### `POST /v0/market/[marketId]/add-liquidity`
Adds a specified amount of liquidity into the market.
- `amount`: Required. The amount of liquidity to add, in M$.
### `POST /v0/market/[marketId]/close`
Closes a market on behalf of the authorized user.
- `closeTime`: Optional. Milliseconds since the epoch to close the market at. If not provided, the market will be closed immediately. Cannot provide close time in past.
### `POST /v0/market/[marketId]/resolve`
Resolves a market on behalf of the authorized user.
Parameters:
For binary markets:
- `outcome`: Required. One of `YES`, `NO`, `MKT`, or `CANCEL`.
- `probabilityInt`: Optional. The probability to use for `MKT` resolution.
For free response or multiple choice markets:
- `outcome`: Required. One of `MKT`, `CANCEL`, or a `number` indicating the answer index.
- `resolutions`: An array of `{ answer, pct }` objects to use as the weights for resolving in favor of multiple free response options. Can only be set with `MKT` outcome. Note that the total weights must add to 100.
For numeric markets:
- `outcome`: Required. One of `CANCEL`, or a `number` indicating the selected numeric bucket ID.
- `value`: The value that the market may resolves to.
- `probabilityInt`: Required if `value` is present. Should be equal to
- If log scale: `log10(value - min + 1) / log10(max - min + 1)`
- Otherwise: `(value - min) / (max - min)`
Example request:
```
# Resolve a binary market
$ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": "YES"}'
# Resolve a binary market with a specified probability
$ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": "MKT", \
"probabilityInt": 75}'
# Resolve a free response market with a single answer chosen
$ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": 2}'
# Resolve a free response market with multiple answers chosen
$ curl https://manifold.markets/api/v0/market/{marketId}/resolve -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": "MKT", \
"resolutions": [ \
{"answer": 0, "pct": 50}, \
{"answer": 2, "pct": 50} \
]}'
```
### `POST /v0/market/[marketId]/sell`
Sells some quantity of shares in a binary market on behalf of the authorized user.
Parameters:
- `outcome`: Optional. One of `YES`, or `NO`. If you leave it off, and you only
own one kind of shares, you will sell that kind of shares.
- `shares`: Optional. The amount of shares to sell of the outcome given
above. If not provided, all the shares you own will be sold.
Example request:
```
$ curl https://manifold.markets/api/v0/market/{marketId}/sell -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Key {...}' \
--data-raw '{"outcome": "YES", "shares": 10}'
```
### `POST /v0/comment`
Creates a comment in the specified market. Only supports top-level comments for now.
Parameters:
- `contractId`: Required. The ID of the market to comment on.
- `content`: The comment to post, formatted as [TipTap json](https://tiptap.dev/guide/output#option-1-json), OR
- `html`: The comment to post, formatted as an HTML string, OR
- `markdown`: The comment to post, formatted as a markdown string.
### `GET /v0/bets`
Gets a list of bets, ordered by creation date descending.
Parameters:
- `username`: Optional. If set, the response will include only bets created by this user.
- `market`: Optional. The slug of a market. If set, the response will only include bets on this market.
- `limit`: Optional. How many bets to return. The maximum and the default is 1000.
- `before`: Optional. The ID of the bet before which the list will start. For
example, if you ask for the most recent 10 bets, and then perform a second
query for 10 more bets with `before=[the id of the 10th bet]`, you will
get bets 11 through 20.
Requires no authorization.
- Example request
```
https://manifold.markets/api/v0/bets?username=ManifoldMarkets&market=will-i-be-able-to-place-a-limit-ord
```
- Response type: A `Bet[]`.
- <details><summary>Example response</summary><p>
```json
[
// Limit bet, partially filled.
{
"isFilled": false,
"amount": 15.596681605353808,
"userId": "IPTOzEqrpkWmEzh6hwvAyY9PqFb2",
"contractId": "Tz5dA01GkK5QKiQfZeDL",
"probBefore": 0.5730753474948571,
"isCancelled": false,
"outcome": "YES",
"fees": { "creatorFee": 0, "liquidityFee": 0, "platformFee": 0 },
"shares": 31.193363210707616,
"limitProb": 0.5,
"id": "yXB8lVbs86TKkhWA1FVi",
"loanAmount": 0,
"orderAmount": 100,
"probAfter": 0.5730753474948571,
"createdTime": 1659482775970,
"fills": [
{
"timestamp": 1659483249648,
"matchedBetId": "MfrMd5HTiGASDXzqibr7",
"amount": 15.596681605353808,
"shares": 31.193363210707616
}
]
},
// Normal bet (no limitProb specified).
{
"shares": 17.350459904608414,
"probBefore": 0.5304358279113885,
"isFilled": true,
"probAfter": 0.5730753474948571,
"userId": "IPTOzEqrpkWmEzh6hwvAyY9PqFb2",
"amount": 10,
"contractId": "Tz5dA01GkK5QKiQfZeDL",
"id": "1LPJHNz5oAX4K6YtJlP1",
"fees": {
"platformFee": 0,
"liquidityFee": 0,
"creatorFee": 0.4251333951457593
},
"isCancelled": false,
"loanAmount": 0,
"orderAmount": 10,
"fills": [
{
"amount": 10,
"matchedBetId": null,
"shares": 17.350459904608414,
"timestamp": 1659482757271
}
],
"createdTime": 1659482757271,
"outcome": "YES"
}
]
```
</p>
</details>
## Changelog
- 2022-09-24: Expand market POST docs to include new market types (`PSEUDO_NUMERIC`, `MULTIPLE_CHOICE`)
- 2022-07-15: Add user by username and user by ID APIs
- 2022-06-08: Add paging to markets endpoint
- 2022-06-05: Add new authorized write endpoints
- 2022-02-28: Add `resolutionTime` to markets, change `closeTime` definition
- 2022-02-19: Removed user IDs from bets
- 2022-02-17: Released our v0 API, with `/markets`, `/market/[marketId]`, and `/slug/[slugId]`

View File

@ -0,0 +1,47 @@
# Awesome Manifold 😎
A list of community-created projects built on, or related to, Manifold Markets.
## Data
- [Manifold Market Stats](https://wasabipesto.com/jupyter/manifold/)
## Sites using Manifold
- [WagerWith.me](https://www.wagerwith.me/) — Bet with your friends, with full Manifold integration to bet with M$.
- [Alignment Markets](https://alignmentmarkets.com/) - Bet on the progress of benchmarks in ML safety!
## API / Dev
- [PyManifold](https://github.com/bcongdon/PyManifold) - Python client for the Manifold API
- [PyManifold fork](https://github.com/gappleto97/PyManifold/) - Fork maintained by [@LivInTheLookingGlass](https://manifold.markets/LivInTheLookingGlass)
- [manifold-markets-python](https://github.com/vluzko/manifold-markets-python) - Python tools for working with Manifold Markets (including various accuracy metrics)
- [ManifoldMarketManager](https://github.com/gappleto97/ManifoldMarketManager) - Python script and library to automatically manage markets
- [manifeed](https://github.com/joy-void-joy/manifeed) - Tool that creates an RSS feed for new Manifold markets
- [manifold-sdk](https://github.com/keriwarr/manifold-sdk) - TypeScript/JavaScript client for the Manifold API
## Bots
- [@manifold@botsin.space](https://botsin.space/@manifold) - Posts new Manifold markets to Mastodon
- [James' Bot](https://github.com/manifoldmarkets/market-maker) — Simple trading bot that makes markets
- [mana](https://github.com/AnnikaCodes/mana) - A Discord bot for Manifold by [@arae](https://manifold.markets/arae)
## Writeups
- [Information Markets, Decision Markets, Attention Markets, Action Markets](https://astralcodexten.substack.com/p/information-markets-decision-markets) by Scott Alexander
- [Mismatched Monetary Motivation in Manifold Markets](https://kevin.zielnicki.com/2022/02/17/manifold/) by Kevin Zielnicki
- [Introducing the Salem/CSPI Forecasting Tournament](https://www.cspicenter.com/p/introducing-the-salemcspi-forecasting) by Richard Hanania
- [What I learned about running a betting market game night contest](https://shakeddown.wordpress.com/2022/08/04/what-i-learned-about-running-a-betting-market-game-night-contest/) by shakeddown
- [Free-riding on prediction markets](https://pedunculate.substack.com/p/free-riding-on-prediction-markets) by John Roxton
## Art
- Folded origami and doodles by [@hamnox](https://manifold.markets/hamnox) ![](https://i.imgur.com/nVGY4pL.png)
- Laser-cut Foldy by [@wasabipesto](https://manifold.markets/wasabipesto) ![](https://i.imgur.com/g9S6v3P.jpg)
## Alumni
_These projects are no longer active, but were really really cool!_
- [Research.Bet](https://research.bet/) - Prediction market for scientific papers, using Manifold
- [CivicDashboard](https://civicdash.org/dashboard) - Uses Manifold to for tracked solutions for the SF city government

145
docs/docs/bounties.md Normal file
View File

@ -0,0 +1,145 @@
# Bounties
## What are Manifold bounties?
From time to time, a member of our community goes above and beyond in helping Manifold make prediction markets accessible & ubiquitous. Wed like to recognize such contributions publicly, and include a token of our appreciation in the form of M$!
Examples of community work that may be eligible for a bounty:
- Blog posts, markets, or comments which lead us to significantly change our views
- A track record of creating markets that help people make better decisions
- Promoting Manifold & forecasting to a wider audience
- Identifying serious exploits with our financial infrastructure
Our community is the beating heart of Manifold; your individual contributions are what make this platform valuable at all. Thanks to everyone listed here (as well as countless unnamed others) for your help & support!
## Awarded bounties
💥 *Awarded on 2022-10-07*
**[Pepe](https://manifold.markets/Pepe): M$10,000**
**[Jack](https://manifold.markets/jack): M$2,000**
**[Martin](https://manifold.markets/MartinRandall): M$2,000**
**[Yev](https://manifold.markets/Yev): M$2,000**
**[Michael](https://manifold.markets/MichaelWheatley): M$2,000**
- For discovering an infinite mana exploit using limit orders, and informing the Manifold team of it privately.
**[Matt](https://manifold.markets/MattP): M$5,000**
**[Adrian](https://manifold.markets/ahalekelly): M$5,000**
**[Yev](https://manifold.markets/Yev): M$5,000**
- For discovering an AMM liquidity exploit and informing the Manifold team of it privately.
🎈 *Awarded on 2022-06-14*
**[Wasabipesto](https://manifold.markets/wasabipesto): M$20,000**
- For creating an awesome stats page which features and analyses various data sets! This can be found on the second tab of our [analytics page](https://manifold.markets/stats).
**[Jack](https://manifold.markets/jack): M$10,000**
- For adding a bunch of charities to [Manifold for Good](https://manifold.markets/charity), working out market math with Austin, and excellent comment activity.
**[Forrest](https://manifold.markets/Forrest): M$10,000**
- For a variety of [open source code contributions](https://github.com/manifoldmarkets/manifold/commits?author=ForrestWeiswolf), making our code base easier to use and maintain.
**[IsaacKing](https://manifold.markets/IsaacKing): M$10,000**
- For responsible disclosure of an exploit involving liquidity withdrawal, which has [now been fixed](https://github.com/manifoldmarkets/manifold/pull/472)! Removing one infinite money glitch at a time.
**[Sjlver](https://manifold.markets/Sjlver): M$5,000**
- For responsible disclosure of a potential exploit. We would say what it is, but it isnt quite fixed yet! 🤫
_🌿 Announced on 2022-05-02_
**[Marshall Polaris](https://manifold.markets/mqp): M$200K**
- For spearheading the effort to [open-source Manifold](https://github.com/manifoldmarkets/manifold), by documenting our processes, triaging bugs, and improving the new contributor experience.
- Marshall contributed over 2 weeks of part-time volunteer work; as such, we are awarding an amount that reflects the extraordinary amount of effort hes put in.
**[Vincent Luczkow](https://manifold.markets/VincentLuczkow): M$10,000**
- For building and releasing https://github.com/vluzko/manifold-markets-python, a super cool Python visualization of the calibration accuracy of all Manifold markets. Turns out were doing okay!
**[Akhil Wable](https://manifold.markets/AkhilWable): M$10,000**
- For writing up [Akhils Product Suggestions](https://www.notion.so/Akhil-s-Product-Suggestions-672e1cba393d4242852ff95ae79528df), an extensive, thoughtful list of improvements we could make to our platform.
**[Alex K. Chen](https://manifold.markets/AlexKChen): M$6,000**
- For the creation of a metric ton of innovative, long term questions. At the time of award, Alex was singlehandedly responsible for 20% of all markets posted in April.
**[ZorbaTHut](https://manifold.markets/ZorbaTHut): M$5,000**
- For [testing out futarchy](https://manifold.markets/tag/themotte_leaving) on an important problem for the community of The Motte.
**[Tetraspace](https://manifold.markets/Tetraspace): M$3,500**
- For the creation of [a focused set of questions on UK politics](https://twitter.com/TetraspaceWest/status/1516824123149848579), with relevant real-world predictions.
- For the idea and execution of using FR bounded buckets for mapping out a scalar range ([example market](https://manifold.markets/Tetraspace/if-ron-desantis-is-elected-presiden), [discussion here](https://manifold.markets/StephenMalina/how-many-daily-active-users-will-ma)).
**[tcheasdfjkl](https://manifold.markets/tcheasdfjkl): M$2,500**
- For calling out numerous areas of improvement, e.g. around our profit numbers being wonky, and problems with the DPM ⇒ CFMM market conversions.
**[Jack](https://manifold.markets/JackC): M$500**
- For recommending we list the Long-Term Future Fund as a supported charity.
**[N.C. Young](https://manifold.markets/NcyRocks): M$500**
- For recommending we list the Givewell Maximum Impact Fund as a supported charity.
\**🥧 *Awarded 2022-03-14\*
**[Kevin Zielnicki](https://manifold.markets/kjz): M$10,000**
- For identifying issues with our Dynamic Parimutuel Market Maker in an [excellent blog post](https://kevin.zielnicki.com/2022/02/17/manifold/) (and [associated market](https://manifold.markets/kjz/will-manifolds-developers-agree-wit)), leading us to change to a different mechanism.
**[Pepe](https://manifold.markets/Pepe): M$10,000**
- For developing the function used in our Constant Function Market Maker, making it easier for us to provision liquidity compared to a CPMM.
**[Gurkenglas](https://manifold.markets/Gurkenglas): M$5,000**
- For concrete suggestions around improving our market maker algorithms, and creating useful graphs to make our different market makers more legible.
**[Scott Alexander](https://manifold.markets/ScottAlexander): M$5,000**
- For [developing and publicizing the idea of providing interest-free loans on each market](https://astralcodexten.substack.com/p/play-money-and-reputation-systems), helping make long-term markets more accurate.
**[David Glidden](https://manifold.markets/dglid): M$5,000**
- For taking on the mantle of [@MetaculusBot](https://manifold.markets/MetaculusBot), which allows traders access to a wider spread of topics, and permits head-to-head comparisons between our prediction markets and other forecasting platforms.
**[Isaac King](https://manifold.markets/IsaacKing): M$5,000**
- For [compiling an FAQ](https://outsidetheasylum.blog/manifold-markets-faq/) that answers a variety of questions that new users commonly face, and also inspiring us to move to [this open-source docs platform](https://docs.manifold.markets/).
**[Blazer](https://manifold.markets/BlazingDarkness/was-it-an-unpleasant-surprise-when): M$2,500**
- For [calling out our mistake](https://manifold.markets/BlazingDarkness/was-it-an-unpleasant-surprise-when) in retroactively publicizing the market creators trades, leading us to revert this feature entirely.
⛑️ _Awarded 2022-01-09_
**[Duncan](https://manifold.markets/Duncan): USD $50**
- For identifying and confidentially reporting an exploit where entering negative numbers into the trade box would allow the trade to go through.
- _Note: this was denominated in USD, as it predated the creation of our bounty program._
## Final note
If a particular contribution isn't listed here, that doesn't mean we didn't really appreciate it. Theres so much great work by our community; we aren't always able to catch them all!
If you feel that someone's exceptional contribution has fallen through the cracks (including your own!), please consider creating a market for “Will <X\> be recognized for a Manifold bounty?” and posting it on our Discord. Thanks!
## See also
- [Will Manifold implement retroactive public goods funding by June 1?](https://manifold.markets/Austin/will-manifold-implement-retroactive)
- [Bounties as described on LessWrong](https://www.lesswrong.com/tag/bounties-active)
- Mistakes pages we admire: [Scott Alexander](https://astralcodexten.substack.com/p/mistakes), [Nintil](https://nintil.com/mistakes), [80K Hours](https://80000hours.org/about/credibility/evaluations/mistakes/)
- [Donald Knuths reward checks](https://en.wikipedia.org/wiki/Knuth_reward_check)
![https://imgs.xkcd.com/comics/applied_math.png](https://imgs.xkcd.com/comics/applied_math.png)

126
docs/docs/faq.md Normal file
View File

@ -0,0 +1,126 @@
# Community FAQ
## General
### Do I have to pay real money in order to participate?
Nope! Each account starts with a free 1000 mana (or M$1000 for short). If you invest it wisely, you can increase your total without ever needing to put any real money into the site.
### Can M$ be sold for real money?
No. Gambling laws put many restrictions on real-money prediction markets, so Manifold uses play money instead.
You can instead redeem your Mana and we will [donate to a charity](http://manifold.markets/charity) on your behalf. Redeeming and purchasing Mana occurs at a rate of M$100 to $1.
### How do the free response markets work?
Any user can enter a response and bet on it, or they can bet on other people's responses. The response probabilities are weighted proportionally to how many people have bet on them. The market creator's ante goes into a "none of the above" pseudo-option that can't be bet on and can't be chosen as a correct answer when the market is resolved. (This means that free response markets tend to lose their creator almost their entire ante. It also means that if there are only a finite number of options that could win, traders can make guaranteed money by investing in them all equally.) See [here](https://manifoldmarkets.substack.com/p/above-the-fold-milestones-and-new) for more information.
### How accurate are the market probabilities?
In general, prediction markets are very accurate. They do have some known issues, most of which can be found on the [Wikipedia page.](https://en.wikipedia.org/wiki/Prediction_market#Accuracy). There are also a few factors that are specific to Manifold Markets:
- Manifold uses play money for their markets, so there's less of an incentive for people to invest safely. People often goof around with silly markets and investments that they don't expect to win M$ from.
- Anyone can create a market on Manifold, and there's nothing preventing the creator of a market from trying to manipulate it to make a profit.
- Manifold Markets is a new project and has a large number of individual markets, which means that many of their markets don't have many participants, sometimes less than 5 people.
- Manifold's betting system isn't perfect and has some sources of error, discussed in detail [here](https://kevin.zielnicki.com/2022/02/17/manifold/).
As a general heuristic, check the total pool for the market in question. The more M$ there is in the market, the more likely it is to be accurate.
### Can I participate without having a Google account?
No. See [here](https://manifold.markets/hamnox/will-manifold-markets-add-nongoogle) for the probability that this changes.
## Placing and winning bets
### The payout probabilities I'm shown sometimes aren't right. For example if a market is at 15% and I bet M$1 on "no", it tells me that I'll make a 42% profit if I win, but the listed payout is just M$1. What's going on?
Payout amounts are visually rounded to the nearest M$1, and only integer amounts can be put into markets. Behind the scenes however, your balance does track fractional amounts, so you're making a M$0.42 profit on that bet. Once you win another M$0.08, that fractional M$0.5 will display as an extra M$1 in your account. (There's no way to view your exact balance, you can only see the rounded value.)
### What are the rules about insider trading? (Using private information about a market to make a profit.)
It's not only allowed, but encouraged. The whole point of a prediction market is to uncover and amplify this sort of hidden information. For example, if there's a market like "will [company] make [decision]?" and you work for that company and know what decision they're going to make, you can use that information to win M$ and make the market more accurate at the same time. (Subject to your company's policies on disclosing internal information of course.) However, if the reason you have private information is because you're colluding with the market creator, this will likely earn both of you a bad reputation and people will be less interested in participating in your markets in the future.
### Can I see who is buying/selling in a market?
All trades before June 1, 2022 are anonymous by default. Trades after that date can be viewed in the Bets tab of any market, and also on that user's profile.
## Creating and resolving markets
### Is there any benefit to creating markets?
You get your question answered! Plus, you earn a commission on trades in your markets.
### What can I create a market about?
Anything you want to! People ask about politics, science, gaming, and even [their personal lives](https://www.smbc-comics.com/?id=2418). Take a look at the [current list of markets](https://manifold.markets/markets) to see what sorts of things people ask about.
### What's the difference between a market being "closed" and being "resolved"?
A market being "closed" means that people can no longer place or sell bets, "locking in" the current probability. Markets close when the close date of the market is met. A market being "resolved" means that the market creator has indicated a given resolution to the market's question, such as "yes", "no", "N/A", or a certain probability. This is the point at which people are cashed out of the market. Resolving a market automatically closes it, but a market can close days, weeks, or even years before it gets resolved.
### What does "PROB" mean?
Resolving a market as "PROB" means that it's resolved at a certain probability, chosen by the market creator. PROB 100% is the same as "yes", and PROB 0% is the same as "no". For example, if a market is resolved at PROB 75%, anyone who bought "yes" at less than 75% will (usually) make a profit, and anyone who bought "yes" at greater than 75% will (usually) take a loss. Vice versa for "no". This is also shown as "MKT" in the interface and API.
### What happens if a market creator resolves a market incorrectly, or doesn't resolve it at all?
Nothing. The idea is for Manifold Markets to function with similar freedom and versatility to a Twitter poll, but with more accurate results due to the dynamics of prediction markets. Individual market resolution is not enforced by the site, so if you don't trust a certain user to judge their markets fairly, you probably shouldn't participate in their markets.
That being said, manifold staff may manually send reminder emails to the creators of large markets if they have not been resolved in some time. There are also some projects in the works to enable automated market resolution after some time has passed.
### How do I tell if a certain market creator is trustworthy?
Look at their market resolution history on their profile page. If their past markets have all been resolved correctly, their future ones probably will be too. You can also look at the comments on those markets to see if any traders noticed anything suspicious. You can also ask about that person in the [Manifold Markets Discord](https://discord.gg/eHQBNBqXuh). And if their profile links to their website or social media pages, you can take that into account too.
### Are there any content filters? What happens if someone creates an inappropriate, offensive, or [dangerous](https://en.wikipedia.org/wiki/Assassination_market) market?
Right now, there are no restrictions on what markets can be created. If this becomes a problem, they may change their policies.
### Can a market creator change the close date of their market?
Yes. As long as the market hasn't been resolved yet, the creator can freely change its close date. They can even reopen a market that has already closed.
### Is there a way to see my closed markets that I need to resolve?
You'll get an automated email when they close. You can also go to your profile page and select "closed" in the dropdown menu. (This will display only markets that you haven't resolved yet.)
### When do market creators get their commission fees?
When the creator resolves their market, they get the commission from all the trades that were executed in the market.
### How do I see markets that are currently open?
You can see the top markets in various categories [here](https://manifold.markets/markets).
### Can I bet in a market I created?
Yes. However if you're doing things that the community would perceive as "shady", such as putting all your money on the correct resolution immediately before closing the market, people may be more reluctant to participate in your markets in the future. Betting "normally" in your own market is fine though.
## Miscellaneous
### How do I report bugs or ask for new features?
Contact them via [email](mailto:info@manifold.markets), post in their [Discord](https://discord.gg/eHQBNBqXuh), or create a market about that bug/feature in order to draw more attention to it and get community input.
If you don't mind putting in a little work, fork the code and open a [pull request](https://github.com/manifoldmarkets/manifold/pulls) on GitHub.
### How can I get notified of new developments?
Being a very recent project, Manifold is adding new features and tweaking existing ones quite frequently. You can keep up with changes by subscribing to their [Substack](https://manifoldmarkets.substack.com/), or joining their [Discord server](https://discord.gg/eHQBNBqXuh).
### Is there an app?
No, but the website is designed responsively and looks great on mobile.
### Does Manifold have an API for programmers?
Yep. Documentation is [here](https://docs.manifold.markets/api).
### If I have a question that isn't answered here, where can I ask it?
You can contact Manifold Markets via [email](mailto:info@manifold.markets) or post in their [Discord](https://discord.gg/eHQBNBqXuh). Once you have an answer, please consider updating this FAQ via "Edit this page" on Github!
## Credits
This FAQ was originally compiled by [Isaac King](https://outsidetheasylum.blog/manifold-markets-faq/).

109
docs/docs/market-details.md Normal file
View File

@ -0,0 +1,109 @@
# Guide to Market Types
# Market Mechanisms
Historically, Manifold used a special type of automated market maker based on a dynamic pari-mutuel (DPM) betting
system. Free response and numeric markets still use this system. Binary markets created prior to March 15, 2022 used
this system, but all of those markets have since closed.
Binary markets created after March 15 use a constant-function market maker which holds constant the weighted geometric
mean, with weights equal to the probabilities chosen by the market creator at creation. This design was inspired by
Uniswap's CPMM and a suggestion from Manifold user Pepe. The benefit of this approach is that the payout for any bet
is fixed at purchase time - 100 shares of YES will always return M$100 if YES is chosen.
Free response markets (and the depreciated numeric markets) still use the DPM system, as they have discrete "buckets"
for the pool to be sorted into.
## Market Creation
- Users can create a market on any question they want.
- When a user creates a market, they must choose a close date, after which trading will halt.
- They must also pay a M$100 market creation fee, which is used as liquidity to subsidize trading on the market.
- The market creator will earn a commission on all bets placed in the market.
- The market creator is responsible for resolving each market in a timely manner. All fees earned as a commission will be paid out after resolution.
- Creators can also resolve N/A to cancel all transactions and reverse all transactions made on the market - this includes profits from selling shares.
# Binary Markets
## Binary Markets: Overview
- Binary markets are structured around a question with a binary outcome, such as:
- [Will Bitcoin be worth more than $60,000 on Jan 1, 2022 at 12 am ET?](https://manifold.markets/SG/will-bitcoin-be-worth-more-than-600)
- [Will Manifold Markets have over $1M in revenue by Jan 1st, 2023?](https://manifold.markets/ManifoldMarkets/will-mantic-markets-have-over-1m)
- [Will we discover life on Mars before 2024?](https://manifold.markets/LarsDoucet/will-we-discover-life-on-mars-befor)
- Some binary markets are used as quasi-numeric markets, such as:
- [How many additional subscribers will my newsletter have by the end of February?](https://manifold.markets/Nu%C3%B1oSempere/how-many-additional-subscribers-wil)
- [How many new signups will Manifold have at the end of launch day?](https://manifold.markets/ManifoldMarkets/how-many-new-signups-will-manifold)
- [What day will US Covid deaths peak in February?](https://manifold.markets/JamesGrugett/what-day-will-us-covid-deaths-peak)
- These markets are made possible by the MKT option described below.
## Binary Markets: Betting & Payouts
- Traders can place a bet on either YES or NO and receive shares in the outcome in return.
- Betting on YES will increase the markets implied probability; betting on NO will decrease the probability.
- Manifold's automated market automatically adjusts the market probability after each trade and determines how many shares a user will get for their bet.
- You can sell back your shares for cash. If you sell YES shares, the market probability will go down. If you sell NO shares, the probability will go up.
- 1 YES share = M$1 if the event happens. 1 NO share = M$1 if the event does not happen.
- Notice that 1 YES share + 1 NO share = M$1. If you ever get multiple YES and NO shares, they will cancel out and you will be left with cash.
- When the market is resolved, you will be paid out according to your shares. If you own 100 YES shares, if the event resolves YES, you will earn M$100. (If the event resolves NO, you will earn M$0).
- The creator of each market is responsible for resolving each market. They can resolve to YES, NO, MKT, or N/A.
- Resolving to MKT allows the creator to choose a percentage. The payout for any YES share is multiplied by this percentage, and vice versa for NO.
- For example, if a market resolves to MKT at 30%, if you have 100 shares of YES you will receive `M$100 * 30% = M$30`.
- In the same situation as above, if you have 100 shares of NO you will receive `M$100 * (100% - 30%) = M$70`.
- Note that even in this instance, 1 YES share plus 1 NO share still equals M$1.
## Binary Markets: Liquidity
- The liquidity in a market is the amount of capital available for traders to trade against. The more liquidity, the greater incentive there is for traders to bet, and the more accurate the market should be.
- When a market is created, the creation fee (also called the ante or subsidy) is used to fill the liquiity pool. This happens whether the creation fee is paid by the user or by Manifold for the daily free market.
- Behind the scenes, when a bet is placed the CPMM mechanism does [a bunch of math](http://bit.ly/maniswap). The end result is that for each M$1 bet, 1 YES share and 1 NO share is created. Some amount of shares are then given to the user who made the bet, and the rest are stored in the liquidity pool.
Due to this mechansim, the number of YES shares in the whole market always equals the number of NO shares.
- You can manually add liquidity to any market to increase the incentives for traders to participate. You can think of added liquidity as a subsidy for getting your question answered. You can do this by opening up the market info popup window located in the (...) section of the header on the market page.
- Adding liquidity provides you with a number of YES and NO shares, which can be withdrawn from the same interface. These shares resolve to M$ like normal when the market resolves, which will return you some amount of your investment.
- If the market moves significantly in either direction, your liquidity will become significantly less valuable. You are currently very unlikely to make money by investing liquidity in a market, it is a way to subsidize a market and encourage more people to bet, to achieve a more accurate answer.
- Adding liquidity to a market also makes it require more capital to move the market, so if you want to subsidize a market, first make sure the market price is roughly where you think it should be.
# Free-Response Markets
## Free-Response Markets: Overview
- Free-response markets are structured around a question with a multiple outcomes, such as:
- [Which team will win the NBA Finals 2022?](https://manifold.markets/howtodowtle/which-team-will-win-the-nba-finals)
- [Who will win "Top Streaming Songs Artist" at the 2022 Billboard Music Awards?](https://manifold.markets/Predictor/who-will-win-top-streaming-songs-ar)
- [What life improvement intervention suggested would I found most useful?](https://manifold.markets/vlad/what-life-improvement-intervention)
- Some free-response markets are used as quasi-numeric markets, such as:
- [What day will Russia invade Ukraine?](https://manifold.markets/Duncan/what-day-will-russia-invade-ukraine)
- [What will inflation be in March?](https://manifold.markets/ManifoldMarkets/what-will-inflation-be-in-march)
- [How many Manifold team members in the Bahamas will test positive for COVID?](https://manifold.markets/Sinclair/how-many-manifold-team-members-in-t)
## Free-Response Markets: Betting & Payouts
- Markets are structured around a list of answers, any of which can be bet on.
- When a Free Response market is created, the market creation fee goes into a hidden answer called the Ante and gets paid to the winner(s), to subsidize the market and create an incentive to bet. This happens whether the creation fee is paid by the user or by Manifold for the daily free market.
- This hidden answer is why a market's probabilities will not add up to 100%.
- If you want to further subsidize a market, it's customary to create an ANTE answer and put money in that.
- Anyone can add answers to a market as long as they stake some amount of M$ on it. Traders can place a bet on any answer and receive shares in the outcome in return.
- When a user places a bet, their M$ goes into the market's pool and they receive a certain amount of shares of the selected answer.
- When the market is resolved, you will be paid out according to your shares. If the creator resolves to answer #1, the entire pool is divided up amongst the users who bet on answer #1 proportional to their shares.
- The creator of each market is responsible for resolving each market. They can resolve to any single answer, or even multiple answers.
- Resolving to multiple answers allows the creator to choose a percentage for each selected answer (or distribute equally). The payout for any answer is taken from the amount of the total pool allocated to that answer.
- For example, let's take a free-response market with many answers. The pool for this market is $500, and you own 100 out of 500 total shares of answer #1.
- If the creator resolves to answer #1 only, you will receive `M$500 * (100 / 500) = M$100`.
- If the creator resolves 50% to answer #1 and 50% to answer #2, you will receive `(M$500 * 50%) * (100 / 500) = M$50`.
- Note that your payout is dependent on the total number of shares, and thus may decrease if more people buy shares in that answer.
# Fees
- Manifold charges fees on each trade. They are automatically calculated and baked into the number of shares you receive when you place a bet.
- Our CPMM fee schedule is currently: `10% * (1 - post-bet probability) * bet amount`
- Note that all current binary markets use this fee schedule.
- The post-bet probability is what the market probability would be after your bet if there were no fees.
- Example:
- If you bet M$100 on NO and the resulting probability without fees would be 10%, then you pay `M$100 * 10% * 10% = M$1.0`.
- If you bet M$100 on YES and the resulting probability without fees would be 50%, then you pay `M$100 * 10% * 50% = M$5.0`.
- 100% of this fee is used to provide a commission to the market creator, which is paid out after the market is resolved.
- Our DPM fee schedule is currently: `5% * (1 - post-bet probability) * bet amount`
- Note that all free-response markets use this fee schedule. The calculation for this is the same as above.
- 4% is used to provide a commission to the market creator, which is paid out after the market is resolved. 1% is "burnt" to prevent inflation.
- No fees are levied on sales. If you have existing shares in a binary market and buy shares on the opposite side, that is equivalent to selling your shares and you do not pay fees.

132
docs/docusaurus.config.js Normal file
View File

@ -0,0 +1,132 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github')
const darkCodeTheme = require('prism-react-renderer/themes/dracula')
const math = require('remark-math')
const katex = require('rehype-katex')
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Manifold Docs',
tagline: 'Learn more about the BESTEST prediction market platform~',
url: 'https://docs.manifold.markets',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'https://manifold.markets/favicon.ico',
organizationName: 'manifoldmarkets', // Usually your GitHub org/user name.
projectName: 'docs', // Usually your repo name.
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
routeBasePath: '/',
sidebarPath: require.resolve('./sidebars.js'),
editUrl: 'https://github.com/manifoldmarkets/manifold/tree/main/docs',
remarkPlugins: [math],
rehypePlugins: [katex],
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
}),
],
],
stylesheets: [
{
href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
type: 'text/css',
integrity:
'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
crossorigin: 'anonymous',
},
],
scripts: [
{
src: 'https://cdn.jsdelivr.net/npm/link-summoner@1.0.2/dist/browser.min.js',
async: 'true',
},
],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
navbar: {
title: 'Manifold Docs',
logo: {
alt: 'Manifold Markets Logo',
src: 'https://manifold.markets/logo.svg',
},
items: [
{
type: 'doc',
docId: 'about',
position: 'left',
label: 'Docs',
},
{
href: 'https://github.com/manifoldmarkets/manifold/tree/main/docs/docs',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Manifold',
items: [
{
label: 'Manifold Markets',
to: 'https://manifold.markets',
},
{
label: 'Docs',
to: '/',
},
],
},
{
title: 'Community',
items: [
{
label: 'Discord',
href: 'https://discord.gg/eHQBNBqXuh',
},
{
label: 'Twitter',
href: 'https://twitter.com/manifoldmarkets',
},
],
},
{
title: 'More',
items: [
{
label: 'Blog',
to: 'https://manifoldmarkets.substack.com',
},
{
label: 'GitHub',
href: 'https://github.com/manifoldmarkets/manifold/',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Manifold Markets, Inc. Built with Docusaurus.`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
},
}),
}
module.exports = config

48
docs/package.json Normal file
View File

@ -0,0 +1,48 @@
{
"name": "docs",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"dev": "yarn start",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"format": "prettier --write ."
},
"dependencies": {
"@docusaurus/core": "2.0.0-beta.17",
"@docusaurus/preset-classic": "2.0.0-beta.17",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.1.1",
"hast-util-is-element": "1.1.0",
"prism-react-renderer": "^1.2.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rehype-katex": "5",
"remark-math": "3"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.0.0-beta.17",
"@tsconfig/docusaurus": "^1.0.4",
"@types/react": "^17.0.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

31
docs/sidebars.js Normal file
View File

@ -0,0 +1,31 @@
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }],
// But you can create a sidebar manually
/*
tutorialSidebar: [
{
type: 'category',
label: 'Tutorial',
items: ['hello'],
},
],
*/
}
module.exports = sidebars

View File

@ -0,0 +1,70 @@
import React from 'react'
import clsx from 'clsx'
import styles from './styles.module.css'
type FeatureItem = {
title: string
Svg: React.ComponentType<React.ComponentProps<'svg'>>
description: JSX.Element
}
const FeatureList: FeatureItem[] = [
{
title: 'Easy to Use',
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
description: (
<>
Docusaurus was designed from the ground up to be easily installed and
used to get your website up and running quickly.
</>
),
},
{
title: 'Focus on What Matters',
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
description: (
<>
Docusaurus lets you focus on your docs, and we&apos;ll do the chores. Go
ahead and move your docs into the <code>docs</code> directory.
</>
),
},
{
title: 'Powered by React',
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
description: (
<>
Extend or customize your website layout by reusing React. Docusaurus can
be extended while reusing the same header and footer.
</>
),
},
]
function Feature({ title, Svg, description }: FeatureItem) {
return (
<div className={clsx('col col--4')}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<h3>{title}</h3>
<p>{description}</p>
</div>
</div>
)
}
export default function HomepageFeatures(): JSX.Element {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
)
}

View File

@ -0,0 +1,11 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}

44
docs/src/css/custom.css Normal file
View File

@ -0,0 +1,44 @@
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
article {
max-width: 720px;
margin: 0 auto;
}
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-code-font-size: 95%;
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
}
.docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.1);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}
[data-theme='dark'] .docusaurus-highlight-code-line {
background-color: rgba(0, 0, 0, 0.3);
}

View File

@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

Some files were not shown because too many files have changed in this diff Show More