@unifold/connect-react 0.1.51 → 0.1.53

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 (3) hide show
  1. package/dist/index.js +162 -71
  2. package/dist/index.mjs +162 -71
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -6271,13 +6271,18 @@ async function getSupportedDepositTokens(publishableKey, options) {
6271
6271
  const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6272
6272
  validatePublishableKey(pk);
6273
6273
  let url = `${API_BASE_URL}/v1/public/tokens/supported_deposit_tokens`;
6274
+ const params = new URLSearchParams();
6274
6275
  if (options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type) {
6275
- const params = new URLSearchParams({
6276
- destination_token_address: options.destination_token_address,
6277
- destination_chain_id: options.destination_chain_id,
6278
- destination_chain_type: options.destination_chain_type
6279
- });
6280
- url = `${url}?${params.toString()}`;
6276
+ params.set("destination_token_address", options.destination_token_address);
6277
+ params.set("destination_chain_id", options.destination_chain_id);
6278
+ params.set("destination_chain_type", options.destination_chain_type);
6279
+ }
6280
+ if (options?.product_type) {
6281
+ params.set("product_type", options.product_type);
6282
+ }
6283
+ const qs = params.toString();
6284
+ if (qs) {
6285
+ url = `${url}?${qs}`;
6281
6286
  }
6282
6287
  const response = await fetch(url, {
6283
6288
  method: "GET",
@@ -11088,6 +11093,7 @@ var Separator = SelectSeparator;
11088
11093
  var import_jsx_runtime59 = require("react/jsx-runtime");
11089
11094
  var import_jsx_runtime60 = require("react/jsx-runtime");
11090
11095
  var React262 = __toESM(require("react"), 1);
11096
+ var import_react_query11 = require("@tanstack/react-query");
11091
11097
  var import_jsx_runtime61 = require("react/jsx-runtime");
11092
11098
  var import_jsx_runtime62 = require("react/jsx-runtime");
11093
11099
  var import_jsx_runtime63 = require("react/jsx-runtime");
@@ -11099,7 +11105,6 @@ var React272 = __toESM(require("react"), 1);
11099
11105
  var import_jsx_runtime67 = require("react/jsx-runtime");
11100
11106
  var import_jsx_runtime68 = require("react/jsx-runtime");
11101
11107
  var import_react26 = require("react");
11102
- var import_react_query11 = require("@tanstack/react-query");
11103
11108
  var import_react_query12 = require("@tanstack/react-query");
11104
11109
  var import_jsx_runtime69 = require("react/jsx-runtime");
11105
11110
  var import_react27 = require("react");
@@ -13516,14 +13521,14 @@ function BuyWithCard({
13516
13521
  );
13517
13522
  if (matchingCurrency) {
13518
13523
  setCurrency(matchingCurrency.currency_code.toLowerCase());
13519
- if (!amount && !hasManualAmountEntry) {
13524
+ if (!amount && !hasManualAmountEntry && matchingCurrency.default_amount != null) {
13520
13525
  setAmount(matchingCurrency.default_amount.toString());
13521
13526
  }
13522
13527
  } else if (!amount && !hasManualAmountEntry) {
13523
13528
  const usdCurrency = fiatCurrencies.find(
13524
13529
  (c) => c.currency_code.toLowerCase() === "usd"
13525
13530
  );
13526
- if (usdCurrency) {
13531
+ if (usdCurrency?.default_amount != null) {
13527
13532
  setAmount(usdCurrency.default_amount.toString());
13528
13533
  }
13529
13534
  }
@@ -13541,7 +13546,7 @@ function BuyWithCard({
13541
13546
  const currentCurrency = fiatCurrencies.find(
13542
13547
  (c) => c.currency_code.toLowerCase() === currency.toLowerCase()
13543
13548
  );
13544
- if (currentCurrency) {
13549
+ if (currentCurrency?.default_amount != null) {
13545
13550
  setAmount(currentCurrency.default_amount.toString());
13546
13551
  }
13547
13552
  }
@@ -17468,8 +17473,11 @@ function BrowserWalletButton({
17468
17473
  solanaProvider.off("accountChanged", handleAccountsChanged);
17469
17474
  }
17470
17475
  for (const provider of ethProviders) {
17471
- provider.removeListener("accountsChanged", handleEthAccountsChanged);
17472
- provider.removeListener("chainChanged", handleAccountsChanged);
17476
+ const off = provider.off?.bind(provider) ?? provider.removeListener?.bind(provider);
17477
+ if (off) {
17478
+ off("accountsChanged", handleEthAccountsChanged);
17479
+ off("chainChanged", handleAccountsChanged);
17480
+ }
17473
17481
  }
17474
17482
  };
17475
17483
  }, [chainType, eip6963ProviderCount]);
@@ -17863,11 +17871,16 @@ function useAddressValidation({
17863
17871
  };
17864
17872
  }
17865
17873
  function useSupportedDepositTokens(publishableKey, options) {
17866
- const filteredOptions = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type ? {
17867
- destination_token_address: options.destination_token_address,
17868
- destination_chain_id: options.destination_chain_id,
17869
- destination_chain_type: options.destination_chain_type
17870
- } : void 0;
17874
+ const hasDestination = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type;
17875
+ const filteredOptions = {
17876
+ ...hasDestination ? {
17877
+ destination_token_address: options.destination_token_address,
17878
+ destination_chain_id: options.destination_chain_id,
17879
+ destination_chain_type: options.destination_chain_type
17880
+ } : {},
17881
+ ...options?.product_type ? { product_type: options.product_type } : {}
17882
+ };
17883
+ const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
17871
17884
  return (0, import_react_query10.useQuery)({
17872
17885
  queryKey: [
17873
17886
  "unifold",
@@ -17875,9 +17888,13 @@ function useSupportedDepositTokens(publishableKey, options) {
17875
17888
  publishableKey,
17876
17889
  filteredOptions?.destination_token_address ?? null,
17877
17890
  filteredOptions?.destination_chain_id ?? null,
17878
- filteredOptions?.destination_chain_type ?? null
17891
+ filteredOptions?.destination_chain_type ?? null,
17892
+ filteredOptions?.product_type ?? null
17879
17893
  ],
17880
- queryFn: () => getSupportedDepositTokens(publishableKey, filteredOptions),
17894
+ queryFn: () => getSupportedDepositTokens(
17895
+ publishableKey,
17896
+ hasFilteredOptions ? filteredOptions : void 0
17897
+ ),
17881
17898
  staleTime: 1e3 * 60 * 5,
17882
17899
  // 5 minutes — token list rarely changes
17883
17900
  gcTime: 1e3 * 60 * 30,
@@ -19076,7 +19093,8 @@ function TransferCryptoSingleInput({
19076
19093
  onDepositError,
19077
19094
  wallets: externalWallets,
19078
19095
  onSourceTokenChange,
19079
- checkoutQuote
19096
+ checkoutQuote,
19097
+ productType
19080
19098
  }) {
19081
19099
  const { themeClass, colors: colors2, fonts, components } = useTheme();
19082
19100
  const isDarkMode = themeClass.includes("uf-dark");
@@ -19089,7 +19107,8 @@ function TransferCryptoSingleInput({
19089
19107
  const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDepositTokens(publishableKey, {
19090
19108
  destination_token_address: destinationTokenAddress,
19091
19109
  destination_chain_id: destinationChainId,
19092
- destination_chain_type: destinationChainType
19110
+ destination_chain_type: destinationChainType,
19111
+ product_type: productType
19093
19112
  });
19094
19113
  const supportedTokens = tokensResponse?.data ?? [];
19095
19114
  const { token, chain, setToken, setChain, initialSelectionDone } = useDefaultSourceToken({
@@ -20124,6 +20143,54 @@ function TransferCryptoDoubleInput({
20124
20143
  }
20125
20144
  ) });
20126
20145
  }
20146
+ function useDepositQuote(params) {
20147
+ const {
20148
+ publishableKey,
20149
+ sourceChainType,
20150
+ sourceChainId,
20151
+ sourceTokenAddress,
20152
+ destinationAmount,
20153
+ destinationChainType,
20154
+ destinationChainId,
20155
+ destinationTokenAddress,
20156
+ adjustForSlippage,
20157
+ enabled = true
20158
+ } = params;
20159
+ const request = {
20160
+ source_chain_type: sourceChainType,
20161
+ source_chain_id: sourceChainId,
20162
+ source_token_address: sourceTokenAddress,
20163
+ destination_amount: destinationAmount,
20164
+ destination_chain_type: destinationChainType,
20165
+ destination_chain_id: destinationChainId,
20166
+ destination_token_address: destinationTokenAddress,
20167
+ ...adjustForSlippage ? { adjust_for_slippage: true } : {}
20168
+ };
20169
+ return (0, import_react_query11.useQuery)({
20170
+ queryKey: [
20171
+ "unifold",
20172
+ "depositQuote",
20173
+ sourceChainType,
20174
+ sourceChainId,
20175
+ sourceTokenAddress,
20176
+ destinationAmount,
20177
+ destinationChainType,
20178
+ destinationChainId,
20179
+ destinationTokenAddress,
20180
+ adjustForSlippage,
20181
+ publishableKey
20182
+ ],
20183
+ queryFn: () => getDepositQuote(request, publishableKey),
20184
+ enabled: enabled && !!publishableKey && !!sourceChainType && !!sourceChainId && !!sourceTokenAddress && !!destinationAmount && destinationAmount !== "0" && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress,
20185
+ staleTime: 3e4,
20186
+ gcTime: 5 * 6e4,
20187
+ refetchInterval: 3e4,
20188
+ refetchIntervalInBackground: false,
20189
+ refetchOnWindowFocus: true,
20190
+ retry: 2,
20191
+ retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 5e3)
20192
+ });
20193
+ }
20127
20194
  var BROWSER_WALLET_STEP_MIN_HEIGHT_CLASS = "uf-min-h-[460px]";
20128
20195
  var WALLET_ICONS = {
20129
20196
  metamask: MetamaskIcon,
@@ -20599,7 +20666,7 @@ function EnterAmountView({
20599
20666
  }
20600
20667
  )
20601
20668
  ] }) }),
20602
- tokenChainDetails && tokenChainDetails.minimum_deposit_amount_usd > 0 && (isCheckout && checkoutAmountUsd && inputUsdNum > parseFloat(checkoutAmountUsd) - parseFloat(checkoutReceivedUsd || "0") + 5e-3 ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
20669
+ tokenChainDetails && tokenChainDetails.minimum_deposit_amount_usd > 0 && (isCheckout && checkoutAmountUsd && tokenChainDetails.minimum_deposit_amount_usd > parseFloat(checkoutAmountUsd) - parseFloat(checkoutReceivedUsd || "0") + 5e-3 ? /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(
20603
20670
  "div",
20604
20671
  {
20605
20672
  className: "uf-rounded-lg uf-px-3 uf-py-2 uf-mb-3 uf-text-center",
@@ -21201,7 +21268,11 @@ function BrowserWalletModal({
21201
21268
  checkoutReceivedUsd,
21202
21269
  onNewDeposit,
21203
21270
  onDone,
21204
- paymentIntentStatus
21271
+ paymentIntentStatus,
21272
+ checkoutQuote,
21273
+ checkoutDestination,
21274
+ checkoutRemainingBaseUnits,
21275
+ productType
21205
21276
  }) {
21206
21277
  const { colors: colors2, fonts, components } = useTheme();
21207
21278
  const [step, setStep] = React262.useState("select-token");
@@ -21223,7 +21294,49 @@ function BrowserWalletModal({
21223
21294
  const themeClass = theme === "dark" ? "uf-dark" : "";
21224
21295
  const chainType = depositWallet.chain_type;
21225
21296
  const recipientAddress = depositWallet.address;
21297
+ const isCheckoutMode = !!checkoutAmountUsd;
21226
21298
  const supportedChainType = chainType === "algorand" || chainType === "xrpl" ? "ethereum" : chainType;
21299
+ const selectedToken = selectedBalance ? getTokenFromBalance(selectedBalance) : null;
21300
+ const effectiveDestinationAmount = React262.useMemo(() => {
21301
+ if (!checkoutRemainingBaseUnits || checkoutRemainingBaseUnits === "0") return "0";
21302
+ if (!checkoutAmountUsd) return checkoutRemainingBaseUnits;
21303
+ const remaining = BigInt(checkoutRemainingBaseUnits);
21304
+ const minUsd = Math.max(tokenChainDetails?.minimum_deposit_amount_usd ?? 0, 3);
21305
+ const totalUsd = parseFloat(checkoutAmountUsd);
21306
+ if (totalUsd <= 0) return remaining > 0n ? remaining.toString() : "0";
21307
+ const receivedUsd = parseFloat(checkoutReceivedUsd ?? "0");
21308
+ const remainingUsd = totalUsd - receivedUsd;
21309
+ if (remainingUsd <= 0) return "0";
21310
+ const baseUnitsPerUsd = Number(remaining) / remainingUsd;
21311
+ const minBaseUnits = BigInt(Math.ceil(minUsd * baseUnitsPerUsd));
21312
+ const effective = remaining > minBaseUnits ? remaining : minBaseUnits;
21313
+ return effective > 0n ? effective.toString() : "0";
21314
+ }, [checkoutRemainingBaseUnits, checkoutAmountUsd, checkoutReceivedUsd, tokenChainDetails]);
21315
+ const { data: walletCheckoutQuote } = useDepositQuote({
21316
+ publishableKey,
21317
+ sourceChainType: selectedToken?.chain_type ?? "",
21318
+ sourceChainId: selectedToken?.chain_id ?? "",
21319
+ sourceTokenAddress: selectedToken?.token_address ?? "",
21320
+ destinationAmount: effectiveDestinationAmount,
21321
+ destinationChainType: checkoutDestination?.chainType ?? "",
21322
+ destinationChainId: checkoutDestination?.chainId ?? "",
21323
+ destinationTokenAddress: checkoutDestination?.tokenAddress ?? "",
21324
+ adjustForSlippage: true,
21325
+ enabled: open && isCheckoutMode && !!selectedToken && !!checkoutDestination && effectiveDestinationAmount !== "0"
21326
+ });
21327
+ const activeCheckoutQuote = React262.useMemo(() => {
21328
+ if (!isCheckoutMode) return null;
21329
+ if (walletCheckoutQuote) {
21330
+ return {
21331
+ sourceAmount: walletCheckoutQuote.source_amount,
21332
+ sourceTokenDecimals: walletCheckoutQuote.source_token_decimals,
21333
+ sourceTokenSymbol: walletCheckoutQuote.source_token_symbol,
21334
+ sourceAmountUsd: walletCheckoutQuote.source_amount_usd,
21335
+ slippageBufferPercent: walletCheckoutQuote.slippage_buffer_percent ?? null
21336
+ };
21337
+ }
21338
+ return checkoutQuote ?? null;
21339
+ }, [isCheckoutMode, walletCheckoutQuote, checkoutQuote]);
21227
21340
  const { executions: depositExecutions, isPolling, handleIveDeposited } = useDepositPolling({
21228
21341
  userId,
21229
21342
  publishableKey,
@@ -21275,7 +21388,8 @@ function BrowserWalletModal({
21275
21388
  const options = {
21276
21389
  destination_token_address: depositWallet.destination_token_address,
21277
21390
  destination_chain_id: depositWallet.destination_chain_id,
21278
- destination_chain_type: depositWallet.destination_chain_type
21391
+ destination_chain_type: depositWallet.destination_chain_type,
21392
+ ...productType ? { product_type: productType } : {}
21279
21393
  };
21280
21394
  const response = await getSupportedDepositTokens(
21281
21395
  publishableKey,
@@ -21677,7 +21791,6 @@ function BrowserWalletModal({
21677
21791
  throw error2;
21678
21792
  }
21679
21793
  };
21680
- const selectedToken = selectedBalance ? getTokenFromBalance(selectedBalance) : null;
21681
21794
  const usdToTokenRate = React262.useMemo(() => {
21682
21795
  if (!selectedBalance || !selectedBalance.amount_usd || !selectedToken)
21683
21796
  return 0;
@@ -21687,15 +21800,24 @@ function BrowserWalletModal({
21687
21800
  return balanceAmount / balanceUsd;
21688
21801
  }, [selectedBalance, selectedToken]);
21689
21802
  const tokenAmount = React262.useMemo(() => {
21803
+ if (isCheckoutMode && activeCheckoutQuote && selectedToken) {
21804
+ return Number(activeCheckoutQuote.sourceAmount) / 10 ** activeCheckoutQuote.sourceTokenDecimals;
21805
+ }
21690
21806
  const usdNum = parseFloat(amountUsd) || 0;
21691
21807
  if (usdNum === 0 || usdToTokenRate === 0) return 0;
21692
21808
  return usdNum * usdToTokenRate;
21693
- }, [amountUsd, usdToTokenRate]);
21809
+ }, [amountUsd, usdToTokenRate, isCheckoutMode, activeCheckoutQuote, selectedToken]);
21810
+ React262.useEffect(() => {
21811
+ if (isCheckoutMode && activeCheckoutQuote?.sourceAmountUsd && step === "input-amount") {
21812
+ const quoteUsd = activeCheckoutQuote.sourceAmountUsd;
21813
+ setAmountUsd(quoteUsd);
21814
+ }
21815
+ }, [isCheckoutMode, activeCheckoutQuote, step]);
21694
21816
  const maxTokenAmount = selectedBalance && selectedToken ? Number(selectedBalance.amount) / 10 ** selectedToken.decimals : 0;
21695
21817
  const maxUsdAmount = selectedBalance?.amount_usd ? parseFloat(selectedBalance.amount_usd) : 0;
21696
21818
  const inputUsdNum = parseFloat(amountUsd) || 0;
21697
21819
  const minDepositUsd = tokenChainDetails?.minimum_deposit_amount_usd || 0;
21698
- const isValidAmount = inputUsdNum > 0 && inputUsdNum <= maxUsdAmount && inputUsdNum >= minDepositUsd;
21820
+ const isValidAmount = isCheckoutMode && activeCheckoutQuote ? tokenAmount > 0 && tokenAmount <= maxTokenAmount : inputUsdNum > 0 && inputUsdNum <= maxUsdAmount && inputUsdNum >= minDepositUsd;
21699
21821
  const formattedTokenAmount = React262.useMemo(() => {
21700
21822
  if (tokenAmount === 0 || !selectedToken) return null;
21701
21823
  return `${tokenAmount.toFixed(6)} ${selectedToken.symbol}`.replace(
@@ -23135,7 +23257,7 @@ function usePaymentIntent(params) {
23135
23257
  enabled = true,
23136
23258
  pollingInterval = 3e3
23137
23259
  } = params;
23138
- return (0, import_react_query11.useQuery)({
23260
+ return (0, import_react_query12.useQuery)({
23139
23261
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
23140
23262
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
23141
23263
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -23151,49 +23273,6 @@ function usePaymentIntent(params) {
23151
23273
  retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 1e4)
23152
23274
  });
23153
23275
  }
23154
- function useDepositQuote(params) {
23155
- const {
23156
- publishableKey,
23157
- sourceChainType,
23158
- sourceChainId,
23159
- sourceTokenAddress,
23160
- destinationAmount,
23161
- destinationChainType,
23162
- destinationChainId,
23163
- destinationTokenAddress,
23164
- enabled = true
23165
- } = params;
23166
- const request = {
23167
- source_chain_type: sourceChainType,
23168
- source_chain_id: sourceChainId,
23169
- source_token_address: sourceTokenAddress,
23170
- destination_amount: destinationAmount,
23171
- destination_chain_type: destinationChainType,
23172
- destination_chain_id: destinationChainId,
23173
- destination_token_address: destinationTokenAddress
23174
- };
23175
- return (0, import_react_query12.useQuery)({
23176
- queryKey: [
23177
- "unifold",
23178
- "depositQuote",
23179
- sourceChainType,
23180
- sourceChainId,
23181
- sourceTokenAddress,
23182
- destinationAmount,
23183
- destinationChainType,
23184
- destinationChainId,
23185
- destinationTokenAddress,
23186
- publishableKey
23187
- ],
23188
- queryFn: () => getDepositQuote(request, publishableKey),
23189
- enabled: enabled && !!publishableKey && !!sourceChainType && !!sourceChainId && !!sourceTokenAddress && !!destinationAmount && destinationAmount !== "0" && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress,
23190
- staleTime: 6e4,
23191
- gcTime: 5 * 6e4,
23192
- refetchOnWindowFocus: false,
23193
- retry: 2,
23194
- retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 5e3)
23195
- });
23196
- }
23197
23276
  function mapDepositAddressesToWallets(depositAddresses, pi) {
23198
23277
  return depositAddresses.map((da, idx) => ({
23199
23278
  id: da.id,
@@ -23343,6 +23422,7 @@ function CheckoutModal({
23343
23422
  destinationChainType: paymentIntent?.destination_chain_type ?? "",
23344
23423
  destinationChainId: paymentIntent?.destination_chain_id ?? "",
23345
23424
  destinationTokenAddress: paymentIntent?.destination_token_address ?? "",
23425
+ adjustForSlippage: true,
23346
23426
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && quoteDestinationAmount !== "0"
23347
23427
  });
23348
23428
  const handleBrowserWalletClick = (0, import_react26.useCallback)(
@@ -23734,6 +23814,7 @@ function CheckoutModal({
23734
23814
  depositConfirmationMode: "auto_ui",
23735
23815
  wallets,
23736
23816
  onSourceTokenChange: setSelectedSource,
23817
+ productType: "payment",
23737
23818
  checkoutQuote: sourceQuote ? {
23738
23819
  sourceAmount: sourceQuote.source_amount,
23739
23820
  sourceTokenDecimals: sourceQuote.source_token_decimals,
@@ -23773,6 +23854,16 @@ function CheckoutModal({
23773
23854
  prefillAmountUsd: remainingAmountUsd,
23774
23855
  checkoutAmountUsd: paymentIntent?.amount_usd,
23775
23856
  checkoutReceivedUsd: paymentIntent?.amount_received_usd,
23857
+ checkoutDestination: paymentIntent ? {
23858
+ chainType: paymentIntent.destination_chain_type,
23859
+ chainId: paymentIntent.destination_chain_id,
23860
+ tokenAddress: paymentIntent.destination_token_address
23861
+ } : void 0,
23862
+ productType: "payment",
23863
+ checkoutRemainingBaseUnits: paymentIntent ? (() => {
23864
+ const remaining = BigInt(paymentIntent.amount) - BigInt(paymentIntent.amount_received);
23865
+ return remaining > 0n ? remaining.toString() : "0";
23866
+ })() : void 0,
23776
23867
  onSuccess: (txHash) => {
23777
23868
  onCheckoutSuccess?.({
23778
23869
  paymentIntentId: paymentIntent?.id || "",
package/dist/index.mjs CHANGED
@@ -6244,13 +6244,18 @@ async function getSupportedDepositTokens(publishableKey, options) {
6244
6244
  const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6245
6245
  validatePublishableKey(pk);
6246
6246
  let url = `${API_BASE_URL}/v1/public/tokens/supported_deposit_tokens`;
6247
+ const params = new URLSearchParams();
6247
6248
  if (options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type) {
6248
- const params = new URLSearchParams({
6249
- destination_token_address: options.destination_token_address,
6250
- destination_chain_id: options.destination_chain_id,
6251
- destination_chain_type: options.destination_chain_type
6252
- });
6253
- url = `${url}?${params.toString()}`;
6249
+ params.set("destination_token_address", options.destination_token_address);
6250
+ params.set("destination_chain_id", options.destination_chain_id);
6251
+ params.set("destination_chain_type", options.destination_chain_type);
6252
+ }
6253
+ if (options?.product_type) {
6254
+ params.set("product_type", options.product_type);
6255
+ }
6256
+ const qs = params.toString();
6257
+ if (qs) {
6258
+ url = `${url}?${qs}`;
6254
6259
  }
6255
6260
  const response = await fetch(url, {
6256
6261
  method: "GET",
@@ -11061,6 +11066,7 @@ var Separator = SelectSeparator;
11061
11066
  import { jsx as jsx422, jsxs as jsxs36 } from "react/jsx-runtime";
11062
11067
  import { jsx as jsx43, jsxs as jsxs37 } from "react/jsx-runtime";
11063
11068
  import * as React262 from "react";
11069
+ import { useQuery as useQuery9 } from "@tanstack/react-query";
11064
11070
  import { jsx as jsx44 } from "react/jsx-runtime";
11065
11071
  import { jsx as jsx45, jsxs as jsxs38 } from "react/jsx-runtime";
11066
11072
  import { Fragment as Fragment52, jsx as jsx46, jsxs as jsxs39 } from "react/jsx-runtime";
@@ -11079,7 +11085,6 @@ import {
11079
11085
  useRef as useRef82,
11080
11086
  useMemo as useMemo102
11081
11087
  } from "react";
11082
- import { useQuery as useQuery9 } from "@tanstack/react-query";
11083
11088
  import { useQuery as useQuery10 } from "@tanstack/react-query";
11084
11089
  import { Fragment as Fragment10, jsx as jsx522, jsxs as jsxs45 } from "react/jsx-runtime";
11085
11090
  import {
@@ -13502,14 +13507,14 @@ function BuyWithCard({
13502
13507
  );
13503
13508
  if (matchingCurrency) {
13504
13509
  setCurrency(matchingCurrency.currency_code.toLowerCase());
13505
- if (!amount && !hasManualAmountEntry) {
13510
+ if (!amount && !hasManualAmountEntry && matchingCurrency.default_amount != null) {
13506
13511
  setAmount(matchingCurrency.default_amount.toString());
13507
13512
  }
13508
13513
  } else if (!amount && !hasManualAmountEntry) {
13509
13514
  const usdCurrency = fiatCurrencies.find(
13510
13515
  (c) => c.currency_code.toLowerCase() === "usd"
13511
13516
  );
13512
- if (usdCurrency) {
13517
+ if (usdCurrency?.default_amount != null) {
13513
13518
  setAmount(usdCurrency.default_amount.toString());
13514
13519
  }
13515
13520
  }
@@ -13527,7 +13532,7 @@ function BuyWithCard({
13527
13532
  const currentCurrency = fiatCurrencies.find(
13528
13533
  (c) => c.currency_code.toLowerCase() === currency.toLowerCase()
13529
13534
  );
13530
- if (currentCurrency) {
13535
+ if (currentCurrency?.default_amount != null) {
13531
13536
  setAmount(currentCurrency.default_amount.toString());
13532
13537
  }
13533
13538
  }
@@ -17454,8 +17459,11 @@ function BrowserWalletButton({
17454
17459
  solanaProvider.off("accountChanged", handleAccountsChanged);
17455
17460
  }
17456
17461
  for (const provider of ethProviders) {
17457
- provider.removeListener("accountsChanged", handleEthAccountsChanged);
17458
- provider.removeListener("chainChanged", handleAccountsChanged);
17462
+ const off = provider.off?.bind(provider) ?? provider.removeListener?.bind(provider);
17463
+ if (off) {
17464
+ off("accountsChanged", handleEthAccountsChanged);
17465
+ off("chainChanged", handleAccountsChanged);
17466
+ }
17459
17467
  }
17460
17468
  };
17461
17469
  }, [chainType, eip6963ProviderCount]);
@@ -17849,11 +17857,16 @@ function useAddressValidation({
17849
17857
  };
17850
17858
  }
17851
17859
  function useSupportedDepositTokens(publishableKey, options) {
17852
- const filteredOptions = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type ? {
17853
- destination_token_address: options.destination_token_address,
17854
- destination_chain_id: options.destination_chain_id,
17855
- destination_chain_type: options.destination_chain_type
17856
- } : void 0;
17860
+ const hasDestination = options?.destination_token_address && options?.destination_chain_id && options?.destination_chain_type;
17861
+ const filteredOptions = {
17862
+ ...hasDestination ? {
17863
+ destination_token_address: options.destination_token_address,
17864
+ destination_chain_id: options.destination_chain_id,
17865
+ destination_chain_type: options.destination_chain_type
17866
+ } : {},
17867
+ ...options?.product_type ? { product_type: options.product_type } : {}
17868
+ };
17869
+ const hasFilteredOptions = Object.keys(filteredOptions).length > 0;
17857
17870
  return useQuery8({
17858
17871
  queryKey: [
17859
17872
  "unifold",
@@ -17861,9 +17874,13 @@ function useSupportedDepositTokens(publishableKey, options) {
17861
17874
  publishableKey,
17862
17875
  filteredOptions?.destination_token_address ?? null,
17863
17876
  filteredOptions?.destination_chain_id ?? null,
17864
- filteredOptions?.destination_chain_type ?? null
17877
+ filteredOptions?.destination_chain_type ?? null,
17878
+ filteredOptions?.product_type ?? null
17865
17879
  ],
17866
- queryFn: () => getSupportedDepositTokens(publishableKey, filteredOptions),
17880
+ queryFn: () => getSupportedDepositTokens(
17881
+ publishableKey,
17882
+ hasFilteredOptions ? filteredOptions : void 0
17883
+ ),
17867
17884
  staleTime: 1e3 * 60 * 5,
17868
17885
  // 5 minutes — token list rarely changes
17869
17886
  gcTime: 1e3 * 60 * 30,
@@ -19062,7 +19079,8 @@ function TransferCryptoSingleInput({
19062
19079
  onDepositError,
19063
19080
  wallets: externalWallets,
19064
19081
  onSourceTokenChange,
19065
- checkoutQuote
19082
+ checkoutQuote,
19083
+ productType
19066
19084
  }) {
19067
19085
  const { themeClass, colors: colors2, fonts, components } = useTheme();
19068
19086
  const isDarkMode = themeClass.includes("uf-dark");
@@ -19075,7 +19093,8 @@ function TransferCryptoSingleInput({
19075
19093
  const { data: tokensResponse, isLoading: tokensLoading } = useSupportedDepositTokens(publishableKey, {
19076
19094
  destination_token_address: destinationTokenAddress,
19077
19095
  destination_chain_id: destinationChainId,
19078
- destination_chain_type: destinationChainType
19096
+ destination_chain_type: destinationChainType,
19097
+ product_type: productType
19079
19098
  });
19080
19099
  const supportedTokens = tokensResponse?.data ?? [];
19081
19100
  const { token, chain, setToken, setChain, initialSelectionDone } = useDefaultSourceToken({
@@ -20110,6 +20129,54 @@ function TransferCryptoDoubleInput({
20110
20129
  }
20111
20130
  ) });
20112
20131
  }
20132
+ function useDepositQuote(params) {
20133
+ const {
20134
+ publishableKey,
20135
+ sourceChainType,
20136
+ sourceChainId,
20137
+ sourceTokenAddress,
20138
+ destinationAmount,
20139
+ destinationChainType,
20140
+ destinationChainId,
20141
+ destinationTokenAddress,
20142
+ adjustForSlippage,
20143
+ enabled = true
20144
+ } = params;
20145
+ const request = {
20146
+ source_chain_type: sourceChainType,
20147
+ source_chain_id: sourceChainId,
20148
+ source_token_address: sourceTokenAddress,
20149
+ destination_amount: destinationAmount,
20150
+ destination_chain_type: destinationChainType,
20151
+ destination_chain_id: destinationChainId,
20152
+ destination_token_address: destinationTokenAddress,
20153
+ ...adjustForSlippage ? { adjust_for_slippage: true } : {}
20154
+ };
20155
+ return useQuery9({
20156
+ queryKey: [
20157
+ "unifold",
20158
+ "depositQuote",
20159
+ sourceChainType,
20160
+ sourceChainId,
20161
+ sourceTokenAddress,
20162
+ destinationAmount,
20163
+ destinationChainType,
20164
+ destinationChainId,
20165
+ destinationTokenAddress,
20166
+ adjustForSlippage,
20167
+ publishableKey
20168
+ ],
20169
+ queryFn: () => getDepositQuote(request, publishableKey),
20170
+ enabled: enabled && !!publishableKey && !!sourceChainType && !!sourceChainId && !!sourceTokenAddress && !!destinationAmount && destinationAmount !== "0" && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress,
20171
+ staleTime: 3e4,
20172
+ gcTime: 5 * 6e4,
20173
+ refetchInterval: 3e4,
20174
+ refetchIntervalInBackground: false,
20175
+ refetchOnWindowFocus: true,
20176
+ retry: 2,
20177
+ retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 5e3)
20178
+ });
20179
+ }
20113
20180
  var BROWSER_WALLET_STEP_MIN_HEIGHT_CLASS = "uf-min-h-[460px]";
20114
20181
  var WALLET_ICONS = {
20115
20182
  metamask: MetamaskIcon,
@@ -20585,7 +20652,7 @@ function EnterAmountView({
20585
20652
  }
20586
20653
  )
20587
20654
  ] }) }),
20588
- tokenChainDetails && tokenChainDetails.minimum_deposit_amount_usd > 0 && (isCheckout && checkoutAmountUsd && inputUsdNum > parseFloat(checkoutAmountUsd) - parseFloat(checkoutReceivedUsd || "0") + 5e-3 ? /* @__PURE__ */ jsxs39(
20655
+ tokenChainDetails && tokenChainDetails.minimum_deposit_amount_usd > 0 && (isCheckout && checkoutAmountUsd && tokenChainDetails.minimum_deposit_amount_usd > parseFloat(checkoutAmountUsd) - parseFloat(checkoutReceivedUsd || "0") + 5e-3 ? /* @__PURE__ */ jsxs39(
20589
20656
  "div",
20590
20657
  {
20591
20658
  className: "uf-rounded-lg uf-px-3 uf-py-2 uf-mb-3 uf-text-center",
@@ -21187,7 +21254,11 @@ function BrowserWalletModal({
21187
21254
  checkoutReceivedUsd,
21188
21255
  onNewDeposit,
21189
21256
  onDone,
21190
- paymentIntentStatus
21257
+ paymentIntentStatus,
21258
+ checkoutQuote,
21259
+ checkoutDestination,
21260
+ checkoutRemainingBaseUnits,
21261
+ productType
21191
21262
  }) {
21192
21263
  const { colors: colors2, fonts, components } = useTheme();
21193
21264
  const [step, setStep] = React262.useState("select-token");
@@ -21209,7 +21280,49 @@ function BrowserWalletModal({
21209
21280
  const themeClass = theme === "dark" ? "uf-dark" : "";
21210
21281
  const chainType = depositWallet.chain_type;
21211
21282
  const recipientAddress = depositWallet.address;
21283
+ const isCheckoutMode = !!checkoutAmountUsd;
21212
21284
  const supportedChainType = chainType === "algorand" || chainType === "xrpl" ? "ethereum" : chainType;
21285
+ const selectedToken = selectedBalance ? getTokenFromBalance(selectedBalance) : null;
21286
+ const effectiveDestinationAmount = React262.useMemo(() => {
21287
+ if (!checkoutRemainingBaseUnits || checkoutRemainingBaseUnits === "0") return "0";
21288
+ if (!checkoutAmountUsd) return checkoutRemainingBaseUnits;
21289
+ const remaining = BigInt(checkoutRemainingBaseUnits);
21290
+ const minUsd = Math.max(tokenChainDetails?.minimum_deposit_amount_usd ?? 0, 3);
21291
+ const totalUsd = parseFloat(checkoutAmountUsd);
21292
+ if (totalUsd <= 0) return remaining > 0n ? remaining.toString() : "0";
21293
+ const receivedUsd = parseFloat(checkoutReceivedUsd ?? "0");
21294
+ const remainingUsd = totalUsd - receivedUsd;
21295
+ if (remainingUsd <= 0) return "0";
21296
+ const baseUnitsPerUsd = Number(remaining) / remainingUsd;
21297
+ const minBaseUnits = BigInt(Math.ceil(minUsd * baseUnitsPerUsd));
21298
+ const effective = remaining > minBaseUnits ? remaining : minBaseUnits;
21299
+ return effective > 0n ? effective.toString() : "0";
21300
+ }, [checkoutRemainingBaseUnits, checkoutAmountUsd, checkoutReceivedUsd, tokenChainDetails]);
21301
+ const { data: walletCheckoutQuote } = useDepositQuote({
21302
+ publishableKey,
21303
+ sourceChainType: selectedToken?.chain_type ?? "",
21304
+ sourceChainId: selectedToken?.chain_id ?? "",
21305
+ sourceTokenAddress: selectedToken?.token_address ?? "",
21306
+ destinationAmount: effectiveDestinationAmount,
21307
+ destinationChainType: checkoutDestination?.chainType ?? "",
21308
+ destinationChainId: checkoutDestination?.chainId ?? "",
21309
+ destinationTokenAddress: checkoutDestination?.tokenAddress ?? "",
21310
+ adjustForSlippage: true,
21311
+ enabled: open && isCheckoutMode && !!selectedToken && !!checkoutDestination && effectiveDestinationAmount !== "0"
21312
+ });
21313
+ const activeCheckoutQuote = React262.useMemo(() => {
21314
+ if (!isCheckoutMode) return null;
21315
+ if (walletCheckoutQuote) {
21316
+ return {
21317
+ sourceAmount: walletCheckoutQuote.source_amount,
21318
+ sourceTokenDecimals: walletCheckoutQuote.source_token_decimals,
21319
+ sourceTokenSymbol: walletCheckoutQuote.source_token_symbol,
21320
+ sourceAmountUsd: walletCheckoutQuote.source_amount_usd,
21321
+ slippageBufferPercent: walletCheckoutQuote.slippage_buffer_percent ?? null
21322
+ };
21323
+ }
21324
+ return checkoutQuote ?? null;
21325
+ }, [isCheckoutMode, walletCheckoutQuote, checkoutQuote]);
21213
21326
  const { executions: depositExecutions, isPolling, handleIveDeposited } = useDepositPolling({
21214
21327
  userId,
21215
21328
  publishableKey,
@@ -21261,7 +21374,8 @@ function BrowserWalletModal({
21261
21374
  const options = {
21262
21375
  destination_token_address: depositWallet.destination_token_address,
21263
21376
  destination_chain_id: depositWallet.destination_chain_id,
21264
- destination_chain_type: depositWallet.destination_chain_type
21377
+ destination_chain_type: depositWallet.destination_chain_type,
21378
+ ...productType ? { product_type: productType } : {}
21265
21379
  };
21266
21380
  const response = await getSupportedDepositTokens(
21267
21381
  publishableKey,
@@ -21663,7 +21777,6 @@ function BrowserWalletModal({
21663
21777
  throw error2;
21664
21778
  }
21665
21779
  };
21666
- const selectedToken = selectedBalance ? getTokenFromBalance(selectedBalance) : null;
21667
21780
  const usdToTokenRate = React262.useMemo(() => {
21668
21781
  if (!selectedBalance || !selectedBalance.amount_usd || !selectedToken)
21669
21782
  return 0;
@@ -21673,15 +21786,24 @@ function BrowserWalletModal({
21673
21786
  return balanceAmount / balanceUsd;
21674
21787
  }, [selectedBalance, selectedToken]);
21675
21788
  const tokenAmount = React262.useMemo(() => {
21789
+ if (isCheckoutMode && activeCheckoutQuote && selectedToken) {
21790
+ return Number(activeCheckoutQuote.sourceAmount) / 10 ** activeCheckoutQuote.sourceTokenDecimals;
21791
+ }
21676
21792
  const usdNum = parseFloat(amountUsd) || 0;
21677
21793
  if (usdNum === 0 || usdToTokenRate === 0) return 0;
21678
21794
  return usdNum * usdToTokenRate;
21679
- }, [amountUsd, usdToTokenRate]);
21795
+ }, [amountUsd, usdToTokenRate, isCheckoutMode, activeCheckoutQuote, selectedToken]);
21796
+ React262.useEffect(() => {
21797
+ if (isCheckoutMode && activeCheckoutQuote?.sourceAmountUsd && step === "input-amount") {
21798
+ const quoteUsd = activeCheckoutQuote.sourceAmountUsd;
21799
+ setAmountUsd(quoteUsd);
21800
+ }
21801
+ }, [isCheckoutMode, activeCheckoutQuote, step]);
21680
21802
  const maxTokenAmount = selectedBalance && selectedToken ? Number(selectedBalance.amount) / 10 ** selectedToken.decimals : 0;
21681
21803
  const maxUsdAmount = selectedBalance?.amount_usd ? parseFloat(selectedBalance.amount_usd) : 0;
21682
21804
  const inputUsdNum = parseFloat(amountUsd) || 0;
21683
21805
  const minDepositUsd = tokenChainDetails?.minimum_deposit_amount_usd || 0;
21684
- const isValidAmount = inputUsdNum > 0 && inputUsdNum <= maxUsdAmount && inputUsdNum >= minDepositUsd;
21806
+ const isValidAmount = isCheckoutMode && activeCheckoutQuote ? tokenAmount > 0 && tokenAmount <= maxTokenAmount : inputUsdNum > 0 && inputUsdNum <= maxUsdAmount && inputUsdNum >= minDepositUsd;
21685
21807
  const formattedTokenAmount = React262.useMemo(() => {
21686
21808
  if (tokenAmount === 0 || !selectedToken) return null;
21687
21809
  return `${tokenAmount.toFixed(6)} ${selectedToken.symbol}`.replace(
@@ -23121,7 +23243,7 @@ function usePaymentIntent(params) {
23121
23243
  enabled = true,
23122
23244
  pollingInterval = 3e3
23123
23245
  } = params;
23124
- return useQuery9({
23246
+ return useQuery10({
23125
23247
  queryKey: ["unifold", "paymentIntent", clientSecret, publishableKey],
23126
23248
  queryFn: () => retrievePaymentIntent(clientSecret, publishableKey),
23127
23249
  enabled: enabled && !!clientSecret && !!publishableKey,
@@ -23137,49 +23259,6 @@ function usePaymentIntent(params) {
23137
23259
  retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 1e4)
23138
23260
  });
23139
23261
  }
23140
- function useDepositQuote(params) {
23141
- const {
23142
- publishableKey,
23143
- sourceChainType,
23144
- sourceChainId,
23145
- sourceTokenAddress,
23146
- destinationAmount,
23147
- destinationChainType,
23148
- destinationChainId,
23149
- destinationTokenAddress,
23150
- enabled = true
23151
- } = params;
23152
- const request = {
23153
- source_chain_type: sourceChainType,
23154
- source_chain_id: sourceChainId,
23155
- source_token_address: sourceTokenAddress,
23156
- destination_amount: destinationAmount,
23157
- destination_chain_type: destinationChainType,
23158
- destination_chain_id: destinationChainId,
23159
- destination_token_address: destinationTokenAddress
23160
- };
23161
- return useQuery10({
23162
- queryKey: [
23163
- "unifold",
23164
- "depositQuote",
23165
- sourceChainType,
23166
- sourceChainId,
23167
- sourceTokenAddress,
23168
- destinationAmount,
23169
- destinationChainType,
23170
- destinationChainId,
23171
- destinationTokenAddress,
23172
- publishableKey
23173
- ],
23174
- queryFn: () => getDepositQuote(request, publishableKey),
23175
- enabled: enabled && !!publishableKey && !!sourceChainType && !!sourceChainId && !!sourceTokenAddress && !!destinationAmount && destinationAmount !== "0" && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress,
23176
- staleTime: 6e4,
23177
- gcTime: 5 * 6e4,
23178
- refetchOnWindowFocus: false,
23179
- retry: 2,
23180
- retryDelay: (attempt) => Math.min(1e3 * 2 ** attempt, 5e3)
23181
- });
23182
- }
23183
23262
  function mapDepositAddressesToWallets(depositAddresses, pi) {
23184
23263
  return depositAddresses.map((da, idx) => ({
23185
23264
  id: da.id,
@@ -23329,6 +23408,7 @@ function CheckoutModal({
23329
23408
  destinationChainType: paymentIntent?.destination_chain_type ?? "",
23330
23409
  destinationChainId: paymentIntent?.destination_chain_id ?? "",
23331
23410
  destinationTokenAddress: paymentIntent?.destination_token_address ?? "",
23411
+ adjustForSlippage: true,
23332
23412
  enabled: open && view === "transfer" && !!paymentIntent && !!selectedSource && quoteDestinationAmount !== "0"
23333
23413
  });
23334
23414
  const handleBrowserWalletClick = useCallback52(
@@ -23720,6 +23800,7 @@ function CheckoutModal({
23720
23800
  depositConfirmationMode: "auto_ui",
23721
23801
  wallets,
23722
23802
  onSourceTokenChange: setSelectedSource,
23803
+ productType: "payment",
23723
23804
  checkoutQuote: sourceQuote ? {
23724
23805
  sourceAmount: sourceQuote.source_amount,
23725
23806
  sourceTokenDecimals: sourceQuote.source_token_decimals,
@@ -23759,6 +23840,16 @@ function CheckoutModal({
23759
23840
  prefillAmountUsd: remainingAmountUsd,
23760
23841
  checkoutAmountUsd: paymentIntent?.amount_usd,
23761
23842
  checkoutReceivedUsd: paymentIntent?.amount_received_usd,
23843
+ checkoutDestination: paymentIntent ? {
23844
+ chainType: paymentIntent.destination_chain_type,
23845
+ chainId: paymentIntent.destination_chain_id,
23846
+ tokenAddress: paymentIntent.destination_token_address
23847
+ } : void 0,
23848
+ productType: "payment",
23849
+ checkoutRemainingBaseUnits: paymentIntent ? (() => {
23850
+ const remaining = BigInt(paymentIntent.amount) - BigInt(paymentIntent.amount_received);
23851
+ return remaining > 0n ? remaining.toString() : "0";
23852
+ })() : void 0,
23762
23853
  onSuccess: (txHash) => {
23763
23854
  onCheckoutSuccess?.({
23764
23855
  paymentIntentId: paymentIntent?.id || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifold/connect-react",
3
- "version": "0.1.51",
3
+ "version": "0.1.53",
4
4
  "description": "Unifold Connect React - Complete React SDK with UI components for crypto deposits",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@tanstack/react-query": "^5.90.11",
29
- "@unifold/core": "0.1.51",
30
- "@unifold/ui-react": "0.1.51",
31
- "@unifold/react-provider": "0.1.51"
29
+ "@unifold/core": "0.1.53",
30
+ "@unifold/ui-react": "0.1.53",
31
+ "@unifold/react-provider": "0.1.53"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/react": "^19.0.0",