@unifold/headless-react 0.1.75 → 0.1.77
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/README.md +156 -14
- package/dist/index.d.mts +183 -3
- package/dist/index.d.ts +183 -3
- package/dist/index.js +276 -35
- package/dist/index.mjs +256 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -117,8 +117,8 @@ stream (they only listen — neither starts nor stops the session):
|
|
|
117
117
|
|
|
118
118
|
```ts
|
|
119
119
|
// Generic primitive over the lifecycle state machine:
|
|
120
|
-
await session.waitForStatus('processing');
|
|
121
|
-
await session.waitForStatus('ready');
|
|
120
|
+
await session.waitForStatus('processing'); // live activity detected
|
|
121
|
+
await session.waitForStatus('ready'); // addresses ready / all settled
|
|
122
122
|
|
|
123
123
|
// The 90% case — mirrors beginDeposit()'s promise contract
|
|
124
124
|
// (resolve on success, reject on failure):
|
|
@@ -130,7 +130,7 @@ try {
|
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
Both accept `{ signal }` — an `AbortSignal` cancels the
|
|
133
|
+
Both accept `{ signal }` — an `AbortSignal` cancels the _wait_, never the
|
|
134
134
|
session (a deposit isn't cancelable: once the user sends funds, they arrive
|
|
135
135
|
whether or not anyone is awaiting). For a deadline, compose the platform
|
|
136
136
|
primitive — `waitForSuccess({ signal: AbortSignal.timeout(60_000) })` — and
|
|
@@ -147,7 +147,7 @@ its own `direct_execution.succeeded` event.
|
|
|
147
147
|
|
|
148
148
|
**Live outcomes only.** The 60s lookback window exists to catch deposits sent
|
|
149
149
|
moments before the session started — it only admits executions still
|
|
150
|
-
|
|
150
|
+
_in-flight_ at first sight (their settlement then fires live). An execution
|
|
151
151
|
that already settled before the session began is history and never re-fires
|
|
152
152
|
success/failure events, so reopening a deposit screen right after a success
|
|
153
153
|
cannot double-credit. Render history with `useExecutions` instead. To credit each deposit, subscribe
|
|
@@ -159,9 +159,129 @@ session.on(DepositSessionEventType.EXECUTION_SUCCEEDED, ({ data }) => {
|
|
|
159
159
|
});
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
+
## Buy with card — a custom fiat onramp screen
|
|
163
|
+
|
|
164
|
+
`useOnramp` is the no-UI equivalent of
|
|
165
|
+
`beginDeposit({ initialScreen: 'card' })`: provider quotes + a hosted
|
|
166
|
+
checkout URL + the same settlement watching as a transfer. You render the
|
|
167
|
+
amount input, the provider list, and the progress UI.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { useOnramp, useFiatCurrencies } from '@unifold/headless-react';
|
|
171
|
+
|
|
172
|
+
function BuyUSDC({ externalUserId }: Props) {
|
|
173
|
+
const [amount, setAmount] = useState('100');
|
|
174
|
+
const { data: fiat } = useFiatCurrencies(); // min/max limits, suggested amounts
|
|
175
|
+
|
|
176
|
+
const buy = useOnramp({
|
|
177
|
+
externalUserId,
|
|
178
|
+
// Payer country is auto-detected from IP; pass `countryCode` to override.
|
|
179
|
+
sourceAmount: amount, // live: changes refetch quotes (debounced 500ms)
|
|
180
|
+
destination: {
|
|
181
|
+
chainType: 'ethereum',
|
|
182
|
+
chainId: '8453',
|
|
183
|
+
tokenAddress: USDC_BASE,
|
|
184
|
+
recipientAddress: userTreasuryAddress,
|
|
185
|
+
},
|
|
186
|
+
onSuccess: (execution) => toast.success(`Received $${execution.destinationAmountUsd}`),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div>
|
|
191
|
+
<AmountInput value={amount} onChange={setAmount} />
|
|
192
|
+
{/* canSelectProvider is false under fiat-onramp smart routing (the
|
|
193
|
+
backend routes to a single provider) or when only one provider
|
|
194
|
+
quotes — hide the picker then, like the modal does. */}
|
|
195
|
+
{buy.canSelectProvider && (
|
|
196
|
+
<ProviderList
|
|
197
|
+
quotes={buy.quotes}
|
|
198
|
+
selected={buy.selectedQuote}
|
|
199
|
+
onSelect={(q) => buy.selectQuote(q.serviceProvider)}
|
|
200
|
+
/>
|
|
201
|
+
)}
|
|
202
|
+
<button
|
|
203
|
+
disabled={buy.status !== 'ready' || !buy.selectedQuote}
|
|
204
|
+
onClick={() => {
|
|
205
|
+
// Synchronous URL build — safe to open inside the click handler.
|
|
206
|
+
const checkout = buy.createCheckout();
|
|
207
|
+
if (checkout) window.open(checkout.url, '_blank');
|
|
208
|
+
}}
|
|
209
|
+
>
|
|
210
|
+
Continue
|
|
211
|
+
</button>
|
|
212
|
+
{buy.status === 'awaiting_payment' && <WaitingForProvider checkout={buy.checkout!} />}
|
|
213
|
+
{buy.status === 'processing' && <MyProgress execution={buy.latestExecution!} />}
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The lifecycle state machine:
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
idle → preparing → quoting → ready → awaiting_payment ⇄ processing
|
|
223
|
+
↘ error (fatal: addresses / no onramp route / invalid recipient)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
A full reference screen — amount modes, currency picker, provider list,
|
|
227
|
+
checkout redirect, settlement — lives in `apps/ui-demo/app/onramp/headless`
|
|
228
|
+
(`/demo/onramp/headless`), and `docs/recipes/deposits/headless-card-onramp.mdx`
|
|
229
|
+
walks through building the flow step by step (amount input and prefills,
|
|
230
|
+
quoting, checkout, polling, crediting, error handling).
|
|
231
|
+
|
|
232
|
+
Same outcome philosophy as `useDeposit`: **outcomes never appear on the
|
|
233
|
+
session status** — read them from `executions`/`latestExecution`, the
|
|
234
|
+
`direct_execution.succeeded`/`.failed` events, or `session.waitForSuccess()`.
|
|
235
|
+
Under the hood the settlement phase _is_ a `DepositSession` (method `'card'`)
|
|
236
|
+
composed over the same deposit addresses, so detection polling, the scan
|
|
237
|
+
nudge, and the lookback window behave identically across rails.
|
|
238
|
+
|
|
239
|
+
Worth knowing:
|
|
240
|
+
|
|
241
|
+
- **Quotes auto-refresh every 60s** until checkout (configure with
|
|
242
|
+
`quoteRefreshIntervalMs`, 0 disables); `quotesUpdatedAt` drives your own
|
|
243
|
+
countdown. `selectQuote()` is sticky across refreshes and falls back to the
|
|
244
|
+
backend's top quote if the provider stops quoting.
|
|
245
|
+
- **Provider choice has three modes.** Default: the backend's top quote
|
|
246
|
+
(`quotes[0]` — the order encodes provider priority) is auto-selected. Host
|
|
247
|
+
choice: render `quotes` and call `selectQuote()`, or pass a one-shot
|
|
248
|
+
`createCheckout({ serviceProvider })` without touching the sticky
|
|
249
|
+
selection. Backend choice: projects with **fiat-onramp smart routing**
|
|
250
|
+
enabled get exactly one routed quote — `canSelectProvider` is `false` and
|
|
251
|
+
there is nothing to pick.
|
|
252
|
+
- **Quote failures are non-fatal** (`error.code === 'QUOTES_FAILED'`) — quotes
|
|
253
|
+
are cleared rather than left stale, and refresh keeps trying.
|
|
254
|
+
- **Payer country is auto-detected** from the user's IP when `countryCode`
|
|
255
|
+
is omitted ('US' fallback if detection fails — modal parity); the
|
|
256
|
+
effective value is exposed as `countryCode` on the result. Pass
|
|
257
|
+
`countryCode` (live) to override with your own geo signal. This is the
|
|
258
|
+
one deliberate exception to the headless SDK's not-IP-aware stance:
|
|
259
|
+
for the onramp, country is a functional input of quoting, not a policy
|
|
260
|
+
gate. Changing it live re-resolves the onramp route as well as the quotes
|
|
261
|
+
(routing is geo-dependent), so `destinationToken` and `quotes` are briefly
|
|
262
|
+
empty while that settles.
|
|
263
|
+
- **Amount validation is host-side**: check `minimum_amount`/`maximum_amount`
|
|
264
|
+
from `useFiatCurrencies` before quoting; the session only requires a
|
|
265
|
+
parseable amount > 0.
|
|
266
|
+
- **Two amount modes — provide exactly one.** `sourceAmount` ("spend 100
|
|
267
|
+
USD", fees included; `quote.destinationAmount` is what you receive) or
|
|
268
|
+
`destinationAmount` ("receive exactly 100 USDC", provider fees added on
|
|
269
|
+
top; `quote.sourceAmount` is the fiat the user pays). Both are live inputs;
|
|
270
|
+
swapping which one you pass switches modes in place. In destination mode
|
|
271
|
+
only providers that support fixed-destination quoting appear in `quotes` —
|
|
272
|
+
the backend filters the rest — and the destination token has to be a
|
|
273
|
+
stablecoin (`destinationToken.isStablecoin`), because the amount prices the
|
|
274
|
+
provider-side currency. For anything else, quoting stops with a non-fatal
|
|
275
|
+
`DESTINATION_AMOUNT_UNSUPPORTED` error until you switch back to
|
|
276
|
+
`sourceAmount` (the modal shows an error screen for the same case).
|
|
277
|
+
- `onramp_session.created` and `direct_execution.*` events are byte-compatible
|
|
278
|
+
with the modal SDK's `onEvent`, so one handler can serve both surfaces.
|
|
279
|
+
- Other rails ride the same hook via `paymentMethodType`
|
|
280
|
+
(`'card' | 'apple_pay' | 'sepa' | 'us_bank_account'`; default `'card'`).
|
|
281
|
+
|
|
162
282
|
### Vanilla JS (no React)
|
|
163
283
|
|
|
164
|
-
The
|
|
284
|
+
The controllers are usable without React from `@unifold/core`:
|
|
165
285
|
|
|
166
286
|
```ts
|
|
167
287
|
import { createUnifoldClient } from '@unifold/core';
|
|
@@ -177,24 +297,46 @@ showSuccess(execution);
|
|
|
177
297
|
session.destroy();
|
|
178
298
|
```
|
|
179
299
|
|
|
300
|
+
The onramp follows the same shape:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
const card = unifold.createOnrampSession({
|
|
304
|
+
externalUserId,
|
|
305
|
+
destination,
|
|
306
|
+
quoteRequest: { sourceAmount: '100' }, // country auto-detected; override with countryCode
|
|
307
|
+
});
|
|
308
|
+
await card.start(); // addresses + onramp route + first quotes
|
|
309
|
+
const checkout = card.createCheckout(); // sync — build URL from the selected quote
|
|
310
|
+
openUrl(checkout.url);
|
|
311
|
+
const execution = await card.waitForSuccess();
|
|
312
|
+
card.destroy();
|
|
313
|
+
```
|
|
314
|
+
|
|
180
315
|
## Hooks
|
|
181
316
|
|
|
182
|
-
| Hook
|
|
183
|
-
|
|
|
184
|
-
| `useDeposit`
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
317
|
+
| Hook | Purpose |
|
|
318
|
+
| --------------------------- | ------------------------------------------------------------------------------------------- |
|
|
319
|
+
| `useDeposit` | Flagship flow hook: deposit addresses + execution detection + status state machine + events |
|
|
320
|
+
| `useOnramp` | Card onramp flow hook: quotes + provider checkout URL + settlement watching |
|
|
321
|
+
| `useDepositAddresses` | Addresses without a live session (cached, idempotent create) |
|
|
322
|
+
| `useSupportedDepositTokens` | Source token/chain list for custom pickers |
|
|
323
|
+
| `useFiatCurrencies` | Fiat currencies + amount limits/suggestions for card onramp screens |
|
|
324
|
+
| `useExecutions` | Deposit history (tracker screens) |
|
|
325
|
+
| `useAllowedCountry` | Geo gate the modal uses; decide what to render when blocked |
|
|
326
|
+
| `useAddressValidation` | Inline recipient validation (e.g. Algorand opt-in) |
|
|
327
|
+
| `useUnifoldClient` | Escape hatch to the configured vanilla client |
|
|
191
328
|
|
|
192
329
|
## Events
|
|
193
330
|
|
|
194
331
|
`resource.action` names with webhook-mirroring envelopes; `direct_execution.succeeded`
|
|
195
332
|
is byte-compatible with the modal SDK's `onEvent`:
|
|
196
333
|
|
|
334
|
+
Envelope fields mirror the webhook payload, so `created` is a Unix timestamp in
|
|
335
|
+
**seconds** (multiply by 1000 before handing it to `Date`).
|
|
336
|
+
|
|
197
337
|
- `deposit_session.started` / `.addresses_created` / `.confirmation_started` / `.stopped` / `.errored`
|
|
338
|
+
- `onramp_session.started` / `.addresses_created` / `.quotes_updated` / `.created` / `.stopped` / `.errored`
|
|
339
|
+
(`onramp_session.created` = checkout built — same name and `externalId` payload as the modal)
|
|
198
340
|
- `direct_execution.detected` / `.updated` / `.succeeded` / `.failed`
|
|
199
341
|
|
|
200
342
|
When mixing the modal and headless surfaces, dedupe on `execution.id` (envelope
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { UnifoldConfig, UnifoldProvider, UnifoldProviderProps, useUnifold } from '@unifold/react-provider';
|
|
2
|
-
import { UnifoldClient, ChainType, EvmContractCall, DepositMethod, DepositSessionEvent, DepositAddress, DirectExecution, ExecutionStatus, DepositSessionError, DepositSessionStatus, DepositSession, ActionType, ProductType, SupportedToken, AddressValidationFailureCode } from '@unifold/core';
|
|
3
|
-
export { ActionType, ChainType, DepositAddress, DepositMethod, DepositSession, DepositSessionDestination, DepositSessionError, DepositSessionErrorCode, DepositSessionEvent, DepositSessionEventMap, DepositSessionEventType, DepositSessionParams, DepositSessionSnapshot, DepositSessionStatus, DepositSessionWaitError, DepositSessionWaitErrorCode, DepositSessionWaitOptions, DirectExecution, EvmContractCall, ExecutionStatus, SupportedChain, SupportedToken, UnifoldClient, UnifoldClientOptions, createUnifoldClient } from '@unifold/core';
|
|
2
|
+
import { UnifoldClient, ChainType, EvmContractCall, DepositMethod, DepositSessionEvent, DepositAddress, DirectExecution, ExecutionStatus, DepositSessionError, DepositSessionStatus, DepositSession, OnrampSessionPaymentMethodType, OnrampSessionEvent, OnrampProviderQuote, OnrampSessionError, OnrampSessionStatus, OnrampDestinationToken, OnrampCheckout, OnrampQuoteRequest, OnrampSession, ActionType, ProductType, SupportedToken, AddressValidationFailureCode, FiatCurrenciesResponse } from '@unifold/core';
|
|
3
|
+
export { ActionType, ChainType, DepositAddress, DepositMethod, DepositSession, DepositSessionDestination, DepositSessionError, DepositSessionErrorCode, DepositSessionEvent, DepositSessionEventMap, DepositSessionEventType, DepositSessionParams, DepositSessionSnapshot, DepositSessionStatus, DepositSessionWaitError, DepositSessionWaitErrorCode, DepositSessionWaitOptions, DirectExecution, EvmContractCall, ExecutionStatus, FiatCurrency, OnrampCheckout, OnrampDestinationToken, OnrampProviderQuote, OnrampQuoteRequest, OnrampSession, OnrampSessionError, OnrampSessionErrorCode, OnrampSessionEvent, OnrampSessionEventMap, OnrampSessionEventType, OnrampSessionParams, OnrampSessionPaymentMethodType, OnrampSessionSnapshot, OnrampSessionStatus, OnrampSessionWaitError, OnrampSessionWaitErrorCode, OnrampSessionWaitOptions, SupportedChain, SupportedToken, UnifoldClient, UnifoldClientOptions, createUnifoldClient } from '@unifold/core';
|
|
4
4
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -98,6 +98,170 @@ interface UseDepositResult {
|
|
|
98
98
|
*/
|
|
99
99
|
declare function useDeposit(options: UseDepositOptions): UseDepositResult;
|
|
100
100
|
|
|
101
|
+
interface UseOnrampOptions {
|
|
102
|
+
/** Host platform's stable user identifier (maps to external_user_id). */
|
|
103
|
+
externalUserId: string | undefined;
|
|
104
|
+
/** Destination — what the purchase converts into and where it lands. */
|
|
105
|
+
destination: UseDepositDestination;
|
|
106
|
+
/**
|
|
107
|
+
* ISO 3166-1 alpha-2 country of the payer. OPTIONAL — when omitted, the
|
|
108
|
+
* session auto-detects it from the user's IP during start ('US' fallback,
|
|
109
|
+
* modal parity). Provide it only to override detection with your own geo
|
|
110
|
+
* signal; live like the other quote inputs. The effective value is
|
|
111
|
+
* exposed as `countryCode` on the result.
|
|
112
|
+
*/
|
|
113
|
+
countryCode?: string;
|
|
114
|
+
/** ISO 3166-2 subdivision (e.g. 'US-NY') when relevant for provider routing. */
|
|
115
|
+
subdivisionCode?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Fiat amount to spend (fees included), as a decimal string. Live: changes
|
|
118
|
+
* refetch quotes (debounced). Provide exactly one of `sourceAmount` /
|
|
119
|
+
* `destinationAmount` — the amount mode.
|
|
120
|
+
*/
|
|
121
|
+
sourceAmount?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Fixed crypto (destination) amount to receive, with provider fees added
|
|
124
|
+
* on top — "receive 100 USDC". Live like `sourceAmount`, and mutually
|
|
125
|
+
* exclusive with it. Only providers that support fixed-destination quoting
|
|
126
|
+
* appear in `quotes`; each quote's `sourceAmount` is the fiat the user
|
|
127
|
+
* pays.
|
|
128
|
+
*
|
|
129
|
+
* Requires a stablecoin destination (`destinationToken.isStablecoin`) —
|
|
130
|
+
* the amount prices the provider-side currency, so it only means "receive
|
|
131
|
+
* 100 of the requested token" for stablecoins. Otherwise quoting stops
|
|
132
|
+
* with a non-fatal `DESTINATION_AMOUNT_UNSUPPORTED` error.
|
|
133
|
+
*/
|
|
134
|
+
destinationAmount?: string;
|
|
135
|
+
/** ISO 4217 fiat currency code the user pays in. Live like `sourceAmount`. @default 'usd' */
|
|
136
|
+
sourceCurrency?: string;
|
|
137
|
+
/** Payment rail forwarded to the provider session. @default 'card' */
|
|
138
|
+
paymentMethodType?: OnrampSessionPaymentMethodType;
|
|
139
|
+
/** Prefill email forwarded to the provider checkout. */
|
|
140
|
+
email?: string;
|
|
141
|
+
/** Quote auto-refresh interval; 0 disables. @default 60000 */
|
|
142
|
+
quoteRefreshIntervalMs?: number;
|
|
143
|
+
/**
|
|
144
|
+
* Debounce applied when the live quote inputs (amount/currency/country)
|
|
145
|
+
* change between renders, so binding an input's value doesn't hit the
|
|
146
|
+
* quotes API per keystroke (parity with the modal's 500ms). @default 500
|
|
147
|
+
*/
|
|
148
|
+
quoteDebounceMs?: number;
|
|
149
|
+
/**
|
|
150
|
+
* When true (default), start() is called automatically once externalUserId
|
|
151
|
+
* and the provider's publishable key are present. Set false to gate on
|
|
152
|
+
* your own UI.
|
|
153
|
+
*/
|
|
154
|
+
autoStart?: boolean;
|
|
155
|
+
/** Every lifecycle event ('*' subscription) — analytics/debug fan-out. */
|
|
156
|
+
onEvent?: (event: OnrampSessionEvent) => void;
|
|
157
|
+
/** onramp_session.addresses_created */
|
|
158
|
+
onAddressesReady?: (addresses: DepositAddress[]) => void;
|
|
159
|
+
/** onramp_session.quotes_updated */
|
|
160
|
+
onQuotes?: (quotes: OnrampProviderQuote[], selectedQuote: OnrampProviderQuote | null) => void;
|
|
161
|
+
/** onramp_session.created (checkout built; host opens `url`) */
|
|
162
|
+
onCheckoutCreated?: (checkout: {
|
|
163
|
+
externalId: string;
|
|
164
|
+
url: string;
|
|
165
|
+
serviceProvider: string;
|
|
166
|
+
}) => void;
|
|
167
|
+
/** direct_execution.detected */
|
|
168
|
+
onExecutionDetected?: (execution: DirectExecution) => void;
|
|
169
|
+
/** direct_execution.updated */
|
|
170
|
+
onExecutionUpdated?: (execution: DirectExecution & {
|
|
171
|
+
previousStatus: ExecutionStatus | null;
|
|
172
|
+
}) => void;
|
|
173
|
+
/** direct_execution.succeeded */
|
|
174
|
+
onSuccess?: (execution: DirectExecution) => void;
|
|
175
|
+
/** direct_execution.failed + onramp_session.errored */
|
|
176
|
+
onError?: (error: OnrampSessionError) => void;
|
|
177
|
+
/** Fired whenever snapshot.status changes. */
|
|
178
|
+
onStatusChange?: (status: OnrampSessionStatus) => void;
|
|
179
|
+
}
|
|
180
|
+
interface UseOnrampResult {
|
|
181
|
+
status: OnrampSessionStatus;
|
|
182
|
+
/**
|
|
183
|
+
* Effective payer country driving quotes (host override or IP-detected;
|
|
184
|
+
* null until known).
|
|
185
|
+
*/
|
|
186
|
+
countryCode: string | null;
|
|
187
|
+
/** Deposit addresses backing the purchase; empty until created. */
|
|
188
|
+
addresses: DepositAddress[];
|
|
189
|
+
/**
|
|
190
|
+
* Provider-side network/currency the fiat converts on; null until resolved,
|
|
191
|
+
* and briefly again while a `countryCode` change re-resolves the route.
|
|
192
|
+
*/
|
|
193
|
+
destinationToken: OnrampDestinationToken | null;
|
|
194
|
+
/** Latest provider quotes, backend priority order. */
|
|
195
|
+
quotes: OnrampProviderQuote[];
|
|
196
|
+
/** Quote used by createCheckout() — quotes[0] unless selectQuote() was called. */
|
|
197
|
+
selectedQuote: OnrampProviderQuote | null;
|
|
198
|
+
/** False once the host picked a provider via selectQuote(). */
|
|
199
|
+
isQuoteAutoSelected: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* True when there is more than one quote to pick between — gate your
|
|
202
|
+
* provider-picker UI on this. False under the project's fiat-onramp smart
|
|
203
|
+
* routing (backend collapses the list to one routed quote).
|
|
204
|
+
*/
|
|
205
|
+
canSelectProvider: boolean;
|
|
206
|
+
isRefreshingQuotes: boolean;
|
|
207
|
+
/** Epoch ms of the last successful quote fetch — drive your own countdown. */
|
|
208
|
+
quotesUpdatedAt: number | null;
|
|
209
|
+
/** The active provider checkout; null until createCheckout(). */
|
|
210
|
+
checkout: OnrampCheckout | null;
|
|
211
|
+
/** All executions observed since checkout, newest first. */
|
|
212
|
+
executions: DirectExecution[];
|
|
213
|
+
latestExecution: DirectExecution | null;
|
|
214
|
+
/** True while the backend is actively scanning for the provider's transfer. */
|
|
215
|
+
isCheckingDeposit: boolean;
|
|
216
|
+
error: OnrampSessionError | null;
|
|
217
|
+
/** Start the session (relevant when autoStart: false, or to retry after error). */
|
|
218
|
+
start: () => Promise<void>;
|
|
219
|
+
/** Refetch quotes with the current request. */
|
|
220
|
+
refreshQuotes: () => Promise<void>;
|
|
221
|
+
/** Imperative alternative to the live sourceAmount/sourceCurrency options. */
|
|
222
|
+
updateQuoteRequest: (patch: Partial<OnrampQuoteRequest>) => void;
|
|
223
|
+
/** Pick a provider quote (sticky across refreshes). Returns it, or null when absent. */
|
|
224
|
+
selectQuote: (serviceProvider: string) => OnrampProviderQuote | null;
|
|
225
|
+
/**
|
|
226
|
+
* Build the provider checkout and start watching for settlement. Uses the
|
|
227
|
+
* selected quote, or a one-shot `options.serviceProvider` choice.
|
|
228
|
+
* Synchronous — call it in your click handler and `window.open(result.url)`
|
|
229
|
+
* to stay popup-blocker safe. Returns null while the session doesn't exist
|
|
230
|
+
* yet; throws when called before quotes are ready (gate on `status`).
|
|
231
|
+
*/
|
|
232
|
+
createCheckout: (options?: {
|
|
233
|
+
serviceProvider?: string;
|
|
234
|
+
email?: string;
|
|
235
|
+
externalId?: string;
|
|
236
|
+
}) => OnrampCheckout | null;
|
|
237
|
+
/** Stop quote refresh and settlement watching. */
|
|
238
|
+
stop: () => void;
|
|
239
|
+
/** stop() + fresh run + start(). */
|
|
240
|
+
restart: () => Promise<void>;
|
|
241
|
+
/** Escape hatch to the underlying session (waiters, extra subscriptions). */
|
|
242
|
+
session: OnrampSession | null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Headless fiat-onramp hook (buy with card by default) — the no-UI
|
|
246
|
+
* equivalent of `beginDeposit({ initialScreen: 'card' })`.
|
|
247
|
+
*
|
|
248
|
+
* Wraps a core {@link OnrampSession}: creates it, subscribes via
|
|
249
|
+
* `useSyncExternalStore`, destroys it on unmount, and exposes the snapshot,
|
|
250
|
+
* imperative controls, and callback sugar. The host renders 100% of the UI —
|
|
251
|
+
* amount input, provider list, checkout button, settlement progress.
|
|
252
|
+
*
|
|
253
|
+
* The quote inputs (`sourceAmount` / `destinationAmount` — provide exactly
|
|
254
|
+
* one — plus `sourceCurrency`, `countryCode`, `subdivisionCode`) are LIVE:
|
|
255
|
+
* changing them updates the running session (debounced) instead of tearing
|
|
256
|
+
* it down, so you can bind them straight to form state. Swapping which
|
|
257
|
+
* amount is provided switches the amount mode in place. Changing the flow
|
|
258
|
+
* identity (user, destination, payment method) creates a fresh session.
|
|
259
|
+
*
|
|
260
|
+
* SSR-safe: the session is constructed in an effect; server renders see
|
|
261
|
+
* `status: 'idle'` and empty arrays.
|
|
262
|
+
*/
|
|
263
|
+
declare function useOnramp(options: UseOnrampOptions): UseOnrampResult;
|
|
264
|
+
|
|
101
265
|
interface UseDepositAddressesOptions {
|
|
102
266
|
/** Host platform's stable user identifier (maps to external_user_id). */
|
|
103
267
|
externalUserId: string | undefined;
|
|
@@ -215,4 +379,20 @@ interface AddressValidationResult {
|
|
|
215
379
|
*/
|
|
216
380
|
declare function useAddressValidation(options: UseAddressValidationOptions): AddressValidationResult;
|
|
217
381
|
|
|
218
|
-
|
|
382
|
+
interface UseFiatCurrenciesOptions {
|
|
383
|
+
/** Whether the query should execute. Defaults to true. */
|
|
384
|
+
enabled?: boolean;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Fiat currencies supported by the card onramp — for building custom
|
|
388
|
+
* currency pickers and amount screens next to {@link useOnramp}.
|
|
389
|
+
*
|
|
390
|
+
* Each {@link FiatCurrency} carries `minimum_amount` / `maximum_amount`
|
|
391
|
+
* (validate the user's amount host-side before quoting — the session only
|
|
392
|
+
* requires a parseable amount > 0), `default_amount`, `suggested_amounts`
|
|
393
|
+
* (quick-pick buttons), and `country_codes` (map your geo signal to a
|
|
394
|
+
* default currency). `data.preferred` lists currency codes to pin first.
|
|
395
|
+
*/
|
|
396
|
+
declare function useFiatCurrencies(options?: UseFiatCurrenciesOptions): _tanstack_react_query.UseQueryResult<FiatCurrenciesResponse, Error>;
|
|
397
|
+
|
|
398
|
+
export { type AddressValidationResult, type AllowedCountryResult, type UseAddressValidationOptions, type UseDepositAddressesOptions, type UseDepositDestination, type UseDepositOptions, type UseDepositResult, type UseExecutionsOptions, type UseFiatCurrenciesOptions, type UseOnrampOptions, type UseOnrampResult, type UseSupportedDepositTokensOptions, useAddressValidation, useAllowedCountry, useDeposit, useDepositAddresses, useExecutions, useFiatCurrencies, useOnramp, useSupportedDepositTokens, useUnifoldClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { UnifoldConfig, UnifoldProvider, UnifoldProviderProps, useUnifold } from '@unifold/react-provider';
|
|
2
|
-
import { UnifoldClient, ChainType, EvmContractCall, DepositMethod, DepositSessionEvent, DepositAddress, DirectExecution, ExecutionStatus, DepositSessionError, DepositSessionStatus, DepositSession, ActionType, ProductType, SupportedToken, AddressValidationFailureCode } from '@unifold/core';
|
|
3
|
-
export { ActionType, ChainType, DepositAddress, DepositMethod, DepositSession, DepositSessionDestination, DepositSessionError, DepositSessionErrorCode, DepositSessionEvent, DepositSessionEventMap, DepositSessionEventType, DepositSessionParams, DepositSessionSnapshot, DepositSessionStatus, DepositSessionWaitError, DepositSessionWaitErrorCode, DepositSessionWaitOptions, DirectExecution, EvmContractCall, ExecutionStatus, SupportedChain, SupportedToken, UnifoldClient, UnifoldClientOptions, createUnifoldClient } from '@unifold/core';
|
|
2
|
+
import { UnifoldClient, ChainType, EvmContractCall, DepositMethod, DepositSessionEvent, DepositAddress, DirectExecution, ExecutionStatus, DepositSessionError, DepositSessionStatus, DepositSession, OnrampSessionPaymentMethodType, OnrampSessionEvent, OnrampProviderQuote, OnrampSessionError, OnrampSessionStatus, OnrampDestinationToken, OnrampCheckout, OnrampQuoteRequest, OnrampSession, ActionType, ProductType, SupportedToken, AddressValidationFailureCode, FiatCurrenciesResponse } from '@unifold/core';
|
|
3
|
+
export { ActionType, ChainType, DepositAddress, DepositMethod, DepositSession, DepositSessionDestination, DepositSessionError, DepositSessionErrorCode, DepositSessionEvent, DepositSessionEventMap, DepositSessionEventType, DepositSessionParams, DepositSessionSnapshot, DepositSessionStatus, DepositSessionWaitError, DepositSessionWaitErrorCode, DepositSessionWaitOptions, DirectExecution, EvmContractCall, ExecutionStatus, FiatCurrency, OnrampCheckout, OnrampDestinationToken, OnrampProviderQuote, OnrampQuoteRequest, OnrampSession, OnrampSessionError, OnrampSessionErrorCode, OnrampSessionEvent, OnrampSessionEventMap, OnrampSessionEventType, OnrampSessionParams, OnrampSessionPaymentMethodType, OnrampSessionSnapshot, OnrampSessionStatus, OnrampSessionWaitError, OnrampSessionWaitErrorCode, OnrampSessionWaitOptions, SupportedChain, SupportedToken, UnifoldClient, UnifoldClientOptions, createUnifoldClient } from '@unifold/core';
|
|
4
4
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -98,6 +98,170 @@ interface UseDepositResult {
|
|
|
98
98
|
*/
|
|
99
99
|
declare function useDeposit(options: UseDepositOptions): UseDepositResult;
|
|
100
100
|
|
|
101
|
+
interface UseOnrampOptions {
|
|
102
|
+
/** Host platform's stable user identifier (maps to external_user_id). */
|
|
103
|
+
externalUserId: string | undefined;
|
|
104
|
+
/** Destination — what the purchase converts into and where it lands. */
|
|
105
|
+
destination: UseDepositDestination;
|
|
106
|
+
/**
|
|
107
|
+
* ISO 3166-1 alpha-2 country of the payer. OPTIONAL — when omitted, the
|
|
108
|
+
* session auto-detects it from the user's IP during start ('US' fallback,
|
|
109
|
+
* modal parity). Provide it only to override detection with your own geo
|
|
110
|
+
* signal; live like the other quote inputs. The effective value is
|
|
111
|
+
* exposed as `countryCode` on the result.
|
|
112
|
+
*/
|
|
113
|
+
countryCode?: string;
|
|
114
|
+
/** ISO 3166-2 subdivision (e.g. 'US-NY') when relevant for provider routing. */
|
|
115
|
+
subdivisionCode?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Fiat amount to spend (fees included), as a decimal string. Live: changes
|
|
118
|
+
* refetch quotes (debounced). Provide exactly one of `sourceAmount` /
|
|
119
|
+
* `destinationAmount` — the amount mode.
|
|
120
|
+
*/
|
|
121
|
+
sourceAmount?: string;
|
|
122
|
+
/**
|
|
123
|
+
* Fixed crypto (destination) amount to receive, with provider fees added
|
|
124
|
+
* on top — "receive 100 USDC". Live like `sourceAmount`, and mutually
|
|
125
|
+
* exclusive with it. Only providers that support fixed-destination quoting
|
|
126
|
+
* appear in `quotes`; each quote's `sourceAmount` is the fiat the user
|
|
127
|
+
* pays.
|
|
128
|
+
*
|
|
129
|
+
* Requires a stablecoin destination (`destinationToken.isStablecoin`) —
|
|
130
|
+
* the amount prices the provider-side currency, so it only means "receive
|
|
131
|
+
* 100 of the requested token" for stablecoins. Otherwise quoting stops
|
|
132
|
+
* with a non-fatal `DESTINATION_AMOUNT_UNSUPPORTED` error.
|
|
133
|
+
*/
|
|
134
|
+
destinationAmount?: string;
|
|
135
|
+
/** ISO 4217 fiat currency code the user pays in. Live like `sourceAmount`. @default 'usd' */
|
|
136
|
+
sourceCurrency?: string;
|
|
137
|
+
/** Payment rail forwarded to the provider session. @default 'card' */
|
|
138
|
+
paymentMethodType?: OnrampSessionPaymentMethodType;
|
|
139
|
+
/** Prefill email forwarded to the provider checkout. */
|
|
140
|
+
email?: string;
|
|
141
|
+
/** Quote auto-refresh interval; 0 disables. @default 60000 */
|
|
142
|
+
quoteRefreshIntervalMs?: number;
|
|
143
|
+
/**
|
|
144
|
+
* Debounce applied when the live quote inputs (amount/currency/country)
|
|
145
|
+
* change between renders, so binding an input's value doesn't hit the
|
|
146
|
+
* quotes API per keystroke (parity with the modal's 500ms). @default 500
|
|
147
|
+
*/
|
|
148
|
+
quoteDebounceMs?: number;
|
|
149
|
+
/**
|
|
150
|
+
* When true (default), start() is called automatically once externalUserId
|
|
151
|
+
* and the provider's publishable key are present. Set false to gate on
|
|
152
|
+
* your own UI.
|
|
153
|
+
*/
|
|
154
|
+
autoStart?: boolean;
|
|
155
|
+
/** Every lifecycle event ('*' subscription) — analytics/debug fan-out. */
|
|
156
|
+
onEvent?: (event: OnrampSessionEvent) => void;
|
|
157
|
+
/** onramp_session.addresses_created */
|
|
158
|
+
onAddressesReady?: (addresses: DepositAddress[]) => void;
|
|
159
|
+
/** onramp_session.quotes_updated */
|
|
160
|
+
onQuotes?: (quotes: OnrampProviderQuote[], selectedQuote: OnrampProviderQuote | null) => void;
|
|
161
|
+
/** onramp_session.created (checkout built; host opens `url`) */
|
|
162
|
+
onCheckoutCreated?: (checkout: {
|
|
163
|
+
externalId: string;
|
|
164
|
+
url: string;
|
|
165
|
+
serviceProvider: string;
|
|
166
|
+
}) => void;
|
|
167
|
+
/** direct_execution.detected */
|
|
168
|
+
onExecutionDetected?: (execution: DirectExecution) => void;
|
|
169
|
+
/** direct_execution.updated */
|
|
170
|
+
onExecutionUpdated?: (execution: DirectExecution & {
|
|
171
|
+
previousStatus: ExecutionStatus | null;
|
|
172
|
+
}) => void;
|
|
173
|
+
/** direct_execution.succeeded */
|
|
174
|
+
onSuccess?: (execution: DirectExecution) => void;
|
|
175
|
+
/** direct_execution.failed + onramp_session.errored */
|
|
176
|
+
onError?: (error: OnrampSessionError) => void;
|
|
177
|
+
/** Fired whenever snapshot.status changes. */
|
|
178
|
+
onStatusChange?: (status: OnrampSessionStatus) => void;
|
|
179
|
+
}
|
|
180
|
+
interface UseOnrampResult {
|
|
181
|
+
status: OnrampSessionStatus;
|
|
182
|
+
/**
|
|
183
|
+
* Effective payer country driving quotes (host override or IP-detected;
|
|
184
|
+
* null until known).
|
|
185
|
+
*/
|
|
186
|
+
countryCode: string | null;
|
|
187
|
+
/** Deposit addresses backing the purchase; empty until created. */
|
|
188
|
+
addresses: DepositAddress[];
|
|
189
|
+
/**
|
|
190
|
+
* Provider-side network/currency the fiat converts on; null until resolved,
|
|
191
|
+
* and briefly again while a `countryCode` change re-resolves the route.
|
|
192
|
+
*/
|
|
193
|
+
destinationToken: OnrampDestinationToken | null;
|
|
194
|
+
/** Latest provider quotes, backend priority order. */
|
|
195
|
+
quotes: OnrampProviderQuote[];
|
|
196
|
+
/** Quote used by createCheckout() — quotes[0] unless selectQuote() was called. */
|
|
197
|
+
selectedQuote: OnrampProviderQuote | null;
|
|
198
|
+
/** False once the host picked a provider via selectQuote(). */
|
|
199
|
+
isQuoteAutoSelected: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* True when there is more than one quote to pick between — gate your
|
|
202
|
+
* provider-picker UI on this. False under the project's fiat-onramp smart
|
|
203
|
+
* routing (backend collapses the list to one routed quote).
|
|
204
|
+
*/
|
|
205
|
+
canSelectProvider: boolean;
|
|
206
|
+
isRefreshingQuotes: boolean;
|
|
207
|
+
/** Epoch ms of the last successful quote fetch — drive your own countdown. */
|
|
208
|
+
quotesUpdatedAt: number | null;
|
|
209
|
+
/** The active provider checkout; null until createCheckout(). */
|
|
210
|
+
checkout: OnrampCheckout | null;
|
|
211
|
+
/** All executions observed since checkout, newest first. */
|
|
212
|
+
executions: DirectExecution[];
|
|
213
|
+
latestExecution: DirectExecution | null;
|
|
214
|
+
/** True while the backend is actively scanning for the provider's transfer. */
|
|
215
|
+
isCheckingDeposit: boolean;
|
|
216
|
+
error: OnrampSessionError | null;
|
|
217
|
+
/** Start the session (relevant when autoStart: false, or to retry after error). */
|
|
218
|
+
start: () => Promise<void>;
|
|
219
|
+
/** Refetch quotes with the current request. */
|
|
220
|
+
refreshQuotes: () => Promise<void>;
|
|
221
|
+
/** Imperative alternative to the live sourceAmount/sourceCurrency options. */
|
|
222
|
+
updateQuoteRequest: (patch: Partial<OnrampQuoteRequest>) => void;
|
|
223
|
+
/** Pick a provider quote (sticky across refreshes). Returns it, or null when absent. */
|
|
224
|
+
selectQuote: (serviceProvider: string) => OnrampProviderQuote | null;
|
|
225
|
+
/**
|
|
226
|
+
* Build the provider checkout and start watching for settlement. Uses the
|
|
227
|
+
* selected quote, or a one-shot `options.serviceProvider` choice.
|
|
228
|
+
* Synchronous — call it in your click handler and `window.open(result.url)`
|
|
229
|
+
* to stay popup-blocker safe. Returns null while the session doesn't exist
|
|
230
|
+
* yet; throws when called before quotes are ready (gate on `status`).
|
|
231
|
+
*/
|
|
232
|
+
createCheckout: (options?: {
|
|
233
|
+
serviceProvider?: string;
|
|
234
|
+
email?: string;
|
|
235
|
+
externalId?: string;
|
|
236
|
+
}) => OnrampCheckout | null;
|
|
237
|
+
/** Stop quote refresh and settlement watching. */
|
|
238
|
+
stop: () => void;
|
|
239
|
+
/** stop() + fresh run + start(). */
|
|
240
|
+
restart: () => Promise<void>;
|
|
241
|
+
/** Escape hatch to the underlying session (waiters, extra subscriptions). */
|
|
242
|
+
session: OnrampSession | null;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Headless fiat-onramp hook (buy with card by default) — the no-UI
|
|
246
|
+
* equivalent of `beginDeposit({ initialScreen: 'card' })`.
|
|
247
|
+
*
|
|
248
|
+
* Wraps a core {@link OnrampSession}: creates it, subscribes via
|
|
249
|
+
* `useSyncExternalStore`, destroys it on unmount, and exposes the snapshot,
|
|
250
|
+
* imperative controls, and callback sugar. The host renders 100% of the UI —
|
|
251
|
+
* amount input, provider list, checkout button, settlement progress.
|
|
252
|
+
*
|
|
253
|
+
* The quote inputs (`sourceAmount` / `destinationAmount` — provide exactly
|
|
254
|
+
* one — plus `sourceCurrency`, `countryCode`, `subdivisionCode`) are LIVE:
|
|
255
|
+
* changing them updates the running session (debounced) instead of tearing
|
|
256
|
+
* it down, so you can bind them straight to form state. Swapping which
|
|
257
|
+
* amount is provided switches the amount mode in place. Changing the flow
|
|
258
|
+
* identity (user, destination, payment method) creates a fresh session.
|
|
259
|
+
*
|
|
260
|
+
* SSR-safe: the session is constructed in an effect; server renders see
|
|
261
|
+
* `status: 'idle'` and empty arrays.
|
|
262
|
+
*/
|
|
263
|
+
declare function useOnramp(options: UseOnrampOptions): UseOnrampResult;
|
|
264
|
+
|
|
101
265
|
interface UseDepositAddressesOptions {
|
|
102
266
|
/** Host platform's stable user identifier (maps to external_user_id). */
|
|
103
267
|
externalUserId: string | undefined;
|
|
@@ -215,4 +379,20 @@ interface AddressValidationResult {
|
|
|
215
379
|
*/
|
|
216
380
|
declare function useAddressValidation(options: UseAddressValidationOptions): AddressValidationResult;
|
|
217
381
|
|
|
218
|
-
|
|
382
|
+
interface UseFiatCurrenciesOptions {
|
|
383
|
+
/** Whether the query should execute. Defaults to true. */
|
|
384
|
+
enabled?: boolean;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Fiat currencies supported by the card onramp — for building custom
|
|
388
|
+
* currency pickers and amount screens next to {@link useOnramp}.
|
|
389
|
+
*
|
|
390
|
+
* Each {@link FiatCurrency} carries `minimum_amount` / `maximum_amount`
|
|
391
|
+
* (validate the user's amount host-side before quoting — the session only
|
|
392
|
+
* requires a parseable amount > 0), `default_amount`, `suggested_amounts`
|
|
393
|
+
* (quick-pick buttons), and `country_codes` (map your geo signal to a
|
|
394
|
+
* default currency). `data.preferred` lists currency codes to pin first.
|
|
395
|
+
*/
|
|
396
|
+
declare function useFiatCurrencies(options?: UseFiatCurrenciesOptions): _tanstack_react_query.UseQueryResult<FiatCurrenciesResponse, Error>;
|
|
397
|
+
|
|
398
|
+
export { type AddressValidationResult, type AllowedCountryResult, type UseAddressValidationOptions, type UseDepositAddressesOptions, type UseDepositDestination, type UseDepositOptions, type UseDepositResult, type UseExecutionsOptions, type UseFiatCurrenciesOptions, type UseOnrampOptions, type UseOnrampResult, type UseSupportedDepositTokensOptions, useAddressValidation, useAllowedCountry, useDeposit, useDepositAddresses, useExecutions, useFiatCurrencies, useOnramp, useSupportedDepositTokens, useUnifoldClient };
|