@unifold/headless-react 0.1.75 → 0.1.76

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { UnifoldProvider, useUnifold as useUnifold8 } from "@unifold/react-provider";
2
+ import { UnifoldProvider, useUnifold as useUnifold10 } from "@unifold/react-provider";
3
3
 
4
4
  // src/use-unifold-client.ts
5
5
  import { useMemo } from "react";
@@ -158,15 +158,231 @@ function useDeposit(options) {
158
158
  };
159
159
  }
160
160
 
161
+ // src/use-onramp.ts
162
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo3, useRef as useRef2, useState as useState2, useSyncExternalStore as useSyncExternalStore2 } from "react";
163
+ import {
164
+ OnrampSession,
165
+ OnrampSessionEventType
166
+ } from "@unifold/core";
167
+ import { useUnifold as useUnifold3 } from "@unifold/react-provider";
168
+ var IDLE_SNAPSHOT2 = {
169
+ status: "idle",
170
+ countryCode: null,
171
+ addresses: [],
172
+ destinationToken: null,
173
+ quotes: [],
174
+ selectedQuote: null,
175
+ isQuoteAutoSelected: true,
176
+ canSelectProvider: false,
177
+ isRefreshingQuotes: false,
178
+ quotesUpdatedAt: null,
179
+ checkout: null,
180
+ executions: [],
181
+ latestExecution: null,
182
+ isCheckingDeposit: false,
183
+ error: null
184
+ };
185
+ function useOnramp(options) {
186
+ const { publishableKey } = useUnifold3();
187
+ const {
188
+ externalUserId,
189
+ destination,
190
+ countryCode,
191
+ subdivisionCode,
192
+ sourceAmount,
193
+ destinationAmount,
194
+ sourceCurrency = "usd",
195
+ paymentMethodType,
196
+ email,
197
+ quoteRefreshIntervalMs,
198
+ quoteDebounceMs = 500,
199
+ autoStart = true
200
+ } = options;
201
+ const callbacksRef = useRef2(options);
202
+ useEffect2(() => {
203
+ callbacksRef.current = options;
204
+ });
205
+ const autoStartRef = useRef2(autoStart);
206
+ autoStartRef.current = autoStart;
207
+ const quoteRequestRef = useRef2({
208
+ countryCode,
209
+ subdivisionCode,
210
+ sourceAmount,
211
+ destinationAmount,
212
+ sourceCurrency
213
+ });
214
+ quoteRequestRef.current = {
215
+ countryCode,
216
+ subdivisionCode,
217
+ sourceAmount,
218
+ destinationAmount,
219
+ sourceCurrency
220
+ };
221
+ const [session, setSession] = useState2(null);
222
+ const sessionKey = [
223
+ publishableKey,
224
+ externalUserId ?? "",
225
+ destination.chainType,
226
+ destination.chainId,
227
+ destination.tokenAddress,
228
+ destination.recipientAddress,
229
+ JSON.stringify(destination.contractCalls ?? null),
230
+ paymentMethodType ?? "",
231
+ email ?? "",
232
+ quoteRefreshIntervalMs ?? ""
233
+ ].join("|");
234
+ useEffect2(() => {
235
+ if (!publishableKey || !externalUserId) {
236
+ setSession(null);
237
+ return;
238
+ }
239
+ const nextSession = new OnrampSession({
240
+ publishableKey,
241
+ externalUserId,
242
+ destination,
243
+ quoteRequest: quoteRequestRef.current,
244
+ paymentMethodType,
245
+ email,
246
+ quoteRefreshIntervalMs
247
+ });
248
+ let previousStatus = nextSession.getSnapshot().status;
249
+ const offEvents = nextSession.on("*", (event) => {
250
+ const callbacks = callbacksRef.current;
251
+ callbacks.onEvent?.(event);
252
+ switch (event.type) {
253
+ case OnrampSessionEventType.ADDRESSES_CREATED:
254
+ callbacks.onAddressesReady?.(event.data.object.addresses);
255
+ break;
256
+ case OnrampSessionEventType.QUOTES_UPDATED:
257
+ callbacks.onQuotes?.(event.data.object.quotes, event.data.object.selectedQuote);
258
+ break;
259
+ case OnrampSessionEventType.CHECKOUT_CREATED:
260
+ callbacks.onCheckoutCreated?.({
261
+ externalId: event.data.object.externalId,
262
+ url: event.data.object.url,
263
+ serviceProvider: event.data.object.serviceProvider
264
+ });
265
+ break;
266
+ case OnrampSessionEventType.EXECUTION_DETECTED:
267
+ callbacks.onExecutionDetected?.(event.data.object);
268
+ break;
269
+ case OnrampSessionEventType.EXECUTION_UPDATED:
270
+ callbacks.onExecutionUpdated?.(event.data.object);
271
+ break;
272
+ case OnrampSessionEventType.EXECUTION_SUCCEEDED:
273
+ callbacks.onSuccess?.(event.data.object);
274
+ break;
275
+ case OnrampSessionEventType.EXECUTION_FAILED:
276
+ callbacks.onError?.({
277
+ code: "DEPOSIT_FAILED",
278
+ message: "Deposit failed",
279
+ fatal: false,
280
+ cause: event.data.object
281
+ });
282
+ break;
283
+ case OnrampSessionEventType.SESSION_ERRORED:
284
+ callbacks.onError?.({
285
+ code: event.data.object.code,
286
+ message: event.data.object.message,
287
+ fatal: event.data.object.fatal
288
+ });
289
+ break;
290
+ }
291
+ });
292
+ const offStatus = nextSession.subscribe(() => {
293
+ const status = nextSession.getSnapshot().status;
294
+ if (status !== previousStatus) {
295
+ previousStatus = status;
296
+ callbacksRef.current.onStatusChange?.(status);
297
+ }
298
+ });
299
+ setSession(nextSession);
300
+ if (autoStartRef.current) {
301
+ void nextSession.start();
302
+ }
303
+ return () => {
304
+ offEvents();
305
+ offStatus();
306
+ nextSession.destroy();
307
+ };
308
+ }, [sessionKey]);
309
+ const quoteInputKey = [
310
+ countryCode ?? "",
311
+ subdivisionCode ?? "",
312
+ sourceAmount ?? "",
313
+ destinationAmount ?? "",
314
+ sourceCurrency
315
+ ].join("|");
316
+ useEffect2(() => {
317
+ if (!session) return;
318
+ const timer = setTimeout(() => {
319
+ session.updateQuoteRequest({
320
+ // undefined keeps the session's value (and IP detection for geo).
321
+ countryCode,
322
+ subdivisionCode,
323
+ sourceAmount,
324
+ destinationAmount,
325
+ sourceCurrency
326
+ });
327
+ }, quoteDebounceMs);
328
+ return () => clearTimeout(timer);
329
+ }, [session, quoteInputKey, quoteDebounceMs]);
330
+ const subscribe = useCallback2(
331
+ (listener) => session ? session.subscribe(listener) : () => {
332
+ },
333
+ [session]
334
+ );
335
+ const getSnapshot = useCallback2(
336
+ () => session ? session.getSnapshot() : IDLE_SNAPSHOT2,
337
+ [session]
338
+ );
339
+ const snapshot = useSyncExternalStore2(subscribe, getSnapshot, () => IDLE_SNAPSHOT2);
340
+ const controls = useMemo3(
341
+ () => ({
342
+ start: () => session?.start() ?? Promise.resolve(),
343
+ refreshQuotes: () => session?.refreshQuotes() ?? Promise.resolve(),
344
+ updateQuoteRequest: (patch) => session?.updateQuoteRequest(patch),
345
+ selectQuote: (serviceProvider) => session?.selectQuote(serviceProvider) ?? null,
346
+ createCheckout: (checkoutOptions) => session ? session.createCheckout(checkoutOptions) : null,
347
+ stop: () => session?.stop(),
348
+ restart: async () => {
349
+ if (!session) return;
350
+ session.stop();
351
+ await session.start();
352
+ }
353
+ }),
354
+ [session]
355
+ );
356
+ return {
357
+ status: snapshot.status,
358
+ countryCode: snapshot.countryCode,
359
+ addresses: snapshot.addresses,
360
+ destinationToken: snapshot.destinationToken,
361
+ quotes: snapshot.quotes,
362
+ selectedQuote: snapshot.selectedQuote,
363
+ isQuoteAutoSelected: snapshot.isQuoteAutoSelected,
364
+ canSelectProvider: snapshot.canSelectProvider,
365
+ isRefreshingQuotes: snapshot.isRefreshingQuotes,
366
+ quotesUpdatedAt: snapshot.quotesUpdatedAt,
367
+ checkout: snapshot.checkout,
368
+ executions: snapshot.executions,
369
+ latestExecution: snapshot.latestExecution,
370
+ isCheckingDeposit: snapshot.isCheckingDeposit,
371
+ error: snapshot.error,
372
+ ...controls,
373
+ session
374
+ };
375
+ }
376
+
161
377
  // src/use-deposit-addresses.ts
162
378
  import { useQuery } from "@tanstack/react-query";
163
379
  import {
164
380
  createDepositAddress,
165
381
  mapWalletToDepositAddress
166
382
  } from "@unifold/core";
167
- import { useUnifold as useUnifold3 } from "@unifold/react-provider";
383
+ import { useUnifold as useUnifold4 } from "@unifold/react-provider";
168
384
  function useDepositAddresses(options) {
169
- const { publishableKey } = useUnifold3();
385
+ const { publishableKey } = useUnifold4();
170
386
  const { externalUserId, destination, actionType, enabled = true } = options;
171
387
  return useQuery({
172
388
  // Key parity with @unifold/ui-react's useDepositAddress — do not reorder.
@@ -213,9 +429,9 @@ import { useQuery as useQuery2 } from "@tanstack/react-query";
213
429
  import {
214
430
  getSupportedDepositTokens
215
431
  } from "@unifold/core";
216
- import { useUnifold as useUnifold4 } from "@unifold/react-provider";
432
+ import { useUnifold as useUnifold5 } from "@unifold/react-provider";
217
433
  function useSupportedDepositTokens(options = {}) {
218
- const { publishableKey } = useUnifold4();
434
+ const { publishableKey } = useUnifold5();
219
435
  const { destination, productType, enabled = true } = options;
220
436
  const completeDestination = destination?.chainType && destination?.chainId && destination?.tokenAddress ? destination : void 0;
221
437
  const apiOptions = completeDestination || productType ? {
@@ -254,9 +470,9 @@ import {
254
470
  mapDirectExecution,
255
471
  ActionType
256
472
  } from "@unifold/core";
257
- import { useUnifold as useUnifold5 } from "@unifold/react-provider";
473
+ import { useUnifold as useUnifold6 } from "@unifold/react-provider";
258
474
  function useExecutions(options) {
259
- const { publishableKey } = useUnifold5();
475
+ const { publishableKey } = useUnifold6();
260
476
  const {
261
477
  externalUserId,
262
478
  refetchInterval = false,
@@ -279,9 +495,9 @@ function useExecutions(options) {
279
495
  // src/use-allowed-country.ts
280
496
  import { useQuery as useQuery4, keepPreviousData } from "@tanstack/react-query";
281
497
  import { getIpAddress, getProjectConfig } from "@unifold/core";
282
- import { useUnifold as useUnifold6 } from "@unifold/react-provider";
498
+ import { useUnifold as useUnifold7 } from "@unifold/react-provider";
283
499
  function useAllowedCountry() {
284
- const { publishableKey } = useUnifold6();
500
+ const { publishableKey } = useUnifold7();
285
501
  const ipQuery = useQuery4({
286
502
  // Key parity with @unifold/core's useUserIp.
287
503
  queryKey: ["unifold", "userIpInfo"],
@@ -339,9 +555,9 @@ import { useQuery as useQuery5 } from "@tanstack/react-query";
339
555
  import {
340
556
  verifyRecipientAddress
341
557
  } from "@unifold/core";
342
- import { useUnifold as useUnifold7 } from "@unifold/react-provider";
558
+ import { useUnifold as useUnifold8 } from "@unifold/react-provider";
343
559
  function useAddressValidation(options) {
344
- const { publishableKey } = useUnifold7();
560
+ const { publishableKey } = useUnifold8();
345
561
  const { recipientAddress, destination, enabled = true } = options;
346
562
  const hasCompleteDestination = !!destination?.chainType && !!destination?.chainId && !!destination?.tokenAddress;
347
563
  const shouldValidate = enabled && !!publishableKey && !!recipientAddress && hasCompleteDestination;
@@ -387,6 +603,24 @@ function useAddressValidation(options) {
387
603
  };
388
604
  }
389
605
 
606
+ // src/use-fiat-currencies.ts
607
+ import { useQuery as useQuery6 } from "@tanstack/react-query";
608
+ import { getFiatCurrencies } from "@unifold/core";
609
+ import { useUnifold as useUnifold9 } from "@unifold/react-provider";
610
+ function useFiatCurrencies(options = {}) {
611
+ const { publishableKey } = useUnifold9();
612
+ const { enabled = true } = options;
613
+ return useQuery6({
614
+ queryKey: ["unifold", "fiatCurrencies", publishableKey],
615
+ queryFn: () => getFiatCurrencies(publishableKey),
616
+ enabled: enabled && !!publishableKey,
617
+ staleTime: 1e3 * 60 * 5,
618
+ gcTime: 1e3 * 60 * 30,
619
+ refetchOnMount: false,
620
+ refetchOnWindowFocus: false
621
+ });
622
+ }
623
+
390
624
  // src/index.ts
391
625
  import {
392
626
  createUnifoldClient as createUnifoldClient2,
@@ -394,7 +628,10 @@ import {
394
628
  DepositSession as DepositSession2,
395
629
  DepositSessionEventType as DepositSessionEventType2,
396
630
  DepositSessionWaitError,
397
- ExecutionStatus as ExecutionStatus2,
631
+ OnrampSession as OnrampSession2,
632
+ OnrampSessionEventType as OnrampSessionEventType2,
633
+ OnrampSessionWaitError,
634
+ ExecutionStatus as ExecutionStatus3,
398
635
  ActionType as ActionType2
399
636
  } from "@unifold/core";
400
637
  export {
@@ -402,7 +639,10 @@ export {
402
639
  DepositSession2 as DepositSession,
403
640
  DepositSessionEventType2 as DepositSessionEventType,
404
641
  DepositSessionWaitError,
405
- ExecutionStatus2 as ExecutionStatus,
642
+ ExecutionStatus3 as ExecutionStatus,
643
+ OnrampSession2 as OnrampSession,
644
+ OnrampSessionEventType2 as OnrampSessionEventType,
645
+ OnrampSessionWaitError,
406
646
  UnifoldClient,
407
647
  UnifoldProvider,
408
648
  createUnifoldClient2 as createUnifoldClient,
@@ -411,7 +651,9 @@ export {
411
651
  useDeposit,
412
652
  useDepositAddresses,
413
653
  useExecutions,
654
+ useFiatCurrencies,
655
+ useOnramp,
414
656
  useSupportedDepositTokens,
415
- useUnifold8 as useUnifold,
657
+ useUnifold10 as useUnifold,
416
658
  useUnifoldClient
417
659
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unifold/headless-react",
3
- "version": "0.1.75",
3
+ "version": "0.1.76",
4
4
  "description": "Unifold Headless React SDK - hooks-only (no UI) crypto deposit flows",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -21,8 +21,8 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@tanstack/react-query": "^5.90.11",
24
- "@unifold/core": "0.1.75",
25
- "@unifold/react-provider": "0.1.75"
24
+ "@unifold/core": "0.1.76",
25
+ "@unifold/react-provider": "0.1.76"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/react": "^19.0.0",