From 428d72d0e60f5f7ed198b8aae5d97ee95a31557b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sune=20M=C3=B8lgaard=20Laursen?= Date: Mon, 24 Aug 2026 13:16:39 +0200 Subject: [PATCH 1/2] remove polymarket feature --- README.md | 7 +- configServerless.ts | 6 - package.json | 6 +- src/analytics/types.ts | 82 -- .../(home)/(bottomsheet)/predictions/page.tsx | 10 - .../polymarket/events/search/[query]/route.ts | 45 -- .../events/tags/[tagId]/constants.ts | 7 - .../polymarket/events/tags/[tagId]/route.ts | 66 -- .../events/tags/[tagId]/validMarkets.ts | 90 --- .../clobTokenIds/[clobTokenId]/route.ts | 65 -- src/app/api/polymarket/positions/route.ts | 46 -- src/app/api/polymarket/sign/route.ts | 63 -- src/components/Snippet/Tabs/Tabs.tsx | 10 - .../Home/components/WalletSection.tsx | 1 - .../PolygonAssets/TransferModal.tsx | 202 ----- .../Predictions/PolygonAssets/index.tsx | 88 --- src/sections/Predictions/Portal.tsx | 21 - .../Trading/Events/CategoryTabs.tsx | 35 - .../Predictions/Trading/Events/EventCard.tsx | 138 ---- .../Trading/Events/Events.module.css | 18 - .../Trading/Events/MarketDrawer.module.css | 15 - .../Trading/Events/MarketDrawer.tsx | 40 - .../Trading/Events/MarketGraph.tsx | 91 --- .../Trading/Events/MarketRow.module.css | 6 - .../Predictions/Trading/Events/MarketRow.tsx | 97 --- .../Trading/Events/OutcomeButton.tsx | 77 -- .../Predictions/Trading/Events/SearchBar.tsx | 23 - .../Predictions/Trading/Events/index.tsx | 184 ----- .../Predictions/Trading/MarketTabs.tsx | 23 - .../Trading/OrderModal/OrderForm.tsx | 113 --- .../Trading/OrderModal/OrderSummary.tsx | 37 - .../Trading/OrderModal/OrderTypeToggle.tsx | 42 -- .../Predictions/Trading/OrderModal/index.tsx | 289 -------- .../Predictions/Trading/Orders/OrderCard.tsx | 123 --- .../Predictions/Trading/Orders/index.tsx | 91 --- .../Trading/Positions/PositionCard.tsx | 149 ---- .../Trading/Positions/PositionFilters.tsx | 25 - .../Predictions/Trading/Positions/index.tsx | 205 ----- .../WelcomeFlow/components/AllowTokens.tsx | 85 --- .../components/AuthenticateWithPolymarket.tsx | 84 --- .../WelcomeFlow/components/BlockedModal.tsx | 31 - .../components/CreateAccountModal.tsx | 160 ---- .../components/CreateSafeModal.tsx | 85 --- .../Predictions/WelcomeFlow/index.tsx | 65 -- src/sections/Predictions/components/Badge.tsx | 34 - src/sections/Predictions/components/Card.tsx | 26 - .../components/CloseDialogButton.tsx | 9 - .../components/Components.module.css | 14 - .../Predictions/components/EmptyState.tsx | 13 - .../Predictions/components/ErrorState.tsx | 24 - .../Predictions/components/Footer.tsx | 16 - .../Predictions/components/InfoTooltip.tsx | 16 - .../Predictions/components/LoadingState.tsx | 17 - .../Predictions/components/StatDisplay.tsx | 32 - src/sections/Predictions/components/index.ts | 8 - src/sections/Predictions/constants/api.ts | 20 - .../Predictions/constants/categories.ts | 64 -- .../Predictions/constants/polymarket.ts | 16 - src/sections/Predictions/constants/query.ts | 16 - src/sections/Predictions/constants/tokens.ts | 16 - src/sections/Predictions/constants/ui.ts | 14 - .../Predictions/constants/validation.ts | 6 - .../Predictions/hooks/useActiveOrders.ts | 60 -- .../Predictions/hooks/useAddressCopy.ts | 15 - .../Predictions/hooks/useClobClient.ts | 65 -- .../Predictions/hooks/useClobOrder.test.ts | 385 ---------- .../Predictions/hooks/useClobOrder.ts | 162 ---- src/sections/Predictions/hooks/useGeoblock.ts | 81 -- src/sections/Predictions/hooks/useMarkets.ts | 67 -- .../Predictions/hooks/usePolygonBalances.ts | 58 -- .../Predictions/hooks/usePriceHistory.ts | 40 - .../Predictions/hooks/useRedeemPosition.ts | 59 -- .../Predictions/hooks/useRelayClient.ts | 58 -- .../Predictions/hooks/useSafeDeployment.ts | 114 --- .../Predictions/hooks/useSearchEvents.ts | 15 - src/sections/Predictions/hooks/useTickSize.ts | 33 - .../Predictions/hooks/useTokenApprovals.ts | 66 -- .../Predictions/hooks/useTradingSession.ts | 305 -------- .../Predictions/hooks/useUsdcTransfer.ts | 46 -- .../hooks/useUserApiCredentials.ts | 62 -- .../Predictions/hooks/useUserPositions.ts | 87 --- src/sections/Predictions/index.tsx | 83 --- .../Predictions/providers/TradingProvider.tsx | 111 --- .../Predictions/providers/WalletContext.tsx | 27 - .../Predictions/providers/WalletProvider.tsx | 96 --- src/sections/Predictions/providers/index.tsx | 14 - src/sections/Predictions/utils/approvals.ts | 169 ----- src/sections/Predictions/utils/classNames.ts | 2 - src/sections/Predictions/utils/order.ts | 5 - src/sections/Predictions/utils/polling.ts | 9 - src/sections/Predictions/utils/redeem.ts | 106 --- src/sections/Predictions/utils/session.ts | 57 -- src/sections/Predictions/utils/transfer.ts | 31 - src/sections/Predictions/utils/validation.ts | 23 - src/translations/translations/da.ts | 39 - src/translations/translations/en.ts | 39 - src/translations/types.ts | 37 - src/ui/assets/illustrations/predictions.svg | 7 - src/ui/assets/index.ts | 2 - src/widgets/registry.test.ts | 3 +- src/widgets/registry.ts | 7 - yarn.lock | 700 +----------------- 102 files changed, 11 insertions(+), 6841 deletions(-) delete mode 100644 src/app/[locale]/@home/(home)/(bottomsheet)/predictions/page.tsx delete mode 100644 src/app/api/polymarket/events/search/[query]/route.ts delete mode 100644 src/app/api/polymarket/events/tags/[tagId]/constants.ts delete mode 100644 src/app/api/polymarket/events/tags/[tagId]/route.ts delete mode 100644 src/app/api/polymarket/events/tags/[tagId]/validMarkets.ts delete mode 100644 src/app/api/polymarket/markets/clobTokenIds/[clobTokenId]/route.ts delete mode 100644 src/app/api/polymarket/positions/route.ts delete mode 100644 src/app/api/polymarket/sign/route.ts delete mode 100644 src/sections/Predictions/PolygonAssets/TransferModal.tsx delete mode 100644 src/sections/Predictions/PolygonAssets/index.tsx delete mode 100644 src/sections/Predictions/Portal.tsx delete mode 100644 src/sections/Predictions/Trading/Events/CategoryTabs.tsx delete mode 100644 src/sections/Predictions/Trading/Events/EventCard.tsx delete mode 100644 src/sections/Predictions/Trading/Events/Events.module.css delete mode 100644 src/sections/Predictions/Trading/Events/MarketDrawer.module.css delete mode 100644 src/sections/Predictions/Trading/Events/MarketDrawer.tsx delete mode 100644 src/sections/Predictions/Trading/Events/MarketGraph.tsx delete mode 100644 src/sections/Predictions/Trading/Events/MarketRow.module.css delete mode 100644 src/sections/Predictions/Trading/Events/MarketRow.tsx delete mode 100644 src/sections/Predictions/Trading/Events/OutcomeButton.tsx delete mode 100644 src/sections/Predictions/Trading/Events/SearchBar.tsx delete mode 100644 src/sections/Predictions/Trading/Events/index.tsx delete mode 100644 src/sections/Predictions/Trading/MarketTabs.tsx delete mode 100644 src/sections/Predictions/Trading/OrderModal/OrderForm.tsx delete mode 100644 src/sections/Predictions/Trading/OrderModal/OrderSummary.tsx delete mode 100644 src/sections/Predictions/Trading/OrderModal/OrderTypeToggle.tsx delete mode 100644 src/sections/Predictions/Trading/OrderModal/index.tsx delete mode 100644 src/sections/Predictions/Trading/Orders/OrderCard.tsx delete mode 100644 src/sections/Predictions/Trading/Orders/index.tsx delete mode 100644 src/sections/Predictions/Trading/Positions/PositionCard.tsx delete mode 100644 src/sections/Predictions/Trading/Positions/PositionFilters.tsx delete mode 100644 src/sections/Predictions/Trading/Positions/index.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/components/AllowTokens.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/components/AuthenticateWithPolymarket.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/components/BlockedModal.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/components/CreateAccountModal.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/components/CreateSafeModal.tsx delete mode 100644 src/sections/Predictions/WelcomeFlow/index.tsx delete mode 100644 src/sections/Predictions/components/Badge.tsx delete mode 100644 src/sections/Predictions/components/Card.tsx delete mode 100644 src/sections/Predictions/components/CloseDialogButton.tsx delete mode 100644 src/sections/Predictions/components/Components.module.css delete mode 100644 src/sections/Predictions/components/EmptyState.tsx delete mode 100644 src/sections/Predictions/components/ErrorState.tsx delete mode 100644 src/sections/Predictions/components/Footer.tsx delete mode 100644 src/sections/Predictions/components/InfoTooltip.tsx delete mode 100644 src/sections/Predictions/components/LoadingState.tsx delete mode 100644 src/sections/Predictions/components/StatDisplay.tsx delete mode 100644 src/sections/Predictions/components/index.ts delete mode 100644 src/sections/Predictions/constants/api.ts delete mode 100644 src/sections/Predictions/constants/categories.ts delete mode 100644 src/sections/Predictions/constants/polymarket.ts delete mode 100644 src/sections/Predictions/constants/query.ts delete mode 100644 src/sections/Predictions/constants/tokens.ts delete mode 100644 src/sections/Predictions/constants/ui.ts delete mode 100644 src/sections/Predictions/constants/validation.ts delete mode 100644 src/sections/Predictions/hooks/useActiveOrders.ts delete mode 100644 src/sections/Predictions/hooks/useAddressCopy.ts delete mode 100644 src/sections/Predictions/hooks/useClobClient.ts delete mode 100644 src/sections/Predictions/hooks/useClobOrder.test.ts delete mode 100644 src/sections/Predictions/hooks/useClobOrder.ts delete mode 100644 src/sections/Predictions/hooks/useGeoblock.ts delete mode 100644 src/sections/Predictions/hooks/useMarkets.ts delete mode 100644 src/sections/Predictions/hooks/usePolygonBalances.ts delete mode 100644 src/sections/Predictions/hooks/usePriceHistory.ts delete mode 100644 src/sections/Predictions/hooks/useRedeemPosition.ts delete mode 100644 src/sections/Predictions/hooks/useRelayClient.ts delete mode 100644 src/sections/Predictions/hooks/useSafeDeployment.ts delete mode 100644 src/sections/Predictions/hooks/useSearchEvents.ts delete mode 100644 src/sections/Predictions/hooks/useTickSize.ts delete mode 100644 src/sections/Predictions/hooks/useTokenApprovals.ts delete mode 100644 src/sections/Predictions/hooks/useTradingSession.ts delete mode 100644 src/sections/Predictions/hooks/useUsdcTransfer.ts delete mode 100644 src/sections/Predictions/hooks/useUserApiCredentials.ts delete mode 100644 src/sections/Predictions/hooks/useUserPositions.ts delete mode 100644 src/sections/Predictions/index.tsx delete mode 100644 src/sections/Predictions/providers/TradingProvider.tsx delete mode 100644 src/sections/Predictions/providers/WalletContext.tsx delete mode 100644 src/sections/Predictions/providers/WalletProvider.tsx delete mode 100644 src/sections/Predictions/providers/index.tsx delete mode 100644 src/sections/Predictions/utils/approvals.ts delete mode 100644 src/sections/Predictions/utils/classNames.ts delete mode 100644 src/sections/Predictions/utils/order.ts delete mode 100644 src/sections/Predictions/utils/polling.ts delete mode 100644 src/sections/Predictions/utils/redeem.ts delete mode 100644 src/sections/Predictions/utils/session.ts delete mode 100644 src/sections/Predictions/utils/transfer.ts delete mode 100644 src/sections/Predictions/utils/validation.ts delete mode 100644 src/ui/assets/illustrations/predictions.svg diff --git a/README.md b/README.md index ecec620..24466c4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## 1. Project Overview -GoodWallet V2 is a multi-chain, multi-protocol crypto wallet built on Next.js 16. It spans EVM chains plus Solana, Bitcoin, Dogecoin and XRP, with token management via Alchemy, cross-chain swaps via Li.Fi, prediction-market trading via Polymarket, and WalletConnect (Reown) connectivity. It centers on the GoodDollar (G$) UBI ecosystem. +GoodWallet V2 is a multi-chain, multi-protocol crypto wallet built on Next.js 16. It spans EVM chains plus Solana, Bitcoin, Dogecoin and XRP, with token management via Alchemy, cross-chain swaps via Li.Fi, and WalletConnect (Reown) connectivity. It centers on the GoodDollar (G$) UBI ecosystem. ## 2. Core Features @@ -12,8 +12,6 @@ GoodWallet V2 is a multi-chain, multi-protocol crypto wallet built on Next.js 16 Alchemy-based balance tracking, send/receive, QR code transactions, gasless mode - **Swaps** Cross-chain and same-chain swaps via Li.Fi (EVM, Solana, Bitcoin providers) -- **Predictions / Trading** - Polymarket prediction markets with on-chain trading and live charts - **GoodDollar Ecosystem** UBI claims, identity/whitelisting, faucet, referrals - **Authentication** @@ -34,7 +32,6 @@ GoodWallet V2 is a multi-chain, multi-protocol crypto wallet built on Next.js 16 - **Swaps & Bridging**: Li.Fi SDK v4 - **Multi-chain Data**: Alchemy, Tatum - **Connectivity**: WalletConnect v2 via Reown WalletKit -- **Predictions**: Polymarket CLOB / builder-relayer clients - **Authentication**: Web3Auth/Torus social logins, Auth0 passwordless (hCaptcha) - **Analytics**: Sentry, Amplitude @@ -47,7 +44,7 @@ GoodWallet V2 is a multi-chain, multi-protocol crypto wallet built on Next.js 16 - **GoodDollar** UBI claims, identity verification, multi-chain G$ support - **Project Structure** - `/src/app` (App Router, `[locale]` routes, `/api`), `/src/sections` (feature flows: Home, Send, Receive, Swap, Predictions, GoodDollar, Login, WalletConnect, Qr, Options), `/src/chain`, `/src/tokens`, `/src/stores`, `/src/hooks`, `/src/components`, `/src/ui` + `/src/app` (App Router, `[locale]` routes, `/api`), `/src/sections` (feature flows: Home, Send, Receive, Swap, GoodDollar, Login, WalletConnect, Qr, Options), `/src/chain`, `/src/tokens`, `/src/stores`, `/src/hooks`, `/src/components`, `/src/ui` ## 6. Development Tools diff --git a/configServerless.ts b/configServerless.ts index 70a0c5c..c89c2ed 100644 --- a/configServerless.ts +++ b/configServerless.ts @@ -23,12 +23,6 @@ export const registerAddressPostAPIKey = { apiKey: process.env.AIRDROP_API_KEY as string, } -export const polymarketKeys = { - builderApiKey: process.env.POLYMARKET_BUILDER_API_KEY as string, - builderSecret: process.env.POLYMARKET_BUILDER_SECRET as string, - builderPassphrase: process.env.POLYMARKET_BUILDER_PASSPHRASE as string, -} - export const amplitudeConfig = { apiKey: process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY as string, } diff --git a/package.json b/package.json index 37dc2d5..ca345c3 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,6 @@ "@lifi/sdk-provider-solana": "^4.0.2", "@lottiefiles/react-lottie-player": "^3.6.0", "@neondatabase/serverless": "^1.1.0", - "@polymarket/builder-relayer-client": "^0.0.10", - "@polymarket/clob-client": "^5.8.1", "@react-hookz/web": "^25.2.0", "@reown/walletkit": "^1.5.6", "@sentry/nextjs": "^10.56.0", @@ -63,7 +61,6 @@ "hdkey": "^2.1.0", "itertools": "^2.7.1", "jsqr": "^1.4.0", - "lightweight-charts": "^5.2.0", "navigator.locks": "^0.8.8", "next": "^16.2.10", "pino-pretty": "^13.1.3", @@ -109,7 +106,6 @@ "not op_mini all" ], "resolutions": { - "esbuild": "0.25.10", - "@polymarket/builder-signing-sdk": "1.0.0" + "esbuild": "0.25.10" } } diff --git a/src/analytics/types.ts b/src/analytics/types.ts index 563caa8..59b6751 100644 --- a/src/analytics/types.ts +++ b/src/analytics/types.ts @@ -48,22 +48,6 @@ export enum AnalyticsEventTypes { GoodDollarNewsTabSelected = "GoodDollar_News_Tab_Selected", WalletSignedMessageConfirmed = "Wallet_Message_Signed_Confirmed", WalletSignedMessageCancelled = "Wallet_Message_Signed_Cancelled", - PolymarketAuthenticationSucceeded = "Polymarket_Authentication_Succeeded", - PolymarketAuthenticationFailed = "Polymarket_Authentication_Failed", - PolymarketSafeDeploymentSucceeded = "Polymarket_Safe_Deployment_Succeeded", - PolymarketSafeDeploymentFailed = "Polymarket_Safe_Deployment_Failed", - PolymarketAllowTokensSucceeded = "Polymarket_Allow_Tokens_Succeeded", - PolymarketAllowTokensFailed = "Polymarket_Allow_Tokens_Failed", - PolymarketOrderPlacementSucceeded = "Polymarket_Order_Placement_Succeeded", - PolymarketOrderPlacementFailed = "Polymarket_Order_Placement_Failed", - PolymarketWalletFunded = "Polymarket_Wallet_Funded", - PolymarketWithdraw = "Polymarket_Withdraw", - PolymarketWithdrawFailed = "Polymarket_Withdraw_Failed", - PolymarketRedeemPositionSucceeded = "Polymarket_Redeem_Position_Succeeded", - PolymarketRedeemPositionFailed = "Polymarket_Redeem_Position_Failed", - PredictionsMarketsTabSelected = "Predictions_Markets_Tab_Selected", - PredictionsOpenOrdersTabSelected = "Predictions_Open_Orders_Tab_Selected", - PredictionsPositionsTabSelected = "Predictions_Positions_Tab_Selected", } export enum AvailableMethodsNames { @@ -277,69 +261,3 @@ export type AnalyticsEvent = chainName: string methodName: AvailableMethods } - | { - type: AnalyticsEventTypes.PolymarketAuthenticationSucceeded - } - | { - type: AnalyticsEventTypes.PolymarketAuthenticationFailed - errorReason: string - } - | { - type: AnalyticsEventTypes.PolymarketSafeDeploymentSucceeded - safeAddress: string - } - | { - type: AnalyticsEventTypes.PolymarketSafeDeploymentFailed - errorReason: string - } - | { - type: AnalyticsEventTypes.PolymarketAllowTokensSucceeded - } - | { - type: AnalyticsEventTypes.PolymarketAllowTokensFailed - errorReason: string - } - | { - type: AnalyticsEventTypes.PolymarketOrderPlacementSucceeded - orderType: "market" | "limit" - size: number - priceUsdce: number - totalCostUsdce: number - side: "BUY" | "SELL" - tokenId: string - marketTitle: string - } - | { - type: AnalyticsEventTypes.PolymarketOrderPlacementFailed - errorReason: string - } - | { - type: AnalyticsEventTypes.PolymarketWalletFunded - usdceAmount: number - } - | { - type: AnalyticsEventTypes.PolymarketWithdraw - usdceAmount: number - } - | { - type: AnalyticsEventTypes.PolymarketWithdrawFailed - usdceAmount: number - errorReason: string - } - | { - type: AnalyticsEventTypes.PolymarketRedeemPositionSucceeded - conditionId: string - } - | { - type: AnalyticsEventTypes.PolymarketRedeemPositionFailed - errorReason: string - } - | { - type: AnalyticsEventTypes.PredictionsMarketsTabSelected - } - | { - type: AnalyticsEventTypes.PredictionsOpenOrdersTabSelected - } - | { - type: AnalyticsEventTypes.PredictionsPositionsTabSelected - } diff --git a/src/app/[locale]/@home/(home)/(bottomsheet)/predictions/page.tsx b/src/app/[locale]/@home/(home)/(bottomsheet)/predictions/page.tsx deleted file mode 100644 index a5e9bf0..0000000 --- a/src/app/[locale]/@home/(home)/(bottomsheet)/predictions/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import PredictionsView from "@/sections/Predictions" -import Providers from "@/sections/Predictions/providers" - -export default function Page() { - return ( - - - - ) -} diff --git a/src/app/api/polymarket/events/search/[query]/route.ts b/src/app/api/polymarket/events/search/[query]/route.ts deleted file mode 100644 index 92c3eaa..0000000 --- a/src/app/api/polymarket/events/search/[query]/route.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server" - -import { GAMMA_API_URL } from "../../tags/[tagId]/constants" -import { validateEvents } from "../../tags/[tagId]/validMarkets" - -export const dynamic = "force-static" -export const revalidate = 60 - -const headers = { - headers: { - "Cache-Control": `public, max-age=${revalidate}`, - }, -} - -export async function GET( - _request: NextRequest, - { params }: { params: Promise<{ query: string }> }, -) { - const { query } = await params - try { - const searchParams = new URLSearchParams({ - q: query, - limit_per_type: "20", - type: "events", - events_status: "active", - optimized: "false", - }) - const url = `${GAMMA_API_URL}/search-v2?${searchParams}` - const response = await fetch(url) - - if (!response.ok) { - console.error(await response.json()) - return NextResponse.json( - { error: "Failed to fetch events" }, - { status: response.status }, - ) - } - const events = await response.json() - const data = validateEvents(events.events) - return NextResponse.json(data, headers) - } catch (error) { - console.error(error) - return NextResponse.json({ error: "Failed to fetch events" }) - } -} diff --git a/src/app/api/polymarket/events/tags/[tagId]/constants.ts b/src/app/api/polymarket/events/tags/[tagId]/constants.ts deleted file mode 100644 index 3821c3d..0000000 --- a/src/app/api/polymarket/events/tags/[tagId]/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Polymarket API URLs -export const RELAYER_URL = "https://relayer-v2.polymarket.com/" -export const CLOB_API_URL = "https://clob.polymarket.com" -export const GEOBLOCK_API_URL = "https://polymarket.com/api/geoblock" -export const GAMMA_API_URL = "https://gamma-api.polymarket.com" -export const POLYMARKET_PROFILE_URL = (address: string) => - `https://polymarket.com/${address}` diff --git a/src/app/api/polymarket/events/tags/[tagId]/route.ts b/src/app/api/polymarket/events/tags/[tagId]/route.ts deleted file mode 100644 index 24e56fb..0000000 --- a/src/app/api/polymarket/events/tags/[tagId]/route.ts +++ /dev/null @@ -1,66 +0,0 @@ -// biome-ignore-all lint/suspicious/noExplicitAny: need to cast the window -import { type NextRequest, NextResponse } from "next/server" - -import { GAMMA_API_URL } from "./constants" -import { validateEvents } from "./validMarkets" - -export const dynamic = "force-static" -export const revalidate = 60 - -const headers = { - headers: { - "Cache-Control": `public, max-age=${revalidate}`, - }, -} - -export async function GET( - request: NextRequest, - { - params, - }: { - params: Promise<{ tagId: string | null }> - }, -) { - const { tagId } = await params - const searchParams = request.nextUrl.searchParams - const limit = searchParams.get("limit") || "50" - - try { - const fetchLimit = parseInt(limit) - - let url = `${GAMMA_API_URL}/events?active=true&archived=false&closed=false&order=volume24hr&ascending=false&limit=${fetchLimit}&offset=0` - - if (tagId && tagId !== "0") { - url += `&tag_id=${tagId}&related_tags=true` - } - - const response = await fetch(url, { - headers: { "Content-Type": "application/json" }, - }) - - if (!response.ok) { - console.error("Gamma API error:", response.status) - throw new Error(`Gamma API error: ${response.status}`) - } - - const events = await response.json() - if (!Array.isArray(events)) { - console.error("Invalid response structure:", events) - return NextResponse.json( - { error: "Invalid API response" }, - { status: 500 }, - ) - } - - return NextResponse.json(validateEvents(events), headers) - } catch (error) { - console.error("Error fetching markets:", error) - return NextResponse.json( - { - error: - error instanceof Error ? error.message : "Failed to fetch markets", - }, - { status: 500 }, - ) - } -} diff --git a/src/app/api/polymarket/events/tags/[tagId]/validMarkets.ts b/src/app/api/polymarket/events/tags/[tagId]/validMarkets.ts deleted file mode 100644 index 7498d44..0000000 --- a/src/app/api/polymarket/events/tags/[tagId]/validMarkets.ts +++ /dev/null @@ -1,90 +0,0 @@ -// biome-ignore-all lint/suspicious/noExplicitAny: need to cast the window -import type { - PolymarketEvent, - PolymarketMarket, -} from "@/sections/Predictions/hooks/useMarkets" - -const MIN_LIQUIDITY_USD = 1000 - -export const validateEvents = (events: any[]): PolymarketEvent[] => { - const validEvents = [] - for (const event of events) { - if (event.ended || event.closed || !event.active) { - continue - } - const validMarkets = filterValidMarkets(event) - if (validMarkets.length === 0) { - continue - } - - validEvents.push({ - id: event.id, - volume: event.volume, - title: event.title, - image: event.image, - markets: validMarkets, - }) - } - return validEvents -} - -const filterValidMarkets = (event: any) => { - const validMarkets = [] - for (const market of event.markets) { - if (!isMarketValid(market)) { - continue - } - validMarkets.push({ - description: market.description, - id: market.id, - question: market.question, - active: market.active, - closed: market.closed, - orderMinSize: market.orderMinSize, - icon: market.icon, - volume: market.volume, - outcomes: market.outcomes ? JSON.parse(market.outcomes) : [], - clobTokenIds: market.clobTokenIds ? JSON.parse(market.clobTokenIds) : [], - acceptingOrders: market.acceptingOrders, - negRisk: market.negRisk, - groupItemTitle: market.groupItemTitle, - }) - } - return validMarkets -} - -const isMarketValid = (market: PolymarketMarket) => { - return ( - !market.closed && - market.acceptingOrders && - market.clobTokenIds && - validOutcomePrices(market) && - validLiquidity(market) - ) -} - -const validOutcomePrices = (market: PolymarketMarket) => { - let { outcomePrices } = market - if (!outcomePrices) { - return false - } - if (typeof outcomePrices === "string") { - outcomePrices = JSON.parse(outcomePrices) - } - - if (!Array.isArray(outcomePrices)) { - return false - } - - return outcomePrices.some( - (price: string) => parseFloat(price) >= 0.05 && parseFloat(price) <= 0.95, - ) -} - -const validLiquidity = (market: PolymarketMarket) => { - const { liquidity } = market - if (liquidity === undefined) { - return false - } - return parseFloat(String(liquidity)) > MIN_LIQUIDITY_USD -} diff --git a/src/app/api/polymarket/markets/clobTokenIds/[clobTokenId]/route.ts b/src/app/api/polymarket/markets/clobTokenIds/[clobTokenId]/route.ts deleted file mode 100644 index dd5f057..0000000 --- a/src/app/api/polymarket/markets/clobTokenIds/[clobTokenId]/route.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server" - -export const dynamic = "force-static" -export const revalidate = 43200 // 12 hours - -const GAMMA_API = "https://gamma-api.polymarket.com" -const headers = { - headers: { - "Cache-Control": `public, max-age=${revalidate}`, - }, -} - -export async function GET( - _request: NextRequest, - { params }: { params: Promise<{ clobTokenId: string }> }, -) { - const clobTokenId = (await params).clobTokenId - - if (!clobTokenId) { - return NextResponse.json( - { error: "clobTokenId parameter is required" }, - headers, - ) - } - - try { - const response = await fetch( - `${GAMMA_API}/markets?clob_token_ids=${clobTokenId}`, - { - headers: { "Content-Type": "application/json" }, - next: { revalidate: 300 }, - }, - ) - - if (!response.ok) { - console.error("Gamma API error:", response.status) - throw new Error(`Gamma API error: ${response.status}`) - } - - const markets = await response.json() - if ( - !markets || - !Array.isArray(markets) || - markets.length === 0 || - markets[0] == null - ) { - throw new Error("Market not found") - } - - return NextResponse.json( - { - outcomes: markets[0].outcomes, - clobTokenIds: markets[0].clobTokenIds, - question: markets[0].question, - }, - headers, - ) - } catch (error) { - console.error(error) - return NextResponse.json( - { error: "Failed to fetch market by token" }, - { status: 500 }, - ) - } -} diff --git a/src/app/api/polymarket/positions/route.ts b/src/app/api/polymarket/positions/route.ts deleted file mode 100644 index 795d620..0000000 --- a/src/app/api/polymarket/positions/route.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server" - -const POLYMARKET_DATA_API = "https://data-api.polymarket.com" - -export async function GET(request: NextRequest) { - const searchParams = request.nextUrl.searchParams - const user = searchParams.get("user") - - if (!user) { - return NextResponse.json( - { error: "User address is required" }, - { status: 400 }, - ) - } - - try { - const params = new URLSearchParams({ - user, - sizeThreshold: "0.01", - limit: "500", - }) - - const response = await fetch(`${POLYMARKET_DATA_API}/positions?${params}`, { - headers: { "Content-Type": "application/json" }, - cache: "no-store", - }) - - if (!response.ok) { - throw new Error(`Polymarket API error: ${response.status}`) - } - - const data = await response.json() - - return NextResponse.json(data, { - headers: { - "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0", - }, - }) - } catch (error) { - console.error("Position fetch error:", error) - return NextResponse.json( - { error: "Failed to fetch positions" }, - { status: 500 }, - ) - } -} diff --git a/src/app/api/polymarket/sign/route.ts b/src/app/api/polymarket/sign/route.ts deleted file mode 100644 index 5198124..0000000 --- a/src/app/api/polymarket/sign/route.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { type NextRequest, NextResponse } from "next/server" -import { - type BuilderApiKeyCreds, - buildHmacSignature, -} from "@polymarket/builder-signing-sdk" - -import { polymarketKeys } from "@/configServerless" - -const BUILDER_CREDENTIALS: BuilderApiKeyCreds = { - key: polymarketKeys.builderApiKey, - secret: polymarketKeys.builderSecret, - passphrase: polymarketKeys.builderPassphrase, -} - -// This route is used to sign messages for builder authentication (RelayClient) or order attribution (ClobClient) - -export async function POST(request: NextRequest) { - try { - const body = await request.json() - const { method, path, body: requestBody } = body - - if ( - !BUILDER_CREDENTIALS.key || - !BUILDER_CREDENTIALS.secret || - !BUILDER_CREDENTIALS.passphrase - ) { - return NextResponse.json( - { error: "Builder credentials not configured" }, - { status: 500 }, - ) - } - - if (!method || !path) { - return NextResponse.json( - { error: "Missing required parameters: method, path" }, - { status: 400 }, - ) - } - - const sigTimestamp = Date.now().toString() - - const signature = buildHmacSignature( - BUILDER_CREDENTIALS.secret, - parseInt(sigTimestamp), - method, - path, - requestBody || "", - ) - - return NextResponse.json({ - POLY_BUILDER_SIGNATURE: signature, - POLY_BUILDER_TIMESTAMP: sigTimestamp, - POLY_BUILDER_API_KEY: BUILDER_CREDENTIALS.key, - POLY_BUILDER_PASSPHRASE: BUILDER_CREDENTIALS.passphrase, - }) - } catch (error) { - console.error("Signing error:", error) - return NextResponse.json( - { error: "Failed to sign message" }, - { status: 500 }, - ) - } -} diff --git a/src/components/Snippet/Tabs/Tabs.tsx b/src/components/Snippet/Tabs/Tabs.tsx index 3f287ec..c9d3825 100644 --- a/src/components/Snippet/Tabs/Tabs.tsx +++ b/src/components/Snippet/Tabs/Tabs.tsx @@ -28,10 +28,6 @@ type TabProps = PropsWithChildren<{ tabPage: "gooddollar" tabId: "claim" | "inviteRewards" | "news" } - | { - tabPage: "predictions" - tabId: "markets" | "openOrders" | "positions" - } ) const eventType = { @@ -42,9 +38,6 @@ const eventType = { privacy: AnalyticsEventTypes.PrivacyPolicyTabSelected, claim: AnalyticsEventTypes.GoodDollarClaimTabSelected, inviteRewards: AnalyticsEventTypes.GoodDollarInviteRewardsTabSelected, - markets: AnalyticsEventTypes.PredictionsMarketsTabSelected, - openOrders: AnalyticsEventTypes.PredictionsOpenOrdersTabSelected, - positions: AnalyticsEventTypes.PredictionsPositionsTabSelected, } as const export const Tab = ({ children }: TabProps) => children @@ -74,7 +67,6 @@ export const Tabs = ({ children, activeTab }: TabsProps) => { legal: legalTranslations, home: homeTranslations, gooddollar: goodDollarTransalations, - predictions: predictionsTranslations, } = translations const getLabelFromTabPage = (tab: ReactElement) => { @@ -86,8 +78,6 @@ export const Tabs = ({ children, activeTab }: TabsProps) => { return legalTranslations[tabId] case "gooddollar": return goodDollarTransalations[tabId] - case "predictions": - return predictionsTranslations[tabId] default: throw new Error("Unknown tab page") } diff --git a/src/sections/Home/components/WalletSection.tsx b/src/sections/Home/components/WalletSection.tsx index f7869dc..688dc47 100644 --- a/src/sections/Home/components/WalletSection.tsx +++ b/src/sections/Home/components/WalletSection.tsx @@ -196,7 +196,6 @@ export default function WalletSection({ send: homeTranslations.send, receive: homeTranslations.receive, swap: homeTranslations.swap, - predictions: homeTranslations.predictions, walletconnect: homeTranslations.walletConnect, } const dashboardActions = [...coreDashboardActions, ...widgetDashboardActions] diff --git a/src/sections/Predictions/PolygonAssets/TransferModal.tsx b/src/sections/Predictions/PolygonAssets/TransferModal.tsx deleted file mode 100644 index 9aef328..0000000 --- a/src/sections/Predictions/PolygonAssets/TransferModal.tsx +++ /dev/null @@ -1,202 +0,0 @@ -"use client" - -import { useEffect, useRef, useState } from "react" -import { Button } from "ui" -import { parseUnits } from "viem" - -import { AnalyticsEventTypes } from "@/analytics/types.ts" -import { useAnalytics } from "@/analytics/useAnalytics.ts" - -import CloseDialogButton from "../components/CloseDialogButton.tsx" -import { USDC_E_DECIMALS } from "../constants/tokens" -import { SUCCESS_STYLES } from "../constants/ui" -import usePolygonBalances from "../hooks/usePolygonBalances" -import useUsdcTransfer from "../hooks/useUsdcTransfer" -import Portal from "../Portal" -import { useTrading } from "../providers/TradingProvider" -import { useWallet } from "../providers/WalletContext.tsx" -import { cn } from "../utils/classNames.ts" - -type TransferModalProps = { - isOpen: boolean - onClose: () => void -} - -export default function TransferModal({ isOpen, onClose }: TransferModalProps) { - const [amount, setAmount] = useState("") - const [showSuccess, setShowSuccess] = useState(false) - - const modalRef = useRef(null) - const { relayClient, safeAddress } = useTrading() - const { isTransferring, error, transferUsdc } = useUsdcTransfer() - const { eoaAddress } = useWallet() - const { formattedUsdcBalance, rawUsdcBalance } = - usePolygonBalances(safeAddress) - const { captureEvent } = useAnalytics() - - useEffect(() => { - if (isOpen) { - setAmount("") - setShowSuccess(false) - } - }, [isOpen]) - - useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === "Escape" && isOpen) onClose() - } - document.addEventListener("keydown", handleEscape) - return () => document.removeEventListener("keydown", handleEscape) - }, [isOpen, onClose]) - - useEffect(() => { - if (isOpen) { - document.body.style.overflow = "hidden" - } else { - document.body.style.overflow = "unset" - } - return () => { - document.body.style.overflow = "unset" - } - }, [isOpen]) - - if (!isOpen) return null - - const handleTransfer = async () => { - if (!relayClient || !amount) return - - try { - const amountBigInt = parseUnits(amount, USDC_E_DECIMALS) - - await transferUsdc(relayClient, { - recipient: eoaAddress as `0x${string}`, - amount: amountBigInt, - }) - captureEvent({ - type: AnalyticsEventTypes.PolymarketWithdraw, - usdceAmount: Number(amount), - }) - setShowSuccess(true) - setTimeout(() => onClose(), 2000) - } catch (err) { - captureEvent({ - type: AnalyticsEventTypes.PolymarketWithdrawFailed, - usdceAmount: Number(amount), - errorReason: err instanceof Error ? err.message : "Unknown error", - }) - console.error("Transfer failed:", err) - } - } - - const handleSendMax = () => { - if (rawUsdcBalance) { - setAmount((Number(rawUsdcBalance) / 10 ** USDC_E_DECIMALS).toString()) - } - } - - const handleBackdropClick = (e: React.MouseEvent) => { - if (e.target === e.currentTarget) onClose() - } - - return ( - -
-
- {/* Header */} -
-

Withdraw

- -
- - {/* Success Message */} - {showSuccess && ( -
-

- Transfer successful! -

-
- )} - - {/* Error Message */} - {error && ( -
-

- {error.message} -

-
- )} - - {/* Balance Display */} -
-

- Available Balance -

-

{formattedUsdcBalance} USDC.e

-
- - {/* Recipient Input */} -
- - -
- - {/* Amount Input */} -
- -
- setAmount(e.target.value)} - placeholder="0.00" - className="w-full px-4 py-2 pr-16 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:border-[var(--brand-primary)] text-white" - disabled={isTransferring} - /> -
-
-
-
- - {/* Send Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/PolygonAssets/index.tsx b/src/sections/Predictions/PolygonAssets/index.tsx deleted file mode 100644 index 63cee80..0000000 --- a/src/sections/Predictions/PolygonAssets/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -"use client" - -import { useState } from "react" -import { Button } from "ui" - -import { truncateString } from "@/components/Utils/format" -import { openSwapOverlay } from "@/sections/Swap/swapOverlayStore" - -import Card from "../components/Card" -import { POLYGON_CHAIN_ID } from "../constants/polymarket" -import { USDC_E_CONTRACT_ADDRESS } from "../constants/tokens" -import useAddressCopy from "../hooks/useAddressCopy" -import usePolygonBalances from "../hooks/usePolygonBalances" -import useSafeDeployment from "../hooks/useSafeDeployment" -import { useWallet } from "../providers/WalletContext" -import TransferModal from "./TransferModal" - -export default function PolygonAssets() { - const [isTransferModalOpen, setIsTransferModalOpen] = useState(false) - const { eoaAddress } = useWallet() - const { derivedSafeAddressFromEoa } = useSafeDeployment(eoaAddress) - const { formattedUsdcBalance, isLoading } = usePolygonBalances( - derivedSafeAddressFromEoa, - ) - const { copied: copiedSafe, copyAddress: copySafeAddress } = useAddressCopy( - derivedSafeAddressFromEoa || null, - ) - - if (!derivedSafeAddressFromEoa) { - return null - } - - if (isLoading) { - return ( - -

Loading balance...

-
- ) - } - - const handleStartSendingFlow = () => { - openSwapOverlay({ - toChainId: POLYGON_CHAIN_ID, - toTokenAddress: USDC_E_CONTRACT_ADDRESS, - toAddress: derivedSafeAddressFromEoa, - }) - } - - return ( - <> - -
- USDC.e -
- -

{formattedUsdcBalance}

- -
-
-
- - setIsTransferModalOpen(false)} - /> - - ) -} diff --git a/src/sections/Predictions/Portal.tsx b/src/sections/Predictions/Portal.tsx deleted file mode 100644 index 48cf2eb..0000000 --- a/src/sections/Predictions/Portal.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client" - -import { useEffect, useState } from "react" -import { createPortal } from "react-dom" - -type PortalProps = { - children: React.ReactNode -} - -export default function Portal({ children }: PortalProps) { - const [mounted, setMounted] = useState(false) - - useEffect(() => { - setMounted(true) - return () => setMounted(false) - }, []) - - if (!mounted) return null - - return createPortal(children, document.body) -} diff --git a/src/sections/Predictions/Trading/Events/CategoryTabs.tsx b/src/sections/Predictions/Trading/Events/CategoryTabs.tsx deleted file mode 100644 index 551b07b..0000000 --- a/src/sections/Predictions/Trading/Events/CategoryTabs.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client" - -import { useRef } from "react" -import { Button } from "ui" - -import { CATEGORIES, type CategoryId } from "../../constants/categories.ts" -import styles from "./Events.module.css" - -interface CategoryTabsProps { - activeCategory: CategoryId - onCategoryChange: (categoryId: CategoryId) => void -} - -export default function CategoryTabs({ - activeCategory, - onCategoryChange, -}: CategoryTabsProps) { - const dashboardRootDiv = useRef(null) - - return ( -
-
- {CATEGORIES.map((category) => ( -
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/EventCard.tsx b/src/sections/Predictions/Trading/Events/EventCard.tsx deleted file mode 100644 index d0db8bf..0000000 --- a/src/sections/Predictions/Trading/Events/EventCard.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { useRef, useState } from "react" -import Image from "next/image" -import { Side } from "@polymarket/clob-client" -import useSWR from "swr" -import { Button } from "ui" - -import { formatTokenPrice } from "@/components/Utils/tokenFormat.ts" -import useInViewport from "@/utils/isVisible.tsx" - -import Card from "../../components/Card.tsx" -import { QUERY_REFETCH_INTERVALS } from "../../constants/query.ts" -import type { - PolymarketEvent, - PolymarketMarket, -} from "../../hooks/useMarkets.ts" -import { useTrading } from "../../providers/TradingProvider.tsx" -import { cn } from "../../utils/classNames.ts" -import MarketRow from "./MarketRow.tsx" - -export interface EventCardProps { - event: PolymarketEvent - disabled?: boolean - onOutcomeClick: ( - marketTitle: string, - outcome: string, - price: number, - tokenId: string, - negRisk: boolean, - orderMinSize: number, - ) => void - onMarketClick: (market: PolymarketMarket) => void -} - -export type RealtimePrices = Record - -const INITIAL_VISIBLE = 2 - -export default function EventCard({ - event, - disabled = false, - onOutcomeClick, - onMarketClick, -}: EventCardProps) { - const ref = useRef(null) - const isVisible = useInViewport(ref) - const { clobClient } = useTrading() - const [expanded, setExpanded] = useState(false) - - // Fetch real-time prices for all token IDs across all markets in this event - const { data: realtimePrices = {} } = useSWR( - clobClient && isVisible ? ["realtime-prices-event", event.id] : null, - async () => { - if (!clobClient) return {} - const allTokenIds = event.markets.flatMap((m) => m.clobTokenIds) - const results = await Promise.all( - allTokenIds.map(async (tokenId: string) => { - const askResponse = await clobClient.getPrice(tokenId, Side.SELL) - const bidPrice = parseFloat(askResponse.price) - if (isNaN(bidPrice) || bidPrice <= 0 || bidPrice >= 1) return null - return { tokenId, bidPrice } - }), - ) - return results.reduce((acc, result) => { - if (result) acc[result.tokenId] = result.bidPrice - return acc - }, {}) - }, - { - refreshInterval: QUERY_REFETCH_INTERVALS.REALTIME_PRICES_MARKET, - }, - ) - - const visibleMarkets = expanded - ? event.markets - : event.markets.slice(0, INITIAL_VISIBLE) - const hiddenCount = event.markets.length - INITIAL_VISIBLE - - return ( - -
- {/* Event Header */} -
- {event.image && ( - - )} -

- {event.title || event.markets[0].question} -

-
- - {/* Market Outcome Rows */} -
- {visibleMarkets.map((market, idx) => ( - onMarketClick(market)} - realtimePrices={realtimePrices} - disabled={disabled} - onOutcomeClick={onOutcomeClick} - showDivider={idx > 0} - /> - ))} -
- - {/* Footer */} -
0 ? "justify-between" : "justify-start", - )} - > - - {formatTokenPrice(event.volume, { - currency: "USD", - usdExchangeRate: 1, - symbol: "USDCe", - })}{" "} - Vol. - - {hiddenCount > 0 && ( -
-
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/Events.module.css b/src/sections/Predictions/Trading/Events/Events.module.css deleted file mode 100644 index 2bf4ba5..0000000 --- a/src/sections/Predictions/Trading/Events/Events.module.css +++ /dev/null @@ -1,18 +0,0 @@ -.container { - -ms-overflow-style: none; - display: flex; - flex-wrap: nowrap; - gap: 8px; - justify-content: space-around; - margin-left: -24px; - margin-right: -24px; - max-width: 100vw; - min-width: fit-content; - min-width: -webkit-fill-available; - overflow: auto; - padding-bottom: 8px; - padding-inline: 24px; - scrollbar-width: none; - width: 100%; - scroll-snap-type: x mandatory; -} diff --git a/src/sections/Predictions/Trading/Events/MarketDrawer.module.css b/src/sections/Predictions/Trading/Events/MarketDrawer.module.css deleted file mode 100644 index c61a4c2..0000000 --- a/src/sections/Predictions/Trading/Events/MarketDrawer.module.css +++ /dev/null @@ -1,15 +0,0 @@ -.container { - display: flex; - flex-direction: column; - padding: 0 4px; -} - -.title { - padding-bottom: 16px; -} - -.description { - padding-top: 16px; - margin: 0; - line-height: 1.5; -} diff --git a/src/sections/Predictions/Trading/Events/MarketDrawer.tsx b/src/sections/Predictions/Trading/Events/MarketDrawer.tsx deleted file mode 100644 index 5ebf12e..0000000 --- a/src/sections/Predictions/Trading/Events/MarketDrawer.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useState } from "react" -import { Button, Text } from "ui" - -import type { PolymarketMarket } from "../../hooks/useMarkets" -import styles from "./MarketDrawer.module.css" -import { MarketGraph } from "./MarketGraph" - -const DESCRIPTION_CHAR_LIMIT = 200 - -export const MarketDrawer = ({ market }: { market: PolymarketMarket }) => { - const [isExpanded, setIsExpanded] = useState(false) - const description = market.description || "" - const shouldTruncate = description.length > DESCRIPTION_CHAR_LIMIT - - const displayedDescription = - shouldTruncate && !isExpanded - ? `${description.slice(0, DESCRIPTION_CHAR_LIMIT)}...` - : description - - return ( -
- - {market.question} - - -
- - {displayedDescription} - - {shouldTruncate && ( -
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/MarketGraph.tsx b/src/sections/Predictions/Trading/Events/MarketGraph.tsx deleted file mode 100644 index 50ed9e3..0000000 --- a/src/sections/Predictions/Trading/Events/MarketGraph.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useEffect, useRef, useState } from "react" -import { PriceHistoryInterval } from "@polymarket/clob-client" -import { createChart, LineSeries, type Time } from "lightweight-charts" -import { Button } from "ui" - -import type { PolymarketMarket } from "../../hooks/useMarkets" -import usePriceHistory from "../../hooks/usePriceHistory" - -const INTERVAL_OPTIONS = [ - { label: "1H", value: PriceHistoryInterval.ONE_HOUR }, - { label: "6H", value: PriceHistoryInterval.SIX_HOURS }, - { label: "1D", value: PriceHistoryInterval.ONE_DAY }, - { label: "1W", value: PriceHistoryInterval.ONE_WEEK }, - { label: "MAX", value: PriceHistoryInterval.MAX }, -] as const - -export const MarketGraph = ({ market }: { market: PolymarketMarket }) => { - const [interval, setInterval] = useState( - PriceHistoryInterval.MAX, - ) - const { data: history } = usePriceHistory(market, interval) - const chartContainerRef = useRef(null) - - useEffect(() => { - if (history?.length && chartContainerRef.current) { - const container = chartContainerRef.current - const s = getComputedStyle(container) - const get = (v: string, fallback: string) => - s.getPropertyValue(v).trim() || fallback - - const chart = createChart(container, { - layout: { - background: { color: get("--bg-surface", "#111") }, - textColor: get("--token-text", "#fff"), - }, - width: container.clientWidth, - height: 300, - grid: { - vertLines: { color: get("--token-border", "#4d4d4d") + "33" }, - horzLines: { color: get("--token-border", "#4d4d4d") + "33" }, - }, - timeScale: { - timeVisible: true, - secondsVisible: false, - }, - }) - - const lineSeries = chart.addSeries(LineSeries, { - color: get("--token-primary", "#1a85ff"), - }) - - const formattedData = history - .map((h) => ({ - value: h.p * 100, - time: h.t as Time, - })) - .sort((a, b) => (a.time as number) - (b.time as number)) - - formattedData.pop() // the clob client returns a duplicated result for the last history item - lineSeries.setData(formattedData) - chart.timeScale().fitContent() - - const handleResize = () => { - chart.applyOptions({ width: container.clientWidth }) - } - - window.addEventListener("resize", handleResize) - - return () => { - window.removeEventListener("resize", handleResize) - chart.remove() - } - } - }, [history, interval]) - - return ( -
-
-
- {INTERVAL_OPTIONS.map((option) => ( -
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/MarketRow.module.css b/src/sections/Predictions/Trading/Events/MarketRow.module.css deleted file mode 100644 index 9345240..0000000 --- a/src/sections/Predictions/Trading/Events/MarketRow.module.css +++ /dev/null @@ -1,6 +0,0 @@ -.marketLabel { - flex: 1; - min-width: 0; - text-decoration: underline; - cursor: pointer; -} diff --git a/src/sections/Predictions/Trading/Events/MarketRow.tsx b/src/sections/Predictions/Trading/Events/MarketRow.tsx deleted file mode 100644 index 9398144..0000000 --- a/src/sections/Predictions/Trading/Events/MarketRow.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Button, Text } from "ui" - -import type { PolymarketMarket } from "../../hooks/useMarkets" -import { cn } from "../../utils/classNames" -import type { EventCardProps, RealtimePrices } from "./EventCard" -import styles from "./MarketRow.module.css" - -interface MarketRowProps { - market: PolymarketMarket - realtimePrices: RealtimePrices - disabled: boolean - onOutcomeClick: EventCardProps["onOutcomeClick"] - showDivider?: boolean - onRowClick?: () => void -} - -export default function MarketRow({ - market, - realtimePrices, - disabled, - onOutcomeClick, - onRowClick, - showDivider = false, -}: MarketRowProps) { - const tokenIds: string[] = market.clobTokenIds - const outcomes: string[] = market.outcomes ?? [] - const negRisk = market.negRisk || false - - const yesTokenId = tokenIds[0] || "" - const noTokenId = tokenIds[1] || "" - const yesPrice = realtimePrices[yesTokenId] || 0 - const noPrice = realtimePrices[noTokenId] || 0 - const yesPercent = Math.round(yesPrice * 100) - - const isDisabled = market.closed || disabled - const label = market.groupItemTitle || market.question - - return ( -
- {/* Label */} -
- - {label} - -
- - {/* Percentage — fixed width so buttons align across rows */} -

- {yesPrice > 0 ? `${yesPercent}%` : "—"} -

- - {/* Yes / No buttons */} -
-
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/OutcomeButton.tsx b/src/sections/Predictions/Trading/Events/OutcomeButton.tsx deleted file mode 100644 index 77f3622..0000000 --- a/src/sections/Predictions/Trading/Events/OutcomeButton.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { cn } from "../../utils/classNames.ts" -import { convertPriceToCents } from "../../utils/order.ts" - -interface OutcomeButtonsProps { - outcomes: string[] - outcomePrices: number[] - tokenIds: string[] - isClosed: boolean - negRisk: boolean - marketQuestion: string - orderMinSize: number - disabled?: boolean - onOutcomeClick: ( - marketTitle: string, - outcome: string, - price: number, - tokenId: string, - negRisk: boolean, - orderMinSize: number, - ) => void -} - -export default function OutcomeButtons({ - outcomes, - outcomePrices, - tokenIds, - isClosed, - negRisk, - marketQuestion, - disabled = false, - onOutcomeClick, - orderMinSize, -}: OutcomeButtonsProps) { - if (outcomes.length === 0) return null - - const isDisabled = isClosed || disabled - - return ( -
- {outcomes.map((outcome: string, idx: number) => { - const tokenId = tokenIds[idx] || "" - const price = outcomePrices[idx] || 0 - const priceInCents = convertPriceToCents(price) - - return ( - - ) - })} -
- ) -} diff --git a/src/sections/Predictions/Trading/Events/SearchBar.tsx b/src/sections/Predictions/Trading/Events/SearchBar.tsx deleted file mode 100644 index 3ac0505..0000000 --- a/src/sections/Predictions/Trading/Events/SearchBar.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client" - -import { Icon } from "ui" - -export default function SearchBar({ - onSearch, -}: { - onSearch: (query: string) => void -}) { - return ( -
-
- - onSearch(e.target.value)} - /> -
-
- ) -} diff --git a/src/sections/Predictions/Trading/Events/index.tsx b/src/sections/Predictions/Trading/Events/index.tsx deleted file mode 100644 index c654a8f..0000000 --- a/src/sections/Predictions/Trading/Events/index.tsx +++ /dev/null @@ -1,184 +0,0 @@ -"use client" - -import { useState } from "react" -import { useDebouncedEffect } from "@react-hookz/web" -import { Drawer } from "ui" - -import EmptyState from "../../components/EmptyState.tsx" -import ErrorState from "../../components/ErrorState.tsx" -import LoadingState from "../../components/LoadingState.tsx" -import { - type CategoryId, - DEFAULT_CATEGORY, - getCategoryById, -} from "../../constants/categories.ts" -import useMarkets, { - type PolymarketEvent, - type PolymarketMarket, -} from "../../hooks/useMarkets.ts" -import useSearchEvents from "../../hooks/useSearchEvents.ts" -import { useTrading } from "../../providers/TradingProvider.tsx" -import OrderPlacementModal from "../OrderModal/index.tsx" -import CategoryTabs from "./CategoryTabs.tsx" -import EventCard from "./EventCard.tsx" -import { MarketDrawer } from "./MarketDrawer.tsx" -import SearchBar from "./SearchBar.tsx" - -export default function HighVolumeMarkets() { - const [selectedMarket, setSelectedMarket] = useState( - null, - ) - const [isModalOpen, setIsModalOpen] = useState(false) - const [activeCategory, setActiveCategory] = - useState(DEFAULT_CATEGORY) - const [query, setQuery] = useState("") - const [events, setEvents] = useState([]) - const [selectedOutcome, setSelectedOutcome] = useState<{ - marketTitle: string - outcome: string - price: number - tokenId: string - negRisk: boolean - orderMinSize: number - } | null>(null) - - const { isGeoblocked } = useTrading() - - const { - data: eventsUnfiltered, - isLoading, - error, - } = useMarkets({ - limit: 10, - categoryId: activeCategory, - }) - - const { searchEvents } = useSearchEvents() - - const category = getCategoryById(activeCategory) - const categoryLabel = category?.label || "Markets" - - const handleOutcomeClick = ( - marketTitle: string, - outcome: string, - price: number, - tokenId: string, - negRisk: boolean, - orderMinSize: number, - ) => { - setSelectedOutcome({ - marketTitle, - outcome, - price, - tokenId, - negRisk, - orderMinSize, - }) - setIsModalOpen(true) - } - - const handleCloseModal = () => { - setIsModalOpen(false) - setSelectedOutcome(null) - } - - const handleCategoryChange = (categoryId: CategoryId) => { - setActiveCategory(categoryId) - } - - const handleMarketClick = (market: PolymarketMarket) => { - setSelectedMarket(market) - } - - const handleOnSearch = async (query: string) => { - setQuery(query) - } - - useDebouncedEffect( - async () => { - if (query.length === 0) { - setEvents(eventsUnfiltered ?? []) - return - } - const events = await searchEvents(query.replace(" ", "+")) - setEvents(events) - }, - [query, eventsUnfiltered], - 500, - ) - - return ( - <> -
- - {/* Category Tabs */} - - - {/* Header */} -
-

{categoryLabel} Markets

-
- - {/* Loading State */} - {isLoading && ( - - )} - - {/* Error State */} - {error && !isLoading && ( - - )} - - {/* Empty State */} - {!isLoading && !error && (!events || events.length === 0) && ( - - )} - - {/* Market Cards */} - {!isLoading && !error && events && events.length > 0 && ( -
- {events.map((event) => ( - - ))} -
- )} -
- - {/* Market Drawer */} - setSelectedMarket(null)} - > - {selectedMarket && } - - - {/* Order Placement Modal */} - {selectedOutcome && ( - - )} - - ) -} diff --git a/src/sections/Predictions/Trading/MarketTabs.tsx b/src/sections/Predictions/Trading/MarketTabs.tsx deleted file mode 100644 index a4136a9..0000000 --- a/src/sections/Predictions/Trading/MarketTabs.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client" - -import { Tab, Tabs } from "@/components/Snippet/Tabs/Tabs" - -import HighVolumeMarkets from "./Events/index.tsx" -import ActiveOrders from "./Orders/index.tsx" -import UserPositions from "./Positions/index.tsx" - -export default function MarketTabs() { - return ( - - - - - - - - - - - - ) -} diff --git a/src/sections/Predictions/Trading/OrderModal/OrderForm.tsx b/src/sections/Predictions/Trading/OrderModal/OrderForm.tsx deleted file mode 100644 index cd76bf8..0000000 --- a/src/sections/Predictions/Trading/OrderModal/OrderForm.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import usePolygonBalances from "../../hooks/usePolygonBalances.ts" -import { useTrading } from "../../providers/TradingProvider.tsx" -import { isValidDecimalInput } from "../../utils/validation.ts" - -interface OrderFormProps { - size: string - onSizeChange: (value: string) => void - limitPrice: string - onLimitPriceChange: (value: string) => void - orderType: "market" | "limit" - currentPrice: number - isSubmitting: boolean - tickSize: number - decimalPlaces: number - isLoadingTickSize: boolean -} - -const isValidPriceInput = (value: string, maxDecimals: number): boolean => { - if (value === "" || value === "0" || value === "0.") return true - const regex = new RegExp(`^(0?\\.[0-9]{0,${maxDecimals}}|0)$`) - return regex.test(value) -} - -export default function OrderForm({ - size, - onSizeChange, - limitPrice, - onLimitPriceChange, - orderType, - isSubmitting, - tickSize, - decimalPlaces, - isLoadingTickSize, -}: OrderFormProps) { - const { safeAddress } = useTrading() - const { formattedUsdcBalance } = usePolygonBalances(safeAddress) - - const handleSizeChange = (value: string) => { - if (isValidDecimalInput(value)) { - onSizeChange(value) - } - } - - const handleLimitPriceChange = (value: string) => { - if (isValidPriceInput(value, decimalPlaces)) { - onLimitPriceChange(value) - } - } - - // Ensure tickSize is a valid number before calling toFixed - const safeTickSize = - typeof tickSize === "number" && !isNaN(tickSize) ? tickSize : 0.01 - const tickSizeDisplay = safeTickSize.toFixed(decimalPlaces) - const maxPriceDisplay = (1 - safeTickSize).toFixed(decimalPlaces) - - return ( - <> - {/* Size Input */} -
-
-
- - {orderType === "limit" ? "Shares" : "Amount ($)"} - - - Balance {formattedUsdcBalance} - -
-
- handleSizeChange(e.target.value)} - placeholder="0" - className="text-3xl font-semibold text-white bg-transparent border-none focus:outline-none w-32 text-right" - disabled={isSubmitting} - /> -
-
-
- - {/* Limit Price Input */} - {orderType === "limit" && ( -
- - handleLimitPriceChange(e.target.value)} - placeholder={tickSizeDisplay} - className="w-full px-4 py-2 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:border-[var(--brand-primary)] text-white" - disabled={isSubmitting || isLoadingTickSize} - /> -

- Tick size: ${tickSizeDisplay} • Range: ${tickSizeDisplay} - $ - {maxPriceDisplay} -

-
- )} - - ) -} diff --git a/src/sections/Predictions/Trading/OrderModal/OrderSummary.tsx b/src/sections/Predictions/Trading/OrderModal/OrderSummary.tsx deleted file mode 100644 index f16c884..0000000 --- a/src/sections/Predictions/Trading/OrderModal/OrderSummary.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { formatTokenValue } from "@/components/Utils/tokenFormat.ts" - -interface OrderSummaryProps { - size: number - price: number -} - -export default function OrderSummary({ size, price }: OrderSummaryProps) { - if (size <= 0 || price <= 0) return null - - const shares = size / price - - return ( -
-
-
- Potential win - - Avg. Price{" "} - {formatTokenValue(price, { - currency: "USD", - symbol: "$", - usdExchangeRate: 1, - })} - -
- - {formatTokenValue(shares, { - currency: "USD", - symbol: "$", - usdExchangeRate: 1, - })} - -
-
- ) -} diff --git a/src/sections/Predictions/Trading/OrderModal/OrderTypeToggle.tsx b/src/sections/Predictions/Trading/OrderModal/OrderTypeToggle.tsx deleted file mode 100644 index 04e9136..0000000 --- a/src/sections/Predictions/Trading/OrderModal/OrderTypeToggle.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Button } from "ui" - -interface OrderTypeToggleProps { - orderType: "market" | "limit" - onChangeOrderType: (type: "market" | "limit") => void -} - -export default function OrderTypeToggle({ - orderType, - onChangeOrderType, -}: OrderTypeToggleProps) { - return ( -
- -
-
-
-
-
-
-
- ) -} diff --git a/src/sections/Predictions/Trading/OrderModal/index.tsx b/src/sections/Predictions/Trading/OrderModal/index.tsx deleted file mode 100644 index 83e1328..0000000 --- a/src/sections/Predictions/Trading/OrderModal/index.tsx +++ /dev/null @@ -1,289 +0,0 @@ -"use client" - -import { useEffect, useRef, useState } from "react" -import { Side } from "@polymarket/clob-client" -import { Button, createToast, updateToast } from "ui" - -import { useTranslation } from "translations" -import { AnalyticsEventTypes } from "@/analytics/types.ts" -import { useAnalytics } from "@/analytics/useAnalytics.ts" - -import CloseDialogButton from "../../components/CloseDialogButton.tsx" -import useClobOrder from "../../hooks/useClobOrder.ts" -import usePolygonBalances from "../../hooks/usePolygonBalances.ts" -import useTickSize from "../../hooks/useTickSize.ts" -import Portal from "../../Portal.tsx" -import { useTrading } from "../../providers/TradingProvider.tsx" -import { isOrderSizeValid } from "../../utils/validation.ts" -import OrderForm from "./OrderForm.tsx" -import OrderSummary from "./OrderSummary.tsx" -import OrderTypeToggle from "./OrderTypeToggle.tsx" - -function getDecimalPlaces(tickSize: number): number { - if (tickSize >= 1) return 0 - const str = tickSize.toString() - const decimalPart = str.split(".")[1] - return decimalPart ? decimalPart.length : 0 -} - -function isValidTickPrice(price: number, tickSize: number): boolean { - if (tickSize <= 0) return false - const multiplier = Math.round(price / tickSize) - const expectedPrice = multiplier * tickSize - // Allow small floating point tolerance - return Math.abs(price - expectedPrice) < 1e-10 -} - -type OrderPlacementModalProps = { - isOpen: boolean - onClose: () => void - marketTitle: string - outcome: string - orderMinSize: number // taker order minimum is 1$, maker order minimum is 5 shares - currentPrice: number - tokenId: string - negRisk?: boolean -} - -export default function OrderPlacementModal({ - isOpen, - onClose, - marketTitle, - outcome, - orderMinSize, - currentPrice, - tokenId, - negRisk = false, -}: OrderPlacementModalProps) { - const [size, setSize] = useState("") - const [orderType, setOrderType] = useState<"market" | "limit">("market") - const [limitPrice, setLimitPrice] = useState("") - const [localError, setLocalError] = useState(null) - const { safeAddress } = useTrading() - const { usdceBalance } = usePolygonBalances(safeAddress) - const { captureEvent } = useAnalytics() - - const modalRef = useRef(null) - - // Fetch tick size dynamically for this market - const { tickSize, isLoading: isLoadingTickSize } = useTickSize( - isOpen ? tokenId : null, - ) - const decimalPlaces = getDecimalPlaces(tickSize) - const { translations } = useTranslation() - - const { - submitOrder, - isSubmitting, - error: orderError, - orderId, - } = useClobOrder(safeAddress) - - useEffect(() => { - if (isOpen) { - setSize("") - setOrderType("market") - setLimitPrice("") - setLocalError(null) - } - }, [isOpen]) - - useEffect(() => { - if (orderId && isOpen) { - const timer = setTimeout(() => { - onClose() - }, 2000) - return () => clearTimeout(timer) - } - }, [orderId, isOpen]) - - useEffect(() => { - if (isOpen) { - document.body.style.overflow = "hidden" - } else { - document.body.style.overflow = "unset" - } - - return () => { - document.body.style.overflow = "unset" - } - }, [isOpen]) - - const sizeNum = parseFloat(size) || 0 - const limitPriceNum = parseFloat(limitPrice) || 0.01 - const effectivePrice = orderType === "limit" ? limitPriceNum : currentPrice - - useEffect(() => { - const requestedAmount = - orderType === "market" ? Number(size) : limitPriceNum * Number(size) - - if (requestedAmount > Number(usdceBalance)) { - setLocalError("Insufficient funds") - } else { - setLocalError(null) - } - }, [usdceBalance, size, limitPriceNum, orderType, currentPrice]) - - useEffect(() => { - if (isOpen && size && orderMinSize) { - if (!isOrderSizeValid(orderType, sizeNum, orderMinSize)) { - setLocalError( - orderType === "market" - ? translations.predictions.marketTakerOrderFailedMiniumAmount - : translations.predictions.limitOrderFailedMiniumAmount( - orderMinSize, - ), - ) - } - } - }, [sizeNum, orderMinSize, isOpen, orderType, limitPriceNum]) - - if (!isOpen) return null - - const handlePlaceOrder = async () => { - if (orderType === "limit") { - if (!limitPrice || limitPriceNum <= 0) { - setLocalError("Limit price is required") - return - } - - if (limitPriceNum < tickSize || limitPriceNum > 1 - tickSize) { - setLocalError( - `Price must be between $${tickSize.toFixed(decimalPlaces)} and $${(1 - tickSize).toFixed(decimalPlaces)}`, - ) - return - } - - if (!isValidTickPrice(limitPriceNum, tickSize)) { - setLocalError(`Price must be a multiple of tick size ($${tickSize})`) - return - } - } - - const notId = createToast({ - message: "Submitting order", - status: "pending", - }) - try { - await submitOrder({ - tokenId, - size: sizeNum, - price: effectivePrice, - side: Side.BUY, - negRisk, - isMarketOrder: orderType === "market", - }) - captureEvent({ - type: AnalyticsEventTypes.PolymarketOrderPlacementSucceeded, - orderType, - size: sizeNum, - priceUsdce: limitPriceNum, - totalCostUsdce: sizeNum * limitPriceNum, - side: "BUY", - tokenId, - marketTitle, - }) - updateToast({ - id: notId, - message: "Order placed successfully", - status: "success", - }) - } catch (err) { - const message = - err instanceof Error ? err.message : "Error while placing the order" - setLocalError(message) - captureEvent({ - type: AnalyticsEventTypes.PolymarketOrderPlacementFailed, - errorReason: message, - }) - updateToast({ - id: notId, - message, - status: "error", - }) - console.error("[predictions] place order:", err) - } - } - - const handleBackdropClick = (e: React.MouseEvent) => { - if (e.target === e.currentTarget) { - onClose() - } - } - - return ( - -
-
- {/* Header */} -
-
-

{marketTitle}

-

- Buying: {outcome} -

-
- -
- - {/* Error Message */} - {(localError || orderError) && ( -
-

- {localError || orderError?.message} -

-
- )} - - {/* Order Type Toggle */} - { - setOrderType(type) - setLocalError(null) - }} - /> - - {/* Order Form */} - { - setSize(value) - setLocalError(null) - }} - limitPrice={limitPrice} - onLimitPriceChange={(value) => { - setLimitPrice(value) - setLocalError(null) - }} - orderType={orderType} - currentPrice={currentPrice} - isSubmitting={isSubmitting} - tickSize={tickSize} - decimalPlaces={decimalPlaces} - isLoadingTickSize={isLoadingTickSize} - /> - - {/* Order Summary */} - - - {/* Place Order Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/Trading/Orders/OrderCard.tsx b/src/sections/Predictions/Trading/Orders/OrderCard.tsx deleted file mode 100644 index 13f02af..0000000 --- a/src/sections/Predictions/Trading/Orders/OrderCard.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import useSWR from "swr" -import { Button } from "ui" - -import { formatTokenValue } from "@/components/Utils/tokenFormat.ts" -import { CURRENCY_SYMBOLS } from "@/stores/currencyStore.ts" - -import Badge from "../../components/Badge.tsx" -import Card from "../../components/Card.tsx" -import StatDisplay from "../../components/StatDisplay.tsx" -import { QUERY_REFETCH_INTERVALS } from "../../constants/query.ts" -import type { PolymarketOrder } from "../../hooks/useActiveOrders.tsx" - -interface OrderCardProps { - order: PolymarketOrder - onCancel: (orderId: string) => void - isCancelling: boolean - isSubmitting: boolean -} - -export default function OrderCard({ - order, - onCancel, - isCancelling, - isSubmitting, -}: OrderCardProps) { - const { data: marketInfo } = useSWR( - [order.asset_id, "market-info"], - async () => { - try { - const response = await fetch( - `/api/polymarket/markets/clobTokenIds/${order.asset_id}`, - ) - if (!response.ok) return null - return await response.json() - } catch { - return null - } - }, - { - refreshInterval: QUERY_REFETCH_INTERVALS.ORDERS, - revalidateOnFocus: true, - }, - ) - - const price = parseFloat(order.price) - const shares = parseFloat(order.original_size) - const totalValue = shares * price - - const getOutcome = () => { - if (!marketInfo?.outcomes || !marketInfo?.clobTokenIds) return null - try { - const outcomes = JSON.parse(marketInfo.outcomes) - const tokenIds = JSON.parse(marketInfo.clobTokenIds) - const outcomeIndex = tokenIds.indexOf(order.asset_id) - return outcomes[outcomeIndex] || outcomes[0] - } catch { - return null - } - } - - return ( - - {/* Market Title and Buy/Sell Badge */} -
-
- {marketInfo ? ( - <> -

- {marketInfo.question || "Market"} -

- {getOutcome() && ( -
- {getOutcome()} -
- )} - - ) : ( -
- Loading... -
- )} -
- - - {order.side} - -
- - {/* Order Details Grid */} -
- - - -
- - {/* Created At Timestamp */} - {order.created_at && ( -
- {new Date(order.created_at * 1000).toLocaleString()} -
- )} - - {/* Cancel Order Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/Trading/Orders/index.tsx b/src/sections/Predictions/Trading/Orders/index.tsx deleted file mode 100644 index 2e3ed46..0000000 --- a/src/sections/Predictions/Trading/Orders/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -"use client" - -import { useState } from "react" -import { createToast, updateToast } from "ui" - -import EmptyState from "../../components/EmptyState.tsx" -import ErrorState from "../../components/ErrorState.tsx" -import LoadingState from "../../components/LoadingState.tsx" -import useActiveOrders from "../../hooks/useActiveOrders.ts" -import useClobOrder from "../../hooks/useClobOrder.ts" -import { useTrading } from "../../providers/TradingProvider.tsx" -import OrderCard from "./OrderCard.tsx" - -export default function ActiveOrders() { - const { clobClient, safeAddress } = useTrading() - const { - data: orders, - isLoading, - error, - } = useActiveOrders(clobClient, safeAddress as `0x${string}` | undefined) - const [cancellingId, setCancellingId] = useState(null) - const { cancelOrder, isSubmitting } = useClobOrder( - safeAddress as `0x${string}` | undefined, - ) - - const handleCancelOrder = async (orderId: string) => { - setCancellingId(orderId) - const notId = createToast({ - message: "Canceling order", - status: "pending", - }) - try { - await cancelOrder(orderId) - updateToast({ - id: notId, - status: "success", - message: "Order canceled", - autoClose: true, - }) - } catch (err) { - console.error("Failed to cancel order:", err) - updateToast({ - id: notId, - status: "error", - message: "Failed to cancel order", - autoClose: true, - }) - } finally { - setCancellingId(null) - } - } - - if (isLoading) { - return - } - - if (error) { - return - } - - if (!orders || orders.length === 0) { - return ( - - ) - } - - return ( -
- {/* Header with Order Count */} -
-

Open Orders ({orders.length})

-
- - {/* Orders List */} -
- {orders.map((order) => ( - - ))} -
-
- ) -} diff --git a/src/sections/Predictions/Trading/Positions/PositionCard.tsx b/src/sections/Predictions/Trading/Positions/PositionCard.tsx deleted file mode 100644 index c0c2f3d..0000000 --- a/src/sections/Predictions/Trading/Positions/PositionCard.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import Image from "next/image" -import { Button } from "ui" - -import { formatTokenValue } from "@/components/Utils/tokenFormat.ts" -import { CURRENCY_SYMBOLS } from "@/stores/currencyStore.ts" - -import Card from "../../components/Card.tsx" -import StatDisplay from "../../components/StatDisplay.tsx" -import type { PolymarketPosition } from "../../hooks/useUserPositions.ts" - -interface PositionCardProps { - position: PolymarketPosition - onSell: (position: PolymarketPosition) => void - onRedeem: (position: PolymarketPosition) => void - isSelling: boolean - isRedeeming: boolean - isPendingVerification: boolean - isSubmitting: boolean - canRedeem: boolean -} - -export default function PositionCard({ - position, - onSell, - onRedeem, - isSelling, - isRedeeming, - isPendingVerification, - isSubmitting, - canRedeem, -}: PositionCardProps) { - return ( - - {/* Market Title and Icon */} -
-
-

{position.title}

-

- Outcome: {position.outcome} -

-
- {position.icon && ( - - )} -
- - {/* Position Stats Grid */} -
- - - - - - = 0 ? "green" : "red"} - /> -
- - {/* Redeemable Event Banner */} - {position.redeemable && ( -
-

- Event Completed - Position Redeemable -

-
- )} - - {/* Action Button - Redeem or Market Sell */} - {position.redeemable ? ( - <> -
- ) -} diff --git a/src/sections/Predictions/Trading/Positions/index.tsx b/src/sections/Predictions/Trading/Positions/index.tsx deleted file mode 100644 index 416c473..0000000 --- a/src/sections/Predictions/Trading/Positions/index.tsx +++ /dev/null @@ -1,205 +0,0 @@ -"use client" - -import { useEffect, useMemo, useState } from "react" -import { Side } from "@polymarket/clob-client" - -import EmptyState from "../../components/EmptyState.tsx" -import ErrorState from "../../components/ErrorState.tsx" -import LoadingState from "../../components/LoadingState.tsx" -import { POLLING_DURATION, POLLING_INTERVAL } from "../../constants/query.ts" -import { DUST_THRESHOLD } from "../../constants/validation.ts" -import useClobOrder from "../../hooks/useClobOrder" -import usePolygonBalances from "../../hooks/usePolygonBalances.ts" -import useRedeemPosition from "../../hooks/useRedeemPosition.ts" -import useUserPositions, { - type PolymarketPosition, -} from "../../hooks/useUserPositions" -import { useTrading } from "../../providers/TradingProvider" -import { useWallet } from "../../providers/WalletContext.tsx" -import { createPollingInterval } from "../../utils/polling.ts" -import PositionCard from "./PositionCard.tsx" -import PositionFilters from "./PositionFilters.tsx" - -export default function UserPositions() { - const { relayClient, safeAddress } = useTrading() - const { eoaAddress } = useWallet() - - const { - data: positions, - isLoading, - mutate: mutateUserPositions, - error, - } = useUserPositions(safeAddress as string | undefined) - - const { mutate: mutatePolygonBalances } = usePolygonBalances(safeAddress) - - const [hideDust, setHideDust] = useState(true) - const [redeemingAsset, setRedeemingAsset] = useState(null) - - const { redeemPosition } = useRedeemPosition() - const { submitOrder, isSubmitting } = useClobOrder(eoaAddress) - const [sellingAsset, setSellingAsset] = useState(null) - - const [pendingVerification, setPendingVerification] = useState< - Map - >(new Map()) - useEffect(() => { - if (!positions || pendingVerification.size === 0) return - - const stillPending = new Map() - - pendingVerification.forEach((originalSize, asset) => { - const currentPosition = positions.find((p) => p.asset === asset) - const currentSize = currentPosition?.size || 0 - const sizeChanged = currentSize < originalSize - - if (!sizeChanged) { - stillPending.set(asset, originalSize) - } - }) - - if (stillPending.size !== pendingVerification.size) { - setPendingVerification(stillPending) - } - }, [positions, pendingVerification]) - - const handleMarketSell = async (position: PolymarketPosition) => { - setSellingAsset(position.asset) - try { - await submitOrder({ - tokenId: position.asset, - size: position.size, - side: Side.SELL, - negRisk: position.negativeRisk, - isMarketOrder: true, - }) - - setPendingVerification((prev) => - new Map(prev).set(position.asset, position.size), - ) - - mutateUserPositions() - createPollingInterval( - () => { - mutateUserPositions() - }, - POLLING_INTERVAL, - POLLING_DURATION, - ) - - setTimeout(() => { - setPendingVerification((prev) => { - const next = new Map(prev) - next.delete(position.asset) - return next - }) - }, POLLING_DURATION) - } catch (err) { - console.error("Failed to sell position:", err) - alert("Failed to sell position. Please try again.") - } finally { - setSellingAsset(null) - } - } - - const handleRedeem = async (position: PolymarketPosition) => { - if (!relayClient) { - alert("Relay client not initialized") - return - } - - setRedeemingAsset(position.asset) - try { - await redeemPosition(relayClient, { - conditionId: position.conditionId, - outcomeIndex: position.outcomeIndex, - negativeRisk: position.negativeRisk, - size: position.size, - }) - - mutatePolygonBalances() - mutateUserPositions() - - createPollingInterval( - () => { - mutatePolygonBalances() - mutateUserPositions - }, - POLLING_INTERVAL, - POLLING_DURATION, - ) - } catch (err) { - console.error("Failed to redeem position:", err) - alert("Failed to redeem position. Please try again.") - } finally { - setRedeemingAsset(null) - } - } - - const activePositions = useMemo(() => { - if (!positions) return [] - - let filtered = positions.filter((p) => p.size >= DUST_THRESHOLD) - - if (hideDust) { - filtered = filtered.filter((p) => p.currentValue >= DUST_THRESHOLD) - } - - return filtered - }, [positions, hideDust]) - - if (isLoading) { - return - } - - if (error) { - return - } - - if (!positions || activePositions.length === 0) { - return ( - - ) - } - - return ( -
- {/* Header with Position Count and Dust Toggle */} - setHideDust(!hideDust)} - /> - - {/* Dust Warning Banner */} - {hideDust && positions && positions.length > activePositions.length && ( -
-

- Hiding {positions.length - activePositions.length} dust position(s) - (value < ${DUST_THRESHOLD.toFixed(2)}) -

-
- )} - - {/* Positions List */} -
- {activePositions.map((position) => ( - - ))} -
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/components/AllowTokens.tsx b/src/sections/Predictions/WelcomeFlow/components/AllowTokens.tsx deleted file mode 100644 index 1a1d5a1..0000000 --- a/src/sections/Predictions/WelcomeFlow/components/AllowTokens.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useCallback, useRef, useState } from "react" -import { Button } from "ui" - -import CloseDialogButton from "../../components/CloseDialogButton" -import { SUCCESS_STYLES } from "../../constants/ui" -import Portal from "../../Portal" -import { useTrading } from "../../providers/TradingProvider" -import { cn } from "../../utils/classNames" - -export default function AllowTokens({ - onComplete, - onClose, -}: { - onComplete: () => void - onClose: () => void -}) { - const [showSuccess, setShowSuccess] = useState(false) - const { - relayClient, - handleThirdWelcomeStep, - welcomeLoading, - tradingSession, - } = useTrading() - const modalRef = useRef(null) - - const handleAllowTokens = useCallback(async () => { - if (!relayClient) { - throw new Error("Relay client not found") - } - await handleThirdWelcomeStep() - setShowSuccess(true) - setTimeout(() => onComplete(), 2000) - onComplete() - }, [onComplete, relayClient, tradingSession]) - - return ( - -
- {/* Header */} -
-
-

Allow tokens

- -
- -
-

- To use Polymarket you have to approve USDC.e tokens -

-
- - {/* Success Message */} - {showSuccess && ( -
-

- Approved successful! -

-
- )} - - {/* Error Message - {error && ( -
-

{error.message}

-
- )} - */} - - {/* Allow Tokens Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/components/AuthenticateWithPolymarket.tsx b/src/sections/Predictions/WelcomeFlow/components/AuthenticateWithPolymarket.tsx deleted file mode 100644 index c34ba82..0000000 --- a/src/sections/Predictions/WelcomeFlow/components/AuthenticateWithPolymarket.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useCallback, useRef, useState } from "react" -import { Button } from "ui" - -import CloseDialogButton from "../../components/CloseDialogButton" -import { SUCCESS_STYLES } from "../../constants/ui" -import Portal from "../../Portal" -import { useTrading } from "../../providers/TradingProvider" -import { cn } from "../../utils/classNames" - -export default function AuthenticateWithPolymarket({ - onComplete, - onClose, -}: { - onComplete: () => void - onClose: () => void -}) { - const [showSuccess, setShowSuccess] = useState(false) - const { - welcomeLoading, - handleFirstWelcomeStep, - relayClient, - tradingSession, - } = useTrading() - const modalRef = useRef(null) - - const handleAuthenticateWithPolymarket = useCallback(async () => { - if (!relayClient) { - throw new Error("Relay client not found") - } - if (!tradingSession) { - throw new Error("Trading session not found") - } - await handleFirstWelcomeStep() - setShowSuccess(true) - setTimeout(() => onComplete(), 2000) - onComplete() - }, [onComplete, relayClient, tradingSession]) - - return ( - -
- {/* Header */} -
-
-

Authenticate with Polymarket

- -
- -
-

- Polymarket needs to authenticate with your wallet to start trading -

-
- - {/* Success Message */} - {showSuccess && ( -
-

- Authenticated with Polymarket successfully! -

-
- )} - - {/* Authenticate Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/components/BlockedModal.tsx b/src/sections/Predictions/WelcomeFlow/components/BlockedModal.tsx deleted file mode 100644 index 0878b7e..0000000 --- a/src/sections/Predictions/WelcomeFlow/components/BlockedModal.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useRef } from "react" - -import CloseDialogButton from "../../components/CloseDialogButton" -import Portal from "../../Portal" - -export default function GeoBlockModal({ onClose }: { onClose: () => void }) { - const modalRef = useRef(null) - - return ( - -
- {/* Header */} -
-
-

Polymarket is blocked

- -
- -
-

- Polymarket is not available in your region. -

-
-
-
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/components/CreateAccountModal.tsx b/src/sections/Predictions/WelcomeFlow/components/CreateAccountModal.tsx deleted file mode 100644 index a903086..0000000 --- a/src/sections/Predictions/WelcomeFlow/components/CreateAccountModal.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react" -import { Button, Icon } from "ui" - -import { useTranslation } from "@/translations" - -import { useTrading } from "../../providers/TradingProvider" - -export default function CreateAccountModal({ - onComplete, -}: { - onComplete: () => void -}) { - const [buttonText, setButtonText] = useState("Continue") - const ref = useRef(null) - const { - tradingSession, - welcomeLoading, - currentStep, - shouldDeriveApiCredentials, - handleFirstWelcomeStep: handleCreateApiCredentials, - handleSecondWelcomeStep: handleDeriveApiCredentials, - handleThirdWelcomeStep: handleDeploySafe, - handleFourthWelcomeStep: handleAllowTokens, - } = useTrading() - const { - translations: { swap }, - } = useTranslation() - - const handleCreateAccountStep = useCallback(async () => { - try { - if (!tradingSession) { - setButtonText("Loading session") - return - } - if (!tradingSession.hasApiCredentials) { - if (shouldDeriveApiCredentials) { - await handleDeriveApiCredentials() - } else { - await handleCreateApiCredentials() - } - } else if (!tradingSession.isSafeDeployed) { - await handleDeploySafe() - } else if (!tradingSession.hasApprovals) { - await handleAllowTokens() - } - } catch (error) { - console.error(error) - setButtonText("There was an error, please try again") - } - }, [ - tradingSession, - shouldDeriveApiCredentials, - handleCreateApiCredentials, - handleDeriveApiCredentials, - handleDeploySafe, - handleAllowTokens, - ]) - - useEffect(() => { - if ( - tradingSession?.isSafeDeployed && - tradingSession?.hasApprovals && - tradingSession?.hasApiCredentials - ) { - setTimeout(() => onComplete(), 2000) - } - }, [tradingSession]) - - return ( -
-
-

{swap.welcomeFlow.title}

-

- {swap.welcomeFlow.subtitle} -

-
- -
- - {shouldDeriveApiCredentials && ( - - )} - - -
- -
- ) -} - -export const RowInCreateAccountModal = ({ - stepNumber, - title, - message, - isCompleted, - isLoading, -}: { - stepNumber: number - title: string - message: string - isCompleted: boolean - isLoading: boolean -}) => { - return ( -
-
- {isLoading ? ( - - - - ) : isCompleted ? ( - - ) : ( -

{stepNumber}

- )} -
-
-

{title}

-

{message}

-
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/components/CreateSafeModal.tsx b/src/sections/Predictions/WelcomeFlow/components/CreateSafeModal.tsx deleted file mode 100644 index 151aa23..0000000 --- a/src/sections/Predictions/WelcomeFlow/components/CreateSafeModal.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { useCallback, useRef, useState } from "react" -import { Button } from "ui" - -import CloseDialogButton from "../../components/CloseDialogButton" -import { SUCCESS_STYLES } from "../../constants/ui" -import Portal from "../../Portal" -import { useTrading } from "../../providers/TradingProvider" -import { cn } from "../../utils/classNames" - -export default function CreateSafeModal({ - onComplete, - onClose, -}: { - onComplete: () => void - onClose: () => void -}) { - const [showSuccess, setShowSuccess] = useState(false) - const { - relayClient, - handleSecondWelcomeStep, - welcomeLoading, - tradingSession, - } = useTrading() - - const handleDeploy = useCallback(async () => { - if (!relayClient) { - throw new Error("Relay client not found") - } - await handleSecondWelcomeStep() - setShowSuccess(true) - setTimeout(() => onComplete(), 2000) - }, [relayClient, onComplete, tradingSession]) - - const modalRef = useRef(null) - - return ( - -
- {/* Header */} -
-
-

Safe not deployed

- -
- -
-

- Deploy a Safe wallet to start trading in polymarket. -

-
- - {/* Success Message */} - {showSuccess && ( -
-

- Safe deployed successfully! -

-
- )} - - {/* Error Message - {error && ( -
-

{error.message}

-
- )} - */} - - {/* Deploy Button */} -
-
-
- ) -} diff --git a/src/sections/Predictions/WelcomeFlow/index.tsx b/src/sections/Predictions/WelcomeFlow/index.tsx deleted file mode 100644 index f60ea9c..0000000 --- a/src/sections/Predictions/WelcomeFlow/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useEffect, useState } from "react" -import { useRouter } from "next/navigation" - -import useGeoblock from "../hooks/useGeoblock" -import { useTrading } from "../providers/TradingProvider" -import BlockedModal from "./components/BlockedModal" -import CreateAccountModal from "./components/CreateAccountModal" - -export default function WelcomeFlow() { - const [shouldShowBlockedModal, setShouldShowBlockedModal] = useState(false) - const [shouldShowCreateAccountModal, setShouldShowCreateAccountModal] = - useState(false) - const { tradingSession, setCurrentStep } = useTrading() - const router = useRouter() - const { isBlocked: isGeoblocked } = useGeoblock() - - useEffect(() => { - if (!tradingSession) return - - if (isGeoblocked) { - setCurrentStep("blocked") - setShouldShowBlockedModal(true) - return - } - - if ( - !tradingSession.hasApiCredentials || - !tradingSession.isSafeDeployed || - !tradingSession.hasApprovals - ) { - setShouldShowCreateAccountModal(true) - return - } - }, [ - tradingSession, - isGeoblocked, - setCurrentStep, - setShouldShowBlockedModal, - setShouldShowCreateAccountModal, - ]) - - const onClose = () => { - router.push("/") - } - - const onComplete = () => { - setShouldShowCreateAccountModal(false) - } - - return ( - <> - {shouldShowBlockedModal ? ( - <> - - - ) : ( - <> - {shouldShowCreateAccountModal && ( - - )} - - )} - - ) -} diff --git a/src/sections/Predictions/components/Badge.tsx b/src/sections/Predictions/components/Badge.tsx deleted file mode 100644 index cffb521..0000000 --- a/src/sections/Predictions/components/Badge.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { cn } from "../utils/classNames.ts" - -type BadgeVariant = "buy" | "sell" | "closed" | "default" - -interface BadgeProps { - variant?: BadgeVariant - children: React.ReactNode - className?: string -} - -const variantStyles: Record = { - buy: "bg-[var(--palette-green-muted)] text-[var(--color-success)] border border-[var(--color-success)]", - sell: "bg-[var(--token-error-muted)] text-[var(--color-error)] border border-[var(--token-error-muted)]", - closed: "bg-[var(--token-error-muted)] text-[var(--color-error)]", - default: "bg-[var(--brand-primary-muted)] text-[var(--brand-primary)]", -} - -export default function Badge({ - variant = "default", - children, - className, -}: BadgeProps) { - return ( - - {children} - - ) -} diff --git a/src/sections/Predictions/components/Card.tsx b/src/sections/Predictions/components/Card.tsx deleted file mode 100644 index bb34208..0000000 --- a/src/sections/Predictions/components/Card.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { CARD_STYLES } from "../constants/ui.ts" -import { cn } from "../utils/classNames.ts" - -interface CardProps { - children: React.ReactNode - className?: string - hover?: boolean -} - -export default function Card({ - children, - className, - hover = false, -}: CardProps) { - return ( -
- {children} -
- ) -} diff --git a/src/sections/Predictions/components/CloseDialogButton.tsx b/src/sections/Predictions/components/CloseDialogButton.tsx deleted file mode 100644 index 7783bcc..0000000 --- a/src/sections/Predictions/components/CloseDialogButton.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Button } from "ui" - -export default function CloseDialogButton({ - onClick, -}: { - onClick: () => void -}) { - return