├── .npmrc ├── apps ├── api │ ├── .prettierignore │ ├── eslint-local-rules.js │ ├── app │ │ ├── api │ │ │ ├── route.ts │ │ │ └── v1 │ │ │ │ ├── users │ │ │ │ ├── me │ │ │ │ │ ├── balance │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── referrals │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── resource-viewed │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── api-keys │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── notifications │ │ │ │ │ │ └── schema.ts │ │ │ │ │ └── schema.ts │ │ │ │ ├── [id] │ │ │ │ │ ├── schema.ts │ │ │ │ │ ├── balance │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── graph │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── transactions │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── positions │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── stats │ │ │ │ │ │ └── schema.ts │ │ │ │ │ └── route.ts │ │ │ │ ├── referral │ │ │ │ │ └── [code] │ │ │ │ │ │ └── schema.ts │ │ │ │ ├── username │ │ │ │ │ └── [username] │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── route.ts │ │ │ │ └── check-username │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── route.ts │ │ │ │ ├── activity │ │ │ │ ├── schema.ts │ │ │ │ └── route.ts │ │ │ │ ├── markets │ │ │ │ ├── [id] │ │ │ │ │ ├── related │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── comments │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── cancel │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── activity │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── buy │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── sell │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── resolve │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── balances │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── liquidity │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── options │ │ │ │ │ │ └── [optionId] │ │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── balance │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── quote │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── graph │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── positions │ │ │ │ │ │ └── schema.ts │ │ │ │ │ └── schema.ts │ │ │ │ └── generate-tags │ │ │ │ │ └── schema.ts │ │ │ │ ├── lists │ │ │ │ ├── [id] │ │ │ │ │ ├── comments │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── balance │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── graph │ │ │ │ │ │ └── schema.ts │ │ │ │ │ ├── markets │ │ │ │ │ │ └── schema.ts │ │ │ │ │ └── schema.ts │ │ │ │ └── schema.ts │ │ │ │ ├── comments │ │ │ │ ├── schema.ts │ │ │ │ ├── [id] │ │ │ │ │ └── reaction │ │ │ │ │ │ └── schema.ts │ │ │ │ └── route.ts │ │ │ │ ├── transparency │ │ │ │ └── stats │ │ │ │ │ └── users │ │ │ │ │ ├── schema.ts │ │ │ │ │ └── route.ts │ │ │ │ └── search │ │ │ │ ├── schema.ts │ │ │ │ └── route.ts │ │ ├── page.tsx │ │ ├── layout.tsx │ │ └── SwaggerUI.tsx │ ├── next-env.d.ts │ ├── tsconfig.json │ ├── .eslintrc.js │ ├── README.md │ ├── .gitignore │ └── next.config.js ├── storybook │ ├── src │ │ └── index.ts │ ├── README.md │ ├── postcss.config.js │ ├── .eslintrc.json │ ├── turbo.json │ ├── tsconfig.json │ ├── tailwind.config.ts │ ├── .storybook │ │ ├── modes.ts │ │ └── main.ts │ └── .gitignore └── web │ ├── app │ ├── globals.css │ ├── favicon.ico │ ├── api │ │ └── auth │ │ │ └── [...nextauth] │ │ │ └── route.ts │ ├── (app) │ │ ├── [username] │ │ │ ├── layout.tsx │ │ │ ├── error.tsx │ │ │ └── page.tsx │ │ ├── transparency │ │ │ ├── page.tsx │ │ │ ├── stats │ │ │ │ └── page.tsx │ │ │ └── layout.tsx │ │ ├── not-found.tsx │ │ ├── create-post │ │ │ └── page.tsx │ │ ├── error.tsx │ │ ├── questions │ │ │ ├── [marketId] │ │ │ │ ├── [slug] │ │ │ │ │ ├── comments │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── positions │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── liquidity │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── trades │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── page.tsx │ │ │ │ └── layout.tsx │ │ │ ├── tagged │ │ │ │ └── [tag] │ │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── settings │ │ │ ├── referrals │ │ │ │ └── page.tsx │ │ │ ├── api │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── me │ │ │ └── page.tsx │ │ └── lists │ │ │ └── [listId] │ │ │ ├── [listSlug] │ │ │ └── page.tsx │ │ │ └── layout.tsx │ ├── login │ │ └── page.tsx │ └── check-email │ │ └── page.tsx │ ├── eslint-local-rules.js │ ├── .eslintrc.js │ ├── next-env.d.ts │ ├── postcss.config.js │ ├── tsconfig.json │ ├── public │ └── images │ │ ├── no-shares.svg │ │ ├── yes-shares.svg │ │ ├── dollars.svg │ │ └── lp-bonuses.svg │ ├── tailwind.config.ts │ ├── README.md │ ├── next.config.js │ └── .gitignore ├── .prettierignore ├── jest.config.js ├── packages ├── auth │ ├── index.ts │ ├── components │ │ ├── SessionProvider.tsx │ │ └── CheckEmailPage.tsx │ ├── tsconfig.json │ ├── lib │ │ ├── exceptions.ts │ │ └── getAuthUser.ts │ └── package.json ├── finance │ ├── jest.config.ts │ ├── tsconfig.json │ ├── lib │ │ ├── getHouseAccount.ts │ │ ├── formatCurrency.ts │ │ ├── createHouseUserGiftTransaction.ts │ │ └── createHouseSingupBonusTransaction.ts │ ├── package.json │ ├── economy.ts │ ├── components │ │ └── CurrencyDisplay.tsx │ └── types.ts ├── markets │ ├── jest.config.ts │ ├── tsconfig.json │ ├── lib │ │ ├── getMarketOption.ts │ │ ├── getMarketAmmAccount.ts │ │ ├── getMarketClearingAccount.ts │ │ ├── getMarketTransactions.ts │ │ ├── updateMarketOption.ts │ │ ├── getCommentsOnMarket.ts │ │ ├── getUniqueTraderIds.tsx │ │ ├── getUniqueLiquidityProviderIds.tsx │ │ ├── getRelatedMarkets.ts │ │ ├── marketSell.ts │ │ ├── getMarket.ts │ │ └── updateMarketOptionProbabilities.ts │ ├── components │ │ ├── LiquidityBoostAlert.stories.ts │ │ ├── EditMarketDialog.stories.ts │ │ ├── CreateMarketForm.stories.ts │ │ ├── MarketComments.tsx │ │ ├── LiquidityBoostDialog.stories.tsx │ │ ├── MarketPageSidebar.tsx │ │ └── MarketTradesPage.stories.tsx │ └── package.json ├── database │ ├── zod │ │ ├── index.ts │ │ ├── inputTypeSchemas │ │ │ ├── SortOrderSchema.ts │ │ │ ├── NullsOrderSchema.ts │ │ │ ├── QueryModeSchema.ts │ │ │ ├── MarketListScalarFieldEnumSchema.ts │ │ │ ├── UserRoleSchema.ts │ │ │ ├── TransactionBatchScalarFieldEnumSchema.ts │ │ │ ├── CommentReactionScalarFieldEnumSchema.ts │ │ │ ├── JsonNullValueInputSchema.ts │ │ │ ├── SessionScalarFieldEnumSchema.ts │ │ │ ├── VerificationTokenScalarFieldEnumSchema.ts │ │ │ ├── AssetTypeSchema.ts │ │ │ ├── AccountScalarFieldEnumSchema.ts │ │ │ ├── TransactionIsolationLevelSchema.ts │ │ │ ├── ApiKeyScalarFieldEnumSchema.ts │ │ │ ├── AccountTypeSchema.ts │ │ │ ├── CommentEntityTypeSchema.ts │ │ │ ├── BalanceScalarFieldEnumSchema.ts │ │ │ ├── CommentScalarFieldEnumSchema.ts │ │ │ ├── ListScalarFieldEnumSchema.ts │ │ │ ├── MarketOptionScalarFieldEnumSchema.ts │ │ │ ├── TransactionScalarFieldEnumSchema.ts │ │ │ ├── MarketResolutionScalarFieldEnumSchema.ts │ │ │ ├── TransactionEntryScalarFieldEnumSchema.ts │ │ │ ├── MarketOptionPositionScalarFieldEnumSchema.ts │ │ │ ├── NotificationGroupScalarFieldEnumSchema.ts │ │ │ ├── JsonNullValueFilterSchema.ts │ │ │ ├── QuestionContributionPolicySchema.ts │ │ │ ├── AuthAccountScalarFieldEnumSchema.ts │ │ │ ├── UserScalarFieldEnumSchema.ts │ │ │ ├── DecimalJsLikeSchema.ts │ │ │ ├── NotificationScalarFieldEnumSchema.ts │ │ │ ├── MarketScalarFieldEnumSchema.ts │ │ │ ├── NotificationTypeSchema.ts │ │ │ ├── JsonValueSchema.ts │ │ │ ├── TransactionTypeSchema.ts │ │ │ ├── InputJsonValueSchema.ts │ │ │ └── isValidDecimalInput.ts │ │ └── modelSchema │ │ │ ├── MarketListSchema.ts │ │ │ ├── VerificationTokenSchema.ts │ │ │ ├── TransactionBatchSchema.ts │ │ │ ├── CommentReactionSchema.ts │ │ │ ├── SessionSchema.ts │ │ │ ├── MarketResolutionSchema.ts │ │ │ ├── AccountSchema.ts │ │ │ ├── ApiKeySchema.ts │ │ │ ├── CommentSchema.ts │ │ │ ├── TransactionSchema.ts │ │ │ ├── NotificationGroupSchema.ts │ │ │ ├── AuthAccountSchema.ts │ │ │ ├── index.ts │ │ │ ├── MarketOptionSchema.ts │ │ │ ├── TransactionEntrySchema.ts │ │ │ ├── ListSchema.ts │ │ │ ├── BalanceSchema.ts │ │ │ ├── UserSchema.ts │ │ │ └── MarketOptionPositionSchema.ts │ ├── migrations │ │ ├── 20240829035918_market_tags │ │ │ └── migration.sql │ │ ├── 20240524035000_add_comments_and_remove_unique_market_slug │ │ │ └── migration.sql │ │ ├── 20240921220907_lists_with_tags │ │ │ └── migration.sql │ │ ├── 20241015225437_cancel_market_reverse_id │ │ │ └── migration.sql │ │ ├── 20241016032104_cancel_market_notification │ │ │ └── migration.sql │ │ ├── 20240826063244_financial_rewrite_remove_unique │ │ │ └── migration.sql │ │ ├── 20240829064844_market_tags_default │ │ │ └── migration.sql │ │ ├── 20240801182408_user_timezone │ │ │ └── migration.sql │ │ ├── migration_lock.toml │ │ ├── 20240711035540_add_liquidity_probability │ │ │ └── migration.sql │ │ ├── 20240926180740_add_user_roles │ │ │ └── migration.sql │ │ ├── 20241015005118_cancel_market │ │ │ └── migration.sql │ │ ├── 20240921200741_lists_with_slug │ │ │ └── migration.sql │ │ ├── 20240710023422_add_option_color │ │ │ └── migration.sql │ │ ├── 20241015232913_cancel_market_cancled_by │ │ │ └── migration.sql │ │ ├── 20240925071947_lists_comments_notification_relation │ │ │ └── migration.sql │ │ ├── 20240519072151_patch_comments │ │ │ └── migration.sql │ │ ├── 20240617180750_switch_to_decimals │ │ │ └── migration.sql │ │ ├── 20240904184941_cache_common_market_values │ │ │ └── migration.sql │ │ ├── 20240916222328_fix_updated_at │ │ │ └── migration.sql │ │ ├── 20240603161723_add_account_internal_type │ │ │ └── migration.sql │ │ ├── 20240530052110_remove_currency_entity │ │ │ └── migration.sql │ │ ├── 20240930235440_referrals_transaction │ │ │ └── migration.sql │ │ ├── 20240930070407_referrals │ │ │ └── migration.sql │ │ ├── 20240529203430_add_currency_table │ │ │ └── migration.sql │ │ ├── 20240518231024_add_markets │ │ │ └── migration.sql │ │ ├── 20240618021418_add_market_options │ │ │ └── migration.sql │ │ ├── 20240925071432_lists_comments_fix │ │ │ └── migration.sql │ │ ├── 20241128223707_api_keys │ │ │ └── migration.sql │ │ └── 20240826044714_remove_currency │ │ │ └── migration.sql │ ├── tsconfig.json │ ├── index.ts │ ├── enums │ │ └── index.ts │ ├── scripts │ │ ├── run.js │ │ ├── delete-old-transactions.ts │ │ ├── backfill-referral-codes.ts │ │ └── send-user-gift.ts │ ├── prisma.ts │ └── package.json ├── config │ ├── eslint │ │ └── README.md │ ├── tailwind │ │ └── tsconfig.json │ ├── typescript │ │ ├── global.d.ts │ │ ├── react-library.json │ │ ├── nextjs.json │ │ └── base.json │ ├── prettier │ │ └── prettier.config.js │ ├── jest │ │ ├── jest.config.js │ │ └── dbMock.ts │ └── package.json ├── users │ ├── rules.ts │ ├── tsconfig.json │ ├── package.json │ ├── lib │ │ ├── getUserById.ts │ │ ├── exceptions.ts │ │ ├── checkUsername.ts │ │ ├── getUserByUsername.ts │ │ ├── getUserByReferralCode.ts │ │ ├── getMarketOptionPosition.ts │ │ └── getUserPrimaryAccount.ts │ ├── components │ │ ├── UserProfileTabs.tsx │ │ ├── ActiveUserBalance.tsx │ │ └── UserLink.tsx │ └── context │ │ └── UserContext.tsx ├── tsconfig.json ├── api-helpers │ ├── tsconfig.json │ ├── lib │ │ ├── formatZodError.ts │ │ ├── ServerError.ts │ │ └── helpers.ts │ ├── index.ts │ ├── package.json │ ├── components │ │ └── SWRProvider.tsx │ └── types.ts ├── comments │ ├── tsconfig.json │ ├── lib │ │ ├── deleteComment.ts │ │ ├── exceptions.ts │ │ ├── updateComment.ts │ │ ├── createCommentReply.ts │ │ ├── getComment.ts │ │ └── flattenReplies.ts │ ├── package.json │ └── components │ │ └── CommentItem.stories.tsx ├── search │ ├── tsconfig.json │ ├── package.json │ └── components │ │ └── GlobalSearchTriggerLink.tsx ├── ui │ ├── src │ │ ├── lib │ │ │ └── utils.ts │ │ ├── index.ts │ │ ├── components │ │ │ ├── ui │ │ │ │ ├── collapsible.tsx │ │ │ │ ├── label.tsx │ │ │ │ ├── separator.tsx │ │ │ │ ├── textarea.tsx │ │ │ │ ├── toaster.tsx │ │ │ │ ├── input.tsx │ │ │ │ └── progress.tsx │ │ │ └── ThemeProvider.tsx │ │ ├── helpers.ts │ │ ├── contexts │ │ │ └── EditorExtensionsContext.tsx │ │ └── hooks │ │ │ └── useSearchParam.ts │ ├── postcss.config.js │ ├── tsconfig.json │ ├── tailwind.config.ts │ ├── .eslintrc.js │ └── components.json ├── transparency │ ├── components │ │ └── TransparencyStatsPage.tsx │ └── package.json ├── referrals │ ├── lib │ │ ├── getUserReferrals.ts │ │ └── helpers.ts │ ├── package.json │ └── components │ │ └── ReferralQuestBonusRow.tsx ├── lists │ ├── types.ts │ ├── package.json │ ├── lib │ │ ├── helpers.ts │ │ ├── getCommentsOnList.ts │ │ └── getLists.ts │ ├── rules.ts │ └── components │ │ └── ListComments.tsx ├── quests │ ├── package.json │ └── components │ │ └── UserQuestCard.tsx └── notifications │ ├── lib │ ├── getUnreadNotificationCount.ts │ └── updateNotificationsRead.ts │ ├── package.json │ ├── components │ └── NotificationItem.stories.tsx │ └── hooks │ └── useTrackResourceViewed.ts ├── prettier.config.js ├── .vscode └── settings.json ├── .gitignore ├── .github └── workflows │ └── ui-tests.yml └── .env.example /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | -------------------------------------------------------------------------------- /apps/api/.prettierignore: -------------------------------------------------------------------------------- 1 | openapi.json -------------------------------------------------------------------------------- /apps/storybook/src/index.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | packages/database/zod 2 | apps/api/openapi.json -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./packages/config/jest/jest.config') 2 | -------------------------------------------------------------------------------- /packages/auth/index.ts: -------------------------------------------------------------------------------- 1 | export { handlers, signIn, signOut, auth } from './lib/auth' 2 | -------------------------------------------------------------------------------- /packages/finance/jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('../config/jest/jest.config') 2 | -------------------------------------------------------------------------------- /packages/markets/jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = require('../config/jest/jest.config') 2 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./packages/config/prettier/prettier.config') 2 | -------------------------------------------------------------------------------- /apps/web/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/api/eslint-local-rules.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@play-money/config/eslint/eslint-local-rules') 2 | -------------------------------------------------------------------------------- /apps/web/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casesandberg/play-money/HEAD/apps/web/app/favicon.ico -------------------------------------------------------------------------------- /apps/web/eslint-local-rules.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@play-money/config/eslint/eslint-local-rules') 2 | -------------------------------------------------------------------------------- /packages/database/zod/index.ts: -------------------------------------------------------------------------------- 1 | export * from './modelSchema'; 2 | export * from './inputTypeSchemas'; 3 | -------------------------------------------------------------------------------- /packages/config/eslint/README.md: -------------------------------------------------------------------------------- 1 | # `@turbo/eslint-config` 2 | 3 | Collection of internal eslint configurations. 4 | -------------------------------------------------------------------------------- /apps/storybook/README.md: -------------------------------------------------------------------------------- 1 | # Play Money Storybook 2 | 3 | - Access Storybook at [localhost:6006](http://localhost:6006). 4 | -------------------------------------------------------------------------------- /apps/storybook/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/storybook/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next/core-web-vitals", 4 | "plugin:storybook/recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { handlers } from '@play-money/auth' 2 | 3 | export const { GET, POST } = handlers 4 | -------------------------------------------------------------------------------- /packages/database/migrations/20240829035918_market_tags/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Market" ADD COLUMN "tags" TEXT[]; 3 | -------------------------------------------------------------------------------- /packages/database/migrations/20240524035000_add_comments_and_remove_unique_market_slug/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "Market_slug_key"; 3 | -------------------------------------------------------------------------------- /packages/auth/components/SessionProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { SessionProvider } from 'next-auth/react' 4 | 5 | export { SessionProvider } 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240921220907_lists_with_tags/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "List" ADD COLUMN "tags" TEXT[] DEFAULT ARRAY[]::TEXT[]; 3 | -------------------------------------------------------------------------------- /packages/database/migrations/20241015225437_cancel_market_reverse_id/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Transaction" ADD COLUMN "reverseOfId" TEXT; 3 | -------------------------------------------------------------------------------- /packages/database/migrations/20241016032104_cancel_market_notification/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "NotificationType" ADD VALUE 'MARKET_CANCELED'; 3 | -------------------------------------------------------------------------------- /apps/web/app/(app)/[username]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { UserProfileLayout } from '@play-money/users/components/UserProfileLayout' 2 | 3 | export default UserProfileLayout 4 | -------------------------------------------------------------------------------- /packages/config/tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../typescript/base.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240826063244_financial_rewrite_remove_unique/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropIndex 2 | DROP INDEX "Balance_accountId_assetType_assetId_marketId_key"; 3 | -------------------------------------------------------------------------------- /packages/database/migrations/20240829064844_market_tags_default/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Market" ALTER COLUMN "tags" SET DEFAULT ARRAY[]::TEXT[]; 3 | -------------------------------------------------------------------------------- /packages/users/rules.ts: -------------------------------------------------------------------------------- 1 | import { User } from '@play-money/database' 2 | 3 | export function isAdmin({ user }: { user: User }) { 4 | return user.role === 'ADMIN' 5 | } 6 | -------------------------------------------------------------------------------- /packages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/app/login/page.tsx: -------------------------------------------------------------------------------- 1 | import { LoginPage } from '@play-money/auth/components/LoginPage' 2 | 3 | export default function AppLoginPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /packages/api-helpers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/nextjs.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/comments/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/nextjs.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240801182408_user_timezone/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "timezone" TEXT NOT NULL DEFAULT 'America/Los_Angeles'; 3 | -------------------------------------------------------------------------------- /packages/database/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /apps/web/app/(app)/transparency/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation' 2 | 3 | export default function AppTransparencyPage() { 4 | redirect('/transparency/stats') 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/markets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/search/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/users/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /apps/api/app/api/route.ts: -------------------------------------------------------------------------------- 1 | export const dynamic = 'force-dynamic' 2 | 3 | export function GET(_request: Request): Response { 4 | return new Response('Welcome to Play Money API') 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/SortOrderSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const SortOrderSchema = z.enum(['asc','desc']); 4 | 5 | export default SortOrderSchema; 6 | -------------------------------------------------------------------------------- /packages/database/index.ts: -------------------------------------------------------------------------------- 1 | import db, { TransactionClient } from './prisma' 2 | 3 | export type { TransactionClient } 4 | export default db 5 | export * from './enums' 6 | export * from './zod' 7 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/NullsOrderSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const NullsOrderSchema = z.enum(['first','last']); 4 | 5 | export default NullsOrderSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/QueryModeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const QueryModeSchema = z.enum(['default','insensitive']); 4 | 5 | export default QueryModeSchema; 6 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['../../packages/config/eslint/next.js'], 3 | rules: { 4 | 'no-nested-ternary': 'off', 5 | 'unicorn/filename-case': 'off', 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/app/check-email/page.tsx: -------------------------------------------------------------------------------- 1 | import { CheckEmailPage } from '@play-money/auth/components/CheckEmailPage' 2 | 3 | export default function AppCheckEmailPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240711035540_add_liquidity_probability/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MarketOption" ADD COLUMN "liquidityProbability" DECIMAL(65,30) NOT NULL DEFAULT 0.5; 3 | -------------------------------------------------------------------------------- /packages/finance/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": [".", "../config/typescript/*.d.ts"], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/ui/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from 'clsx' 2 | import { twMerge } from 'tailwind-merge' 3 | 4 | export function cn(...inputs: Array) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /packages/auth/lib/exceptions.ts: -------------------------------------------------------------------------------- 1 | export class UserExistsError extends Error { 2 | constructor(message = 'User with that email already exists') { 3 | super(message) 4 | this.name = 'UserExistsError' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /apps/storybook/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["//"], 3 | "pipeline": { 4 | "build:storybook": { 5 | "dependsOn": ["^build:storybook"], 6 | "outputs": ["storybook-static/**"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { SwaggerUI } from './SwaggerUI' 2 | 3 | export default function IndexPage() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /packages/api-helpers/lib/formatZodError.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod' 2 | import { fromError } from 'zod-validation-error' 3 | 4 | export function formatZodError(error: z.ZodError): string { 5 | return fromError(error).toString() 6 | } 7 | -------------------------------------------------------------------------------- /apps/api/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/api-helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/createSchema' 2 | export * from './lib/formatZodError' 3 | export * from './lib/ServerError' 4 | export * from './lib/helpers' 5 | export * from './lib/pagination' 6 | export * from './types' 7 | -------------------------------------------------------------------------------- /packages/database/migrations/20240926180740_add_user_roles/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "UserRole" AS ENUM ('USER', 'ADMIN'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "role" "UserRole" NOT NULL DEFAULT 'USER'; 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20241015005118_cancel_market/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Market" ADD COLUMN "canceledAt" TIMESTAMP(3); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Transaction" ADD COLUMN "isReverse" BOOLEAN; 6 | -------------------------------------------------------------------------------- /packages/comments/lib/deleteComment.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function deleteComment({ id }: { id: string }) { 4 | await db.comment.delete({ 5 | where: { id }, 6 | }) 7 | 8 | return 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/app/(app)/transparency/stats/page.tsx: -------------------------------------------------------------------------------- 1 | import { TransparencyStatsPage } from '@play-money/transparency/components/TransparencyStatsPage' 2 | 3 | export default function AppTransparencyStatsPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/MarketListScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const MarketListScalarFieldEnumSchema = z.enum(['id','listId','marketId','createdAt']); 4 | 5 | export default MarketListScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/UserRoleSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const UserRoleSchema = z.enum(['USER','ADMIN']); 4 | 5 | export type UserRoleType = `${z.infer}` 6 | 7 | export default UserRoleSchema; 8 | -------------------------------------------------------------------------------- /packages/transparency/components/TransparencyStatsPage.tsx: -------------------------------------------------------------------------------- 1 | import { UserStatsGraph } from './UserStatsGraph' 2 | 3 | export function TransparencyStatsPage() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/app/(app)/not-found.tsx: -------------------------------------------------------------------------------- 1 | export default function NotFound() { 2 | return ( 3 |
4 |

404 Not Found

5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /packages/comments/lib/exceptions.ts: -------------------------------------------------------------------------------- 1 | export class CommentNotFoundError extends Error { 2 | static code = 'COMMENT_NOT_FOUND' 3 | 4 | constructor(message = 'Comment not found') { 5 | super(message) 6 | this.name = 'CommentNotFoundError' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/TransactionBatchScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const TransactionBatchScalarFieldEnumSchema = z.enum(['id','createdAt','updatedAt']); 4 | 5 | export default TransactionBatchScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | 4 | module.exports = { 5 | plugins: { 6 | tailwindcss: {}, 7 | autoprefixer: {}, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/CommentReactionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const CommentReactionScalarFieldEnumSchema = z.enum(['id','emoji','userId','commentId']); 4 | 5 | export default CommentReactionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/JsonNullValueInputSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { Prisma } from '@prisma/client'; 3 | 4 | export const JsonNullValueInputSchema = z.enum(['JsonNull',]).transform((value) => (value === 'JsonNull' ? Prisma.JsonNull : value)); -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/SessionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const SessionScalarFieldEnumSchema = z.enum(['sessionToken','userId','expires','createdAt','updatedAt']); 4 | 5 | export default SessionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/VerificationTokenScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const VerificationTokenScalarFieldEnumSchema = z.enum(['identifier','token','expires']); 4 | 5 | export default VerificationTokenScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/nextjs.json", 3 | "compilerOptions": { 4 | "plugins": [{ "name": "next" }] 5 | }, 6 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 7 | "exclude": ["node_modules", ".next"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/AssetTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const AssetTypeSchema = z.enum(['CURRENCY','MARKET_OPTION']); 4 | 5 | export type AssetTypeType = `${z.infer}` 6 | 7 | export default AssetTypeSchema; 8 | -------------------------------------------------------------------------------- /packages/referrals/lib/getUserReferrals.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getUserReferrals({ userId }: { userId: string }) { 4 | return db.user.findMany({ 5 | where: { 6 | referredBy: userId, 7 | }, 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | 4 | module.exports = { 5 | plugins: { 6 | tailwindcss: {}, 7 | autoprefixer: {}, 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/AccountScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const AccountScalarFieldEnumSchema = z.enum(['id','type','internalType','userId','marketId','createdAt','updatedAt']); 4 | 5 | export default AccountScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/TransactionIsolationLevelSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const TransactionIsolationLevelSchema = z.enum(['ReadUncommitted','ReadCommitted','RepeatableRead','Serializable']); 4 | 5 | export default TransactionIsolationLevelSchema; 6 | -------------------------------------------------------------------------------- /packages/ui/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './contexts/EditorExtensionsContext' 2 | export * from './contexts/SelectedItemContext' 3 | export * from './hooks/useLocalStorage' 4 | export * from './hooks/usePersistForm' 5 | export * from './hooks/useSearchParam' 6 | export * from './helpers' 7 | -------------------------------------------------------------------------------- /apps/api/app/layout.tsx: -------------------------------------------------------------------------------- 1 | export const metadata = { 2 | title: 'PlayMoney API', 3 | } 4 | 5 | export default function RootLayout({ children }: { children: React.ReactNode }) { 6 | return ( 7 | 8 | {children} 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/nextjs.json", 3 | "compilerOptions": { 4 | "plugins": [{ "name": "next" }] 5 | }, 6 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 7 | "exclude": ["node_modules", ".next"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/nextjs.json", 3 | "compilerOptions": { 4 | "plugins": [{ "name": "next" }] 5 | }, 6 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 7 | "exclude": ["node_modules", ".next"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['../../packages/config/eslint/next.js'], 3 | rules: { 4 | '@typescript-eslint/consistent-type-imports': 'off', 5 | 'unicorn/filename-case': 'off', 6 | camelcase: 'off', 7 | 'import/order': 'off', 8 | }, 9 | } 10 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/ApiKeyScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const ApiKeyScalarFieldEnumSchema = z.enum(['id','name','key','userId','lastUsedAt','createdAt','updatedAt','expiresAt','isRevoked']); 4 | 5 | export default ApiKeyScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/web/public/images/no-shares.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/AccountTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const AccountTypeSchema = z.enum(['USER','MARKET_AMM','MARKET_CLEARING','HOUSE']); 4 | 5 | export type AccountTypeType = `${z.infer}` 6 | 7 | export default AccountTypeSchema; 8 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/CommentEntityTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const CommentEntityTypeSchema = z.enum(['MARKET','LIST']); 4 | 5 | export type CommentEntityTypeType = `${z.infer}` 6 | 7 | export default CommentEntityTypeSchema; 8 | -------------------------------------------------------------------------------- /packages/lists/types.ts: -------------------------------------------------------------------------------- 1 | import { List, User } from '@play-money/database' 2 | import { ExtendedMarket } from '@play-money/markets/types' 3 | 4 | export type ExtendedList = List & { 5 | owner: User 6 | markets: Array<{ 7 | createdAt: Date 8 | market: ExtendedMarket 9 | }> 10 | } 11 | -------------------------------------------------------------------------------- /packages/search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/search", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@play-money/database": "*", 10 | "@play-money/users": "*" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/app/(app)/transparency/layout.tsx: -------------------------------------------------------------------------------- 1 | import { TransparencyLayout } from '@play-money/transparency/components/TransparencyLayout' 2 | 3 | export default function AppTransparencyLayout({ children }: { children: React.ReactNode }) { 4 | return {children} 5 | } 6 | -------------------------------------------------------------------------------- /packages/database/enums/index.ts: -------------------------------------------------------------------------------- 1 | // This file was generated by a custom prisma generator, do not edit manually. 2 | export const CommentEntityType = { 3 | MARKET: 'MARKET', 4 | LIST: 'LIST', 5 | } as const 6 | 7 | export type CommentEntityType = (typeof CommentEntityType)[keyof typeof CommentEntityType] 8 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/BalanceScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const BalanceScalarFieldEnumSchema = z.enum(['id','accountId','assetType','assetId','total','subtotals','marketId','createdAt','updatedAt']); 4 | 5 | export default BalanceScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@play-money/config/typescript/react-library.json", 3 | "include": ["."], 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "paths": { 7 | "@/*": ["src/*"] 8 | } 9 | }, 10 | "exclude": ["dist", "build", "node_modules"] 11 | } 12 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/CommentScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const CommentScalarFieldEnumSchema = z.enum(['id','content','createdAt','updatedAt','edited','authorId','parentId','hidden','entityType','entityId']); 4 | 5 | export default CommentScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240921200741_lists_with_slug/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Added the required column `slug` to the `List` table without a default value. This is not possible if the table is not empty. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "List" ADD COLUMN "slug" TEXT NOT NULL; 9 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/ListScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const ListScalarFieldEnumSchema = z.enum(['id','title','slug','description','ownerId','contributionPolicy','contributionReview','tags','createdAt','updatedAt']); 4 | 5 | export default ListScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/MarketOptionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const MarketOptionScalarFieldEnumSchema = z.enum(['id','name','marketId','color','liquidityProbability','createdAt','updatedAt','probability']); 4 | 5 | export default MarketOptionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/TransactionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const TransactionScalarFieldEnumSchema = z.enum(['id','type','initiatorId','isReverse','reverseOfId','createdAt','updatedAt','batchId','marketId']); 4 | 5 | export default TransactionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/ui/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import sharedConfig from '@play-money/config/tailwind/tailwind.config' 3 | 4 | const config = { 5 | content: ['./src/**/*.tsx'], 6 | prefix: '', 7 | presets: [sharedConfig], 8 | } satisfies Config 9 | 10 | export default config 11 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/MarketResolutionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const MarketResolutionScalarFieldEnumSchema = z.enum(['id','marketId','resolvedById','resolutionId','supportingLink','createdAt','updatedAt']); 4 | 5 | export default MarketResolutionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/web/public/images/yes-shares.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/api-helpers/lib/ServerError.ts: -------------------------------------------------------------------------------- 1 | import z from 'zod' 2 | 3 | export class ServerError extends Error { 4 | constructor(message = 'There was an error on the server') { 5 | super(message) 6 | this.name = 'ServerError' 7 | } 8 | } 9 | 10 | export const ServerErrorSchema = z.object({ error: z.string() }) 11 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/TransactionEntryScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const TransactionEntryScalarFieldEnumSchema = z.enum(['id','amount','assetType','assetId','fromAccountId','toAccountId','transactionId','createdAt']); 4 | 5 | export default TransactionEntryScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/database/migrations/20240710023422_add_option_color/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MarketOption" ADD COLUMN "color" TEXT NOT NULL DEFAULT '#FF00FF'; 3 | 4 | UPDATE "MarketOption" SET "color" = '#3B82F6' WHERE "currencyCode" = 'YES'; 5 | UPDATE "MarketOption" SET "color" = '#EC4899' WHERE "currencyCode" = 'NO'; 6 | -------------------------------------------------------------------------------- /packages/quests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/quests", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@play-money/database": "*", 10 | "@play-money/users": "*", 11 | "date-fns-tz": "^3.1.3" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/api-helpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/api-helpers", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "main": "./index.ts", 9 | "types": "./index.ts", 10 | "dependencies": { 11 | "zod-validation-error": "^3.2.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/database/migrations/20241015232913_cancel_market_cancled_by/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Market" ADD COLUMN "canceledById" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Market" ADD CONSTRAINT "Market_canceledById_fkey" FOREIGN KEY ("canceledById") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/MarketOptionPositionScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const MarketOptionPositionScalarFieldEnumSchema = z.enum(['id','accountId','marketId','optionId','cost','quantity','value','createdAt','updatedAt']); 4 | 5 | export default MarketOptionPositionScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/storybook/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | import sharedConfig from '@play-money/config/tailwind/tailwind.config' 3 | 4 | const config: Pick = { 5 | content: ['./app/**/*.tsx', '../../packages/**/*.tsx'], 6 | presets: [sharedConfig], 7 | } 8 | 9 | export default config 10 | -------------------------------------------------------------------------------- /packages/database/migrations/20240925071947_lists_comments_notification_relation/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Notification" ADD COLUMN "listId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Notification" ADD CONSTRAINT "Notification_listId_fkey" FOREIGN KEY ("listId") REFERENCES "List"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/NotificationGroupScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const NotificationGroupScalarFieldEnumSchema = z.enum(['id','recipientId','type','count','lastNotificationId','groupWindowEnd','groupKey','createdAt','updatedAt']); 4 | 5 | export default NotificationGroupScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /apps/web/public/images/dollars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apps/web/public/images/lp-bonuses.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/config/typescript/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace jest { 3 | interface Expect { 4 | closeToDecimal(expected: string | number, precision?: string | number): CustomMatcherResult 5 | } 6 | interface Matchers { 7 | toBeCloseToDecimal(expected: Decimal.Value, precision?: Decimal.Value): R 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/database/migrations/20240519072151_patch_comments/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "CommentReaction" DROP CONSTRAINT "CommentReaction_commentId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "CommentReaction" ADD CONSTRAINT "CommentReaction_commentId_fkey" FOREIGN KEY ("commentId") REFERENCES "Comment"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/JsonNullValueFilterSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { Prisma } from '@prisma/client'; 3 | 4 | export const JsonNullValueFilterSchema = z.enum(['DbNull','JsonNull','AnyNull',]).transform((value) => value === 'JsonNull' ? Prisma.JsonNull : value === 'DbNull' ? Prisma.JsonNull : value === 'AnyNull' ? Prisma.AnyNull : value); -------------------------------------------------------------------------------- /packages/ui/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['../config/eslint/react.js'], 3 | rules: { 4 | 'jsx-a11y/heading-has-content': 'off', 5 | '@typescript-eslint/consistent-type-definitions': 'off', 6 | 'import/no-named-as-default-member': 'off', 7 | 'unicorn/filename-case': 'off', 8 | 'no-nested-ternary': 'off', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/QuestionContributionPolicySchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const QuestionContributionPolicySchema = z.enum(['DISABLED','OWNERS_ONLY','FRIENDS_ONLY','PUBLIC']); 4 | 5 | export type QuestionContributionPolicyType = `${z.infer}` 6 | 7 | export default QuestionContributionPolicySchema; 8 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/AuthAccountScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const AuthAccountScalarFieldEnumSchema = z.enum(['userId','type','provider','providerAccountId','refresh_token','access_token','expires_at','token_type','scope','id_token','session_state','createdAt','updatedAt']); 4 | 5 | export default AuthAccountScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/finance/lib/getHouseAccount.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getHouseAccount() { 4 | const account = await db.account.findUnique({ 5 | where: { 6 | internalType: 'HOUSE', 7 | }, 8 | }) 9 | 10 | if (!account) { 11 | throw new Error('House account does not exist') 12 | } 13 | 14 | return account 15 | } 16 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | // tailwind config is required for editor support 2 | import type { Config } from 'tailwindcss' 3 | import sharedConfig from '@play-money/config/tailwind/tailwind.config' 4 | 5 | const config: Pick = { 6 | content: ['./app/**/*.tsx', '../../packages/**/*.tsx'], 7 | presets: [sharedConfig], 8 | } 9 | 10 | export default config 11 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/UserScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const UserScalarFieldEnumSchema = z.enum(['id','username','displayName','avatarUrl','twitterHandle','discordHandle','website','bio','timezone','primaryAccountId','role','referralCode','referredBy','createdAt','updatedAt','email','emailVerified']); 4 | 5 | export default UserScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/markets/lib/getMarketOption.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getMarketOption({ id, marketId }: { id: string; marketId?: string }) { 4 | const marketOption = await db.marketOption.findUnique({ where: { id, marketId } }) 5 | 6 | if (!marketOption) { 7 | throw new Error('Market option not found') 8 | } 9 | 10 | return marketOption 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | # Play Money Web App 2 | 3 | The **Play Money** web app is a lightweight Next.js application built with React and TailwindCSS. 4 | 5 | ## Features 6 | 7 | - **Lightweight**: Encourages development to occur within feature packages rather than the main app. 8 | - **Authentication**: Handled by NextAuth (soon to be renamed to Auth.js). Configuration is found in the `packages/auth` package. 9 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/DecimalJsLikeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import type { Prisma } from '@prisma/client'; 3 | 4 | export const DecimalJsLikeSchema: z.ZodType = z.object({ 5 | d: z.array(z.number()), 6 | e: z.number(), 7 | s: z.number(), 8 | toFixed: z.function(z.tuple([]), z.string()), 9 | }) 10 | 11 | export default DecimalJsLikeSchema; 12 | -------------------------------------------------------------------------------- /packages/database/migrations/20240617180750_switch_to_decimals/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to alter the column `amount` on the `TransactionItem` table. The data in that column could be lost. The data in that column will be cast from `DoublePrecision` to `Decimal(65,30)`. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "TransactionItem" ALTER COLUMN "amount" SET DATA TYPE DECIMAL(65,30); 9 | -------------------------------------------------------------------------------- /packages/config/prettier/prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | semi: false, 4 | trailingComma: 'es5', 5 | printWidth: 120, 6 | importOrder: ['', '^@(play-money)/(.*)$', '^~/(.*)$', '^[./]'], 7 | plugins: ['@trivago/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss'], 8 | tailwindConfig: './packages/config/tailwind/tailwind.config.ts', 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/src/components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as CollapsiblePrimitive from '@radix-ui/react-collapsible' 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /packages/users/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/users", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@play-money/database": "*", 10 | "lodash": "^4.17.21", 11 | "resend": "^4.0.0", 12 | "zod": "^3.23.5" 13 | }, 14 | "devDependencies": { 15 | "@types/lodash": "^4.17.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/database/migrations/20240904184941_cache_common_market_values/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Market" ADD COLUMN "commentCount" DECIMAL(65,30), 3 | ADD COLUMN "liquidityCount" DECIMAL(65,30), 4 | ADD COLUMN "uniquePromotersCount" DECIMAL(65,30), 5 | ADD COLUMN "uniqueTradersCount" DECIMAL(65,30); 6 | 7 | -- AlterTable 8 | ALTER TABLE "MarketOption" ADD COLUMN "probability" DECIMAL(65,30); 9 | -------------------------------------------------------------------------------- /packages/users/lib/getUserById.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | import { UserNotFoundError } from './exceptions' 3 | 4 | export async function getUserById({ id }: { id: string }) { 5 | const user = await db.user.findUnique({ 6 | where: { 7 | id, 8 | }, 9 | }) 10 | 11 | if (!user) { 12 | throw new UserNotFoundError(`User with id "${id}" not found`) 13 | } 14 | 15 | return user 16 | } 17 | -------------------------------------------------------------------------------- /packages/database/migrations/20240916222328_fix_updated_at/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Account" ALTER COLUMN "updatedAt" DROP DEFAULT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Balance" ALTER COLUMN "updatedAt" DROP DEFAULT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "MarketOptionPosition" ALTER COLUMN "updatedAt" DROP DEFAULT; 9 | 10 | -- AlterTable 11 | ALTER TABLE "Transaction" ALTER COLUMN "updatedAt" DROP DEFAULT; 12 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/NotificationScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const NotificationScalarFieldEnumSchema = z.enum(['id','recipientId','actorId','type','content','marketId','marketOptionId','marketResolutionId','transactionId','listId','commentId','parentCommentId','commentReactionId','actionUrl','readAt','createdAt','updatedAt']); 4 | 5 | export default NotificationScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/markets/lib/getMarketAmmAccount.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getMarketAmmAccount({ marketId }: { marketId: string }) { 4 | const account = await db.account.findFirst({ 5 | where: { 6 | type: 'MARKET_AMM', 7 | marketId, 8 | }, 9 | }) 10 | 11 | if (!account) { 12 | throw new Error('Market amm account does not exist') 13 | } 14 | 15 | return account 16 | } 17 | -------------------------------------------------------------------------------- /packages/notifications/lib/getUnreadNotificationCount.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getUnreadNotificationCount({ userId }: { userId: string }): Promise { 4 | const unreadCount = await db.notificationGroup.count({ 5 | where: { 6 | recipientId: userId, 7 | lastNotification: { 8 | readAt: null, 9 | }, 10 | }, 11 | }) 12 | 13 | return unreadCount 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/styles.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/ui/src/components/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { ThemeProvider as NextThemesProvider, useTheme } from 'next-themes' 4 | import { type ThemeProviderProps } from 'next-themes/dist/types' 5 | import * as React from 'react' 6 | 7 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) { 8 | return {children} 9 | } 10 | 11 | export { useTheme } 12 | -------------------------------------------------------------------------------- /packages/lists/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/lists", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "decimal.js": "^10.4.3", 10 | "next": "^14.1.1", 11 | "react": "^18.2.0", 12 | "react-color": "^2.19.3", 13 | "zod": "^3.23.5" 14 | }, 15 | "devDependencies": { 16 | "@types/react-color": "^3.0.12" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/users/lib/exceptions.ts: -------------------------------------------------------------------------------- 1 | export class UserNotFoundError extends Error { 2 | static code = 'USER_NOT_FOUND' 3 | 4 | constructor(message = 'User not found') { 5 | super(message) 6 | this.name = 'UserNotFoundError' 7 | } 8 | } 9 | 10 | export class UserExistsError extends Error { 11 | constructor(message = 'User with that email already exists') { 12 | super(message) 13 | this.name = 'UserExistsError' 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/auth", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "main": "./index.ts", 9 | "types": "./index.ts", 10 | "dependencies": { 11 | "@auth/prisma-adapter": "^2.0.0", 12 | "@play-money/database": "*", 13 | "lucide-react": "^0.378.0", 14 | "next-auth": "^5.0.0-beta.17", 15 | "zod": "^3.23.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/config/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | setupFilesAfterEnv: ['/../config/jest/dbMock.ts', '/../config/jest/jest-setup.ts'], 6 | transform: { 7 | '^.+\\.tsx?$': [ 8 | 'ts-jest', 9 | { 10 | tsconfig: '../config/typescript/nextjs.json', 11 | }, 12 | ], 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/MarketScalarFieldEnumSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const MarketScalarFieldEnumSchema = z.enum(['id','question','description','slug','closeDate','resolvedAt','canceledAt','canceledById','createdBy','tags','ammAccountId','clearingAccountId','createdAt','updatedAt','commentCount','uniqueTradersCount','uniquePromotersCount','liquidityCount','parentListId']); 4 | 5 | export default MarketScalarFieldEnumSchema; 6 | -------------------------------------------------------------------------------- /packages/config/typescript/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "lib": ["ES2015", "DOM"], 8 | "module": "ESNext", 9 | "target": "ES6", 10 | "jsx": "react-jsx", 11 | "noEmit": true 12 | }, 13 | "include": ["../config/typescript/*.d.ts", "../config/jest/jest-setup.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/markets/lib/getMarketClearingAccount.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getMarketClearingAccount({ marketId }: { marketId: string }) { 4 | const account = await db.account.findFirst({ 5 | where: { 6 | type: 'MARKET_CLEARING', 7 | marketId, 8 | }, 9 | }) 10 | 11 | if (!account) { 12 | throw new Error('Market clearing account does not exist') 13 | } 14 | 15 | return account 16 | } 17 | -------------------------------------------------------------------------------- /packages/database/migrations/20240603161723_add_account_internal_type/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[internalType]` on the table `Account` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "Account" ADD COLUMN "internalType" TEXT; 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "Account_internalType_key" ON "Account"("internalType"); 12 | -------------------------------------------------------------------------------- /packages/notifications/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/notifications", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@play-money/database": "*", 10 | "@play-money/finance": "*", 11 | "decimal.js": "^10.4.3", 12 | "next": "^14.1.1", 13 | "react": "^18.2.0", 14 | "react-error-boundary": "^4.0.13", 15 | "zod": "^3.23.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/users/lib/checkUsername.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function checkUsername({ username }: { username: string }) { 4 | const user = await db.user.findFirst({ 5 | where: { 6 | username: { 7 | equals: username, 8 | mode: 'insensitive', 9 | }, 10 | }, 11 | }) 12 | 13 | return { 14 | available: !user, 15 | message: user ? 'Username is already taken' : undefined, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/NotificationTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const NotificationTypeSchema = z.enum(['MARKET_RESOLVED','MARKET_CANCELED','MARKET_TRADE','MARKET_LIQUIDITY_ADDED','MARKET_COMMENT','LIST_COMMENT','LIST_MARKET_ADDED','COMMENT_REPLY','COMMENT_MENTION','COMMENT_REACTION','REFERRER_BONUS']); 4 | 5 | export type NotificationTypeType = `${z.infer}` 6 | 7 | export default NotificationTypeSchema; 8 | -------------------------------------------------------------------------------- /packages/users/lib/getUserByUsername.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | import { UserNotFoundError } from './exceptions' 3 | 4 | export async function getUserByUsername({ username }: { username: string }) { 5 | const user = await db.user.findUnique({ 6 | where: { 7 | username, 8 | }, 9 | }) 10 | 11 | if (!user) { 12 | throw new UserNotFoundError(`User with username "${username}" not found`) 13 | } 14 | 15 | return user 16 | } 17 | -------------------------------------------------------------------------------- /packages/users/components/UserProfileTabs.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | import { useSearchParam } from '@play-money/ui' 5 | import { Tabs } from '@play-money/ui/tabs' 6 | 7 | export function UserProfileTabs({ children }: { children: React.ReactNode }) { 8 | const [tab, setTab] = useSearchParam('tab') 9 | 10 | return ( 11 | 12 | {children} 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /packages/lists/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { INITIAL_MARKET_LIQUIDITY_PRIMARY, LOWEST_MARKET_LIQUIDITY_PRIMARY } from '@play-money/finance/economy' 2 | 3 | export function calculateTotalCost(numItems: number): number { 4 | let totalCost = 0 5 | 6 | for (let i = 1; i <= numItems; i++) { 7 | const costPerItem = Math.max(INITIAL_MARKET_LIQUIDITY_PRIMARY - (i - 1) * 100, LOWEST_MARKET_LIQUIDITY_PRIMARY) 8 | totalCost += costPerItem 9 | } 10 | 11 | return totalCost 12 | } 13 | -------------------------------------------------------------------------------- /packages/users/lib/getUserByReferralCode.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | import { UserNotFoundError } from './exceptions' 3 | 4 | export async function getUserByReferralCode({ code }: { code: string }) { 5 | const user = await db.user.findUnique({ 6 | where: { 7 | referralCode: code, 8 | }, 9 | }) 10 | 11 | if (!user) { 12 | throw new UserNotFoundError(`User with referral code "${code}" not found`) 13 | } 14 | 15 | return user 16 | } 17 | -------------------------------------------------------------------------------- /packages/markets/components/LiquidityBoostAlert.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import { LiquidityBoostAlert } from './LiquidityBoostAlert' 3 | 4 | const meta = { 5 | component: LiquidityBoostAlert, 6 | tags: ['autodocs'], 7 | } satisfies Meta 8 | 9 | export default meta 10 | type Story = StoryObj 11 | 12 | export const Default: Story = { 13 | args: { 14 | onClick: console.log, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/me/balance/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the balance for the current user', 7 | security: true, 8 | responses: { 9 | 200: z.object({ data: z.object({ balance: z.number() }) }), 10 | 404: ServerErrorSchema, 11 | 500: ServerErrorSchema, 12 | }, 13 | }, 14 | } as const satisfies ApiEndpoints 15 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/MarketListSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // MARKET LIST SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const MarketListSchema = z.object({ 8 | id: z.string().cuid(), 9 | listId: z.string(), 10 | marketId: z.string(), 11 | createdAt: z.coerce.date(), 12 | }) 13 | 14 | export type MarketList = z.infer 15 | 16 | export default MarketListSchema; 17 | -------------------------------------------------------------------------------- /packages/referrals/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/referrals", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "decimal.js": "^10.4.3", 10 | "lucide-react": "^0.378.0", 11 | "next": "^14.1.1", 12 | "react": "^18.2.0", 13 | "react-color": "^2.19.3", 14 | "zod": "^3.23.5" 15 | }, 16 | "devDependencies": { 17 | "@types/react-color": "^3.0.12" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/app/(app)/create-post/page.tsx: -------------------------------------------------------------------------------- 1 | import { revalidateTag } from 'next/cache' 2 | import { CreateMarketForm } from '@play-money/markets/components/CreateMarketForm' 3 | 4 | export default function CreatePost() { 5 | // eslint-disable-next-line @typescript-eslint/require-await -- Await is required by next 6 | const handleRevalidate = async () => { 7 | 'use server' 8 | revalidateTag('markets') 9 | } 10 | 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/VerificationTokenSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // VERIFICATION TOKEN SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const VerificationTokenSchema = z.object({ 8 | identifier: z.string(), 9 | token: z.string(), 10 | expires: z.coerce.date(), 11 | }) 12 | 13 | export type VerificationToken = z.infer 14 | 15 | export default VerificationTokenSchema; 16 | -------------------------------------------------------------------------------- /packages/transparency/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/transparency", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@play-money/database": "*", 10 | "decimal.js": "^10.4.3", 11 | "next": "^14.1.1", 12 | "react": "^18.2.0", 13 | "react-color": "^2.19.3", 14 | "zod": "^3.23.5" 15 | }, 16 | "devDependencies": { 17 | "@types/react-color": "^3.0.12" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/TransactionBatchSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // TRANSACTION BATCH SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const TransactionBatchSchema = z.object({ 8 | id: z.string().cuid(), 9 | createdAt: z.coerce.date(), 10 | updatedAt: z.coerce.date(), 11 | }) 12 | 13 | export type TransactionBatch = z.infer 14 | 15 | export default TransactionBatchSchema; 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/activity/schema.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketActivitySchema } from '@play-money/markets/types' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get all site activity', 8 | responses: { 9 | 200: zod.object({ data: zod.array(MarketActivitySchema) }), 10 | 404: ServerErrorSchema, 11 | 500: ServerErrorSchema, 12 | }, 13 | }, 14 | } as const satisfies ApiEndpoints 15 | -------------------------------------------------------------------------------- /packages/database/migrations/20240530052110_remove_currency_entity/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `entityId` on the `Currency` table. All the data in the column will be lost. 5 | - You are about to drop the column `entityType` on the `Currency` table. All the data in the column will be lost. 6 | 7 | */ 8 | -- AlterTable 9 | ALTER TABLE "Currency" DROP COLUMN "entityId", 10 | DROP COLUMN "entityType"; 11 | 12 | -- DropEnum 13 | DROP TYPE "CurrencyEntityType"; 14 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/[id]/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get a user', 8 | parameters: UserSchema.pick({ id: true }), 9 | responses: { 10 | 200: z.object({ data: UserSchema }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /apps/web/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | transpilePackages: ['@play-money/ui'], 5 | experimental: { 6 | serverActions: { 7 | allowedOrigins: [process.env.NEXT_PUBLIC_API_URL], 8 | }, 9 | }, 10 | images: { 11 | remotePatterns: [ 12 | { 13 | protocol: 'https', 14 | hostname: 'bijbnpk9x6qrvjzj.public.blob.vercel-storage.com', 15 | port: '', 16 | }, 17 | ], 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/CommentReactionSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // COMMENT REACTION SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const CommentReactionSchema = z.object({ 8 | id: z.string().cuid(), 9 | emoji: z.string(), 10 | userId: z.string(), 11 | commentId: z.string(), 12 | }) 13 | 14 | export type CommentReaction = z.infer 15 | 16 | export default CommentReactionSchema; 17 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/SessionSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // SESSION SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const SessionSchema = z.object({ 8 | sessionToken: z.string(), 9 | userId: z.string(), 10 | expires: z.coerce.date(), 11 | createdAt: z.coerce.date(), 12 | updatedAt: z.coerce.date(), 13 | }) 14 | 15 | export type Session = z.infer 16 | 17 | export default SessionSchema; 18 | -------------------------------------------------------------------------------- /packages/finance/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/finance", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest", 7 | "test:watch": "jest --watch", 8 | "type-check": "tsc --noEmit" 9 | }, 10 | "dependencies": { 11 | "decimal.js": "^10.4.3", 12 | "next": "^14.1.1", 13 | "react": "^18.2.0", 14 | "react-color": "^2.19.3", 15 | "zod": "^3.23.5" 16 | }, 17 | "devDependencies": { 18 | "@types/react-color": "^3.0.12" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/api-helpers/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export function stripUndefined>( 4 | obj: T 5 | ): { [K in keyof T]: Exclude } { 6 | return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null)) as { 7 | [K in keyof T]: Exclude 8 | } 9 | } 10 | 11 | export const zodCoerceCSVToArray = z.preprocess( 12 | (val) => (typeof val === 'string' ? val.split(',') : val), 13 | z.array(z.string()) 14 | ) 15 | -------------------------------------------------------------------------------- /packages/api-helpers/components/SWRProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { SWRConfig } from 'swr' 4 | 5 | export const SWRProvider = ({ children }: { children: React.ReactNode }) => { 6 | return ( 7 | 10 | fetch(process.env.NEXT_PUBLIC_API_URL + resource, { ...init, credentials: 'include' }).then((res) => 11 | res.json() 12 | ), 13 | }} 14 | > 15 | {children} 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /apps/storybook/.storybook/modes.ts: -------------------------------------------------------------------------------- 1 | export const allModes = { 2 | 'dark mobile': { 3 | backgrounds: 'dark', 4 | theme: 'dark', 5 | viewport: 'small', 6 | }, 7 | 'dark desktop': { 8 | backgrounds: 'dark', 9 | theme: 'dark', 10 | viewport: 'large', 11 | }, 12 | 'light mobile': { 13 | backgrounds: 'light', 14 | theme: 'light', 15 | viewport: 'small', 16 | }, 17 | 'light desktop': { 18 | backgrounds: 'light', 19 | theme: 'light', 20 | viewport: 'large', 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/me/referrals/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get all referrals for the current user', 8 | security: true, 9 | responses: { 10 | 200: z.object({ data: z.array(UserSchema) }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /packages/comments/lib/updateComment.ts: -------------------------------------------------------------------------------- 1 | import db, { Comment } from '@play-money/database' 2 | 3 | export async function updateComment({ id, content }: { id: string; content?: string }) { 4 | const updatedData: Partial = {} 5 | 6 | if (content) { 7 | updatedData.content = content 8 | updatedData.edited = true 9 | } 10 | 11 | const updatedComment = await db.comment.update({ 12 | where: { id }, 13 | data: { ...updatedData, updatedAt: new Date() }, 14 | }) 15 | 16 | return updatedComment 17 | } 18 | -------------------------------------------------------------------------------- /packages/markets/lib/getMarketTransactions.ts: -------------------------------------------------------------------------------- 1 | import db, { Transaction, TransactionEntry } from '@play-money/database' 2 | 3 | export type MarketTransaction = Transaction & { 4 | entries: Array 5 | } 6 | 7 | export async function getMarketTransactions({ marketId }: { marketId: string }) { 8 | const transactions = await db.transaction.findMany({ 9 | where: { 10 | marketId: marketId, 11 | }, 12 | include: { 13 | entries: true, 14 | }, 15 | }) 16 | 17 | return transactions 18 | } 19 | -------------------------------------------------------------------------------- /packages/lists/rules.ts: -------------------------------------------------------------------------------- 1 | import { List } from '@play-money/database' 2 | import { User } from '@play-money/database' 3 | import { isAdmin } from '@play-money/users/rules' 4 | 5 | export function canAddToList({ list, userId }: { list: List; userId?: string }) { 6 | return list.contributionPolicy === 'PUBLIC' || (list.contributionPolicy === 'OWNERS_ONLY' && list.ownerId === userId) 7 | } 8 | 9 | export function canModifyList({ list, user }: { list: List; user: User }) { 10 | return list.ownerId === user.id || isAdmin({ user }) 11 | } 12 | -------------------------------------------------------------------------------- /apps/api/README.md: -------------------------------------------------------------------------------- 1 | # Play Money API Server 2 | 3 | The **Play Money** API server is a Next.js application dedicated to serving the platform's API routes. It aims to be a standalone instrument for frontend clients and bots. 4 | 5 | ## Features 6 | 7 | - **API Endpoint**: Accessible locally at [localhost:3001](http://localhost:3001) 8 | - **API Versioning**: All routes are under `/v1/` 9 | - **Strongly Typed**: API requests and responses are strongly typed to facilitate future OpenAPI schema generation and automatic client library creation. 10 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/related/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get related markets', 8 | parameters: z.object({ id: z.string() }), 9 | responses: { 10 | 200: z.object({ data: z.array(MarketSchema) }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/referral/[code]/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get a user by referral code', 8 | parameters: z.object({ code: z.string() }), 9 | responses: { 10 | 200: z.object({ data: UserSchema }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /packages/quests/components/UserQuestCard.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useUserStats } from '@play-money/api-helpers/client/hooks' 4 | import { useUser } from '@play-money/users/context/UserContext' 5 | import { QuestCard } from './QuestCard' 6 | 7 | export function UserQuestCard() { 8 | const { user } = useUser() 9 | const { data: statsData } = useUserStats({ userId: user?.id ?? '', skip: !user }) 10 | const data = statsData?.data 11 | 12 | return user && data?.quests.length ? : null 13 | } 14 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/username/[username]/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get a user by username', 8 | parameters: UserSchema.pick({ username: true }), 9 | responses: { 10 | 200: z.object({ data: UserSchema }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /packages/users/lib/getMarketOptionPosition.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | // Other Comprehensive Income (OCI) is recorded as Equity. It does not count towards income or net income. Unrealized Gains and Losses from Market Options. 4 | export async function getMarketOptionPosition({ accountId, optionId }: { accountId: string; optionId: string }) { 5 | const position = await db.marketOptionPosition.findUnique({ 6 | where: { 7 | accountId_optionId: { accountId, optionId }, 8 | }, 9 | }) 10 | return position 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [{ "mode": "auto" }], 3 | "[javascript]": { 4 | "editor.defaultFormatter": "esbenp.prettier-vscode" 5 | }, 6 | "[javascriptreact]": { 7 | "editor.defaultFormatter": "esbenp.prettier-vscode" 8 | }, 9 | "[typescript]": { 10 | "editor.defaultFormatter": "esbenp.prettier-vscode" 11 | }, 12 | "[typescriptreact]": { 13 | "editor.defaultFormatter": "esbenp.prettier-vscode" 14 | }, 15 | "tailwindCSS.experimental.classRegex": ["class:\\s*?[\"'`]([^\"'`]*).*?,"] 16 | } 17 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/lists/[id]/comments/schema.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { CommentSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get comments for a list', 8 | parameters: zod.object({ id: zod.string() }), 9 | responses: { 10 | 200: zod.object({ data: zod.array(CommentSchema) }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/generate-tags/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Generate tags for a market', 7 | security: true, 8 | requestBody: z.object({ question: z.string() }), 9 | responses: { 10 | 200: z.object({ 11 | data: z.array(z.string()), 12 | }), 13 | 404: ServerErrorSchema, 14 | 500: ServerErrorSchema, 15 | }, 16 | }, 17 | } as const satisfies ApiEndpoints 18 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/JsonValueSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import type { Prisma } from '@prisma/client'; 3 | 4 | export const JsonValueSchema: z.ZodType = z.lazy(() => 5 | z.union([ 6 | z.string(), 7 | z.number(), 8 | z.boolean(), 9 | z.literal(null), 10 | z.record(z.lazy(() => JsonValueSchema.optional())), 11 | z.array(z.lazy(() => JsonValueSchema)), 12 | ]) 13 | ); 14 | 15 | export type JsonValueType = z.infer; 16 | 17 | export default JsonValueSchema 18 | -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/comments/schema.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { CommentSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get comments for a market', 8 | parameters: zod.object({ id: zod.string() }), 9 | responses: { 10 | 200: zod.object({ data: zod.array(CommentSchema) }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /packages/comments/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/comments", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "type-check": "tsc --noEmit" 7 | }, 8 | "dependencies": { 9 | "@emoji-mart/data": "^1.2.1", 10 | "@emoji-mart/react": "^1.1.1", 11 | "@play-money/database": "*", 12 | "@play-money/users": "*", 13 | "emoji-mart": "^5.6.0", 14 | "lodash": "^4.17.21", 15 | "zod": "^3.23.5" 16 | }, 17 | "devDependencies": { 18 | "@types/lodash": "^4.17.1", 19 | "date-fns": "^3.6.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/referrals/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | import { User } from '@play-money/database' 2 | 3 | export function generateReferralCode(): string { 4 | const chars = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789' 5 | let code = '' 6 | for (let i = 0; i < 4; i++) { 7 | code += chars.charAt(Math.floor(Math.random() * chars.length)) 8 | } 9 | return code 10 | } 11 | 12 | export function isNewlyReferredUser(user: User): boolean { 13 | const SEVEN_DAYS_AGO = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) 14 | return user.createdAt > SEVEN_DAYS_AGO && !!user.referredBy 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/check-username/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Check if a username is available', 8 | parameters: UserSchema.pick({ username: true }), 9 | responses: { 10 | 200: z.object({ data: z.object({ available: z.boolean(), message: z.string().optional() }) }), 11 | 500: ServerErrorSchema, 12 | }, 13 | }, 14 | } as const satisfies ApiEndpoints 15 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/cancel/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Cancel a market', 7 | security: true, 8 | parameters: z.object({ id: z.string() }), 9 | requestBody: z.object({ reason: z.string() }), 10 | responses: { 11 | 200: z.object({ data: z.object({ success: z.boolean() }) }), 12 | 404: ServerErrorSchema, 13 | 500: ServerErrorSchema, 14 | }, 15 | }, 16 | } as const satisfies ApiEndpoints 17 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/activity/schema.ts: -------------------------------------------------------------------------------- 1 | import zod from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketActivitySchema } from '@play-money/markets/types' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get the activity for a market', 8 | parameters: zod.object({ id: zod.string() }), 9 | responses: { 10 | 200: zod.object({ data: zod.array(MarketActivitySchema) }), 11 | 404: ServerErrorSchema, 12 | 500: ServerErrorSchema, 13 | }, 14 | }, 15 | } as const satisfies ApiEndpoints 16 | -------------------------------------------------------------------------------- /packages/database/migrations/20240930235440_referrals_transaction/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "NotificationType" ADD VALUE 'REFERRER_BONUS'; 3 | 4 | -- AlterEnum 5 | -- This migration adds more than one value to an enum. 6 | -- With PostgreSQL versions 11 and earlier, this is not possible 7 | -- in a single migration. This can be worked around by creating 8 | -- multiple migrations, each migration adding only one value to 9 | -- the enum. 10 | 11 | 12 | ALTER TYPE "TransactionType" ADD VALUE 'REFERRER_BONUS'; 13 | ALTER TYPE "TransactionType" ADD VALUE 'REFERREE_BONUS'; 14 | -------------------------------------------------------------------------------- /apps/storybook/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | *storybook.log -------------------------------------------------------------------------------- /packages/markets/lib/updateMarketOption.ts: -------------------------------------------------------------------------------- 1 | import db, { MarketOption } from '@play-money/database' 2 | 3 | export async function updateMarketOption({ id, name, color }: { id: string; name?: string; color?: string }) { 4 | const updatedData: Partial = {} 5 | 6 | if (name) { 7 | updatedData.name = name 8 | } 9 | 10 | if (color) { 11 | updatedData.color = color 12 | } 13 | 14 | const updatedMarket = await db.marketOption.update({ 15 | where: { id }, 16 | data: { ...updatedData, updatedAt: new Date() }, 17 | }) 18 | 19 | return updatedMarket 20 | } 21 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/TransactionTypeSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | export const TransactionTypeSchema = z.enum(['TRADE_BUY','TRADE_SELL','TRADE_WIN','TRADE_LOSS','CREATOR_TRADER_BONUS','LIQUIDITY_INITIALIZE','LIQUIDITY_DEPOSIT','LIQUIDITY_WITHDRAWAL','LIQUIDITY_RETURNED','LIQUIDITY_VOLUME_BONUS','DAILY_TRADE_BONUS','DAILY_MARKET_BONUS','DAILY_COMMENT_BONUS','DAILY_LIQUIDITY_BONUS','HOUSE_GIFT','HOUSE_SIGNUP_BONUS','REFERRER_BONUS','REFERREE_BONUS']); 4 | 5 | export type TransactionTypeType = `${z.infer}` 6 | 7 | export default TransactionTypeSchema; 8 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/buy/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Buy an option in a market', 7 | security: true, 8 | parameters: z.object({ id: z.string() }), 9 | requestBody: z.object({ optionId: z.string(), amount: z.number() }), 10 | responses: { 11 | 200: z.object({ data: z.object({ success: z.boolean() }) }), 12 | 404: ServerErrorSchema, 13 | 500: ServerErrorSchema, 14 | }, 15 | }, 16 | } as const satisfies ApiEndpoints 17 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/sell/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Sell an option in a market', 7 | security: true, 8 | parameters: z.object({ id: z.string() }), 9 | requestBody: z.object({ optionId: z.string(), amount: z.number() }), 10 | responses: { 11 | 200: z.object({ data: z.object({ success: z.boolean() }) }), 12 | 404: ServerErrorSchema, 13 | 500: ServerErrorSchema, 14 | }, 15 | }, 16 | } as const satisfies ApiEndpoints 17 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/[id]/balance/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the balance for a user', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.object({ 11 | // TODO: Hookup with NetBalance 12 | balance: z.object({}), 13 | }), 14 | }), 15 | 404: ServerErrorSchema, 16 | 500: ServerErrorSchema, 17 | }, 18 | }, 19 | } as const satisfies ApiEndpoints 20 | -------------------------------------------------------------------------------- /apps/web/app/(app)/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useEffect } from 'react' 4 | 5 | export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { 6 | useEffect(() => { 7 | console.error(error) // eslint-disable-line no-console -- Client-side error log 8 | }, [error]) 9 | 10 | return ( 11 |
12 |

Something went wrong!

13 | 21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/resolve/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Resolve a market', 7 | security: true, 8 | parameters: z.object({ id: z.string() }), 9 | requestBody: z.object({ optionId: z.string(), supportingLink: z.string().optional() }), 10 | responses: { 11 | 200: z.object({ data: z.object({ success: z.boolean() }) }), 12 | 404: ServerErrorSchema, 13 | 500: ServerErrorSchema, 14 | }, 15 | }, 16 | } as const satisfies ApiEndpoints 17 | -------------------------------------------------------------------------------- /packages/finance/economy.ts: -------------------------------------------------------------------------------- 1 | export const INITIAL_MARKET_LIQUIDITY_PRIMARY = 500 2 | export const LOWEST_MARKET_LIQUIDITY_PRIMARY = 200 3 | 4 | export const LIQUIDITY_VOLUME_BONUS_PERCENT = 0.05 5 | 6 | export const UNIQUE_TRADER_BONUS_PRIMARY = 50 7 | export const UNIQUE_TRADER_LIQUIDITY_PRIMARY = 50 8 | 9 | export const INITIAL_USER_BALANCE_PRIMARY = 20000 10 | 11 | export const DAILY_TRADE_BONUS_PRIMARY = 100 12 | export const DAILY_MARKET_BONUS_PRIMARY = 500 13 | export const DAILY_COMMENT_BONUS_PRIMARY = 50 14 | export const DAILY_LIQUIDITY_BONUS_PRIMARY = 250 15 | 16 | export const REALIZED_GAINS_TAX = 0.05 17 | -------------------------------------------------------------------------------- /packages/lists/lib/getCommentsOnList.ts: -------------------------------------------------------------------------------- 1 | import { CommentWithReactions } from '@play-money/comments/lib/getComment' 2 | import db from '@play-money/database' 3 | 4 | export async function getCommentsOnList({ listId }: { listId: string }): Promise> { 5 | const comments = await db.comment.findMany({ 6 | where: { 7 | entityType: 'LIST', 8 | entityId: listId, 9 | }, 10 | include: { 11 | author: true, 12 | reactions: { 13 | include: { 14 | user: true, 15 | }, 16 | }, 17 | }, 18 | }) 19 | 20 | return comments 21 | } 22 | -------------------------------------------------------------------------------- /packages/users/components/ActiveUserBalance.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useMyBalance } from '@play-money/api-helpers/client/hooks' 4 | import { CurrencyDisplay } from '@play-money/finance/components/CurrencyDisplay' 5 | import { useUser } from '@play-money/users/context/UserContext' 6 | 7 | export function ActiveUserBalance({ initialBalance }: { initialBalance?: number }) { 8 | const { user } = useUser() 9 | const { data: balanceData } = useMyBalance({ skip: !user }) 10 | const data = balanceData?.data 11 | 12 | return user ? : null 13 | } 14 | -------------------------------------------------------------------------------- /packages/api-helpers/types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const paginationSchema = z.object({ 4 | cursor: z.string().optional(), 5 | limit: z.coerce.number().min(1).max(100).optional(), 6 | sortField: z.string().optional(), 7 | sortDirection: z.enum(['asc', 'desc']).optional(), 8 | }) 9 | 10 | export type PaginationRequest = z.infer & { sortDirection?: 'asc' | 'desc' } 11 | export type PageInfo = { 12 | hasNextPage: boolean 13 | endCursor?: string 14 | total: number 15 | } 16 | 17 | export interface PaginatedResponse { 18 | data: T[] 19 | pageInfo: PageInfo 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/app/(app)/[username]/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { useEffect } from 'react' 4 | import { UserNotFoundError } from '@play-money/users/lib/exceptions' 5 | 6 | export default function Error({ error }: { error: Error & { digest?: string; code?: string } }) { 7 | useEffect(() => { 8 | console.dir(error) // eslint-disable-line -- Log the error to an error reporting service 9 | }, [error]) 10 | 11 | if (error.message === UserNotFoundError.code) { 12 | return

User not found

13 | } 14 | 15 | return ( 16 |
17 |

Something went wrong!

18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /packages/markets/lib/getCommentsOnMarket.ts: -------------------------------------------------------------------------------- 1 | import { CommentWithReactions } from '@play-money/comments/lib/getComment' 2 | import db from '@play-money/database' 3 | 4 | export async function getCommentsOnMarket({ marketId }: { marketId: string }): Promise> { 5 | const comments = await db.comment.findMany({ 6 | where: { 7 | entityType: 'MARKET', 8 | entityId: marketId, 9 | }, 10 | include: { 11 | author: true, 12 | reactions: { 13 | include: { 14 | user: true, 15 | }, 16 | }, 17 | }, 18 | }) 19 | 20 | return comments 21 | } 22 | -------------------------------------------------------------------------------- /packages/notifications/lib/updateNotificationsRead.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function updateNotificationsRead({ 4 | userId, 5 | marketId, 6 | listId, 7 | }: { 8 | userId: string 9 | marketId?: string 10 | listId?: string 11 | }): Promise { 12 | const now = new Date() 13 | 14 | const result = await db.notification.updateMany({ 15 | where: { 16 | recipientId: userId, 17 | readAt: null, 18 | marketId: marketId, 19 | listId: listId, 20 | }, 21 | data: { 22 | readAt: now, 23 | }, 24 | }) 25 | 26 | return result.count 27 | } 28 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/me/resource-viewed/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | private: true, 7 | summary: 'Mark a resource as viewed for the current user', 8 | security: true, 9 | requestBody: z.object({ resourceType: z.string(), resourceId: z.string(), timestamp: z.string() }), 10 | responses: { 11 | 200: z.object({ data: z.object({ success: z.boolean() }) }), 12 | 404: ServerErrorSchema, 13 | 500: ServerErrorSchema, 14 | }, 15 | }, 16 | } as const satisfies ApiEndpoints 17 | -------------------------------------------------------------------------------- /packages/config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@tailwindcss/typography": "^0.5.13", 7 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 8 | "@vercel/style-guide": "^5.2.0", 9 | "eslint-config-prettier": "^9.1.0", 10 | "eslint-config-turbo": "^1.12.4", 11 | "eslint-plugin-local-rules": "^2.0.1", 12 | "eslint-plugin-prettier": "^5.1.3", 13 | "jest-mock-extended": "^3.0.7", 14 | "prettier": "^3.2.5", 15 | "prettier-plugin-tailwindcss": "^0.5.14", 16 | "tailwindcss": "^3.4.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/markets/components/EditMarketDialog.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import { mockComment, mockMarket, mockUser } from '@play-money/database/mocks' 3 | import { EditMarketDialog } from './EditMarketDialog' 4 | 5 | const meta = { 6 | component: EditMarketDialog, 7 | tags: ['autodocs'], 8 | } satisfies Meta 9 | 10 | export default meta 11 | type Story = StoryObj 12 | 13 | export const Default: Story = { 14 | args: { 15 | open: true, 16 | onClose: console.log, 17 | onSuccess: console.log, 18 | market: mockMarket(), 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/app/(app)/questions/[marketId]/[slug]/comments/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { getExtendedMarket } from '@play-money/api-helpers/client' 3 | import { MarketComments } from '@play-money/markets/components/MarketComments' 4 | import { MarketCommentsPage } from '@play-money/markets/components/MarketCommentsPage' 5 | 6 | export default async function AppPostsSlugPage({ params }: { params: { marketId: string } }) { 7 | const { data: market } = await getExtendedMarket({ marketId: params.marketId }) 8 | 9 | return } /> 10 | } 11 | -------------------------------------------------------------------------------- /packages/config/jest/dbMock.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from '@prisma/client' 2 | import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended' 3 | import db from '@play-money/database' 4 | 5 | const _ = jest.requireActual('lodash') 6 | global._ = _ 7 | 8 | jest.mock('@play-money/database', () => { 9 | const original = jest.requireActual('@play-money/database') 10 | 11 | return { 12 | __esModule: true, 13 | ...original, 14 | default: mockDeep(), 15 | } 16 | }) 17 | 18 | beforeEach(() => { 19 | mockReset(dbMock) 20 | }) 21 | 22 | export const dbMock = db as unknown as DeepMockProxy 23 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/MarketResolutionSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // MARKET RESOLUTION SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const MarketResolutionSchema = z.object({ 8 | id: z.string().cuid(), 9 | marketId: z.string(), 10 | resolvedById: z.string(), 11 | resolutionId: z.string(), 12 | supportingLink: z.string().nullable(), 13 | createdAt: z.coerce.date(), 14 | updatedAt: z.coerce.date(), 15 | }) 16 | 17 | export type MarketResolution = z.infer 18 | 19 | export default MarketResolutionSchema; 20 | -------------------------------------------------------------------------------- /apps/web/app/(app)/settings/referrals/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from 'next/navigation' 2 | import React from 'react' 3 | import { getMyReferrals } from '@play-money/api-helpers/client' 4 | import { auth } from '@play-money/auth' 5 | import { SettingsReferralPage } from '@play-money/referrals/components/SettingsReferralPage' 6 | 7 | export default async function AppSettingsPage() { 8 | const session = await auth() 9 | 10 | if (!session) { 11 | redirect('/login?redirect=/settings/referrals') 12 | } 13 | 14 | const { data: referrals } = await getMyReferrals() 15 | 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /packages/database/migrations/20240930070407_referrals/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - A unique constraint covering the columns `[referralCode]` on the table `User` will be added. If there are existing duplicate values, this will fail. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "User" ADD COLUMN "referralCode" TEXT, 9 | ADD COLUMN "referredBy" TEXT; 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "User_referralCode_key" ON "User"("referralCode"); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "User" ADD CONSTRAINT "User_referredBy_fkey" FOREIGN KEY ("referredBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/lists/[id]/balance/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the balance for a list', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.object({ 11 | // TODO: Hookup with NetBalance 12 | user: z.array(z.object({})), 13 | userPositions: z.array(z.object({})), 14 | }), 15 | }), 16 | 404: ServerErrorSchema, 17 | 500: ServerErrorSchema, 18 | }, 19 | }, 20 | } as const satisfies ApiEndpoints 21 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/balances/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the balances for a market', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.object({ 11 | // TODO: Hookup with NetBalance 12 | balances: z.array(z.object({})), 13 | user: z.object({}).optional(), 14 | }), 15 | }), 16 | 404: ServerErrorSchema, 17 | 500: ServerErrorSchema, 18 | }, 19 | }, 20 | } as const satisfies ApiEndpoints 21 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/[id]/graph/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the graph for a user', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.array( 11 | z.object({ 12 | balance: z.number(), 13 | startAt: z.date(), 14 | endAt: z.date(), 15 | }) 16 | ), 17 | }), 18 | 404: ServerErrorSchema, 19 | 500: ServerErrorSchema, 20 | }, 21 | }, 22 | } as const satisfies ApiEndpoints 23 | -------------------------------------------------------------------------------- /packages/lists/components/ListComments.tsx: -------------------------------------------------------------------------------- 1 | import { revalidateTag } from 'next/cache' 2 | import React from 'react' 3 | import { getListComments } from '@play-money/api-helpers/client' 4 | import { CommentsList } from '@play-money/comments/components/CommentsList' 5 | 6 | export async function ListComments({ listId }: { listId: string }) { 7 | const { data: comments } = await getListComments({ listId }) 8 | 9 | const handleRevalidate = async () => { 10 | 'use server' 11 | revalidateTag(`list:${listId}:comments`) 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/comments/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { CommentSchema } from '@play-money/database' 4 | 5 | export default { 6 | post: { 7 | summary: 'Create a comment on an entity', 8 | security: true, 9 | requestBody: CommentSchema.pick({ 10 | content: true, 11 | parentId: true, 12 | entityType: true, 13 | entityId: true, 14 | }), 15 | responses: { 16 | 200: z.object({ data: CommentSchema }), 17 | 404: ServerErrorSchema, 18 | 500: ServerErrorSchema, 19 | }, 20 | }, 21 | } as const satisfies ApiEndpoints 22 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/InputJsonValueSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { Prisma } from '@prisma/client'; 3 | 4 | export const InputJsonValueSchema: z.ZodType = z.lazy(() => 5 | z.union([ 6 | z.string(), 7 | z.number(), 8 | z.boolean(), 9 | z.object({ toJSON: z.function(z.tuple([]), z.any()) }), 10 | z.record(z.lazy(() => z.union([InputJsonValueSchema, z.literal(null)]))), 11 | z.array(z.lazy(() => z.union([InputJsonValueSchema, z.literal(null)]))), 12 | ]) 13 | ); 14 | 15 | export type InputJsonValueType = z.infer; 16 | 17 | export default InputJsonValueSchema; 18 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/transparency/stats/users/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | private: true, 7 | responses: { 8 | 200: z.object({ 9 | data: z.array( 10 | z.object({ 11 | dau: z.number(), 12 | signups: z.number(), 13 | referrals: z.number(), 14 | startAt: z.date(), 15 | endAt: z.date(), 16 | }) 17 | ), 18 | }), 19 | 404: ServerErrorSchema, 20 | 500: ServerErrorSchema, 21 | }, 22 | }, 23 | } as const satisfies ApiEndpoints 24 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/[id]/transactions/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { TransactionEntrySchema, TransactionSchema, UserSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Get transactions for a user', 8 | parameters: UserSchema.pick({ id: true }), 9 | responses: { 10 | 200: z.object({ 11 | data: z.array(TransactionSchema.extend({ entries: z.array(TransactionEntrySchema) })), 12 | }), 13 | 404: ServerErrorSchema, 14 | 500: ServerErrorSchema, 15 | }, 16 | }, 17 | } as const satisfies ApiEndpoints 18 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/AccountSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { AccountTypeSchema } from '../inputTypeSchemas/AccountTypeSchema' 3 | 4 | ///////////////////////////////////////// 5 | // ACCOUNT SCHEMA 6 | ///////////////////////////////////////// 7 | 8 | export const AccountSchema = z.object({ 9 | type: AccountTypeSchema, 10 | id: z.string().cuid(), 11 | internalType: z.string().nullable(), 12 | userId: z.string().nullable(), 13 | marketId: z.string().nullable(), 14 | createdAt: z.coerce.date(), 15 | updatedAt: z.coerce.date(), 16 | }) 17 | 18 | export type Account = z.infer 19 | 20 | export default AccountSchema; 21 | -------------------------------------------------------------------------------- /packages/database/migrations/20240529203430_add_currency_table/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "CurrencyEntityType" AS ENUM ('MARKET'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "Currency" ( 6 | "id" TEXT NOT NULL, 7 | "name" TEXT NOT NULL, 8 | "symbol" TEXT NOT NULL, 9 | "code" TEXT NOT NULL, 10 | "imageUrl" TEXT, 11 | "entityType" "CurrencyEntityType", 12 | "entityId" TEXT, 13 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 14 | "updatedAt" TIMESTAMP(3) NOT NULL, 15 | 16 | CONSTRAINT "Currency_pkey" PRIMARY KEY ("id") 17 | ); 18 | 19 | -- CreateIndex 20 | CREATE UNIQUE INDEX "Currency_code_key" ON "Currency"("code"); 21 | -------------------------------------------------------------------------------- /packages/markets/components/CreateMarketForm.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import { CreateMarketForm } from './CreateMarketForm' 3 | 4 | const meta = { 5 | component: CreateMarketForm, 6 | tags: ['autodocs'], 7 | } satisfies Meta 8 | 9 | export default meta 10 | type Story = StoryObj 11 | 12 | export const Default: Story = { 13 | args: { 14 | colors: [ 15 | '#f44336', 16 | '#f44336', 17 | '#f44336', 18 | '#f44336', 19 | '#f44336', 20 | '#f44336', 21 | '#f44336', 22 | '#f44336', 23 | '#f44336', 24 | '#f44336', 25 | ], 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /packages/markets/components/MarketComments.tsx: -------------------------------------------------------------------------------- 1 | import { revalidateTag } from 'next/cache' 2 | import React from 'react' 3 | import { getMarketComments } from '@play-money/api-helpers/client' 4 | import { CommentsList } from '@play-money/comments/components/CommentsList' 5 | 6 | export async function MarketComments({ marketId }: { marketId: string }) { 7 | const { data: comments } = await getMarketComments({ marketId }) 8 | 9 | const handleRevalidate = async () => { 10 | 'use server' 11 | revalidateTag(`${marketId}:comments`) 12 | } 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/ApiKeySchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // API KEY SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const ApiKeySchema = z.object({ 8 | id: z.string().cuid(), 9 | name: z.string().trim().min(1, { message: "Name is required" }), 10 | key: z.string(), 11 | userId: z.string(), 12 | lastUsedAt: z.coerce.date().nullable(), 13 | createdAt: z.coerce.date(), 14 | updatedAt: z.coerce.date(), 15 | expiresAt: z.coerce.date().nullable(), 16 | isRevoked: z.boolean(), 17 | }) 18 | 19 | export type ApiKey = z.infer 20 | 21 | export default ApiKeySchema; 22 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/liquidity/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketSchema } from '@play-money/database' 4 | 5 | export default { 6 | post: { 7 | summary: 'Add liquidity to a market', 8 | security: true, 9 | parameters: MarketSchema.pick({ id: true }), 10 | requestBody: z.object({ amount: z.number() }), 11 | responses: { 12 | 200: z.object({ 13 | data: z.object({ 14 | success: z.boolean(), 15 | }), 16 | }), 17 | 404: ServerErrorSchema, 18 | 500: ServerErrorSchema, 19 | }, 20 | }, 21 | } as const satisfies ApiEndpoints 22 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/options/[optionId]/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketOptionSchema } from '@play-money/database' 4 | 5 | export default { 6 | patch: { 7 | summary: 'Update an option in a market', 8 | security: true, 9 | parameters: z.object({ id: z.string(), optionId: z.string() }), 10 | requestBody: MarketOptionSchema.pick({ name: true, color: true }).partial(), 11 | responses: { 12 | 200: z.object({ data: MarketOptionSchema }), 13 | 404: ServerErrorSchema, 14 | 500: ServerErrorSchema, 15 | }, 16 | }, 17 | } as const satisfies ApiEndpoints 18 | -------------------------------------------------------------------------------- /packages/users/lib/getUserPrimaryAccount.ts: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | import { UserNotFoundError } from '@play-money/users/lib/exceptions' 3 | 4 | export async function getUserPrimaryAccount({ userId }: { userId: string }) { 5 | const user = await db.user.findUnique({ 6 | where: { 7 | id: userId, 8 | }, 9 | include: { 10 | primaryAccount: true, 11 | }, 12 | }) 13 | 14 | if (!user) { 15 | throw new UserNotFoundError(`User with id "${userId}" not found`) 16 | } 17 | 18 | const account = user.primaryAccount 19 | 20 | if (!account) { 21 | throw new Error('User does not have a primary account') 22 | } 23 | 24 | return account 25 | } 26 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/lists/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { 3 | ApiEndpoints, 4 | createPaginatedResponseSchema, 5 | paginationSchema, 6 | ServerErrorSchema, 7 | } from '@play-money/api-helpers' 8 | import { ListSchema } from '@play-money/database' 9 | 10 | export default { 11 | get: { 12 | summary: 'Get lists', 13 | parameters: z 14 | .object({ 15 | ownerId: z.string().optional(), 16 | }) 17 | .merge(paginationSchema) 18 | .optional(), 19 | responses: { 20 | 200: createPaginatedResponseSchema(ListSchema), 21 | 404: ServerErrorSchema, 22 | 500: ServerErrorSchema, 23 | }, 24 | }, 25 | } as const satisfies ApiEndpoints 26 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/balance/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the balance for a market', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.object({ 11 | // TODO: Hookup with NetBalance 12 | amm: z.array(z.object({})), 13 | user: z.array(z.object({})), 14 | userPositions: z.array(z.object({})), 15 | }), 16 | }), 17 | 404: ServerErrorSchema, 18 | 500: ServerErrorSchema, 19 | }, 20 | }, 21 | } as const satisfies ApiEndpoints 22 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/search/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { MarketSchema, UserSchema, ListSchema } from '@play-money/database' 4 | 5 | export default { 6 | get: { 7 | summary: 'Search for users, markets, and lists', 8 | parameters: z.object({ query: z.string().optional() }), 9 | responses: { 10 | 200: z.object({ 11 | data: z.object({ 12 | users: z.array(UserSchema), 13 | markets: z.array(MarketSchema), 14 | lists: z.array(ListSchema), 15 | }), 16 | }), 17 | 500: ServerErrorSchema, 18 | }, 19 | }, 20 | } as const satisfies ApiEndpoints 21 | -------------------------------------------------------------------------------- /apps/web/app/(app)/questions/[marketId]/[slug]/positions/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { getExtendedMarket, getMarketPositions } from '@play-money/api-helpers/client' 3 | import { MarketPositionsPage } from '@play-money/markets/components/MarketPositionsPage' 4 | 5 | export default async function AppPostsSlugPage({ params }: { params: { marketId: string } }) { 6 | const { data: market } = await getExtendedMarket({ marketId: params.marketId }) 7 | const { data: marketPositions } = await getMarketPositions({ 8 | marketId: params.marketId, 9 | status: 'active', 10 | limit: 100, 11 | }) 12 | 13 | return 14 | } 15 | -------------------------------------------------------------------------------- /packages/markets/lib/getUniqueTraderIds.tsx: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getUniqueTraderIds(marketId: string, ignoreIds: string[] = []): Promise { 4 | const result = await db.transaction.findMany({ 5 | where: { 6 | type: { 7 | in: ['TRADE_BUY', 'TRADE_SELL'], 8 | }, 9 | marketId, 10 | }, 11 | select: { 12 | initiatorId: true, 13 | }, 14 | distinct: ['initiatorId'], 15 | }) 16 | 17 | const uniqueTraderIds = result 18 | .map((transaction) => transaction.initiatorId) 19 | .filter((userId): userId is string => userId != null && !ignoreIds.includes(userId)) 20 | 21 | return uniqueTraderIds 22 | } 23 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/quote/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | post: { 6 | summary: 'Get a quote for a market', 7 | parameters: z.object({ id: z.string() }), 8 | requestBody: z.object({ optionId: z.string(), amount: z.number(), isBuy: z.boolean().optional() }), 9 | responses: { 10 | 200: z.object({ 11 | data: z.object({ 12 | newProbability: z.number(), 13 | potentialReturn: z.number(), 14 | }), 15 | }), 16 | 404: ServerErrorSchema, 17 | 500: ServerErrorSchema, 18 | }, 19 | }, 20 | } as const satisfies ApiEndpoints 21 | -------------------------------------------------------------------------------- /packages/config/typescript/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "plugins": [{ "name": "next" }], 8 | "allowJs": true, 9 | "declaration": false, 10 | "declarationMap": false, 11 | "incremental": true, 12 | "jsx": "preserve", 13 | "lib": ["dom", "dom.iterable", "esnext"], 14 | "module": "esnext", 15 | "resolveJsonModule": true, 16 | "strict": true, 17 | "target": "es5" 18 | }, 19 | "include": ["src", "next-env.d.ts", "../config/typescript/*.d.ts", "../config/jest/jest-setup.ts"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/users/components/UserLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | import { User } from '@play-money/database' 4 | import { cn } from '@play-money/ui/utils' 5 | 6 | export function UserLink({ 7 | user, 8 | hideUsername = false, 9 | className, 10 | }: { 11 | user: Pick 12 | hideUsername?: boolean 13 | className?: string 14 | }) { 15 | return ( 16 | 17 | {user.displayName} 18 | {!hideUsername && @{user.username}} 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | build 15 | .swc/ 16 | 17 | # misc 18 | .DS_Store 19 | *.pem 20 | 21 | # debug 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # local env files 27 | /.env 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # turbo 34 | .turbo 35 | 36 | # ui 37 | dist/ 38 | 39 | # aider 40 | .aider* 41 | 42 | # intellij 43 | .idea/ 44 | 45 | tsconfig.tsbuildinfo 46 | tmp/ 47 | 48 | # storybook 49 | storybook-static 50 | .vercel 51 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/comments/[id]/reaction/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | import { CommentReactionSchema } from '@play-money/database' 4 | 5 | export default { 6 | post: { 7 | summary: 'React to a comment, will remove reaction if already exists', 8 | security: true, 9 | parameters: z.object({ id: z.string() }), 10 | requestBody: CommentReactionSchema.pick({ 11 | emoji: true, 12 | }), 13 | responses: { 14 | 200: z.object({ data: CommentReactionSchema }), 15 | 204: z.object({}), 16 | 404: ServerErrorSchema, 17 | 500: ServerErrorSchema, 18 | }, 19 | }, 20 | } as const satisfies ApiEndpoints 21 | -------------------------------------------------------------------------------- /packages/comments/lib/createCommentReply.ts: -------------------------------------------------------------------------------- 1 | import { Comment } from '@play-money/database' 2 | import { createComment } from './createComment' 3 | import { getComment } from './getComment' 4 | 5 | export async function createCommentReply({ 6 | content, 7 | authorId, 8 | parentId, 9 | }: Pick) { 10 | if (!parentId) { 11 | throw new Error('Parent comment id is required') 12 | } 13 | 14 | const parentComment = await getComment({ id: parentId }) 15 | 16 | const comment = await createComment({ 17 | content, 18 | authorId, 19 | parentId, 20 | entityType: parentComment.entityType, 21 | entityId: parentComment.entityId, 22 | }) 23 | 24 | return comment 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/app/(app)/me/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { LoaderCircleIcon } from 'lucide-react' 4 | import { useRouter } from 'next/navigation' 5 | import React, { useEffect } from 'react' 6 | import { useUser } from '@play-money/users/context/UserContext' 7 | 8 | export default function AppSetupPage() { 9 | const router = useRouter() 10 | const { user } = useUser() 11 | 12 | useEffect(() => { 13 | if (user) { 14 | router.push(`/${user.username}`) 15 | } else { 16 | router.push('/login?redirect=/me') 17 | } 18 | }, []) 19 | 20 | return ( 21 |
22 | 23 |
24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/app/(app)/questions/tagged/[tag]/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { getMarkets } from '@play-money/api-helpers/client' 3 | import { MarketList } from '@play-money/markets/components/MarketList' 4 | 5 | export default async function AppQuestionsPage({ params }: { params: { tag: string } }) { 6 | const { data: markets } = await getMarkets({ tags: [params.tag] }) 7 | 8 | return ( 9 |
10 |
11 |
{params.tag}
12 | 13 |
14 | 15 |
16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/ui-tests.yml: -------------------------------------------------------------------------------- 1 | name: 'UI Tests' 2 | 3 | on: push 4 | 5 | jobs: 6 | chromatic-deployment: 7 | name: Chromatic Deployment 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Setup Node.js environment 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: 20 18 | cache: 'npm' 19 | 20 | - name: Install dependencies 21 | run: npm ci 22 | 23 | - name: Publish to Chromatic 24 | uses: chromaui/action@latest 25 | with: 26 | projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} 27 | workingDir: apps/storybook 28 | onlyChanged: true 29 | -------------------------------------------------------------------------------- /packages/ui/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { formatDistanceToNow } from 'date-fns' 2 | 3 | export function formatDistanceToNowShort(date: Date | number): string { 4 | const formatted = formatDistanceToNow(date, { addSuffix: true }) 5 | 6 | return formatted 7 | .replace(/less than a minute/, 'now') 8 | .replace(/\sabout\s/, '') 9 | .replace(/\sless than\s/, '<') 10 | .replace(/\sover\s/, '>') 11 | .replace(/\syears?/, 'y') 12 | .replace(/\smonths?/, 'mo') 13 | .replace(/\sweeks?/, 'w') 14 | .replace(/\sdays?/, 'd') 15 | .replace(/\shours?/, 'h') 16 | .replace(/\sminutes?/, 'm') 17 | .replace(/\sseconds?/, 's') 18 | .replace(/^in\s?/, '') 19 | .replace(/^about\s?/, '') 20 | .replace(/\sago/, '') 21 | .trim() 22 | } 23 | -------------------------------------------------------------------------------- /packages/config/typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "allowImportingTsExtensions": true, 11 | "noEmit": true, 12 | "inlineSources": false, 13 | "isolatedModules": true, 14 | "module": "ESNext", 15 | "moduleResolution": "Bundler", 16 | "noUnusedLocals": false, 17 | "noUnusedParameters": false, 18 | "preserveWatchOutput": true, 19 | "skipLibCheck": true, 20 | "strict": true, 21 | "strictNullChecks": true 22 | }, 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/src/contexts/EditorExtensionsContext.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { Extensions } from '@tiptap/react' 4 | import React, { createContext, useContext } from 'react' 5 | 6 | type EditorExtensionsContextType = { 7 | extensions: Extensions 8 | } 9 | 10 | const EditorExtensionsContext = createContext(undefined) 11 | 12 | export function EditorExtensionsProvider({ 13 | children, 14 | extensions, 15 | }: { 16 | children: React.ReactNode 17 | extensions: Extensions 18 | }) { 19 | return {children} 20 | } 21 | 22 | export const useEditorExtensions = () => { 23 | return useContext(EditorExtensionsContext) 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/app/(app)/questions/[marketId]/[slug]/liquidity/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { getExtendedMarket, getMarketLiquidityTransactions } from '@play-money/api-helpers/client' 3 | import { MarketLiquidityPage } from '@play-money/markets/components/MarketLiquidityPage' 4 | 5 | export default async function AppPostsSlugPage({ params }: { params: { marketId: string } }) { 6 | const { data: market } = await getExtendedMarket({ marketId: params.marketId }) 7 | const { data: transactions, pageInfo } = await getMarketLiquidityTransactions({ marketId: params.marketId }) 8 | const userLiquidity = transactions.filter((t) => t.initiatorId) 9 | 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /packages/markets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@play-money/markets", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest", 7 | "test:watch": "jest --watch", 8 | "type-check": "tsc --noEmit" 9 | }, 10 | "dependencies": { 11 | "@play-money/database": "*", 12 | "@play-money/finance": "*", 13 | "@play-money/notifications": "*", 14 | "@play-money/quests": "*", 15 | "chrono-node": "^2.7.7", 16 | "decimal.js": "^10.4.3", 17 | "next": "^14.1.1", 18 | "openai": "^4.56.1", 19 | "react": "^18.2.0", 20 | "react-color": "^2.19.3", 21 | "zod": "^3.23.5" 22 | }, 23 | "devDependencies": { 24 | "@types/react-color": "^3.0.12", 25 | "jest": "^29.7.0", 26 | "ts-jest": "^29.1.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/database/migrations/20240518231024_add_markets/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Market" ( 3 | "id" TEXT NOT NULL, 4 | "question" TEXT NOT NULL, 5 | "description" TEXT NOT NULL, 6 | "slug" TEXT NOT NULL, 7 | "closeDate" TIMESTAMP(3), 8 | "resolvedAt" TIMESTAMP(3), 9 | "createdBy" TEXT NOT NULL, 10 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | "updatedAt" TIMESTAMP(3) NOT NULL, 12 | 13 | CONSTRAINT "Market_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- CreateIndex 17 | CREATE UNIQUE INDEX "Market_slug_key" ON "Market"("slug"); 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "Market" ADD CONSTRAINT "Market_createdBy_fkey" FOREIGN KEY ("createdBy") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/web/app/(app)/[username]/page.tsx: -------------------------------------------------------------------------------- 1 | import { UserProfilePage } from '@play-money/users/components/UserProfilePage' 2 | 3 | export default function AppUsernamePage({ 4 | params, 5 | searchParams, 6 | }: { 7 | params: { username: string } 8 | searchParams: { limit?: string; cursor?: string; sort?: string; status?: string } 9 | }) { 10 | return ( 11 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/users/[id]/positions/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { 3 | ApiEndpoints, 4 | createPaginatedResponseSchema, 5 | paginationSchema, 6 | ServerErrorSchema, 7 | } from '@play-money/api-helpers' 8 | import { MarketOptionPositionSchema } from '@play-money/database' 9 | 10 | export default { 11 | get: { 12 | summary: 'Get positions for a user', 13 | parameters: z 14 | .object({ 15 | id: z.string(), 16 | status: z.enum(['active', 'closed', 'all']).optional(), 17 | }) 18 | .merge(paginationSchema), 19 | responses: { 20 | 200: createPaginatedResponseSchema(MarketOptionPositionSchema), 21 | 404: ServerErrorSchema, 22 | 500: ServerErrorSchema, 23 | }, 24 | }, 25 | } as const satisfies ApiEndpoints 26 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/CommentSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { CommentEntityTypeSchema } from '../inputTypeSchemas/CommentEntityTypeSchema' 3 | 4 | ///////////////////////////////////////// 5 | // COMMENT SCHEMA 6 | ///////////////////////////////////////// 7 | 8 | export const CommentSchema = z.object({ 9 | entityType: CommentEntityTypeSchema, 10 | id: z.string().cuid(), 11 | content: z.string().trim().min(1).max(5000), 12 | createdAt: z.coerce.date(), 13 | updatedAt: z.coerce.date().nullable(), 14 | edited: z.boolean(), 15 | authorId: z.string(), 16 | parentId: z.string().nullable(), 17 | hidden: z.boolean(), 18 | entityId: z.string(), 19 | }) 20 | 21 | export type Comment = z.infer 22 | 23 | export default CommentSchema; 24 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/TransactionSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { TransactionTypeSchema } from '../inputTypeSchemas/TransactionTypeSchema' 3 | 4 | ///////////////////////////////////////// 5 | // TRANSACTION SCHEMA 6 | ///////////////////////////////////////// 7 | 8 | export const TransactionSchema = z.object({ 9 | type: TransactionTypeSchema, 10 | id: z.string().cuid(), 11 | initiatorId: z.string().nullable(), 12 | isReverse: z.boolean().nullable(), 13 | reverseOfId: z.string().nullable(), 14 | createdAt: z.coerce.date(), 15 | updatedAt: z.coerce.date(), 16 | batchId: z.string().nullable(), 17 | marketId: z.string().nullable(), 18 | }) 19 | 20 | export type Transaction = z.infer 21 | 22 | export default TransactionSchema; 23 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/NotificationGroupSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | import { NotificationTypeSchema } from '../inputTypeSchemas/NotificationTypeSchema' 3 | 4 | ///////////////////////////////////////// 5 | // NOTIFICATION GROUP SCHEMA 6 | ///////////////////////////////////////// 7 | 8 | export const NotificationGroupSchema = z.object({ 9 | type: NotificationTypeSchema, 10 | id: z.string().cuid(), 11 | recipientId: z.string(), 12 | count: z.number().int(), 13 | lastNotificationId: z.string(), 14 | groupWindowEnd: z.coerce.date(), 15 | groupKey: z.string(), 16 | createdAt: z.coerce.date(), 17 | updatedAt: z.coerce.date(), 18 | }) 19 | 20 | export type NotificationGroup = z.infer 21 | 22 | export default NotificationGroupSchema; 23 | -------------------------------------------------------------------------------- /packages/database/scripts/run.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process') 2 | const path = require('path') 3 | 4 | // Get the script name from the arguments 5 | const scriptName = process.argv[2] 6 | 7 | if (!scriptName) { 8 | console.error('Please provide a script name as an argument.') 9 | process.exit(1) 10 | } 11 | 12 | // Construct the path to the TypeScript script 13 | const scriptPath = path.join(__dirname, `${scriptName}.ts`) 14 | 15 | // Run the TypeScript script using ts-node 16 | exec(`tsx ${scriptPath}`, (error, stdout, stderr) => { 17 | if (error) { 18 | console.error(`Error executing script: ${error.message}`) 19 | return 20 | } 21 | if (stderr) { 22 | console.error(`Script stderr: ${stderr}`) 23 | return 24 | } 25 | console.log(`Script stdout: ${stdout}`) 26 | }) 27 | -------------------------------------------------------------------------------- /packages/notifications/components/NotificationItem.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import React from 'react' 3 | import { mockNotification } from '@play-money/database/mocks' 4 | import { NotificationItem } from './NotificationItem' 5 | 6 | const meta = { 7 | component: NotificationItem, 8 | tags: ['autodocs'], 9 | decorators: [ 10 | (Story) => ( 11 |
12 | 13 |
14 | ), 15 | ], 16 | } satisfies Meta 17 | 18 | export default meta 19 | type Story = StoryObj 20 | 21 | export const Default: Story = { 22 | args: { 23 | notification: mockNotification() as unknown as Story['args']['notification'], 24 | unread: true, 25 | count: 1, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /packages/ui/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as LabelPrimitive from '@radix-ui/react-label' 4 | import { cva, type VariantProps } from 'class-variance-authority' 5 | import * as React from 'react' 6 | import { cn } from '../../lib/utils' 7 | 8 | const labelVariants = cva('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70') 9 | 10 | const Label = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef & VariantProps 13 | >(({ className, ...props }, ref) => ( 14 | 15 | )) 16 | Label.displayName = LabelPrimitive.Root.displayName 17 | 18 | export { Label } 19 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/markets/[id]/graph/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the graph for a market', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.array( 11 | z.object({ 12 | startAt: z.date(), 13 | endAt: z.date(), 14 | options: z.array( 15 | z.object({ 16 | id: z.string(), 17 | probability: z.number(), 18 | }) 19 | ), 20 | }) 21 | ), 22 | }), 23 | 404: ServerErrorSchema, 24 | 500: ServerErrorSchema, 25 | }, 26 | }, 27 | } as const satisfies ApiEndpoints 28 | -------------------------------------------------------------------------------- /packages/markets/lib/getUniqueLiquidityProviderIds.tsx: -------------------------------------------------------------------------------- 1 | import db from '@play-money/database' 2 | 3 | export async function getUniqueLiquidityProviderIds( 4 | marketId: string, 5 | ignoreIds: Array = [] 6 | ): Promise { 7 | const result = await db.transaction.findMany({ 8 | where: { 9 | type: { 10 | in: ['LIQUIDITY_DEPOSIT', 'LIQUIDITY_INITIALIZE'], 11 | }, 12 | marketId, 13 | isReverse: null, 14 | }, 15 | select: { 16 | initiatorId: true, 17 | }, 18 | distinct: ['initiatorId'], 19 | }) 20 | 21 | const uniqueProviderIds = result 22 | .map((transaction) => transaction.initiatorId) 23 | .filter((userId): userId is string => userId != null && !ignoreIds.includes(userId)) 24 | 25 | return uniqueProviderIds 26 | } 27 | -------------------------------------------------------------------------------- /apps/api/app/api/v1/lists/[id]/graph/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ApiEndpoints, ServerErrorSchema } from '@play-money/api-helpers' 3 | 4 | export default { 5 | get: { 6 | summary: 'Get the graph for a List of Markets', 7 | parameters: z.object({ id: z.string() }), 8 | responses: { 9 | 200: z.object({ 10 | data: z.array( 11 | z.object({ 12 | startAt: z.date(), 13 | endAt: z.date(), 14 | markets: z.array( 15 | z.object({ 16 | id: z.string(), 17 | probability: z.number(), 18 | }) 19 | ), 20 | }) 21 | ), 22 | }), 23 | 404: ServerErrorSchema, 24 | 500: ServerErrorSchema, 25 | }, 26 | }, 27 | } as const satisfies ApiEndpoints 28 | -------------------------------------------------------------------------------- /packages/database/scripts/delete-old-transactions.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import db from '../prisma' 3 | 4 | // TODO: Cascade deletes, at least for transactions->transactionItems 5 | 6 | async function main() { 7 | try { 8 | await db.$transaction([db.transactionItem.deleteMany({}), db.transaction.deleteMany({}), db.account.deleteMany({})]) 9 | 10 | console.log(`Market ${marketId} deleted`) 11 | } catch (fetchError) { 12 | const error = fetchError as Error 13 | console.error(`An error occurred while fetching users: ${error.message}`) 14 | } finally { 15 | await db.$disconnect() 16 | console.log('Database connection closed.') 17 | } 18 | } 19 | 20 | main().catch((e) => { 21 | const error = e as Error 22 | console.error(`Unexpected error: ${error.message}`) 23 | process.exit(1) 24 | }) 25 | -------------------------------------------------------------------------------- /packages/finance/lib/formatCurrency.ts: -------------------------------------------------------------------------------- 1 | export function formatCurrency(value: number, currencySymbol: string, decimals = 0): string { 2 | const options = { 3 | style: 'currency', 4 | currency: 'USD', 5 | currencyDisplay: 'symbol', 6 | minimumFractionDigits: decimals, 7 | maximumFractionDigits: decimals, 8 | } as const 9 | 10 | return new Intl.NumberFormat('en-US', options).format(value).replace('$', currencySymbol) 11 | } 12 | 13 | export function formatNumber(fullNumber: number): string { 14 | const num = Math.round(fullNumber) 15 | if (num >= 1000000) { 16 | return (num / 1000000).toFixed(num % 1000000 === 0 ? 0 : 1) + 'M' 17 | } else if (num >= 1000) { 18 | return (num / 1000).toFixed(num % 1000 === 0 ? 0 : 1) + 'k' 19 | } else { 20 | return num.toString() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/database/zod/inputTypeSchemas/isValidDecimalInput.ts: -------------------------------------------------------------------------------- 1 | import type { Prisma } from '@prisma/client'; 2 | 3 | export const DECIMAL_STRING_REGEX = /^(?:-?Infinity|NaN|-?(?:0[bB][01]+(?:\.[01]+)?(?:[pP][-+]?\d+)?|0[oO][0-7]+(?:\.[0-7]+)?(?:[pP][-+]?\d+)?|0[xX][\da-fA-F]+(?:\.[\da-fA-F]+)?(?:[pP][-+]?\d+)?|(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?))$/; 4 | 5 | export const isValidDecimalInput = 6 | (v?: null | string | number | Prisma.DecimalJsLike): v is string | number | Prisma.DecimalJsLike => { 7 | if (v === undefined || v === null) return false; 8 | return ( 9 | (typeof v === 'object' && 'd' in v && 'e' in v && 's' in v && 'toFixed' in v) || 10 | (typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) || 11 | typeof v === 'number' 12 | ) 13 | }; 14 | 15 | export default isValidDecimalInput; 16 | -------------------------------------------------------------------------------- /packages/referrals/components/ReferralQuestBonusRow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Badge } from '@play-money/ui/badge' 3 | import { InfoTooltip } from '@play-money/ui/info-tooltip' 4 | import { useUser } from '@play-money/users/context/UserContext' 5 | import { isNewlyReferredUser } from '../lib/helpers' 6 | 7 | export function ReferralQuestBonusRow() { 8 | const { user } = useUser() 9 | 10 | return user && isNewlyReferredUser(user) ? ( 11 |
12 |

13 | Plus x2 referral bonus 14 |

15 | 16 | 17 |
18 | ) : null 19 | } 20 | -------------------------------------------------------------------------------- /packages/ui/src/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import * as SeparatorPrimitive from '@radix-ui/react-separator' 4 | import * as React from 'react' 5 | import { cn } from '../../lib/utils' 6 | 7 | const Separator = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => ( 11 | 18 | )) 19 | Separator.displayName = SeparatorPrimitive.Root.displayName 20 | 21 | export { Separator } 22 | -------------------------------------------------------------------------------- /packages/database/migrations/20240618021418_add_market_options/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "MarketOption" ( 3 | "id" TEXT NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "marketId" TEXT NOT NULL, 6 | "currencyCode" "CurrencyCode" NOT NULL, 7 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | "updatedAt" TIMESTAMP(3) NOT NULL, 9 | 10 | CONSTRAINT "MarketOption_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "MarketOption" ADD CONSTRAINT "MarketOption_marketId_fkey" FOREIGN KEY ("marketId") REFERENCES "Market"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "MarketOption" ADD CONSTRAINT "MarketOption_currencyCode_fkey" FOREIGN KEY ("currencyCode") REFERENCES "Currency"("code") ON DELETE RESTRICT ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /packages/comments/components/CommentItem.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import { mockComment, mockUser } from '@play-money/database/mocks' 3 | import { CommentItem } from './CommentItem' 4 | 5 | const meta = { 6 | component: CommentItem, 7 | tags: ['autodocs'], 8 | } satisfies Meta 9 | 10 | export default meta 11 | type Story = StoryObj 12 | 13 | export const Default: Story = { 14 | args: { 15 | activeUserId: 'user-1', 16 | comment: { 17 | ...mockComment(), 18 | author: mockUser(), 19 | reactions: [], 20 | }, 21 | isHighlighted: false, 22 | onEmojiSelect: console.log, 23 | onCreateReply: async () => console.log('create reply'), 24 | onEdit: async () => console.log('edit'), 25 | onDelete: console.log, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /packages/comments/lib/getComment.ts: -------------------------------------------------------------------------------- 1 | import db, { Comment, CommentReaction, User } from '@play-money/database' 2 | import { CommentNotFoundError } from './exceptions' 3 | 4 | export type CommentWithReactions = Comment & { 5 | author: User 6 | reactions: Array< 7 | CommentReaction & { 8 | user: User 9 | } 10 | > 11 | } 12 | 13 | export async function getComment({ id }: { id: string }): Promise { 14 | const comment = await db.comment.findUnique({ 15 | where: { 16 | id, 17 | }, 18 | include: { 19 | author: true, 20 | reactions: { 21 | include: { 22 | user: true, 23 | }, 24 | }, 25 | }, 26 | }) 27 | 28 | if (!comment) { 29 | throw new CommentNotFoundError(`Comment with id "${id}" not found`) 30 | } 31 | 32 | return comment 33 | } 34 | -------------------------------------------------------------------------------- /packages/finance/components/CurrencyDisplay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { formatCurrency, formatNumber } from '@play-money/finance/lib/formatCurrency' 3 | import { cn } from '@play-money/ui/utils' 4 | 5 | export function CurrencyDisplay({ 6 | value, 7 | className, 8 | hasSymbol = true, 9 | isShort = false, 10 | }: { 11 | value: number 12 | className?: string 13 | hasSymbol?: boolean 14 | isShort?: boolean 15 | }) { 16 | const formattedValue = formatCurrency(value, '', 0) 17 | const formattedShort = formatNumber(value) 18 | 19 | return ( 20 | 21 | {hasSymbol ? '¤' : null} 22 | {isShort ? formattedShort : formattedValue} 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /packages/database/zod/modelSchema/AuthAccountSchema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | 3 | ///////////////////////////////////////// 4 | // AUTH ACCOUNT SCHEMA 5 | ///////////////////////////////////////// 6 | 7 | export const AuthAccountSchema = z.object({ 8 | userId: z.string(), 9 | type: z.string(), 10 | provider: z.string(), 11 | providerAccountId: z.string(), 12 | refresh_token: z.string().nullable(), 13 | access_token: z.string().nullable(), 14 | expires_at: z.number().int().nullable(), 15 | token_type: z.string().nullable(), 16 | scope: z.string().nullable(), 17 | id_token: z.string().nullable(), 18 | session_state: z.string().nullable(), 19 | createdAt: z.coerce.date(), 20 | updatedAt: z.coerce.date(), 21 | }) 22 | 23 | export type AuthAccount = z.infer 24 | 25 | export default AuthAccountSchema; 26 | -------------------------------------------------------------------------------- /packages/markets/components/LiquidityBoostDialog.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | import { mockMarket } from '@play-money/database/mocks' 3 | import { LiquidityBoostDialog } from './LiquidityBoostDialog' 4 | 5 | const meta = { 6 | component: LiquidityBoostDialog, 7 | tags: ['autodocs'], 8 | } satisfies Meta 9 | 10 | export default meta 11 | type Story = StoryObj 12 | 13 | export const Default: Story = { 14 | args: { 15 | open: true, 16 | onClose: console.log, 17 | onSuccess: console.log, 18 | market: mockMarket(), 19 | stats: { 20 | totalLiquidity: 1200, 21 | lpUserCount: 2, 22 | traderBonusPayouts: 450, 23 | positions: {}, 24 | earnings: { 25 | traderBonusPayouts: 297, 26 | }, 27 | }, 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /packages/ui/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { cn } from '../../lib/utils' 3 | 4 | export type TextareaProps = React.TextareaHTMLAttributes 5 | 6 | const Textarea = React.forwardRef(({ className, ...props }, ref) => { 7 | return ( 8 |