Merge branch 'main' into loans2

This commit is contained in:
James Grugett 2022-08-21 23:56:26 -05:00
commit 2eb88a8d36
12 changed files with 347 additions and 321 deletions

View File

@ -23,13 +23,8 @@ type JwtCredentials = { kind: 'jwt'; data: admin.auth.DecodedIdToken }
type KeyCredentials = { kind: 'key'; data: string } type KeyCredentials = { kind: 'key'; data: string }
type Credentials = JwtCredentials | KeyCredentials type Credentials = JwtCredentials | KeyCredentials
const auth = admin.auth()
const firestore = admin.firestore()
const privateUsers = firestore.collection(
'private-users'
) as admin.firestore.CollectionReference<PrivateUser>
export const parseCredentials = async (req: Request): Promise<Credentials> => { export const parseCredentials = async (req: Request): Promise<Credentials> => {
const auth = admin.auth()
const authHeader = req.get('Authorization') const authHeader = req.get('Authorization')
if (!authHeader) { if (!authHeader) {
throw new APIError(403, 'Missing Authorization header.') throw new APIError(403, 'Missing Authorization header.')
@ -57,6 +52,8 @@ export const parseCredentials = async (req: Request): Promise<Credentials> => {
} }
export const lookupUser = async (creds: Credentials): Promise<AuthedUser> => { export const lookupUser = async (creds: Credentials): Promise<AuthedUser> => {
const firestore = admin.firestore()
const privateUsers = firestore.collection('private-users')
switch (creds.kind) { switch (creds.kind) {
case 'jwt': { case 'jwt': {
if (typeof creds.data.user_id !== 'string') { if (typeof creds.data.user_id !== 'string') {
@ -70,7 +67,7 @@ export const lookupUser = async (creds: Credentials): Promise<AuthedUser> => {
if (privateUserQ.empty) { if (privateUserQ.empty) {
throw new APIError(403, `No private user exists with API key ${key}.`) throw new APIError(403, `No private user exists with API key ${key}.`)
} }
const privateUser = privateUserQ.docs[0].data() const privateUser = privateUserQ.docs[0].data() as PrivateUser
return { uid: privateUser.id, creds: { privateUser, ...creds } } return { uid: privateUser.id, creds: { privateUser, ...creds } }
} }
default: default:

View File

@ -21,6 +21,7 @@ const bodySchema = z.object({
}) })
export const creategroup = newEndpoint({}, async (req, auth) => { export const creategroup = newEndpoint({}, async (req, auth) => {
const firestore = admin.firestore()
const { name, about, memberIds, anyoneCanJoin } = validate( const { name, about, memberIds, anyoneCanJoin } = validate(
bodySchema, bodySchema,
req.body req.body
@ -67,7 +68,7 @@ export const creategroup = newEndpoint({}, async (req, auth) => {
return { status: 'success', group: group } return { status: 'success', group: group }
}) })
const getSlug = async (name: string) => { export const getSlug = async (name: string) => {
const proposedSlug = slugify(name) const proposedSlug = slugify(name)
const preexistingGroup = await getGroupFromSlug(proposedSlug) const preexistingGroup = await getGroupFromSlug(proposedSlug)
@ -75,9 +76,8 @@ const getSlug = async (name: string) => {
return preexistingGroup ? proposedSlug + '-' + randomString() : proposedSlug return preexistingGroup ? proposedSlug + '-' + randomString() : proposedSlug
} }
const firestore = admin.firestore()
export async function getGroupFromSlug(slug: string) { export async function getGroupFromSlug(slug: string) {
const firestore = admin.firestore()
const snap = await firestore const snap = await firestore
.collection('groups') .collection('groups')
.where('slug', '==', slug) .where('slug', '==', slug)

View File

@ -15,17 +15,15 @@ import {
import { slugify } from '../../common/util/slugify' import { slugify } from '../../common/util/slugify'
import { randomString } from '../../common/util/random' import { randomString } from '../../common/util/random'
import { chargeUser, getContract, isProd } from './utils' import { chargeUser, getContract } from './utils'
import { APIError, newEndpoint, validate, zTimestamp } from './api' import { APIError, newEndpoint, validate, zTimestamp } from './api'
import { import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
FIXED_ANTE, FIXED_ANTE,
getCpmmInitialLiquidity, getCpmmInitialLiquidity,
getFreeAnswerAnte, getFreeAnswerAnte,
getMultipleChoiceAntes, getMultipleChoiceAntes,
getNumericAnte, getNumericAnte,
HOUSE_LIQUIDITY_PROVIDER_ID,
} from '../../common/antes' } from '../../common/antes'
import { Answer, getNoneAnswer } from '../../common/answer' import { Answer, getNoneAnswer } from '../../common/answer'
import { getNewContract } from '../../common/new-contract' import { getNewContract } from '../../common/new-contract'
@ -223,9 +221,7 @@ export const createmarket = newEndpoint({}, async (req, auth) => {
} }
} }
const providerId = isProd() const providerId = user.id
? HOUSE_LIQUIDITY_PROVIDER_ID
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID
if (outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC') { if (outcomeType === 'BINARY' || outcomeType === 'PSEUDO_NUMERIC') {
const liquidityDoc = firestore const liquidityDoc = firestore

View File

@ -103,94 +103,28 @@
</head> </head>
<body style="word-spacing: normal; background-color: #f4f4f4"> <body style="word-spacing: normal; background-color: #f4f4f4">
<div style="background-color: #f4f4f4">
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]--> <!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style=" <div style="
background: #ffffff; background: #ffffff;
background-color: #ffffff; background-color: #ffffff;
margin: 0px auto; margin: 0px auto;
max-width: 600px; max-width: 600px;
"> ">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="background: #ffffff; background-color: #ffffff; width: 100%"> style="background: #ffffff; background-color: #ffffff; width: 100%">
<tbody> <tbody>
<tr> <tr>
<td style=" <td style="width:550px;">
direction: ltr; <a href="https://manifold.markets" target="_blank">
font-size: 0px; <img alt="banner logo" height="auto" src="https://manifold.markets/logo-banner.png"
padding: 0px 0px 0px 0px; style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;"
padding-bottom: 0px; title="" width="550">
padding-left: 0px; </a>
padding-right: 0px; </td>
padding-top: 0px; </tr>
text-align: center; <tr>
"> <td style="
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="
font-size: 0px;
text-align: left;
direction: ltr;
display: inline-block;
vertical-align: top;
width: 100%;
">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align: top"
width="100%">
<tbody>
<tr>
<td align="center" style="
font-size: 0px;
padding: 0px 25px 0px 25px;
padding-top: 0px;
padding-right: 25px;
padding-bottom: 0px;
padding-left: 25px;
word-break: break-word;
">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="
border-collapse: collapse;
border-spacing: 0px;
">
<tbody>
<tr>
<td style="width: 550px">
<a href="https://manifold.markets/home" target="_blank"><img alt="" height="auto"
src="https://03jlj.mjt.lu/img/03jlj/b/96u/omk8.gif" style="
border: none;
display: block;
outline: none;
text-decoration: none;
height: auto;
width: 100%;
font-size: 13px;
" width="550" /></a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="
background: #ffffff;
background-color: #ffffff;
margin: 0px auto;
max-width: 600px;
">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="background: #ffffff; background-color: #ffffff; width: 100%">
<tbody>
<tr>
<td style="
direction: ltr; direction: ltr;
font-size: 0px; font-size: 0px;
padding: 20px 0px 0px 0px; padding: 20px 0px 0px 0px;
@ -200,8 +134,8 @@
padding-top: 20px; padding-top: 20px;
text-align: center; text-align: center;
"> ">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]--> <!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style=" <div class="mj-column-per-100 mj-outlook-group-fix" style="
font-size: 0px; font-size: 0px;
text-align: left; text-align: left;
direction: ltr; direction: ltr;
@ -209,24 +143,24 @@
vertical-align: top; vertical-align: top;
width: 100%; width: 100%;
"> ">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align: top" <table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align: top"
width="100%"> width="100%">
<tbody> <tbody>
<tr> <tr>
<td align="left" <td align="left"
style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;"> style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;">
<div <div
style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;"> style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;">
<p class="text-build-content" <p class="text-build-content"
style="line-height: 24px; margin: 10px 0; margin-top: 10px; margin-bottom: 10px;" style="line-height: 24px; margin: 10px 0; margin-top: 10px; margin-bottom: 10px;"
data-testid="4XoHRGw1Y"><span data-testid="4XoHRGw1Y"><span
style="color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:18px;"> style="color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:18px;">
Hi {{name}},</span></p> Hi {{name}},</span></p>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="left" style=" <td align="left" style="
font-size: 0px; font-size: 0px;
padding: 0px 25px 20px 25px; padding: 0px 25px 20px 25px;
padding-top: 0px; padding-top: 0px;
@ -235,7 +169,7 @@
padding-left: 25px; padding-left: 25px;
word-break: break-word; word-break: break-word;
"> ">
<div style=" <div style="
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
font-size: 17px; font-size: 17px;
letter-spacing: normal; letter-spacing: normal;
@ -243,197 +177,253 @@
text-align: left; text-align: left;
color: #000000; color: #000000;
"> ">
<p class="text-build-content" style=" <p class="text-build-content" style="
line-height: 23px; line-height: 23px;
margin: 10px 0; margin: 10px 0;
margin-top: 10px; margin-top: 10px;
" data-testid="3Q8BP69fq"> " data-testid="3Q8BP69fq">
<span style=" <span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
">Congrats on creating your first market on <a class="link-build-content" ">Did you know you create your own prediction market on <a class="link-build-content"
style="color: #55575d" target="_blank" style="color: #55575d" target="_blank" href="https://manifold.markets">Manifold</a> for
href="https://manifold.markets">Manifold</a>!</span> any question you care about?</span>
</p> </p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" <p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
data-testid="3Q8BP69fq"> <span style="
<span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
">The following is a short guide to creating markets.</span> ">Whether it's current events like <a class="link-build-content" style="color: #55575d"
</p> target="_blank"
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" href="https://manifold.markets/SG/will-elon-musk-buy-twitter-this-yea">Musk buying
data-testid="3Q8BP69fq"> Twitter</a> or <a class="link-build-content" style="color: #55575d" target="_blank"
&nbsp; href="https://manifold.markets/NathanpmYoung/will-biden-be-the-2024-democratic-n">2024
</p> elections</a> or personal matters
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" like <a class="link-build-content" style="color: #55575d" target="_blank"
data-testid="3Q8BP69fq"> href="https://manifold.markets/dreev/which-book-will-i-like-best">book
<span style=" recommendations</a> or <a class="link-build-content" style="color: #55575d"
target="_blank"
href="https://manifold.markets/agentydragon/will-my-weight-go-under-115-kg-in-2">losing
weight</a>,
Manifold can help you find the answer.</span>
</p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0;margin-bottom: 20px;"
data-testid="3Q8BP69fq">
<span style="
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 17px;
">The following is a
short guide to creating markets.</span>
</p>
<table cellspacing="0" cellpadding="0" align="center">
<tr>
<td style="border-radius: 4px;" bgcolor="#4337c9">
<a href="https://manifold.markets/create" target="_blank"
style="padding: 12px 16px; border: 1px solid #4337c9;border-radius: 16px;font-family: Helvetica, Arial, sans-serif;font-size: 24px; color: #ffffff;text-decoration: none;font-weight:semibold;display: inline-block;">
Create a market
</a>
</td>
</tr>
</table>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
&nbsp;
</p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
<span style="
color: #292fd7; color: #292fd7;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 20px; font-size: 20px;
"><b>What makes a good market?</b></span> "><b>What makes a good market?</b></span>
</p> </p>
<ul> <ul>
<li style="line-height: 23px; margin-bottom: 8px;"> <li style="line-height: 23px; margin-bottom: 8px;">
<span <span
style="font-family: Readex Pro, Arial, Helvetica, sans-serif;font-size: 17px;"><b>Interesting style="font-family: Readex Pro, Arial, Helvetica, sans-serif;font-size: 17px;"><b>Interesting
topic. </b>Manifold gives topic. </b>Manifold gives
creators M$10 for creators M$10 for
each unique trader that bets on your each unique trader that bets on your
market, so it pays to ask a question people are interested in!</span> market, so it pays to ask a question people are interested in!</span>
</li> </li>
<li style="line-height: 23px; margin-bottom: 8px;"> <li style="line-height: 23px; margin-bottom: 8px;">
<span style=" <span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><b>Clear resolution criteria. </b>Any ambiguities or edge cases in your description "><b>Clear resolution criteria. </b>Any ambiguities or edge cases in your description
will drive traders away from your markets.</span> will drive traders away from your markets.</span>
</li> </li>
<li style="line-height: 23px; margin-bottom: 8px;"> <li style="line-height: 23px; margin-bottom: 8px;">
<span style=" <span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><b>Detailed description. </b>Include images/videos/tweets and any context or "><b>Detailed description. </b>Include images/videos/tweets and any context or
background background
information that could be useful to people who information that could be useful to people who
are interested in learning more that are are interested in learning more that are
uneducated on the subject.</span> uneducated on the subject.</span>
</li> </li>
<li style="line-height: 23px; margin-bottom: 8px;"> <li style="line-height: 23px; margin-bottom: 8px;">
<span style=" <span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><b>Add it to a group. </b>Groups are the "><b>Part of a group. </b>Groups are the
primary way users filter for relevant markets. primary way users filter for relevant markets.
Also, consider making your own groups and Also, consider making your own groups and
inviting friends/interested communities to inviting friends/interested communities to
them from other sites!</span> them from other sites!</span>
</li> </li>
<li style="line-height: 23px; margin-bottom: 8px;"> <li style="line-height: 23px; margin-bottom: 8px;">
<span style=" <span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><b>Share it on social media</b>. You'll earn the <a class="link-build-content" "><b>Sharing it on social media</b>. You'll earn the <a class="link-build-content"
style="color: inherit; text-decoration: none" target="_blank" style="color: inherit; text-decoration: none" target="_blank"
href="https://manifold.markets/referrals"><span style=" href="https://manifold.markets/referrals"><span style="
color: #55575d; color: #55575d;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><u>M$500 "><u>M$500
referral bonus</u></span></a> if you get new users to sign up!</span> referral bonus</u></span></a> if you get new users to sign up!</span>
</li> </li>
</ul> </ul>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" <p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
data-testid="3Q8BP69fq"> &nbsp;
&nbsp; </p>
</p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" <p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
data-testid="3Q8BP69fq"> <span style="
<span style="
color: #292fd7;
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 20px;
"><b>Examples of markets you should
emulate!&nbsp;</b></span>
</p>
<ul>
<li style="line-height: 23px">
<a class="link-build-content" style="color: inherit; text-decoration: none"
target="_blank"
href="https://manifold.markets/DavidChee/will-our-upcoming-twitch-bot-be-a-s"><span
style="
color: #55575d;
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 17px;
"><u>This complex market</u></span></a><span style="
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 17px;
">
about the project I am working on.</span>
</li>
<li style="line-height: 23px">
<a class="link-build-content" style="color: inherit; text-decoration: none"
target="_blank"
href="https://manifold.markets/SneakySly/will-manifold-reach-1000-weekly-act"><span
style="
color: #55575d;
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 17px;
"><u>This simple market</u></span></a><span style="
font-family: Readex Pro, Arial, Helvetica,
sans-serif;
font-size: 17px;
">
about Manifold&apos;s weekly active
users.</span>
</li>
</ul>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0"
data-testid="3Q8BP69fq">
&nbsp;
</p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0"
data-testid="3Q8BP69fq">
<span style="
color: #000000; color: #000000;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
">Why not </span> ">Why not </span>
<a class="link-build-content" style="color: inherit; text-decoration: none" target="_blank"
href="https://manifold.markets/create"><span style="
<a class="link-build-content" style="color: inherit; text-decoration: none" target="_blank"
href="https://manifold.markets/create"><span style="
color: #55575d; color: #55575d;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"><u>create another market</u></span></a><span style=" "><u>create a market</u></span></a><span style="
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
"> ">
while it is still fresh on your mind? while it is still fresh on your mind?
</p> </p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" <p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
data-testid="3Q8BP69fq"> &nbsp;
<span style=" </p>
<p class="text-build-content" style="line-height: 23px; margin: 10px 0" data-testid="3Q8BP69fq">
<span style="
color: #000000; color: #000000;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
">Thanks for reading!</span> ">Thanks for reading!</span>
</p> </p>
<p class="text-build-content" style=" <p class="text-build-content" style="
line-height: 23px; line-height: 23px;
margin: 10px 0; margin: 10px 0;
margin-bottom: 10px; margin-bottom: 10px;
" data-testid="3Q8BP69fq"> " data-testid="3Q8BP69fq">
<span style=" <span style="
color: #000000; color: #000000;
font-family: Readex Pro, Arial, Helvetica, font-family: Readex Pro, Arial, Helvetica,
sans-serif; sans-serif;
font-size: 17px; font-size: 17px;
">David from Manifold</span> ">David from Manifold</span>
</p> </p>
</div> </div>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>
</div>
<div style="background-color: #f4f4f4">
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
<div style="
background: #ffffff;
background-color: #ffffff;
margin: 0px auto;
max-width: 600px;
">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation"
style="background: #ffffff; background-color: #ffffff; width: 100%">
<tbody>
<tr>
<td style="
direction: ltr;
font-size: 0px;
padding: 0px 0px 0px 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
text-align: center;
">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div class="mj-column-per-100 mj-outlook-group-fix" style="
font-size: 0px;
text-align: left;
direction: ltr;
display: inline-block;
vertical-align: top;
width: 100%;
">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align: top"
width="100%">
<tbody>
<tr>
<td align="center" style="
font-size: 0px;
padding: 0px 25px 0px 25px;
padding-top: 0px;
padding-right: 25px;
padding-bottom: 0px;
padding-left: 25px;
word-break: break-word;
">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="
border-collapse: collapse;
border-spacing: 0px;
">
<tbody>
<tr>
<td style="width: 550px">
<a href="https://manifold.markets/create" target="_blank"><img alt="" height="auto"
src="https://03jlj.mjt.lu/img/03jlj/b/96u/omk8.gif" style="
border: none;
display: block;
outline: none;
text-decoration: none;
height: auto;
width: 100%;
font-size: 13px;
" width="550" /></a>
</td>
</tr>
</tbody>
</table>
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@ -107,19 +107,12 @@
width="100%"> width="100%">
<tbody> <tbody>
<tr> <tr>
<td align="center" <td style="width:550px;">
style="font-size:0px;padding:0px 25px 0px 25px;padding-top:0px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;"> <a href="https://manifold.markets" target="_blank">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" <img alt="banner logo" height="auto" src="https://manifold.markets/logo-banner.png"
style="border-collapse:collapse;border-spacing:0px;"> style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;"
<tbody> title="" width="550">
<tr> </a>
<td style="width:550px;"><a href="https://manifold.markets/home" target="_blank"><img
alt="" height="auto" src="https://03jlj.mjt.lu/img/03jlj/b/urx/sjtz.gif"
style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;"
width="550"></a></td>
</tr>
</tbody>
</table>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -175,9 +168,9 @@
<td> <td>
<table cellspacing="0" cellpadding="0"> <table cellspacing="0" cellpadding="0">
<tr> <tr>
<td style="border-radius: 2px;" bgcolor="#4337c9"> <td style="border-radius: 4px;" bgcolor="#4337c9">
<a href="https://manifold.markets" target="_blank" <a href="https://manifold.markets" target="_blank"
style="padding: 12px 16px; border: 1px solid #4337c9;border-radius: 16px;font-family: Helvetica, Arial, sans-serif;font-size: 24px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;"> style="padding: 12px 16px; border: 1px solid #4337c9;border-radius: 16px;font-family: Helvetica, Arial, sans-serif;font-size: 24px; color: #ffffff;text-decoration: none;font-weight:semibold;display: inline-block;">
Explore markets Explore markets
</a> </a>
</td> </td>
@ -225,22 +218,12 @@
style="color:#55575d;font-family:Arial;font-size:18px;"><u>Join our Discord style="color:#55575d;font-family:Arial;font-size:18px;"><u>Join our Discord
chat</u></span></a></span></li> chat</u></span></a></span></li>
</ul> </ul>
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;">&nbsp;</p>
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span
style="color:#000000;font-family:Arial;font-size:18px;">Cheers,</span>
</p>
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span
style="color:#000000;font-family:Arial;font-size:18px;">David
from Manifold</span></p>
<p class="text-build-content" data-testid="3Q8BP69fq"
style="margin: 10px 0; margin-bottom: 10px;">&nbsp;</p>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td align="left" <td align="left"
style="font-size:0px;padding:15px 25px 0px 25px;padding-top:15px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;"> style="font-size:0px;padding:15px 25px 0px 25px;padding-top:0px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;">
<div <div
style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;"> style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;">
<p class="text-build-content" style="line-height: 23px; margin: 10px 0; margin-top: 10px;" <p class="text-build-content" style="line-height: 23px; margin: 10px 0; margin-top: 10px;"

View File

@ -1,4 +1,3 @@
import { DOMAIN } from '../../common/envs/constants' import { DOMAIN } from '../../common/envs/constants'
import { Answer } from '../../common/answer' import { Answer } from '../../common/answer'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'
@ -192,7 +191,6 @@ Cofounder of Manifold Markets
https://manifold.markets https://manifold.markets
` `
await sendTextEmail( await sendTextEmail(
privateUser.email, privateUser.email,
'How are you finding Manifold?', 'How are you finding Manifold?',
@ -238,7 +236,8 @@ export const sendOneWeekBonusEmail = async (
export const sendCreatorGuideEmail = async ( export const sendCreatorGuideEmail = async (
user: User, user: User,
privateUser: PrivateUser privateUser: PrivateUser,
sendTime: string
) => { ) => {
if ( if (
!privateUser || !privateUser ||
@ -255,7 +254,7 @@ export const sendCreatorGuideEmail = async (
return await sendTemplateEmail( return await sendTemplateEmail(
privateUser.email, privateUser.email,
'Market creation guide', 'Create your own prediction market',
'creating-market', 'creating-market',
{ {
name: firstName, name: firstName,
@ -263,6 +262,7 @@ export const sendCreatorGuideEmail = async (
}, },
{ {
from: 'David from Manifold <david@manifold.markets>', from: 'David from Manifold <david@manifold.markets>',
'o:deliverytime': sendTime,
} }
) )
} }

View File

@ -1,13 +1,10 @@
import * as functions from 'firebase-functions' import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import { getPrivateUser, getUser } from './utils' import { getUser } from './utils'
import { createNotification } from './create-notification' import { createNotification } from './create-notification'
import { Contract } from '../../common/contract' import { Contract } from '../../common/contract'
import { parseMentions, richTextToString } from '../../common/util/parse' import { parseMentions, richTextToString } from '../../common/util/parse'
import { JSONContent } from '@tiptap/core' import { JSONContent } from '@tiptap/core'
import { User } from 'common/user'
import { sendCreatorGuideEmail } from './emails'
export const onCreateContract = functions export const onCreateContract = functions
.runWith({ secrets: ['MAILGUN_KEY'] }) .runWith({ secrets: ['MAILGUN_KEY'] })
@ -31,23 +28,4 @@ export const onCreateContract = functions
richTextToString(desc), richTextToString(desc),
{ contract, recipients: mentioned } { contract, recipients: mentioned }
) )
await sendGuideEmail(contractCreator)
}) })
const firestore = admin.firestore()
const sendGuideEmail = async (contractCreator: User) => {
const query = await firestore
.collection(`contracts`)
.where('creatorId', '==', contractCreator.id)
.limit(2)
.get()
if (query.size >= 2) return
const privateUser = await getPrivateUser(contractCreator.id)
if (!privateUser) return
await sendCreatorGuideEmail(contractCreator, privateUser)
}

View File

@ -6,6 +6,7 @@ dayjs.extend(utc)
import { getPrivateUser } from './utils' import { getPrivateUser } from './utils'
import { User } from 'common/user' import { User } from 'common/user'
import { import {
sendCreatorGuideEmail,
sendInterestingMarketsEmail, sendInterestingMarketsEmail,
sendPersonalFollowupEmail, sendPersonalFollowupEmail,
sendWelcomeEmail, sendWelcomeEmail,
@ -22,7 +23,10 @@ export const onCreateUser = functions
await sendWelcomeEmail(user, privateUser) await sendWelcomeEmail(user, privateUser)
const followupSendTime = dayjs().add(4, 'hours').toString() const guideSendTime = dayjs().add(28, 'hours').toString()
await sendCreatorGuideEmail(user, privateUser, guideSendTime)
const followupSendTime = dayjs().add(48, 'hours').toString()
await sendPersonalFollowupEmail(user, privateUser, followupSendTime) await sendPersonalFollowupEmail(user, privateUser, followupSendTime)
// skip email if weekly email is about to go out // skip email if weekly email is about to go out

View File

@ -0,0 +1,66 @@
// Takes a tag and makes a new group with all the contracts in it.
import * as admin from 'firebase-admin'
import { initAdmin } from './script-init'
import { isProd, log } from '../utils'
import { getSlug } from '../create-group'
import { Group } from '../../../common/group'
const getTaggedContractIds = async (tag: string) => {
const firestore = admin.firestore()
const results = await firestore
.collection('contracts')
.where('lowercaseTags', 'array-contains', tag.toLowerCase())
.get()
return results.docs.map((d) => d.id)
}
const createGroup = async (
name: string,
about: string,
contractIds: string[]
) => {
const firestore = admin.firestore()
const creatorId = isProd()
? 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2'
: '94YYTk1AFWfbWMpfYcvnnwI1veP2'
const slug = await getSlug(name)
const groupRef = firestore.collection('groups').doc()
const now = Date.now()
const group: Group = {
id: groupRef.id,
creatorId,
slug,
name,
about,
createdTime: now,
mostRecentActivityTime: now,
contractIds: contractIds,
anyoneCanJoin: true,
memberIds: [],
}
return await groupRef.create(group)
}
const convertTagToGroup = async (tag: string, groupName: string) => {
log(`Looking up contract IDs with tag ${tag}...`)
const contractIds = await getTaggedContractIds(tag)
log(`${contractIds.length} contracts found.`)
if (contractIds.length > 0) {
log(`Creating group ${groupName}...`)
const about = `Contracts that used to be tagged ${tag}.`
const result = await createGroup(groupName, about, contractIds)
log(`Done. Group: `, result)
}
}
if (require.main === module) {
initAdmin()
const args = process.argv.slice(2)
if (args.length != 2) {
console.log('Usage: convert-tag-to-group [tag] [group-name]')
} else {
convertTagToGroup(args[0], args[1]).catch((e) => console.error(e))
}
}

View File

@ -22,20 +22,20 @@ export function LimitBets(props: {
className?: string className?: string
}) { }) {
const { contract, bets, className } = props const { contract, bets, className } = props
const sortedBets = sortBy(
bets,
(bet) => -1 * bet.limitProb,
(bet) => -1 * bet.createdTime
)
const user = useUser() const user = useUser()
const yourBets = sortedBets.filter((bet) => bet.userId === user?.id)
const yourBets = sortBy(
bets.filter((bet) => bet.userId === user?.id),
(bet) => -1 * bet.limitProb,
(bet) => bet.createdTime
)
return ( return (
<Col className={className}> <Col className={className}>
{yourBets.length === 0 && ( {yourBets.length === 0 && (
<OrderBookButton <OrderBookButton
className="self-end" className="self-end"
limitBets={sortedBets} limitBets={bets}
contract={contract} contract={contract}
/> />
)} )}
@ -49,7 +49,7 @@ export function LimitBets(props: {
<OrderBookButton <OrderBookButton
className="self-end" className="self-end"
limitBets={sortedBets} limitBets={bets}
contract={contract} contract={contract}
/> />
</Row> </Row>
@ -163,8 +163,18 @@ export function OrderBookButton(props: {
const { limitBets, contract, className } = props const { limitBets, contract, className } = props
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const yesBets = limitBets.filter((bet) => bet.outcome === 'YES') const sortedBets = sortBy(
const noBets = limitBets.filter((bet) => bet.outcome === 'NO').reverse() limitBets,
(bet) => -1 * bet.limitProb,
(bet) => bet.createdTime
)
const yesBets = sortedBets.filter((bet) => bet.outcome === 'YES')
const noBets = sortBy(
sortedBets.filter((bet) => bet.outcome === 'NO'),
(bet) => bet.limitProb,
(bet) => bet.createdTime
)
return ( return (
<> <>
@ -194,7 +204,7 @@ export function OrderBookButton(props: {
</Row> </Row>
<Col className="md:hidden"> <Col className="md:hidden">
<LimitOrderTable <LimitOrderTable
limitBets={limitBets} limitBets={sortedBets}
contract={contract} contract={contract}
isYou={false} isYou={false}
/> />

View File

@ -14,7 +14,7 @@ export const PortfolioValueSection = memo(
}) { }) {
const { disableSelector, userId } = props const { disableSelector, userId } = props
const [portfolioPeriod, setPortfolioPeriod] = useState<Period>('allTime') const [portfolioPeriod, setPortfolioPeriod] = useState<Period>('weekly')
const [portfolioHistory, setUsersPortfolioHistory] = useState< const [portfolioHistory, setUsersPortfolioHistory] = useState<
PortfolioMetrics[] PortfolioMetrics[]
>([]) >([])
@ -53,13 +53,15 @@ export const PortfolioValueSection = memo(
{!disableSelector && ( {!disableSelector && (
<select <select
className="select select-bordered self-start" className="select select-bordered self-start"
value={portfolioPeriod}
onChange={(e) => { onChange={(e) => {
setPortfolioPeriod(e.target.value as Period) setPortfolioPeriod(e.target.value as Period)
}} }}
> >
<option value="allTime">{allTimeLabel}</option> <option value="allTime">{allTimeLabel}</option>
<option value="weekly">7 days</option> <option value="weekly">Last 7d</option>
<option value="daily">24 hours</option> {/* Note: 'daily' seems to be broken? */}
{/* <option value="daily">Last 24h</option> */}
</select> </select>
)} )}
</Row> </Row>

View File

@ -39,8 +39,8 @@ export async function getStaticProps() {
]) ])
const matches = quadraticMatches(txns, totalRaised) const matches = quadraticMatches(txns, totalRaised)
const numDonors = uniqBy(txns, (txn) => txn.fromId).length const numDonors = uniqBy(txns, (txn) => txn.fromId).length
const mostRecentDonor = await getUser(txns[0].fromId) const mostRecentDonor = txns[0] ? await getUser(txns[0].fromId) : null
const mostRecentCharity = txns[0].toId const mostRecentCharity = txns[0]?.toId ?? ''
return { return {
props: { props: {
@ -94,8 +94,8 @@ export default function Charity(props: {
matches: { [charityId: string]: number } matches: { [charityId: string]: number }
txns: Txn[] txns: Txn[]
numDonors: number numDonors: number
mostRecentDonor: User mostRecentDonor?: User | null
mostRecentCharity: string mostRecentCharity?: string
}) { }) {
const { const {
totalRaised, totalRaised,
@ -159,8 +159,8 @@ export default function Charity(props: {
}, },
{ {
name: 'Most recent donor', name: 'Most recent donor',
stat: mostRecentDonor.name ?? 'Nobody', stat: mostRecentDonor?.name ?? 'Nobody',
url: `/${mostRecentDonor.username}`, url: `/${mostRecentDonor?.username}`,
}, },
{ {
name: 'Most recent donation', name: 'Most recent donation',