@agg-build/hooks 4.0.1 → 4.5.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.
package/dist/index.js CHANGED
@@ -84,9 +84,11 @@ __export(src_exports, {
84
84
  MarketStatus: () => MarketStatus,
85
85
  MatchStatus: () => MatchStatus,
86
86
  MatchType: () => MatchType,
87
- QueryClient: () => import_react_query44.QueryClient,
88
- QueryClientProvider: () => import_react_query44.QueryClientProvider,
87
+ QueryClient: () => import_react_query46.QueryClient,
88
+ QueryClientProvider: () => import_react_query46.QueryClientProvider,
89
89
  RedeemRejectedError: () => RedeemRejectedError,
90
+ SPORTS_LIVE_POLL_INTERVAL_MS: () => SPORTS_LIVE_POLL_INTERVAL_MS,
91
+ SPORTS_SCHEDULED_POLL_INTERVAL_MS: () => SPORTS_SCHEDULED_POLL_INTERVAL_MS,
90
92
  TradeSide: () => TradeSide,
91
93
  TurnstileChallengeError: () => import_sdk4.TurnstileChallengeError,
92
94
  Venue: () => Venue,
@@ -97,6 +99,7 @@ __export(src_exports, {
97
99
  defaultSdkUiConfig: () => defaultAggUiConfig,
98
100
  executionKeys: () => executionKeys,
99
101
  extractOutcomeBestCandidate: () => extractOutcomeBestCandidate,
102
+ fetchRecurringCryptoMarkets: () => fetchRecurringCryptoMarkets,
100
103
  findLivePriceById: () => findLivePriceById,
101
104
  getBuilder: () => getBuilder,
102
105
  getDepositAddress: () => getDepositAddress,
@@ -112,11 +115,13 @@ __export(src_exports, {
112
115
  invalidateUserMoneyState: () => invalidateUserMoneyState,
113
116
  isVenueDisabledByConfig: () => isVenueDisabledByConfig,
114
117
  mergeBestPricesPreferringLive: () => mergeBestPricesPreferringLive,
118
+ mergeRecurringCryptoWindows: () => mergeRecurringCryptoWindows,
115
119
  normalizeVenueId: () => normalizeVenueId,
116
120
  optimizedImageUrl: () => optimizedImageUrl,
117
121
  parseEmail: () => parseEmail,
118
122
  parseEmailStrict: () => parseEmailStrict,
119
123
  rangeToSeconds: () => rangeToSeconds,
124
+ recurringCryptoTransitionPollInterval: () => recurringCryptoTransitionPollInterval,
120
125
  requestAggAuthChooserOpen: () => requestAggAuthChooserOpen,
121
126
  resolveAggUiLabels: () => resolveAggUiLabels,
122
127
  resolveDefaultTradingMarket: () => resolveDefaultMarket,
@@ -124,9 +129,13 @@ __export(src_exports, {
124
129
  resolveMarketTradingState: () => resolveMarketTradingState,
125
130
  resolveMarketWinningOutcome: () => resolveMarketWinningOutcome,
126
131
  resolveOrderEligibility: () => resolveOrderEligibility,
132
+ resolveRecurringCryptoIdentity: () => resolveRecurringCryptoIdentity,
133
+ resolveRecurringCryptoTimeline: () => resolveRecurringCryptoTimeline,
127
134
  resolveRollingWindow: () => resolveRollingWindow,
128
135
  resolveTradingStateKind: () => resolveTradingStateKind,
129
136
  sortVenues: () => sortVenues,
137
+ sportsLiveQueryKey: () => sportsLiveQueryKey,
138
+ sportsLiveRefetchInterval: () => sportsLiveRefetchInterval,
130
139
  timeRangeToInterval: () => timeRangeToInterval,
131
140
  tradingReducer: () => tradingReducer,
132
141
  useAggAuth: () => useAggAuth,
@@ -186,9 +195,10 @@ __export(src_exports, {
186
195
  useOrders: () => useOrders,
187
196
  usePlaceLimitOrder: () => usePlaceLimitOrder,
188
197
  usePositions: () => usePositions,
189
- useQueryClient: () => import_react_query44.useQueryClient,
198
+ useQueryClient: () => import_react_query46.useQueryClient,
190
199
  useRampQuotes: () => useRampQuotes,
191
200
  useRampSession: () => useRampSession,
201
+ useRecurringCryptoMarkets: () => useRecurringCryptoMarkets,
192
202
  useRedeem: () => useRedeem,
193
203
  useRedeemEligibleCount: () => useRedeemEligibleCount,
194
204
  useRedeemLifecycle: () => useRedeemLifecycle,
@@ -198,6 +208,7 @@ __export(src_exports, {
198
208
  useSdkUiConfig: () => useSdkUiConfig,
199
209
  useSearch: () => useSearch,
200
210
  useSmartRoute: () => useSmartRoute,
211
+ useSportsLive: () => useSportsLive,
201
212
  useSyncBalances: () => useSyncBalances,
202
213
  useUserActivity: () => useUserActivity,
203
214
  useUserHoldings: () => useUserHoldings,
@@ -229,9 +240,11 @@ var Venue = /* @__PURE__ */ ((Venue2) => {
229
240
  Venue2["probable"] = "probable";
230
241
  Venue2["myriad"] = "myriad";
231
242
  Venue2["hyperliquid"] = "hyperliquid";
243
+ Venue2["novig"] = "novig";
232
244
  return Venue2;
233
245
  })(Venue || {});
234
246
  var ACTIVE_VENUES = [
247
+ "kalshi" /* kalshi */,
235
248
  "polymarket" /* polymarket */,
236
249
  "limitless" /* limitless */,
237
250
  "opinion" /* opinion */,
@@ -240,7 +253,7 @@ var ACTIVE_VENUES = [
240
253
  "myriad" /* myriad */,
241
254
  "hyperliquid" /* hyperliquid */
242
255
  ];
243
- var INACTIVE_VENUES = ["kalshi" /* kalshi */];
256
+ var INACTIVE_VENUES = ["novig" /* novig */];
244
257
  var ACTIVE_VENUE_SET = new Set(ACTIVE_VENUES);
245
258
  var INACTIVE_VENUE_SET = new Set(INACTIVE_VENUES);
246
259
 
@@ -353,7 +366,7 @@ var getVenueOrder = (venue) => {
353
366
  var sortVenues = (venues) => [...venues].sort((a, b) => getVenueOrder(a) - getVenueOrder(b));
354
367
 
355
368
  // src/index.ts
356
- var import_react_query44 = require("@tanstack/react-query");
369
+ var import_react_query46 = require("@tanstack/react-query");
357
370
 
358
371
  // src/candle-store.ts
359
372
  var import_sdk = require("@agg-build/sdk");
@@ -586,7 +599,8 @@ var enUsLabels = {
586
599
  limitless: "Limitless",
587
600
  probable: "Probable",
588
601
  myriad: "Myriad",
589
- hyperliquid: "Hyperliquid"
602
+ hyperliquid: "Hyperliquid",
603
+ novig: "Novig"
590
604
  },
591
605
  auth: {
592
606
  connect: (connectorName) => connectorName ? `Sign in with ${connectorName}` : "Sign in",
@@ -867,7 +881,7 @@ var enUsLabels = {
867
881
  userProfile: {
868
882
  tabs: {
869
883
  positions: "Positions",
870
- openOrders: "Open orders",
884
+ openOrders: "Open limit orders",
871
885
  activity: "Activity"
872
886
  },
873
887
  balance: {
@@ -970,6 +984,7 @@ var enUsLabels = {
970
984
  goodTilCanceled: "Good 'til canceled",
971
985
  expired: "Expired",
972
986
  cancel: "Cancel",
987
+ pending: "Pending",
973
988
  cancelOrderAria: (orderId) => `Cancel order ${orderId}`,
974
989
  loading: "Loading open orders",
975
990
  loadingMore: "Loading more open orders",
@@ -1048,7 +1063,19 @@ var enUsLabels = {
1048
1063
  volumeSuffix: "Vol.",
1049
1064
  marketSelectPlaceholder: "Select market",
1050
1065
  marketSelectSearchPlaceholder: "Search markets...",
1051
- marketSelectEmpty: "No markets found"
1066
+ marketSelectEmpty: "No markets found",
1067
+ moreMarkets: "More",
1068
+ liveMarket: "Live"
1069
+ },
1070
+ sportsLive: {
1071
+ scoreAria: (firstName, firstScore, secondName, secondScore, status) => `${firstName} ${firstScore}, ${secondName} ${secondScore}, ${status}`,
1072
+ scheduled: "Scheduled",
1073
+ live: "Live",
1074
+ paused: "Paused",
1075
+ finished: "Final",
1076
+ cancelled: "Cancelled",
1077
+ postponed: "Postponed",
1078
+ unknown: "Status unavailable"
1052
1079
  },
1053
1080
  settlementSummary: {
1054
1081
  title: "Settlement differences",
@@ -1095,6 +1122,7 @@ var enUsLabels = {
1095
1122
  showOutcomeAria: (label) => `Show ${label} outcome`,
1096
1123
  viewResolved: "View resolved",
1097
1124
  hideResolved: "Hide resolved",
1125
+ resolvingBadge: "Resolving",
1098
1126
  resolvingTitle: "Resolving market\u2026",
1099
1127
  resolvingDescription: "This market has closed. We're waiting for final settlement data and will update the result once it's confirmed on-chain.",
1100
1128
  volumeUnavailable: "Volume unavailable",
@@ -1175,7 +1203,9 @@ var enUsLabels = {
1175
1203
  orderDisabledNoOutcome: "Select an outcome to place an order.",
1176
1204
  orderDisabledNoEvent: "No event selected.",
1177
1205
  insufficientBalance: "Insufficient balance",
1178
- lowLiquidityWarning: "This market has low liquidity, which may impact execution price for larger orders.",
1206
+ insufficientBalanceDetails: "Insufficient balance details",
1207
+ lowLiquidityWarning: "Your avg. price is higher than the market price due to your order size and available market liquidity.",
1208
+ lowLiquiditySellWarning: "Your avg. price is lower than the market price due to your order size and available market liquidity.",
1179
1209
  refreshQuote: "Refresh Quote",
1180
1210
  confirmOrder: "Confirm Order",
1181
1211
  withdrawSubmitted: "Withdrawal submitted",
@@ -1251,6 +1281,12 @@ var enUsLabels = {
1251
1281
  limitPrice: "Limit price",
1252
1282
  limitPriceInputAria: "Limit price in cents",
1253
1283
  limitPriceInvalid: "Limit price must be greater than 0 and less than 100.",
1284
+ limitPriceAdjusted: (venueLabels, tickSizeCents, originalPriceLabel, side, adjustedPriceLabel) => {
1285
+ const venues = venueLabels.length <= 1 ? venueLabels.join("") : venueLabels.length === 2 ? `${venueLabels[0]} and ${venueLabels[1]}` : `${venueLabels.slice(0, -1).join(", ")} and ${venueLabels[venueLabels.length - 1]}`;
1286
+ const increment = tickSizeCents === 1 ? "whole cents" : `${tickSizeCents.toLocaleString("en-US", { maximumFractionDigits: 4 })}\xA2 increments`;
1287
+ const direction = side === "buy" ? "down" : "up";
1288
+ return `${venues} price in ${increment}, so your ${originalPriceLabel} ${side} rounds ${direction} to ${adjustedPriceLabel}`;
1289
+ },
1254
1290
  limitPriceDecreaseAria: "Decrease limit price",
1255
1291
  limitPriceIncreaseAria: "Increase limit price",
1256
1292
  expiration: "Expiration",
@@ -1294,9 +1330,23 @@ var enUsLabels = {
1294
1330
  updatingRoute: "Updating route\u2026",
1295
1331
  refreshingQuotes: "Refreshing quotes\u2026",
1296
1332
  checkingBalance: "Checking balance",
1333
+ executingOrder: "Executing order...",
1297
1334
  submittingOrderProgress: "Submitting order...",
1335
+ executionOrderSubmitted: "Order submitted",
1298
1336
  orderSubmittedProgress: (orderId) => `Order #${orderId.replace(/^#/, "")} submitted`,
1299
1337
  executingOnVenue: (venueLabel) => `Executing on ${venueLabel}...`,
1338
+ venueOrder: (venueLabel) => `${venueLabel} order`,
1339
+ settingUpBridge: "Setting up bridging...",
1340
+ bridgeSetupComplete: "Bridge setup complete",
1341
+ bridgingFundsProgress: "Bridging funds...",
1342
+ bridgeComplete: "Bridge complete",
1343
+ bridgingFunds: (amount, sourceChain, destinationChain, tokenSymbol = "USDC") => `Bridging ${amount} ${tokenSymbol} from ${sourceChain} to ${destinationChain}...`,
1344
+ fundsBridged: (amount, sourceChain, destinationChain, tokenSymbol = "USDC") => `Bridged ${amount} ${tokenSymbol} from ${sourceChain} to ${destinationChain}`,
1345
+ depositingFunds: (amount, tokenSymbol, destinationChain) => `Depositing ${amount} ${tokenSymbol} to ${destinationChain}...`,
1346
+ fundsDeposited: (amount, tokenSymbol, destinationChain) => `Deposited ${amount} ${tokenSymbol} to ${destinationChain}`,
1347
+ fillingOnVenue: (venueLabel) => `Filling on ${venueLabel}...`,
1348
+ venueFilledAtPriceWithAmount: (venueLabel, amountLabel, priceLabel) => `${venueLabel} filled ${amountLabel} at ${priceLabel}`,
1349
+ executionShares: (value) => `${value} shares`,
1300
1350
  confirmTransactionInWallet: "Confirm the transaction in your wallet",
1301
1351
  executionConfirmed: "Execution confirmed",
1302
1352
  orderSuccessTitle: "Order placed",
@@ -1304,7 +1354,7 @@ var enUsLabels = {
1304
1354
  orderFailureTitle: "Order failed",
1305
1355
  orderFailureDescription: (message) => message || "Something went wrong. Please try again.",
1306
1356
  orderDone: "Done",
1307
- orderNewTrade: "New Trade",
1357
+ orderNewTrade: "New trade",
1308
1358
  orderShare: "Share",
1309
1359
  orderRetry: "Try again",
1310
1360
  orderContinue: "Continue",
@@ -1317,12 +1367,20 @@ var enUsLabels = {
1317
1367
  orderSummaryAvgPrice: "Avg. price",
1318
1368
  orderSummaryEstCost: "Est. cost",
1319
1369
  orderSummaryVenue: "Venue",
1370
+ orderAmountFilled: "Amount filled",
1371
+ orderAmountFilledOf: (requestedAmount) => `of ${requestedAmount}`,
1372
+ orderPayout: "Payout",
1373
+ orderFullyFilled: "Order fully filled",
1374
+ orderPartiallyFilled: "Order partially filled",
1375
+ orderAveragePrice: (priceLabel) => `avg. ${priceLabel}`,
1320
1376
  orderComplete: "Order complete",
1321
1377
  orderFilledAtPrice: (venueLabel, priceLabel) => `${venueLabel} order filled at ${priceLabel}`,
1322
1378
  orderFilledAtPriceWithAmount: (venueLabel, priceLabel, amountLabel) => `${venueLabel} order filled at ${priceLabel} (${amountLabel})`,
1323
1379
  orderPartiallyFilledAtPriceWithAmount: (venueLabel, priceLabel, amountLabel) => `${venueLabel} order partially filled at ${priceLabel} (${amountLabel})`,
1324
1380
  orderSkip: "Skip",
1325
1381
  orderRetryRemaining: "Retry remaining",
1382
+ orderTryRemaining: (remainingLabel) => `Try remaining ${remainingLabel}`,
1383
+ orderClose: "Close",
1326
1384
  resolvedEarningsTitle: "Your Earnings",
1327
1385
  resolvedResolutionDateLabel: "Resolution date",
1328
1386
  resolvedSharesLabel: "Shares",
@@ -1331,6 +1389,7 @@ var enUsLabels = {
1331
1389
  boughtOutcomeAtPrice: (label, priceLabel) => `Bought ${label} at ${priceLabel}`,
1332
1390
  soldOutcomeAtPrice: (label, priceLabel) => `Sold ${label} at ${priceLabel}`,
1333
1391
  potentialReturnBonus: (value) => `+${value} Better Payout`,
1392
+ potentialReturnBetter: (value) => `+${value} Better`,
1334
1393
  withSmartRouting: "with Smart Routing",
1335
1394
  buy: "Buy",
1336
1395
  sell: "Sell",
@@ -1475,6 +1534,9 @@ var enUsLabels = {
1475
1534
  resolvedOutcome: (label) => `Resolved: ${label}`,
1476
1535
  marketDetailsAriaByDate: (dateLabel) => `Market details for ${dateLabel}`,
1477
1536
  marketDetailsAriaByQuestion: (question) => `Market details for ${question}`,
1537
+ recurringCountdownAria: (countdown) => `Current market ends in ${countdown}`,
1538
+ goToLive: "Go to live",
1539
+ liveMarketUnavailable: "A live market is not available yet",
1478
1540
  sports: {
1479
1541
  summaryAria: "Sports event summary",
1480
1542
  sectionTabsAria: "Sports market sections",
@@ -2301,6 +2363,7 @@ function AggBalanceProvider({ children }) {
2301
2363
  if (!isAuthenticated) {
2302
2364
  return {
2303
2365
  managedBalances,
2366
+ dataUpdatedAt: 0,
2304
2367
  totalBalance: 0,
2305
2368
  availableBalance: 0,
2306
2369
  reservedBalance: 0,
@@ -2319,6 +2382,7 @@ function AggBalanceProvider({ children }) {
2319
2382
  const error = balancesError instanceof Error ? balancesError : balancesError ? new Error(String(balancesError)) : null;
2320
2383
  return {
2321
2384
  managedBalances,
2385
+ dataUpdatedAt: balancesQuery.dataUpdatedAt,
2322
2386
  totalBalance,
2323
2387
  availableBalance,
2324
2388
  reservedBalance,
@@ -2344,6 +2408,7 @@ function AggBalanceProvider({ children }) {
2344
2408
  kalshiBalance,
2345
2409
  kalshiPortfolioValue,
2346
2410
  managedBalances,
2411
+ balancesQuery.dataUpdatedAt,
2347
2412
  positionsBalanceTotal,
2348
2413
  refetch,
2349
2414
  refetchConnectionStatus,
@@ -4142,7 +4207,7 @@ function useQueryClientCheck() {
4142
4207
  if (!hasWarnedMissingQueryClient) {
4143
4208
  hasWarnedMissingQueryClient = true;
4144
4209
  console.error(
4145
- "[agg] AggProvider requires a QueryClientProvider ancestor. Either wrap AggProvider in a <QueryClientProvider>, or use <AggProviders> which includes one automatically."
4210
+ "[agg] AggProvider requires a QueryClientProvider ancestor. Wrap AggProvider in a <QueryClientProvider client={queryClient}>."
4146
4211
  );
4147
4212
  }
4148
4213
  }
@@ -4151,7 +4216,7 @@ var AggProvider = ({ client, config, children }) => {
4151
4216
  useQueryClientCheck();
4152
4217
  if (!client) {
4153
4218
  throw new Error(
4154
- "AggProvider requires a valid AggClient instance. Pass one via the 'client' prop, or use <AggProviders> which handles setup automatically."
4219
+ "AggProvider requires a valid AggClient instance. Create one with createAggClient() and pass it via the 'client' prop."
4155
4220
  );
4156
4221
  }
4157
4222
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AggClientProvider, { client, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AggUiProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EventListStateProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AggWebSocketProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AggAuthProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AggBalanceProvider, { children }) }) }) }) }) });
@@ -4386,6 +4451,118 @@ function mapOrderStatusToTerminalEvent(status) {
4386
4451
  if (status === "failed" || status === "cancelled" || status === "expired") return "failed";
4387
4452
  return null;
4388
4453
  }
4454
+ var mergeTerminalOrderEvents = (currentEvents, incomingEvents) => {
4455
+ const mergedByOrderId = new Map(currentEvents.map((event) => [event.orderId, event]));
4456
+ for (const incomingEvent of incomingEvents) {
4457
+ const currentEvent = mergedByOrderId.get(incomingEvent.orderId);
4458
+ if (!currentEvent) {
4459
+ mergedByOrderId.set(incomingEvent.orderId, incomingEvent);
4460
+ continue;
4461
+ }
4462
+ if (currentEvent.event === "filled") continue;
4463
+ if (incomingEvent.event === "filled" || incomingEvent.timestamp >= currentEvent.timestamp) {
4464
+ mergedByOrderId.set(incomingEvent.orderId, incomingEvent);
4465
+ }
4466
+ }
4467
+ return [...mergedByOrderId.values()].sort((left, right) => left.timestamp - right.timestamp);
4468
+ };
4469
+ var EXECUTION_STEP_STATUS_RANK = {
4470
+ pending: 0,
4471
+ in_progress: 1,
4472
+ failed: 2,
4473
+ completed: 3,
4474
+ skipped: 3
4475
+ };
4476
+ var getExecutionStepIdentity = (step) => [step.groupId, step.sequence, step.stepType, ...step.orderIds].join(":");
4477
+ var getExecutionGroupAttempt = (steps) => steps.reduce((highestAttempt, step) => Math.max(highestAttempt, step.attempt), 0);
4478
+ var groupExecutionStepsByGroupId = (steps) => {
4479
+ var _a;
4480
+ const groupedSteps = /* @__PURE__ */ new Map();
4481
+ for (const step of steps) {
4482
+ const existingSteps = (_a = groupedSteps.get(step.groupId)) != null ? _a : [];
4483
+ existingSteps.push(step);
4484
+ groupedSteps.set(step.groupId, existingSteps);
4485
+ }
4486
+ return groupedSteps;
4487
+ };
4488
+ var pickExecutionGroupPresentation = (step) => __spreadValues({
4489
+ groupStatus: step.groupStatus,
4490
+ label: step.label
4491
+ }, step.bridge ? { bridge: step.bridge } : {});
4492
+ var mergeExecutionStepProgress = (previous, incoming) => {
4493
+ var _a, _b;
4494
+ const previousByGroupId = groupExecutionStepsByGroupId(previous);
4495
+ const incomingByGroupId = groupExecutionStepsByGroupId(incoming);
4496
+ const mergedByIdentity = new Map(previous.map((step) => [getExecutionStepIdentity(step), step]));
4497
+ for (const step of incoming) {
4498
+ const identity = getExecutionStepIdentity(step);
4499
+ const current = mergedByIdentity.get(identity);
4500
+ if (!current || step.attempt > current.attempt) {
4501
+ mergedByIdentity.set(identity, step);
4502
+ continue;
4503
+ }
4504
+ if (step.attempt < current.attempt) continue;
4505
+ if (EXECUTION_STEP_STATUS_RANK[step.status] < EXECUTION_STEP_STATUS_RANK[current.status]) {
4506
+ continue;
4507
+ }
4508
+ mergedByIdentity.set(identity, step);
4509
+ }
4510
+ const groupPresentation = /* @__PURE__ */ new Map();
4511
+ const groupIds = /* @__PURE__ */ new Set([...previousByGroupId.keys(), ...incomingByGroupId.keys()]);
4512
+ for (const groupId of groupIds) {
4513
+ const previousGroup = (_a = previousByGroupId.get(groupId)) != null ? _a : [];
4514
+ const incomingGroup = (_b = incomingByGroupId.get(groupId)) != null ? _b : [];
4515
+ const previousStep = previousGroup[0];
4516
+ const incomingStep = incomingGroup[0];
4517
+ if (!previousStep && incomingStep) {
4518
+ groupPresentation.set(groupId, pickExecutionGroupPresentation(incomingStep));
4519
+ continue;
4520
+ }
4521
+ if (!incomingStep && previousStep) {
4522
+ groupPresentation.set(groupId, pickExecutionGroupPresentation(previousStep));
4523
+ continue;
4524
+ }
4525
+ if (!previousStep || !incomingStep) continue;
4526
+ const isNewerAttempt = getExecutionGroupAttempt(incomingGroup) > getExecutionGroupAttempt(previousGroup);
4527
+ const shouldUseIncoming = isNewerAttempt || EXECUTION_STEP_STATUS_RANK[incomingStep.groupStatus] >= EXECUTION_STEP_STATUS_RANK[previousStep.groupStatus];
4528
+ groupPresentation.set(
4529
+ groupId,
4530
+ pickExecutionGroupPresentation(shouldUseIncoming ? incomingStep : previousStep)
4531
+ );
4532
+ }
4533
+ return Array.from(mergedByIdentity.values()).map((step) => __spreadValues(__spreadValues({}, step), groupPresentation.get(step.groupId))).sort((left, right) => {
4534
+ if (left.sequence !== right.sequence) return left.sequence - right.sequence;
4535
+ return getExecutionStepIdentity(left).localeCompare(getExecutionStepIdentity(right));
4536
+ });
4537
+ };
4538
+ var mergeRawExecutionStepProgress = (previous, incoming) => {
4539
+ const mergedBySequence = new Map(previous.map((step) => [step.sequence, step]));
4540
+ for (const step of incoming) {
4541
+ const current = mergedBySequence.get(step.sequence);
4542
+ if (!current || step.attempt > current.attempt) {
4543
+ mergedBySequence.set(step.sequence, step);
4544
+ continue;
4545
+ }
4546
+ if (step.attempt < current.attempt) continue;
4547
+ if (EXECUTION_STEP_STATUS_RANK[step.status] < EXECUTION_STEP_STATUS_RANK[current.status]) {
4548
+ continue;
4549
+ }
4550
+ mergedBySequence.set(step.sequence, step);
4551
+ }
4552
+ return Array.from(mergedBySequence.values()).sort(
4553
+ (left, right) => left.sequence - right.sequence
4554
+ );
4555
+ };
4556
+ var mergeExecutionStatusOrders = (previous, incoming) => {
4557
+ const mergedByOrderId = new Map(previous.map((order) => [order.orderId, order]));
4558
+ for (const order of incoming) {
4559
+ const current = mergedByOrderId.get(order.orderId);
4560
+ if (!current || new Date(order.updatedAt).getTime() >= new Date(current.updatedAt).getTime()) {
4561
+ mergedByOrderId.set(order.orderId, order);
4562
+ }
4563
+ }
4564
+ return Array.from(mergedByOrderId.values());
4565
+ };
4389
4566
  function useExecutionProgress({
4390
4567
  orderIds,
4391
4568
  quoteId,
@@ -4397,6 +4574,9 @@ function useExecutionProgress({
4397
4574
  const [latestBalance, setLatestBalance] = (0, import_react10.useState)(null);
4398
4575
  const [dagProgress, setDagProgress] = (0, import_react10.useState)(null);
4399
4576
  const [terminalOrderEvents, setTerminalOrderEvents] = (0, import_react10.useState)([]);
4577
+ const [executionSteps, setExecutionSteps] = (0, import_react10.useState)([]);
4578
+ const [rawExecutionSteps, setRawExecutionSteps] = (0, import_react10.useState)([]);
4579
+ const [executionOrders, setExecutionOrders] = (0, import_react10.useState)([]);
4400
4580
  const orderIdSetRef = (0, import_react10.useRef)(/* @__PURE__ */ new Set());
4401
4581
  const submittedOrderVenueByIdRef = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
4402
4582
  (0, import_react10.useEffect)(() => {
@@ -4409,9 +4589,21 @@ function useExecutionProgress({
4409
4589
  setLatestBalance(null);
4410
4590
  setDagProgress(null);
4411
4591
  setTerminalOrderEvents([]);
4592
+ setExecutionSteps([]);
4593
+ setRawExecutionSteps([]);
4594
+ setExecutionOrders([]);
4412
4595
  submittedOrderVenueByIdRef.current = /* @__PURE__ */ new Map();
4413
4596
  } else if (!enabled) {
4414
4597
  setPhase("idle");
4598
+ if (!orderIds || orderIds.length === 0) {
4599
+ setSubmittedOrders([]);
4600
+ setLatestBalance(null);
4601
+ setDagProgress(null);
4602
+ setTerminalOrderEvents([]);
4603
+ setExecutionSteps([]);
4604
+ setRawExecutionSteps([]);
4605
+ setExecutionOrders([]);
4606
+ }
4415
4607
  submittedOrderVenueByIdRef.current = /* @__PURE__ */ new Map();
4416
4608
  }
4417
4609
  }, [enabled, orderIds]);
@@ -4463,13 +4655,7 @@ function useExecutionProgress({
4463
4655
  errorReason: (_a = msg.errorReason) != null ? _a : fallbackErrorReason,
4464
4656
  timestamp: msg.timestamp
4465
4657
  };
4466
- setTerminalOrderEvents((prev) => {
4467
- const nextEvents = [
4468
- ...prev.filter((terminalEvent) => terminalEvent.orderId !== msg.orderId),
4469
- nextTerminalEvent
4470
- ];
4471
- return nextEvents.sort((left, right) => left.timestamp - right.timestamp);
4472
- });
4658
+ setTerminalOrderEvents((previous) => mergeTerminalOrderEvents(previous, [nextTerminalEvent]));
4473
4659
  return;
4474
4660
  }
4475
4661
  if (event === "dag_started") {
@@ -4489,20 +4675,53 @@ function useExecutionProgress({
4489
4675
  status: "running",
4490
4676
  errorReason: null
4491
4677
  });
4678
+ setRawExecutionSteps(
4679
+ (previous) => mergeRawExecutionStepProgress(
4680
+ previous,
4681
+ Object.entries(initialStepTypes).map(([sequence, stepType]) => ({
4682
+ sequence: Number(sequence),
4683
+ stepType,
4684
+ status: "pending",
4685
+ attempt: 0,
4686
+ startedAt: null,
4687
+ completedAt: null,
4688
+ errorReason: null
4689
+ }))
4690
+ )
4691
+ );
4492
4692
  return;
4493
4693
  }
4494
- if (event === "step_started") {
4694
+ if (event === "step_started" || event === "step_retrying") {
4495
4695
  setDagProgress((prev) => {
4496
4696
  var _a2, _b2;
4497
4697
  if (!prev) return prev;
4498
4698
  const seq = (_a2 = msg.sequence) != null ? _a2 : prev.currentSequence;
4499
- const stepType = (_b2 = msg.stepType) != null ? _b2 : prev.currentStepType;
4699
+ const stepType2 = (_b2 = msg.stepType) != null ? _b2 : prev.currentStepType;
4500
4700
  return __spreadProps(__spreadValues({}, prev), {
4501
4701
  currentSequence: seq,
4502
- currentStepType: stepType,
4503
- stepTypes: stepType != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType }) : prev.stepTypes
4702
+ currentStepType: stepType2,
4703
+ stepTypes: stepType2 != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType2 }) : prev.stepTypes
4504
4704
  });
4505
4705
  });
4706
+ const { sequence, stepType } = msg;
4707
+ if (sequence != null && stepType) {
4708
+ setRawExecutionSteps(
4709
+ (previous) => {
4710
+ var _a2, _b2, _c2;
4711
+ return mergeRawExecutionStepProgress(previous, [
4712
+ {
4713
+ sequence,
4714
+ stepType,
4715
+ status: "in_progress",
4716
+ attempt: (_c2 = (_b2 = msg.attempt) != null ? _b2 : (_a2 = previous.find((step) => step.sequence === sequence)) == null ? void 0 : _a2.attempt) != null ? _c2 : 0,
4717
+ startedAt: new Date(msg.timestamp).toISOString(),
4718
+ completedAt: null,
4719
+ errorReason: null
4720
+ }
4721
+ ]);
4722
+ }
4723
+ );
4724
+ }
4506
4725
  return;
4507
4726
  }
4508
4727
  if (event === "step_waiting") {
@@ -4510,25 +4729,64 @@ function useExecutionProgress({
4510
4729
  var _a2, _b2;
4511
4730
  if (!prev) return prev;
4512
4731
  const seq = (_a2 = msg.sequence) != null ? _a2 : prev.currentSequence;
4513
- const stepType = (_b2 = msg.stepType) != null ? _b2 : prev.currentStepType;
4732
+ const stepType2 = (_b2 = msg.stepType) != null ? _b2 : prev.currentStepType;
4514
4733
  return __spreadProps(__spreadValues({}, prev), {
4515
4734
  currentSequence: seq,
4516
- currentStepType: stepType,
4517
- stepTypes: stepType != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType }) : prev.stepTypes
4735
+ currentStepType: stepType2,
4736
+ stepTypes: stepType2 != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType2 }) : prev.stepTypes
4518
4737
  });
4519
4738
  });
4739
+ const { sequence, stepType } = msg;
4740
+ if (sequence != null && stepType) {
4741
+ setRawExecutionSteps(
4742
+ (previous) => {
4743
+ var _a2, _b2, _c2;
4744
+ return mergeRawExecutionStepProgress(previous, [
4745
+ {
4746
+ sequence,
4747
+ stepType,
4748
+ status: "in_progress",
4749
+ attempt: (_c2 = (_b2 = msg.attempt) != null ? _b2 : (_a2 = previous.find((step) => step.sequence === sequence)) == null ? void 0 : _a2.attempt) != null ? _c2 : 0,
4750
+ startedAt: new Date(msg.timestamp).toISOString(),
4751
+ completedAt: null,
4752
+ errorReason: null
4753
+ }
4754
+ ]);
4755
+ }
4756
+ );
4757
+ }
4520
4758
  return;
4521
4759
  }
4522
4760
  if (event === "step_completed") {
4523
4761
  setDagProgress((prev) => {
4524
4762
  if (!prev) return prev;
4525
4763
  const seq = msg.sequence;
4526
- const stepType = msg.stepType;
4764
+ const stepType2 = msg.stepType;
4527
4765
  return __spreadProps(__spreadValues({}, prev), {
4528
4766
  completedSequences: seq != null ? [...prev.completedSequences, seq] : prev.completedSequences,
4529
- stepTypes: seq != null && stepType != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType }) : prev.stepTypes
4767
+ stepTypes: seq != null && stepType2 != null ? __spreadProps(__spreadValues({}, prev.stepTypes), { [seq]: stepType2 }) : prev.stepTypes
4530
4768
  });
4531
4769
  });
4770
+ const { sequence, stepType } = msg;
4771
+ if (sequence != null && stepType) {
4772
+ const completedAt = new Date(msg.timestamp).toISOString();
4773
+ setRawExecutionSteps(
4774
+ (previous) => {
4775
+ var _a2, _b2, _c2;
4776
+ return mergeRawExecutionStepProgress(previous, [
4777
+ {
4778
+ sequence,
4779
+ stepType,
4780
+ status: "completed",
4781
+ attempt: (_c2 = (_b2 = msg.attempt) != null ? _b2 : (_a2 = previous.find((step) => step.sequence === sequence)) == null ? void 0 : _a2.attempt) != null ? _c2 : 0,
4782
+ startedAt: null,
4783
+ completedAt,
4784
+ errorReason: null
4785
+ }
4786
+ ]);
4787
+ }
4788
+ );
4789
+ }
4532
4790
  return;
4533
4791
  }
4534
4792
  if (event === "dag_completed") {
@@ -4557,6 +4815,25 @@ function useExecutionProgress({
4557
4815
  };
4558
4816
  if (event === "step_failed") {
4559
4817
  setDagProgress((prev) => buildFailedProgress(prev));
4818
+ const { sequence, stepType } = msg;
4819
+ if (sequence != null && stepType) {
4820
+ setRawExecutionSteps(
4821
+ (previous) => {
4822
+ var _a2, _b2, _c2, _d;
4823
+ return mergeRawExecutionStepProgress(previous, [
4824
+ {
4825
+ sequence,
4826
+ stepType,
4827
+ status: "failed",
4828
+ attempt: (_c2 = (_b2 = msg.attempt) != null ? _b2 : (_a2 = previous.find((step) => step.sequence === sequence)) == null ? void 0 : _a2.attempt) != null ? _c2 : 0,
4829
+ startedAt: null,
4830
+ completedAt: new Date(msg.timestamp).toISOString(),
4831
+ errorReason: (_d = msg.errorReason) != null ? _d : null
4832
+ }
4833
+ ]);
4834
+ }
4835
+ );
4836
+ }
4560
4837
  return;
4561
4838
  }
4562
4839
  if (event === "dag_failed") {
@@ -4565,7 +4842,7 @@ function useExecutionProgress({
4565
4842
  }
4566
4843
  }, []);
4567
4844
  const isStatusPolling = enabled && !!quoteId;
4568
- const isListening = !isStatusPolling && enabled && !!orderIds && orderIds.length > 0;
4845
+ const isListening = enabled && !!orderIds && orderIds.length > 0;
4569
4846
  useOnOrderSubmitted(isListening ? handleOrderSubmitted : null);
4570
4847
  useOnBalanceUpdate(isListening ? handleBalanceUpdate : null);
4571
4848
  useOnOrderEvent(isListening ? handleOrderEvent : null);
@@ -4611,19 +4888,25 @@ function useExecutionProgress({
4611
4888
  timestamp: new Date(order.updatedAt).getTime() || fallbackTimestamp
4612
4889
  };
4613
4890
  }).filter((order) => order != null);
4614
- if (!status.terminal || terminalEvents.length > 0 || !["failed", "cancelled", "expired"].includes(status.overallState)) {
4891
+ if (!status.terminal) {
4615
4892
  return terminalEvents;
4616
4893
  }
4617
- return fallbackOrderIds.map((orderId) => {
4618
- var _a;
4619
- return {
4620
- orderId,
4621
- venue: "",
4622
- event: "failed",
4623
- errorReason: (_a = status.errorReason) != null ? _a : "Execution failed.",
4624
- timestamp: fallbackTimestamp
4625
- };
4626
- });
4894
+ const coveredOrderIds = new Set(terminalEvents.map((event) => event.orderId));
4895
+ const fallbackEvent = status.overallState === "filled" ? "filled" : status.overallState === "partially_filled" ? "partial_fill" : ["failed", "cancelled", "expired"].includes(status.overallState) ? "failed" : null;
4896
+ if (!fallbackEvent) return terminalEvents;
4897
+ return [
4898
+ ...terminalEvents,
4899
+ ...fallbackOrderIds.filter((orderId) => !coveredOrderIds.has(orderId)).map((orderId) => {
4900
+ var _a;
4901
+ return {
4902
+ orderId,
4903
+ venue: "",
4904
+ event: fallbackEvent,
4905
+ errorReason: fallbackEvent === "failed" ? (_a = status.errorReason) != null ? _a : "Execution failed." : void 0,
4906
+ timestamp: fallbackTimestamp
4907
+ };
4908
+ })
4909
+ ];
4627
4910
  },
4628
4911
  []
4629
4912
  );
@@ -4691,6 +4974,7 @@ function useExecutionProgress({
4691
4974
  setPhase("order_submitted");
4692
4975
  }
4693
4976
  setSubmittedOrders(buildSubmittedOrdersFromStatus(status.orders));
4977
+ setExecutionOrders((previous) => mergeExecutionStatusOrders(previous, status.orders));
4694
4978
  if (status.dagProgress) {
4695
4979
  setDagProgress(__spreadProps(__spreadValues({}, status.dagProgress), {
4696
4980
  stepTypes: Object.fromEntries(
@@ -4715,13 +4999,30 @@ function useExecutionProgress({
4715
4999
  };
4716
5000
  });
4717
5001
  }
5002
+ setExecutionSteps((previous) => mergeExecutionStepProgress(previous, status.steps));
5003
+ setRawExecutionSteps(
5004
+ (previous) => mergeRawExecutionStepProgress(
5005
+ previous,
5006
+ status.steps.map((step) => ({
5007
+ sequence: step.sequence,
5008
+ stepType: step.stepType,
5009
+ status: step.status,
5010
+ attempt: step.attempt,
5011
+ startedAt: step.startedAt,
5012
+ completedAt: step.completedAt,
5013
+ errorReason: step.errorReason
5014
+ }))
5015
+ )
5016
+ );
4718
5017
  const fallbackTimestamp = Date.now();
4719
5018
  const nextTerminalEvents = buildTerminalEventsFromStatus(
4720
5019
  status,
4721
5020
  resolvedOrderIds,
4722
5021
  fallbackTimestamp
4723
5022
  );
4724
- setTerminalOrderEvents(nextTerminalEvents);
5023
+ setTerminalOrderEvents(
5024
+ (previous) => mergeTerminalOrderEvents(previous, nextTerminalEvents)
5025
+ );
4725
5026
  if (status.terminal) {
4726
5027
  stopPolling();
4727
5028
  return;
@@ -4816,17 +5117,7 @@ function useExecutionProgress({
4816
5117
  });
4817
5118
  }
4818
5119
  if (terminalEvents.length > 0) {
4819
- setTerminalOrderEvents((prev) => {
4820
- const nextById = /* @__PURE__ */ new Map();
4821
- for (const ev of prev) nextById.set(ev.orderId, ev);
4822
- for (const ev of terminalEvents) {
4823
- const existing = nextById.get(ev.orderId);
4824
- if (!existing || existing.timestamp < ev.timestamp) {
4825
- nextById.set(ev.orderId, ev);
4826
- }
4827
- }
4828
- return Array.from(nextById.values()).sort((l, r) => l.timestamp - r.timestamp);
4829
- });
5120
+ setTerminalOrderEvents((previous) => mergeTerminalOrderEvents(previous, terminalEvents));
4830
5121
  }
4831
5122
  const covered = /* @__PURE__ */ new Set([
4832
5123
  ...terminalOrderIdsRef.current,
@@ -4848,7 +5139,16 @@ function useExecutionProgress({
4848
5139
  stopPolling();
4849
5140
  };
4850
5141
  }, [client, isListening, isStatusPolling, mode, orderIds]);
4851
- return { phase, submittedOrders, latestBalance, dagProgress, terminalOrderEvents };
5142
+ return {
5143
+ phase,
5144
+ submittedOrders,
5145
+ latestBalance,
5146
+ dagProgress,
5147
+ terminalOrderEvents,
5148
+ executionSteps,
5149
+ rawExecutionSteps,
5150
+ executionOrders
5151
+ };
4852
5152
  }
4853
5153
 
4854
5154
  // src/execution/use-balance-refill-policies.ts
@@ -5748,7 +6048,7 @@ function useLinkAccount() {
5748
6048
  [client]
5749
6049
  );
5750
6050
  const confirmLink = (0, import_react18.useCallback)(
5751
- (token) => run(() => client.linkAccountConfirm(token)),
6051
+ (input) => run(() => client.linkAccountConfirm(input)),
5752
6052
  [client]
5753
6053
  );
5754
6054
  return { startLink, confirmLink, isLoading, error };
@@ -5851,6 +6151,7 @@ function useExecutionOrders(options = {}) {
5851
6151
  const client = useAggClient();
5852
6152
  const {
5853
6153
  status,
6154
+ orderType,
5854
6155
  orderId,
5855
6156
  quoteId,
5856
6157
  mode,
@@ -5866,11 +6167,13 @@ function useExecutionOrders(options = {}) {
5866
6167
  status != null ? status : "all",
5867
6168
  quoteId != null ? quoteId : null,
5868
6169
  orderId != null ? orderId : null,
6170
+ ...orderType ? [orderType] : [],
5869
6171
  ...mode ? [mode] : [],
5870
6172
  limit
5871
6173
  ],
5872
6174
  queryFn: ({ pageParam }) => client.getExecutionOrders(__spreadProps(__spreadValues({
5873
6175
  status,
6176
+ orderType,
5874
6177
  orderId,
5875
6178
  quoteId
5876
6179
  }, mode ? { mode } : {}), {
@@ -7817,7 +8120,7 @@ var getIsDocumentVisible = () => {
7817
8120
  return document.visibilityState !== "hidden";
7818
8121
  };
7819
8122
  function useSmartRoute(options) {
7820
- var _a, _b, _c, _d;
8123
+ var _a, _b, _c, _d, _e, _f;
7821
8124
  const client = useAggClient();
7822
8125
  const {
7823
8126
  venueMarketOutcomeId,
@@ -7832,6 +8135,7 @@ function useSmartRoute(options) {
7832
8135
  slipCapBps,
7833
8136
  compareVenues,
7834
8137
  sellCacheScope,
8138
+ balanceCacheScope,
7835
8139
  deepEstimate,
7836
8140
  enabled = true,
7837
8141
  staleTimeMs = SMART_ROUTE_STALE_TIME_MS,
@@ -7853,7 +8157,8 @@ function useSmartRoute(options) {
7853
8157
  slipCapBps != null ? slipCapBps : null,
7854
8158
  compareVenues != null ? compareVenues : false,
7855
8159
  deepEstimate != null ? deepEstimate : false,
7856
- ...tradeSide === "sell" ? [(_b = sellCacheScope == null ? void 0 : sellCacheScope.accountId) != null ? _b : null, (_c = sellCacheScope == null ? void 0 : sellCacheScope.positionVersion) != null ? _c : null] : []
8160
+ ...balanceCacheScope ? [(_b = balanceCacheScope.accountId) != null ? _b : null, (_c = balanceCacheScope.balanceVersion) != null ? _c : null] : [],
8161
+ ...tradeSide === "sell" ? [(_d = sellCacheScope == null ? void 0 : sellCacheScope.accountId) != null ? _d : null, (_e = sellCacheScope == null ? void 0 : sellCacheScope.positionVersion) != null ? _e : null] : []
7857
8162
  ],
7858
8163
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
7859
8164
  return client.getSmartRoute(
@@ -7910,7 +8215,7 @@ function useSmartRoute(options) {
7910
8215
  }
7911
8216
  }
7912
8217
  return {
7913
- data: (_d = query.data) != null ? _d : null,
8218
+ data: (_f = query.data) != null ? _f : null,
7914
8219
  isLoading: query.isLoading,
7915
8220
  isFetching: query.isFetching,
7916
8221
  error: error instanceof Error ? error : error ? new Error(String(error)) : null,
@@ -7933,6 +8238,27 @@ var getUniqueVenueRequests = (requests) => {
7933
8238
  }
7934
8239
  return [...requestsByVenue.values()].sort((left, right) => left.venue.localeCompare(right.venue));
7935
8240
  };
8241
+ var resolveLimitOrderTickSizesByVenue = (response) => {
8242
+ var _a, _b;
8243
+ const tickSizes = /* @__PURE__ */ new Map();
8244
+ const setTickSize = (venue, tickSize) => {
8245
+ if (tickSizes.has(venue)) return;
8246
+ if (typeof tickSize !== "number" || !Number.isFinite(tickSize) || tickSize <= 0) return;
8247
+ tickSizes.set(venue, tickSize);
8248
+ };
8249
+ for (const item of (_a = response == null ? void 0 : response.data) != null ? _a : []) {
8250
+ for (const [venue, entry] of Object.entries(item.venueOrderbooks)) {
8251
+ setTickSize(venue, (_b = entry.tick) != null ? _b : entry.tickSize);
8252
+ }
8253
+ for (const market of item.matchedMarkets) {
8254
+ setTickSize(market.venue, market.tickSize);
8255
+ }
8256
+ if (item.requestedMarket) {
8257
+ setTickSize(item.requestedMarket.venue, item.requestedMarket.tickSize);
8258
+ }
8259
+ }
8260
+ return tickSizes;
8261
+ };
7936
8262
  var useLimitOrderVenueQuotes = (options) => {
7937
8263
  var _a, _b;
7938
8264
  const client = useAggClient();
@@ -7954,6 +8280,15 @@ var useLimitOrderVenueQuotes = (options) => {
7954
8280
  const hasRequiredAccount = tradeSide === "buy" || Boolean(accountId);
7955
8281
  const queryEnabled = enabled && hasValidInput && hasRequiredAccount;
7956
8282
  const serializedChainBalances = chainBalances ? JSON.stringify(chainBalances) : null;
8283
+ const tickMetadataQuery = (0, import_react_query36.useQuery)({
8284
+ queryKey: ["orderbooks", marketId != null ? marketId : "__disabled__", "limit-order-ticks"],
8285
+ queryFn: ({ signal }) => client.getOrderbooks({ venueMarketIds: [marketId] }, { signal }),
8286
+ enabled: enabled && Boolean(marketId),
8287
+ staleTime: 6e4,
8288
+ gcTime: 5 * 6e4,
8289
+ refetchOnWindowFocus: false,
8290
+ retry: 1
8291
+ });
7957
8292
  const queries = (0, import_react_query36.useQueries)({
7958
8293
  queries: uniqueRequests.map((request) => ({
7959
8294
  queryKey: [
@@ -8008,12 +8343,15 @@ var useLimitOrderVenueQuotes = (options) => {
8008
8343
  retry: 1
8009
8344
  }))
8010
8345
  });
8346
+ const tickSizesByVenue = resolveLimitOrderTickSizesByVenue(tickMetadataQuery.data);
8011
8347
  const quotes = queries.map((query, index) => {
8012
- var _a2;
8348
+ var _a2, _b2;
8013
8349
  const queryError = query.error;
8350
+ const request = uniqueRequests[index];
8014
8351
  return {
8015
- request: uniqueRequests[index],
8352
+ request,
8016
8353
  data: (_a2 = query.data) != null ? _a2 : null,
8354
+ tickSize: (_b2 = tickSizesByVenue.get(request.venue)) != null ? _b2 : null,
8017
8355
  isLoading: query.isLoading,
8018
8356
  isFetching: query.isFetching,
8019
8357
  error: queryError instanceof Error ? queryError : queryError ? new Error(String(queryError)) : null,
@@ -8025,7 +8363,10 @@ var useLimitOrderVenueQuotes = (options) => {
8025
8363
  isLoading: quotes.some((quote) => quote.isLoading),
8026
8364
  isFetching: quotes.some((quote) => quote.isFetching),
8027
8365
  error: (_b = (_a = quotes.find((quote) => quote.error)) == null ? void 0 : _a.error) != null ? _b : null,
8028
- refetch: () => Promise.all(quotes.map((quote) => quote.refetch()))
8366
+ refetch: () => Promise.all([
8367
+ ...marketId ? [tickMetadataQuery.refetch()] : [],
8368
+ ...quotes.map((quote) => quote.refetch())
8369
+ ])
8029
8370
  };
8030
8371
  };
8031
8372
 
@@ -8178,25 +8519,566 @@ function useUserHoldings(options) {
8178
8519
  var import_react34 = require("react");
8179
8520
 
8180
8521
  // src/use-venue-event.ts
8522
+ var import_react_query40 = require("@tanstack/react-query");
8523
+
8524
+ // src/use-recurring-crypto-markets.ts
8181
8525
  var import_react_query39 = require("@tanstack/react-query");
8526
+ var RESOLVING_POLL_INTERVAL_MS = 15e3;
8527
+ var FALLBACK_VENUE_EVENT_PAGE_LIMIT = 100;
8528
+ var FALLBACK_VENUE_EVENT_MAX_PAGES = 5;
8529
+ var FALLBACK_TARGET_WINDOW_COUNT = 5;
8530
+ var RECURRING_MARKET_STATUSES = [
8531
+ "open" /* open */,
8532
+ "closed" /* closed */,
8533
+ "resolved" /* resolved */,
8534
+ "unopened" /* unopened */,
8535
+ "paused" /* paused */
8536
+ ];
8537
+ var RECURRING_DURATION_MS = {
8538
+ PT5M: 5 * 6e4,
8539
+ PT15M: 15 * 6e4,
8540
+ PT1H: 60 * 6e4,
8541
+ P1D: 24 * 60 * 6e4
8542
+ };
8543
+ var RECURRING_SLUG_DURATION = {
8544
+ "5m": "PT5M",
8545
+ "15m": "PT15M",
8546
+ "1h": "PT1H",
8547
+ "1d": "P1D"
8548
+ };
8549
+ var RECURRING_DURATION_AGG_KEY_SEGMENT = {
8550
+ PT5M: "5m",
8551
+ PT15M: "15m",
8552
+ PT1H: "1h"
8553
+ };
8554
+ var RECURRING_CRYPTO_SLUG_PATTERN = /^([a-z0-9]+)-updown-(5m|15m|1h|1d)-(\d+)$/i;
8555
+ var RECURRING_CRYPTO_AGG_KEY_PATTERN = /^agg_ud_([a-z0-9]+)_(5m|15m|1h)_(\d{2})(\d{2})(\d{2})T(\d{2})(\d{2})$/i;
8556
+ var RECURRING_CRYPTO_DAILY_AGG_KEY_PATTERN = /^agg_ud_([a-z0-9]+)_1d_(\d{2})(\d{2})(\d{2})$/i;
8557
+ var isRecurringCryptoDuration = (value) => value != null && Object.prototype.hasOwnProperty.call(RECURRING_DURATION_MS, value);
8558
+ var parseTimestamp = (value) => {
8559
+ if (!value) return null;
8560
+ const timestamp = Date.parse(value);
8561
+ return Number.isFinite(timestamp) ? timestamp : null;
8562
+ };
8563
+ var hasCryptoCategory = (event) => event.categories.some(
8564
+ ({ category }) => [category.name, category.displayName].some((value) => (value == null ? void 0 : value.trim().toLowerCase()) === "crypto")
8565
+ );
8566
+ var resolveWindowStartFromEnd = (event, duration) => {
8567
+ var _a, _b;
8568
+ const end = (_b = (_a = parseTimestamp(event.endDate)) != null ? _a : event.venueMarkets.map((market) => parseTimestamp(market.endDate)).find((value) => value != null)) != null ? _b : null;
8569
+ if (end != null) return end - RECURRING_DURATION_MS[duration];
8570
+ return parseTimestamp(event.startDate);
8571
+ };
8572
+ var parseAggKeyWindowStart = (match) => {
8573
+ const year = 2e3 + Number(match[3]);
8574
+ const monthIndex = Number(match[4]) - 1;
8575
+ const day = Number(match[5]);
8576
+ const hour = Number(match[6]);
8577
+ const minute = Number(match[7]);
8578
+ const timestamp = Date.UTC(year, monthIndex, day, hour, minute);
8579
+ const parsed = new Date(timestamp);
8580
+ if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== monthIndex || parsed.getUTCDate() !== day || parsed.getUTCHours() !== hour || parsed.getUTCMinutes() !== minute) {
8581
+ return null;
8582
+ }
8583
+ return timestamp;
8584
+ };
8585
+ var resolveRecurringCryptoIdentity = (event) => {
8586
+ var _a, _b, _c, _d, _e, _f, _g;
8587
+ const slugMatch = (_a = event.slug) == null ? void 0 : _a.match(RECURRING_CRYPTO_SLUG_PATTERN);
8588
+ if (slugMatch && hasCryptoCategory(event)) {
8589
+ const [, asset, durationSegment, epochSeconds] = slugMatch;
8590
+ const duration = RECURRING_SLUG_DURATION[durationSegment.toLowerCase()];
8591
+ const parsedEpochSeconds = Number(epochSeconds);
8592
+ const windowStart = parsedEpochSeconds * 1e3;
8593
+ if (duration && Number.isSafeInteger(parsedEpochSeconds) && Number.isFinite(windowStart)) {
8594
+ return { asset: asset.toUpperCase(), duration, windowStart };
8595
+ }
8596
+ }
8597
+ const aggKeyMatch = (_b = event.aggKey) == null ? void 0 : _b.match(RECURRING_CRYPTO_AGG_KEY_PATTERN);
8598
+ if (aggKeyMatch) {
8599
+ const [, asset, durationSegment] = aggKeyMatch;
8600
+ const duration = RECURRING_SLUG_DURATION[durationSegment.toLowerCase()];
8601
+ const windowStart = parseAggKeyWindowStart(aggKeyMatch);
8602
+ if (duration && windowStart != null) {
8603
+ return { asset: asset.toUpperCase(), duration, windowStart };
8604
+ }
8605
+ }
8606
+ const dailyAggKeyMatch = (_c = event.aggKey) == null ? void 0 : _c.match(RECURRING_CRYPTO_DAILY_AGG_KEY_PATTERN);
8607
+ if (dailyAggKeyMatch) {
8608
+ return {
8609
+ asset: dailyAggKeyMatch[1].toUpperCase(),
8610
+ duration: "P1D",
8611
+ windowStart: resolveWindowStartFromEnd(event, "P1D")
8612
+ };
8613
+ }
8614
+ if (!isRecurringCryptoDuration(event.recurrence)) return null;
8615
+ return {
8616
+ asset: (_g = (_f = (_d = slugMatch == null ? void 0 : slugMatch[1]) == null ? void 0 : _d.toUpperCase()) != null ? _f : (_e = aggKeyMatch == null ? void 0 : aggKeyMatch[1]) == null ? void 0 : _e.toUpperCase()) != null ? _g : null,
8617
+ duration: event.recurrence,
8618
+ windowStart: resolveWindowStartFromEnd(event, event.recurrence)
8619
+ };
8620
+ };
8621
+ var mergeWindowMarkets = (current, incoming) => {
8622
+ const marketsById = new Map(
8623
+ current.map((market) => [
8624
+ `${market.venue}:${market.venueEventId}:${market.venueMarketId}`,
8625
+ market
8626
+ ])
8627
+ );
8628
+ incoming.forEach((market) => {
8629
+ marketsById.set(`${market.venue}:${market.venueEventId}:${market.venueMarketId}`, market);
8630
+ });
8631
+ return [...marketsById.values()];
8632
+ };
8633
+ var mergeRecurringCryptoWindows = (windows) => {
8634
+ const windowsById = /* @__PURE__ */ new Map();
8635
+ windows.forEach((windowMarket) => {
8636
+ const existing = windowsById.get(windowMarket.id);
8637
+ if (!existing) {
8638
+ windowsById.set(windowMarket.id, windowMarket);
8639
+ return;
8640
+ }
8641
+ windowsById.set(windowMarket.id, __spreadProps(__spreadValues(__spreadValues({}, existing), windowMarket), {
8642
+ markets: mergeWindowMarkets(existing.markets, windowMarket.markets)
8643
+ }));
8644
+ });
8645
+ return [...windowsById.values()].sort(
8646
+ (left, right) => {
8647
+ var _a, _b;
8648
+ return ((_a = parseTimestamp(left.windowStart)) != null ? _a : 0) - ((_b = parseTimestamp(right.windowStart)) != null ? _b : 0);
8649
+ }
8650
+ );
8651
+ };
8652
+ var selectNavigableMarket = (markets, event) => {
8653
+ var _a, _b, _c;
8654
+ const navigableMarkets = markets.filter((market) => market.priceSource === "snag");
8655
+ return (_c = (_b = (_a = navigableMarkets.find((market) => market.venueEventId === event.id)) != null ? _a : navigableMarkets.find((market) => market.venue === event.venue)) != null ? _b : navigableMarkets[0]) != null ? _c : null;
8656
+ };
8657
+ var resolveRecurringCryptoTimeline = (windows, event, now) => {
8658
+ var _a, _b, _c;
8659
+ const all = mergeRecurringCryptoWindows(windows).flatMap((windowMarket) => {
8660
+ const windowStart = parseTimestamp(windowMarket.windowStart);
8661
+ const windowEnd = parseTimestamp(windowMarket.windowEnd);
8662
+ const market = selectNavigableMarket(windowMarket.markets, event);
8663
+ if (windowStart == null || windowEnd == null || windowEnd <= windowStart || !market) return [];
8664
+ return [
8665
+ {
8666
+ id: windowMarket.id,
8667
+ asset: windowMarket.asset,
8668
+ duration: windowMarket.duration,
8669
+ windowStart: windowMarket.windowStart,
8670
+ windowEnd: windowMarket.windowEnd,
8671
+ status: market.status,
8672
+ venueEventId: market.venueEventId,
8673
+ venueMarketId: market.venueMarketId,
8674
+ venue: market.venue
8675
+ }
8676
+ ];
8677
+ });
8678
+ const viewed = (_a = all.find((market) => market.venueEventId === event.id)) != null ? _a : null;
8679
+ const live = (_b = all.find((market) => {
8680
+ const startsAt = parseTimestamp(market.windowStart);
8681
+ const endsAt = parseTimestamp(market.windowEnd);
8682
+ return market.status === "open" /* open */ && startsAt != null && endsAt != null && startsAt <= now && now < endsAt;
8683
+ })) != null ? _b : null;
8684
+ const endedMarkets = all.filter(
8685
+ (market) => {
8686
+ var _a2;
8687
+ return ((_a2 = parseTimestamp(market.windowEnd)) != null ? _a2 : Infinity) <= now;
8688
+ }
8689
+ );
8690
+ const ended = (_c = endedMarkets[endedMarkets.length - 1]) != null ? _c : null;
8691
+ const upcoming = all.filter((market) => {
8692
+ var _a2;
8693
+ return ((_a2 = parseTimestamp(market.windowStart)) != null ? _a2 : -Infinity) > now;
8694
+ }).slice(0, 3);
8695
+ const defaultVisible = [ended, live, ...upcoming].filter(
8696
+ (market, index, values) => market != null && values.findIndex((candidate) => (candidate == null ? void 0 : candidate.id) === market.id) === index
8697
+ );
8698
+ let visible = defaultVisible;
8699
+ if (viewed && !defaultVisible.some((market) => market.id === viewed.id)) {
8700
+ const viewedStart = Date.parse(viewed.windowStart);
8701
+ const viewedEnd = Date.parse(viewed.windowEnd);
8702
+ const marketToReplace = viewedEnd <= now ? ended : viewedStart > now ? upcoming[upcoming.length - 1] : ended != null ? ended : upcoming[upcoming.length - 1];
8703
+ visible = [
8704
+ ...defaultVisible.filter((market) => market.id !== (marketToReplace == null ? void 0 : marketToReplace.id)),
8705
+ viewed
8706
+ ].sort(
8707
+ (left, right) => {
8708
+ var _a2, _b2;
8709
+ return ((_a2 = parseTimestamp(left.windowStart)) != null ? _a2 : 0) - ((_b2 = parseTimestamp(right.windowStart)) != null ? _b2 : 0);
8710
+ }
8711
+ );
8712
+ }
8713
+ return { all, visible, ended, live, upcoming, viewed };
8714
+ };
8715
+ var fetchRecurringCryptoPage = (client, options, signal) => __async(null, null, function* () {
8716
+ var _a;
8717
+ return client.listRecurringCryptoMarkets(__spreadProps(__spreadValues({}, options), {
8718
+ status: (_a = options.status) != null ? _a : RECURRING_MARKET_STATUSES,
8719
+ includeOrderbookPrices: false,
8720
+ includeReferencePrices: false,
8721
+ includeDirectVenueMarkets: options.includeDirectVenueMarkets,
8722
+ limit: 100,
8723
+ signal
8724
+ }));
8725
+ });
8726
+ var createEmptyPriceComparison = () => ({
8727
+ count: 0,
8728
+ min: null,
8729
+ max: null,
8730
+ spread: null,
8731
+ spreadBps: null,
8732
+ byVenue: []
8733
+ });
8734
+ var normalizeFallbackVenueEvent = (candidate, identity) => {
8735
+ var _a, _b, _c, _d;
8736
+ const candidateIdentity = resolveRecurringCryptoIdentity(candidate);
8737
+ const venueMarket = candidate.venueMarkets[0];
8738
+ if (!(candidateIdentity == null ? void 0 : candidateIdentity.asset) || !identity.asset || candidateIdentity.asset !== identity.asset || candidateIdentity.duration !== identity.duration || candidateIdentity.windowStart == null || !venueMarket) {
8739
+ return null;
8740
+ }
8741
+ const status = (_b = (_a = candidate.status) != null ? _a : venueMarket.status) != null ? _b : "unopened" /* unopened */;
8742
+ const windowStart = candidateIdentity.windowStart;
8743
+ return {
8744
+ id: `${candidateIdentity.asset}:${candidateIdentity.duration}:${new Date(windowStart).toISOString()}`,
8745
+ asset: candidateIdentity.asset,
8746
+ quoteAsset: "USD",
8747
+ duration: candidateIdentity.duration,
8748
+ windowStart: new Date(windowStart).toISOString(),
8749
+ windowEnd: new Date(windowStart + RECURRING_DURATION_MS[identity.duration]).toISOString(),
8750
+ status,
8751
+ markets: [
8752
+ {
8753
+ venue: candidate.venue,
8754
+ venueEventId: candidate.id,
8755
+ venueMarketId: venueMarket.id,
8756
+ externalEventId: candidate.externalIdentifier,
8757
+ externalMarketId: venueMarket.externalIdentifier,
8758
+ question: venueMarket.question,
8759
+ status,
8760
+ priceSource: "snag",
8761
+ routeable: false,
8762
+ splitOrdersAllowed: false,
8763
+ outcomes: ((_c = venueMarket.venueMarketOutcomes) != null ? _c : []).map((outcome) => {
8764
+ var _a2, _b2;
8765
+ return {
8766
+ venueMarketOutcomeId: outcome.id,
8767
+ externalOutcomeId: (_a2 = outcome.externalIdentifier) != null ? _a2 : null,
8768
+ routeable: false,
8769
+ splitOrdersAllowed: false,
8770
+ label: outcome.label,
8771
+ title: (_b2 = outcome.title) != null ? _b2 : null,
8772
+ midpoint: null,
8773
+ bestBid: null,
8774
+ bestAsk: null,
8775
+ markSource: null,
8776
+ lastKnownPrice: null,
8777
+ orderbookDepth: null
8778
+ };
8779
+ }),
8780
+ metrics: {
8781
+ volume: (_d = venueMarket.volume) != null ? _d : null,
8782
+ volume24h: null,
8783
+ openInterest: null,
8784
+ liquidity: null,
8785
+ spread: null
8786
+ },
8787
+ resolution: {
8788
+ sourceName: null,
8789
+ sourceUrl: null,
8790
+ sourceDomain: null,
8791
+ rawCriteria: null,
8792
+ confidence: "missing"
8793
+ },
8794
+ referencePrice: {
8795
+ asset: candidateIdentity.asset,
8796
+ quoteAsset: "USD",
8797
+ raw: null,
8798
+ decimals: null,
8799
+ normalized: null,
8800
+ timestamp: null,
8801
+ provider: null,
8802
+ providerMarketId: null,
8803
+ confidence: "missing"
8804
+ }
8805
+ }
8806
+ ],
8807
+ comparisons: {
8808
+ referencePrice: createEmptyPriceComparison(),
8809
+ outcomePrices: {
8810
+ up: createEmptyPriceComparison(),
8811
+ down: createEmptyPriceComparison()
8812
+ }
8813
+ }
8814
+ };
8815
+ };
8816
+ var formatCanonicalRecurringAggKey = (asset, duration, windowStart) => {
8817
+ const durationSegment = RECURRING_DURATION_AGG_KEY_SEGMENT[duration];
8818
+ if (!durationSegment) return null;
8819
+ const date = new Date(windowStart);
8820
+ if (Number.isNaN(date.getTime())) return null;
8821
+ const twoDigits = (value) => String(value).padStart(2, "0");
8822
+ const timestamp = [
8823
+ twoDigits(date.getUTCFullYear() % 100),
8824
+ twoDigits(date.getUTCMonth() + 1),
8825
+ twoDigits(date.getUTCDate()),
8826
+ "T",
8827
+ twoDigits(date.getUTCHours()),
8828
+ twoDigits(date.getUTCMinutes())
8829
+ ].join("");
8830
+ return `agg_ud_${asset.toLowerCase()}_${durationSegment}_${timestamp}`;
8831
+ };
8832
+ var fetchCanonicalAggKeyWindows = (client, event, identity, signal, now) => __async(null, null, function* () {
8833
+ var _a;
8834
+ const aggKeyMatch = (_a = event.aggKey) == null ? void 0 : _a.match(RECURRING_CRYPTO_AGG_KEY_PATTERN);
8835
+ const asset = identity.asset;
8836
+ if (!aggKeyMatch || !asset) return [];
8837
+ const durationMs = RECURRING_DURATION_MS[identity.duration];
8838
+ const currentWindowStart = Math.floor(now / durationMs) * durationMs;
8839
+ const windowStarts = /* @__PURE__ */ new Set([
8840
+ currentWindowStart - durationMs,
8841
+ currentWindowStart,
8842
+ currentWindowStart + durationMs,
8843
+ currentWindowStart + durationMs * 2,
8844
+ currentWindowStart + durationMs * 3,
8845
+ identity.windowStart
8846
+ ]);
8847
+ const aggKeys = [...windowStarts].flatMap((windowStart) => {
8848
+ if (windowStart == null) return [];
8849
+ const aggKey = formatCanonicalRecurringAggKey(asset, identity.duration, windowStart);
8850
+ return aggKey ? [aggKey] : [];
8851
+ });
8852
+ if (!aggKeys.length) return [];
8853
+ const response = yield client.getVenueEvents({
8854
+ venues: [event.venue],
8855
+ aggKey: aggKeys,
8856
+ limit: 100,
8857
+ signal
8858
+ });
8859
+ return mergeRecurringCryptoWindows(
8860
+ response.data.flatMap((candidate) => {
8861
+ const normalized = normalizeFallbackVenueEvent(candidate, identity);
8862
+ return normalized ? [normalized] : [];
8863
+ })
8864
+ );
8865
+ });
8866
+ var fetchFallbackVenueEventWindows = (client, event, identity, signal, now) => __async(null, null, function* () {
8867
+ var _a;
8868
+ const durationMs = RECURRING_DURATION_MS[identity.duration];
8869
+ const categoryIds = event.categories.filter(
8870
+ ({ category }) => [category.name, category.displayName].some(
8871
+ (value) => (value == null ? void 0 : value.trim().toLowerCase()) === "crypto"
8872
+ )
8873
+ ).map(({ category }) => category.id);
8874
+ const windowStarts = /* @__PURE__ */ new Set([((_a = identity.windowStart) != null ? _a : now) - durationMs, now - durationMs * 2]);
8875
+ const responses = yield Promise.all(
8876
+ [...windowStarts].map((windowStart) => __async(null, null, function* () {
8877
+ var _a2;
8878
+ const windows = [];
8879
+ const seenCursors = /* @__PURE__ */ new Set();
8880
+ let cursor;
8881
+ for (let page = 0; page < FALLBACK_VENUE_EVENT_MAX_PAGES; page += 1) {
8882
+ const response = yield client.getVenueEvents({
8883
+ venues: [event.venue],
8884
+ categoryIds,
8885
+ status: RECURRING_MARKET_STATUSES,
8886
+ sortBy: "endDate",
8887
+ sortDir: "asc",
8888
+ endDateFrom: new Date(windowStart).toISOString(),
8889
+ limit: FALLBACK_VENUE_EVENT_PAGE_LIMIT,
8890
+ cursor,
8891
+ signal
8892
+ });
8893
+ response.data.forEach((candidate) => {
8894
+ const normalized = normalizeFallbackVenueEvent(
8895
+ candidate,
8896
+ identity
8897
+ );
8898
+ if (normalized) windows.push(normalized);
8899
+ });
8900
+ if (mergeRecurringCryptoWindows(windows).length >= FALLBACK_TARGET_WINDOW_COUNT) break;
8901
+ const nextCursor = response.hasMore ? (_a2 = response.nextCursor) != null ? _a2 : void 0 : void 0;
8902
+ if (!nextCursor || seenCursors.has(nextCursor)) break;
8903
+ seenCursors.add(nextCursor);
8904
+ cursor = nextCursor;
8905
+ }
8906
+ return windows;
8907
+ }))
8908
+ );
8909
+ return mergeRecurringCryptoWindows(responses.flat());
8910
+ });
8911
+ var fetchRecurringCryptoMarkets = (_0, _1, _2, ..._3) => __async(null, [_0, _1, _2, ..._3], function* (client, event, signal, now = Date.now()) {
8912
+ var _a, _b;
8913
+ const identity = resolveRecurringCryptoIdentity(event);
8914
+ if (!identity) return [];
8915
+ const { asset, duration, windowStart: anchorStart } = identity;
8916
+ const queryAssets = ((_a = event.aggKey) == null ? void 0 : _a.match(RECURRING_CRYPTO_DAILY_AGG_KEY_PATTERN)) ? void 0 : asset ? [asset] : void 0;
8917
+ const canonicalAggKeyWindowsPromise = fetchCanonicalAggKeyWindows(
8918
+ client,
8919
+ event,
8920
+ identity,
8921
+ signal,
8922
+ now
8923
+ ).catch(() => []);
8924
+ const anchorOptions = anchorStart == null ? {
8925
+ assets: queryAssets,
8926
+ durations: [duration],
8927
+ venues: [event.venue],
8928
+ window: "current",
8929
+ includeDirectVenueMarkets: duration === "P1D"
8930
+ } : {
8931
+ assets: queryAssets,
8932
+ durations: [duration],
8933
+ venues: [event.venue],
8934
+ windowStartFrom: new Date(anchorStart - 1e3).toISOString(),
8935
+ windowStartTo: new Date(anchorStart + 1e3).toISOString(),
8936
+ includeDirectVenueMarkets: duration === "P1D"
8937
+ };
8938
+ let anchorResponse = yield fetchRecurringCryptoPage(client, anchorOptions, signal);
8939
+ let anchor = anchorResponse.data.find(
8940
+ (windowMarket) => windowMarket.markets.some((market) => market.venueEventId === event.id)
8941
+ );
8942
+ let timelineVenues = [event.venue];
8943
+ let canonicalAggKeyWindows = null;
8944
+ if (!anchor) {
8945
+ canonicalAggKeyWindows = yield canonicalAggKeyWindowsPromise;
8946
+ if (canonicalAggKeyWindows.some(
8947
+ (windowMarket) => windowMarket.markets.some((market) => market.venueEventId === event.id)
8948
+ )) {
8949
+ return canonicalAggKeyWindows;
8950
+ }
8951
+ }
8952
+ if (!anchor && asset && anchorStart != null) {
8953
+ const unscopedAnchorResponse = yield fetchRecurringCryptoPage(
8954
+ client,
8955
+ __spreadProps(__spreadValues({}, anchorOptions), { venues: void 0 }),
8956
+ signal
8957
+ );
8958
+ const matchingWindow = unscopedAnchorResponse.data.find(
8959
+ (windowMarket) => windowMarket.asset === asset && windowMarket.duration === duration && parseTimestamp(windowMarket.windowStart) === anchorStart
8960
+ );
8961
+ const viewedWindow = normalizeFallbackVenueEvent(event, identity);
8962
+ if (matchingWindow && viewedWindow) {
8963
+ anchorResponse = unscopedAnchorResponse;
8964
+ anchor = __spreadProps(__spreadValues({}, matchingWindow), {
8965
+ markets: mergeWindowMarkets(matchingWindow.markets, viewedWindow.markets)
8966
+ });
8967
+ timelineVenues = void 0;
8968
+ }
8969
+ }
8970
+ if (!anchor) {
8971
+ return fetchFallbackVenueEventWindows(client, event, identity, signal, now);
8972
+ }
8973
+ const timelineStart = now - RECURRING_DURATION_MS[duration] * 2;
8974
+ const windows = [
8975
+ anchor,
8976
+ ...anchorResponse.data.filter(
8977
+ (windowMarket) => windowMarket.asset === anchor.asset && windowMarket.duration === duration
8978
+ )
8979
+ ];
8980
+ const seenCursors = /* @__PURE__ */ new Set();
8981
+ let cursor;
8982
+ do {
8983
+ const response = yield fetchRecurringCryptoPage(
8984
+ client,
8985
+ {
8986
+ assets: [anchor.asset],
8987
+ durations: [duration],
8988
+ venues: timelineVenues,
8989
+ windowStartFrom: new Date(timelineStart).toISOString(),
8990
+ includeDirectVenueMarkets: duration === "P1D",
8991
+ cursor
8992
+ },
8993
+ signal
8994
+ );
8995
+ windows.push(...response.data);
8996
+ const nextCursor = response.hasMore ? (_b = response.nextCursor) != null ? _b : void 0 : void 0;
8997
+ if (!nextCursor || seenCursors.has(nextCursor)) break;
8998
+ seenCursors.add(nextCursor);
8999
+ cursor = nextCursor;
9000
+ } while (cursor);
9001
+ windows.push(...canonicalAggKeyWindows != null ? canonicalAggKeyWindows : yield canonicalAggKeyWindowsPromise);
9002
+ return mergeRecurringCryptoWindows(windows);
9003
+ });
9004
+ var recurringCryptoTransitionPollInterval = (windows, event, now = Date.now()) => {
9005
+ var _a, _b;
9006
+ if (!windows) return false;
9007
+ const timeline = resolveRecurringCryptoTimeline(windows, event, now);
9008
+ const viewedEnd = parseTimestamp((_b = (_a = timeline.viewed) == null ? void 0 : _a.windowEnd) != null ? _b : event.endDate);
9009
+ return viewedEnd != null && viewedEnd <= now && !timeline.live ? RESOLVING_POLL_INTERVAL_MS : false;
9010
+ };
9011
+ var useRecurringCryptoMarkets = ({
9012
+ event,
9013
+ enabled = true
9014
+ }) => {
9015
+ var _a;
9016
+ const client = useAggClient();
9017
+ const identity = event ? resolveRecurringCryptoIdentity(event) : null;
9018
+ const isEnabled = enabled && event != null && identity != null;
9019
+ const query = (0, import_react_query39.useQuery)({
9020
+ queryKey: [
9021
+ "recurring-crypto-markets",
9022
+ event == null ? void 0 : event.id,
9023
+ identity == null ? void 0 : identity.asset,
9024
+ identity == null ? void 0 : identity.duration,
9025
+ identity == null ? void 0 : identity.windowStart,
9026
+ event == null ? void 0 : event.venue
9027
+ ],
9028
+ queryFn: ({ signal }) => fetchRecurringCryptoMarkets(client, event, signal),
9029
+ enabled: isEnabled,
9030
+ staleTime: 1e4,
9031
+ gcTime: 5 * 6e4,
9032
+ refetchOnWindowFocus: true,
9033
+ refetchOnReconnect: true,
9034
+ refetchOnMount: true,
9035
+ refetchInterval: (queryState) => event ? recurringCryptoTransitionPollInterval(
9036
+ queryState.state.data,
9037
+ event
9038
+ ) : false,
9039
+ retry: 1
9040
+ });
9041
+ return __spreadProps(__spreadValues({}, query), {
9042
+ windows: (_a = query.data) != null ? _a : [],
9043
+ isRecurringCryptoMarket: isEnabled
9044
+ });
9045
+ };
9046
+
9047
+ // src/use-venue-event.ts
8182
9048
  var RESOLUTION_POLL_MS = 15e3;
8183
9049
  var RESOLUTION_POLL_MAX_PAST_END_MS = 60 * 6e4;
8184
9050
  function resolutionPollInterval(event) {
8185
- var _a;
8186
- if (!((_a = event == null ? void 0 : event.venueMarkets) == null ? void 0 : _a.length)) return false;
9051
+ if (!event) return false;
8187
9052
  const now = Date.now();
9053
+ const isRecurringCryptoMarket = resolveRecurringCryptoIdentity(event) != null;
9054
+ if (isRecurringCryptoMarket && event.status === "resolved" /* resolved */) return false;
9055
+ if (isRecurringCryptoMarket && event.status === "closed" /* closed */) {
9056
+ return RESOLUTION_POLL_MS;
9057
+ }
9058
+ const eventEndsAt = event.endDate ? Date.parse(event.endDate) : Number.NaN;
9059
+ if (isRecurringCryptoMarket && event.status === "paused" /* paused */ && Number.isFinite(eventEndsAt) && eventEndsAt <= now) {
9060
+ return RESOLUTION_POLL_MS;
9061
+ }
8188
9062
  const shouldPoll = event.venueMarkets.some((market) => {
8189
- if (market.status !== "open" /* open */ || !market.endDate) return false;
9063
+ if (!market.endDate) return false;
8190
9064
  const endsAt = new Date(market.endDate).getTime();
9065
+ if (isRecurringCryptoMarket && (market.status === "closed" /* closed */ || market.status === "paused" /* paused */) && Number.isFinite(endsAt) && endsAt <= now) {
9066
+ return true;
9067
+ }
9068
+ if (market.status !== "open" /* open */) return false;
8191
9069
  return Number.isFinite(endsAt) && now > endsAt && now < endsAt + RESOLUTION_POLL_MAX_PAST_END_MS;
8192
9070
  });
8193
- return shouldPoll ? RESOLUTION_POLL_MS : false;
9071
+ if (shouldPoll) return RESOLUTION_POLL_MS;
9072
+ if (!isRecurringCryptoMarket || event.status !== "open" /* open */) return false;
9073
+ if (!Number.isFinite(eventEndsAt)) return false;
9074
+ if (now <= eventEndsAt || now >= eventEndsAt + RESOLUTION_POLL_MAX_PAST_END_MS) return false;
9075
+ return RESOLUTION_POLL_MS;
8194
9076
  }
8195
9077
  function useVenueEvent(options) {
8196
9078
  var _a;
8197
9079
  const client = useAggClient();
8198
9080
  const { eventId, enabled = true } = options;
8199
- const query = (0, import_react_query39.useQuery)({
9081
+ const query = (0, import_react_query40.useQuery)({
8200
9082
  queryKey: ["venue-event", eventId],
8201
9083
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
8202
9084
  return client.getVenueEventById(eventId, { signal });
@@ -8216,13 +9098,13 @@ function useVenueEvent(options) {
8216
9098
  }
8217
9099
 
8218
9100
  // src/use-venue-markets.ts
8219
- var import_react_query40 = require("@tanstack/react-query");
9101
+ var import_react_query41 = require("@tanstack/react-query");
8220
9102
  var import_react33 = require("react");
8221
9103
  function useVenueMarkets(options) {
8222
9104
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
8223
9105
  const client = (0, import_react33.useContext)(AggClientContext);
8224
- const queryClient = (0, import_react33.useContext)(import_react_query40.QueryClientContext);
8225
- const [fallbackQueryClient] = (0, import_react33.useState)(() => new import_react_query40.QueryClient());
9106
+ const queryClient = (0, import_react33.useContext)(import_react_query41.QueryClientContext);
9107
+ const [fallbackQueryClient] = (0, import_react33.useState)(() => new import_react_query41.QueryClient());
8226
9108
  const venue = options == null ? void 0 : options.venue;
8227
9109
  const venueEventId = options == null ? void 0 : options.venueEventId;
8228
9110
  const search = options == null ? void 0 : options.search;
@@ -8249,7 +9131,7 @@ function useVenueMarkets(options) {
8249
9131
  if (enabled && !client) {
8250
9132
  throw new Error("useVenueMarkets must be used within an <AggProvider>");
8251
9133
  }
8252
- const query = (0, import_react_query40.useInfiniteQuery)(
9134
+ const query = (0, import_react_query41.useInfiniteQuery)(
8253
9135
  {
8254
9136
  queryKey: [
8255
9137
  "venue-markets",
@@ -8370,13 +9252,13 @@ function useEnrichedVenueEvent(options) {
8370
9252
  }
8371
9253
 
8372
9254
  // src/use-venue-events.ts
8373
- var import_react_query41 = require("@tanstack/react-query");
9255
+ var import_react_query42 = require("@tanstack/react-query");
8374
9256
  var import_react35 = require("react");
8375
9257
  function useVenueEvents(options) {
8376
9258
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
8377
9259
  const client = (0, import_react35.useContext)(AggClientContext);
8378
- const queryClient = (0, import_react35.useContext)(import_react_query41.QueryClientContext);
8379
- const [fallbackQueryClient] = (0, import_react35.useState)(() => new import_react_query41.QueryClient());
9260
+ const queryClient = (0, import_react35.useContext)(import_react_query42.QueryClientContext);
9261
+ const [fallbackQueryClient] = (0, import_react35.useState)(() => new import_react_query42.QueryClient());
8380
9262
  const venues = options == null ? void 0 : options.venues;
8381
9263
  const search = options == null ? void 0 : options.search;
8382
9264
  const categoryIds = options == null ? void 0 : options.categoryIds;
@@ -8403,7 +9285,7 @@ function useVenueEvents(options) {
8403
9285
  if (enabled && !client) {
8404
9286
  throw new Error("useVenueEvents must be used within an <AggProvider>");
8405
9287
  }
8406
- const query = (0, import_react_query41.useInfiniteQuery)(
9288
+ const query = (0, import_react_query42.useInfiniteQuery)(
8407
9289
  {
8408
9290
  queryKey: [
8409
9291
  queryKeyScope,
@@ -8450,7 +9332,7 @@ function useVenueEvents(options) {
8450
9332
  return (_a2 = lastPage.nextCursor) != null ? _a2 : void 0;
8451
9333
  },
8452
9334
  // TODO: RMIK - Comment out to show skeletons on category switch
8453
- placeholderData: import_react_query41.keepPreviousData,
9335
+ placeholderData: import_react_query42.keepPreviousData,
8454
9336
  enabled: enabled && !!client
8455
9337
  },
8456
9338
  queryClient != null ? queryClient : fallbackQueryClient
@@ -8491,8 +9373,48 @@ function useVenueEvents(options) {
8491
9373
  });
8492
9374
  }
8493
9375
 
9376
+ // src/use-sports-live.ts
9377
+ var import_react_query43 = require("@tanstack/react-query");
9378
+ var SPORTS_LIVE_POLL_INTERVAL_MS = 5e3;
9379
+ var SPORTS_SCHEDULED_POLL_INTERVAL_MS = 3e4;
9380
+ var sportsLiveQueryKey = (sortedEventIds) => ["sports-live", ...sortedEventIds];
9381
+ var sportsLiveRefetchInterval = (response) => {
9382
+ if (!response) return false;
9383
+ if (response.data.some((state) => state.status === "live" || state.status === "paused")) {
9384
+ return SPORTS_LIVE_POLL_INTERVAL_MS;
9385
+ }
9386
+ if (response.data.some((state) => state.status === "scheduled")) {
9387
+ return SPORTS_SCHEDULED_POLL_INTERVAL_MS;
9388
+ }
9389
+ return false;
9390
+ };
9391
+ var useSportsLive = (eventIds, enabled = true) => {
9392
+ var _a, _b, _c, _d;
9393
+ const client = useAggClient();
9394
+ const sortedEventIds = [
9395
+ ...new Set(eventIds.map((eventId) => eventId.trim()).filter(Boolean))
9396
+ ].sort();
9397
+ const query = (0, import_react_query43.useQuery)({
9398
+ queryKey: sportsLiveQueryKey(sortedEventIds),
9399
+ queryFn: ({ signal }) => client.getSportsLive(sortedEventIds, { signal }),
9400
+ enabled: enabled && sortedEventIds.length > 0,
9401
+ refetchInterval: (queryState) => sportsLiveRefetchInterval(queryState.state.data),
9402
+ refetchIntervalInBackground: false,
9403
+ refetchOnWindowFocus: true
9404
+ });
9405
+ const states = (_b = (_a = query.data) == null ? void 0 : _a.data) != null ? _b : [];
9406
+ const stateByEventId = new Map(
9407
+ states.map((state) => [state.eventId, state])
9408
+ );
9409
+ return __spreadProps(__spreadValues({}, query), {
9410
+ states,
9411
+ stateByEventId,
9412
+ missingEventIds: (_d = (_c = query.data) == null ? void 0 : _c.missingEventIds) != null ? _d : []
9413
+ });
9414
+ };
9415
+
8494
9416
  // src/use-venue-market-midpoints.ts
8495
- var import_react_query42 = require("@tanstack/react-query");
9417
+ var import_react_query44 = require("@tanstack/react-query");
8496
9418
  var normalizeVenueMarketIds2 = (venueMarketIds) => {
8497
9419
  return [
8498
9420
  ...new Set(venueMarketIds.map((venueMarketId) => venueMarketId.trim()).filter(Boolean))
@@ -8531,7 +9453,7 @@ function useVenueMarketMidpoints(options) {
8531
9453
  const enabled = (_a = options.enabled) != null ? _a : true;
8532
9454
  const requestedVenueMarketIds = normalizeVenueMarketIds2(options.venueMarketIds);
8533
9455
  const maxMidpointIdsPerRequest = (_b = options.maxMidpointIdsPerRequest) != null ? _b : configuredMaxMidpointIdsPerRequest;
8534
- const query = (0, import_react_query42.useQuery)({
9456
+ const query = (0, import_react_query44.useQuery)({
8535
9457
  queryKey: requestedVenueMarketIds.length ? ["venue-market-midpoints", requestedVenueMarketIds, maxMidpointIdsPerRequest] : ["venue-market-midpoints", "__disabled__"],
8536
9458
  queryFn: (_0) => __async(null, [_0], function* ({ signal }) {
8537
9459
  if (requestedVenueMarketIds.length === 0) {
@@ -8547,7 +9469,7 @@ function useVenueMarketMidpoints(options) {
8547
9469
  gcTime: 5 * 6e4,
8548
9470
  refetchOnWindowFocus: false,
8549
9471
  retry: 1,
8550
- placeholderData: import_react_query42.keepPreviousData
9472
+ placeholderData: import_react_query44.keepPreviousData
8551
9473
  });
8552
9474
  const midpointRows = (_d = (_c = query.data) == null ? void 0 : _c.data) != null ? _d : [];
8553
9475
  const midpointsByVenueMarketId = mapMidpointsByVenueMarketId(midpointRows);
@@ -8798,12 +9720,12 @@ function useVisibleIds(options = {}) {
8798
9720
  }
8799
9721
 
8800
9722
  // src/use-app-config.ts
8801
- var import_react_query43 = require("@tanstack/react-query");
9723
+ var import_react_query45 = require("@tanstack/react-query");
8802
9724
  var FIVE_MINUTES = 5 * 60 * 1e3;
8803
9725
  var APP_CONFIG_QUERY_KEY = ["agg", "app-config"];
8804
9726
  function useAppConfig() {
8805
9727
  const client = useAggClient();
8806
- const query = (0, import_react_query43.useQuery)({
9728
+ const query = (0, import_react_query45.useQuery)({
8807
9729
  queryKey: APP_CONFIG_QUERY_KEY,
8808
9730
  queryFn: () => client.getAppConfig(),
8809
9731
  staleTime: FIVE_MINUTES
@@ -8815,8 +9737,8 @@ function useAppConfig() {
8815
9737
  });
8816
9738
  }
8817
9739
  function useCachedAppConfig() {
8818
- const queryClient = (0, import_react_query43.useQueryClient)();
8819
- const query = (0, import_react_query43.useQuery)({
9740
+ const queryClient = (0, import_react_query45.useQueryClient)();
9741
+ const query = (0, import_react_query45.useQuery)({
8820
9742
  queryKey: APP_CONFIG_QUERY_KEY,
8821
9743
  enabled: false,
8822
9744
  initialData: () => queryClient.getQueryData(APP_CONFIG_QUERY_KEY),
@@ -8858,6 +9780,8 @@ var toAppConfigResult = (query) => {
8858
9780
  QueryClient,
8859
9781
  QueryClientProvider,
8860
9782
  RedeemRejectedError,
9783
+ SPORTS_LIVE_POLL_INTERVAL_MS,
9784
+ SPORTS_SCHEDULED_POLL_INTERVAL_MS,
8861
9785
  TradeSide,
8862
9786
  TurnstileChallengeError,
8863
9787
  Venue,
@@ -8868,6 +9792,7 @@ var toAppConfigResult = (query) => {
8868
9792
  defaultSdkUiConfig,
8869
9793
  executionKeys,
8870
9794
  extractOutcomeBestCandidate,
9795
+ fetchRecurringCryptoMarkets,
8871
9796
  findLivePriceById,
8872
9797
  getBuilder,
8873
9798
  getDepositAddress,
@@ -8883,11 +9808,13 @@ var toAppConfigResult = (query) => {
8883
9808
  invalidateUserMoneyState,
8884
9809
  isVenueDisabledByConfig,
8885
9810
  mergeBestPricesPreferringLive,
9811
+ mergeRecurringCryptoWindows,
8886
9812
  normalizeVenueId,
8887
9813
  optimizedImageUrl,
8888
9814
  parseEmail,
8889
9815
  parseEmailStrict,
8890
9816
  rangeToSeconds,
9817
+ recurringCryptoTransitionPollInterval,
8891
9818
  requestAggAuthChooserOpen,
8892
9819
  resolveAggUiLabels,
8893
9820
  resolveDefaultTradingMarket,
@@ -8895,9 +9822,13 @@ var toAppConfigResult = (query) => {
8895
9822
  resolveMarketTradingState,
8896
9823
  resolveMarketWinningOutcome,
8897
9824
  resolveOrderEligibility,
9825
+ resolveRecurringCryptoIdentity,
9826
+ resolveRecurringCryptoTimeline,
8898
9827
  resolveRollingWindow,
8899
9828
  resolveTradingStateKind,
8900
9829
  sortVenues,
9830
+ sportsLiveQueryKey,
9831
+ sportsLiveRefetchInterval,
8901
9832
  timeRangeToInterval,
8902
9833
  tradingReducer,
8903
9834
  useAggAuth,
@@ -8960,6 +9891,7 @@ var toAppConfigResult = (query) => {
8960
9891
  useQueryClient,
8961
9892
  useRampQuotes,
8962
9893
  useRampSession,
9894
+ useRecurringCryptoMarkets,
8963
9895
  useRedeem,
8964
9896
  useRedeemEligibleCount,
8965
9897
  useRedeemLifecycle,
@@ -8969,6 +9901,7 @@ var toAppConfigResult = (query) => {
8969
9901
  useSdkUiConfig,
8970
9902
  useSearch,
8971
9903
  useSmartRoute,
9904
+ useSportsLive,
8972
9905
  useSyncBalances,
8973
9906
  useUserActivity,
8974
9907
  useUserHoldings,