@unifold/headless-react 0.1.70-beta.1

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 ADDED
@@ -0,0 +1,198 @@
1
+ # @unifold/headless-react
2
+
3
+ Hooks-only (no UI) React SDK for Unifold crypto deposits. You own 100% of the
4
+ rendering; the SDK owns the data flow, session lifecycle, polling, and events.
5
+
6
+ - **Zero UI, zero CSS** — no components, no Tailwind, no portals.
7
+ - **Layered like Stripe.js** — the flow logic lives in a framework-agnostic
8
+ `DepositSession` controller in `@unifold/core`; this package is a thin React
9
+ binding over it.
10
+ - **Events first** — every state transition is observable as a typed event
11
+ using the same webhook-style envelope (`{ id, type, created, data: { object } }`)
12
+ as `@unifold/connect-react`'s `onEvent`. Callbacks (`onSuccess`, …) are sugar
13
+ over the event stream.
14
+
15
+ > Already using `@unifold/connect-react` (the modal SDK)? Don't install this
16
+ > package — import the same hooks from `@unifold/connect-react/headless`
17
+ > instead. Install exactly one of the two packages.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install @unifold/headless-react
23
+ # or
24
+ pnpm add @unifold/headless-react
25
+ ```
26
+
27
+ ## Setup
28
+
29
+ Same provider as the modal SDK — the two SDKs can coexist in one app, sharing
30
+ the provider and QueryClient:
31
+
32
+ ```tsx
33
+ import { UnifoldProvider } from '@unifold/headless-react';
34
+
35
+ function App() {
36
+ return (
37
+ <UnifoldProvider publishableKey="pk_live_...">
38
+ <YourApp />
39
+ </UnifoldProvider>
40
+ );
41
+ }
42
+ ```
43
+
44
+ ## Quickstart — a custom deposit screen
45
+
46
+ ```tsx
47
+ import { useDeposit } from '@unifold/headless-react';
48
+
49
+ function DepositUSDC({ externalUserId }: { externalUserId: string }) {
50
+ const { status, getAddress, latestExecution } = useDeposit({
51
+ externalUserId,
52
+ destination: {
53
+ chainType: 'ethereum',
54
+ chainId: '8453',
55
+ tokenAddress: USDC_BASE,
56
+ recipientAddress: userTreasuryAddress,
57
+ },
58
+ onSuccess: (execution) => toast.success(`Received $${execution.destinationAmountUsd}`),
59
+ });
60
+
61
+ const eth = getAddress({ chainType: 'ethereum' });
62
+ if (!eth) return <Spinner />;
63
+
64
+ return (
65
+ <div>
66
+ <MyQrCode value={eth.address} />
67
+ <CopyField value={eth.address} />
68
+ {status === 'processing' && <MyProgress execution={latestExecution!} />}
69
+ </div>
70
+ );
71
+ }
72
+ ```
73
+
74
+ `useDeposit` drives an explicit status state machine:
75
+
76
+ ```
77
+ idle → creating_addresses → awaiting_funds → processing → succeeded | failed
78
+ ↘ error (fatal: address creation / invalid recipient)
79
+ ```
80
+
81
+ The headless SDK is deliberately **not IP-aware** — `useDeposit` never geo-gates
82
+ address creation. If you want the modal's region gate, opt in with
83
+ `useAllowedCountry` and gate your own UI on `isAllowed`.
84
+
85
+ ### Manual confirmation + full event wiring
86
+
87
+ ```tsx
88
+ const deposit = useDeposit({
89
+ externalUserId,
90
+ destination,
91
+ confirmationMode: 'manual',
92
+ onEvent: (event) => analytics.track(event.type, event),
93
+ onExecutionUpdated: (execution) => setTimeline((t) => [...t, execution]),
94
+ onError: (error) => {
95
+ if (error.code === 'POLLING_ERROR') showBanner('Connection hiccup — still watching');
96
+ else showFailure(error);
97
+ },
98
+ });
99
+
100
+ <Button onClick={deposit.confirmFundsSent} disabled={deposit.isCheckingDeposit}>
101
+ I've made the transfer
102
+ </Button>;
103
+ ```
104
+
105
+ ### Promise waiters
106
+
107
+ For imperative flows, the session exposes `await`-style sugar over the event
108
+ stream (they only listen — neither starts nor stops the session):
109
+
110
+ ```ts
111
+ // Generic primitive over the status state machine:
112
+ await session.waitForStatus('processing'); // deposit detected
113
+ await session.waitForStatus(['succeeded', 'failed']); // terminal outcome
114
+
115
+ // The 90% case — mirrors beginDeposit()'s promise contract
116
+ // (resolve on success, reject on failure):
117
+ try {
118
+ const execution = await session.waitForSuccess();
119
+ creditUser(execution.destinationAmountUsd);
120
+ } catch (error) {
121
+ // DepositSessionWaitError: DEPOSIT_FAILED | SESSION_ERROR | ABORTED | DESTROYED
122
+ }
123
+ ```
124
+
125
+ Both accept `{ signal }` — an `AbortSignal` cancels the *wait*, never the
126
+ session (a deposit isn't cancelable: once the user sends funds, they arrive
127
+ whether or not anyone is awaiting). For a deadline, compose the platform
128
+ primitive — `waitForSuccess({ signal: AbortSignal.timeout(60_000) })` — and
129
+ treat it as "outcome still unknown", not failure: keep the session (and your
130
+ UI) watching. From `useDeposit`, reach the waiters via the `session` escape
131
+ hatch.
132
+
133
+ **One session, many executions.** Unlike quote-scoped models (e.g. Privy's,
134
+ where one address maps to one order), a Unifold session can observe multiple
135
+ executions — the user may send twice, or on two different chains, to the same
136
+ universal addresses. `waitForSuccess` is one-shot **first-completion**
137
+ detection; the session keeps polling afterwards, and every settlement fires
138
+ its own `direct_execution.succeeded` event.
139
+
140
+ **Live outcomes only.** The 60s lookback window exists to catch deposits sent
141
+ moments before the session started — it only admits executions still
142
+ *in-flight* at first sight (their settlement then fires live). An execution
143
+ that already settled before the session began is history and never re-fires
144
+ success/failure events, so reopening a deposit screen right after a success
145
+ cannot double-credit. Render history with `useExecutions` instead. To credit each deposit, subscribe
146
+ to events (dedupe on `execution.id`) or render `executions` from the hook:
147
+
148
+ ```ts
149
+ session.on(DepositSessionEventType.EXECUTION_SUCCEEDED, ({ data }) => {
150
+ creditUser(data.object); // fires once per settled execution
151
+ });
152
+ ```
153
+
154
+ ### Vanilla JS (no React)
155
+
156
+ The controller is usable without React from `@unifold/core`:
157
+
158
+ ```ts
159
+ import { createUnifoldClient } from '@unifold/core';
160
+
161
+ const unifold = createUnifoldClient({ publishableKey: 'pk_live_...' });
162
+ const session = unifold.createDepositSession({ externalUserId, destination });
163
+
164
+ await session.start();
165
+ renderQr(session.getSnapshot().addresses);
166
+
167
+ const execution = await session.waitForSuccess();
168
+ showSuccess(execution);
169
+ session.destroy();
170
+ ```
171
+
172
+ ## Hooks
173
+
174
+ | Hook | Purpose |
175
+ | --- | --- |
176
+ | `useDeposit` | Flagship flow hook: deposit addresses + execution detection + status state machine + events |
177
+ | `useDepositAddresses` | Addresses without a live session (cached, idempotent create) |
178
+ | `useSupportedDepositTokens` | Source token/chain list for custom pickers |
179
+ | `useExecutions` | Deposit history (tracker screens) |
180
+ | `useAllowedCountry` | Geo gate the modal uses; decide what to render when blocked |
181
+ | `useAddressValidation` | Inline recipient validation (e.g. Algorand opt-in) |
182
+ | `useUnifoldClient` | Escape hatch to the configured vanilla client |
183
+
184
+ ## Events
185
+
186
+ `resource.action` names with webhook-mirroring envelopes; `direct_execution.succeeded`
187
+ is byte-compatible with the modal SDK's `onEvent`:
188
+
189
+ - `deposit_session.started` / `.addresses_created` / `.confirmation_started` / `.stopped` / `.errored`
190
+ - `direct_execution.detected` / `.updated` / `.succeeded` / `.failed`
191
+
192
+ When mixing the modal and headless surfaces, dedupe on `execution.id` (envelope
193
+ `sevt_` ids are minted per emitter), and keep one active surface per flow at a
194
+ time.
195
+
196
+ ## License
197
+
198
+ Apache-2.0
@@ -0,0 +1,218 @@
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';
4
+ import * as _tanstack_react_query from '@tanstack/react-query';
5
+
6
+ /**
7
+ * Returns a memoized {@link UnifoldClient} configured with the publishable key
8
+ * from the surrounding `UnifoldProvider`.
9
+ *
10
+ * Escape hatch for imperative access to the headless API (resource calls,
11
+ * `createDepositSession`) outside the provided hooks.
12
+ */
13
+ declare function useUnifoldClient(): UnifoldClient | null;
14
+
15
+ interface UseDepositDestination {
16
+ chainType: ChainType;
17
+ chainId: string;
18
+ tokenAddress: string;
19
+ /** Address that receives the deposited funds. */
20
+ recipientAddress: string;
21
+ /** EVM-only post-delivery calls; same constraints as DepositConfig.contractCalls. */
22
+ contractCalls?: EvmContractCall[];
23
+ }
24
+ interface UseDepositOptions {
25
+ /** Host platform's stable user identifier (maps to external_user_id). */
26
+ externalUserId: string | undefined;
27
+ /** Destination — what the deposit converts into and where it lands. */
28
+ destination: UseDepositDestination;
29
+ /**
30
+ * When the backend scan nudge is armed: 'auto' (default, after 5s) or
31
+ * 'manual' (host calls confirmFundsSent() from its own "I've sent it" button).
32
+ */
33
+ confirmationMode?: 'auto' | 'manual';
34
+ /** Rail hint stamped onto emitted events' `method`. @default 'transfer' */
35
+ method?: DepositMethod;
36
+ /**
37
+ * When true (default), start() is called automatically once externalUserId and the
38
+ * provider's publishable key are present. Set false to gate on your own UI
39
+ * (e.g. only start when the user opens your deposit sheet).
40
+ */
41
+ autoStart?: boolean;
42
+ /** Every lifecycle event ('*' subscription) — analytics/debug fan-out. */
43
+ onEvent?: (event: DepositSessionEvent) => void;
44
+ /** deposit_session.addresses_created */
45
+ onAddressesReady?: (addresses: DepositAddress[]) => void;
46
+ /** direct_execution.detected */
47
+ onExecutionDetected?: (execution: DirectExecution) => void;
48
+ /** direct_execution.updated */
49
+ onExecutionUpdated?: (execution: DirectExecution & {
50
+ previousStatus: ExecutionStatus | null;
51
+ }) => void;
52
+ /** direct_execution.succeeded */
53
+ onSuccess?: (execution: DirectExecution) => void;
54
+ /** direct_execution.failed + deposit_session.errored */
55
+ onError?: (error: DepositSessionError) => void;
56
+ /** Fired whenever snapshot.status changes. */
57
+ onStatusChange?: (status: DepositSessionStatus) => void;
58
+ }
59
+ interface UseDepositResult {
60
+ status: DepositSessionStatus;
61
+ /** Deposit addresses (one per chain type); empty until created. */
62
+ addresses: DepositAddress[];
63
+ /**
64
+ * Convenience: the deposit address matching the query. Takes an options
65
+ * object so future selectors (e.g. `addressType` when a chain type exposes
66
+ * multiple address formats) can be added without a breaking change.
67
+ */
68
+ getAddress: (query: {
69
+ chainType: ChainType;
70
+ }) => DepositAddress | undefined;
71
+ /** All executions observed this session, newest first. */
72
+ executions: DirectExecution[];
73
+ latestExecution: DirectExecution | null;
74
+ /** True while the backend is actively checking for the deposit (scan nudge armed). */
75
+ isCheckingDeposit: boolean;
76
+ error: DepositSessionError | null;
77
+ /** Start the session (relevant when autoStart: false, or to retry after error). */
78
+ start: () => Promise<void>;
79
+ /** Arm the backend scan nudge in 'manual' mode ("I've sent it" button). */
80
+ confirmFundsSent: () => void;
81
+ /** Stop all polling. */
82
+ stop: () => void;
83
+ /** stop() + fresh baseline + start(). */
84
+ restart: () => Promise<void>;
85
+ /** Escape hatch to the underlying session (e.g. extra event subscriptions). */
86
+ session: DepositSession | null;
87
+ }
88
+ /**
89
+ * Flagship headless deposit hook — the no-UI equivalent of
90
+ * `beginDeposit({ initialScreen: 'transfer' })`.
91
+ *
92
+ * Wraps a core {@link DepositSession}: creates it, subscribes via
93
+ * `useSyncExternalStore`, destroys it on unmount, and exposes the snapshot,
94
+ * imperative controls, and callback sugar. The host renders 100% of the UI.
95
+ *
96
+ * SSR-safe: the session is constructed in an effect; server renders see
97
+ * `status: 'idle'` and empty arrays.
98
+ */
99
+ declare function useDeposit(options: UseDepositOptions): UseDepositResult;
100
+
101
+ interface UseDepositAddressesOptions {
102
+ /** Host platform's stable user identifier (maps to external_user_id). */
103
+ externalUserId: string | undefined;
104
+ destination?: {
105
+ chainType?: ChainType;
106
+ chainId?: string;
107
+ tokenAddress?: string;
108
+ recipientAddress?: string;
109
+ contractCalls?: EvmContractCall[];
110
+ };
111
+ /** Action type for the deposit address. @default server default ('deposit') */
112
+ actionType?: ActionType;
113
+ /** Whether the query should execute. Defaults to true. */
114
+ enabled?: boolean;
115
+ }
116
+ /**
117
+ * Fetch/create the user's deposit addresses with react-query caching.
118
+ *
119
+ * `POST /v1/public/deposit_addresses` is idempotent, so this is safe to cache
120
+ * (1h stale). For hosts that want addresses without a live deposit session
121
+ * (e.g. pre-rendering a receive screen). Shares its cache key with the modal
122
+ * SDK's internal hook, so mixing surfaces never double-fetches.
123
+ */
124
+ declare function useDepositAddresses(options: UseDepositAddressesOptions): _tanstack_react_query.UseQueryResult<DepositAddress[], Error>;
125
+
126
+ interface UseSupportedDepositTokensOptions {
127
+ /** All three destination fields must be provided together to filter. */
128
+ destination?: {
129
+ chainType: string;
130
+ chainId: string;
131
+ tokenAddress: string;
132
+ };
133
+ productType?: ProductType;
134
+ /** Whether the query should execute. Defaults to true. */
135
+ enabled?: boolean;
136
+ }
137
+ /**
138
+ * Source tokens/chains a user can deposit from — for building custom
139
+ * token/chain pickers. Cached 5 minutes; deduped with the modal SDK.
140
+ */
141
+ declare function useSupportedDepositTokens(options?: UseSupportedDepositTokensOptions): _tanstack_react_query.UseQueryResult<SupportedToken[], Error>;
142
+
143
+ interface UseExecutionsOptions {
144
+ /** Host platform's stable user identifier (maps to external_user_id). */
145
+ externalUserId: string | undefined;
146
+ /**
147
+ * Polling interval in milliseconds; `false` (default) disables polling.
148
+ * Deposit detection should use `useDeposit` — this hook is for
149
+ * history/tracker screens.
150
+ */
151
+ refetchInterval?: number | false;
152
+ /** Whether the query should execute. Defaults to true. */
153
+ enabled?: boolean;
154
+ /** Filter by action type. Defaults to Deposit. */
155
+ actionType?: ActionType;
156
+ }
157
+ /**
158
+ * The user's executions (deposit history), mapped to camelCase
159
+ * {@link DirectExecution}. Shares its cache key with the modal SDK's tracker.
160
+ */
161
+ declare function useExecutions(options: UseExecutionsOptions): _tanstack_react_query.UseQueryResult<DirectExecution[], Error>;
162
+
163
+ interface AllowedCountryResult {
164
+ /** Whether the user is in an allowed country/subdivision. null while loading. */
165
+ isAllowed: boolean | null;
166
+ /** ISO 3166-1 alpha-2 country code (e.g. "US", "PT") */
167
+ alpha2: string | null;
168
+ /** Full country name (e.g. "United States") */
169
+ country: string | null;
170
+ /** ISO 3166-2 subdivision code (e.g. "NY"), null if unavailable */
171
+ subdivisionCode: string | null;
172
+ isLoading: boolean;
173
+ error: Error | null;
174
+ }
175
+ /**
176
+ * Whether the current user's region is allowed by the project's blocked
177
+ * countries configuration — the same geo gate the modal runs before showing
178
+ * deposit rails.
179
+ *
180
+ * The headless SDK is deliberately not IP-aware: `useDeposit` never runs this
181
+ * check. Hosts that want the modal's geo behavior opt in by rendering
182
+ * against this hook (and gating their deposit UI on `isAllowed`).
183
+ */
184
+ declare function useAllowedCountry(): AllowedCountryResult;
185
+
186
+ interface UseAddressValidationOptions {
187
+ /** Address that will receive the deposited funds. */
188
+ recipientAddress?: string;
189
+ destination?: {
190
+ chainType: ChainType;
191
+ chainId: string;
192
+ tokenAddress: string;
193
+ };
194
+ /** Whether to run validation. Defaults to true. */
195
+ enabled?: boolean;
196
+ }
197
+ interface AddressValidationResult {
198
+ /** Whether the address can receive funds. null while loading or when params are incomplete. */
199
+ isValid: boolean | null;
200
+ /** Standardized error code for i18n. */
201
+ failureCode: AddressValidationFailureCode | null;
202
+ /** Metadata for message interpolation. */
203
+ metadata: {
204
+ chain_name?: string;
205
+ token_symbol?: string;
206
+ } | null;
207
+ isLoading: boolean;
208
+ error: Error | null;
209
+ }
210
+ /**
211
+ * Validate that a recipient address can receive funds for a destination
212
+ * (primarily Algorand asset opt-in; typically valid=true elsewhere).
213
+ * `useDeposit` already fails fast on invalid recipients; use this hook for
214
+ * inline form validation before starting a session.
215
+ */
216
+ declare function useAddressValidation(options: UseAddressValidationOptions): AddressValidationResult;
217
+
218
+ export { type AddressValidationResult, type AllowedCountryResult, type UseAddressValidationOptions, type UseDepositAddressesOptions, type UseDepositDestination, type UseDepositOptions, type UseDepositResult, type UseExecutionsOptions, type UseSupportedDepositTokensOptions, useAddressValidation, useAllowedCountry, useDeposit, useDepositAddresses, useExecutions, useSupportedDepositTokens, useUnifoldClient };
@@ -0,0 +1,218 @@
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';
4
+ import * as _tanstack_react_query from '@tanstack/react-query';
5
+
6
+ /**
7
+ * Returns a memoized {@link UnifoldClient} configured with the publishable key
8
+ * from the surrounding `UnifoldProvider`.
9
+ *
10
+ * Escape hatch for imperative access to the headless API (resource calls,
11
+ * `createDepositSession`) outside the provided hooks.
12
+ */
13
+ declare function useUnifoldClient(): UnifoldClient | null;
14
+
15
+ interface UseDepositDestination {
16
+ chainType: ChainType;
17
+ chainId: string;
18
+ tokenAddress: string;
19
+ /** Address that receives the deposited funds. */
20
+ recipientAddress: string;
21
+ /** EVM-only post-delivery calls; same constraints as DepositConfig.contractCalls. */
22
+ contractCalls?: EvmContractCall[];
23
+ }
24
+ interface UseDepositOptions {
25
+ /** Host platform's stable user identifier (maps to external_user_id). */
26
+ externalUserId: string | undefined;
27
+ /** Destination — what the deposit converts into and where it lands. */
28
+ destination: UseDepositDestination;
29
+ /**
30
+ * When the backend scan nudge is armed: 'auto' (default, after 5s) or
31
+ * 'manual' (host calls confirmFundsSent() from its own "I've sent it" button).
32
+ */
33
+ confirmationMode?: 'auto' | 'manual';
34
+ /** Rail hint stamped onto emitted events' `method`. @default 'transfer' */
35
+ method?: DepositMethod;
36
+ /**
37
+ * When true (default), start() is called automatically once externalUserId and the
38
+ * provider's publishable key are present. Set false to gate on your own UI
39
+ * (e.g. only start when the user opens your deposit sheet).
40
+ */
41
+ autoStart?: boolean;
42
+ /** Every lifecycle event ('*' subscription) — analytics/debug fan-out. */
43
+ onEvent?: (event: DepositSessionEvent) => void;
44
+ /** deposit_session.addresses_created */
45
+ onAddressesReady?: (addresses: DepositAddress[]) => void;
46
+ /** direct_execution.detected */
47
+ onExecutionDetected?: (execution: DirectExecution) => void;
48
+ /** direct_execution.updated */
49
+ onExecutionUpdated?: (execution: DirectExecution & {
50
+ previousStatus: ExecutionStatus | null;
51
+ }) => void;
52
+ /** direct_execution.succeeded */
53
+ onSuccess?: (execution: DirectExecution) => void;
54
+ /** direct_execution.failed + deposit_session.errored */
55
+ onError?: (error: DepositSessionError) => void;
56
+ /** Fired whenever snapshot.status changes. */
57
+ onStatusChange?: (status: DepositSessionStatus) => void;
58
+ }
59
+ interface UseDepositResult {
60
+ status: DepositSessionStatus;
61
+ /** Deposit addresses (one per chain type); empty until created. */
62
+ addresses: DepositAddress[];
63
+ /**
64
+ * Convenience: the deposit address matching the query. Takes an options
65
+ * object so future selectors (e.g. `addressType` when a chain type exposes
66
+ * multiple address formats) can be added without a breaking change.
67
+ */
68
+ getAddress: (query: {
69
+ chainType: ChainType;
70
+ }) => DepositAddress | undefined;
71
+ /** All executions observed this session, newest first. */
72
+ executions: DirectExecution[];
73
+ latestExecution: DirectExecution | null;
74
+ /** True while the backend is actively checking for the deposit (scan nudge armed). */
75
+ isCheckingDeposit: boolean;
76
+ error: DepositSessionError | null;
77
+ /** Start the session (relevant when autoStart: false, or to retry after error). */
78
+ start: () => Promise<void>;
79
+ /** Arm the backend scan nudge in 'manual' mode ("I've sent it" button). */
80
+ confirmFundsSent: () => void;
81
+ /** Stop all polling. */
82
+ stop: () => void;
83
+ /** stop() + fresh baseline + start(). */
84
+ restart: () => Promise<void>;
85
+ /** Escape hatch to the underlying session (e.g. extra event subscriptions). */
86
+ session: DepositSession | null;
87
+ }
88
+ /**
89
+ * Flagship headless deposit hook — the no-UI equivalent of
90
+ * `beginDeposit({ initialScreen: 'transfer' })`.
91
+ *
92
+ * Wraps a core {@link DepositSession}: creates it, subscribes via
93
+ * `useSyncExternalStore`, destroys it on unmount, and exposes the snapshot,
94
+ * imperative controls, and callback sugar. The host renders 100% of the UI.
95
+ *
96
+ * SSR-safe: the session is constructed in an effect; server renders see
97
+ * `status: 'idle'` and empty arrays.
98
+ */
99
+ declare function useDeposit(options: UseDepositOptions): UseDepositResult;
100
+
101
+ interface UseDepositAddressesOptions {
102
+ /** Host platform's stable user identifier (maps to external_user_id). */
103
+ externalUserId: string | undefined;
104
+ destination?: {
105
+ chainType?: ChainType;
106
+ chainId?: string;
107
+ tokenAddress?: string;
108
+ recipientAddress?: string;
109
+ contractCalls?: EvmContractCall[];
110
+ };
111
+ /** Action type for the deposit address. @default server default ('deposit') */
112
+ actionType?: ActionType;
113
+ /** Whether the query should execute. Defaults to true. */
114
+ enabled?: boolean;
115
+ }
116
+ /**
117
+ * Fetch/create the user's deposit addresses with react-query caching.
118
+ *
119
+ * `POST /v1/public/deposit_addresses` is idempotent, so this is safe to cache
120
+ * (1h stale). For hosts that want addresses without a live deposit session
121
+ * (e.g. pre-rendering a receive screen). Shares its cache key with the modal
122
+ * SDK's internal hook, so mixing surfaces never double-fetches.
123
+ */
124
+ declare function useDepositAddresses(options: UseDepositAddressesOptions): _tanstack_react_query.UseQueryResult<DepositAddress[], Error>;
125
+
126
+ interface UseSupportedDepositTokensOptions {
127
+ /** All three destination fields must be provided together to filter. */
128
+ destination?: {
129
+ chainType: string;
130
+ chainId: string;
131
+ tokenAddress: string;
132
+ };
133
+ productType?: ProductType;
134
+ /** Whether the query should execute. Defaults to true. */
135
+ enabled?: boolean;
136
+ }
137
+ /**
138
+ * Source tokens/chains a user can deposit from — for building custom
139
+ * token/chain pickers. Cached 5 minutes; deduped with the modal SDK.
140
+ */
141
+ declare function useSupportedDepositTokens(options?: UseSupportedDepositTokensOptions): _tanstack_react_query.UseQueryResult<SupportedToken[], Error>;
142
+
143
+ interface UseExecutionsOptions {
144
+ /** Host platform's stable user identifier (maps to external_user_id). */
145
+ externalUserId: string | undefined;
146
+ /**
147
+ * Polling interval in milliseconds; `false` (default) disables polling.
148
+ * Deposit detection should use `useDeposit` — this hook is for
149
+ * history/tracker screens.
150
+ */
151
+ refetchInterval?: number | false;
152
+ /** Whether the query should execute. Defaults to true. */
153
+ enabled?: boolean;
154
+ /** Filter by action type. Defaults to Deposit. */
155
+ actionType?: ActionType;
156
+ }
157
+ /**
158
+ * The user's executions (deposit history), mapped to camelCase
159
+ * {@link DirectExecution}. Shares its cache key with the modal SDK's tracker.
160
+ */
161
+ declare function useExecutions(options: UseExecutionsOptions): _tanstack_react_query.UseQueryResult<DirectExecution[], Error>;
162
+
163
+ interface AllowedCountryResult {
164
+ /** Whether the user is in an allowed country/subdivision. null while loading. */
165
+ isAllowed: boolean | null;
166
+ /** ISO 3166-1 alpha-2 country code (e.g. "US", "PT") */
167
+ alpha2: string | null;
168
+ /** Full country name (e.g. "United States") */
169
+ country: string | null;
170
+ /** ISO 3166-2 subdivision code (e.g. "NY"), null if unavailable */
171
+ subdivisionCode: string | null;
172
+ isLoading: boolean;
173
+ error: Error | null;
174
+ }
175
+ /**
176
+ * Whether the current user's region is allowed by the project's blocked
177
+ * countries configuration — the same geo gate the modal runs before showing
178
+ * deposit rails.
179
+ *
180
+ * The headless SDK is deliberately not IP-aware: `useDeposit` never runs this
181
+ * check. Hosts that want the modal's geo behavior opt in by rendering
182
+ * against this hook (and gating their deposit UI on `isAllowed`).
183
+ */
184
+ declare function useAllowedCountry(): AllowedCountryResult;
185
+
186
+ interface UseAddressValidationOptions {
187
+ /** Address that will receive the deposited funds. */
188
+ recipientAddress?: string;
189
+ destination?: {
190
+ chainType: ChainType;
191
+ chainId: string;
192
+ tokenAddress: string;
193
+ };
194
+ /** Whether to run validation. Defaults to true. */
195
+ enabled?: boolean;
196
+ }
197
+ interface AddressValidationResult {
198
+ /** Whether the address can receive funds. null while loading or when params are incomplete. */
199
+ isValid: boolean | null;
200
+ /** Standardized error code for i18n. */
201
+ failureCode: AddressValidationFailureCode | null;
202
+ /** Metadata for message interpolation. */
203
+ metadata: {
204
+ chain_name?: string;
205
+ token_symbol?: string;
206
+ } | null;
207
+ isLoading: boolean;
208
+ error: Error | null;
209
+ }
210
+ /**
211
+ * Validate that a recipient address can receive funds for a destination
212
+ * (primarily Algorand asset opt-in; typically valid=true elsewhere).
213
+ * `useDeposit` already fails fast on invalid recipients; use this hook for
214
+ * inline form validation before starting a session.
215
+ */
216
+ declare function useAddressValidation(options: UseAddressValidationOptions): AddressValidationResult;
217
+
218
+ export { type AddressValidationResult, type AllowedCountryResult, type UseAddressValidationOptions, type UseDepositAddressesOptions, type UseDepositDestination, type UseDepositOptions, type UseDepositResult, type UseExecutionsOptions, type UseSupportedDepositTokensOptions, useAddressValidation, useAllowedCountry, useDeposit, useDepositAddresses, useExecutions, useSupportedDepositTokens, useUnifoldClient };