@imtbl/checkout-widgets 2.13.1-alpha.2 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/browser/{AddTokensWidget-DtxxgqCF.js → AddTokensWidget-DZt1XWIr.js} +3 -3
  2. package/dist/browser/{BridgeWidget-BZuzfW2J.js → BridgeWidget-Cekt7iyn.js} +6 -6
  3. package/dist/browser/{CommerceWidget-C77uJBo2.js → CommerceWidget-Bas7CXfv.js} +13 -13
  4. package/dist/browser/{FeesBreakdown-CRh1DLhl.js → FeesBreakdown-C8sYbiJc.js} +1 -1
  5. package/dist/browser/{OnRampWidget-BhN5m26y.js → OnRampWidget-Ca707N9m.js} +3 -3
  6. package/dist/browser/{SaleWidget-D6Ace1BZ.js → SaleWidget-Cx2LielB.js} +64 -24
  7. package/dist/browser/{SpendingCapHero-yVpOdczj.js → SpendingCapHero-oc3m1-Mi.js} +1 -1
  8. package/dist/browser/{SwapWidget-ClIRsyTo.js → SwapWidget-BCZBj17n.js} +6 -6
  9. package/dist/browser/{TokenImage-BP8hEsU_.js → TokenImage-DbilAygL.js} +1 -1
  10. package/dist/browser/{TopUpView-jh7TXNI-.js → TopUpView-CmNYtiOg.js} +1 -1
  11. package/dist/browser/{WalletApproveHero-Eg7vp2Us.js → WalletApproveHero-DFsYy_qL.js} +2 -2
  12. package/dist/browser/{WalletWidget-C51tZZB0.js → WalletWidget-DUEwMbGY.js} +3 -3
  13. package/dist/browser/{auto-track-DsvmUw_O.js → auto-track-DSs_-wn6.js} +1 -1
  14. package/dist/browser/{index-Ct3oyZEC.js → index-BADDrOPQ.js} +19 -19
  15. package/dist/browser/{index-B_FiZcJq.js → index-BJ_7WzV9.js} +1 -1
  16. package/dist/browser/{index-DWE9xBUf.js → index-CTt27k75.js} +1 -1
  17. package/dist/browser/{index-kT3FIcpI.js → index-Cc8boMMi.js} +1 -1
  18. package/dist/browser/{index-BjKL0jz3.js → index-Cha8hldT.js} +1 -1
  19. package/dist/browser/{index-B3VZS6ym.js → index-DVXVFcXh.js} +1 -1
  20. package/dist/browser/{index-DXv1oK4B.js → index-QVU5nPCH.js} +2 -2
  21. package/dist/browser/{index-Cl_Sj41T.js → index-ThfDB_pG.js} +1 -1
  22. package/dist/browser/index.js +1 -1
  23. package/dist/browser/{index.umd-DvqLg8Q3.js → index.umd-Dk45HtZe.js} +1 -1
  24. package/dist/browser/{useInterval-BnmMwpZh.js → useInterval-CZgAUw2x.js} +1 -1
  25. package/dist/types/widgets/sale/hooks/useFundingBalances.d.ts +1 -0
  26. package/package.json +7 -7
  27. package/src/widgets/sale/context/SaleContextProvider.tsx +3 -0
  28. package/src/widgets/sale/functions/fetchFundingBalances.ts +0 -8
  29. package/src/widgets/sale/hooks/useFundingBalances.ts +6 -5
  30. package/src/widgets/sale/hooks/useQuoteOrder.ts +44 -1
  31. package/src/widgets/sale/views/OrderSummary.tsx +10 -0
@@ -47,7 +47,6 @@ export const fetchFundingBalances = async (
47
47
  onFundingBalance,
48
48
  getAmountByCurrency,
49
49
  getIsGasless,
50
- onComplete,
51
50
  onFundingRequirement,
52
51
  onUpdateGasFees,
53
52
  } = params;
@@ -85,10 +84,6 @@ export const fetchFundingBalances = async (
85
84
  ? undefined
86
85
  : getGasEstimate();
87
86
 
88
- const handleOnComplete = () => {
89
- onComplete?.(pushToFoundBalances([]));
90
- };
91
-
92
87
  const handleOnFundingRoute = (route) => {
93
88
  updateFundingBalances(getAlternativeFundingSteps([route], environment));
94
89
  };
@@ -99,9 +94,6 @@ export const fetchFundingBalances = async (
99
94
  transactionOrGasAmount,
100
95
  routingOptions: { bridge: false, onRamp: false, swap: true },
101
96
  fundingRouteFullAmount: true,
102
- onComplete: isBaseCurrency(currency.name)
103
- ? handleOnComplete
104
- : undefined,
105
97
  onFundingRoute: isBaseCurrency(currency.name)
106
98
  ? handleOnFundingRoute
107
99
  : undefined,
@@ -25,6 +25,7 @@ export const useFundingBalances = () => {
25
25
  >([]);
26
26
  const [loadingBalances, setLoadingBalances] = useState(false);
27
27
  const [gasFees, setGasFees] = useState<TokenBalance | undefined>();
28
+ const [fundingBalancesError, setFundingBalancesError] = useState<Error | null>(null);
28
29
 
29
30
  const queryFundingBalances = () => {
30
31
  if (
@@ -40,6 +41,7 @@ export const useFundingBalances = () => {
40
41
  (async () => {
41
42
  fetching.current = true;
42
43
  setLoadingBalances(true);
44
+ setFundingBalancesError(null);
43
45
  try {
44
46
  const results = await fetchFundingBalances({
45
47
  provider,
@@ -55,9 +57,6 @@ export const useFundingBalances = () => {
55
57
  onFundingBalance: (foundBalances) => {
56
58
  setFundingBalances([...foundBalances]);
57
59
  },
58
- onComplete: () => {
59
- setLoadingBalances(false);
60
- },
61
60
  onFundingRequirement: (requirement) => {
62
61
  setTransactionRequirement(requirement);
63
62
  },
@@ -67,9 +66,10 @@ export const useFundingBalances = () => {
67
66
  });
68
67
 
69
68
  setFundingBalancesResult(results);
70
- } catch {
71
- setLoadingBalances(false);
69
+ } catch (err) {
70
+ setFundingBalancesError(err instanceof Error ? err : new Error(String(err)));
72
71
  } finally {
72
+ setLoadingBalances(false);
73
73
  fetching.current = false;
74
74
  }
75
75
  })();
@@ -79,6 +79,7 @@ export const useFundingBalances = () => {
79
79
  fundingBalances,
80
80
  loadingBalances,
81
81
  fundingBalancesResult,
82
+ fundingBalancesError,
82
83
  transactionRequirement,
83
84
  gasFees,
84
85
  queryFundingBalances,
@@ -29,6 +29,35 @@ export type ConfigError = {
29
29
  data?: Record<string, unknown>;
30
30
  };
31
31
 
32
+ /**
33
+ * Validates the order quote response before use. Ensures:
34
+ * - Currencies are non-empty (empty usually indicates wrong project config or endpoint).
35
+ * - Products are non-empty.
36
+ * - Every sale item has a matching product in the quote (no missing productIds).
37
+ */
38
+ function validateOrderQuote(
39
+ config: OrderQuote,
40
+ items: SaleItem[],
41
+ ): { valid: true } | { valid: false; reason: string } {
42
+ if (!config.currencies?.length) {
43
+ return { valid: false, reason: 'Quote returned no currencies' };
44
+ }
45
+
46
+ const productIds = Object.keys(config.products || {});
47
+ if (productIds.length === 0) {
48
+ return { valid: false, reason: 'Quote returned no products' };
49
+ }
50
+
51
+ const missing = items.filter((item) => !config.products![item.productId]);
52
+ if (missing.length > 0) {
53
+ return {
54
+ valid: false,
55
+ reason: `Quote missing products for: ${missing.map((m) => m.productId).join(', ')}`,
56
+ };
57
+ }
58
+ return { valid: true };
59
+ }
60
+
32
61
  export const useQuoteOrder = ({
33
62
  items,
34
63
  environment,
@@ -53,6 +82,13 @@ export const useQuoteOrder = ({
53
82
  });
54
83
  };
55
84
 
85
+ const setQuoteValidationError = (reason: string) => {
86
+ setOrderQuoteError({
87
+ type: SaleErrorTypes.SERVICE_BREAKDOWN,
88
+ data: { reason: 'Invalid order quote response', error: reason },
89
+ });
90
+ };
91
+
56
92
  useEffect(() => {
57
93
  // Set request params
58
94
  if (!items?.length || !provider) return;
@@ -110,6 +146,13 @@ export const useQuoteOrder = ({
110
146
  await response.json(),
111
147
  preferredCurrency,
112
148
  );
149
+
150
+ const validation = validateOrderQuote(config, items);
151
+ if (!validation.valid) {
152
+ setQuoteValidationError(validation.reason);
153
+ return;
154
+ }
155
+
113
156
  setOrderQuote(config);
114
157
  } catch (error) {
115
158
  setError(errorToString(error));
@@ -117,7 +160,7 @@ export const useQuoteOrder = ({
117
160
  fetching.current = false;
118
161
  }
119
162
  })();
120
- }, [environment, environmentId, queryParams]);
163
+ }, [environment, environmentId, queryParams, items]);
121
164
 
122
165
  useEffect(() => {
123
166
  // Set default currency
@@ -185,11 +185,21 @@ export function OrderSummary({ subView }: OrderSummaryProps) {
185
185
  fundingBalances,
186
186
  loadingBalances,
187
187
  fundingBalancesResult,
188
+ fundingBalancesError,
188
189
  transactionRequirement,
189
190
  gasFees,
190
191
  queryFundingBalances,
191
192
  } = useFundingBalances();
192
193
 
194
+ // If funding balances failed to load, transition to error view
195
+ useEffect(() => {
196
+ if (!fundingBalancesError) return;
197
+ closeHandover();
198
+ goToErrorView(SaleErrorTypes.SERVICE_BREAKDOWN, {
199
+ error: errorToString(fundingBalancesError),
200
+ });
201
+ }, [fundingBalancesError, goToErrorView, closeHandover]);
202
+
193
203
  // Initialise funding balances
194
204
  useEffect(() => {
195
205
  if (subView !== OrderSummarySubViews.INIT || !fromTokenAddress) return;