Skip to content
This repository was archived by the owner on May 11, 2026. It is now read-only.

Commit 73092cb

Browse files
committed
updates gamma api usage
1 parent 6300de5 commit 73092cb

7 files changed

Lines changed: 261 additions & 92 deletions

File tree

app/api/polymarket/markets/route.ts

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,69 @@
11
import { NextRequest, NextResponse } from "next/server";
2+
import { GAMMA_API_URL } from "@/constants/api";
23

3-
const GAMMA_API = "https://gamma-api.polymarket.com";
4+
const MIN_LIQUIDITY_USD = 1000;
5+
const MIN_LIQUIDITY_NON_EVERGREEN_USD = 5000;
6+
7+
const EVERGREEN_TAG_IDS = [2, 21, 120, 596, 1401, 100265, 100639];
48

59
export async function GET(request: NextRequest) {
610
const searchParams = request.nextUrl.searchParams;
711
const limit = searchParams.get("limit") || "10";
12+
const tagId = searchParams.get("tag_id");
813

914
try {
1015
const fetchLimit = parseInt(limit) * 5;
1116

12-
const response = await fetch(
13-
`${GAMMA_API}/markets?limit=${fetchLimit}&offset=0&active=true&closed=false&order=volume24hr&ascending=false`,
14-
{
15-
headers: { "Content-Type": "application/json" },
16-
next: { revalidate: 60 },
17-
}
18-
);
17+
let url = `${GAMMA_API_URL}/events?closed=false&order=volume24hr&ascending=false&limit=${fetchLimit}&offset=0`;
18+
19+
if (tagId) {
20+
url += `&tag_id=${tagId}&related_tags=true`;
21+
}
22+
23+
const response = await fetch(url, {
24+
headers: { "Content-Type": "application/json" },
25+
next: { revalidate: 60 },
26+
});
1927

2028
if (!response.ok) {
2129
console.error("Gamma API error:", response.status);
2230
throw new Error(`Gamma API error: ${response.status}`);
2331
}
2432

25-
const markets = await response.json();
33+
const events = await response.json();
2634

27-
if (!Array.isArray(markets)) {
28-
console.error("Invalid response structure:", markets);
35+
if (!Array.isArray(events)) {
36+
console.error("Invalid response structure:", events);
2937
return NextResponse.json(
3038
{ error: "Invalid API response" },
3139
{ status: 500 }
3240
);
3341
}
3442

35-
const validMarkets = markets.filter((market: any) => {
36-
if (market.events && market.events.length > 0) {
37-
const hasEndedEvent = market.events.some(
38-
(event: any) =>
39-
event.ended === true ||
40-
event.live === false ||
41-
event.finishedTimestamp
42-
);
43-
if (hasEndedEvent) return false;
43+
const allMarkets: any[] = [];
44+
45+
for (const event of events) {
46+
if (event.ended || event.closed || !event.active) continue;
47+
48+
const markets = event.markets || [];
49+
50+
for (const market of markets) {
51+
allMarkets.push({
52+
...market,
53+
eventTitle: event.title,
54+
eventSlug: event.slug,
55+
eventId: event.id,
56+
eventIcon: event.image || event.icon,
57+
negRisk: event.negRisk || false,
58+
});
4459
}
60+
}
4561

62+
const validMarkets = allMarkets.filter((market: any) => {
4663
if (market.acceptingOrders === false) return false;
64+
if (market.closed === true) return false;
4765
if (!market.clobTokenIds) return false;
66+
4867
if (market.outcomePrices) {
4968
try {
5069
const prices = JSON.parse(market.outcomePrices);
@@ -53,41 +72,34 @@ export async function GET(request: NextRequest) {
5372
return priceNum >= 0.05 && priceNum <= 0.95;
5473
});
5574
if (!hasTradeablePrice) return false;
56-
} catch (e) {
75+
} catch {
5776
return false;
5877
}
5978
}
6079

61-
const evergreenTags = [
62-
"crypto",
63-
"politics",
64-
"sports",
65-
"technology",
66-
"business",
67-
"entertainment",
68-
"science",
69-
"ai",
70-
"pop-culture",
71-
];
72-
73-
const marketTags =
74-
market.tags?.map((t: any) => t.slug.toLowerCase()) || [];
75-
const hasEvergreenTag = evergreenTags.some((tag) =>
76-
marketTags.includes(tag)
80+
const marketTagIds =
81+
market.tags?.map((t: any) => parseInt(t.id)) || [];
82+
const hasEvergreenTag = EVERGREEN_TAG_IDS.some((id) =>
83+
marketTagIds.includes(id)
7784
);
7885

7986
const liquidity = parseFloat(market.liquidity || "0");
80-
if (!hasEvergreenTag && liquidity < 5000) return false;
81-
if (liquidity < 1000) return false;
87+
88+
if (!hasEvergreenTag && liquidity < MIN_LIQUIDITY_NON_EVERGREEN_USD) {
89+
return false;
90+
}
91+
if (liquidity < MIN_LIQUIDITY_USD) return false;
8292

8393
return true;
8494
});
8595

8696
const sortedMarkets = validMarkets.sort((a: any, b: any) => {
8797
const aScore =
88-
parseFloat(a.liquidity || "0") + parseFloat(a.volume || "0");
98+
parseFloat(a.liquidity || "0") +
99+
parseFloat(a.volume24hr || a.volume || "0");
89100
const bScore =
90-
parseFloat(b.liquidity || "0") + parseFloat(b.volume || "0");
101+
parseFloat(b.liquidity || "0") +
102+
parseFloat(b.volume24hr || b.volume || "0");
91103
return bScore - aScore;
92104
});
93105

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"use client";
2+
3+
import { CATEGORIES, type CategoryId } from "@/constants/categories";
4+
import { cn } from "@/utils/classNames";
5+
6+
interface CategoryTabsProps {
7+
activeCategory: CategoryId;
8+
onCategoryChange: (categoryId: CategoryId) => void;
9+
}
10+
11+
export default function CategoryTabs({
12+
activeCategory,
13+
onCategoryChange,
14+
}: CategoryTabsProps) {
15+
return (
16+
<div className="flex gap-2 flex-wrap mb-4">
17+
{CATEGORIES.map((category) => (
18+
<button
19+
key={category.id}
20+
onClick={() => onCategoryChange(category.id)}
21+
className={cn(
22+
"px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200",
23+
activeCategory === category.id
24+
? "bg-blue-600 text-white"
25+
: "bg-white/5 text-gray-300 hover:bg-white/10 hover:text-white"
26+
)}
27+
>
28+
{category.label}
29+
</button>
30+
))}
31+
</div>
32+
);
33+
}
34+

components/Trading/Markets/index.tsx

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22

33
import { useState } from "react";
44
import { useTrading } from "@/providers/TradingProvider";
5-
import useHighVolumeMarkets from "@/hooks/useHighVolumeMarkets";
5+
import useMarkets from "@/hooks/useMarkets";
6+
import { type CategoryId, DEFAULT_CATEGORY, getCategoryById } from "@/constants/categories";
67

78
import ErrorState from "@/components/shared/ErrorState";
89
import EmptyState from "@/components/shared/EmptyState";
910
import LoadingState from "@/components/shared/LoadingState";
1011
import MarketCard from "@/components/Trading/Markets/MarketCard";
12+
import CategoryTabs from "@/components/Trading/Markets/CategoryTabs";
1113
import OrderPlacementModal from "@/components/Trading/OrderModal";
1214

1315
export default function HighVolumeMarkets() {
14-
const { clobClient } = useTrading();
1516
const [isModalOpen, setIsModalOpen] = useState(false);
17+
const [activeCategory, setActiveCategory] = useState<CategoryId>(DEFAULT_CATEGORY);
1618
const [selectedOutcome, setSelectedOutcome] = useState<{
1719
marketTitle: string;
1820
outcome: string;
@@ -21,24 +23,15 @@ export default function HighVolumeMarkets() {
2123
negRisk: boolean;
2224
} | null>(null);
2325

24-
const { data: markets, isLoading, error } = useHighVolumeMarkets(10);
26+
const { clobClient, isGeoblocked } = useTrading();
2527

26-
if (isLoading) {
27-
return <LoadingState message="Loading high volume markets..." />;
28-
}
28+
const { data: markets, isLoading, error } = useMarkets({
29+
limit: 10,
30+
categoryId: activeCategory,
31+
});
2932

30-
if (error) {
31-
return <ErrorState error={error} title="Error loading markets" />;
32-
}
33-
34-
if (!markets || markets.length === 0) {
35-
return (
36-
<EmptyState
37-
title="No Markets Available"
38-
message="No active markets found."
39-
/>
40-
);
41-
}
33+
const category = getCategoryById(activeCategory);
34+
const categoryLabel = category?.label || "Markets";
4235

4336
const handleOutcomeClick = (
4437
marketTitle: string,
@@ -56,28 +49,59 @@ export default function HighVolumeMarkets() {
5649
setSelectedOutcome(null);
5750
};
5851

52+
const handleCategoryChange = (categoryId: CategoryId) => {
53+
setActiveCategory(categoryId);
54+
};
55+
5956
return (
6057
<>
6158
<div className="space-y-4">
59+
{/* Category Tabs */}
60+
<CategoryTabs
61+
activeCategory={activeCategory}
62+
onCategoryChange={handleCategoryChange}
63+
/>
64+
65+
{/* Header */}
6266
<div className="flex items-center justify-between">
6367
<h3 className="text-xl font-bold">
64-
High Volume Markets ({markets.length})
68+
{categoryLabel} Markets {markets ? `(${markets.length})` : ""}
6569
</h3>
66-
<p className="text-xs text-gray-400">Sorted by 24h volume</p>
70+
<p className="text-xs text-gray-400">Sorted by volume + liquidity</p>
6771
</div>
6872

69-
<div className="space-y-3">
70-
{markets.map((market) => (
71-
<MarketCard
72-
key={market.id}
73-
market={market}
74-
onOutcomeClick={handleOutcomeClick}
75-
/>
76-
))}
77-
</div>
73+
{/* Loading State */}
74+
{isLoading && <LoadingState message={`Loading ${categoryLabel.toLowerCase()} markets...`} />}
75+
76+
{/* Error State */}
77+
{error && !isLoading && (
78+
<ErrorState error={error} title="Error loading markets" />
79+
)}
80+
81+
{/* Empty State */}
82+
{!isLoading && !error && (!markets || markets.length === 0) && (
83+
<EmptyState
84+
title="No Markets Available"
85+
message={`No active ${categoryLabel.toLowerCase()} markets found.`}
86+
/>
87+
)}
88+
89+
{/* Market Cards */}
90+
{!isLoading && !error && markets && markets.length > 0 && (
91+
<div className="space-y-3">
92+
{markets.map((market) => (
93+
<MarketCard
94+
key={market.id}
95+
market={market}
96+
disabled={isGeoblocked}
97+
onOutcomeClick={handleOutcomeClick}
98+
/>
99+
))}
100+
</div>
101+
)}
78102
</div>
79103

80-
{/* Modal */}
104+
{/* Order Placement Modal */}
81105
{selectedOutcome && (
82106
<OrderPlacementModal
83107
isOpen={isModalOpen}

constants/api.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Polymarket API URLs
2+
export const RELAYER_URL = "https://relayer-v2.polymarket.com/";
3+
export const CLOB_API_URL = "https://clob.polymarket.com";
4+
export const GEOBLOCK_API_URL = "https://polymarket.com/api/geoblock";
5+
export const GAMMA_API_URL = "https://gamma-api.polymarket.com";
6+
export const POLYMARKET_PROFILE_URL = (address: string) =>
7+
`https://polymarket.com/${address}`;
8+
9+
// RPC
10+
export const POLYGON_RPC_URL =
11+
process.env.NEXT_PUBLIC_POLYGON_RPC_URL || "https://polygon-rpc.com";
12+
13+
// Remote signing endpoint
14+
export const REMOTE_SIGNING_URL = () =>
15+
typeof window !== "undefined"
16+
? `${window.location.origin}/api/polymarket/sign`
17+
: "/api/polymarket/sign";
18+

constants/categories.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
export type CategoryId =
2+
| "trending"
3+
| "politics"
4+
| "finance"
5+
| "crypto"
6+
| "sports"
7+
| "tech"
8+
| "culture"
9+
| "geopolitics";
10+
11+
export interface Category {
12+
id: CategoryId;
13+
label: string;
14+
tagId: number | null;
15+
}
16+
17+
export const CATEGORIES: Category[] = [
18+
{
19+
id: "trending",
20+
label: "Trending",
21+
tagId: null,
22+
},
23+
{
24+
id: "politics",
25+
label: "Politics",
26+
tagId: 2,
27+
},
28+
{
29+
id: "finance",
30+
label: "Finance",
31+
tagId: 120,
32+
},
33+
{
34+
id: "crypto",
35+
label: "Crypto",
36+
tagId: 21,
37+
},
38+
{
39+
id: "sports",
40+
label: "Sports",
41+
tagId: 100639,
42+
},
43+
{
44+
id: "tech",
45+
label: "Tech",
46+
tagId: 1401,
47+
},
48+
{
49+
id: "culture",
50+
label: "Culture",
51+
tagId: 596,
52+
},
53+
{
54+
id: "geopolitics",
55+
label: "Geopolitics",
56+
tagId: 100265,
57+
},
58+
];
59+
60+
export const DEFAULT_CATEGORY: CategoryId = "trending";
61+
62+
export function getCategoryById(id: CategoryId): Category | undefined {
63+
return CATEGORIES.find((c) => c.id === id);
64+
}
65+

0 commit comments

Comments
 (0)