Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trading ban #2853

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions backend/api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ import {
} from './get-dashboard-from-slug'
import { unresolve } from './unresolve'
import { referuser } from 'api/refer-user'
import { banuser } from 'api/ban-user'
import { banUserFromPosting } from 'api/ban-user-from-posting'
import { banUserFromMana } from './ban-user-from-mana'
import { updateMarket } from 'api/update-market'
import { createprivateusermessage } from 'api/create-private-user-message'
import { createprivateusermessagechannel } from 'api/create-private-user-message-channel'
Expand Down Expand Up @@ -192,6 +193,7 @@ import { getUserLimitOrdersWithContracts } from 'api/get-user-limit-orders-with-
import { getInterestingGroupsFromViews } from 'api/get-interesting-groups-from-views'
import { completeCashoutSession } from 'api/gidx/complete-cashout-session'
import { getCashouts } from './get-cashouts'
import { banUserFromSweepcash } from './ban-user-from-sweepcash'

const allowCorsUnrestricted: RequestHandler = cors({})

Expand Down Expand Up @@ -512,7 +514,9 @@ app.post('/updatedashboard', ...apiRoute(updatedashboard))
app.post('/delete-dashboard', ...apiRoute(deletedashboard))
app.get('/get-news-dashboards', ...apiRoute(getnews))
app.post('/getdashboardfromslug', ...apiRoute(getdashboardfromslug))
app.post('/ban-user', ...apiRoute(banuser))
app.post('/ban-user-from-posting', ...apiRoute(banUserFromPosting))
app.post('/ban-user-from-mana', ...apiRoute(banUserFromMana))
app.post('/ban-user-from-sweepcash', ...apiRoute(banUserFromSweepcash))
app.post('/create-private-user-message', ...apiRoute(createprivateusermessage))
app.post(
'/create-private-user-message-channel',
Expand Down
30 changes: 30 additions & 0 deletions backend/api/src/ban-user-from-mana.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { APIError, authEndpoint, validate } from 'api/helpers/endpoint'
import { z } from 'zod'
import { trackPublicEvent } from 'shared/analytics'
import { throwErrorIfNotMod } from 'shared/helpers/auth'
import { isAdminId } from 'common/envs/constants'
import { log } from 'shared/utils'
import { createSupabaseDirectClient } from 'shared/supabase/init'
import { updateUser } from 'shared/supabase/users'

const bodySchema = z
.object({
userId: z.string(),
unban: z.boolean().optional(),
})
.strict()

export const banUserFromMana = authEndpoint(async (req, auth) => {
const { userId, unban } = validate(bodySchema, req.body)
const pg = createSupabaseDirectClient()
await throwErrorIfNotMod(auth.uid)
if (isAdminId(userId)) throw new APIError(403, 'Cannot ban admin')
await trackPublicEvent(auth.uid, 'ban user from trading mana', {
userId,
})
await updateUser(pg, userId, {
isBannedFromMana: !unban,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would prefer isBannedFromManaTrading

})
log(`Updated trading ban status for user ${userId}`)
return { success: true }
})
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const bodySchema = z
})
.strict()

export const banuser = authEndpoint(async (req, auth) => {
export const banUserFromPosting = authEndpoint(async (req, auth) => {
const { userId, unban } = validate(bodySchema, req.body)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to name properties in the positive valence, like ban rather than unban

const db = createSupabaseDirectClient()
const pg = createSupabaseDirectClient()
await throwErrorIfNotMod(auth.uid)
if (isAdminId(userId)) throw new APIError(403, 'Cannot ban admin')
await trackPublicEvent(auth.uid, 'ban user', {
userId,
})
await updateUser(db, userId, {
await updateUser(pg, userId, {
isBannedFromPosting: !unban,
})
log('updated user')
Expand Down
30 changes: 30 additions & 0 deletions backend/api/src/ban-user-from-sweepcash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { APIError, authEndpoint, validate } from 'api/helpers/endpoint'
import { z } from 'zod'
import { trackPublicEvent } from 'shared/analytics'
import { throwErrorIfNotMod } from 'shared/helpers/auth'
import { isAdminId } from 'common/envs/constants'
import { log } from 'shared/utils'
import { createSupabaseDirectClient } from 'shared/supabase/init'
import { updateUser } from 'shared/supabase/users'

const bodySchema = z
.object({
userId: z.string(),
unban: z.boolean().optional(),
})
.strict()

export const banUserFromSweepcash = authEndpoint(async (req, auth) => {
const { userId, unban } = validate(bodySchema, req.body)
const pg = createSupabaseDirectClient()
await throwErrorIfNotMod(auth.uid)
if (isAdminId(userId)) throw new APIError(403, 'Cannot ban admin')
await trackPublicEvent(auth.uid, 'ban user from trading sweepcash', {
userId,
})
await updateUser(pg, userId, {
isBannedFromSweepcash: !unban,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isBannedFromSweeipcashTrading

})
log(`Updated trading ban status for user ${userId}`)
return { success: true }
})
1 change: 0 additions & 1 deletion backend/api/src/delete-me.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const deleteMe: APIHandler<'me/delete'> = async (body, auth) => {
const pg = createSupabaseDirectClient()
await updateUser(pg, auth.uid, {
userDeleted: true,
isBannedFromPosting: true,
})
await updatePrivateUser(pg, auth.uid, {
email: FieldVal.delete(),
Expand Down
4 changes: 3 additions & 1 deletion backend/api/src/get-mod-reports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const getModReports: APIHandler<'get-mod-reports'> = async () => {
owner.username as owner_username,
owner.data->>'avatarUrl' as owner_avatar_url,
owner.name as owner_name,
(owner.data->>'isBannedFromPosting')::boolean as owner_is_banned_from_posting
(owner.data->>'isBannedFromPosting')::boolean as owner_is_banned_from_posting,
(owner.data->>'isBannedFromMana')::boolean as owner_is_banned_from_mana,
(owner.data->>'isBannedFromSweepcash')::boolean as owner_is_banned_from_sweepcash
from mod_reports mr
join contract_comments cc on cc.comment_id = mr.comment_id
join contracts c on c.id = mr.contract_id
Expand Down
13 changes: 10 additions & 3 deletions backend/api/src/get-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ export const getLiteUser = async (
) => {
const pg = createSupabaseDirectClient()
const liteUser = await pg.oneOrNone(
`select id, name, username, data->>'avatarUrl' as "avatarUrl", data->'isBannedFromPosting' as "isBannedFromPosting"
from users
where ${'id' in props ? 'id' : 'username'} = $1`,
`select
id,
name,
username,
data->>'avatarUrl' as "avatarUrl",
(data->>'isBannedFromPosting')::boolean as "isBannedFromPosting",
(data->>'isBannedFromMana')::boolean as "isBannedFromMana",
(data->>'isBannedFromSweepcash')::boolean as "isBannedFromSweepcash"
from users
where ${'id' in props ? 'id' : 'username'} = $1`,
['id' in props ? props.id : props.username]
)
if (!liteUser) throw new APIError(404, 'User not found')
Expand Down
24 changes: 23 additions & 1 deletion backend/api/src/place-bet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@ import { Answer } from 'common/answer'
import { CpmmState, getCpmmProbability } from 'common/calculate-cpmm'
import { ValidatedAPIParams } from 'common/api/schema'
import { onCreateBets } from 'api/on-create-bet'
<<<<<<< trading-ban
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix these merge errors

import { isAdminId, TWOMBA_ENABLED } from 'common/envs/constants'
=======
import {
BANNED_TRADING_USER_IDS,
isAdminId,
TWOMBA_ENABLED,
} from 'common/envs/constants'
>>>>>>> main
import * as crypto from 'crypto'
import { formatMoneyWithDecimals } from 'common/util/format'
import {
Expand Down Expand Up @@ -294,7 +298,15 @@ export const fetchContractBetDataAndValidate = async (
contract.token === 'CASH' ? bet.cash_balance : bet.balance,
])
)

const unfilledBetUserIds = Object.keys(balanceByUserId)
if (
(isAdminId(uid) && contract.token === 'CASH') ||
(user.isBannedFromSweepcash && contract.token === 'CASH')
) {
throw new APIError(403, 'Banned from trading on sweepstakes markets.')
}

const balance = contract.token === 'CASH' ? user.cashBalance : user.balance
if (amount !== undefined && balance < amount)
throw new APIError(403, 'Insufficient balance.')
Expand All @@ -307,11 +319,16 @@ export const fetchContractBetDataAndValidate = async (
'You must be kyc verified to trade on sweepstakes markets.'
)
}
<<<<<<< trading-ban
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more merge indicators to remove

if (user.userDeleted) {
throw new APIError(403, 'You are banned or deleted.')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just deleted now

=======
if (isAdminId(user.id) && contract.token === 'CASH' && isProd()) {
throw new APIError(403, 'Admins cannot trade on sweepstakes markets.')
}
if (BANNED_TRADING_USER_IDS.includes(user.id) || user.userDeleted) {
throw new APIError(403, 'You are banned or deleted. And not #blessed.')
>>>>>>> main
}
log(
`Loaded user ${user.username} with id ${user.id} betting on slug ${contract.slug} with contract id: ${contract.id}.`
Expand All @@ -322,7 +339,12 @@ export const fetchContractBetDataAndValidate = async (
log(
`Loaded user ${user.username} with id ${user.id} betting on slug ${contract.slug} with contract id: ${contract.id}.`
)

if (user.isBannedFromMana && contract.token !== 'CASH') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might as well check for the mana token

throw new APIError(403, 'You are banned from trading mana (or deleted).')
}
log(
`Loaded user ${user.username} with id ${user.id} betting on slug ${contract.slug} with contract id: ${contract.id}.`
)
return {
user,
contract,
Expand Down
2 changes: 2 additions & 0 deletions common/src/api/user-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export type DisplayUser = {
username: string
avatarUrl: string
isBannedFromPosting?: boolean
isBannedFromMana?: boolean
isBannedFromSweepcash?: boolean
}

export type FullUser = User & {
Expand Down
7 changes: 0 additions & 7 deletions common/src/envs/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,6 @@ export const VERIFIED_USERNAMES = [
'DanHendrycks',
]

export const BANNED_TRADING_USER_IDS = [
'zgCIqq8AmRUYVu6AdQ9vVEJN8On1', //firstuserhere aka _deleted_
'LIBAoi7tpqeNLYM1xxJ1QJBQqW32', //lastuserhere
'p3ADzwIUS3fk0ka80XYEE3OM3S32', //PC
'4JuXgDx47xPagH5mcLDqLzUSN5g2', // BTE
]

export const PARTNER_USER_IDS: string[] = [
'sTUV8ejuM2byukNZp7qKP2OKXMx2', // NFL
'rFJu0EIdR6RP8d1vHKSh62pbnbH2', // SimonGrayson
Expand Down
2 changes: 2 additions & 0 deletions common/src/mod-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ export type ModReport = {
owner_username: string
owner_avatar_url: string
owner_is_banned_from_posting: boolean
owner_is_banned_from_mana: boolean
owner_is_banned_from_sweepcash: boolean
owner_name: string
}
2 changes: 2 additions & 0 deletions common/src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export type User = {
hasSeenLoanModal?: boolean
hasSeenContractFollowModal?: boolean
isBannedFromPosting?: boolean
isBannedFromMana?: boolean
isBannedFromSweepcash?: boolean
userDeleted?: boolean
optOutBetWarnings?: boolean
freeQuestionsCreated?: number
Expand Down
11 changes: 4 additions & 7 deletions web/components/auth-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import { createUser } from 'web/lib/api/api'
import { randomString } from 'common/util/random'
import { identifyUser, setUserProperty } from 'web/lib/service/analytics'
import { useStateCheckEquality } from 'web/hooks/use-state-check-equality'
import {
AUTH_COOKIE_NAME,
BANNED_TRADING_USER_IDS,
TEN_YEARS_SECS,
} from 'common/envs/constants'
import { AUTH_COOKIE_NAME, TEN_YEARS_SECS } from 'common/envs/constants'
import { getCookie, setCookie } from 'web/lib/util/cookie'
import {
type PrivateUser,
Expand Down Expand Up @@ -114,12 +110,13 @@ export function AuthProvider(props: {
useEffect(() => {
if (authUser) {
if (
BANNED_TRADING_USER_IDS.includes(authUser.user.id) ||
(authUser.user.isBannedFromPosting &&
authUser.user.isBannedFromMana) && authUser.user.isBannedFromSweepcash ||
authUser.user.userDeleted
) {
const message = authUser.user.userDeleted
? 'You have deleted the account associated with this email. To restore your account please email [email protected]'
: 'You are banned from trading. To learn more please email [email protected]'
: 'You are banned from Manifold. To learn more please email [email protected]'

firebaseLogout().then(() => {
alert(message)
Expand Down
43 changes: 39 additions & 4 deletions web/components/buttons/user-settings-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import {
Referrals,
useReferralCount,
} from 'web/components/buttons/referrals-button'
import { banUser } from 'web/lib/api/api'
import {
banUserFromPosting,
banUserFromMana,
banUserFromSweepcash,
} from 'web/lib/api/api'
import SuperBanControl from '../SuperBanControl'
import { buildArray } from 'common/util/array'
import { AccountSettings } from '../profile/settings'
Expand Down Expand Up @@ -80,19 +84,50 @@ export function UserSettingButton(props: { user: User }) {
<div className="mb-2 flex flex-wrap justify-between">
<Title className={'!mb-0'}>{name}</Title>
{(isAdmin || isTrusted) && (
<Row className="gap-2">
<Row className="mt-2 gap-2">
<SuperBanControl userId={userId} />
<Button
color={'red'}
size="xs"
onClick={() => {
banUser({
banUserFromPosting({
userId,
unban: user.isBannedFromPosting ?? false,
})
}}
>
{user.isBannedFromPosting ? 'Banned' : 'Ban User'}
{user.isBannedFromPosting
? 'Banned (posting)'
: 'Ban Posting'}
</Button>

<Button
color={'red'}
size="xs"
onClick={() => {
banUserFromMana({
userId,
unban: user.isBannedFromMana ?? false,
})
}}
>
{user.isBannedFromMana
? 'Banned (mana)'
: 'Ban Mana Trading'}
</Button>
<Button
color={'red'}
size="xs"
onClick={() => {
banUserFromSweepcash({
userId,
unban: user.isBannedFromSweepcash ?? false,
})
}}
>
{user.isBannedFromSweepcash
? 'Banned (sweepcash)'
: 'Ban Sweepcash Trading'}
</Button>
</Row>
)}
Expand Down
12 changes: 11 additions & 1 deletion web/components/mod-report-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,18 @@ const ModReportItem: React.FC<ReportItemProps> = ({
avatarUrl={report.owner_avatar_url || ''}
size="sm"
/>

<UserLink user={owner} className="text-ink-800 ml-2" />
{report.owner_is_banned_from_posting && <BannedBadge />}
{(report.owner_is_banned_from_posting ||
report.owner_is_banned_from_mana) || report.owner_is_banned_from_sweepcash && (
<span className="ml-1.5">
<BannedBadge
isBannedFromPosting={report.owner_is_banned_from_posting}
isBannedFromMana={report.owner_is_banned_from_mana}
isBannedFromSweepcash={report.owner_is_banned_from_sweepcash}
/>
</span>
)}
</div>
</UserHovercard>
<Row className="ml-2">commented:</Row>
Expand Down
8 changes: 7 additions & 1 deletion web/components/profile/blocked-user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ export function BlockedUser(props: { user: User; privateUser: PrivateUser }) {
{user.name}
{' (Blocked) '}
{<UserBadge userId={user.id} username={user.username} />}
{user.isBannedFromPosting && <BannedBadge />}
<span className="ml-1.5">
<BannedBadge
isBannedFromPosting={user.isBannedFromPosting ?? false}
isBannedFromMana={user.isBannedFromMana ?? false}
isBannedFromSweepcash={user.isBannedFromSweepcash ?? false}
/>
</span>
</span>
<Row className="sm:text-md items-center gap-x-3 text-sm ">
<span className={' text-ink-400'}>@{user.username}</span>
Expand Down
Loading
Loading