@agg-build/ui 4.7.1 → 4.7.2

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.
@@ -21,7 +21,7 @@ import {
21
21
  formatChainIdLabel,
22
22
  useClaimWinnings,
23
23
  useResolvedMarketClaim
24
- } from "./chunk-3RQCBGIK.mjs";
24
+ } from "./chunk-KDBKKJ7W.mjs";
25
25
  import {
26
26
  getDisplayOutcomePrice,
27
27
  resolveHeaderOutcomeItems,
@@ -993,6 +993,37 @@ var findMatchingVenueOutcome = (market, selectedOutcome) => {
993
993
  return normalizedLabel !== "" && selectedNames.has(normalizedLabel);
994
994
  })) != null ? _b : null;
995
995
  };
996
+ var resolveSellableSharesByVenue = ({
997
+ positions,
998
+ market,
999
+ selectedOutcome
1000
+ }) => {
1001
+ var _a, _b, _c, _d, _e, _f, _g;
1002
+ const sharesByVenue = /* @__PURE__ */ new Map();
1003
+ if (!market || !selectedOutcome) return sharesByVenue;
1004
+ const normalizedSelectedOutcomeLabel = normalizeOutcomeName(selectedOutcome.label);
1005
+ const selectedMarketIdSet = new Set(
1006
+ [
1007
+ market.id,
1008
+ (_a = market.marketId) != null ? _a : void 0,
1009
+ (_b = market.externalIdentifier) != null ? _b : void 0,
1010
+ ...((_c = market.matchedVenueMarkets) != null ? _c : []).map((matched) => matched.id)
1011
+ ].filter((value) => Boolean(value))
1012
+ );
1013
+ for (const group of positions) {
1014
+ for (const outcome of (_e = (_d = group.venueMarket) == null ? void 0 : _d.venueMarketOutcomes) != null ? _e : []) {
1015
+ if (normalizeOutcomeName(outcome.label) !== normalizedSelectedOutcomeLabel) continue;
1016
+ for (const breakdown of (_f = outcome.venueBreakdown) != null ? _f : []) {
1017
+ const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
1018
+ if (!matchesMarketId) continue;
1019
+ const size = Number.isFinite(breakdown.size) ? breakdown.size : 0;
1020
+ if (size <= 0) continue;
1021
+ sharesByVenue.set(breakdown.venue, ((_g = sharesByVenue.get(breakdown.venue)) != null ? _g : 0) + size);
1022
+ }
1023
+ }
1024
+ }
1025
+ return sharesByVenue;
1026
+ };
996
1027
  var buildLimitOrderVenueOptions = ({
997
1028
  currentPrices,
998
1029
  livePrices,
@@ -4255,6 +4286,14 @@ var PlaceOrder = ({
4255
4286
  enabled: Boolean(isAuthenticated && isSell),
4256
4287
  mode: isPaperMode ? "paper" : void 0
4257
4288
  });
4289
+ const sellableSharesByVenue = useMemo(
4290
+ () => isSell ? resolveSellableSharesByVenue({
4291
+ positions: executionPositions,
4292
+ market: scopedSelectedMarket,
4293
+ selectedOutcome: scopedSelectedOutcome
4294
+ }) : /* @__PURE__ */ new Map(),
4295
+ [executionPositions, isSell, scopedSelectedMarket, scopedSelectedOutcome]
4296
+ );
4258
4297
  const isSmartRouteEnabled = !isLimitOrder && isQuotePanelActive && !isEndedWithoutKnownWinner && !submissionProgressState && !isBeforeActionPending && !executeManaged.isPending && debouncedAmount > 0 && orderEligibility.canPlaceOrder && // On the sell tab, hold the quote until the amount has been initialized
4259
4298
  // from the user's position for THIS outcome AND the debounced value has
4260
4299
  // caught up to it. The amount input is shared with the buy tab, so right
@@ -4305,13 +4344,26 @@ var PlaceOrder = ({
4305
4344
  }, [limitVenueQuotes.quotes]);
4306
4345
  const availableLimitVenueOptions = useMemo(() => {
4307
4346
  return visibleLimitVenueOptions.filter((option) => {
4308
- return !geoBlockedLimitVenues.has(option.venue) && getVenueAvailabilityState({
4347
+ var _a2;
4348
+ return !geoBlockedLimitVenues.has(option.venue) && // A limit SELL rests against shares held at that specific venue, so a
4349
+ // venue the user has no position on is a guaranteed 409 from the
4350
+ // server's reservation check — and the automatic pick, which sorts on
4351
+ // price alone, would happily choose one. Only narrow once we know
4352
+ // where the shares are; an empty map means positions haven't resolved.
4353
+ (!isSell || sellableSharesByVenue.size === 0 || ((_a2 = sellableSharesByVenue.get(option.venue)) != null ? _a2 : 0) > 0) && getVenueAvailabilityState({
4309
4354
  venueId: option.venue,
4310
4355
  disabledVenues,
4311
4356
  isConfigLoading: isAppConfigLoading
4312
4357
  }) === "available";
4313
4358
  });
4314
- }, [disabledVenues, geoBlockedLimitVenues, isAppConfigLoading, visibleLimitVenueOptions]);
4359
+ }, [
4360
+ disabledVenues,
4361
+ geoBlockedLimitVenues,
4362
+ isAppConfigLoading,
4363
+ isSell,
4364
+ sellableSharesByVenue,
4365
+ visibleLimitVenueOptions
4366
+ ]);
4315
4367
  const automaticLimitVenueOption = useMemo(
4316
4368
  () => resolveAutomaticLimitOrderVenueOption(availableLimitVenueOptions, internalTab),
4317
4369
  [availableLimitVenueOptions, internalTab]
@@ -4413,39 +4465,15 @@ var PlaceOrder = ({
4413
4465
  refreshContext: smartRouteRefreshContext
4414
4466
  });
4415
4467
  const currentSellableShares = useMemo(() => {
4416
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
4468
+ var _a2, _b2;
4417
4469
  const sharesFromQuote = (_b2 = (_a2 = smartRoute.data) == null ? void 0 : _a2.positionAvailability) == null ? void 0 : _b2.totalSellableShares;
4418
4470
  if (typeof sharesFromQuote === "number" && Number.isFinite(sharesFromQuote)) {
4419
4471
  return Math.max(0, sharesFromQuote);
4420
4472
  }
4421
- if (!isSell || !scopedSelectedOutcome || !scopedSelectedMarket) return 0;
4422
- const normalizedSelectedOutcomeLabel = scopedSelectedOutcome.label.trim().toLowerCase();
4423
- const selectedMarketIds = [
4424
- scopedSelectedMarket.id,
4425
- (_c2 = scopedSelectedMarket.marketId) != null ? _c2 : void 0,
4426
- (_d2 = scopedSelectedMarket.externalIdentifier) != null ? _d2 : void 0
4427
- ].filter((value) => Boolean(value));
4428
- const selectedMarketIdSet = new Set(selectedMarketIds);
4429
4473
  let resolvedShares = 0;
4430
- for (const group of executionPositions) {
4431
- const groupOutcomes = (_f2 = (_e2 = group.venueMarket) == null ? void 0 : _e2.venueMarketOutcomes) != null ? _f2 : [];
4432
- for (const outcome of groupOutcomes) {
4433
- if (outcome.label.trim().toLowerCase() !== normalizedSelectedOutcomeLabel) continue;
4434
- for (const breakdown of (_g2 = outcome.venueBreakdown) != null ? _g2 : []) {
4435
- const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
4436
- if (!matchesMarketId) continue;
4437
- resolvedShares += Number.isFinite(breakdown.size) ? breakdown.size : 0;
4438
- }
4439
- }
4440
- }
4474
+ for (const size of sellableSharesByVenue.values()) resolvedShares += size;
4441
4475
  return Math.max(0, resolvedShares);
4442
- }, [
4443
- executionPositions,
4444
- isSell,
4445
- scopedSelectedMarket,
4446
- scopedSelectedOutcome,
4447
- (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares
4448
- ]);
4476
+ }, [sellableSharesByVenue, (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares]);
4449
4477
  const maxSellableShares = useMemo(
4450
4478
  () => floorToDecimals(currentSellableShares, SELL_SHARE_INPUT_DECIMALS),
4451
4479
  [currentSellableShares]
package/dist/index.js CHANGED
@@ -20744,6 +20744,37 @@ var findMatchingVenueOutcome = (market, selectedOutcome) => {
20744
20744
  return normalizedLabel !== "" && selectedNames.has(normalizedLabel);
20745
20745
  })) != null ? _b : null;
20746
20746
  };
20747
+ var resolveSellableSharesByVenue = ({
20748
+ positions,
20749
+ market,
20750
+ selectedOutcome
20751
+ }) => {
20752
+ var _a, _b, _c, _d, _e, _f, _g;
20753
+ const sharesByVenue = /* @__PURE__ */ new Map();
20754
+ if (!market || !selectedOutcome) return sharesByVenue;
20755
+ const normalizedSelectedOutcomeLabel = normalizeOutcomeName(selectedOutcome.label);
20756
+ const selectedMarketIdSet = new Set(
20757
+ [
20758
+ market.id,
20759
+ (_a = market.marketId) != null ? _a : void 0,
20760
+ (_b = market.externalIdentifier) != null ? _b : void 0,
20761
+ ...((_c = market.matchedVenueMarkets) != null ? _c : []).map((matched) => matched.id)
20762
+ ].filter((value) => Boolean(value))
20763
+ );
20764
+ for (const group of positions) {
20765
+ for (const outcome of (_e = (_d = group.venueMarket) == null ? void 0 : _d.venueMarketOutcomes) != null ? _e : []) {
20766
+ if (normalizeOutcomeName(outcome.label) !== normalizedSelectedOutcomeLabel) continue;
20767
+ for (const breakdown of (_f = outcome.venueBreakdown) != null ? _f : []) {
20768
+ const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
20769
+ if (!matchesMarketId) continue;
20770
+ const size = Number.isFinite(breakdown.size) ? breakdown.size : 0;
20771
+ if (size <= 0) continue;
20772
+ sharesByVenue.set(breakdown.venue, ((_g = sharesByVenue.get(breakdown.venue)) != null ? _g : 0) + size);
20773
+ }
20774
+ }
20775
+ }
20776
+ return sharesByVenue;
20777
+ };
20747
20778
  var buildLimitOrderVenueOptions = ({
20748
20779
  currentPrices,
20749
20780
  livePrices,
@@ -24006,6 +24037,14 @@ var PlaceOrder = ({
24006
24037
  enabled: Boolean(isAuthenticated && isSell),
24007
24038
  mode: isPaperMode ? "paper" : void 0
24008
24039
  });
24040
+ const sellableSharesByVenue = (0, import_react38.useMemo)(
24041
+ () => isSell ? resolveSellableSharesByVenue({
24042
+ positions: executionPositions,
24043
+ market: scopedSelectedMarket,
24044
+ selectedOutcome: scopedSelectedOutcome
24045
+ }) : /* @__PURE__ */ new Map(),
24046
+ [executionPositions, isSell, scopedSelectedMarket, scopedSelectedOutcome]
24047
+ );
24009
24048
  const isSmartRouteEnabled = !isLimitOrder && isQuotePanelActive && !isEndedWithoutKnownWinner && !submissionProgressState && !isBeforeActionPending && !executeManaged.isPending && debouncedAmount > 0 && orderEligibility.canPlaceOrder && // On the sell tab, hold the quote until the amount has been initialized
24010
24049
  // from the user's position for THIS outcome AND the debounced value has
24011
24050
  // caught up to it. The amount input is shared with the buy tab, so right
@@ -24056,13 +24095,26 @@ var PlaceOrder = ({
24056
24095
  }, [limitVenueQuotes.quotes]);
24057
24096
  const availableLimitVenueOptions = (0, import_react38.useMemo)(() => {
24058
24097
  return visibleLimitVenueOptions.filter((option) => {
24059
- return !geoBlockedLimitVenues.has(option.venue) && (0, import_hooks66.getVenueAvailabilityState)({
24098
+ var _a2;
24099
+ return !geoBlockedLimitVenues.has(option.venue) && // A limit SELL rests against shares held at that specific venue, so a
24100
+ // venue the user has no position on is a guaranteed 409 from the
24101
+ // server's reservation check — and the automatic pick, which sorts on
24102
+ // price alone, would happily choose one. Only narrow once we know
24103
+ // where the shares are; an empty map means positions haven't resolved.
24104
+ (!isSell || sellableSharesByVenue.size === 0 || ((_a2 = sellableSharesByVenue.get(option.venue)) != null ? _a2 : 0) > 0) && (0, import_hooks66.getVenueAvailabilityState)({
24060
24105
  venueId: option.venue,
24061
24106
  disabledVenues,
24062
24107
  isConfigLoading: isAppConfigLoading
24063
24108
  }) === "available";
24064
24109
  });
24065
- }, [disabledVenues, geoBlockedLimitVenues, isAppConfigLoading, visibleLimitVenueOptions]);
24110
+ }, [
24111
+ disabledVenues,
24112
+ geoBlockedLimitVenues,
24113
+ isAppConfigLoading,
24114
+ isSell,
24115
+ sellableSharesByVenue,
24116
+ visibleLimitVenueOptions
24117
+ ]);
24066
24118
  const automaticLimitVenueOption = (0, import_react38.useMemo)(
24067
24119
  () => resolveAutomaticLimitOrderVenueOption(availableLimitVenueOptions, internalTab),
24068
24120
  [availableLimitVenueOptions, internalTab]
@@ -24164,39 +24216,15 @@ var PlaceOrder = ({
24164
24216
  refreshContext: smartRouteRefreshContext
24165
24217
  });
24166
24218
  const currentSellableShares = (0, import_react38.useMemo)(() => {
24167
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
24219
+ var _a2, _b2;
24168
24220
  const sharesFromQuote = (_b2 = (_a2 = smartRoute.data) == null ? void 0 : _a2.positionAvailability) == null ? void 0 : _b2.totalSellableShares;
24169
24221
  if (typeof sharesFromQuote === "number" && Number.isFinite(sharesFromQuote)) {
24170
24222
  return Math.max(0, sharesFromQuote);
24171
24223
  }
24172
- if (!isSell || !scopedSelectedOutcome || !scopedSelectedMarket) return 0;
24173
- const normalizedSelectedOutcomeLabel = scopedSelectedOutcome.label.trim().toLowerCase();
24174
- const selectedMarketIds = [
24175
- scopedSelectedMarket.id,
24176
- (_c2 = scopedSelectedMarket.marketId) != null ? _c2 : void 0,
24177
- (_d2 = scopedSelectedMarket.externalIdentifier) != null ? _d2 : void 0
24178
- ].filter((value) => Boolean(value));
24179
- const selectedMarketIdSet = new Set(selectedMarketIds);
24180
24224
  let resolvedShares = 0;
24181
- for (const group of executionPositions) {
24182
- const groupOutcomes = (_f2 = (_e2 = group.venueMarket) == null ? void 0 : _e2.venueMarketOutcomes) != null ? _f2 : [];
24183
- for (const outcome of groupOutcomes) {
24184
- if (outcome.label.trim().toLowerCase() !== normalizedSelectedOutcomeLabel) continue;
24185
- for (const breakdown of (_g2 = outcome.venueBreakdown) != null ? _g2 : []) {
24186
- const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
24187
- if (!matchesMarketId) continue;
24188
- resolvedShares += Number.isFinite(breakdown.size) ? breakdown.size : 0;
24189
- }
24190
- }
24191
- }
24225
+ for (const size of sellableSharesByVenue.values()) resolvedShares += size;
24192
24226
  return Math.max(0, resolvedShares);
24193
- }, [
24194
- executionPositions,
24195
- isSell,
24196
- scopedSelectedMarket,
24197
- scopedSelectedOutcome,
24198
- (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares
24199
- ]);
24227
+ }, [sellableSharesByVenue, (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares]);
24200
24228
  const maxSellableShares = (0, import_react38.useMemo)(
24201
24229
  () => floorToDecimals(currentSellableShares, SELL_SHARE_INPUT_DECIMALS),
24202
24230
  [currentSellableShares]
package/dist/index.mjs CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  formatRecurringCountdown,
15
15
  formatShareAmount,
16
16
  useHomePageModals
17
- } from "./chunk-EOLR7DHL.mjs";
17
+ } from "./chunk-3UBMK2YH.mjs";
18
18
  import {
19
19
  EventList,
20
20
  EventListItem,
@@ -61,7 +61,7 @@ import {
61
61
  requestAggWithdrawModalOpen,
62
62
  useClaimWinnings,
63
63
  useResolvedMarketClaim
64
- } from "./chunk-3RQCBGIK.mjs";
64
+ } from "./chunk-KDBKKJ7W.mjs";
65
65
  import {
66
66
  SETTLEMENT_SECTION_ID,
67
67
  Settlement,
package/dist/pages.js CHANGED
@@ -18519,6 +18519,37 @@ var findMatchingVenueOutcome = (market, selectedOutcome) => {
18519
18519
  return normalizedLabel !== "" && selectedNames.has(normalizedLabel);
18520
18520
  })) != null ? _b : null;
18521
18521
  };
18522
+ var resolveSellableSharesByVenue = ({
18523
+ positions,
18524
+ market,
18525
+ selectedOutcome
18526
+ }) => {
18527
+ var _a, _b, _c, _d, _e, _f, _g;
18528
+ const sharesByVenue = /* @__PURE__ */ new Map();
18529
+ if (!market || !selectedOutcome) return sharesByVenue;
18530
+ const normalizedSelectedOutcomeLabel = normalizeOutcomeName(selectedOutcome.label);
18531
+ const selectedMarketIdSet = new Set(
18532
+ [
18533
+ market.id,
18534
+ (_a = market.marketId) != null ? _a : void 0,
18535
+ (_b = market.externalIdentifier) != null ? _b : void 0,
18536
+ ...((_c = market.matchedVenueMarkets) != null ? _c : []).map((matched) => matched.id)
18537
+ ].filter((value) => Boolean(value))
18538
+ );
18539
+ for (const group of positions) {
18540
+ for (const outcome of (_e = (_d = group.venueMarket) == null ? void 0 : _d.venueMarketOutcomes) != null ? _e : []) {
18541
+ if (normalizeOutcomeName(outcome.label) !== normalizedSelectedOutcomeLabel) continue;
18542
+ for (const breakdown of (_f = outcome.venueBreakdown) != null ? _f : []) {
18543
+ const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
18544
+ if (!matchesMarketId) continue;
18545
+ const size = Number.isFinite(breakdown.size) ? breakdown.size : 0;
18546
+ if (size <= 0) continue;
18547
+ sharesByVenue.set(breakdown.venue, ((_g = sharesByVenue.get(breakdown.venue)) != null ? _g : 0) + size);
18548
+ }
18549
+ }
18550
+ }
18551
+ return sharesByVenue;
18552
+ };
18522
18553
  var buildLimitOrderVenueOptions = ({
18523
18554
  currentPrices,
18524
18555
  livePrices,
@@ -21781,6 +21812,14 @@ var PlaceOrder = ({
21781
21812
  enabled: Boolean(isAuthenticated && isSell),
21782
21813
  mode: isPaperMode ? "paper" : void 0
21783
21814
  });
21815
+ const sellableSharesByVenue = (0, import_react35.useMemo)(
21816
+ () => isSell ? resolveSellableSharesByVenue({
21817
+ positions: executionPositions,
21818
+ market: scopedSelectedMarket,
21819
+ selectedOutcome: scopedSelectedOutcome
21820
+ }) : /* @__PURE__ */ new Map(),
21821
+ [executionPositions, isSell, scopedSelectedMarket, scopedSelectedOutcome]
21822
+ );
21784
21823
  const isSmartRouteEnabled = !isLimitOrder && isQuotePanelActive && !isEndedWithoutKnownWinner && !submissionProgressState && !isBeforeActionPending && !executeManaged.isPending && debouncedAmount > 0 && orderEligibility.canPlaceOrder && // On the sell tab, hold the quote until the amount has been initialized
21785
21824
  // from the user's position for THIS outcome AND the debounced value has
21786
21825
  // caught up to it. The amount input is shared with the buy tab, so right
@@ -21831,13 +21870,26 @@ var PlaceOrder = ({
21831
21870
  }, [limitVenueQuotes.quotes]);
21832
21871
  const availableLimitVenueOptions = (0, import_react35.useMemo)(() => {
21833
21872
  return visibleLimitVenueOptions.filter((option) => {
21834
- return !geoBlockedLimitVenues.has(option.venue) && (0, import_hooks63.getVenueAvailabilityState)({
21873
+ var _a2;
21874
+ return !geoBlockedLimitVenues.has(option.venue) && // A limit SELL rests against shares held at that specific venue, so a
21875
+ // venue the user has no position on is a guaranteed 409 from the
21876
+ // server's reservation check — and the automatic pick, which sorts on
21877
+ // price alone, would happily choose one. Only narrow once we know
21878
+ // where the shares are; an empty map means positions haven't resolved.
21879
+ (!isSell || sellableSharesByVenue.size === 0 || ((_a2 = sellableSharesByVenue.get(option.venue)) != null ? _a2 : 0) > 0) && (0, import_hooks63.getVenueAvailabilityState)({
21835
21880
  venueId: option.venue,
21836
21881
  disabledVenues,
21837
21882
  isConfigLoading: isAppConfigLoading
21838
21883
  }) === "available";
21839
21884
  });
21840
- }, [disabledVenues, geoBlockedLimitVenues, isAppConfigLoading, visibleLimitVenueOptions]);
21885
+ }, [
21886
+ disabledVenues,
21887
+ geoBlockedLimitVenues,
21888
+ isAppConfigLoading,
21889
+ isSell,
21890
+ sellableSharesByVenue,
21891
+ visibleLimitVenueOptions
21892
+ ]);
21841
21893
  const automaticLimitVenueOption = (0, import_react35.useMemo)(
21842
21894
  () => resolveAutomaticLimitOrderVenueOption(availableLimitVenueOptions, internalTab),
21843
21895
  [availableLimitVenueOptions, internalTab]
@@ -21939,39 +21991,15 @@ var PlaceOrder = ({
21939
21991
  refreshContext: smartRouteRefreshContext
21940
21992
  });
21941
21993
  const currentSellableShares = (0, import_react35.useMemo)(() => {
21942
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
21994
+ var _a2, _b2;
21943
21995
  const sharesFromQuote = (_b2 = (_a2 = smartRoute.data) == null ? void 0 : _a2.positionAvailability) == null ? void 0 : _b2.totalSellableShares;
21944
21996
  if (typeof sharesFromQuote === "number" && Number.isFinite(sharesFromQuote)) {
21945
21997
  return Math.max(0, sharesFromQuote);
21946
21998
  }
21947
- if (!isSell || !scopedSelectedOutcome || !scopedSelectedMarket) return 0;
21948
- const normalizedSelectedOutcomeLabel = scopedSelectedOutcome.label.trim().toLowerCase();
21949
- const selectedMarketIds = [
21950
- scopedSelectedMarket.id,
21951
- (_c2 = scopedSelectedMarket.marketId) != null ? _c2 : void 0,
21952
- (_d2 = scopedSelectedMarket.externalIdentifier) != null ? _d2 : void 0
21953
- ].filter((value) => Boolean(value));
21954
- const selectedMarketIdSet = new Set(selectedMarketIds);
21955
21999
  let resolvedShares = 0;
21956
- for (const group of executionPositions) {
21957
- const groupOutcomes = (_f2 = (_e2 = group.venueMarket) == null ? void 0 : _e2.venueMarketOutcomes) != null ? _f2 : [];
21958
- for (const outcome of groupOutcomes) {
21959
- if (outcome.label.trim().toLowerCase() !== normalizedSelectedOutcomeLabel) continue;
21960
- for (const breakdown of (_g2 = outcome.venueBreakdown) != null ? _g2 : []) {
21961
- const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
21962
- if (!matchesMarketId) continue;
21963
- resolvedShares += Number.isFinite(breakdown.size) ? breakdown.size : 0;
21964
- }
21965
- }
21966
- }
22000
+ for (const size of sellableSharesByVenue.values()) resolvedShares += size;
21967
22001
  return Math.max(0, resolvedShares);
21968
- }, [
21969
- executionPositions,
21970
- isSell,
21971
- scopedSelectedMarket,
21972
- scopedSelectedOutcome,
21973
- (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares
21974
- ]);
22002
+ }, [sellableSharesByVenue, (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares]);
21975
22003
  const maxSellableShares = (0, import_react35.useMemo)(
21976
22004
  () => floorToDecimals(currentSellableShares, SELL_SHARE_INPUT_DECIMALS),
21977
22005
  [currentSellableShares]
package/dist/pages.mjs CHANGED
@@ -11,14 +11,14 @@ import {
11
11
  formatRecurringCountdown,
12
12
  formatShareAmount,
13
13
  useHomePageModals
14
- } from "./chunk-EOLR7DHL.mjs";
14
+ } from "./chunk-3UBMK2YH.mjs";
15
15
  import "./chunk-C4GFVJ62.mjs";
16
16
  import {
17
17
  DEPOSIT_MODAL_OPEN_EVENT,
18
18
  WITHDRAW_MODAL_OPEN_EVENT,
19
19
  requestAggDepositModalOpen,
20
20
  requestAggWithdrawModalOpen
21
- } from "./chunk-3RQCBGIK.mjs";
21
+ } from "./chunk-KDBKKJ7W.mjs";
22
22
  import "./chunk-WYCSTAN7.mjs";
23
23
  import "./chunk-XN2NB2NP.mjs";
24
24
  import "./chunk-RDBMHBR2.mjs";
package/dist/trading.js CHANGED
@@ -8444,6 +8444,37 @@ var findMatchingVenueOutcome = (market, selectedOutcome) => {
8444
8444
  return normalizedLabel !== "" && selectedNames.has(normalizedLabel);
8445
8445
  })) != null ? _b : null;
8446
8446
  };
8447
+ var resolveSellableSharesByVenue = ({
8448
+ positions,
8449
+ market,
8450
+ selectedOutcome
8451
+ }) => {
8452
+ var _a, _b, _c, _d, _e, _f, _g;
8453
+ const sharesByVenue = /* @__PURE__ */ new Map();
8454
+ if (!market || !selectedOutcome) return sharesByVenue;
8455
+ const normalizedSelectedOutcomeLabel = normalizeOutcomeName(selectedOutcome.label);
8456
+ const selectedMarketIdSet = new Set(
8457
+ [
8458
+ market.id,
8459
+ (_a = market.marketId) != null ? _a : void 0,
8460
+ (_b = market.externalIdentifier) != null ? _b : void 0,
8461
+ ...((_c = market.matchedVenueMarkets) != null ? _c : []).map((matched) => matched.id)
8462
+ ].filter((value) => Boolean(value))
8463
+ );
8464
+ for (const group of positions) {
8465
+ for (const outcome of (_e = (_d = group.venueMarket) == null ? void 0 : _d.venueMarketOutcomes) != null ? _e : []) {
8466
+ if (normalizeOutcomeName(outcome.label) !== normalizedSelectedOutcomeLabel) continue;
8467
+ for (const breakdown of (_f = outcome.venueBreakdown) != null ? _f : []) {
8468
+ const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
8469
+ if (!matchesMarketId) continue;
8470
+ const size = Number.isFinite(breakdown.size) ? breakdown.size : 0;
8471
+ if (size <= 0) continue;
8472
+ sharesByVenue.set(breakdown.venue, ((_g = sharesByVenue.get(breakdown.venue)) != null ? _g : 0) + size);
8473
+ }
8474
+ }
8475
+ }
8476
+ return sharesByVenue;
8477
+ };
8447
8478
  var buildLimitOrderVenueOptions = ({
8448
8479
  currentPrices,
8449
8480
  livePrices,
@@ -11706,6 +11737,14 @@ var PlaceOrder = ({
11706
11737
  enabled: Boolean(isAuthenticated && isSell),
11707
11738
  mode: isPaperMode ? "paper" : void 0
11708
11739
  });
11740
+ const sellableSharesByVenue = (0, import_react15.useMemo)(
11741
+ () => isSell ? resolveSellableSharesByVenue({
11742
+ positions: executionPositions,
11743
+ market: scopedSelectedMarket,
11744
+ selectedOutcome: scopedSelectedOutcome
11745
+ }) : /* @__PURE__ */ new Map(),
11746
+ [executionPositions, isSell, scopedSelectedMarket, scopedSelectedOutcome]
11747
+ );
11709
11748
  const isSmartRouteEnabled = !isLimitOrder && isQuotePanelActive && !isEndedWithoutKnownWinner && !submissionProgressState && !isBeforeActionPending && !executeManaged.isPending && debouncedAmount > 0 && orderEligibility.canPlaceOrder && // On the sell tab, hold the quote until the amount has been initialized
11710
11749
  // from the user's position for THIS outcome AND the debounced value has
11711
11750
  // caught up to it. The amount input is shared with the buy tab, so right
@@ -11756,13 +11795,26 @@ var PlaceOrder = ({
11756
11795
  }, [limitVenueQuotes.quotes]);
11757
11796
  const availableLimitVenueOptions = (0, import_react15.useMemo)(() => {
11758
11797
  return visibleLimitVenueOptions.filter((option) => {
11759
- return !geoBlockedLimitVenues.has(option.venue) && (0, import_hooks38.getVenueAvailabilityState)({
11798
+ var _a2;
11799
+ return !geoBlockedLimitVenues.has(option.venue) && // A limit SELL rests against shares held at that specific venue, so a
11800
+ // venue the user has no position on is a guaranteed 409 from the
11801
+ // server's reservation check — and the automatic pick, which sorts on
11802
+ // price alone, would happily choose one. Only narrow once we know
11803
+ // where the shares are; an empty map means positions haven't resolved.
11804
+ (!isSell || sellableSharesByVenue.size === 0 || ((_a2 = sellableSharesByVenue.get(option.venue)) != null ? _a2 : 0) > 0) && (0, import_hooks38.getVenueAvailabilityState)({
11760
11805
  venueId: option.venue,
11761
11806
  disabledVenues,
11762
11807
  isConfigLoading: isAppConfigLoading
11763
11808
  }) === "available";
11764
11809
  });
11765
- }, [disabledVenues, geoBlockedLimitVenues, isAppConfigLoading, visibleLimitVenueOptions]);
11810
+ }, [
11811
+ disabledVenues,
11812
+ geoBlockedLimitVenues,
11813
+ isAppConfigLoading,
11814
+ isSell,
11815
+ sellableSharesByVenue,
11816
+ visibleLimitVenueOptions
11817
+ ]);
11766
11818
  const automaticLimitVenueOption = (0, import_react15.useMemo)(
11767
11819
  () => resolveAutomaticLimitOrderVenueOption(availableLimitVenueOptions, internalTab),
11768
11820
  [availableLimitVenueOptions, internalTab]
@@ -11864,39 +11916,15 @@ var PlaceOrder = ({
11864
11916
  refreshContext: smartRouteRefreshContext
11865
11917
  });
11866
11918
  const currentSellableShares = (0, import_react15.useMemo)(() => {
11867
- var _a2, _b2, _c2, _d2, _e2, _f2, _g2;
11919
+ var _a2, _b2;
11868
11920
  const sharesFromQuote = (_b2 = (_a2 = smartRoute.data) == null ? void 0 : _a2.positionAvailability) == null ? void 0 : _b2.totalSellableShares;
11869
11921
  if (typeof sharesFromQuote === "number" && Number.isFinite(sharesFromQuote)) {
11870
11922
  return Math.max(0, sharesFromQuote);
11871
11923
  }
11872
- if (!isSell || !scopedSelectedOutcome || !scopedSelectedMarket) return 0;
11873
- const normalizedSelectedOutcomeLabel = scopedSelectedOutcome.label.trim().toLowerCase();
11874
- const selectedMarketIds = [
11875
- scopedSelectedMarket.id,
11876
- (_c2 = scopedSelectedMarket.marketId) != null ? _c2 : void 0,
11877
- (_d2 = scopedSelectedMarket.externalIdentifier) != null ? _d2 : void 0
11878
- ].filter((value) => Boolean(value));
11879
- const selectedMarketIdSet = new Set(selectedMarketIds);
11880
11924
  let resolvedShares = 0;
11881
- for (const group of executionPositions) {
11882
- const groupOutcomes = (_f2 = (_e2 = group.venueMarket) == null ? void 0 : _e2.venueMarketOutcomes) != null ? _f2 : [];
11883
- for (const outcome of groupOutcomes) {
11884
- if (outcome.label.trim().toLowerCase() !== normalizedSelectedOutcomeLabel) continue;
11885
- for (const breakdown of (_g2 = outcome.venueBreakdown) != null ? _g2 : []) {
11886
- const matchesMarketId = selectedMarketIdSet.size === 0 || selectedMarketIdSet.has(group.targetMarketId) || selectedMarketIdSet.has(breakdown.venueMarketId);
11887
- if (!matchesMarketId) continue;
11888
- resolvedShares += Number.isFinite(breakdown.size) ? breakdown.size : 0;
11889
- }
11890
- }
11891
- }
11925
+ for (const size of sellableSharesByVenue.values()) resolvedShares += size;
11892
11926
  return Math.max(0, resolvedShares);
11893
- }, [
11894
- executionPositions,
11895
- isSell,
11896
- scopedSelectedMarket,
11897
- scopedSelectedOutcome,
11898
- (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares
11899
- ]);
11927
+ }, [sellableSharesByVenue, (_q = (_p = smartRoute.data) == null ? void 0 : _p.positionAvailability) == null ? void 0 : _q.totalSellableShares]);
11900
11928
  const maxSellableShares = (0, import_react15.useMemo)(
11901
11929
  () => floorToDecimals(currentSellableShares, SELL_SHARE_INPUT_DECIMALS),
11902
11930
  [currentSellableShares]
package/dist/trading.mjs CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  parseVenue,
13
13
  useClaimWinnings,
14
14
  useResolvedMarketClaim
15
- } from "./chunk-3RQCBGIK.mjs";
15
+ } from "./chunk-KDBKKJ7W.mjs";
16
16
  import {
17
17
  SETTLEMENT_SECTION_ID,
18
18
  Settlement,
@@ -10,6 +10,32 @@ export declare const isPlaceOrderAveragePriceWorse: ({ averageExecutionPrice, di
10
10
  }) => boolean;
11
11
  export declare const floorToDecimals: (value: number, decimals: number) => number;
12
12
  export declare const formatFlooredShareAmount: (value: number, decimals: number) => string;
13
+ /** Minimal structural view of `useExecutionPositions` rows this module reads. */
14
+ export interface SellablePositionGroup {
15
+ targetMarketId: string;
16
+ venueMarket?: {
17
+ venueMarketOutcomes?: {
18
+ label: string;
19
+ venueBreakdown?: {
20
+ venue: string;
21
+ venueMarketId: string;
22
+ size: number;
23
+ }[];
24
+ }[];
25
+ };
26
+ }
27
+ /**
28
+ * Gross held shares for `selectedOutcome`, keyed by the venue holding them.
29
+ *
30
+ * Positions sit on ONE venue market of a matched cluster, so the accepted id
31
+ * set spans `matchedVenueMarkets` as well — scoping it to the selected market
32
+ * alone reads a cluster position held on another venue as zero.
33
+ */
34
+ export declare const resolveSellableSharesByVenue: ({ positions, market, selectedOutcome, }: {
35
+ positions: readonly SellablePositionGroup[];
36
+ market: VenueMarket | null | undefined;
37
+ selectedOutcome: VenueMarket["venueMarketOutcomes"][number] | null | undefined;
38
+ }) => Map<string, number>;
13
39
  export declare const buildLimitOrderVenueOptions: ({ currentPrices, livePrices, market, selectedOutcomeId, }: {
14
40
  currentPrices: ReadonlyMap<string, number>;
15
41
  livePrices?: ReadonlyMap<string, number>;
@@ -10,6 +10,32 @@ export declare const isPlaceOrderAveragePriceWorse: ({ averageExecutionPrice, di
10
10
  }) => boolean;
11
11
  export declare const floorToDecimals: (value: number, decimals: number) => number;
12
12
  export declare const formatFlooredShareAmount: (value: number, decimals: number) => string;
13
+ /** Minimal structural view of `useExecutionPositions` rows this module reads. */
14
+ export interface SellablePositionGroup {
15
+ targetMarketId: string;
16
+ venueMarket?: {
17
+ venueMarketOutcomes?: {
18
+ label: string;
19
+ venueBreakdown?: {
20
+ venue: string;
21
+ venueMarketId: string;
22
+ size: number;
23
+ }[];
24
+ }[];
25
+ };
26
+ }
27
+ /**
28
+ * Gross held shares for `selectedOutcome`, keyed by the venue holding them.
29
+ *
30
+ * Positions sit on ONE venue market of a matched cluster, so the accepted id
31
+ * set spans `matchedVenueMarkets` as well — scoping it to the selected market
32
+ * alone reads a cluster position held on another venue as zero.
33
+ */
34
+ export declare const resolveSellableSharesByVenue: ({ positions, market, selectedOutcome, }: {
35
+ positions: readonly SellablePositionGroup[];
36
+ market: VenueMarket | null | undefined;
37
+ selectedOutcome: VenueMarket["venueMarketOutcomes"][number] | null | undefined;
38
+ }) => Map<string, number>;
13
39
  export declare const buildLimitOrderVenueOptions: ({ currentPrices, livePrices, market, selectedOutcomeId, }: {
14
40
  currentPrices: ReadonlyMap<string, number>;
15
41
  livePrices?: ReadonlyMap<string, number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agg-build/ui",
3
- "version": "4.7.1",
3
+ "version": "4.7.2",
4
4
  "description": "Pre-built React component library for the AGG prediction market aggregator. Tailwind-based, themeable, with primitives, event surfaces, trading flows, full pages, and modals.",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -100,8 +100,8 @@
100
100
  "liveline": "^0.0.7",
101
101
  "react": "^18.0.0 || ^19.0.0",
102
102
  "react-dom": "^18.0.0 || ^19.0.0",
103
- "@agg-build/sdk": "^4.7.0",
104
- "@agg-build/hooks": "^4.7.0"
103
+ "@agg-build/hooks": "^4.7.0",
104
+ "@agg-build/sdk": "^4.7.0"
105
105
  },
106
106
  "dependencies": {
107
107
  "@number-flow/react": "^0.6.0",