@catalyst-team/poly-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/.env +0 -0
  2. package/README.md +803 -0
  3. package/dist/__tests__/clob-api.test.d.ts +5 -0
  4. package/dist/__tests__/clob-api.test.d.ts.map +1 -0
  5. package/dist/__tests__/clob-api.test.js +240 -0
  6. package/dist/__tests__/clob-api.test.js.map +1 -0
  7. package/dist/__tests__/integration/bridge-client.integration.test.d.ts +11 -0
  8. package/dist/__tests__/integration/bridge-client.integration.test.d.ts.map +1 -0
  9. package/dist/__tests__/integration/bridge-client.integration.test.js +260 -0
  10. package/dist/__tests__/integration/bridge-client.integration.test.js.map +1 -0
  11. package/dist/__tests__/integration/clob-api.integration.test.d.ts +13 -0
  12. package/dist/__tests__/integration/clob-api.integration.test.d.ts.map +1 -0
  13. package/dist/__tests__/integration/clob-api.integration.test.js +170 -0
  14. package/dist/__tests__/integration/clob-api.integration.test.js.map +1 -0
  15. package/dist/__tests__/integration/ctf-client.integration.test.d.ts +17 -0
  16. package/dist/__tests__/integration/ctf-client.integration.test.d.ts.map +1 -0
  17. package/dist/__tests__/integration/ctf-client.integration.test.js +234 -0
  18. package/dist/__tests__/integration/ctf-client.integration.test.js.map +1 -0
  19. package/dist/__tests__/integration/data-api.integration.test.d.ts +9 -0
  20. package/dist/__tests__/integration/data-api.integration.test.d.ts.map +1 -0
  21. package/dist/__tests__/integration/data-api.integration.test.js +161 -0
  22. package/dist/__tests__/integration/data-api.integration.test.js.map +1 -0
  23. package/dist/__tests__/integration/gamma-api.integration.test.d.ts +9 -0
  24. package/dist/__tests__/integration/gamma-api.integration.test.d.ts.map +1 -0
  25. package/dist/__tests__/integration/gamma-api.integration.test.js +170 -0
  26. package/dist/__tests__/integration/gamma-api.integration.test.js.map +1 -0
  27. package/dist/__tests__/test-utils.d.ts +92 -0
  28. package/dist/__tests__/test-utils.d.ts.map +1 -0
  29. package/dist/__tests__/test-utils.js +143 -0
  30. package/dist/__tests__/test-utils.js.map +1 -0
  31. package/dist/clients/bridge-client.d.ts +388 -0
  32. package/dist/clients/bridge-client.d.ts.map +1 -0
  33. package/dist/clients/bridge-client.js +587 -0
  34. package/dist/clients/bridge-client.js.map +1 -0
  35. package/dist/clients/clob-api.d.ts +318 -0
  36. package/dist/clients/clob-api.d.ts.map +1 -0
  37. package/dist/clients/clob-api.js +388 -0
  38. package/dist/clients/clob-api.js.map +1 -0
  39. package/dist/clients/ctf-client.d.ts +473 -0
  40. package/dist/clients/ctf-client.d.ts.map +1 -0
  41. package/dist/clients/ctf-client.js +915 -0
  42. package/dist/clients/ctf-client.js.map +1 -0
  43. package/dist/clients/data-api.d.ts +134 -0
  44. package/dist/clients/data-api.d.ts.map +1 -0
  45. package/dist/clients/data-api.js +265 -0
  46. package/dist/clients/data-api.js.map +1 -0
  47. package/dist/clients/gamma-api.d.ts +401 -0
  48. package/dist/clients/gamma-api.d.ts.map +1 -0
  49. package/dist/clients/gamma-api.js +352 -0
  50. package/dist/clients/gamma-api.js.map +1 -0
  51. package/dist/clients/trading-client.d.ts +252 -0
  52. package/dist/clients/trading-client.d.ts.map +1 -0
  53. package/dist/clients/trading-client.js +543 -0
  54. package/dist/clients/trading-client.js.map +1 -0
  55. package/dist/clients/websocket-manager.d.ts +100 -0
  56. package/dist/clients/websocket-manager.d.ts.map +1 -0
  57. package/dist/clients/websocket-manager.js +193 -0
  58. package/dist/clients/websocket-manager.js.map +1 -0
  59. package/dist/core/cache-adapter-bridge.d.ts +36 -0
  60. package/dist/core/cache-adapter-bridge.d.ts.map +1 -0
  61. package/dist/core/cache-adapter-bridge.js +81 -0
  62. package/dist/core/cache-adapter-bridge.js.map +1 -0
  63. package/dist/core/cache.d.ts +40 -0
  64. package/dist/core/cache.d.ts.map +1 -0
  65. package/dist/core/cache.js +71 -0
  66. package/dist/core/cache.js.map +1 -0
  67. package/dist/core/errors.d.ts +38 -0
  68. package/dist/core/errors.d.ts.map +1 -0
  69. package/dist/core/errors.js +84 -0
  70. package/dist/core/errors.js.map +1 -0
  71. package/dist/core/rate-limiter.d.ts +31 -0
  72. package/dist/core/rate-limiter.d.ts.map +1 -0
  73. package/dist/core/rate-limiter.js +70 -0
  74. package/dist/core/rate-limiter.js.map +1 -0
  75. package/dist/core/types.d.ts +314 -0
  76. package/dist/core/types.d.ts.map +1 -0
  77. package/dist/core/types.js +19 -0
  78. package/dist/core/types.js.map +1 -0
  79. package/dist/core/unified-cache.d.ts +63 -0
  80. package/dist/core/unified-cache.d.ts.map +1 -0
  81. package/dist/core/unified-cache.js +114 -0
  82. package/dist/core/unified-cache.js.map +1 -0
  83. package/dist/index.d.ts +94 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +258 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/mcp/errors.d.ts +33 -0
  88. package/dist/mcp/errors.d.ts.map +1 -0
  89. package/dist/mcp/errors.js +86 -0
  90. package/dist/mcp/errors.js.map +1 -0
  91. package/dist/mcp/index.d.ts +62 -0
  92. package/dist/mcp/index.d.ts.map +1 -0
  93. package/dist/mcp/index.js +173 -0
  94. package/dist/mcp/index.js.map +1 -0
  95. package/dist/mcp/server.d.ts +17 -0
  96. package/dist/mcp/server.d.ts.map +1 -0
  97. package/dist/mcp/server.js +155 -0
  98. package/dist/mcp/server.js.map +1 -0
  99. package/dist/mcp/tools/guide.d.ts +12 -0
  100. package/dist/mcp/tools/guide.d.ts.map +1 -0
  101. package/dist/mcp/tools/guide.js +801 -0
  102. package/dist/mcp/tools/guide.js.map +1 -0
  103. package/dist/mcp/tools/index.d.ts +11 -0
  104. package/dist/mcp/tools/index.d.ts.map +1 -0
  105. package/dist/mcp/tools/index.js +27 -0
  106. package/dist/mcp/tools/index.js.map +1 -0
  107. package/dist/mcp/tools/market.d.ts +11 -0
  108. package/dist/mcp/tools/market.d.ts.map +1 -0
  109. package/dist/mcp/tools/market.js +314 -0
  110. package/dist/mcp/tools/market.js.map +1 -0
  111. package/dist/mcp/tools/order.d.ts +10 -0
  112. package/dist/mcp/tools/order.d.ts.map +1 -0
  113. package/dist/mcp/tools/order.js +258 -0
  114. package/dist/mcp/tools/order.js.map +1 -0
  115. package/dist/mcp/tools/trade.d.ts +38 -0
  116. package/dist/mcp/tools/trade.d.ts.map +1 -0
  117. package/dist/mcp/tools/trade.js +314 -0
  118. package/dist/mcp/tools/trade.js.map +1 -0
  119. package/dist/mcp/tools/trader.d.ts +11 -0
  120. package/dist/mcp/tools/trader.d.ts.map +1 -0
  121. package/dist/mcp/tools/trader.js +277 -0
  122. package/dist/mcp/tools/trader.js.map +1 -0
  123. package/dist/mcp/tools/wallet.d.ts +274 -0
  124. package/dist/mcp/tools/wallet.d.ts.map +1 -0
  125. package/dist/mcp/tools/wallet.js +579 -0
  126. package/dist/mcp/tools/wallet.js.map +1 -0
  127. package/dist/mcp/types.d.ts +413 -0
  128. package/dist/mcp/types.d.ts.map +1 -0
  129. package/dist/mcp/types.js +5 -0
  130. package/dist/mcp/types.js.map +1 -0
  131. package/dist/services/authorization-service.d.ts +97 -0
  132. package/dist/services/authorization-service.d.ts.map +1 -0
  133. package/dist/services/authorization-service.js +279 -0
  134. package/dist/services/authorization-service.js.map +1 -0
  135. package/dist/services/market-service.d.ts +108 -0
  136. package/dist/services/market-service.d.ts.map +1 -0
  137. package/dist/services/market-service.js +458 -0
  138. package/dist/services/market-service.js.map +1 -0
  139. package/dist/services/realtime-service.d.ts +82 -0
  140. package/dist/services/realtime-service.d.ts.map +1 -0
  141. package/dist/services/realtime-service.js +150 -0
  142. package/dist/services/realtime-service.js.map +1 -0
  143. package/dist/services/swap-service.d.ts +217 -0
  144. package/dist/services/swap-service.d.ts.map +1 -0
  145. package/dist/services/swap-service.js +695 -0
  146. package/dist/services/swap-service.js.map +1 -0
  147. package/dist/services/wallet-service.d.ts +94 -0
  148. package/dist/services/wallet-service.d.ts.map +1 -0
  149. package/dist/services/wallet-service.js +173 -0
  150. package/dist/services/wallet-service.js.map +1 -0
  151. package/dist/utils/price-utils.d.ts +153 -0
  152. package/dist/utils/price-utils.d.ts.map +1 -0
  153. package/dist/utils/price-utils.js +236 -0
  154. package/dist/utils/price-utils.js.map +1 -0
  155. package/docs/00-design.md +760 -0
  156. package/docs/01-mcp.md +2041 -0
  157. package/docs/02-API.md +1148 -0
  158. package/docs/e2e/01-trader-tools.md +159 -0
  159. package/docs/e2e/02-market-tools.md +180 -0
  160. package/docs/e2e/03-order-tools.md +166 -0
  161. package/docs/e2e/04-wallet-tools.md +224 -0
  162. package/docs/e2e/05-trading-tools.md +327 -0
  163. package/docs/e2e/06-integration-scenarios.md +481 -0
  164. package/docs/e2e/coordinator.md +376 -0
  165. package/examples/01-basic-usage.ts +68 -0
  166. package/examples/02-smart-money.ts +95 -0
  167. package/examples/03-market-analysis.ts +108 -0
  168. package/examples/04-kline-aggregation.ts +158 -0
  169. package/examples/05-follow-wallet-strategy.ts +156 -0
  170. package/examples/06-services-demo.ts +124 -0
  171. package/examples/07-realtime-websocket.ts +117 -0
  172. package/examples/08-trading-orders.ts +278 -0
  173. package/examples/09-rewards-tracking.ts +187 -0
  174. package/examples/10-ctf-operations.ts +336 -0
  175. package/examples/11-live-arbitrage-scan.ts +221 -0
  176. package/examples/12-trending-arb-monitor.ts +406 -0
  177. package/examples/README.md +179 -0
  178. package/package.json +62 -0
  179. package/scripts/README.md +163 -0
  180. package/scripts/approvals/approve-erc1155.ts +129 -0
  181. package/scripts/approvals/approve-neg-risk-erc1155.ts +149 -0
  182. package/scripts/approvals/approve-neg-risk.ts +102 -0
  183. package/scripts/approvals/check-all-allowances.ts +150 -0
  184. package/scripts/approvals/check-allowance.ts +129 -0
  185. package/scripts/approvals/check-ctf-approval.ts +158 -0
  186. package/scripts/datas/001-report.md +486 -0
  187. package/scripts/datas/clone-modal-screenshot.png +0 -0
  188. package/scripts/deposit/deposit-native-usdc.ts +179 -0
  189. package/scripts/deposit/deposit-usdc.ts +155 -0
  190. package/scripts/deposit/swap-usdc-to-usdce.ts +375 -0
  191. package/scripts/research/research-markets.ts +166 -0
  192. package/scripts/trading/check-orders.ts +50 -0
  193. package/scripts/trading/sell-nvidia-positions.ts +206 -0
  194. package/scripts/trading/test-order.ts +172 -0
  195. package/scripts/truth.md +440 -0
  196. package/scripts/verify/test-approve-trading.ts +98 -0
  197. package/scripts/verify/test-provider-fix.ts +43 -0
  198. package/scripts/verify/test-search-mcp.ts +113 -0
  199. package/scripts/verify/verify-all-apis.ts +160 -0
  200. package/scripts/wallet/check-wallet-balances.ts +75 -0
  201. package/scripts/wallet/test-wallet-operations.ts +191 -0
  202. package/scripts/wallet/verify-wallet-tools.ts +124 -0
  203. package/src/__tests__/clob-api.test.ts +301 -0
  204. package/src/__tests__/integration/bridge-client.integration.test.ts +314 -0
  205. package/src/__tests__/integration/clob-api.integration.test.ts +218 -0
  206. package/src/__tests__/integration/ctf-client.integration.test.ts +331 -0
  207. package/src/__tests__/integration/data-api.integration.test.ts +194 -0
  208. package/src/__tests__/integration/gamma-api.integration.test.ts +206 -0
  209. package/src/__tests__/test-utils.ts +170 -0
  210. package/src/clients/bridge-client.ts +841 -0
  211. package/src/clients/clob-api.ts +629 -0
  212. package/src/clients/ctf-client.ts +1216 -0
  213. package/src/clients/data-api.ts +469 -0
  214. package/src/clients/gamma-api.ts +597 -0
  215. package/src/clients/trading-client.ts +749 -0
  216. package/src/clients/websocket-manager.ts +267 -0
  217. package/src/core/cache-adapter-bridge.ts +94 -0
  218. package/src/core/cache.ts +85 -0
  219. package/src/core/errors.ts +117 -0
  220. package/src/core/rate-limiter.ts +74 -0
  221. package/src/core/types.ts +360 -0
  222. package/src/core/unified-cache.ts +153 -0
  223. package/src/index.ts +455 -0
  224. package/src/mcp/README.md +380 -0
  225. package/src/mcp/errors.ts +124 -0
  226. package/src/mcp/index.ts +309 -0
  227. package/src/mcp/server.ts +183 -0
  228. package/src/mcp/tools/guide.ts +821 -0
  229. package/src/mcp/tools/index.ts +73 -0
  230. package/src/mcp/tools/market.ts +363 -0
  231. package/src/mcp/tools/order.ts +326 -0
  232. package/src/mcp/tools/trade.ts +417 -0
  233. package/src/mcp/tools/trader.ts +322 -0
  234. package/src/mcp/tools/wallet.ts +683 -0
  235. package/src/mcp/types.ts +472 -0
  236. package/src/services/authorization-service.ts +357 -0
  237. package/src/services/market-service.ts +544 -0
  238. package/src/services/realtime-service.ts +196 -0
  239. package/src/services/swap-service.ts +896 -0
  240. package/src/services/wallet-service.ts +259 -0
  241. package/src/utils/price-utils.ts +307 -0
  242. package/tsconfig.json +8 -0
  243. package/vitest.config.ts +19 -0
  244. package/vitest.integration.config.ts +18 -0
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Trader Tools - MCP tools for trader/wallet analysis
3
+ */
4
+
5
+ import type { PolymarketSDK } from '../../index.js';
6
+ import type {
7
+ ToolDefinition,
8
+ GetTraderPositionsInput,
9
+ GetTraderPositionsOutput,
10
+ GetTraderTradesInput,
11
+ GetTraderTradesOutput,
12
+ GetTraderProfileInput,
13
+ GetTraderProfileOutput,
14
+ GetLeaderboardInput,
15
+ GetLeaderboardOutput,
16
+ } from '../types.js';
17
+ import { validateAddress, wrapError } from '../errors.js';
18
+
19
+ export const traderToolDefinitions: ToolDefinition[] = [
20
+ {
21
+ name: 'get_trader_positions',
22
+ description: 'Get all positions held by a trader with PnL breakdown',
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ address: {
27
+ type: 'string',
28
+ description: 'Trader wallet address (0x...)',
29
+ },
30
+ },
31
+ required: ['address'],
32
+ },
33
+ },
34
+ {
35
+ name: 'get_trader_trades',
36
+ description: 'Get recent trading activity for a trader',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {
40
+ address: {
41
+ type: 'string',
42
+ description: 'Trader wallet address',
43
+ },
44
+ limit: {
45
+ type: 'number',
46
+ description: 'Maximum number of trades to return',
47
+ default: 20,
48
+ },
49
+ side: {
50
+ type: 'string',
51
+ enum: ['BUY', 'SELL'],
52
+ description: 'Filter by trade side',
53
+ },
54
+ },
55
+ required: ['address'],
56
+ },
57
+ },
58
+ {
59
+ name: 'get_trader_profile',
60
+ description: 'Get comprehensive trader profile with performance metrics',
61
+ inputSchema: {
62
+ type: 'object',
63
+ properties: {
64
+ address: {
65
+ type: 'string',
66
+ description: 'Trader wallet address',
67
+ },
68
+ },
69
+ required: ['address'],
70
+ },
71
+ },
72
+ {
73
+ name: 'get_leaderboard',
74
+ description: 'Get top traders by PnL',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ limit: {
79
+ type: 'number',
80
+ description: 'Number of traders to return',
81
+ default: 10,
82
+ },
83
+ offset: {
84
+ type: 'number',
85
+ description: 'Pagination offset',
86
+ default: 0,
87
+ },
88
+ },
89
+ },
90
+ },
91
+ ];
92
+
93
+ export async function handleGetTraderPositions(
94
+ sdk: PolymarketSDK,
95
+ input: GetTraderPositionsInput
96
+ ): Promise<GetTraderPositionsOutput> {
97
+ validateAddress(input.address);
98
+
99
+ try {
100
+ const positions = await sdk.wallets.getWalletPositions(input.address);
101
+
102
+ const totalUnrealizedPnl = positions.reduce((sum, p) => sum + (p.cashPnl || 0), 0);
103
+ const totalRealizedPnl = positions.reduce((sum, p) => sum + (p.realizedPnl || 0), 0);
104
+ const winningPositions = positions.filter((p) => (p.cashPnl || 0) > 0).length;
105
+ const losingPositions = positions.filter((p) => (p.cashPnl || 0) < 0).length;
106
+
107
+ return {
108
+ trader: {
109
+ address: input.address,
110
+ displayName: null, // Will be enriched if available
111
+ },
112
+ positions: positions.map((p) => ({
113
+ market: {
114
+ conditionId: p.conditionId,
115
+ title: p.title,
116
+ slug: p.slug,
117
+ },
118
+ holding: {
119
+ outcome: p.outcome,
120
+ size: p.size,
121
+ avgPrice: p.avgPrice,
122
+ curPrice: p.curPrice,
123
+ },
124
+ pnl: {
125
+ unrealized: p.cashPnl,
126
+ unrealizedPercent: p.percentPnl,
127
+ realized: p.realizedPnl,
128
+ },
129
+ status: {
130
+ redeemable: p.redeemable,
131
+ endDate: p.endDate,
132
+ },
133
+ })),
134
+ summary: {
135
+ totalPositions: positions.length,
136
+ totalUnrealizedPnl,
137
+ totalRealizedPnl,
138
+ winningPositions,
139
+ losingPositions,
140
+ },
141
+ };
142
+ } catch (err) {
143
+ throw wrapError(err);
144
+ }
145
+ }
146
+
147
+ export async function handleGetTraderTrades(
148
+ sdk: PolymarketSDK,
149
+ input: GetTraderTradesInput
150
+ ): Promise<GetTraderTradesOutput> {
151
+ validateAddress(input.address);
152
+
153
+ try {
154
+ const activityResult = await sdk.wallets.getWalletActivity(
155
+ input.address,
156
+ input.limit || 100
157
+ );
158
+
159
+ let trades = activityResult.activities.filter((a) => a.type === 'TRADE');
160
+
161
+ // Filter by side if specified
162
+ if (input.side) {
163
+ trades = trades.filter((a) => a.side === input.side);
164
+ }
165
+
166
+ // Apply limit
167
+ const limit = input.limit || 20;
168
+ trades = trades.slice(0, limit);
169
+
170
+ const buyTrades = trades.filter((t) => t.side === 'BUY');
171
+ const sellTrades = trades.filter((t) => t.side === 'SELL');
172
+
173
+ return {
174
+ trader: {
175
+ address: input.address,
176
+ displayName: null,
177
+ },
178
+ trades: trades.map((t) => ({
179
+ type: t.type,
180
+ side: t.side,
181
+ market: {
182
+ conditionId: t.conditionId,
183
+ title: t.title || '',
184
+ slug: t.slug,
185
+ },
186
+ outcome: t.outcome,
187
+ execution: {
188
+ size: t.size,
189
+ price: t.price,
190
+ usdcValue: t.usdcSize || t.size * t.price,
191
+ },
192
+ timestamp: new Date(t.timestamp).toISOString(),
193
+ txHash: t.transactionHash,
194
+ })),
195
+ summary: {
196
+ totalTrades: trades.length,
197
+ buyCount: buyTrades.length,
198
+ sellCount: sellTrades.length,
199
+ buyVolume: buyTrades.reduce((sum, t) => sum + (t.usdcSize || t.size * t.price), 0),
200
+ sellVolume: sellTrades.reduce((sum, t) => sum + (t.usdcSize || t.size * t.price), 0),
201
+ },
202
+ };
203
+ } catch (err) {
204
+ throw wrapError(err);
205
+ }
206
+ }
207
+
208
+ export async function handleGetTraderProfile(
209
+ sdk: PolymarketSDK,
210
+ input: GetTraderProfileInput
211
+ ): Promise<GetTraderProfileOutput> {
212
+ validateAddress(input.address);
213
+
214
+ try {
215
+ const profile = await sdk.wallets.getWalletProfile(input.address);
216
+
217
+ // Try to get leaderboard data for rank and official PnL
218
+ let rank: number | null = null;
219
+ let leaderboardPnl: number | null = null;
220
+ let leaderboardVolume: number | null = null;
221
+ let displayName: string | null = null;
222
+ let verified = false;
223
+ try {
224
+ const leaderboard = await sdk.wallets.getLeaderboard(0, 100);
225
+ const entry = leaderboard.entries.find(
226
+ (e) => e.address.toLowerCase() === input.address.toLowerCase()
227
+ );
228
+ if (entry) {
229
+ rank = entry.rank;
230
+ leaderboardPnl = entry.pnl;
231
+ leaderboardVolume = entry.volume;
232
+ displayName = entry.userName || null;
233
+ verified = entry.verifiedBadge || false;
234
+ }
235
+ } catch {
236
+ // Ignore leaderboard errors
237
+ }
238
+
239
+ const winningCount = profile.positionCount > 0
240
+ ? Math.round(profile.positionCount * (profile.avgPercentPnL > 0 ? 0.6 : 0.4))
241
+ : 0;
242
+ const winRate = profile.positionCount > 0
243
+ ? winningCount / profile.positionCount
244
+ : 0;
245
+
246
+ return {
247
+ trader: {
248
+ address: input.address,
249
+ displayName,
250
+ xUsername: null,
251
+ verified,
252
+ profileImage: null,
253
+ },
254
+ ranking: {
255
+ rank,
256
+ totalTraders: 10000, // Approximate
257
+ },
258
+ performance: {
259
+ // Official PnL from leaderboard (used for ranking)
260
+ officialPnl: leaderboardPnl,
261
+ // Volume from leaderboard
262
+ totalVolume: leaderboardVolume ?? (profile.totalPnL + profile.unrealizedPnL),
263
+ // Calculated PnL from profile positions
264
+ unrealizedPnl: profile.unrealizedPnL,
265
+ realizedPnl: profile.realizedPnL,
266
+ },
267
+ stats: {
268
+ positionCount: profile.positionCount,
269
+ winRate,
270
+ avgPercentPnl: profile.avgPercentPnL,
271
+ smartScore: profile.smartScore,
272
+ },
273
+ activity: {
274
+ lastTradeAt: profile.lastActiveAt.getTime() > 0
275
+ ? profile.lastActiveAt.toISOString()
276
+ : null,
277
+ isActive: Date.now() - profile.lastActiveAt.getTime() < 7 * 24 * 60 * 60 * 1000,
278
+ },
279
+ // Explain potential PnL differences
280
+ notes: leaderboardPnl !== null && Math.abs((leaderboardPnl || 0) - (profile.unrealizedPnL + profile.realizedPnL)) > 100
281
+ ? 'Note: officialPnl (from leaderboard) may differ from unrealizedPnl + realizedPnl (from positions) due to different calculation methods. Leaderboard PnL includes historical settled positions.'
282
+ : undefined,
283
+ };
284
+ } catch (err) {
285
+ throw wrapError(err);
286
+ }
287
+ }
288
+
289
+ export async function handleGetLeaderboard(
290
+ sdk: PolymarketSDK,
291
+ input: GetLeaderboardInput
292
+ ): Promise<GetLeaderboardOutput> {
293
+ const limit = input.limit || 10;
294
+ const offset = input.offset || 0;
295
+
296
+ try {
297
+ const page = Math.floor(offset / 50);
298
+ const leaderboard = await sdk.wallets.getLeaderboard(page, 50);
299
+
300
+ // Apply offset and limit within the page
301
+ const startIdx = offset % 50;
302
+ const entries = leaderboard.entries.slice(startIdx, startIdx + limit);
303
+
304
+ return {
305
+ traders: entries.map((e) => ({
306
+ rank: e.rank,
307
+ address: e.address,
308
+ displayName: e.userName || null,
309
+ pnl: e.pnl,
310
+ volume: e.volume,
311
+ verified: e.verifiedBadge || false,
312
+ })),
313
+ pagination: {
314
+ total: leaderboard.total,
315
+ offset,
316
+ limit,
317
+ },
318
+ };
319
+ } catch (err) {
320
+ throw wrapError(err);
321
+ }
322
+ }