@unifold/connect-react 0.1.45 → 0.1.47

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.d.mts CHANGED
@@ -2,7 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { ThemeMode, ThemeConfig, FontConfig, ComponentConfig, DepositModalInitialScreen, ChainType, DepositConfirmationMode, WithdrawTransactionInfo, AllowedCountryResult } from '@unifold/ui-react';
4
4
  export { AllowedCountryResult, BrowserWalletAmountQuickSelect, Button, ButtonProps, ButtonTokens, CardTokens, ComponentConfig, ComponentTokens, ConfirmingView, ContainerTokens, CustomThemeColors, DepositConfirmationMode, DepositModalInitialScreen as DepositInitialScreen, FontConfig, HeaderTokens, InputTokens, ListTokens, SearchTokens, ThemeColors, ThemeConfig, ThemeMode, WithdrawTransactionInfo } from '@unifold/ui-react';
5
- export { ActionType, AutoSwapRequest, AutoSwapResponse, ChainType, CreateDepositAddressRequest, DefaultTokenChain, DefaultTokenMetadata, DefaultTokenResponse, DepositAddressResponse, DestinationToken, DestinationTokenChain, ExecutionStatus, FeaturedToken, FiatCurrenciesResponse, FiatCurrency, I18nStrings, IconUrl, IpAddressResponse, OnrampQuote, OnrampQuotesRequest, OnrampQuotesResponse, OnrampSessionRequest, OnrampSessionResponse, PaymentIntent, PaymentIntentDepositAddress, PaymentNetwork, ProjectConfigResponse, QueryExecutionsRequest, QueryExecutionsResponse, SOLANA_USDC_ADDRESS, SendSolanaTransactionRequest, SendSolanaTransactionResponse, SupportedChain, SupportedDepositTokensResponse, SupportedDestinationTokensResponse, SupportedToken, TokenChain, TokenChainIconUrl, TokenChainsResponse, UserIpInfo, Wallet, createDepositAddress, createOnrampSession, getApiBaseUrl, getChainName, getDefaultOnrampToken, getFiatCurrencies, getIconUrl, getIconUrlWithCdn, getIpAddress, getOnrampQuotes, getPreferredIconUrl, getProjectConfig, getSupportedDepositTokens, getSupportedDestinationTokens, getTokenChains, getWalletByChainType, i18n, queryExecutions, sendSolanaTransaction, setApiConfig, useUserIp } from '@unifold/core';
5
+ import { DepositEvent } from '@unifold/core';
6
+ export { ActionType, AutoSwapRequest, AutoSwapResponse, ChainType, CreateDepositAddressRequest, DefaultTokenChain, DefaultTokenMetadata, DefaultTokenResponse, DepositAddressResponse, DepositEvent, DepositEventType, DestinationToken, DestinationTokenChain, ExecutionStatus, FeaturedToken, FiatCurrenciesResponse, FiatCurrency, I18nStrings, IconUrl, IpAddressResponse, OnrampQuote, OnrampQuotesRequest, OnrampQuotesResponse, OnrampSessionCreatedData, OnrampSessionCreatedEvent, OnrampSessionRequest, OnrampSessionResponse, PaymentIntent, PaymentIntentDepositAddress, PaymentNetwork, ProjectConfigResponse, QueryExecutionsRequest, QueryExecutionsResponse, SOLANA_USDC_ADDRESS, SendSolanaTransactionRequest, SendSolanaTransactionResponse, SupportedChain, SupportedDepositTokensResponse, SupportedDestinationTokensResponse, SupportedToken, TokenChain, TokenChainIconUrl, TokenChainsResponse, UserIpInfo, Wallet, createDepositAddress, createOnrampSession, generatePrefixedKSUID, getApiBaseUrl, getChainName, getDefaultOnrampToken, getFiatCurrencies, getIconUrl, getIconUrlWithCdn, getIpAddress, getOnrampQuotes, getPreferredIconUrl, getProjectConfig, getSupportedDepositTokens, getSupportedDestinationTokens, getTokenChains, getWalletByChainType, i18n, queryExecutions, sendSolanaTransaction, setApiConfig, useUserIp } from '@unifold/core';
6
7
 
7
8
  interface UnifoldConnectProviderConfig {
8
9
  publishableKey: string;
@@ -86,6 +87,8 @@ interface DepositConfig {
86
87
  onError?: (error: DepositError) => void;
87
88
  /** Called when the user dismisses the deposit dialog (X button, Escape key, or programmatic close) */
88
89
  onClose?: () => void;
90
+ /** Called when deposit lifecycle events occur. Use {@link DepositEventType} to narrow the event type. See {@link DepositEvent} for the full union. */
91
+ onEvent?: (event: DepositEvent) => void;
89
92
  /**
90
93
  * Which screen opens first: menu (`main`, default), `transfer`, `card`, or `tracker`.
91
94
  * `transfer` and `card` run the same geo / recipient checks as the menu before the flow. `tracker` does not
@@ -115,6 +118,18 @@ interface WithdrawError {
115
118
  interface CheckoutConfig {
116
119
  /** The client_secret from a PaymentIntent created on your server */
117
120
  clientSecret: string;
121
+ /**
122
+ * Pre-select source token/chain in the Transfer Crypto view.
123
+ * All four props are optional. To match a specific token, provide `chainType` + `chainId` + (`symbol` OR `tokenAddress`).
124
+ * If omitted or no match is found in `/supported_deposit_tokens`, the first available token and chain are used.
125
+ */
126
+ defaultSourceChainType?: ChainType;
127
+ /** Source chain ID (e.g. `"mainnet"`, `"137"`). Must be paired with `defaultSourceChainType` + symbol or token address. */
128
+ defaultSourceChainId?: string;
129
+ /** Source token contract address. Must be paired with `defaultSourceChainType` + `defaultSourceChainId`. */
130
+ defaultSourceTokenAddress?: string;
131
+ /** Source token symbol (e.g. `"USDC"`). Must be paired with `defaultSourceChainType` + `defaultSourceChainId`. */
132
+ defaultSourceSymbol?: string;
118
133
  /** Optional callbacks (fired immediately when events occur) */
119
134
  onSuccess?: (data: CheckoutResult) => void;
120
135
  onError?: (error: CheckoutError) => void;
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { ThemeMode, ThemeConfig, FontConfig, ComponentConfig, DepositModalInitialScreen, ChainType, DepositConfirmationMode, WithdrawTransactionInfo, AllowedCountryResult } from '@unifold/ui-react';
4
4
  export { AllowedCountryResult, BrowserWalletAmountQuickSelect, Button, ButtonProps, ButtonTokens, CardTokens, ComponentConfig, ComponentTokens, ConfirmingView, ContainerTokens, CustomThemeColors, DepositConfirmationMode, DepositModalInitialScreen as DepositInitialScreen, FontConfig, HeaderTokens, InputTokens, ListTokens, SearchTokens, ThemeColors, ThemeConfig, ThemeMode, WithdrawTransactionInfo } from '@unifold/ui-react';
5
- export { ActionType, AutoSwapRequest, AutoSwapResponse, ChainType, CreateDepositAddressRequest, DefaultTokenChain, DefaultTokenMetadata, DefaultTokenResponse, DepositAddressResponse, DestinationToken, DestinationTokenChain, ExecutionStatus, FeaturedToken, FiatCurrenciesResponse, FiatCurrency, I18nStrings, IconUrl, IpAddressResponse, OnrampQuote, OnrampQuotesRequest, OnrampQuotesResponse, OnrampSessionRequest, OnrampSessionResponse, PaymentIntent, PaymentIntentDepositAddress, PaymentNetwork, ProjectConfigResponse, QueryExecutionsRequest, QueryExecutionsResponse, SOLANA_USDC_ADDRESS, SendSolanaTransactionRequest, SendSolanaTransactionResponse, SupportedChain, SupportedDepositTokensResponse, SupportedDestinationTokensResponse, SupportedToken, TokenChain, TokenChainIconUrl, TokenChainsResponse, UserIpInfo, Wallet, createDepositAddress, createOnrampSession, getApiBaseUrl, getChainName, getDefaultOnrampToken, getFiatCurrencies, getIconUrl, getIconUrlWithCdn, getIpAddress, getOnrampQuotes, getPreferredIconUrl, getProjectConfig, getSupportedDepositTokens, getSupportedDestinationTokens, getTokenChains, getWalletByChainType, i18n, queryExecutions, sendSolanaTransaction, setApiConfig, useUserIp } from '@unifold/core';
5
+ import { DepositEvent } from '@unifold/core';
6
+ export { ActionType, AutoSwapRequest, AutoSwapResponse, ChainType, CreateDepositAddressRequest, DefaultTokenChain, DefaultTokenMetadata, DefaultTokenResponse, DepositAddressResponse, DepositEvent, DepositEventType, DestinationToken, DestinationTokenChain, ExecutionStatus, FeaturedToken, FiatCurrenciesResponse, FiatCurrency, I18nStrings, IconUrl, IpAddressResponse, OnrampQuote, OnrampQuotesRequest, OnrampQuotesResponse, OnrampSessionCreatedData, OnrampSessionCreatedEvent, OnrampSessionRequest, OnrampSessionResponse, PaymentIntent, PaymentIntentDepositAddress, PaymentNetwork, ProjectConfigResponse, QueryExecutionsRequest, QueryExecutionsResponse, SOLANA_USDC_ADDRESS, SendSolanaTransactionRequest, SendSolanaTransactionResponse, SupportedChain, SupportedDepositTokensResponse, SupportedDestinationTokensResponse, SupportedToken, TokenChain, TokenChainIconUrl, TokenChainsResponse, UserIpInfo, Wallet, createDepositAddress, createOnrampSession, generatePrefixedKSUID, getApiBaseUrl, getChainName, getDefaultOnrampToken, getFiatCurrencies, getIconUrl, getIconUrlWithCdn, getIpAddress, getOnrampQuotes, getPreferredIconUrl, getProjectConfig, getSupportedDepositTokens, getSupportedDestinationTokens, getTokenChains, getWalletByChainType, i18n, queryExecutions, sendSolanaTransaction, setApiConfig, useUserIp } from '@unifold/core';
6
7
 
7
8
  interface UnifoldConnectProviderConfig {
8
9
  publishableKey: string;
@@ -86,6 +87,8 @@ interface DepositConfig {
86
87
  onError?: (error: DepositError) => void;
87
88
  /** Called when the user dismisses the deposit dialog (X button, Escape key, or programmatic close) */
88
89
  onClose?: () => void;
90
+ /** Called when deposit lifecycle events occur. Use {@link DepositEventType} to narrow the event type. See {@link DepositEvent} for the full union. */
91
+ onEvent?: (event: DepositEvent) => void;
89
92
  /**
90
93
  * Which screen opens first: menu (`main`, default), `transfer`, `card`, or `tracker`.
91
94
  * `transfer` and `card` run the same geo / recipient checks as the menu before the flow. `tracker` does not
@@ -115,6 +118,18 @@ interface WithdrawError {
115
118
  interface CheckoutConfig {
116
119
  /** The client_secret from a PaymentIntent created on your server */
117
120
  clientSecret: string;
121
+ /**
122
+ * Pre-select source token/chain in the Transfer Crypto view.
123
+ * All four props are optional. To match a specific token, provide `chainType` + `chainId` + (`symbol` OR `tokenAddress`).
124
+ * If omitted or no match is found in `/supported_deposit_tokens`, the first available token and chain are used.
125
+ */
126
+ defaultSourceChainType?: ChainType;
127
+ /** Source chain ID (e.g. `"mainnet"`, `"137"`). Must be paired with `defaultSourceChainType` + symbol or token address. */
128
+ defaultSourceChainId?: string;
129
+ /** Source token contract address. Must be paired with `defaultSourceChainType` + `defaultSourceChainId`. */
130
+ defaultSourceTokenAddress?: string;
131
+ /** Source token symbol (e.g. `"USDC"`). Must be paired with `defaultSourceChainType` + `defaultSourceChainId`. */
132
+ defaultSourceSymbol?: string;
118
133
  /** Optional callbacks (fired immediately when events occur) */
119
134
  onSuccess?: (data: CheckoutResult) => void;
120
135
  onError?: (error: CheckoutError) => void;
package/dist/index.js CHANGED
@@ -1155,11 +1155,13 @@ __export(index_exports, {
1155
1155
  ActionType: () => ActionType,
1156
1156
  Button: () => Button,
1157
1157
  ConfirmingView: () => ConfirmingView,
1158
+ DepositEventType: () => DepositEventType,
1158
1159
  ExecutionStatus: () => ExecutionStatus,
1159
1160
  SOLANA_USDC_ADDRESS: () => SOLANA_USDC_ADDRESS,
1160
1161
  UnifoldProvider: () => UnifoldProvider2,
1161
1162
  createDepositAddress: () => createDepositAddress,
1162
1163
  createOnrampSession: () => createOnrampSession,
1164
+ generatePrefixedKSUID: () => generatePrefixedKSUID,
1163
1165
  getApiBaseUrl: () => getApiBaseUrl,
1164
1166
  getChainName: () => getChainName,
1165
1167
  getDefaultOnrampToken: () => getDefaultOnrampToken,
@@ -6392,6 +6394,9 @@ function getOnrampSessionStartUrl(request, publishableKey) {
6392
6394
  if (request.subdivision_code) {
6393
6395
  params.append("subdivision_code", request.subdivision_code);
6394
6396
  }
6397
+ if (request.external_id) {
6398
+ params.append("external_id", request.external_id);
6399
+ }
6395
6400
  return `${API_BASE_URL}/v1/public/onramps/sessions/start?${params.toString()}`;
6396
6401
  }
6397
6402
  async function getDefaultOnrampToken(params, publishableKey) {
@@ -6715,52 +6720,38 @@ async function getDepositQuote(request, publishableKey) {
6715
6720
  const json = await response.json();
6716
6721
  return json.data;
6717
6722
  }
6718
- async function buildHypercoreTransaction(request, publishableKey) {
6719
- const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6720
- validatePublishableKey(pk);
6721
- const response = await fetch(
6722
- `${API_BASE_URL}/v1/public/transactions/hypercore/build`,
6723
- {
6724
- method: "POST",
6725
- headers: {
6726
- accept: "application/json",
6727
- "x-publishable-key": pk,
6728
- "Content-Type": "application/json"
6729
- },
6730
- body: JSON.stringify(request)
6723
+ function generatePrefixedKSUID(prefix) {
6724
+ const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
6725
+ const KSUID_EPOCH = 14e8;
6726
+ const timestampSeconds = Math.floor(Date.now() / 1e3) - KSUID_EPOCH;
6727
+ const payload = new Uint8Array(20);
6728
+ payload[0] = timestampSeconds >>> 24 & 255;
6729
+ payload[1] = timestampSeconds >>> 16 & 255;
6730
+ payload[2] = timestampSeconds >>> 8 & 255;
6731
+ payload[3] = timestampSeconds & 255;
6732
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
6733
+ crypto.getRandomValues(payload.subarray(4));
6734
+ } else {
6735
+ for (let i = 4; i < 20; i++) {
6736
+ payload[i] = Math.floor(Math.random() * 256);
6731
6737
  }
6732
- );
6733
- if (!response.ok) {
6734
- const error = await response.json().catch(() => ({ message: response.statusText }));
6735
- throw new Error(
6736
- `Failed to build HyperCore transaction: ${error.message || response.statusText}`
6737
- );
6738
6738
  }
6739
- return response.json();
6740
- }
6741
- async function sendHypercoreTransaction(request, publishableKey) {
6742
- const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6743
- validatePublishableKey(pk);
6744
- const response = await fetch(
6745
- `${API_BASE_URL}/v1/public/transactions/hypercore/send`,
6746
- {
6747
- method: "POST",
6748
- headers: {
6749
- accept: "application/json",
6750
- "x-publishable-key": pk,
6751
- "Content-Type": "application/json"
6752
- },
6753
- body: JSON.stringify(request)
6754
- }
6755
- );
6756
- if (!response.ok) {
6757
- const error = await response.json().catch(() => ({ message: response.statusText }));
6758
- throw new Error(
6759
- `Failed to send HyperCore transaction: ${error.message || response.statusText}`
6760
- );
6739
+ let value = 0n;
6740
+ for (const byte of payload) {
6741
+ value = value << 8n | BigInt(byte);
6761
6742
  }
6762
- return response.json();
6743
+ let encoded = "";
6744
+ while (value > 0n) {
6745
+ encoded = BASE62[Number(value % 62n)] + encoded;
6746
+ value = value / 62n;
6747
+ }
6748
+ encoded = encoded.padStart(27, "0");
6749
+ return `${prefix}_${encoded}`;
6763
6750
  }
6751
+ var DepositEventType = /* @__PURE__ */ ((DepositEventType2) => {
6752
+ DepositEventType2["ONRAMP_SESSION_CREATED"] = "onramp_session.created";
6753
+ return DepositEventType2;
6754
+ })(DepositEventType || {});
6764
6755
  function useUserIp() {
6765
6756
  const {
6766
6757
  data: userIpInfo,
@@ -13406,6 +13397,7 @@ function BuyWithCard({
13406
13397
  destinationTokenAddress,
13407
13398
  onDepositSuccess,
13408
13399
  onDepositError,
13400
+ onEvent,
13409
13401
  themeClass = "",
13410
13402
  wallets: externalWallets,
13411
13403
  assetCdnUrl,
@@ -13759,6 +13751,7 @@ function BuyWithCard({
13759
13751
  setQuotesError("Wallet address not available");
13760
13752
  return;
13761
13753
  }
13754
+ const externalId = generatePrefixedKSUID("orsext");
13762
13755
  const sessionRequest = {
13763
13756
  service_provider: selectedProvider.service_provider,
13764
13757
  country_code: selectedProvider.country_code.toUpperCase() || "US",
@@ -13767,7 +13760,8 @@ function BuyWithCard({
13767
13760
  destination_currency: selectedProvider.destination_currency,
13768
13761
  destination_network: selectedProvider.destination_network,
13769
13762
  wallet_address: wallet.address,
13770
- subdivision_code: userIpInfo?.state || void 0
13763
+ subdivision_code: userIpInfo?.state || void 0,
13764
+ external_id: externalId
13771
13765
  };
13772
13766
  const sessionStartUrl = getOnrampSessionStartUrl(
13773
13767
  sessionRequest,
@@ -13777,7 +13771,14 @@ function BuyWithCard({
13777
13771
  provider: selectedProvider,
13778
13772
  sourceCurrency: currency,
13779
13773
  sourceAmount: amount,
13780
- sessionUrl: sessionStartUrl
13774
+ sessionUrl: sessionStartUrl,
13775
+ externalId
13776
+ });
13777
+ onEvent?.({
13778
+ id: generatePrefixedKSUID("sevt"),
13779
+ type: DepositEventType.ONRAMP_SESSION_CREATED,
13780
+ created: Math.floor(Date.now() / 1e3),
13781
+ data: { object: { externalId } }
13781
13782
  });
13782
13783
  window.open(sessionStartUrl, "_blank");
13783
13784
  handleViewChange("onramp");
@@ -21248,10 +21249,11 @@ function BrowserWalletModal({
21248
21249
  const chainType = depositWallet.chain_type;
21249
21250
  const recipientAddress = depositWallet.address;
21250
21251
  const supportedChainType = chainType === "algorand" || chainType === "xrpl" ? "ethereum" : chainType;
21251
- const { executions: depositExecutions, isPolling } = useDepositPolling({
21252
+ const { executions: depositExecutions, isPolling, handleIveDeposited } = useDepositPolling({
21252
21253
  userId,
21253
21254
  publishableKey,
21254
21255
  clientSecret,
21256
+ depositWalletId: depositWallet.id,
21255
21257
  enabled: open && hasSignedTransaction,
21256
21258
  onDepositSuccess,
21257
21259
  onDepositError
@@ -21502,6 +21504,7 @@ function BrowserWalletModal({
21502
21504
  }
21503
21505
  setReceivedUsdAtSubmission(checkoutReceivedUsd ?? "0");
21504
21506
  setHasSignedTransaction(true);
21507
+ handleIveDeposited();
21505
21508
  setIsConfirming(false);
21506
21509
  setStep("confirming");
21507
21510
  onSuccess?.(txHash);
@@ -22538,6 +22541,7 @@ function DepositModal({
22538
22541
  hideDisplayDescription = false,
22539
22542
  onDepositSuccess,
22540
22543
  onDepositError,
22544
+ onEvent,
22541
22545
  theme = "dark",
22542
22546
  hideOverlay = false,
22543
22547
  initialScreen = "main",
@@ -23052,6 +23056,7 @@ function DepositModal({
23052
23056
  destinationTokenAddress,
23053
23057
  onDepositSuccess,
23054
23058
  onDepositError,
23059
+ onEvent,
23055
23060
  themeClass,
23056
23061
  wallets,
23057
23062
  assetCdnUrl: projectConfig?.asset_cdn_url,
@@ -23235,6 +23240,10 @@ function CheckoutModal({
23235
23240
  publishableKey,
23236
23241
  modalTitle,
23237
23242
  enableConnectWallet = false,
23243
+ defaultSourceChainType,
23244
+ defaultSourceChainId,
23245
+ defaultSourceTokenAddress,
23246
+ defaultSourceSymbol,
23238
23247
  theme = "dark",
23239
23248
  onCheckoutSuccess,
23240
23249
  onCheckoutError
@@ -23732,6 +23741,10 @@ function CheckoutModal({
23732
23741
  destinationChainType: paymentIntent.destination_chain_type,
23733
23742
  destinationChainId: paymentIntent.destination_chain_id,
23734
23743
  destinationTokenAddress: paymentIntent.destination_token_address,
23744
+ defaultSourceChainType,
23745
+ defaultSourceChainId,
23746
+ defaultSourceTokenAddress,
23747
+ defaultSourceSymbol,
23735
23748
  depositConfirmationMode: "auto_ui",
23736
23749
  wallets,
23737
23750
  onSourceTokenChange: setSelectedSource,
@@ -24233,249 +24246,10 @@ function useVerifyRecipientAddress(params) {
24233
24246
  refetchOnWindowFocus: false
24234
24247
  });
24235
24248
  }
24236
- async function sendEvmWithdraw(params) {
24237
- const {
24238
- provider,
24239
- fromAddress,
24240
- depositWalletAddress,
24241
- sourceTokenAddress,
24242
- sourceChainId,
24243
- amountBaseUnit
24244
- } = params;
24245
- const currentChainIdHex = await provider.request({
24246
- method: "eth_chainId",
24247
- params: []
24248
- });
24249
- const currentChainId = parseInt(currentChainIdHex, 16).toString();
24250
- if (currentChainId !== sourceChainId) {
24251
- const requiredHex = "0x" + parseInt(sourceChainId).toString(16);
24252
- try {
24253
- await provider.request({
24254
- method: "wallet_switchEthereumChain",
24255
- params: [{ chainId: requiredHex }]
24256
- });
24257
- const newHex = await provider.request({ method: "eth_chainId", params: [] });
24258
- if (parseInt(newHex, 16).toString() !== sourceChainId) {
24259
- throw new Error(`Failed to switch to chain ${sourceChainId}. Please switch manually.`);
24260
- }
24261
- } catch (err) {
24262
- if (err && typeof err === "object" && "code" in err) {
24263
- const e = err;
24264
- if (e.code === 4902) throw new Error(`Chain ${sourceChainId} is not configured in your wallet.`);
24265
- if (e.code === 4001) throw new Error("You must approve the network switch to withdraw.");
24266
- }
24267
- throw err;
24268
- }
24269
- }
24270
- const isNative = sourceTokenAddress === "native" || sourceTokenAddress === "0x0000000000000000000000000000000000000000" || sourceTokenAddress === "";
24271
- const amountBig = BigInt(amountBaseUnit);
24272
- const txParams = isNative ? { from: fromAddress, to: depositWalletAddress, value: "0x" + amountBig.toString(16) } : {
24273
- from: fromAddress,
24274
- to: sourceTokenAddress,
24275
- data: "0xa9059cbb" + depositWalletAddress.slice(2).padStart(64, "0") + amountBig.toString(16).padStart(64, "0")
24276
- };
24277
- let gasEstimate;
24278
- try {
24279
- const hex = await provider.request({ method: "eth_estimateGas", params: [txParams] });
24280
- gasEstimate = BigInt(hex);
24281
- } catch {
24282
- gasEstimate = isNative ? BigInt(21e3) : BigInt(65e3);
24283
- }
24284
- const gasPrice = BigInt(await provider.request({ method: "eth_gasPrice", params: [] }));
24285
- const gasWithBuffer = gasEstimate * BigInt(120) / BigInt(100);
24286
- const gasCost = gasWithBuffer * gasPrice;
24287
- const ethBalance = BigInt(
24288
- await provider.request({ method: "eth_getBalance", params: [fromAddress, "latest"] })
24289
- );
24290
- const totalRequired = isNative ? gasCost + amountBig : gasCost;
24291
- if (ethBalance < totalRequired) {
24292
- const gasFmt = (Number(gasCost) / 1e18).toFixed(6);
24293
- if (isNative) {
24294
- throw new Error(`Insufficient balance. Need ${(Number(totalRequired) / 1e18).toFixed(6)} ETH (amount + ~${gasFmt} gas).`);
24295
- }
24296
- throw new Error(`Insufficient ETH for gas. Need ~${gasFmt} ETH for fees.`);
24297
- }
24298
- const txHash = await provider.request({ method: "eth_sendTransaction", params: [txParams] });
24299
- return txHash;
24300
- }
24301
- async function sendSolanaWithdraw(params) {
24302
- const {
24303
- provider,
24304
- fromAddress,
24305
- depositWalletAddress,
24306
- sourceTokenAddress,
24307
- amountBaseUnit,
24308
- publishableKey
24309
- } = params;
24310
- if (!provider.publicKey) {
24311
- await provider.connect();
24312
- }
24313
- const buildResponse = await buildSolanaTransaction(
24314
- {
24315
- chain_id: "mainnet",
24316
- token_address: sourceTokenAddress === "" ? "native" : sourceTokenAddress,
24317
- source_address: fromAddress,
24318
- destination_address: depositWalletAddress,
24319
- amount: amountBaseUnit
24320
- },
24321
- publishableKey
24322
- );
24323
- const { VersionedTransaction } = await import(
24324
- /* @vite-ignore */
24325
- "@solana/web3.js"
24326
- );
24327
- const binaryString = atob(buildResponse.transaction);
24328
- const bytes = new Uint8Array(binaryString.length);
24329
- for (let i = 0; i < binaryString.length; i++) {
24330
- bytes[i] = binaryString.charCodeAt(i);
24331
- }
24332
- const transaction = VersionedTransaction.deserialize(bytes);
24333
- const signedTransaction = await provider.signTransaction(transaction);
24334
- const serialized = signedTransaction.serialize();
24335
- let binaryStr = "";
24336
- for (let i = 0; i < serialized.length; i++) {
24337
- binaryStr += String.fromCharCode(serialized[i]);
24338
- }
24339
- const sendResponse = await sendSolanaTransaction(
24340
- { chain_id: "mainnet", signed_transaction: btoa(binaryStr) },
24341
- publishableKey
24342
- );
24343
- return sendResponse.signature;
24344
- }
24345
24249
  var HYPERCORE_CHAIN_ID = "1337";
24346
- var HYPERCORE_SPOT_USDC_ADDRESS = "0x6d1e7cde53ba9467b783cb7c530ce054";
24347
24250
  function isHypercoreChain(chainId) {
24348
24251
  return chainId === HYPERCORE_CHAIN_ID;
24349
24252
  }
24350
- async function sendHypercoreWithdraw(params) {
24351
- const {
24352
- provider,
24353
- fromAddress,
24354
- depositWalletAddress,
24355
- sourceTokenAddress,
24356
- amount,
24357
- tokenSymbol,
24358
- publishableKey
24359
- } = params;
24360
- const isSpot = sourceTokenAddress.toLowerCase() === HYPERCORE_SPOT_USDC_ADDRESS;
24361
- const currentChainHex = await provider.request({
24362
- method: "eth_chainId",
24363
- params: []
24364
- });
24365
- const activeChainId = String(parseInt(currentChainHex, 16));
24366
- const buildResult = await buildHypercoreTransaction(
24367
- {
24368
- action_type: isSpot ? "spot_send" : "usd_send",
24369
- signature_chain_type: "ethereum",
24370
- signature_chain_id: activeChainId,
24371
- recipient_address: depositWalletAddress,
24372
- token_address: sourceTokenAddress,
24373
- token_symbol: tokenSymbol || void 0,
24374
- amount
24375
- },
24376
- publishableKey
24377
- );
24378
- const signature = await provider.request({
24379
- method: "eth_signTypedData_v4",
24380
- params: [fromAddress, JSON.stringify(buildResult.typed_data)]
24381
- });
24382
- await sendHypercoreTransaction(
24383
- {
24384
- action_payload: buildResult.action_payload,
24385
- signature,
24386
- nonce: buildResult.nonce
24387
- },
24388
- publishableKey
24389
- );
24390
- }
24391
- async function detectBrowserWallet(chainType, senderAddress) {
24392
- const win = typeof window !== "undefined" ? window : null;
24393
- if (!win || !senderAddress) return null;
24394
- if (getUserDisconnectedWallet()) return null;
24395
- const anyWin = win;
24396
- if (chainType === "solana") {
24397
- const solProviders = [];
24398
- if (win.phantom?.solana) solProviders.push({ provider: win.phantom.solana, name: "Phantom" });
24399
- if (anyWin.solflare) solProviders.push({ provider: anyWin.solflare, name: "Solflare" });
24400
- if (anyWin.backpack) solProviders.push({ provider: anyWin.backpack, name: "Backpack" });
24401
- if (anyWin.trustwallet?.solana) solProviders.push({ provider: anyWin.trustwallet.solana, name: "Trust Wallet" });
24402
- for (const { provider, name } of solProviders) {
24403
- if (!provider) continue;
24404
- try {
24405
- let addr;
24406
- if (provider.isConnected && provider.publicKey) {
24407
- addr = provider.publicKey.toString();
24408
- } else {
24409
- const resp = await provider.connect({ onlyIfTrusted: true });
24410
- if (resp?.publicKey) addr = resp.publicKey.toString();
24411
- }
24412
- if (addr && addr === senderAddress) {
24413
- return { chainFamily: "solana", provider, name, address: addr };
24414
- }
24415
- } catch {
24416
- }
24417
- }
24418
- }
24419
- if (chainType === "ethereum") {
24420
- const evmProviders = [];
24421
- const seen = /* @__PURE__ */ new Set();
24422
- const add = (p, name) => {
24423
- if (p && typeof p.request === "function" && !seen.has(p)) {
24424
- seen.add(p);
24425
- evmProviders.push({ provider: p, name });
24426
- }
24427
- };
24428
- if (!anyWin.__eip6963Providers) {
24429
- anyWin.__eip6963Providers = [];
24430
- }
24431
- const handleAnnouncement = (event) => {
24432
- const { detail } = event;
24433
- if (!detail?.info || !detail?.provider) return;
24434
- const exists = anyWin.__eip6963Providers.some((p) => p.info.uuid === detail.info.uuid);
24435
- if (!exists) anyWin.__eip6963Providers.push(detail);
24436
- };
24437
- win.addEventListener("eip6963:announceProvider", handleAnnouncement);
24438
- win.dispatchEvent(new Event("eip6963:requestProvider"));
24439
- win.removeEventListener("eip6963:announceProvider", handleAnnouncement);
24440
- for (const detail of anyWin.__eip6963Providers) {
24441
- const rdns = detail.info?.rdns || "";
24442
- let name = detail.info?.name || "Wallet";
24443
- if (rdns.includes("metamask")) name = "MetaMask";
24444
- else if (rdns.includes("phantom")) name = "Phantom";
24445
- else if (rdns.includes("coinbase")) name = "Coinbase";
24446
- else if (rdns.includes("rabby")) name = "Rabby";
24447
- else if (rdns.includes("rainbow")) name = "Rainbow";
24448
- else if (rdns.includes("okx")) name = "OKX Wallet";
24449
- else if (rdns.includes("trust")) name = "Trust Wallet";
24450
- add(detail.provider, name);
24451
- }
24452
- if (evmProviders.length === 0) {
24453
- add(anyWin.phantom?.ethereum, "Phantom");
24454
- add(anyWin.coinbaseWalletExtension, "Coinbase");
24455
- add(anyWin.trustwallet?.ethereum, "Trust Wallet");
24456
- add(anyWin.okxwallet, "OKX Wallet");
24457
- if (evmProviders.length === 0 && win.ethereum) {
24458
- const eth = win.ethereum;
24459
- let name = "Wallet";
24460
- if (eth.isMetaMask && !eth.isPhantom && !eth.isRabby) name = "MetaMask";
24461
- else if (eth.isRabby) name = "Rabby";
24462
- else if (eth.isRainbow) name = "Rainbow";
24463
- else if (eth.isCoinbaseWallet) name = "Coinbase";
24464
- add(eth, name);
24465
- }
24466
- }
24467
- for (const { provider, name } of evmProviders) {
24468
- try {
24469
- const accounts = await provider.request({ method: "eth_accounts" });
24470
- if (accounts?.length > 0 && accounts[0].toLowerCase() === senderAddress.toLowerCase()) {
24471
- return { chainFamily: "evm", provider, name, address: accounts[0] };
24472
- }
24473
- } catch {
24474
- }
24475
- }
24476
- }
24477
- return null;
24478
- }
24479
24253
  var t8 = i18n2.withdrawModal;
24480
24254
  var tCrypto = i18n2.transferCrypto;
24481
24255
  function formatProcessingTime2(seconds) {
@@ -24623,8 +24397,8 @@ function WithdrawForm({
24623
24397
  setAmount(fiat.toFixed(2));
24624
24398
  setInputUnit("fiat");
24625
24399
  } else {
24626
- const crypto = val / exchangeRate;
24627
- setAmount(crypto.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
24400
+ const crypto2 = val / exchangeRate;
24401
+ setAmount(crypto2.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
24628
24402
  setInputUnit("crypto");
24629
24403
  }
24630
24404
  }, [amount, inputUnit, exchangeRate, sourceDecimals]);
@@ -24646,6 +24420,9 @@ function WithdrawForm({
24646
24420
  setIsSubmitting(true);
24647
24421
  setSubmitError(null);
24648
24422
  try {
24423
+ if (!onWithdraw) {
24424
+ throw new Error("No withdrawal method available. Please provide an onWithdraw handler.");
24425
+ }
24649
24426
  const depositWallet = await onDepositWalletCreation({
24650
24427
  destinationChainType: selectedChain.chain_type,
24651
24428
  destinationChainId: selectedChain.chain_id,
@@ -24703,63 +24480,16 @@ function WithdrawForm({
24703
24480
  withdrawIntentAddress: depositWallet.address,
24704
24481
  recipientAddress: trimmedAddress
24705
24482
  };
24706
- const wallet = await detectBrowserWallet(sourceChainType, senderAddress);
24707
- console.log("browser wallet", wallet);
24708
- if (wallet) {
24709
- try {
24710
- if (wallet.chainFamily === "evm" && isHypercoreChain(sourceChainId)) {
24711
- await sendHypercoreWithdraw({
24712
- provider: wallet.provider,
24713
- fromAddress: wallet.address,
24714
- depositWalletAddress: depositWallet.address,
24715
- sourceTokenAddress,
24716
- amount: humanAmount,
24717
- tokenSymbol,
24718
- publishableKey
24719
- });
24720
- } else if (wallet.chainFamily === "evm") {
24721
- await sendEvmWithdraw({
24722
- provider: wallet.provider,
24723
- fromAddress: wallet.address,
24724
- depositWalletAddress: depositWallet.address,
24725
- sourceTokenAddress,
24726
- sourceChainId,
24727
- amountBaseUnit
24728
- });
24729
- } else if (wallet.chainFamily === "solana") {
24730
- await sendSolanaWithdraw({
24731
- provider: wallet.provider,
24732
- fromAddress: wallet.address,
24733
- depositWalletAddress: depositWallet.address,
24734
- sourceTokenAddress,
24735
- amountBaseUnit,
24736
- publishableKey
24737
- });
24738
- }
24739
- } catch (walletErr) {
24740
- console.error("[Unifold] Browser wallet send failed:", walletErr, {
24741
- wallet: `${wallet.name} (${wallet.chainFamily})`,
24742
- sourceChainId,
24743
- amount: humanAmount,
24744
- amountBaseUnit,
24745
- depositWallet: depositWallet.address
24746
- });
24747
- throw walletErr;
24748
- }
24749
- } else if (onWithdraw) {
24750
- try {
24751
- await onWithdraw(txInfo);
24752
- } catch (callbackErr) {
24753
- console.error("[Unifold] onWithdraw callback failed:", callbackErr, {
24754
- sourceChainId,
24755
- amount: humanAmount,
24756
- amountBaseUnit,
24757
- depositWallet: depositWallet.address
24758
- });
24759
- throw callbackErr;
24760
- }
24761
- } else {
24762
- throw new Error("No withdrawal method available. Please connect a wallet.");
24483
+ try {
24484
+ await onWithdraw(txInfo);
24485
+ } catch (callbackErr) {
24486
+ console.error("[Unifold] onWithdraw callback failed:", callbackErr, {
24487
+ sourceChainId,
24488
+ amount: humanAmount,
24489
+ amountBaseUnit,
24490
+ depositWallet: depositWallet.address
24491
+ });
24492
+ throw callbackErr;
24763
24493
  }
24764
24494
  onWithdrawSubmitted?.(txInfo);
24765
24495
  } catch (err) {
@@ -25868,6 +25598,10 @@ function UnifoldProvider2({
25868
25598
  clientSecret: checkoutConfig.clientSecret,
25869
25599
  publishableKey,
25870
25600
  enableConnectWallet: config?.enableConnectWallet,
25601
+ defaultSourceChainType: checkoutConfig.defaultSourceChainType,
25602
+ defaultSourceChainId: checkoutConfig.defaultSourceChainId,
25603
+ defaultSourceTokenAddress: checkoutConfig.defaultSourceTokenAddress,
25604
+ defaultSourceSymbol: checkoutConfig.defaultSourceSymbol,
25871
25605
  theme: resolvedTheme,
25872
25606
  onCheckoutSuccess: handleCheckoutSuccess,
25873
25607
  onCheckoutError: handleCheckoutError
@@ -25918,6 +25652,7 @@ function UnifoldProvider2({
25918
25652
  enablePayWithExchange: config?.enablePayWithExchange,
25919
25653
  onDepositSuccess: handleDepositSuccess,
25920
25654
  onDepositError: handleDepositError,
25655
+ onEvent: depositConfig.onEvent,
25921
25656
  theme: resolvedTheme,
25922
25657
  initialScreen: depositConfig.initialScreen ?? config?.defaultInitialScreen,
25923
25658
  transferCryptoTitle: config?.transferCryptoTitle,
@@ -25974,11 +25709,13 @@ function useAllowedCountry2() {
25974
25709
  ActionType,
25975
25710
  Button,
25976
25711
  ConfirmingView,
25712
+ DepositEventType,
25977
25713
  ExecutionStatus,
25978
25714
  SOLANA_USDC_ADDRESS,
25979
25715
  UnifoldProvider,
25980
25716
  createDepositAddress,
25981
25717
  createOnrampSession,
25718
+ generatePrefixedKSUID,
25982
25719
  getApiBaseUrl,
25983
25720
  getChainName,
25984
25721
  getDefaultOnrampToken,
package/dist/index.mjs CHANGED
@@ -6370,6 +6370,9 @@ function getOnrampSessionStartUrl(request, publishableKey) {
6370
6370
  if (request.subdivision_code) {
6371
6371
  params.append("subdivision_code", request.subdivision_code);
6372
6372
  }
6373
+ if (request.external_id) {
6374
+ params.append("external_id", request.external_id);
6375
+ }
6373
6376
  return `${API_BASE_URL}/v1/public/onramps/sessions/start?${params.toString()}`;
6374
6377
  }
6375
6378
  async function getDefaultOnrampToken(params, publishableKey) {
@@ -6693,52 +6696,38 @@ async function getDepositQuote(request, publishableKey) {
6693
6696
  const json = await response.json();
6694
6697
  return json.data;
6695
6698
  }
6696
- async function buildHypercoreTransaction(request, publishableKey) {
6697
- const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6698
- validatePublishableKey(pk);
6699
- const response = await fetch(
6700
- `${API_BASE_URL}/v1/public/transactions/hypercore/build`,
6701
- {
6702
- method: "POST",
6703
- headers: {
6704
- accept: "application/json",
6705
- "x-publishable-key": pk,
6706
- "Content-Type": "application/json"
6707
- },
6708
- body: JSON.stringify(request)
6699
+ function generatePrefixedKSUID(prefix) {
6700
+ const BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
6701
+ const KSUID_EPOCH = 14e8;
6702
+ const timestampSeconds = Math.floor(Date.now() / 1e3) - KSUID_EPOCH;
6703
+ const payload = new Uint8Array(20);
6704
+ payload[0] = timestampSeconds >>> 24 & 255;
6705
+ payload[1] = timestampSeconds >>> 16 & 255;
6706
+ payload[2] = timestampSeconds >>> 8 & 255;
6707
+ payload[3] = timestampSeconds & 255;
6708
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
6709
+ crypto.getRandomValues(payload.subarray(4));
6710
+ } else {
6711
+ for (let i = 4; i < 20; i++) {
6712
+ payload[i] = Math.floor(Math.random() * 256);
6709
6713
  }
6710
- );
6711
- if (!response.ok) {
6712
- const error = await response.json().catch(() => ({ message: response.statusText }));
6713
- throw new Error(
6714
- `Failed to build HyperCore transaction: ${error.message || response.statusText}`
6715
- );
6716
6714
  }
6717
- return response.json();
6718
- }
6719
- async function sendHypercoreTransaction(request, publishableKey) {
6720
- const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6721
- validatePublishableKey(pk);
6722
- const response = await fetch(
6723
- `${API_BASE_URL}/v1/public/transactions/hypercore/send`,
6724
- {
6725
- method: "POST",
6726
- headers: {
6727
- accept: "application/json",
6728
- "x-publishable-key": pk,
6729
- "Content-Type": "application/json"
6730
- },
6731
- body: JSON.stringify(request)
6732
- }
6733
- );
6734
- if (!response.ok) {
6735
- const error = await response.json().catch(() => ({ message: response.statusText }));
6736
- throw new Error(
6737
- `Failed to send HyperCore transaction: ${error.message || response.statusText}`
6738
- );
6715
+ let value = 0n;
6716
+ for (const byte of payload) {
6717
+ value = value << 8n | BigInt(byte);
6739
6718
  }
6740
- return response.json();
6719
+ let encoded = "";
6720
+ while (value > 0n) {
6721
+ encoded = BASE62[Number(value % 62n)] + encoded;
6722
+ value = value / 62n;
6723
+ }
6724
+ encoded = encoded.padStart(27, "0");
6725
+ return `${prefix}_${encoded}`;
6741
6726
  }
6727
+ var DepositEventType = /* @__PURE__ */ ((DepositEventType2) => {
6728
+ DepositEventType2["ONRAMP_SESSION_CREATED"] = "onramp_session.created";
6729
+ return DepositEventType2;
6730
+ })(DepositEventType || {});
6742
6731
  function useUserIp() {
6743
6732
  const {
6744
6733
  data: userIpInfo,
@@ -13397,6 +13386,7 @@ function BuyWithCard({
13397
13386
  destinationTokenAddress,
13398
13387
  onDepositSuccess,
13399
13388
  onDepositError,
13389
+ onEvent,
13400
13390
  themeClass = "",
13401
13391
  wallets: externalWallets,
13402
13392
  assetCdnUrl,
@@ -13750,6 +13740,7 @@ function BuyWithCard({
13750
13740
  setQuotesError("Wallet address not available");
13751
13741
  return;
13752
13742
  }
13743
+ const externalId = generatePrefixedKSUID("orsext");
13753
13744
  const sessionRequest = {
13754
13745
  service_provider: selectedProvider.service_provider,
13755
13746
  country_code: selectedProvider.country_code.toUpperCase() || "US",
@@ -13758,7 +13749,8 @@ function BuyWithCard({
13758
13749
  destination_currency: selectedProvider.destination_currency,
13759
13750
  destination_network: selectedProvider.destination_network,
13760
13751
  wallet_address: wallet.address,
13761
- subdivision_code: userIpInfo?.state || void 0
13752
+ subdivision_code: userIpInfo?.state || void 0,
13753
+ external_id: externalId
13762
13754
  };
13763
13755
  const sessionStartUrl = getOnrampSessionStartUrl(
13764
13756
  sessionRequest,
@@ -13768,7 +13760,14 @@ function BuyWithCard({
13768
13760
  provider: selectedProvider,
13769
13761
  sourceCurrency: currency,
13770
13762
  sourceAmount: amount,
13771
- sessionUrl: sessionStartUrl
13763
+ sessionUrl: sessionStartUrl,
13764
+ externalId
13765
+ });
13766
+ onEvent?.({
13767
+ id: generatePrefixedKSUID("sevt"),
13768
+ type: DepositEventType.ONRAMP_SESSION_CREATED,
13769
+ created: Math.floor(Date.now() / 1e3),
13770
+ data: { object: { externalId } }
13772
13771
  });
13773
13772
  window.open(sessionStartUrl, "_blank");
13774
13773
  handleViewChange("onramp");
@@ -21239,10 +21238,11 @@ function BrowserWalletModal({
21239
21238
  const chainType = depositWallet.chain_type;
21240
21239
  const recipientAddress = depositWallet.address;
21241
21240
  const supportedChainType = chainType === "algorand" || chainType === "xrpl" ? "ethereum" : chainType;
21242
- const { executions: depositExecutions, isPolling } = useDepositPolling({
21241
+ const { executions: depositExecutions, isPolling, handleIveDeposited } = useDepositPolling({
21243
21242
  userId,
21244
21243
  publishableKey,
21245
21244
  clientSecret,
21245
+ depositWalletId: depositWallet.id,
21246
21246
  enabled: open && hasSignedTransaction,
21247
21247
  onDepositSuccess,
21248
21248
  onDepositError
@@ -21493,6 +21493,7 @@ function BrowserWalletModal({
21493
21493
  }
21494
21494
  setReceivedUsdAtSubmission(checkoutReceivedUsd ?? "0");
21495
21495
  setHasSignedTransaction(true);
21496
+ handleIveDeposited();
21496
21497
  setIsConfirming(false);
21497
21498
  setStep("confirming");
21498
21499
  onSuccess?.(txHash);
@@ -22529,6 +22530,7 @@ function DepositModal({
22529
22530
  hideDisplayDescription = false,
22530
22531
  onDepositSuccess,
22531
22532
  onDepositError,
22533
+ onEvent,
22532
22534
  theme = "dark",
22533
22535
  hideOverlay = false,
22534
22536
  initialScreen = "main",
@@ -23043,6 +23045,7 @@ function DepositModal({
23043
23045
  destinationTokenAddress,
23044
23046
  onDepositSuccess,
23045
23047
  onDepositError,
23048
+ onEvent,
23046
23049
  themeClass,
23047
23050
  wallets,
23048
23051
  assetCdnUrl: projectConfig?.asset_cdn_url,
@@ -23226,6 +23229,10 @@ function CheckoutModal({
23226
23229
  publishableKey,
23227
23230
  modalTitle,
23228
23231
  enableConnectWallet = false,
23232
+ defaultSourceChainType,
23233
+ defaultSourceChainId,
23234
+ defaultSourceTokenAddress,
23235
+ defaultSourceSymbol,
23229
23236
  theme = "dark",
23230
23237
  onCheckoutSuccess,
23231
23238
  onCheckoutError
@@ -23723,6 +23730,10 @@ function CheckoutModal({
23723
23730
  destinationChainType: paymentIntent.destination_chain_type,
23724
23731
  destinationChainId: paymentIntent.destination_chain_id,
23725
23732
  destinationTokenAddress: paymentIntent.destination_token_address,
23733
+ defaultSourceChainType,
23734
+ defaultSourceChainId,
23735
+ defaultSourceTokenAddress,
23736
+ defaultSourceSymbol,
23726
23737
  depositConfirmationMode: "auto_ui",
23727
23738
  wallets,
23728
23739
  onSourceTokenChange: setSelectedSource,
@@ -24224,249 +24235,10 @@ function useVerifyRecipientAddress(params) {
24224
24235
  refetchOnWindowFocus: false
24225
24236
  });
24226
24237
  }
24227
- async function sendEvmWithdraw(params) {
24228
- const {
24229
- provider,
24230
- fromAddress,
24231
- depositWalletAddress,
24232
- sourceTokenAddress,
24233
- sourceChainId,
24234
- amountBaseUnit
24235
- } = params;
24236
- const currentChainIdHex = await provider.request({
24237
- method: "eth_chainId",
24238
- params: []
24239
- });
24240
- const currentChainId = parseInt(currentChainIdHex, 16).toString();
24241
- if (currentChainId !== sourceChainId) {
24242
- const requiredHex = "0x" + parseInt(sourceChainId).toString(16);
24243
- try {
24244
- await provider.request({
24245
- method: "wallet_switchEthereumChain",
24246
- params: [{ chainId: requiredHex }]
24247
- });
24248
- const newHex = await provider.request({ method: "eth_chainId", params: [] });
24249
- if (parseInt(newHex, 16).toString() !== sourceChainId) {
24250
- throw new Error(`Failed to switch to chain ${sourceChainId}. Please switch manually.`);
24251
- }
24252
- } catch (err) {
24253
- if (err && typeof err === "object" && "code" in err) {
24254
- const e = err;
24255
- if (e.code === 4902) throw new Error(`Chain ${sourceChainId} is not configured in your wallet.`);
24256
- if (e.code === 4001) throw new Error("You must approve the network switch to withdraw.");
24257
- }
24258
- throw err;
24259
- }
24260
- }
24261
- const isNative = sourceTokenAddress === "native" || sourceTokenAddress === "0x0000000000000000000000000000000000000000" || sourceTokenAddress === "";
24262
- const amountBig = BigInt(amountBaseUnit);
24263
- const txParams = isNative ? { from: fromAddress, to: depositWalletAddress, value: "0x" + amountBig.toString(16) } : {
24264
- from: fromAddress,
24265
- to: sourceTokenAddress,
24266
- data: "0xa9059cbb" + depositWalletAddress.slice(2).padStart(64, "0") + amountBig.toString(16).padStart(64, "0")
24267
- };
24268
- let gasEstimate;
24269
- try {
24270
- const hex = await provider.request({ method: "eth_estimateGas", params: [txParams] });
24271
- gasEstimate = BigInt(hex);
24272
- } catch {
24273
- gasEstimate = isNative ? BigInt(21e3) : BigInt(65e3);
24274
- }
24275
- const gasPrice = BigInt(await provider.request({ method: "eth_gasPrice", params: [] }));
24276
- const gasWithBuffer = gasEstimate * BigInt(120) / BigInt(100);
24277
- const gasCost = gasWithBuffer * gasPrice;
24278
- const ethBalance = BigInt(
24279
- await provider.request({ method: "eth_getBalance", params: [fromAddress, "latest"] })
24280
- );
24281
- const totalRequired = isNative ? gasCost + amountBig : gasCost;
24282
- if (ethBalance < totalRequired) {
24283
- const gasFmt = (Number(gasCost) / 1e18).toFixed(6);
24284
- if (isNative) {
24285
- throw new Error(`Insufficient balance. Need ${(Number(totalRequired) / 1e18).toFixed(6)} ETH (amount + ~${gasFmt} gas).`);
24286
- }
24287
- throw new Error(`Insufficient ETH for gas. Need ~${gasFmt} ETH for fees.`);
24288
- }
24289
- const txHash = await provider.request({ method: "eth_sendTransaction", params: [txParams] });
24290
- return txHash;
24291
- }
24292
- async function sendSolanaWithdraw(params) {
24293
- const {
24294
- provider,
24295
- fromAddress,
24296
- depositWalletAddress,
24297
- sourceTokenAddress,
24298
- amountBaseUnit,
24299
- publishableKey
24300
- } = params;
24301
- if (!provider.publicKey) {
24302
- await provider.connect();
24303
- }
24304
- const buildResponse = await buildSolanaTransaction(
24305
- {
24306
- chain_id: "mainnet",
24307
- token_address: sourceTokenAddress === "" ? "native" : sourceTokenAddress,
24308
- source_address: fromAddress,
24309
- destination_address: depositWalletAddress,
24310
- amount: amountBaseUnit
24311
- },
24312
- publishableKey
24313
- );
24314
- const { VersionedTransaction } = await import(
24315
- /* @vite-ignore */
24316
- "@solana/web3.js"
24317
- );
24318
- const binaryString = atob(buildResponse.transaction);
24319
- const bytes = new Uint8Array(binaryString.length);
24320
- for (let i = 0; i < binaryString.length; i++) {
24321
- bytes[i] = binaryString.charCodeAt(i);
24322
- }
24323
- const transaction = VersionedTransaction.deserialize(bytes);
24324
- const signedTransaction = await provider.signTransaction(transaction);
24325
- const serialized = signedTransaction.serialize();
24326
- let binaryStr = "";
24327
- for (let i = 0; i < serialized.length; i++) {
24328
- binaryStr += String.fromCharCode(serialized[i]);
24329
- }
24330
- const sendResponse = await sendSolanaTransaction(
24331
- { chain_id: "mainnet", signed_transaction: btoa(binaryStr) },
24332
- publishableKey
24333
- );
24334
- return sendResponse.signature;
24335
- }
24336
24238
  var HYPERCORE_CHAIN_ID = "1337";
24337
- var HYPERCORE_SPOT_USDC_ADDRESS = "0x6d1e7cde53ba9467b783cb7c530ce054";
24338
24239
  function isHypercoreChain(chainId) {
24339
24240
  return chainId === HYPERCORE_CHAIN_ID;
24340
24241
  }
24341
- async function sendHypercoreWithdraw(params) {
24342
- const {
24343
- provider,
24344
- fromAddress,
24345
- depositWalletAddress,
24346
- sourceTokenAddress,
24347
- amount,
24348
- tokenSymbol,
24349
- publishableKey
24350
- } = params;
24351
- const isSpot = sourceTokenAddress.toLowerCase() === HYPERCORE_SPOT_USDC_ADDRESS;
24352
- const currentChainHex = await provider.request({
24353
- method: "eth_chainId",
24354
- params: []
24355
- });
24356
- const activeChainId = String(parseInt(currentChainHex, 16));
24357
- const buildResult = await buildHypercoreTransaction(
24358
- {
24359
- action_type: isSpot ? "spot_send" : "usd_send",
24360
- signature_chain_type: "ethereum",
24361
- signature_chain_id: activeChainId,
24362
- recipient_address: depositWalletAddress,
24363
- token_address: sourceTokenAddress,
24364
- token_symbol: tokenSymbol || void 0,
24365
- amount
24366
- },
24367
- publishableKey
24368
- );
24369
- const signature = await provider.request({
24370
- method: "eth_signTypedData_v4",
24371
- params: [fromAddress, JSON.stringify(buildResult.typed_data)]
24372
- });
24373
- await sendHypercoreTransaction(
24374
- {
24375
- action_payload: buildResult.action_payload,
24376
- signature,
24377
- nonce: buildResult.nonce
24378
- },
24379
- publishableKey
24380
- );
24381
- }
24382
- async function detectBrowserWallet(chainType, senderAddress) {
24383
- const win = typeof window !== "undefined" ? window : null;
24384
- if (!win || !senderAddress) return null;
24385
- if (getUserDisconnectedWallet()) return null;
24386
- const anyWin = win;
24387
- if (chainType === "solana") {
24388
- const solProviders = [];
24389
- if (win.phantom?.solana) solProviders.push({ provider: win.phantom.solana, name: "Phantom" });
24390
- if (anyWin.solflare) solProviders.push({ provider: anyWin.solflare, name: "Solflare" });
24391
- if (anyWin.backpack) solProviders.push({ provider: anyWin.backpack, name: "Backpack" });
24392
- if (anyWin.trustwallet?.solana) solProviders.push({ provider: anyWin.trustwallet.solana, name: "Trust Wallet" });
24393
- for (const { provider, name } of solProviders) {
24394
- if (!provider) continue;
24395
- try {
24396
- let addr;
24397
- if (provider.isConnected && provider.publicKey) {
24398
- addr = provider.publicKey.toString();
24399
- } else {
24400
- const resp = await provider.connect({ onlyIfTrusted: true });
24401
- if (resp?.publicKey) addr = resp.publicKey.toString();
24402
- }
24403
- if (addr && addr === senderAddress) {
24404
- return { chainFamily: "solana", provider, name, address: addr };
24405
- }
24406
- } catch {
24407
- }
24408
- }
24409
- }
24410
- if (chainType === "ethereum") {
24411
- const evmProviders = [];
24412
- const seen = /* @__PURE__ */ new Set();
24413
- const add = (p, name) => {
24414
- if (p && typeof p.request === "function" && !seen.has(p)) {
24415
- seen.add(p);
24416
- evmProviders.push({ provider: p, name });
24417
- }
24418
- };
24419
- if (!anyWin.__eip6963Providers) {
24420
- anyWin.__eip6963Providers = [];
24421
- }
24422
- const handleAnnouncement = (event) => {
24423
- const { detail } = event;
24424
- if (!detail?.info || !detail?.provider) return;
24425
- const exists = anyWin.__eip6963Providers.some((p) => p.info.uuid === detail.info.uuid);
24426
- if (!exists) anyWin.__eip6963Providers.push(detail);
24427
- };
24428
- win.addEventListener("eip6963:announceProvider", handleAnnouncement);
24429
- win.dispatchEvent(new Event("eip6963:requestProvider"));
24430
- win.removeEventListener("eip6963:announceProvider", handleAnnouncement);
24431
- for (const detail of anyWin.__eip6963Providers) {
24432
- const rdns = detail.info?.rdns || "";
24433
- let name = detail.info?.name || "Wallet";
24434
- if (rdns.includes("metamask")) name = "MetaMask";
24435
- else if (rdns.includes("phantom")) name = "Phantom";
24436
- else if (rdns.includes("coinbase")) name = "Coinbase";
24437
- else if (rdns.includes("rabby")) name = "Rabby";
24438
- else if (rdns.includes("rainbow")) name = "Rainbow";
24439
- else if (rdns.includes("okx")) name = "OKX Wallet";
24440
- else if (rdns.includes("trust")) name = "Trust Wallet";
24441
- add(detail.provider, name);
24442
- }
24443
- if (evmProviders.length === 0) {
24444
- add(anyWin.phantom?.ethereum, "Phantom");
24445
- add(anyWin.coinbaseWalletExtension, "Coinbase");
24446
- add(anyWin.trustwallet?.ethereum, "Trust Wallet");
24447
- add(anyWin.okxwallet, "OKX Wallet");
24448
- if (evmProviders.length === 0 && win.ethereum) {
24449
- const eth = win.ethereum;
24450
- let name = "Wallet";
24451
- if (eth.isMetaMask && !eth.isPhantom && !eth.isRabby) name = "MetaMask";
24452
- else if (eth.isRabby) name = "Rabby";
24453
- else if (eth.isRainbow) name = "Rainbow";
24454
- else if (eth.isCoinbaseWallet) name = "Coinbase";
24455
- add(eth, name);
24456
- }
24457
- }
24458
- for (const { provider, name } of evmProviders) {
24459
- try {
24460
- const accounts = await provider.request({ method: "eth_accounts" });
24461
- if (accounts?.length > 0 && accounts[0].toLowerCase() === senderAddress.toLowerCase()) {
24462
- return { chainFamily: "evm", provider, name, address: accounts[0] };
24463
- }
24464
- } catch {
24465
- }
24466
- }
24467
- }
24468
- return null;
24469
- }
24470
24242
  var t8 = i18n2.withdrawModal;
24471
24243
  var tCrypto = i18n2.transferCrypto;
24472
24244
  function formatProcessingTime2(seconds) {
@@ -24614,8 +24386,8 @@ function WithdrawForm({
24614
24386
  setAmount(fiat.toFixed(2));
24615
24387
  setInputUnit("fiat");
24616
24388
  } else {
24617
- const crypto = val / exchangeRate;
24618
- setAmount(crypto.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
24389
+ const crypto2 = val / exchangeRate;
24390
+ setAmount(crypto2.toFixed(sourceDecimals > 6 ? 6 : sourceDecimals));
24619
24391
  setInputUnit("crypto");
24620
24392
  }
24621
24393
  }, [amount, inputUnit, exchangeRate, sourceDecimals]);
@@ -24637,6 +24409,9 @@ function WithdrawForm({
24637
24409
  setIsSubmitting(true);
24638
24410
  setSubmitError(null);
24639
24411
  try {
24412
+ if (!onWithdraw) {
24413
+ throw new Error("No withdrawal method available. Please provide an onWithdraw handler.");
24414
+ }
24640
24415
  const depositWallet = await onDepositWalletCreation({
24641
24416
  destinationChainType: selectedChain.chain_type,
24642
24417
  destinationChainId: selectedChain.chain_id,
@@ -24694,63 +24469,16 @@ function WithdrawForm({
24694
24469
  withdrawIntentAddress: depositWallet.address,
24695
24470
  recipientAddress: trimmedAddress
24696
24471
  };
24697
- const wallet = await detectBrowserWallet(sourceChainType, senderAddress);
24698
- console.log("browser wallet", wallet);
24699
- if (wallet) {
24700
- try {
24701
- if (wallet.chainFamily === "evm" && isHypercoreChain(sourceChainId)) {
24702
- await sendHypercoreWithdraw({
24703
- provider: wallet.provider,
24704
- fromAddress: wallet.address,
24705
- depositWalletAddress: depositWallet.address,
24706
- sourceTokenAddress,
24707
- amount: humanAmount,
24708
- tokenSymbol,
24709
- publishableKey
24710
- });
24711
- } else if (wallet.chainFamily === "evm") {
24712
- await sendEvmWithdraw({
24713
- provider: wallet.provider,
24714
- fromAddress: wallet.address,
24715
- depositWalletAddress: depositWallet.address,
24716
- sourceTokenAddress,
24717
- sourceChainId,
24718
- amountBaseUnit
24719
- });
24720
- } else if (wallet.chainFamily === "solana") {
24721
- await sendSolanaWithdraw({
24722
- provider: wallet.provider,
24723
- fromAddress: wallet.address,
24724
- depositWalletAddress: depositWallet.address,
24725
- sourceTokenAddress,
24726
- amountBaseUnit,
24727
- publishableKey
24728
- });
24729
- }
24730
- } catch (walletErr) {
24731
- console.error("[Unifold] Browser wallet send failed:", walletErr, {
24732
- wallet: `${wallet.name} (${wallet.chainFamily})`,
24733
- sourceChainId,
24734
- amount: humanAmount,
24735
- amountBaseUnit,
24736
- depositWallet: depositWallet.address
24737
- });
24738
- throw walletErr;
24739
- }
24740
- } else if (onWithdraw) {
24741
- try {
24742
- await onWithdraw(txInfo);
24743
- } catch (callbackErr) {
24744
- console.error("[Unifold] onWithdraw callback failed:", callbackErr, {
24745
- sourceChainId,
24746
- amount: humanAmount,
24747
- amountBaseUnit,
24748
- depositWallet: depositWallet.address
24749
- });
24750
- throw callbackErr;
24751
- }
24752
- } else {
24753
- throw new Error("No withdrawal method available. Please connect a wallet.");
24472
+ try {
24473
+ await onWithdraw(txInfo);
24474
+ } catch (callbackErr) {
24475
+ console.error("[Unifold] onWithdraw callback failed:", callbackErr, {
24476
+ sourceChainId,
24477
+ amount: humanAmount,
24478
+ amountBaseUnit,
24479
+ depositWallet: depositWallet.address
24480
+ });
24481
+ throw callbackErr;
24754
24482
  }
24755
24483
  onWithdrawSubmitted?.(txInfo);
24756
24484
  } catch (err) {
@@ -25859,6 +25587,10 @@ function UnifoldProvider2({
25859
25587
  clientSecret: checkoutConfig.clientSecret,
25860
25588
  publishableKey,
25861
25589
  enableConnectWallet: config?.enableConnectWallet,
25590
+ defaultSourceChainType: checkoutConfig.defaultSourceChainType,
25591
+ defaultSourceChainId: checkoutConfig.defaultSourceChainId,
25592
+ defaultSourceTokenAddress: checkoutConfig.defaultSourceTokenAddress,
25593
+ defaultSourceSymbol: checkoutConfig.defaultSourceSymbol,
25862
25594
  theme: resolvedTheme,
25863
25595
  onCheckoutSuccess: handleCheckoutSuccess,
25864
25596
  onCheckoutError: handleCheckoutError
@@ -25909,6 +25641,7 @@ function UnifoldProvider2({
25909
25641
  enablePayWithExchange: config?.enablePayWithExchange,
25910
25642
  onDepositSuccess: handleDepositSuccess,
25911
25643
  onDepositError: handleDepositError,
25644
+ onEvent: depositConfig.onEvent,
25912
25645
  theme: resolvedTheme,
25913
25646
  initialScreen: depositConfig.initialScreen ?? config?.defaultInitialScreen,
25914
25647
  transferCryptoTitle: config?.transferCryptoTitle,
@@ -25964,11 +25697,13 @@ export {
25964
25697
  ActionType,
25965
25698
  Button,
25966
25699
  ConfirmingView,
25700
+ DepositEventType,
25967
25701
  ExecutionStatus,
25968
25702
  SOLANA_USDC_ADDRESS,
25969
25703
  UnifoldProvider2 as UnifoldProvider,
25970
25704
  createDepositAddress,
25971
25705
  createOnrampSession,
25706
+ generatePrefixedKSUID,
25972
25707
  getApiBaseUrl,
25973
25708
  getChainName,
25974
25709
  getDefaultOnrampToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifold/connect-react",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
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",
@@ -31,9 +31,9 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@tanstack/react-query": "^5.90.11",
34
- "@unifold/core": "0.1.45",
35
- "@unifold/react-provider": "0.1.45",
36
- "@unifold/ui-react": "0.1.45"
34
+ "@unifold/core": "0.1.47",
35
+ "@unifold/react-provider": "0.1.47",
36
+ "@unifold/ui-react": "0.1.47"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "^19.0.0",