@liberfi.io/react-predict 0.1.1

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1460 @@
1
+ import { httpGet, httpDelete, httpPost } from '@liberfi.io/utils';
2
+ import { createContext, useMemo, useState, useRef, useCallback, useContext, useEffect } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import { useQuery, useInfiniteQuery, useQueries, useQueryClient, useMutation } from '@tanstack/react-query';
5
+
6
+ // src/client/client.ts
7
+ function buildQuery(params) {
8
+ const qs = new URLSearchParams();
9
+ for (const [key, value] of Object.entries(params)) {
10
+ if (value !== void 0 && value !== null) {
11
+ qs.set(key, String(value));
12
+ }
13
+ }
14
+ const str = qs.toString();
15
+ return str ? `?${str}` : "";
16
+ }
17
+ var PredictClient = class {
18
+ constructor(endpoint) {
19
+ this.endpoint = endpoint;
20
+ }
21
+ // -------------------------------------------------------------------------
22
+ // Events
23
+ // -------------------------------------------------------------------------
24
+ /**
25
+ * List prediction events with optional filtering, sorting, and pagination.
26
+ *
27
+ * Maps to `GET /api/v1/events`.
28
+ *
29
+ * @param params - Optional query parameters (filter, sort, pagination).
30
+ * @returns A paginated page of events.
31
+ */
32
+ async listEvents(params) {
33
+ const query = buildQuery(params ?? {});
34
+ const url = `${this.endpoint}/api/v1/events${query}`;
35
+ return await httpGet(url);
36
+ }
37
+ /**
38
+ * Fetch a single prediction event by its slug.
39
+ *
40
+ * Maps to `GET /api/v1/events/:slug?source=...`.
41
+ *
42
+ * @param slug - Canonical event slug (e.g. "will-trump-win-2024" for Polymarket
43
+ * or "KXBTCD-25FEB-T68000" for DFlow).
44
+ * @param source - Upstream provider (`"dflow"` or `"polymarket"`).
45
+ * @returns The matching event.
46
+ * @throws When the server responds with 404 or any other non-2xx status.
47
+ */
48
+ async getEvent(slug, source) {
49
+ const query = source ? buildQuery({ source }) : "";
50
+ const url = `${this.endpoint}/api/v1/events/${encodeURIComponent(slug)}${query}`;
51
+ return await httpGet(url);
52
+ }
53
+ /**
54
+ * Fetch events similar to the given slug.
55
+ *
56
+ * Maps to `GET /api/v1/events/:slug/similar?source=...`.
57
+ */
58
+ async getSimilarEvents(slug, source, params) {
59
+ const query = buildQuery({ source, ...params });
60
+ const url = `${this.endpoint}/api/v1/events/${encodeURIComponent(slug)}/similar${query}`;
61
+ return await httpGet(url);
62
+ }
63
+ // -------------------------------------------------------------------------
64
+ // Markets
65
+ // -------------------------------------------------------------------------
66
+ /**
67
+ * Fetch a single prediction market by its slug.
68
+ *
69
+ * Maps to `GET /api/v1/markets/:slug?source=...`.
70
+ *
71
+ * @param slug - Canonical market slug.
72
+ * @param source - Upstream provider (`"dflow"` or `"polymarket"`).
73
+ * @returns The matching market.
74
+ * @throws When the server responds with 404 or any other non-2xx status.
75
+ */
76
+ async getMarket(slug, source) {
77
+ const query = source ? buildQuery({ source }) : "";
78
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}${query}`;
79
+ return await httpGet(url);
80
+ }
81
+ /** Maps to `GET /api/v1/markets/:slug/orderbook?source=...`. */
82
+ async getOrderbook(slug, source) {
83
+ const query = buildQuery({ source });
84
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/orderbook${query}`;
85
+ return await httpGet(url);
86
+ }
87
+ /** Maps to `GET /api/v1/markets/:slug/trades?source=...`. */
88
+ async listMarketTrades(slug, params) {
89
+ const query = buildQuery(params);
90
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/trades${query}`;
91
+ return await httpGet(url);
92
+ }
93
+ /** Maps to `GET /api/v1/markets/:slug/price-history?source=...&range=...`. */
94
+ async getPriceHistory(slug, source, range) {
95
+ const query = buildQuery({ source, range });
96
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/price-history${query}`;
97
+ return await httpGet(url);
98
+ }
99
+ /** Maps to `GET /api/v1/markets/:slug/candlesticks?interval=...&limit=...`. */
100
+ async listCandlesticks(slug, params) {
101
+ const query = buildQuery(params ?? {});
102
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/candlesticks${query}`;
103
+ return await httpGet(url);
104
+ }
105
+ // -------------------------------------------------------------------------
106
+ // Positions
107
+ // -------------------------------------------------------------------------
108
+ /**
109
+ * Maps to `GET /api/v1/positions?source=...&user=...`.
110
+ *
111
+ * @param user - Wallet address.
112
+ * @param source - Provider source. Omit to aggregate all providers.
113
+ */
114
+ async getPositions(user, source) {
115
+ const query = buildQuery({ source, user });
116
+ const url = `${this.endpoint}/api/v1/positions${query}`;
117
+ return await httpGet(url);
118
+ }
119
+ // -------------------------------------------------------------------------
120
+ // Balance
121
+ // -------------------------------------------------------------------------
122
+ /**
123
+ * Get the on-chain USDC balance for a wallet.
124
+ *
125
+ * Maps to `GET /api/v1/balance?source=...&user=...`.
126
+ *
127
+ * @param source - Provider source (`"dflow"` for Solana, `"polymarket"` for Polygon).
128
+ * @param user - Wallet address.
129
+ */
130
+ async getBalance(source, user) {
131
+ const query = buildQuery({ source, user });
132
+ const url = `${this.endpoint}/api/v1/balance${query}`;
133
+ return await httpGet(url);
134
+ }
135
+ // -------------------------------------------------------------------------
136
+ // Orders
137
+ // -------------------------------------------------------------------------
138
+ /** Maps to `GET /api/v1/orders?source=...&wallet_address=...`. */
139
+ async listOrders(params) {
140
+ const query = buildQuery(params);
141
+ const url = `${this.endpoint}/api/v1/orders${query}`;
142
+ return await httpGet(url);
143
+ }
144
+ /** Maps to `GET /api/v1/orders/:id?source=...`. */
145
+ async getOrder(id, source) {
146
+ const query = buildQuery({ source });
147
+ const url = `${this.endpoint}/api/v1/orders/${encodeURIComponent(id)}${query}`;
148
+ return await httpGet(url);
149
+ }
150
+ /** Maps to `DELETE /api/v1/orders/:id?source=...`. */
151
+ async cancelOrder(id, source) {
152
+ const query = buildQuery({ source });
153
+ const url = `${this.endpoint}/api/v1/orders/${encodeURIComponent(id)}${query}`;
154
+ return await httpDelete(url);
155
+ }
156
+ // -------------------------------------------------------------------------
157
+ // Polymarket trading
158
+ // -------------------------------------------------------------------------
159
+ /**
160
+ * Create a Polymarket limit order via the prediction-server proxy.
161
+ *
162
+ * Maps to `POST /api/v1/orders/polymarket`.
163
+ *
164
+ * The caller must attach Polymarket CLOB authentication headers:
165
+ * - `POLY_ADDRESS` — the EVM wallet address.
166
+ * - `POLY_SIGNATURE` — L1 signature or L2 API key.
167
+ * - `POLY_TIMESTAMP` — Unix milliseconds string.
168
+ * - `POLY_NONCE` — credential nonce (use `"0"` for in-session derived creds).
169
+ *
170
+ * @param input - Order parameters.
171
+ * @param headers - Polymarket CLOB auth headers (`POLY_*`).
172
+ */
173
+ async createPolymarketOrder(input, headers) {
174
+ const url = `${this.endpoint}/api/v1/orders/polymarket`;
175
+ return await httpPost(url, input, { headers });
176
+ }
177
+ // -------------------------------------------------------------------------
178
+ // DFlow trading
179
+ // -------------------------------------------------------------------------
180
+ /** Maps to `POST /api/v1/orders/dflow/quote`. */
181
+ async createDFlowQuote(body) {
182
+ const url = `${this.endpoint}/api/v1/orders/dflow/quote`;
183
+ return await httpPost(url, body);
184
+ }
185
+ /** Maps to `POST /api/v1/orders/dflow/submit`. */
186
+ async submitDFlowTransaction(body) {
187
+ const url = `${this.endpoint}/api/v1/orders/dflow/submit`;
188
+ return await httpPost(url, body);
189
+ }
190
+ // -------------------------------------------------------------------------
191
+ // Trades by wallet
192
+ // -------------------------------------------------------------------------
193
+ /** Maps to `GET /api/v1/trades?source=...&wallet=...`. */
194
+ async listTrades(params) {
195
+ const query = buildQuery(params);
196
+ const url = `${this.endpoint}/api/v1/trades${query}`;
197
+ return await httpGet(url);
198
+ }
199
+ };
200
+ function createPredictClient(endpoint) {
201
+ return new PredictClient(endpoint);
202
+ }
203
+
204
+ // src/client/ws.ts
205
+ var DEFAULT_RECONNECT_BASE = 1e3;
206
+ var DEFAULT_RECONNECT_MAX = 3e4;
207
+ var DEFAULT_PING_INTERVAL = 2e4;
208
+ var DEFAULT_PONG_TIMEOUT = 1e4;
209
+ var PredictWsClient = class {
210
+ ws = null;
211
+ wsUrl;
212
+ autoReconnect;
213
+ reconnectBase;
214
+ reconnectMax;
215
+ pingMs;
216
+ pongTimeoutMs;
217
+ status = "disconnected";
218
+ reconnectAttempts = 0;
219
+ reconnectTimer = null;
220
+ pingTimer = null;
221
+ pongTimer = null;
222
+ shouldReconnect = true;
223
+ listeners = {
224
+ connect: [],
225
+ disconnect: [],
226
+ reconnecting: [],
227
+ error: [],
228
+ status: [],
229
+ subscribed: [],
230
+ orderbook: [],
231
+ prices: [],
232
+ trades: []
233
+ };
234
+ subs = {
235
+ channels: /* @__PURE__ */ new Map([
236
+ ["orderbook", /* @__PURE__ */ new Set()],
237
+ ["prices", /* @__PURE__ */ new Set()],
238
+ ["trades", /* @__PURE__ */ new Set()]
239
+ ])
240
+ };
241
+ constructor(config) {
242
+ this.wsUrl = config.wsUrl;
243
+ this.autoReconnect = config.autoReconnect ?? true;
244
+ this.reconnectBase = config.reconnectIntervalBase ?? DEFAULT_RECONNECT_BASE;
245
+ this.reconnectMax = config.reconnectMaxInterval ?? DEFAULT_RECONNECT_MAX;
246
+ this.pingMs = config.pingInterval ?? DEFAULT_PING_INTERVAL;
247
+ this.pongTimeoutMs = config.pongTimeout ?? DEFAULT_PONG_TIMEOUT;
248
+ if (config.autoConnect !== false) {
249
+ this.connect();
250
+ }
251
+ }
252
+ // -------------------------------------------------------------------------
253
+ // Connection management
254
+ // -------------------------------------------------------------------------
255
+ connect() {
256
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
257
+ return;
258
+ }
259
+ this.shouldReconnect = true;
260
+ this.setStatus("connecting");
261
+ try {
262
+ this.ws = new WebSocket(this.wsUrl);
263
+ this.ws.onopen = () => {
264
+ this.reconnectAttempts = 0;
265
+ this.setStatus("connected");
266
+ this.emit("connect", void 0);
267
+ this.restoreSubscriptions();
268
+ this.startPing();
269
+ };
270
+ this.ws.onmessage = (event) => {
271
+ this.handleMessage(event.data);
272
+ };
273
+ this.ws.onerror = () => {
274
+ };
275
+ this.ws.onclose = (event) => {
276
+ this.stopPing();
277
+ this.setStatus("disconnected");
278
+ this.emit("disconnect", { code: event.code, reason: event.reason });
279
+ if (this.shouldReconnect && this.autoReconnect) {
280
+ this.scheduleReconnect();
281
+ }
282
+ };
283
+ } catch {
284
+ if (this.shouldReconnect && this.autoReconnect) {
285
+ this.scheduleReconnect();
286
+ }
287
+ }
288
+ }
289
+ disconnect() {
290
+ this.shouldReconnect = false;
291
+ this.stopPing();
292
+ this.clearReconnectTimer();
293
+ if (this.ws) {
294
+ this.ws.close();
295
+ this.ws = null;
296
+ }
297
+ this.setStatus("disconnected");
298
+ }
299
+ /** Disconnect and clear all subscription state and listeners. */
300
+ destroy() {
301
+ this.disconnect();
302
+ for (const ch of this.subs.channels.values()) ch.clear();
303
+ for (const key of Object.keys(this.listeners)) {
304
+ this.listeners[key].length = 0;
305
+ }
306
+ }
307
+ getStatus() {
308
+ return this.status;
309
+ }
310
+ isConnected() {
311
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
312
+ }
313
+ // -------------------------------------------------------------------------
314
+ // Subscription — low-level (multi-channel, multi-slug)
315
+ // -------------------------------------------------------------------------
316
+ /**
317
+ * Subscribe to one or more channels for the given market slugs.
318
+ * The server acknowledges with a `subscribed` message.
319
+ */
320
+ subscribe(channels, marketSlugs) {
321
+ for (const ch of channels) {
322
+ const set = this.subs.channels.get(ch);
323
+ for (const slug of marketSlugs) set.add(slug);
324
+ }
325
+ this.send({ type: "subscribe", channels, market_slugs: marketSlugs });
326
+ }
327
+ /**
328
+ * Unsubscribe from one or more channels for the given market slugs.
329
+ */
330
+ unsubscribe(channels, marketSlugs) {
331
+ for (const ch of channels) {
332
+ const set = this.subs.channels.get(ch);
333
+ for (const slug of marketSlugs) set.delete(slug);
334
+ }
335
+ this.send({ type: "unsubscribe", channels, market_slugs: marketSlugs });
336
+ }
337
+ // -------------------------------------------------------------------------
338
+ // Subscription — convenience (single channel)
339
+ // -------------------------------------------------------------------------
340
+ /**
341
+ * Subscribe to price updates for the given slugs.
342
+ * Returns an unsubscribe function.
343
+ */
344
+ subscribePrices(slugs, onUpdate) {
345
+ this.subscribe(["prices"], slugs);
346
+ const off = this.on("prices", onUpdate);
347
+ return () => {
348
+ off();
349
+ this.unsubscribe(["prices"], slugs);
350
+ };
351
+ }
352
+ /**
353
+ * Subscribe to orderbook snapshots for the given slugs.
354
+ * Returns an unsubscribe function.
355
+ */
356
+ subscribeOrderbook(slugs, onUpdate) {
357
+ this.subscribe(["orderbook"], slugs);
358
+ const off = this.on("orderbook", onUpdate);
359
+ return () => {
360
+ off();
361
+ this.unsubscribe(["orderbook"], slugs);
362
+ };
363
+ }
364
+ /**
365
+ * Subscribe to trade events for the given slugs.
366
+ * Returns an unsubscribe function.
367
+ */
368
+ subscribeTrades(slugs, onUpdate) {
369
+ this.subscribe(["trades"], slugs);
370
+ const off = this.on("trades", onUpdate);
371
+ return () => {
372
+ off();
373
+ this.unsubscribe(["trades"], slugs);
374
+ };
375
+ }
376
+ // -------------------------------------------------------------------------
377
+ // Status observation
378
+ // -------------------------------------------------------------------------
379
+ /** Register a callback for connection status changes. Returns unsubscribe. */
380
+ onStatusChange(cb) {
381
+ return this.on("status", cb);
382
+ }
383
+ /** Register a callback for server-sent errors. Returns unsubscribe. */
384
+ onError(cb) {
385
+ return this.on("error", cb);
386
+ }
387
+ // -------------------------------------------------------------------------
388
+ // Event emitter
389
+ // -------------------------------------------------------------------------
390
+ on(event, callback) {
391
+ const list = this.listeners[event];
392
+ list.push(callback);
393
+ return () => this.off(event, callback);
394
+ }
395
+ off(event, callback) {
396
+ const list = this.listeners[event];
397
+ const idx = list.indexOf(callback);
398
+ if (idx !== -1) list.splice(idx, 1);
399
+ }
400
+ // -------------------------------------------------------------------------
401
+ // Private
402
+ // -------------------------------------------------------------------------
403
+ emit(event, data) {
404
+ const list = this.listeners[event];
405
+ for (const cb of list) {
406
+ try {
407
+ cb(data);
408
+ } catch (e) {
409
+ }
410
+ }
411
+ }
412
+ send(msg) {
413
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
414
+ try {
415
+ this.ws.send(JSON.stringify(msg));
416
+ return true;
417
+ } catch {
418
+ return false;
419
+ }
420
+ }
421
+ setStatus(next) {
422
+ if (this.status !== next) {
423
+ this.status = next;
424
+ this.emit("status", next);
425
+ }
426
+ }
427
+ handleMessage(raw) {
428
+ let msg;
429
+ try {
430
+ msg = JSON.parse(raw);
431
+ } catch {
432
+ return;
433
+ }
434
+ if (msg.type) {
435
+ switch (msg.type) {
436
+ case "pong":
437
+ this.clearPongTimer();
438
+ return;
439
+ case "subscribed":
440
+ this.emit("subscribed", {
441
+ channels: msg.channels,
442
+ market_slugs: msg.market_slugs
443
+ });
444
+ return;
445
+ case "error":
446
+ this.emit("error", {
447
+ code: msg.code,
448
+ message: msg.message
449
+ });
450
+ return;
451
+ }
452
+ }
453
+ if (msg.channel && msg.data) {
454
+ const dataMsg = msg;
455
+ switch (dataMsg.channel) {
456
+ case "orderbook":
457
+ this.emit("orderbook", dataMsg);
458
+ break;
459
+ case "prices":
460
+ this.emit("prices", dataMsg);
461
+ break;
462
+ case "trades":
463
+ this.emit("trades", dataMsg);
464
+ break;
465
+ }
466
+ }
467
+ }
468
+ scheduleReconnect() {
469
+ if (this.reconnectTimer) return;
470
+ const delay = Math.min(
471
+ this.reconnectBase * Math.pow(2, this.reconnectAttempts),
472
+ this.reconnectMax
473
+ );
474
+ this.setStatus("reconnecting");
475
+ this.emit("reconnecting", { attempt: this.reconnectAttempts + 1, delay });
476
+ this.reconnectTimer = setTimeout(() => {
477
+ this.reconnectTimer = null;
478
+ this.reconnectAttempts++;
479
+ this.connect();
480
+ }, delay);
481
+ }
482
+ clearReconnectTimer() {
483
+ if (this.reconnectTimer) {
484
+ clearTimeout(this.reconnectTimer);
485
+ this.reconnectTimer = null;
486
+ }
487
+ }
488
+ restoreSubscriptions() {
489
+ for (const [channel, slugs] of this.subs.channels.entries()) {
490
+ if (slugs.size > 0) {
491
+ this.send({
492
+ type: "subscribe",
493
+ channels: [channel],
494
+ market_slugs: Array.from(slugs)
495
+ });
496
+ }
497
+ }
498
+ }
499
+ startPing() {
500
+ this.stopPing();
501
+ this.pingTimer = setInterval(() => {
502
+ this.send({ type: "ping" });
503
+ this.armPongTimeout();
504
+ }, this.pingMs);
505
+ }
506
+ stopPing() {
507
+ if (this.pingTimer) {
508
+ clearInterval(this.pingTimer);
509
+ this.pingTimer = null;
510
+ }
511
+ this.clearPongTimer();
512
+ }
513
+ /**
514
+ * Start a timer that fires if the server does not reply with pong
515
+ * within `pongTimeoutMs`. On timeout the socket is closed so
516
+ * the normal reconnect flow kicks in.
517
+ */
518
+ armPongTimeout() {
519
+ this.clearPongTimer();
520
+ this.pongTimer = setTimeout(() => {
521
+ this.pongTimer = null;
522
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
523
+ this.ws.close(4e3, "pong timeout");
524
+ }
525
+ }, this.pongTimeoutMs);
526
+ }
527
+ clearPongTimer() {
528
+ if (this.pongTimer) {
529
+ clearTimeout(this.pongTimer);
530
+ this.pongTimer = null;
531
+ }
532
+ }
533
+ };
534
+ function createPredictWsClient(config) {
535
+ return new PredictWsClient(config);
536
+ }
537
+ var PredictContext = createContext(null);
538
+ function PredictProvider({
539
+ client,
540
+ wsClient,
541
+ children
542
+ }) {
543
+ const ws = wsClient ?? null;
544
+ const value = useMemo(() => ({ client, wsClient: ws }), [client, ws]);
545
+ return /* @__PURE__ */ jsx(PredictContext.Provider, { value, children });
546
+ }
547
+
548
+ // src/utils/polymarket-hmac.ts
549
+ function encode(str) {
550
+ return new TextEncoder().encode(str);
551
+ }
552
+ function base64ToBytes(b64) {
553
+ const binary = atob(b64);
554
+ const bytes = new Uint8Array(binary.length);
555
+ for (let i = 0; i < binary.length; i++) {
556
+ bytes[i] = binary.charCodeAt(i);
557
+ }
558
+ return bytes;
559
+ }
560
+ function bytesToBase64(bytes) {
561
+ let binary = "";
562
+ for (let i = 0; i < bytes.byteLength; i++) {
563
+ binary += String.fromCharCode(bytes[i]);
564
+ }
565
+ return btoa(binary);
566
+ }
567
+ async function hmacSha256Base64(secretBase64, message) {
568
+ const keyBytes = base64ToBytes(secretBase64);
569
+ const cryptoKey = await crypto.subtle.importKey(
570
+ "raw",
571
+ keyBytes.buffer,
572
+ { name: "HMAC", hash: "SHA-256" },
573
+ false,
574
+ ["sign"]
575
+ );
576
+ const msgBytes = encode(message);
577
+ const signature = await crypto.subtle.sign(
578
+ "HMAC",
579
+ cryptoKey,
580
+ msgBytes.buffer
581
+ );
582
+ return bytesToBase64(new Uint8Array(signature));
583
+ }
584
+ async function buildPolymarketL2Headers(address, input) {
585
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
586
+ const body = input.body ?? "";
587
+ const message = `${timestamp}${input.method}${input.requestPath}${body}`;
588
+ const signature = await hmacSha256Base64(input.secret, message);
589
+ return {
590
+ POLY_ADDRESS: address,
591
+ POLY_SIGNATURE: signature,
592
+ POLY_TIMESTAMP: timestamp,
593
+ POLY_API_KEY: input.apiKey,
594
+ POLY_PASSPHRASE: input.passphrase
595
+ };
596
+ }
597
+ var CLOB_AUTH_DOMAIN = {
598
+ name: "ClobAuthDomain",
599
+ version: "1",
600
+ chainId: 137
601
+ };
602
+ var CLOB_AUTH_TYPES = {
603
+ ClobAuth: [
604
+ { name: "address", type: "address" },
605
+ { name: "timestamp", type: "string" },
606
+ { name: "nonce", type: "uint256" },
607
+ { name: "message", type: "string" }
608
+ ]
609
+ };
610
+ function buildClobAuthMessage(input) {
611
+ return {
612
+ address: input.address,
613
+ timestamp: input.timestamp,
614
+ nonce: input.nonce,
615
+ message: "This message attests that I control the given wallet"
616
+ };
617
+ }
618
+ async function derivePolymarketApiKey(address, signature, timestamp, nonce) {
619
+ const res = await fetch(
620
+ `https://clob.polymarket.com/auth/derive-api-key?nonce=${nonce}`,
621
+ {
622
+ method: "GET",
623
+ headers: {
624
+ "Content-Type": "application/json",
625
+ POLY_ADDRESS: address,
626
+ POLY_SIGNATURE: signature,
627
+ POLY_TIMESTAMP: timestamp,
628
+ POLY_NONCE: String(nonce)
629
+ }
630
+ }
631
+ );
632
+ if (!res.ok) {
633
+ const text = await res.text().catch(() => res.statusText);
634
+ throw new Error(
635
+ `Polymarket credential exchange failed (${res.status}): ${text}`
636
+ );
637
+ }
638
+ return res.json();
639
+ }
640
+ var PolymarketContext = createContext(
641
+ null
642
+ );
643
+ function PolymarketProvider({ children }) {
644
+ const [credentials, setCredentials] = useState(
645
+ null
646
+ );
647
+ const [isAuthenticating, setIsAuthenticating] = useState(false);
648
+ const [authError, setAuthError] = useState(null);
649
+ const lastAuthAddressRef = useRef(null);
650
+ const authenticate = useCallback(
651
+ async (signer) => {
652
+ if (lastAuthAddressRef.current === signer.address && credentials !== null) {
653
+ return;
654
+ }
655
+ setIsAuthenticating(true);
656
+ setAuthError(null);
657
+ try {
658
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
659
+ const nonce = 0;
660
+ const message = buildClobAuthMessage({
661
+ address: signer.address,
662
+ timestamp,
663
+ nonce
664
+ });
665
+ const signature = await signer.signTypedData(
666
+ CLOB_AUTH_DOMAIN,
667
+ CLOB_AUTH_TYPES,
668
+ message
669
+ );
670
+ const { apiKey, secret, passphrase } = await derivePolymarketApiKey(
671
+ signer.address,
672
+ signature,
673
+ timestamp,
674
+ nonce
675
+ );
676
+ lastAuthAddressRef.current = signer.address;
677
+ setCredentials({ apiKey, secret, passphrase, address: signer.address });
678
+ } catch (err) {
679
+ setAuthError(
680
+ err instanceof Error ? err : new Error("Polymarket authentication failed")
681
+ );
682
+ lastAuthAddressRef.current = null;
683
+ } finally {
684
+ setIsAuthenticating(false);
685
+ }
686
+ },
687
+ [credentials]
688
+ );
689
+ const clearCredentials = useCallback(() => {
690
+ setCredentials(null);
691
+ lastAuthAddressRef.current = null;
692
+ }, []);
693
+ return /* @__PURE__ */ jsx(
694
+ PolymarketContext.Provider,
695
+ {
696
+ value: {
697
+ credentials,
698
+ isAuthenticating,
699
+ authError,
700
+ authenticate,
701
+ clearCredentials
702
+ },
703
+ children
704
+ }
705
+ );
706
+ }
707
+ function usePredictClient() {
708
+ const context = useContext(PredictContext);
709
+ if (!context) {
710
+ throw new Error("usePredictClient must be used within a PredictProvider");
711
+ }
712
+ return context.client;
713
+ }
714
+ function eventsQueryKey(params) {
715
+ return ["predict", "events", params ?? {}];
716
+ }
717
+ async function fetchEvents(client, params) {
718
+ return client.listEvents(params);
719
+ }
720
+ function useEvents(params, queryOptions = {}) {
721
+ const client = usePredictClient();
722
+ return useQuery({
723
+ queryKey: eventsQueryKey(params),
724
+ queryFn: () => client.listEvents(params),
725
+ ...queryOptions
726
+ });
727
+ }
728
+
729
+ // src/hooks/predict/events.params.ts
730
+ var DEFAULT_PAGE_SIZE = 48;
731
+ function resolveTagSlug(selection) {
732
+ if (!selection) return void 0;
733
+ return selection.tagSlug ?? selection.categorySlug ?? void 0;
734
+ }
735
+ function resolveEventsParams(input = {}) {
736
+ const {
737
+ tagSlugSelection,
738
+ limit = DEFAULT_PAGE_SIZE,
739
+ status = "open",
740
+ with_markets = true,
741
+ source,
742
+ sort_by,
743
+ sort_asc
744
+ } = input;
745
+ const tag_slug = resolveTagSlug(tagSlugSelection);
746
+ return {
747
+ limit,
748
+ status,
749
+ with_markets,
750
+ ...source ? { source } : {},
751
+ ...tag_slug ? { tag_slug } : {},
752
+ ...sort_by ? { sort_by } : {},
753
+ ...sort_asc !== void 0 ? { sort_asc } : {}
754
+ };
755
+ }
756
+ function infiniteEventsQueryKey(params) {
757
+ return ["predict", "events-infinite", params];
758
+ }
759
+ async function fetchEventsPage(client, params) {
760
+ return client.listEvents(params);
761
+ }
762
+ function eventQueryKey(slug, source) {
763
+ return ["predict", "event", slug, source];
764
+ }
765
+ async function fetchEvent(client, slug, source) {
766
+ return client.getEvent(slug, source);
767
+ }
768
+
769
+ // src/hooks/predict/useEvent.ts
770
+ function useEvent(params, queryOptions = {}) {
771
+ const client = usePredictClient();
772
+ return useQuery({
773
+ queryKey: eventQueryKey(params.slug, params.source),
774
+ queryFn: () => fetchEvent(client, params.slug, params.source),
775
+ enabled: Boolean(params.slug),
776
+ ...queryOptions
777
+ });
778
+ }
779
+ function useInfiniteEvents(params, queryOptions = {}) {
780
+ const client = usePredictClient();
781
+ return useInfiniteQuery({
782
+ queryKey: infiniteEventsQueryKey(params),
783
+ queryFn: ({ pageParam }) => fetchEventsPage(client, {
784
+ ...params,
785
+ ...pageParam !== void 0 ? { cursor: pageParam } : {}
786
+ }),
787
+ initialPageParam: void 0,
788
+ getNextPageParam: (lastPage) => lastPage.has_more && lastPage.next_cursor ? lastPage.next_cursor : void 0,
789
+ ...queryOptions
790
+ });
791
+ }
792
+ function searchEventsQueryKey(params) {
793
+ return ["predict", "search", params];
794
+ }
795
+ function useSearchEvents(params, queryOptions) {
796
+ const client = usePredictClient();
797
+ const apiParams = useMemo(
798
+ () => ({
799
+ search: params.keyword,
800
+ limit: params.limit ?? 20,
801
+ status: params.status ?? "open",
802
+ source: params.source,
803
+ with_markets: params.with_markets ?? false
804
+ }),
805
+ [
806
+ params.keyword,
807
+ params.limit,
808
+ params.status,
809
+ params.source,
810
+ params.with_markets
811
+ ]
812
+ );
813
+ return useInfiniteQuery({
814
+ queryKey: searchEventsQueryKey(apiParams),
815
+ queryFn: ({ pageParam }) => client.listEvents({
816
+ ...apiParams,
817
+ ...pageParam !== void 0 ? { cursor: pageParam } : {}
818
+ }),
819
+ initialPageParam: void 0,
820
+ getNextPageParam: (lastPage) => lastPage.has_more && lastPage.next_cursor ? lastPage.next_cursor : void 0,
821
+ enabled: queryOptions?.enabled ?? !!params.keyword
822
+ });
823
+ }
824
+ function similarEventsQueryKey(slug, source, params) {
825
+ return ["predict", "similar-events", slug, source, params];
826
+ }
827
+ function useSimilarEvents(params, queryOptions = {}) {
828
+ const client = usePredictClient();
829
+ const apiParams = {
830
+ limit: params.limit,
831
+ same_source: params.same_source
832
+ };
833
+ return useQuery({
834
+ queryKey: similarEventsQueryKey(params.slug, params.source, apiParams),
835
+ queryFn: () => client.getSimilarEvents(params.slug, params.source, apiParams),
836
+ enabled: Boolean(params.slug),
837
+ staleTime: 5 * 6e4,
838
+ ...queryOptions
839
+ });
840
+ }
841
+ function marketQueryKey(slug, source) {
842
+ return ["predict", "market", slug, source];
843
+ }
844
+ async function fetchMarket(client, slug, source) {
845
+ return client.getMarket(slug, source);
846
+ }
847
+ function useMarket(params, queryOptions = {}) {
848
+ const client = usePredictClient();
849
+ return useQuery({
850
+ queryKey: marketQueryKey(params.slug, params.source),
851
+ queryFn: () => fetchMarket(client, params.slug, params.source),
852
+ enabled: Boolean(params.slug),
853
+ ...queryOptions
854
+ });
855
+ }
856
+ function priceHistoryQueryKey(slug, source, range) {
857
+ return ["predict", "price-history", slug, source, range];
858
+ }
859
+ function usePriceHistory(params, queryOptions = {}) {
860
+ const client = usePredictClient();
861
+ return useQuery({
862
+ queryKey: priceHistoryQueryKey(params.slug, params.source, params.range),
863
+ queryFn: () => client.getPriceHistory(params.slug, params.source, params.range),
864
+ enabled: Boolean(params.slug),
865
+ staleTime: 6e4,
866
+ ...queryOptions
867
+ });
868
+ }
869
+
870
+ // src/hooks/predict/useMarketHistory.ts
871
+ var ChartRange = {
872
+ ONE_DAY: "1d",
873
+ ONE_WEEK: "1w",
874
+ ONE_MONTH: "1m",
875
+ ALL: "all"
876
+ };
877
+ var RANGE_MAP = {
878
+ [ChartRange.ONE_DAY]: "1d",
879
+ [ChartRange.ONE_WEEK]: "1w",
880
+ [ChartRange.ONE_MONTH]: "1m",
881
+ [ChartRange.ALL]: "all"
882
+ };
883
+ function useMarketHistory(markets, range = ChartRange.ALL) {
884
+ const client = usePredictClient();
885
+ const apiRange = RANGE_MAP[range];
886
+ const queries = useQueries({
887
+ queries: markets.map((market) => ({
888
+ queryKey: priceHistoryQueryKey(market.slug, market.source, apiRange),
889
+ queryFn: () => client.getPriceHistory(market.slug, market.source, apiRange),
890
+ staleTime: 6e4,
891
+ enabled: Boolean(market.slug)
892
+ }))
893
+ });
894
+ const isLoading = queries.some((q) => q.isLoading);
895
+ const series = useMemo(() => {
896
+ return markets.map((market, idx) => {
897
+ const result = queries[idx];
898
+ const points = result?.data?.points ?? [];
899
+ if (points.length > 0) {
900
+ return {
901
+ marketSlug: market.slug,
902
+ label: market.outcomes?.[0]?.label ?? market.question,
903
+ data: points.map((pt) => ({
904
+ timestamp: pt.t * 1e3,
905
+ price: pt.p
906
+ }))
907
+ };
908
+ }
909
+ const yesOutcome = market.outcomes?.find((o) => o.label.toLowerCase() === "yes") ?? market.outcomes?.[0];
910
+ const currentPrice = yesOutcome?.price ?? 0.5;
911
+ const now = Date.now();
912
+ const dayMs = 864e5;
913
+ return {
914
+ marketSlug: market.slug,
915
+ label: market.outcomes?.[0]?.label ?? market.question,
916
+ data: [
917
+ { timestamp: now - 7 * dayMs, price: currentPrice },
918
+ { timestamp: now, price: currentPrice }
919
+ ]
920
+ };
921
+ });
922
+ }, [markets, queries]);
923
+ return { series, isLoading };
924
+ }
925
+ function orderbookQueryKey(slug, source) {
926
+ return ["predict", "orderbook", slug, source];
927
+ }
928
+ function useOrderbook(params, queryOptions = {}) {
929
+ const client = usePredictClient();
930
+ return useQuery({
931
+ queryKey: orderbookQueryKey(params.slug, params.source),
932
+ queryFn: () => client.getOrderbook(params.slug, params.source),
933
+ enabled: Boolean(params.slug),
934
+ refetchInterval: 5e3,
935
+ ...queryOptions
936
+ });
937
+ }
938
+ function marketTradesQueryKey(slug, params) {
939
+ return ["predict", "market-trades", slug, params];
940
+ }
941
+ function useMarketTrades(params, queryOptions = {}) {
942
+ const client = usePredictClient();
943
+ const apiParams = {
944
+ source: params.source,
945
+ limit: params.limit,
946
+ cursor: params.cursor,
947
+ side: params.side
948
+ };
949
+ return useQuery({
950
+ queryKey: marketTradesQueryKey(params.slug, apiParams),
951
+ queryFn: () => client.listMarketTrades(params.slug, apiParams),
952
+ enabled: Boolean(params.slug),
953
+ ...queryOptions
954
+ });
955
+ }
956
+ function candlesticksQueryKey(slug, params) {
957
+ return ["predict", "candlesticks", slug, params];
958
+ }
959
+ function useCandlesticks(params, queryOptions = {}) {
960
+ const client = usePredictClient();
961
+ const apiParams = {
962
+ interval: params.interval,
963
+ limit: params.limit
964
+ };
965
+ return useQuery({
966
+ queryKey: candlesticksQueryKey(params.slug, apiParams),
967
+ queryFn: () => client.listCandlesticks(params.slug, apiParams),
968
+ enabled: Boolean(params.slug),
969
+ staleTime: 6e4,
970
+ ...queryOptions
971
+ });
972
+ }
973
+ function positionsQueryKey(user, source) {
974
+ return ["predict", "positions", source ?? "all", user];
975
+ }
976
+ function usePositions(params, queryOptions = {}) {
977
+ const client = usePredictClient();
978
+ return useQuery({
979
+ queryKey: positionsQueryKey(params.user, params.source),
980
+ queryFn: () => client.getPositions(params.user, params.source),
981
+ enabled: Boolean(params.user),
982
+ staleTime: 1e4,
983
+ ...queryOptions
984
+ });
985
+ }
986
+ function balanceQueryKey(source, user) {
987
+ return ["predict", "balance", source, user];
988
+ }
989
+ function useBalance(params, queryOptions = {}) {
990
+ const client = usePredictClient();
991
+ return useQuery({
992
+ queryKey: balanceQueryKey(params.source, params.user),
993
+ queryFn: () => client.getBalance(params.source, params.user),
994
+ enabled: Boolean(params.user && params.source),
995
+ staleTime: 1e4,
996
+ ...queryOptions
997
+ });
998
+ }
999
+ function ordersQueryKey(params) {
1000
+ return ["predict", "orders", params];
1001
+ }
1002
+ function useOrders(params, queryOptions = {}) {
1003
+ const client = usePredictClient();
1004
+ return useQuery({
1005
+ queryKey: ordersQueryKey(params),
1006
+ queryFn: () => client.listOrders(params),
1007
+ enabled: Boolean(params.wallet_address),
1008
+ ...queryOptions
1009
+ });
1010
+ }
1011
+ function orderQueryKey(id, source) {
1012
+ return ["predict", "order", id, source];
1013
+ }
1014
+ function useOrder(params, queryOptions = {}) {
1015
+ const client = usePredictClient();
1016
+ return useQuery({
1017
+ queryKey: orderQueryKey(params.id, params.source),
1018
+ queryFn: () => client.getOrder(params.id, params.source),
1019
+ enabled: Boolean(params.id),
1020
+ refetchInterval: 1e3,
1021
+ ...queryOptions
1022
+ });
1023
+ }
1024
+ function useCancelOrder(mutationOptions = {}) {
1025
+ const client = usePredictClient();
1026
+ const queryClient = useQueryClient();
1027
+ return useMutation({
1028
+ mutationFn: (vars) => client.cancelOrder(vars.id, vars.source),
1029
+ onSuccess: () => {
1030
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1031
+ },
1032
+ ...mutationOptions
1033
+ });
1034
+ }
1035
+ function tradesQueryKey(params) {
1036
+ return ["predict", "trades-by-wallet", params];
1037
+ }
1038
+ function useTrades(params, queryOptions = {}) {
1039
+ const client = usePredictClient();
1040
+ return useQuery({
1041
+ queryKey: tradesQueryKey(params),
1042
+ queryFn: () => client.listTrades(params),
1043
+ enabled: Boolean(params.wallet),
1044
+ ...queryOptions
1045
+ });
1046
+ }
1047
+ function dflowQuoteQueryKey(params) {
1048
+ return ["predict", "dflow-quote", params];
1049
+ }
1050
+ function useDFlowQuote(params, queryOptions = {}) {
1051
+ const client = usePredictClient();
1052
+ const enabled = Boolean(params.inputMint) && Boolean(params.outputMint) && Boolean(params.userPublicKey) && params.amount !== "0" && params.amount !== "";
1053
+ return useQuery({
1054
+ queryKey: dflowQuoteQueryKey(params),
1055
+ queryFn: () => client.createDFlowQuote(params),
1056
+ enabled,
1057
+ staleTime: 1e4,
1058
+ ...queryOptions
1059
+ });
1060
+ }
1061
+ function useDFlowSubmit(mutationOptions = {}) {
1062
+ const client = usePredictClient();
1063
+ const queryClient = useQueryClient();
1064
+ return useMutation({
1065
+ mutationFn: (body) => client.submitDFlowTransaction(body),
1066
+ onSuccess: () => {
1067
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1068
+ queryClient.invalidateQueries({ queryKey: ["predict", "positions"] });
1069
+ },
1070
+ ...mutationOptions
1071
+ });
1072
+ }
1073
+ function usePredictWsClient() {
1074
+ const context = useContext(PredictContext);
1075
+ if (!context) {
1076
+ throw new Error("usePredictWsClient must be used within a PredictProvider");
1077
+ }
1078
+ const { wsClient } = context;
1079
+ const [wsStatus, setWsStatus] = useState(
1080
+ wsClient?.getStatus() ?? "disconnected"
1081
+ );
1082
+ useEffect(() => {
1083
+ if (!wsClient) {
1084
+ setWsStatus("disconnected");
1085
+ return;
1086
+ }
1087
+ setWsStatus(wsClient.getStatus());
1088
+ return wsClient.onStatusChange(setWsStatus);
1089
+ }, [wsClient]);
1090
+ return {
1091
+ wsClient,
1092
+ wsStatus,
1093
+ isWsConnected: wsStatus === "connected"
1094
+ };
1095
+ }
1096
+ function usePricesSubscription({
1097
+ wsClient,
1098
+ slugs,
1099
+ enabled = true,
1100
+ onUpdate
1101
+ }) {
1102
+ const [prices, setPrices] = useState(
1103
+ () => /* @__PURE__ */ new Map()
1104
+ );
1105
+ const [isSubscribed, setIsSubscribed] = useState(false);
1106
+ const onUpdateRef = useRef(onUpdate);
1107
+ onUpdateRef.current = onUpdate;
1108
+ const slugsKey = slugs.join(",");
1109
+ const handleUpdate = useCallback((msg) => {
1110
+ setPrices((prev) => {
1111
+ const next = new Map(prev);
1112
+ next.set(msg.data.market_slug, msg.data);
1113
+ return next;
1114
+ });
1115
+ onUpdateRef.current?.(msg);
1116
+ }, []);
1117
+ useEffect(() => {
1118
+ if (!wsClient || !enabled || slugs.length === 0) {
1119
+ setIsSubscribed(false);
1120
+ return;
1121
+ }
1122
+ const unsub = wsClient.subscribePrices(slugs, handleUpdate);
1123
+ setIsSubscribed(true);
1124
+ return () => {
1125
+ unsub();
1126
+ setIsSubscribed(false);
1127
+ };
1128
+ }, [wsClient, enabled, slugsKey, handleUpdate]);
1129
+ return { prices, isSubscribed };
1130
+ }
1131
+ function useOrderbookSubscription({
1132
+ wsClient,
1133
+ slug,
1134
+ enabled = true,
1135
+ onUpdate
1136
+ }) {
1137
+ const [orderbook, setOrderbook] = useState(null);
1138
+ const [isSubscribed, setIsSubscribed] = useState(false);
1139
+ const onUpdateRef = useRef(onUpdate);
1140
+ onUpdateRef.current = onUpdate;
1141
+ const handleUpdate = useCallback(
1142
+ (msg) => {
1143
+ if (msg.data.market_slug === slug) {
1144
+ setOrderbook(msg.data);
1145
+ onUpdateRef.current?.(msg);
1146
+ }
1147
+ },
1148
+ [slug]
1149
+ );
1150
+ useEffect(() => {
1151
+ if (!wsClient || !enabled || !slug) {
1152
+ setIsSubscribed(false);
1153
+ return;
1154
+ }
1155
+ setOrderbook(null);
1156
+ const unsub = wsClient.subscribeOrderbook([slug], handleUpdate);
1157
+ setIsSubscribed(true);
1158
+ return () => {
1159
+ unsub();
1160
+ setIsSubscribed(false);
1161
+ };
1162
+ }, [wsClient, enabled, slug, handleUpdate]);
1163
+ return { orderbook, isSubscribed };
1164
+ }
1165
+ var DEFAULT_MAX_HISTORY = 100;
1166
+ function useTradesSubscription({
1167
+ wsClient,
1168
+ slug,
1169
+ enabled = true,
1170
+ maxHistory = DEFAULT_MAX_HISTORY,
1171
+ onUpdate
1172
+ }) {
1173
+ const [trades, setTrades] = useState([]);
1174
+ const [isSubscribed, setIsSubscribed] = useState(false);
1175
+ const onUpdateRef = useRef(onUpdate);
1176
+ onUpdateRef.current = onUpdate;
1177
+ const maxRef = useRef(maxHistory);
1178
+ maxRef.current = maxHistory;
1179
+ const handleUpdate = useCallback(
1180
+ (msg) => {
1181
+ if (msg.data.market_slug === slug) {
1182
+ setTrades((prev) => {
1183
+ const next = [msg.data, ...prev];
1184
+ return next.length > maxRef.current ? next.slice(0, maxRef.current) : next;
1185
+ });
1186
+ onUpdateRef.current?.(msg);
1187
+ }
1188
+ },
1189
+ [slug]
1190
+ );
1191
+ const clearHistory = useCallback(() => {
1192
+ setTrades([]);
1193
+ }, []);
1194
+ useEffect(() => {
1195
+ if (!wsClient || !enabled || !slug) {
1196
+ setIsSubscribed(false);
1197
+ return;
1198
+ }
1199
+ setTrades([]);
1200
+ const unsub = wsClient.subscribeTrades([slug], handleUpdate);
1201
+ setIsSubscribed(true);
1202
+ return () => {
1203
+ unsub();
1204
+ setIsSubscribed(false);
1205
+ };
1206
+ }, [wsClient, enabled, slug, handleUpdate]);
1207
+ return { trades, isSubscribed, clearHistory };
1208
+ }
1209
+ var REST_POLL_INTERVAL = 5e3;
1210
+ function useRealtimeOrderbook(params, queryOptions = {}) {
1211
+ const { wsClient } = usePredictWsClient();
1212
+ const queryClient = useQueryClient();
1213
+ const subParams = {
1214
+ wsClient,
1215
+ slug: params.slug,
1216
+ enabled: Boolean(params.slug)
1217
+ };
1218
+ const { isSubscribed } = useOrderbookSubscription(subParams);
1219
+ const qk = orderbookQueryKey(params.slug, params.source);
1220
+ const qkRef = useRef(qk);
1221
+ qkRef.current = qk;
1222
+ useEffect(() => {
1223
+ if (!wsClient || !params.slug) return;
1224
+ const unsub = wsClient.subscribeOrderbook([params.slug], (msg) => {
1225
+ const ob = {
1226
+ market_id: msg.data.market_slug,
1227
+ bids: msg.data.bids,
1228
+ asks: msg.data.asks,
1229
+ spread: msg.data.spread
1230
+ };
1231
+ queryClient.setQueryData(qkRef.current, ob);
1232
+ });
1233
+ return unsub;
1234
+ }, [wsClient, params.slug, queryClient]);
1235
+ return useOrderbook(params, {
1236
+ refetchInterval: isSubscribed ? false : REST_POLL_INTERVAL,
1237
+ ...queryOptions
1238
+ });
1239
+ }
1240
+
1241
+ // src/hooks/predict/useRealtimePrices.ts
1242
+ function useRealtimePrices({
1243
+ slugs,
1244
+ enabled = true,
1245
+ onUpdate
1246
+ }) {
1247
+ const { wsClient } = usePredictWsClient();
1248
+ return usePricesSubscription({
1249
+ wsClient,
1250
+ slugs,
1251
+ enabled,
1252
+ onUpdate
1253
+ });
1254
+ }
1255
+ function useRealtimeTrades({
1256
+ slug,
1257
+ enabled = true,
1258
+ maxHistory,
1259
+ onUpdate,
1260
+ syncToQueryCache = true
1261
+ }) {
1262
+ const { wsClient } = usePredictWsClient();
1263
+ const queryClient = useQueryClient();
1264
+ const slugRef = useRef(slug);
1265
+ slugRef.current = slug;
1266
+ useEffect(() => {
1267
+ if (!wsClient || !slug || !enabled || !syncToQueryCache) return;
1268
+ const unsub = wsClient.subscribeTrades([slug], (msg) => {
1269
+ const trade = msg.data;
1270
+ const prefix = ["predict", "market-trades", slugRef.current];
1271
+ queryClient.setQueriesData(
1272
+ { queryKey: prefix },
1273
+ (old) => {
1274
+ if (!old) return old;
1275
+ const syntheticTrade = {
1276
+ id: trade.trade_id ?? `ws-${msg.ts}`,
1277
+ source: "dflow",
1278
+ side: trade.side,
1279
+ outcome: trade.outcome ?? "",
1280
+ outcome_index: 0,
1281
+ price: trade.price,
1282
+ size: trade.size,
1283
+ usd_size: trade.size,
1284
+ timestamp: Math.floor(msg.ts / 1e3),
1285
+ tx_hash: "",
1286
+ wallet: "",
1287
+ type: "TRADE"
1288
+ };
1289
+ return {
1290
+ ...old,
1291
+ items: [syntheticTrade, ...old.items]
1292
+ };
1293
+ }
1294
+ );
1295
+ });
1296
+ return unsub;
1297
+ }, [wsClient, slug, enabled, syncToQueryCache, queryClient]);
1298
+ return useTradesSubscription({
1299
+ wsClient,
1300
+ slug,
1301
+ enabled,
1302
+ maxHistory,
1303
+ onUpdate
1304
+ });
1305
+ }
1306
+ function usePolymarket() {
1307
+ const context = useContext(PolymarketContext);
1308
+ if (!context) {
1309
+ throw new Error("usePolymarket must be used within a PolymarketProvider");
1310
+ }
1311
+ return context;
1312
+ }
1313
+
1314
+ // src/utils/polymarket-order.ts
1315
+ var CTF_EXCHANGE_ADDRESS = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E";
1316
+ var NEG_RISK_CTF_EXCHANGE_ADDRESS = "0xC5d563A36AE78145C45a50134d48A1215220f80a";
1317
+ var USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
1318
+ var POLYGON_CHAIN_ID = 137;
1319
+ function buildCtfExchangeDomain(negRisk = false) {
1320
+ return {
1321
+ name: "CTFExchange",
1322
+ version: "1",
1323
+ chainId: POLYGON_CHAIN_ID,
1324
+ verifyingContract: negRisk ? NEG_RISK_CTF_EXCHANGE_ADDRESS : CTF_EXCHANGE_ADDRESS
1325
+ };
1326
+ }
1327
+ var CTF_ORDER_TYPES = {
1328
+ Order: [
1329
+ { name: "salt", type: "uint256" },
1330
+ { name: "maker", type: "address" },
1331
+ { name: "signer", type: "address" },
1332
+ { name: "taker", type: "address" },
1333
+ { name: "tokenId", type: "uint256" },
1334
+ { name: "makerAmount", type: "uint256" },
1335
+ { name: "takerAmount", type: "uint256" },
1336
+ { name: "expiration", type: "uint256" },
1337
+ { name: "nonce", type: "uint256" },
1338
+ { name: "feeRateBps", type: "uint256" },
1339
+ { name: "side", type: "uint8" },
1340
+ { name: "signatureType", type: "uint8" }
1341
+ ]
1342
+ };
1343
+ var ORDER_TYPE = {
1344
+ GTC: 0,
1345
+ // Good-Till-Cancelled
1346
+ FOK: 1,
1347
+ // Fill-Or-Kill
1348
+ GTD: 2,
1349
+ // Good-Till-Date
1350
+ FAK: 3
1351
+ // Fill-And-Kill
1352
+ };
1353
+ var SIDE = { BUY: 0, SELL: 1 };
1354
+ var TICK_DECIMALS = {
1355
+ "0.1": 1,
1356
+ "0.01": 2,
1357
+ "0.001": 3,
1358
+ "0.0001": 4
1359
+ };
1360
+ function roundToTick(price, tickSize) {
1361
+ const decimals = TICK_DECIMALS[tickSize] ?? 2;
1362
+ return price.toFixed(decimals);
1363
+ }
1364
+ function toMicroUsdc(amount) {
1365
+ return BigInt(Math.round(amount * 1e6));
1366
+ }
1367
+ function buildOrderMessage(input) {
1368
+ const side = input.side === "BUY" ? SIDE.BUY : SIDE.SELL;
1369
+ const priceStr = roundToTick(input.price, input.tickSize);
1370
+ const priceNum = parseFloat(priceStr);
1371
+ const sizeShares = toMicroUsdc(input.size);
1372
+ const sizeUsdc = toMicroUsdc(input.size * priceNum);
1373
+ const makerAmount = side === SIDE.BUY ? sizeUsdc.toString() : sizeShares.toString();
1374
+ const takerAmount = side === SIDE.BUY ? sizeShares.toString() : sizeUsdc.toString();
1375
+ const maker = input.funderAddress ?? input.signerAddress;
1376
+ return {
1377
+ salt: Math.floor(Math.random() * 1e15).toString(),
1378
+ maker,
1379
+ signer: input.signerAddress,
1380
+ taker: "0x0000000000000000000000000000000000000000",
1381
+ tokenId: input.tokenId,
1382
+ makerAmount,
1383
+ takerAmount,
1384
+ expiration: String(input.expiration ?? 0),
1385
+ nonce: "0",
1386
+ feeRateBps: "0",
1387
+ side,
1388
+ signatureType: input.signatureType
1389
+ };
1390
+ }
1391
+ function buildSignedOrder(input) {
1392
+ const message = buildOrderMessage(input);
1393
+ return {
1394
+ ...message,
1395
+ signature: input.signature,
1396
+ orderType: input.orderType ?? "GTC"
1397
+ };
1398
+ }
1399
+
1400
+ // src/hooks/polymarket/useCreatePolymarketOrder.ts
1401
+ function useCreatePolymarketOrder(mutationOptions = {}) {
1402
+ const client = usePredictClient();
1403
+ const { credentials, authenticate } = usePolymarket();
1404
+ const queryClient = useQueryClient();
1405
+ return useMutation({
1406
+ mutationFn: async ({
1407
+ input,
1408
+ signer
1409
+ }) => {
1410
+ const creds = credentials;
1411
+ if (!creds) {
1412
+ await authenticate(signer);
1413
+ if (!creds) {
1414
+ throw new Error(
1415
+ "Polymarket authentication failed. Cannot create order without credentials."
1416
+ );
1417
+ }
1418
+ }
1419
+ const domain = buildCtfExchangeDomain(input.negRisk ?? false);
1420
+ const orderMessage = buildOrderMessage({
1421
+ ...input,
1422
+ signerAddress: signer.address,
1423
+ signatureType: signer.signatureType
1424
+ });
1425
+ const signature = await signer.signTypedData(
1426
+ domain,
1427
+ CTF_ORDER_TYPES,
1428
+ orderMessage
1429
+ );
1430
+ const signedOrder = buildSignedOrder({
1431
+ ...input,
1432
+ signerAddress: signer.address,
1433
+ signatureType: signer.signatureType,
1434
+ signature
1435
+ });
1436
+ const body = JSON.stringify(signedOrder);
1437
+ const headers = await buildPolymarketL2Headers(creds.address, {
1438
+ apiKey: creds.apiKey,
1439
+ secret: creds.secret,
1440
+ passphrase: creds.passphrase,
1441
+ method: "POST",
1442
+ requestPath: "/api/v1/orders/polymarket",
1443
+ body
1444
+ });
1445
+ return client.createPolymarketOrder(
1446
+ signedOrder,
1447
+ headers
1448
+ );
1449
+ },
1450
+ onSuccess: () => {
1451
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1452
+ queryClient.invalidateQueries({ queryKey: ["predict", "positions"] });
1453
+ },
1454
+ ...mutationOptions
1455
+ });
1456
+ }
1457
+
1458
+ export { CLOB_AUTH_DOMAIN, CLOB_AUTH_TYPES, CTF_EXCHANGE_ADDRESS, CTF_ORDER_TYPES, ChartRange, NEG_RISK_CTF_EXCHANGE_ADDRESS, ORDER_TYPE, POLYGON_CHAIN_ID, PolymarketContext, PolymarketProvider, PredictClient, PredictContext, PredictProvider, PredictWsClient, SIDE, USDC_ADDRESS, balanceQueryKey, buildClobAuthMessage, buildCtfExchangeDomain, buildOrderMessage, buildPolymarketL2Headers, buildSignedOrder, candlesticksQueryKey, createPredictClient, createPredictWsClient, derivePolymarketApiKey, dflowQuoteQueryKey, eventQueryKey, eventsQueryKey, fetchEvent, fetchEvents, fetchEventsPage, fetchMarket, hmacSha256Base64, infiniteEventsQueryKey, marketQueryKey, marketTradesQueryKey, orderQueryKey, orderbookQueryKey, ordersQueryKey, positionsQueryKey, priceHistoryQueryKey, resolveEventsParams, resolveTagSlug, similarEventsQueryKey, tradesQueryKey, useBalance, useCancelOrder, useCandlesticks, useCreatePolymarketOrder, useDFlowQuote, useDFlowSubmit, useEvent, useEvents, useInfiniteEvents, useMarket, useMarketHistory, useMarketTrades, useOrder, useOrderbook, useOrderbookSubscription, useOrders, usePolymarket, usePositions, usePredictClient, usePredictWsClient, usePriceHistory, usePricesSubscription, useRealtimeOrderbook, useRealtimePrices, useRealtimeTrades, useSearchEvents, useSimilarEvents, useTrades, useTradesSubscription };
1459
+ //# sourceMappingURL=index.mjs.map
1460
+ //# sourceMappingURL=index.mjs.map