@coinlist-co/react 0.6.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/{chunk-5E3P7AMH.js → chunk-AAER5LOL.js} +3 -1
  2. package/dist/chunk-AAER5LOL.js.map +1 -0
  3. package/dist/chunk-I5YTJ5SL.js +644 -0
  4. package/dist/chunk-I5YTJ5SL.js.map +1 -0
  5. package/dist/{chunk-5C4TEVM7.js → chunk-MKCOK3DF.js} +68 -57
  6. package/dist/chunk-MKCOK3DF.js.map +1 -0
  7. package/dist/chunk-Z2HAA2TI.js +768 -0
  8. package/dist/chunk-Z2HAA2TI.js.map +1 -0
  9. package/dist/client/index.cjs +2895 -230
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +902 -29
  12. package/dist/client/index.d.ts +902 -29
  13. package/dist/client/index.js +1980 -211
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/collections-B84Vw55t.d.cts +28 -0
  16. package/dist/collections-BQbFJS3g.d.ts +28 -0
  17. package/dist/requirement-C2w45Q11.d.cts +969 -0
  18. package/dist/requirement-C2w45Q11.d.ts +969 -0
  19. package/dist/server/index.cjs +521 -37
  20. package/dist/server/index.cjs.map +1 -1
  21. package/dist/server/index.d.cts +116 -9
  22. package/dist/server/index.d.ts +116 -9
  23. package/dist/server/index.js +95 -28
  24. package/dist/server/index.js.map +1 -1
  25. package/dist/shared/index.cjs +1245 -72
  26. package/dist/shared/index.cjs.map +1 -1
  27. package/dist/shared/index.d.cts +624 -3
  28. package/dist/shared/index.d.ts +624 -3
  29. package/dist/shared/index.js +216 -5
  30. package/dist/shared/index.js.map +1 -1
  31. package/package.json +7 -3
  32. package/dist/chunk-5C4TEVM7.js.map +0 -1
  33. package/dist/chunk-5E3P7AMH.js.map +0 -1
  34. package/dist/chunk-7SB2GKEU.js +0 -311
  35. package/dist/chunk-7SB2GKEU.js.map +0 -1
  36. package/dist/chunk-UEJVCU2J.js +0 -43
  37. package/dist/chunk-UEJVCU2J.js.map +0 -1
  38. package/dist/requirement-Dk6nYN1c.d.cts +0 -389
  39. package/dist/requirement-Dk6nYN1c.d.ts +0 -389
@@ -1,7 +1,204 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
- import { A as AuthorizationCode, C as CodeVerifier, a as Config, O as OAuthAccessToken, b as Offer, P as PaginationParams, c as PaginatedResponse, d as OfferId, e as OfferDetail, f as Participation, g as ParticipationsPaginationParams, h as ParticipationId, i as CreateParticipationParams, j as OfferOptionId, R as Requirement, k as RequirementStatusInfo, l as RequirementType, m as RequirementStatusValue } from '../requirement-Dk6nYN1c.cjs';
4
+ import { Hex, Abi, Hash, TransactionReceipt } from 'viem';
5
+ import { E as EvmWalletAddress, a as EthereumChain, C as CoinListSwapNamespace, O as OfferId, b as EvmContractAddress, B as Bps, c as BlockchainAmount, S as SwapNamespaceImpl, A as AuthorizationCode, d as CodeVerifier, e as Config, f as OAuthAccessToken, g as Offer, P as PaginationParams, h as PaginatedResponse, i as OfferDetail, j as Participation, k as ParticipationsPaginationParams, l as ParticipationId, m as CreateParticipationParams, n as CreateWalletOwnershipChallengeParams, W as WalletOwnershipChallenge, o as ConnectExternalWalletParams, p as OfferOptionAddress, q as OfferOptionId, r as OfferOptionAddressId, R as Requirement, s as RequirementStatusInfo, t as Pii, D as DocumentType, u as DocumentSubmission, K as KycLevelName, v as KycToken, w as WalletChallengeType, x as RequirementType, y as RequirementStatusValue, z as Erc20Asset, F as AssetDecimals, G as StablecoinSymbol } from '../requirement-C2w45Q11.cjs';
6
+ import { S as SwapQuote, N as NonEmptyArray } from '../collections-B84Vw55t.cjs';
7
+
8
+ type WriteContractParams = {
9
+ abi: Abi;
10
+ address: `0x${string}`;
11
+ functionName: string;
12
+ args?: readonly unknown[];
13
+ value?: bigint;
14
+ chain: EthereumChain;
15
+ };
16
+ type BroadcastTxParams = {
17
+ to: `0x${string}`;
18
+ data: Hex;
19
+ chain: EthereumChain;
20
+ };
21
+ /**
22
+ * The signing-only wallet capability: address + `signMessage`. This is all the
23
+ * external-wallet ownership proof needs (see `useConnectWallet`), so hosts can
24
+ * satisfy that flow without implementing on-chain capabilities. The richer
25
+ * {@link EvmWallet} extends this for swap flows.
26
+ *
27
+ * SDK users implement this against their own wallet stack (e.g. viem, wagmi,
28
+ * Privy). `signMessage` throws on failure and the SDK classifies it.
29
+ */
30
+ interface EvmSigner {
31
+ readonly address: EvmWalletAddress;
32
+ signMessage(message: string): Promise<Hex>;
33
+ }
34
+ /**
35
+ * The full on-chain wallet the SDK needs to run the swap flows: an
36
+ * {@link EvmSigner} plus contract writes, raw-tx broadcast, and confirmation.
37
+ * SDK users implement this against their own wallet stack (e.g. viem, wagmi,
38
+ * Privy).
39
+ *
40
+ * Methods throw on failure. The SDK catches and classifies the thrown error
41
+ * (viem's `UserRejectedRequestError`, `InsufficientFundsError`, timeouts, …)
42
+ * into a typed {@link WalletError}, so implementers can simply let their wallet
43
+ * library's errors propagate.
44
+ */
45
+ interface EvmWallet extends EvmSigner {
46
+ writeContract(params: WriteContractParams): Promise<Hash>;
47
+ broadcastRawTx(params: BroadcastTxParams): Promise<Hash>;
48
+ awaitTx(hash: Hash, chain: EthereumChain): Promise<TransactionReceipt>;
49
+ }
50
+
51
+ /**
52
+ * A classified wallet/transaction failure. The SDK derives this from whatever
53
+ * the {@link EvmWallet} throws, so consumers get a stable, typed error
54
+ * shape regardless of the underlying wallet library.
55
+ */
56
+ type WalletError = {
57
+ type: 'user_rejected';
58
+ } | {
59
+ type: 'insufficient_funds';
60
+ } | {
61
+ type: 'contract_reverted';
62
+ reason: string;
63
+ } | {
64
+ type: 'timeout';
65
+ hash: Hash;
66
+ } | {
67
+ type: 'unknown';
68
+ cause: unknown;
69
+ };
70
+ /**
71
+ * Classifies an error thrown by an {@link EvmWallet} into a typed
72
+ * {@link WalletError}. Pass the transaction `hash` when awaiting a receipt so a
73
+ * timeout can be reported against it.
74
+ */
75
+ declare function classifyWalletError(error: unknown, ctx?: {
76
+ hash?: Hash;
77
+ }): WalletError;
78
+
79
+ /**
80
+ * Progress phases emitted by {@link executeSwap}, in the order they occur. An
81
+ * allowance that already covers the swap skips the approval phases.
82
+ */
83
+ type SwapExecutionPhase = 'checking-status' | 'checking-allowance' | 'resetting-allowance' | 'confirming-allowance-reset' | 'approving' | 'confirming-approval' | 'swapping' | 'confirming-swap';
84
+ /** A step-tagged reason {@link executeSwap} failed. */
85
+ type SwapExecutionError = {
86
+ step: 'status-check';
87
+ } | {
88
+ step: 'swap-stopped';
89
+ } | {
90
+ step: 'allowance-check';
91
+ } | {
92
+ step: 'approval';
93
+ cause: WalletError;
94
+ } | {
95
+ step: 'approval-reverted';
96
+ } | {
97
+ step: 'swap';
98
+ cause: WalletError;
99
+ } | {
100
+ step: 'swap-reverted';
101
+ };
102
+ type SwapExecutionResult = {
103
+ type: 'success';
104
+ swapTxHash: Hash;
105
+ inputAmount: BlockchainAmount;
106
+ fee: BlockchainAmount;
107
+ outputAmount: BlockchainAmount;
108
+ pricePerShare: BlockchainAmount | null;
109
+ recipientAddress: EvmWalletAddress;
110
+ } | {
111
+ type: 'error';
112
+ error: SwapExecutionError;
113
+ };
114
+ type ExecuteSwapParams = {
115
+ swap: CoinListSwapNamespace;
116
+ wallet: EvmWallet;
117
+ contractAddress: EvmContractAddress;
118
+ chain: EthereumChain;
119
+ inputTokenAddress: EvmContractAddress;
120
+ quote: SwapQuote;
121
+ slippageBps: Bps;
122
+ onProgress?: (phase: SwapExecutionPhase) => void;
123
+ };
124
+ /**
125
+ * Executes an on-chain swap end-to-end: verifies the contract is not paused,
126
+ * ensures a sufficient ERC-20 allowance (resetting a stale non-zero allowance
127
+ * first for USDT-style tokens), submits the swap, waits for it to mine, and
128
+ * decodes the confirmed output from the `Swapped` event (falling back to the
129
+ * quote's estimate).
130
+ *
131
+ * The wallet is only asked to sign after all read-only checks pass, so a user
132
+ * never signs a transaction the swap would revert. Progress is reported via
133
+ * `onProgress` for consumers rendering loading states.
134
+ */
135
+ declare function executeSwap(params: ExecuteSwapParams): Promise<SwapExecutionResult>;
136
+ /** Progress phases emitted by {@link authorizeWallet}, in order. */
137
+ type WalletAuthorizationPhase = 'checking-authorization' | 'requesting-challenge' | 'signing-message' | 'submitting-signature' | 'broadcasting-transaction' | 'awaiting-confirmation' | 'verifying-authorization';
138
+ /** A step-tagged reason {@link authorizeWallet} failed. */
139
+ type WalletAuthorizationError = {
140
+ step: 'authorization-check';
141
+ } | {
142
+ step: 'challenge-request';
143
+ } | {
144
+ step: 'signing';
145
+ cause: WalletError;
146
+ } | {
147
+ step: 'allow-wallet';
148
+ } | {
149
+ step: 'broadcast';
150
+ cause: WalletError;
151
+ } | {
152
+ step: 'not-authorized';
153
+ };
154
+ type WalletAuthorizationResult = {
155
+ type: 'success';
156
+ } | {
157
+ type: 'error';
158
+ error: WalletAuthorizationError;
159
+ };
160
+ type AuthorizeWalletParams = {
161
+ swap: CoinListSwapNamespace;
162
+ wallet: EvmWallet;
163
+ offerId: OfferId;
164
+ contractAddress: EvmContractAddress;
165
+ chain: EthereumChain;
166
+ onProgress?: (phase: WalletAuthorizationPhase) => void;
167
+ };
168
+ /**
169
+ * Proves ownership of a wallet and allow-lists it for an offer's swap: checks
170
+ * whether it is already authorized, otherwise signs a challenge, submits it,
171
+ * broadcasts any returned allow-list transaction, and re-verifies on-chain.
172
+ *
173
+ * Wallet selection/connection is the app's responsibility — this operates on an
174
+ * already-connected {@link EvmWallet}. Progress is reported via
175
+ * `onProgress`.
176
+ */
177
+ declare function authorizeWallet(params: AuthorizeWalletParams): Promise<WalletAuthorizationResult>;
178
+
179
+ /**
180
+ * The client-side swap namespace: everything the shared
181
+ * {@link CoinListSwapNamespace} reads/writes over the API, plus the on-chain
182
+ * flows that drive a wallet (`executeSwap`, `authorizeWallet`). The flows take
183
+ * the wallet and addresses per call; the namespace itself supplies the API
184
+ * reads they depend on.
185
+ */
186
+ interface CoinListClientSwapNamespace extends CoinListSwapNamespace {
187
+ /**
188
+ * Runs an on-chain swap end-to-end against the given wallet: status check,
189
+ * ERC-20 allowance/approval, swap submission, and receipt confirmation.
190
+ */
191
+ executeSwap(params: Omit<ExecuteSwapParams, 'swap'>): Promise<SwapExecutionResult>;
192
+ /**
193
+ * Proves ownership of a wallet and allow-lists it for an offer's swap,
194
+ * broadcasting any required allow-list transaction.
195
+ */
196
+ authorizeWallet(params: Omit<AuthorizeWalletParams, 'swap'>): Promise<WalletAuthorizationResult>;
197
+ }
198
+ declare class ClientSwapNamespaceImpl extends SwapNamespaceImpl implements CoinListClientSwapNamespace {
199
+ executeSwap(params: Omit<ExecuteSwapParams, 'swap'>): Promise<SwapExecutionResult>;
200
+ authorizeWallet(params: Omit<AuthorizeWalletParams, 'swap'>): Promise<WalletAuthorizationResult>;
201
+ }
5
202
 
6
203
  type AuthState = 'unknown' | 'logged-in' | 'logged-out';
7
204
  /** Discriminated error reasons from {@link CoinListClient.completeOAuth}. */
@@ -132,6 +329,44 @@ interface CoinListClient {
132
329
  * {@link NotAuthenticatedError}.
133
330
  */
134
331
  createParticipation(params: CreateParticipationParams): Promise<Participation>;
332
+ /**
333
+ * Creates a single-use wallet-ownership challenge for the given wallet and
334
+ * chain. The user signs the returned {@link WalletOwnershipChallenge.message}
335
+ * with their wallet, then passes the signature to
336
+ * {@link connectExternalWallet}.
337
+ *
338
+ * This method must be called only when the user's {@link AuthState} is
339
+ * `'logged-in'`. If the user is not authenticated, it throws
340
+ * {@link NotAuthenticatedError}.
341
+ */
342
+ createWalletOwnershipChallenge(params: CreateWalletOwnershipChallengeParams): Promise<WalletOwnershipChallenge>;
343
+ /**
344
+ * Connects a proven external wallet to an offer option, using a signature of
345
+ * a challenge from {@link createWalletOwnershipChallenge}.
346
+ *
347
+ * This method must be called only when the user's {@link AuthState} is
348
+ * `'logged-in'`. If the user is not authenticated, it throws
349
+ * {@link NotAuthenticatedError}.
350
+ */
351
+ connectExternalWallet(offerId: OfferId, params: ConnectExternalWalletParams): Promise<OfferOptionAddress>;
352
+ /**
353
+ * Lists the user's proven wallet bindings for a single offer option (the
354
+ * bound address for `external_wallet`, or all whitelisted wallets for
355
+ * `whitelisted_wallet`).
356
+ *
357
+ * This method must be called only when the user's {@link AuthState} is
358
+ * `'logged-in'`. If the user is not authenticated, it throws
359
+ * {@link NotAuthenticatedError}.
360
+ */
361
+ listOptionAddresses(offerId: OfferId, offerOptionId: OfferOptionId): Promise<OfferOptionAddress[]>;
362
+ /**
363
+ * Removes one of the user's wallet bindings and returns the removed binding.
364
+ *
365
+ * This method must be called only when the user's {@link AuthState} is
366
+ * `'logged-in'`. If the user is not authenticated, it throws
367
+ * {@link NotAuthenticatedError}.
368
+ */
369
+ removeOptionAddress(offerId: OfferId, addressId: OfferOptionAddressId): Promise<OfferOptionAddress>;
135
370
  /**
136
371
  * Fetches the requirements for all options of a given offer, grouped by option ID.
137
372
  *
@@ -148,15 +383,63 @@ interface CoinListClient {
148
383
  * {@link NotAuthenticatedError}.
149
384
  */
150
385
  fetchRequirementStatuses(offerId: OfferId): Promise<RequirementStatusInfo[]>;
386
+ /**
387
+ * Fetches the current user's PII, used to pre-fill tax forms such as the
388
+ * W-8BEN. Fields the entity hasn't provided are `null`.
389
+ *
390
+ * This method must be called only when the user's {@link AuthState} is
391
+ * `'logged-in'`. If the user is not authenticated, it throws
392
+ * {@link NotAuthenticatedError}.
393
+ */
394
+ fetchPii(): Promise<Pii>;
395
+ /**
396
+ * Starts (or resumes) a document signing submission for the given type
397
+ * (currently only `tax_certification`, e.g. W-8BEN/W-8BEN-E). `fields` are
398
+ * signing-form values keyed by the document's DocuSeal field names,
399
+ * forwarded verbatim to Passport to pre-fill the document.
400
+ *
401
+ * This method must be called only when the user's {@link AuthState} is
402
+ * `'logged-in'`. If the user is not authenticated, it throws
403
+ * {@link NotAuthenticatedError}.
404
+ */
405
+ submitDocument(documentType: DocumentType, fields: Record<string, string>): Promise<DocumentSubmission>;
406
+ /**
407
+ * Creates a short-lived Sumsub WebSDK access token for the current user so an
408
+ * identity verification (KYC) flow can be started, e.g. by the
409
+ * `IdentityVerification` component. `levelName` selects the Sumsub
410
+ * verification level; defaults to the backend's standard level. `reset`
411
+ * resets the Sumsub applicant first, so an already-approved level can be
412
+ * executed again (e.g. to update stale PII) — pass the `kycReset` value
413
+ * from the requirement status, and never on mid-flow token refreshes.
414
+ *
415
+ * This method must be called only when the user's {@link AuthState} is
416
+ * `'logged-in'`. If the user is not authenticated, it throws
417
+ * {@link NotAuthenticatedError}.
418
+ */
419
+ createKycToken(levelName?: KycLevelName, reset?: boolean): Promise<KycToken>;
151
420
  /**
152
421
  * Opens the CoinList page for completing a given requirement in a new tab.
153
- * For `jurisdiction` requirements there is no CTA, so this is a no-op.
422
+ * For `jurisdiction` and `document` requirements there is no CTA, so this
423
+ * is a no-op — `document` requires the caller to use {@link fetchPii} and
424
+ * {@link submitDocument} directly (e.g. via a custom
425
+ * `onRequirementActionOverride`).
154
426
  */
155
427
  handleRequirement(requirement: Requirement): void;
156
428
  /**
157
429
  * Opens the CoinList support ticket page in a new tab.
158
430
  */
159
431
  contactSupport(): void;
432
+ /**
433
+ * On-chain swap operations: quoting a swap, reading swap-contract state and
434
+ * token allowances, and proving/allow-listing wallet ownership — e.g.
435
+ * `coinlist.swap.getOutputToken({ contractAddress, chain })`.
436
+ *
437
+ * The API-backed reads must be called only when the user's {@link AuthState}
438
+ * is `'logged-in'`; if the user is not authenticated, they throw
439
+ * {@link NotAuthenticatedError}. The on-chain flows (`executeSwap`,
440
+ * `authorizeWallet`) additionally drive a caller-supplied wallet.
441
+ */
442
+ readonly swap: CoinListClientSwapNamespace;
160
443
  }
161
444
  declare function createCoinListClient(config: ClientConfig): CoinListClient;
162
445
 
@@ -174,21 +457,22 @@ type CoinListProviderProps = {
174
457
  * React context: no DOM, no <style>, no .clco-sdk-root. Safe to mount app-wide:
175
458
  * it has zero visual impact on the host's own pages.
176
459
  *
177
- * Use this when you need useCoinList() available app-wide. Wrap SDK visual components
178
- * in {@link CoinListStyleScope} separately. For dropping in a single widget, use the
179
- * all-in-one {@link CoinListProvider}.
460
+ * Use this when you need useCoinList() available app-wide. SDK visual components
461
+ * self-scope, so you do not need to wrap them in {@link CoinListStyleScope}.
462
+ * {@link CoinListProvider} is an identical alias with a friendlier name.
180
463
  */
181
464
  declare function CoinListContextProvider({ config, children, }: CoinListProviderProps): react_jsx_runtime.JSX.Element;
182
465
  /**
183
- * All-in-one provider: the React context plus the SDK styling wrapper. Convenience for
184
- * dropping in a single widget. Composes {@link CoinListContextProvider} and
185
- * {@link CoinListStyleScope}.
466
+ * The provider to mount once at your app root. Provides the CoinList client context
467
+ * to the whole tree with zero visual impact: it renders no DOM, no `<style>`, and no
468
+ * `.clco-sdk-root`, so it never styles the host's own pages. SDK visual components
469
+ * (CoinListSignInCard, OffersGrid, ...) style themselves, so you do not need to wrap
470
+ * them in {@link CoinListStyleScope}.
186
471
  *
187
- * Caution: this applies SDK styling (.clco-sdk-root) to everything below it. If you need
188
- * context app-wide without styling the host's pages, mount {@link CoinListContextProvider}
189
- * at the root and wrap only SDK visual components in {@link CoinListStyleScope}.
472
+ * Alias of {@link CoinListContextProvider}: identical behavior, friendlier name for
473
+ * the common "mount one provider" case.
190
474
  */
191
- declare function CoinListProvider({ config, children }: CoinListProviderProps): react_jsx_runtime.JSX.Element;
475
+ declare const CoinListProvider: typeof CoinListContextProvider;
192
476
 
193
477
  type CoinListStyleScopeProps = {
194
478
  children: ReactNode;
@@ -198,9 +482,17 @@ type CoinListStyleScopeProps = {
198
482
  * with the self-contained, scoped <style> injection. Mount this around SDK visual
199
483
  * components only. Keep it off the host's own pages to avoid styling them.
200
484
  *
201
- * This component self-injects all SDK styles via an inline <style> tag so consumers
202
- * do not need to import any CSS files or configure their bundler. Styles are fully
203
- * scoped to .clco-sdk-root and do not leak to the host page.
485
+ * This component self-injects all SDK styles via a <style> tag so consumers do not
486
+ * need to import any CSS files or configure their bundler. Styles are fully scoped
487
+ * to .clco-sdk-root and do not leak to the host page. On React 19 the <style> is a
488
+ * hoistable resource (href + precedence), so it is lifted to <head> and deduplicated
489
+ * across every scope on the page; on React 18 the extra attributes are inert and it
490
+ * renders in place.
491
+ *
492
+ * The wrapper div uses `display: contents` so it generates no box of its own, so an
493
+ * SDK component wrapped by this stays the layout element in the host's flex/grid.
494
+ * Inheritance (font, color, custom-property tokens) and the scoped descendant reset
495
+ * (`.clco-sdk-root *`) still apply through it.
204
496
  */
205
497
  declare function CoinListStyleScope({ children }: CoinListStyleScopeProps): react_jsx_runtime.JSX.Element;
206
498
 
@@ -219,9 +511,11 @@ interface CoinListSignInCardProps {
219
511
  /**
220
512
  * Sign-in card that prompts the user to authenticate with CoinList.
221
513
  *
222
- * Use this component only inside a tree wrapped by {@link CoinListProvider}.
223
- * By default, clicking the sign-in button starts the CoinList OAuth flow via
224
- * {@link CoinListClient#startOAuth}; pass `onSignIn` to override.
514
+ * Use this component inside a tree wrapped by {@link CoinListProvider} (for the
515
+ * client context). By default, clicking the sign-in button starts the CoinList
516
+ * OAuth flow via {@link CoinListClient#startOAuth}; pass `onSignIn` to override.
517
+ *
518
+ * Self-scoped: renders fully styled on its own, no `CoinListStyleScope` needed.
225
519
  */
226
520
  declare function CoinListSignInCard({ state, onSignIn, onClose, className, }?: CoinListSignInCardProps): ReactNode;
227
521
 
@@ -244,6 +538,10 @@ interface Props {
244
538
  /** Optional className applied to an outer wrapper element. */
245
539
  containerClassName?: string;
246
540
  }
541
+ /**
542
+ * Offer card. Self-scoped: renders fully styled on its own, with no
543
+ * `CoinListStyleScope` needed. Also composed by {@link OffersGrid}.
544
+ */
247
545
  declare function OfferCard({ offer, onClick, className, containerClassName, }: Props): ReactNode;
248
546
 
249
547
  interface OffersGridProps {
@@ -276,8 +574,239 @@ interface OffersGridProps {
276
574
  * Uses `loading` / `error` slots when provided; otherwise renders standard
277
575
  * fallback loading and error states. Offer cards are interactive only when
278
576
  * `onOfferClick` is provided.
577
+ *
578
+ * Self-scoped: renders fully styled on its own, with no `CoinListStyleScope` needed.
579
+ */
580
+ declare function OffersGrid(props?: OffersGridProps): ReactNode;
581
+
582
+ /** Load state of a connected-wallet list. */
583
+ type WalletsStatus = 'ready' | 'loading' | 'error';
584
+ /** A single connected wallet, shaped for display in {@link ConnectedWalletList}. */
585
+ type ConnectedWalletUi = {
586
+ /** Id of the connected wallet, used to remove it. */
587
+ id: string;
588
+ /** Full wallet address (for `title`/`aria`). */
589
+ address: string;
590
+ /** Shortened address for display (e.g. `0x1234…abcd`). */
591
+ shortAddress: string;
592
+ };
593
+ interface ConnectedWalletListProps {
594
+ wallets: ConnectedWalletUi[];
595
+ /**
596
+ * Removes a connected wallet by id. May return a promise; the row shows a
597
+ * removing state until it settles and surfaces an inline error on rejection.
598
+ * `null`/omitted hides the remove control (read-only list).
599
+ */
600
+ onRemove?: ((id: string) => void | Promise<void>) | null;
601
+ /** Opens the connect flow to add another wallet. Omit to hide the button. */
602
+ onAdd?: (() => void) | null;
603
+ /** Label for the add button. Defaults to "Add wallet". */
604
+ addLabel?: string;
605
+ /**
606
+ * Load state of the wallets. `'loading'`/`'error'` render a status message so
607
+ * a failed or in-flight fetch is not mistaken for a genuinely empty list.
608
+ * Defaults to `'ready'`.
609
+ */
610
+ status?: WalletsStatus;
611
+ className?: string;
612
+ }
613
+ /**
614
+ * Presentational list of a user's connected wallets for a whitelisted-wallet
615
+ * requirement: each row shows the address with an inline two-step remove
616
+ * confirmation, plus an optional "Add wallet" button. Self-contained — holds
617
+ * only per-row confirm/removing/error UI state; all data and side effects come
618
+ * from props.
619
+ */
620
+ declare function ConnectedWalletList({ wallets, onRemove, onAdd, addLabel, status, className, }: ConnectedWalletListProps): ReactNode;
621
+
622
+ /**
623
+ * The host-provided wallet the ownership flow signs with: the signing-only
624
+ * {@link EvmSigner} plus the {@link EthereumChain} the signature is proven on.
625
+ * `chain` rides on the wallet (not the offer) because the binding is
626
+ * chain-agnostic: it only records which EVM chain signed.
627
+ */
628
+ type ConnectWallet = EvmSigner & {
629
+ readonly chain: EthereumChain;
630
+ };
631
+ /**
632
+ * Machine-readable classification of a connect-wallet failure, so consumers can
633
+ * branch on the cause (e.g. hide a retry affordance on a terminal failure)
634
+ * instead of pattern-matching a display string.
635
+ */
636
+ type ConnectWalletErrorCode = 'not_authenticated' | 'user_rejected' | 'wallet_not_whitelisted' | 'max_wallets_reached' | 'unknown';
637
+ /**
638
+ * A classified connect-wallet failure. `retryable` is false when re-signing the
639
+ * same wallet can never succeed (a terminal binding rejection, or no signed-in
640
+ * session). Display copy is the caller's concern, kept out of the core.
641
+ */
642
+ type ConnectWalletFlowError = {
643
+ code: ConnectWalletErrorCode;
644
+ retryable: boolean;
645
+ };
646
+ /** Progress phases emitted by {@link connectExternalWalletFlow}, in order. */
647
+ type ConnectWalletPhase = 'requesting-challenge' | 'signing-message' | 'submitting-signature';
648
+ type ConnectWalletFlowResult = {
649
+ type: 'success';
650
+ binding: OfferOptionAddress;
651
+ } | {
652
+ type: 'cancelled';
653
+ } | {
654
+ type: 'error';
655
+ error: ConnectWalletFlowError;
656
+ };
657
+ /** The client surface the flow actually depends on, kept narrow on purpose. */
658
+ type ConnectWalletClient = Pick<CoinListClient, 'createWalletOwnershipChallenge' | 'connectExternalWallet'>;
659
+ type ConnectWalletFlowParams = {
660
+ coinlist: ConnectWalletClient;
661
+ /** Host-provided connected wallet the flow signs with. */
662
+ wallet: ConnectWallet;
663
+ offerId: OfferId;
664
+ offerOptionId: OfferOptionId;
665
+ /** Challenge framing; defaults to `'siwe'`. */
666
+ challengeType?: WalletChallengeType;
667
+ /** SIWE statement shown in the signing prompt. */
668
+ statement?: string;
669
+ /**
670
+ * Polled before each irreversible step (signing, binding). Return true to
671
+ * abandon a superseded attempt; the flow then resolves `{ type: 'cancelled' }`
672
+ * without prompting the wallet or binding. React hooks pass a generation
673
+ * check here; non-React callers can omit it.
674
+ */
675
+ isCancelled?: () => boolean;
676
+ /** Reports flow progress for consumers rendering loading states. */
677
+ onProgress?: (phase: ConnectWalletPhase) => void;
678
+ };
679
+ /**
680
+ * Drives the external-wallet ownership flow for one offer option:
681
+ * `createWalletOwnershipChallenge` -> host `wallet.signMessage` ->
682
+ * `connectExternalWallet`. Pure and signer-agnostic: signing is delegated to
683
+ * the host-provided {@link ConnectWallet}, and no React is involved so non-hook
684
+ * consumers can drive it directly (mirrors `authorizeWallet` in `swap-flows`).
685
+ * EVM / EOA + SIWE only for now.
279
686
  */
280
- declare function OffersGrid({ data, maxColumns, className, containerClassName, loading, error, emptyState, onOfferClick, }?: OffersGridProps): ReactNode;
687
+ declare function connectExternalWalletFlow(params: ConnectWalletFlowParams): Promise<ConnectWalletFlowResult>;
688
+
689
+ /**
690
+ * A display-ready connect-wallet error: the core flow's machine-readable code
691
+ * and `retryable` flag, plus copy for the UI.
692
+ */
693
+ type ConnectWalletError = {
694
+ code: ConnectWalletErrorCode;
695
+ message: string;
696
+ retryable: boolean;
697
+ };
698
+ /**
699
+ * Signing-in-flight and the last sign error live inside the READY state (not a
700
+ * separate top-level state) so a failed attempt returns to READY and can be
701
+ * retried without re-opening the modal. Mirrors {@link SignState} in
702
+ * `useTaxDocument`.
703
+ */
704
+ type ConnectWalletSignState = {
705
+ type: 'idle';
706
+ error: ConnectWalletError | null;
707
+ } | {
708
+ type: 'signing';
709
+ };
710
+ type ConnectWalletState = {
711
+ type: 'NEEDS_WALLET';
712
+ } | {
713
+ type: 'READY';
714
+ address: EvmWalletAddress;
715
+ sign: ConnectWalletSignState;
716
+ } | {
717
+ type: 'CONNECTED';
718
+ binding: OfferOptionAddress;
719
+ };
720
+ interface UseConnectWalletOptions {
721
+ /** The hook resets state each time it opens. */
722
+ isOpen: boolean;
723
+ offerId: OfferId;
724
+ offerOptionId: OfferOptionId;
725
+ /** Host-provided connected wallet, or `null` while none is connected. */
726
+ wallet: ConnectWallet | null;
727
+ /** Challenge framing; defaults to `'siwe'`. */
728
+ challengeType?: WalletChallengeType;
729
+ /** SIWE statement shown in the signing prompt. */
730
+ statement?: string;
731
+ }
732
+ interface UseConnectWalletResult {
733
+ state: ConnectWalletState;
734
+ /** READY.sign: idle -> signing -> (CONNECTED | idle w/ error). No-op otherwise. */
735
+ onSign: () => void;
736
+ }
737
+ /**
738
+ * React wrapper over {@link connectExternalWalletFlow}. Owns the modal state
739
+ * machine, the display copy, and generation-token supersession (so a wallet
740
+ * switch, modal close, or unmount cancels an in-flight attempt). The flow logic
741
+ * itself lives in the framework-free core so non-React consumers can drive it.
742
+ */
743
+ declare function useConnectWallet({ isOpen, offerId, offerOptionId, wallet, challengeType, statement, }: UseConnectWalletOptions): UseConnectWalletResult;
744
+
745
+ interface ConnectWalletModalProps {
746
+ isOpen: boolean;
747
+ onClose: () => void;
748
+ offerId: OfferId;
749
+ optionId: OfferOptionId;
750
+ /** Host-provided connected wallet, or `null` while none is connected. */
751
+ wallet: ConnectWallet | null;
752
+ /** Called once the wallet is bound to the offer option, right before close. */
753
+ onConnected?: (binding: OfferOptionAddress) => void;
754
+ /**
755
+ * Called when the user asks to connect (or switch) a wallet: the "Connect
756
+ * wallet" button when none is connected, and "Use a different wallet" when one
757
+ * already is.
758
+ */
759
+ onRequestConnect?: () => void;
760
+ /** Challenge framing; defaults to `'siwe'`. */
761
+ challengeType?: WalletChallengeType;
762
+ /** SIWE statement shown in the signing prompt. */
763
+ statement?: string;
764
+ className?: string;
765
+ }
766
+ /**
767
+ * Batteries-included modal for the external-wallet ownership flow. Drives
768
+ * {@link useConnectWallet} (challenge -> host signs -> connect) for one offer
769
+ * option. Signing is delegated to the host-provided {@link ConnectWallet}: the SDK
770
+ * never bundles a wallet stack.
771
+ *
772
+ * No portal is used — rendering stays inside the normal DOM tree so it keeps
773
+ * this SDK's `.clco-sdk-root`-scoped theme tokens without needing its own
774
+ * {@link CoinListStyleScope}.
775
+ */
776
+ declare function ConnectWalletModal({ isOpen, onClose, offerId, optionId, wallet, onConnected, onRequestConnect, challengeType, statement, className, }: ConnectWalletModalProps): react_jsx_runtime.JSX.Element | null;
777
+
778
+ interface IdentityVerificationProps {
779
+ /**
780
+ * Sumsub verification level to start. Determines which screens the Sumsub
781
+ * WebSDK shows. Defaults to the backend's standard identity level.
782
+ */
783
+ levelName?: KycLevelName;
784
+ /**
785
+ * Reset the Sumsub applicant before starting the flow, so an
786
+ * already-approved level can be executed again (e.g. to update stale PII).
787
+ * Pass the `kycReset` value from the requirement status. Applies only to
788
+ * the initial token — mid-flow token refreshes never repeat the reset.
789
+ */
790
+ reset?: boolean;
791
+ /** BCP-47 language tag for the Sumsub UI, e.g. `'en'`. */
792
+ locale?: string;
793
+ /**
794
+ * Called when the user submits their data in the Sumsub flow. Verification
795
+ * continues asynchronously on the backend, so the requirement typically moves
796
+ * to a pending state first — refetch requirement statuses to observe it.
797
+ */
798
+ onSubmitted?: () => void;
799
+ /** Called when the Sumsub flow reports an error. */
800
+ onError?: (error: Error) => void;
801
+ className?: string;
802
+ }
803
+ /**
804
+ * Inline Sumsub identity verification flow. Fetches a Sumsub WebSDK access
805
+ * token via {@link useKycToken} and renders the Sumsub iframe. Used by
806
+ * `RequirementsChecklist` for `identity_verified` requirements, and can also
807
+ * be rendered standalone.
808
+ */
809
+ declare function IdentityVerification({ levelName, reset, locale, onSubmitted, onError, className, }: IdentityVerificationProps): ReactNode;
281
810
 
282
811
  type RequirementVariant = 'verification' | 'wallet';
283
812
  declare const RequirementVariant: {
@@ -296,21 +825,40 @@ type RequirementItemUi = {
296
825
  status: RequirementStatus;
297
826
  label: string;
298
827
  description: string;
299
- walletAddress?: string;
828
+ /**
829
+ * `true` for `whitelisted_wallet` (a multi-slot list the user can add to and
830
+ * remove from), `false` for the single-slot `external_wallet`.
831
+ */
832
+ multiWallet: boolean;
833
+ /** The user's connected wallets. Single-slot requirements have at most one. */
834
+ connectedWallets: ConnectedWalletUi[];
300
835
  };
301
836
  declare const RequirementItemUi: {
302
- fromDomain(req: Requirement, statusInfo: RequirementStatusInfo | undefined, label: string, description: string, walletAddress?: string): RequirementItemUi;
837
+ fromDomain(req: Requirement, statusInfo: RequirementStatusInfo | undefined, label: string, description: string, addresses?: OfferOptionAddress[]): RequirementItemUi;
303
838
  };
304
839
  interface RequirementItemProps {
305
840
  ui: RequirementItemUi;
306
841
  expanded?: boolean;
307
842
  onToggle?: () => void;
308
- walletAddress?: string;
309
843
  className?: string;
844
+ /** Connect / change / add-wallet action (opens the connect flow). */
310
845
  onAction?: (() => void) | null;
846
+ /** Removes a bound wallet by id (whitelisted-wallet requirements only). */
847
+ onRemoveWallet?: ((id: string) => void | Promise<void>) | null;
848
+ /**
849
+ * Load state of {@link RequirementItemUi.connectedWallets}. `'loading'` /
850
+ * `'error'` keep a fetch-in-flight or failed fetch from looking like an empty
851
+ * wallet list. Defaults to `'ready'`.
852
+ */
853
+ walletsStatus?: WalletsStatus;
311
854
  onContactSupport?: (() => void) | null;
312
855
  }
313
- declare function RequirementItem({ ui, expanded, onToggle, className, onAction, onContactSupport, }: RequirementItemProps): react_jsx_runtime.JSX.Element;
856
+ /**
857
+ * A single requirement row (label, status, expandable action zone). Self-scoped:
858
+ * renders fully styled on its own, with no `CoinListStyleScope` needed. Also composed
859
+ * by {@link RequirementsChecklist}.
860
+ */
861
+ declare function RequirementItem({ ui, expanded, onToggle, className, onAction, onRemoveWallet, walletsStatus, onContactSupport, }: RequirementItemProps): react_jsx_runtime.JSX.Element;
314
862
 
315
863
  type LoadRequirementsReason = 'not-authenticated' | 'generic-error';
316
864
  type LoadRequirementsState = {
@@ -351,7 +899,9 @@ interface UseRequirementsResult {
351
899
  * Returns `LOADING` while CoinList is initializing or while data is being fetched.
352
900
  * Returns `CONTENT` with requirements and statuses on success.
353
901
  * Returns `ERROR` with:
354
- * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
902
+ * - `not-authenticated` immediately (no request is sent) when the client's
903
+ * auth state is logged-out, or when fetching fails with
904
+ * {@link NotAuthenticatedError}
355
905
  * - `generic-error` for any other failure
356
906
  */
357
907
  declare function useRequirements(offerId: OfferId, options?: UseRequirementsOptions): UseRequirementsResult;
@@ -365,8 +915,9 @@ interface RequirementsChecklistProps {
365
915
  /**
366
916
  * Called when the user clicks a requirement's action button (e.g. "Continue", "Connect wallet").
367
917
  * Defaults to {@link CoinListClient#handleRequirement}, which opens the corresponding CoinList
368
- * page in a new tab. Pass your own handler to override this behavior, or pass `null` to disable
369
- * the action button entirely.
918
+ * page in a new tab — except for `document` requirements, which default to opening a built-in
919
+ * {@link TaxDocumentModal} instead. Pass your own handler to override this behavior for all
920
+ * requirement types, or pass `null` to disable the action button entirely.
370
921
  */
371
922
  onRequirementActionOverride?: ((requirement: Requirement) => void) | null;
372
923
  /**
@@ -376,21 +927,54 @@ interface RequirementsChecklistProps {
376
927
  * the contact support button entirely.
377
928
  */
378
929
  onContactSupportOverride?: ((requirement: Requirement) => void) | null;
930
+ /**
931
+ * Host-provided connected wallet used to satisfy `external_wallet` /
932
+ * `whitelisted_wallet` requirements in-app via {@link ConnectWalletModal}.
933
+ * Pass a connected {@link ConnectWallet}, or `null` while none is connected
934
+ * (then also pass {@link onRequestConnect} so the modal can prompt to
935
+ * connect). When this prop is omitted entirely — or is `null` without an
936
+ * {@link onRequestConnect} to act on — wallet requirements fall back to
937
+ * {@link CoinListClient#handleRequirement} (opening CoinList in a new tab).
938
+ */
939
+ wallet?: ConnectWallet | null;
940
+ /** Called when the user asks to connect a wallet and none is connected yet. */
941
+ onRequestConnect?: () => void;
379
942
  /** Override the default label for a requirement type. */
380
943
  getLabel?: (requirement: Requirement) => string;
381
944
  /** Override the default description for a requirement type. */
382
945
  getDescription?: (requirement: Requirement) => string | null;
383
946
  /** Optional loading slot. */
384
947
  loading?: ReactNode;
948
+ /**
949
+ * Rendered when the user is not authenticated. Defaults to
950
+ * {@link CoinListSignInCard}, which prompts the user to sign in and starts
951
+ * the CoinList OAuth flow. Pass `null` to render nothing.
952
+ */
953
+ unauthenticatedState?: ReactNode;
385
954
  /** Optional error slot. */
386
955
  error?: ReactNode;
387
956
  className?: string;
388
957
  /**
389
958
  * Requirements data pre-fetched on the server (e.g. via
390
959
  * `CoinListServer.fetchOfferRequirements()` + `fetchRequirementStatuses()`).
391
- * When provided, the component uses this data as-is and skips the client-side fetch entirely.
960
+ * When provided, the component uses this data as-is and skips the client-side
961
+ * requirements fetch. Connected-wallet addresses for wallet requirements are
962
+ * not part of `RequirementsData` and are always fetched client-side.
392
963
  */
393
964
  data?: RequirementsData;
965
+ /**
966
+ * Options for the inline Sumsub verification flow shown for KYC-backed
967
+ * requirements (`identity_verified`, `kyc_approved`, `accreditation`) when
968
+ * `onRequirementActionOverride` is not provided. The flow starts whenever
969
+ * the requirement's status carries a `kycLevel` — the backend prescribes
970
+ * both the level and whether the applicant must be reset first.
971
+ */
972
+ identityVerificationOptions?: {
973
+ /** Override the backend-prescribed Sumsub verification level. */
974
+ levelName?: KycLevelName;
975
+ /** BCP-47 language tag for the Sumsub UI, e.g. `'en'`. */
976
+ locale?: string;
977
+ };
394
978
  }
395
979
  /**
396
980
  * Connected requirements checklist that fetches requirements and statuses
@@ -400,13 +984,142 @@ interface RequirementsChecklistProps {
400
984
  * Pass `data` (pre-fetched server-side) to use that data as-is and skip
401
985
  * the initial client-side fetch.
402
986
  *
987
+ * When the user is not authenticated, renders a sign-in card instead of the
988
+ * checklist — without fetching. Pass `unauthenticatedState` to customize
989
+ * that state, or `null` to render nothing.
990
+ *
403
991
  * By default, clicking a requirement's action button opens the corresponding
404
992
  * CoinList page in a new tab via {@link CoinListClient#handleRequirement}. It is
405
993
  * recommended to omit `onRequirementActionOverride` and rely on this default.
406
994
  * Pass your own handler only if you need custom navigation behavior, or `null`
407
995
  * to disable the action button entirely.
996
+ *
997
+ * Self-scoped: renders fully styled on its own, with no `CoinListStyleScope` needed.
998
+ */
999
+ declare function RequirementsChecklist(props: RequirementsChecklistProps): ReactNode;
1000
+
1001
+ interface TaxDocumentModalProps {
1002
+ isOpen: boolean;
1003
+ onClose: () => void;
1004
+ /** Called once the document is successfully signed, right before the modal closes. */
1005
+ onSubmitted?: (submission: DocumentSubmission) => void;
1006
+ className?: string;
1007
+ }
1008
+ /**
1009
+ * Batteries-included modal for the tax document (W-8BEN / W-8BEN-E) signing
1010
+ * flow. Fetches the current user's PII via {@link useTaxDocument}, lets
1011
+ * individuals review/edit it before signing; company/trust entities see a
1012
+ * plain sign screen with no fields.
1013
+ *
1014
+ * No portal is used — rendering stays inside the normal DOM tree so it keeps
1015
+ * this SDK's `.clco-sdk-root`-scoped theme tokens without needing its own
1016
+ * {@link CoinListStyleScope}.
1017
+ */
1018
+ declare function TaxDocumentModal({ isOpen, onClose, onSubmitted, className, }: TaxDocumentModalProps): react_jsx_runtime.JSX.Element | null;
1019
+
1020
+ /**
1021
+ * The state of the swap's output-token fetch.
1022
+ *
1023
+ * - `LOADING`: no result yet (initial, or provider still initializing).
1024
+ * - `ERROR`: the fetch failed.
1025
+ * - `CONTENT`: the output token was fetched successfully.
1026
+ *
1027
+ * A stale `CONTENT`/`ERROR` is preserved while a re-fetch (e.g. after a chain
1028
+ * change) is in flight, rather than flipping back to `LOADING`.
1029
+ */
1030
+ type SwapOutputTokenState = {
1031
+ type: 'LOADING';
1032
+ } | {
1033
+ type: 'ERROR';
1034
+ } | {
1035
+ type: 'CONTENT';
1036
+ outputToken: Erc20Asset;
1037
+ };
1038
+ interface UseSwapOutputTokenOptions {
1039
+ /** The swap contract whose output token to read. */
1040
+ contractAddress: EvmContractAddress;
1041
+ chain: EthereumChain;
1042
+ /** When false, the hook does not fetch. */
1043
+ enabled: boolean;
1044
+ }
1045
+ interface UseSwapOutputTokenResult {
1046
+ outputTokenState: SwapOutputTokenState;
1047
+ }
1048
+ /**
1049
+ * Fetches the ERC-20 output token a swap contract pays out. Re-fetches when the
1050
+ * contract or chain changes, and once when `enabled` flips to true.
1051
+ *
1052
+ * The fetch runs once (no polling); a failure resolves to `ERROR` and is not
1053
+ * retried until a dependency changes.
1054
+ */
1055
+ declare function useSwapOutputToken(options: UseSwapOutputTokenOptions): UseSwapOutputTokenResult;
1056
+
1057
+ interface UseSwapQuoteOptions {
1058
+ /** The swap contract to quote against. */
1059
+ contractAddress: EvmContractAddress;
1060
+ chain: EthereumChain;
1061
+ /** The gross amount the user wants to swap, in the input token. */
1062
+ inputAmount: BlockchainAmount;
1063
+ inputTokenAddress: EvmContractAddress;
1064
+ /**
1065
+ * Decimals of the swap's output token. Quoting is skipped until this is
1066
+ * known (e.g. while {@link useSwapOutputToken} is still loading).
1067
+ */
1068
+ outputTokenDecimals: AssetDecimals | null;
1069
+ /** How often to refresh the quote. Defaults to {@link SWAP_POLL_INTERVAL_MS}. */
1070
+ pollIntervalMs?: number;
1071
+ /** When false, the hook neither fetches nor polls. */
1072
+ enabled: boolean;
1073
+ }
1074
+ interface UseSwapQuoteResult {
1075
+ /** The latest quote, or `null` until the first successful fetch. */
1076
+ quote: SwapQuote | null;
1077
+ /** True until the first quote arrives; false while the hook is disabled. */
1078
+ isLoading: boolean;
1079
+ /** True while a background poll refreshes an existing quote. */
1080
+ isRefreshing: boolean;
1081
+ }
1082
+ /**
1083
+ * Polls a read-only swap quote for `inputAmount` of `inputTokenAddress` against
1084
+ * the given swap contract, refreshing every `pollIntervalMs`.
1085
+ *
1086
+ * A failed fetch is swallowed: the last quote stays visible and the next poll
1087
+ * tick retries. Quoting is skipped while `outputTokenDecimals` is `null` or
1088
+ * `enabled` is false, and until the CoinList provider finishes initializing.
408
1089
  */
409
- declare function RequirementsChecklist({ offerId, optionId, title, description, onContinue, onRequirementActionOverride, onContactSupportOverride, getLabel, getDescription, loading, error, className, data, }: RequirementsChecklistProps): ReactNode;
1090
+ declare function useSwapQuote(options: UseSwapQuoteOptions): UseSwapQuoteResult;
1091
+
1092
+ interface UseSwapTokenBalancesOptions {
1093
+ address: EvmWalletAddress;
1094
+ chain: EthereumChain;
1095
+ /** The assets to read balances for. */
1096
+ assets: NonEmptyArray<StablecoinSymbol>;
1097
+ /** How often to refresh balances. Defaults to {@link SWAP_POLL_INTERVAL_MS}. */
1098
+ pollIntervalMs?: number;
1099
+ /** When false, the hook neither fetches nor polls. */
1100
+ enabled: boolean;
1101
+ }
1102
+ interface UseSwapTokenBalancesResult {
1103
+ /**
1104
+ * Raw on-chain balances keyed by the requested assets. Each requested asset
1105
+ * has an entry once a fetch settles (`bigint` on success, `null` on failure).
1106
+ * Assets that were not requested are absent.
1107
+ */
1108
+ balances: Map<StablecoinSymbol, bigint | null>;
1109
+ isLoading: boolean;
1110
+ }
1111
+ /**
1112
+ * Polls the raw ERC-20 balances of `assets` for `address`, refreshing every
1113
+ * `pollIntervalMs`. Each asset settles independently: a failed read maps to
1114
+ * `null` while the others still resolve. A poll tick is skipped while the
1115
+ * previous fetch is still in flight. `assets` is compared by contents, so
1116
+ * passing an inline array does not restart the poll.
1117
+ *
1118
+ * `isLoading` is true only until the first poll settles; background polls do
1119
+ * not toggle it. Disabled (or before the provider is ready) the hook does not
1120
+ * fetch.
1121
+ */
1122
+ declare function useSwapTokenBalances(options: UseSwapTokenBalancesOptions): UseSwapTokenBalancesResult;
410
1123
 
411
1124
  interface UseCoinListResult {
412
1125
  /**
@@ -462,6 +1175,47 @@ interface UseCompleteOAuthOptions {
462
1175
  */
463
1176
  declare function useCompleteOAuth(options: UseCompleteOAuthOptions): void;
464
1177
 
1178
+ type KycTokenErrorReason = 'not-authenticated' | 'generic-error';
1179
+ type KycTokenState = {
1180
+ type: 'IDLE';
1181
+ } | {
1182
+ type: 'LOADING';
1183
+ } | {
1184
+ type: 'ERROR';
1185
+ reason: KycTokenErrorReason;
1186
+ } | {
1187
+ type: 'CONTENT';
1188
+ token: string;
1189
+ };
1190
+ interface UseKycTokenResult {
1191
+ kycTokenState: KycTokenState;
1192
+ /**
1193
+ * Fetches a fresh Sumsub WebSDK access token and transitions the state to
1194
+ * `CONTENT` (or `ERROR`). Resolves with the token so it can also be used
1195
+ * imperatively, e.g. as the Sumsub `expirationHandler`. Rejects on failure.
1196
+ */
1197
+ fetchToken: () => Promise<string>;
1198
+ }
1199
+ /**
1200
+ * Creates Sumsub WebSDK access tokens for the current user via
1201
+ * {@link CoinListClient#createKycToken}, exposed as a state machine.
1202
+ *
1203
+ * Starts in `IDLE`; call `fetchToken()` to request a token. Returns `LOADING`
1204
+ * while the request is in flight, then `CONTENT` with the token on success, or
1205
+ * `ERROR` with:
1206
+ * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
1207
+ * - `generic-error` for any other failure
1208
+ *
1209
+ * `reset` (resetting the Sumsub applicant so an already-approved level can be
1210
+ * redone) tracks the latest value of the `reset` argument up until a token is
1211
+ * first issued; after that, mid-flow token refreshes must never repeat the
1212
+ * reset, or the user's in-progress submission would be wiped, so further
1213
+ * changes to `reset` are ignored. Failed attempts keep the reset pending, so
1214
+ * retrying still requests it — the backend treats resetting an
1215
+ * already-pristine applicant as a no-op.
1216
+ */
1217
+ declare function useKycToken(levelName?: KycLevelName, reset?: boolean): UseKycTokenResult;
1218
+
465
1219
  type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';
466
1220
  type LoadOfferDetailsState = {
467
1221
  type: 'LOADING';
@@ -530,6 +1284,51 @@ interface UseOffersResult {
530
1284
  */
531
1285
  declare function useOffers(options?: UseOffersOptions): UseOffersResult;
532
1286
 
1287
+ type LoadOptionAddressesReason = 'not-authenticated' | 'generic-error';
1288
+ type LoadOptionAddressesState = {
1289
+ type: 'LOADING';
1290
+ } | {
1291
+ type: 'ERROR';
1292
+ reason: LoadOptionAddressesReason;
1293
+ } | {
1294
+ type: 'CONTENT';
1295
+ addresses: OfferOptionAddress[];
1296
+ };
1297
+ interface UseOptionAddressesOptions {
1298
+ /**
1299
+ * Bound wallets pre-fetched on the server (e.g. via
1300
+ * `CoinListServer.listOptionAddresses()`). When provided, the hook uses this
1301
+ * data as-is and skips the initial client-side fetch. Calling `refetch()`
1302
+ * (or `disconnect()`) will still trigger a fresh fetch.
1303
+ */
1304
+ data?: OfferOptionAddress[];
1305
+ /**
1306
+ * When `false`, the hook stays in a benign empty `CONTENT` state and sends no
1307
+ * request. Use it to avoid fetching bindings for options with no wallet
1308
+ * requirement. Defaults to `true`.
1309
+ */
1310
+ enabled?: boolean;
1311
+ }
1312
+ interface UseOptionAddressesResult {
1313
+ addressesState: LoadOptionAddressesState;
1314
+ /** Triggers a re-fetch of the bound wallets. */
1315
+ refetch: () => void;
1316
+ /**
1317
+ * Removes a bound wallet, then refetches the list. Rejects (without
1318
+ * refetching) if the removal fails, so callers can surface the error.
1319
+ */
1320
+ disconnect: (addressId: OfferOptionAddressId) => Promise<void>;
1321
+ }
1322
+ /**
1323
+ * Loads the user's proven wallet bindings for a single offer option, then
1324
+ * exposes them via a state machine plus `refetch`/`disconnect` actions.
1325
+ *
1326
+ * Returns `LOADING` while CoinList is initializing or data is being fetched,
1327
+ * `CONTENT` with the bindings on success, and `ERROR` with `not-authenticated`
1328
+ * (logged-out, or a {@link NotAuthenticatedError}) or `generic-error` otherwise.
1329
+ */
1330
+ declare function useOptionAddresses(offerId: OfferId, offerOptionId: OfferOptionId, options?: UseOptionAddressesOptions): UseOptionAddressesResult;
1331
+
533
1332
  type LoadParticipationsReason = 'not-authenticated' | 'generic-error';
534
1333
  type LoadParticipationsState = {
535
1334
  type: 'LOADING';
@@ -565,4 +1364,78 @@ interface UseParticipationsResult {
565
1364
  */
566
1365
  declare function useParticipations(offerId?: OfferId, options?: UseParticipationsOptions): UseParticipationsResult;
567
1366
 
568
- export { type AuthState, ChecklistStatus, type ClientConfig, type CoinListClient, CoinListClientInitializationError, CoinListContext, CoinListContextProvider, type CoinListContextValue, CoinListProvider, type CoinListProviderProps, CoinListSignInCard, type CoinListSignInCardProps, CoinListStyleScope, type CoinListStyleScopeProps, type CompleteOAuthFailureReason, type LoadOfferDetailsReason, type LoadOfferDetailsState, type LoadOffersReason, type LoadOffersState, type LoadParticipationsReason, type LoadParticipationsState, type LoadRequirementsReason, type LoadRequirementsState, OAUTH_CODE_VERIFIER_KEY, OAUTH_STATE_KEY, type OauthClientErrorReason, type OauthClientResult, OfferCard, type Props as OfferCardProps, OfferCardUi, OffersGrid, type OffersGridProps, RequirementItem, type RequirementItemProps, RequirementItemUi, RequirementStatus, RequirementVariant, RequirementsChecklist, type RequirementsChecklistProps, type RequirementsData, type UseCoinListResult, type UseCompleteOAuthOptions, type UseOfferDetailsOptions, type UseOfferDetailsResult, type UseOffersOptions, type UseOffersResult, type UseParticipationsOptions, type UseParticipationsResult, type UseRequirementsOptions, type UseRequirementsResult, createCoinListClient, useCoinList, useCompleteOAuth, useOfferDetails, useOffers, useParticipations, useRequirements };
1367
+ type TaxDocumentFields = {
1368
+ fullLegalName: string;
1369
+ dob: string;
1370
+ countryOfCitizenship: string;
1371
+ taxId: string;
1372
+ permanentAddress: string;
1373
+ };
1374
+ /**
1375
+ * Structured address pieces sent split in the payload (`City` / `Country`).
1376
+ * Kept separate from the single editable `permanentAddress` display string
1377
+ * and never shown or edited in the form.
1378
+ */
1379
+ type TaxDocumentAddressParts = {
1380
+ city: string;
1381
+ country: string;
1382
+ };
1383
+ /**
1384
+ * Signing-in-flight and the last sign error live inside the REVIEW/SIGN_ONLY
1385
+ * state (not a separate top-level state) so a failed sign never loses the
1386
+ * user's fields.
1387
+ */
1388
+ type SignState = {
1389
+ type: 'idle';
1390
+ error: string | null;
1391
+ } | {
1392
+ type: 'signing';
1393
+ };
1394
+ type TaxDocumentState = {
1395
+ type: 'LOADING';
1396
+ } | {
1397
+ type: 'REVIEW';
1398
+ fields: TaxDocumentFields;
1399
+ addressParts: TaxDocumentAddressParts;
1400
+ piiUnavailable: boolean;
1401
+ sign: SignState;
1402
+ } | {
1403
+ type: 'EDITING';
1404
+ fields: TaxDocumentFields;
1405
+ original: TaxDocumentFields;
1406
+ addressParts: TaxDocumentAddressParts;
1407
+ piiUnavailable: boolean;
1408
+ } | {
1409
+ type: 'SIGN_ONLY';
1410
+ sign: SignState;
1411
+ } | {
1412
+ type: 'ERROR';
1413
+ reason: 'not-authenticated' | 'generic-error';
1414
+ } | {
1415
+ type: 'SUBMITTED';
1416
+ submission: DocumentSubmission;
1417
+ };
1418
+ interface UseTaxDocumentOptions {
1419
+ /** The hook only fetches PII while open; state resets each time it opens. */
1420
+ isOpen: boolean;
1421
+ }
1422
+ interface UseTaxDocumentResult {
1423
+ state: TaxDocumentState;
1424
+ onEditField: (field: keyof TaxDocumentFields, value: string) => void;
1425
+ onStartEdit: () => void;
1426
+ onSaveEdit: () => void;
1427
+ onCancelEdit: () => void;
1428
+ onSign: () => void;
1429
+ }
1430
+ /**
1431
+ * Loads the current user's PII and drives the tax document (W-8BEN /
1432
+ * W-8BEN-E) signing flow.
1433
+ *
1434
+ * Always fetches PII once per open — it's the only way to learn the
1435
+ * entity's `kind`. For company/trust entities the fetched values are
1436
+ * discarded (only `kind` is used) and the form is skipped entirely
1437
+ * (`SIGN_ONLY`); for individuals they seed the review screen (`REVIEW`).
1438
+ */
1439
+ declare function useTaxDocument({ isOpen, }: UseTaxDocumentOptions): UseTaxDocumentResult;
1440
+
1441
+ export { type AuthState, type AuthorizeWalletParams, type BroadcastTxParams, ChecklistStatus, type ClientConfig, ClientSwapNamespaceImpl, type CoinListClient, CoinListClientInitializationError, type CoinListClientSwapNamespace, CoinListContext, CoinListContextProvider, type CoinListContextValue, CoinListProvider, type CoinListProviderProps, CoinListSignInCard, type CoinListSignInCardProps, CoinListStyleScope, type CoinListStyleScopeProps, type CompleteOAuthFailureReason, type ConnectWallet, type ConnectWalletError, type ConnectWalletErrorCode, type ConnectWalletFlowError, type ConnectWalletFlowParams, type ConnectWalletFlowResult, ConnectWalletModal, type ConnectWalletModalProps, type ConnectWalletPhase, type ConnectWalletSignState, type ConnectWalletState, ConnectedWalletList, type ConnectedWalletListProps, type ConnectedWalletUi, type EvmSigner, type EvmWallet, type ExecuteSwapParams, IdentityVerification, type IdentityVerificationProps, KycLevelName, KycToken, type KycTokenErrorReason, type KycTokenState, type LoadOfferDetailsReason, type LoadOfferDetailsState, type LoadOffersReason, type LoadOffersState, type LoadOptionAddressesReason, type LoadOptionAddressesState, type LoadParticipationsReason, type LoadParticipationsState, type LoadRequirementsReason, type LoadRequirementsState, OAUTH_CODE_VERIFIER_KEY, OAUTH_STATE_KEY, type OauthClientErrorReason, type OauthClientResult, OfferCard, type Props as OfferCardProps, OfferCardUi, OffersGrid, type OffersGridProps, RequirementItem, type RequirementItemProps, RequirementItemUi, RequirementStatus, RequirementVariant, RequirementsChecklist, type RequirementsChecklistProps, type RequirementsData, type SignState, type SwapExecutionError, type SwapExecutionPhase, type SwapExecutionResult, type SwapOutputTokenState, type TaxDocumentAddressParts, type TaxDocumentFields, TaxDocumentModal, type TaxDocumentModalProps, type TaxDocumentState, type UseCoinListResult, type UseCompleteOAuthOptions, type UseConnectWalletOptions, type UseConnectWalletResult, type UseKycTokenResult, type UseOfferDetailsOptions, type UseOfferDetailsResult, type UseOffersOptions, type UseOffersResult, type UseOptionAddressesOptions, type UseOptionAddressesResult, type UseParticipationsOptions, type UseParticipationsResult, type UseRequirementsOptions, type UseRequirementsResult, type UseSwapOutputTokenOptions, type UseSwapOutputTokenResult, type UseSwapQuoteOptions, type UseSwapQuoteResult, type UseSwapTokenBalancesOptions, type UseSwapTokenBalancesResult, type UseTaxDocumentOptions, type UseTaxDocumentResult, type WalletAuthorizationError, type WalletAuthorizationPhase, type WalletAuthorizationResult, type WalletError, type WalletsStatus, type WriteContractParams, authorizeWallet, classifyWalletError, connectExternalWalletFlow, createCoinListClient, executeSwap, useCoinList, useCompleteOAuth, useConnectWallet, useKycToken, useOfferDetails, useOffers, useOptionAddresses, useParticipations, useRequirements, useSwapOutputToken, useSwapQuote, useSwapTokenBalances, useTaxDocument };