@coinlist-co/react 0.6.0 → 0.10.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-5C4TEVM7.js → chunk-7BJ2HAG7.js} +62 -92
  2. package/dist/chunk-7BJ2HAG7.js.map +1 -0
  3. package/dist/{chunk-5E3P7AMH.js → chunk-AAER5LOL.js} +3 -1
  4. package/dist/chunk-AAER5LOL.js.map +1 -0
  5. package/dist/chunk-GSSAB4K5.js +644 -0
  6. package/dist/chunk-GSSAB4K5.js.map +1 -0
  7. package/dist/chunk-TUZKKFNW.js +836 -0
  8. package/dist/chunk-TUZKKFNW.js.map +1 -0
  9. package/dist/client/index.cjs +3203 -412
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +996 -41
  12. package/dist/client/index.d.ts +996 -41
  13. package/dist/client/index.js +2069 -208
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/collections-CJ24dOda.d.cts +28 -0
  16. package/dist/collections-ZYLKp8JB.d.ts +28 -0
  17. package/dist/requirement-CDi5NJI8.d.cts +1036 -0
  18. package/dist/requirement-CDi5NJI8.d.ts +1036 -0
  19. package/dist/server/index.cjs +588 -85
  20. package/dist/server/index.cjs.map +1 -1
  21. package/dist/server/index.d.cts +117 -17
  22. package/dist/server/index.d.ts +117 -17
  23. package/dist/server/index.js +92 -40
  24. package/dist/server/index.js.map +1 -1
  25. package/dist/shared/index.cjs +1334 -91
  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 +220 -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,296 @@
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.js';
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, c as CoinListErc20Namespace, B as Bps, d as BlockchainAmount, S as SwapNamespaceImpl, e as SharedNamespaceContext, f as CoinListTokenSaleNamespace, g as OfferOptionId, A as AssetId, P as Participation, T as TokenSaleNamespaceImpl, h as AuthorizationCode, i as CodeVerifier, j as Config, k as OAuthAccessToken, l as Offer, m as PaginationParams, n as PaginatedResponse, o as OfferDetail, p as CreateWalletOwnershipChallengeParams, W as WalletOwnershipChallenge, q as ConnectExternalWalletParams, r as OfferOptionAddress, s as OfferOptionAddressId, R as Requirement, t as RequirementStatusInfo, u as Pii, D as DocumentType, v as DocumentSubmission, K as KycLevelName, w as KycToken, x as WalletChallengeType, y as RequirementType, z as RequirementStatusValue, F as Erc20Asset, G as AssetDecimals, H as StablecoinSymbol } from '../requirement-CDi5NJI8.js';
6
+ import { S as SwapQuote, N as NonEmptyArray } from '../collections-ZYLKp8JB.js';
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
+ /** A step-tagged reason {@link submitErc20Approval} failed. */
80
+ type Erc20ApprovalError = {
81
+ step: 'approval';
82
+ cause: WalletError;
83
+ } | {
84
+ step: 'approval-reverted';
85
+ };
86
+
87
+ /**
88
+ * Progress phases emitted by {@link executeSwap}, in the order they occur. An
89
+ * allowance that already covers the swap skips the approval phases.
90
+ */
91
+ type SwapExecutionPhase = 'checking-status' | 'checking-allowance' | 'resetting-allowance' | 'confirming-allowance-reset' | 'approving' | 'confirming-approval' | 'swapping' | 'confirming-swap';
92
+ /** A step-tagged reason {@link executeSwap} failed. */
93
+ type SwapExecutionError = {
94
+ step: 'status-check';
95
+ } | {
96
+ step: 'swap-stopped';
97
+ } | {
98
+ step: 'allowance-check';
99
+ } | Erc20ApprovalError | {
100
+ step: 'swap';
101
+ cause: WalletError;
102
+ } | {
103
+ step: 'swap-reverted';
104
+ };
105
+ type SwapExecutionResult = {
106
+ type: 'success';
107
+ swapTxHash: Hash;
108
+ inputAmount: BlockchainAmount;
109
+ fee: BlockchainAmount;
110
+ outputAmount: BlockchainAmount;
111
+ pricePerShare: BlockchainAmount | null;
112
+ recipientAddress: EvmWalletAddress;
113
+ } | {
114
+ type: 'error';
115
+ error: SwapExecutionError;
116
+ };
117
+ type ExecuteSwapParams = {
118
+ swap: CoinListSwapNamespace;
119
+ erc20: CoinListErc20Namespace;
120
+ wallet: EvmWallet;
121
+ contractAddress: EvmContractAddress;
122
+ chain: EthereumChain;
123
+ inputTokenAddress: EvmContractAddress;
124
+ quote: SwapQuote;
125
+ slippageBps: Bps;
126
+ onProgress?: (phase: SwapExecutionPhase) => void;
127
+ };
128
+ /**
129
+ * Executes an on-chain swap end-to-end: verifies the contract is not paused,
130
+ * ensures a sufficient ERC-20 allowance (resetting a stale non-zero allowance
131
+ * first for USDT-style tokens), submits the swap, waits for it to mine, and
132
+ * decodes the confirmed output from the `Swapped` event (falling back to the
133
+ * quote's estimate).
134
+ *
135
+ * The wallet is only asked to sign after all read-only checks pass, so a user
136
+ * never signs a transaction the swap would revert. Progress is reported via
137
+ * `onProgress` for consumers rendering loading states.
138
+ */
139
+ declare function executeSwap(params: ExecuteSwapParams): Promise<SwapExecutionResult>;
140
+ /** Progress phases emitted by {@link authorizeWallet}, in order. */
141
+ type WalletAuthorizationPhase = 'checking-authorization' | 'requesting-challenge' | 'signing-message' | 'submitting-signature' | 'broadcasting-transaction' | 'awaiting-confirmation' | 'verifying-authorization';
142
+ /** A step-tagged reason {@link authorizeWallet} failed. */
143
+ type WalletAuthorizationError = {
144
+ step: 'authorization-check';
145
+ } | {
146
+ step: 'challenge-request';
147
+ } | {
148
+ step: 'signing';
149
+ cause: WalletError;
150
+ } | {
151
+ step: 'allow-wallet';
152
+ } | {
153
+ step: 'broadcast';
154
+ cause: WalletError;
155
+ } | {
156
+ step: 'not-authorized';
157
+ };
158
+ type WalletAuthorizationResult = {
159
+ type: 'success';
160
+ } | {
161
+ type: 'error';
162
+ error: WalletAuthorizationError;
163
+ };
164
+ type AuthorizeWalletParams = {
165
+ swap: CoinListSwapNamespace;
166
+ wallet: EvmWallet;
167
+ offerId: OfferId;
168
+ contractAddress: EvmContractAddress;
169
+ chain: EthereumChain;
170
+ onProgress?: (phase: WalletAuthorizationPhase) => void;
171
+ };
172
+ /**
173
+ * Proves ownership of a wallet and allow-lists it for an offer's swap: checks
174
+ * whether it is already authorized, otherwise signs a challenge, submits it,
175
+ * broadcasts any returned allow-list transaction, and re-verifies on-chain.
176
+ *
177
+ * Wallet selection/connection is the app's responsibility — this operates on an
178
+ * already-connected {@link EvmWallet}. Progress is reported via
179
+ * `onProgress`.
180
+ */
181
+ declare function authorizeWallet(params: AuthorizeWalletParams): Promise<WalletAuthorizationResult>;
182
+
183
+ /**
184
+ * The client-side swap namespace: everything the shared
185
+ * {@link CoinListSwapNamespace} reads/writes over the API, plus the on-chain
186
+ * flows that drive a wallet (`executeSwap`, `authorizeWallet`). The flows take
187
+ * the wallet and addresses per call; the namespace itself supplies the API
188
+ * reads they depend on.
189
+ */
190
+ interface CoinListClientSwapNamespace extends CoinListSwapNamespace {
191
+ /**
192
+ * Runs an on-chain swap end-to-end against the given wallet: status check,
193
+ * ERC-20 allowance/approval, swap submission, and receipt confirmation.
194
+ */
195
+ executeSwap(params: Omit<ExecuteSwapParams, 'swap' | 'erc20'>): Promise<SwapExecutionResult>;
196
+ /**
197
+ * Proves ownership of a wallet and allow-lists it for an offer's swap,
198
+ * broadcasting any required allow-list transaction.
199
+ */
200
+ authorizeWallet(params: Omit<AuthorizeWalletParams, 'swap'>): Promise<WalletAuthorizationResult>;
201
+ }
202
+ declare class ClientSwapNamespaceImpl extends SwapNamespaceImpl implements CoinListClientSwapNamespace {
203
+ private readonly erc20;
204
+ constructor(ctx: SharedNamespaceContext, erc20: CoinListErc20Namespace);
205
+ executeSwap(params: Omit<ExecuteSwapParams, 'swap' | 'erc20'>): Promise<SwapExecutionResult>;
206
+ authorizeWallet(params: Omit<AuthorizeWalletParams, 'swap'>): Promise<WalletAuthorizationResult>;
207
+ }
208
+
209
+ /**
210
+ * Progress phases emitted by {@link executeTokenSale}, in the order they occur.
211
+ * The `resetting-allowance`/`confirming-allowance-reset` phases only occur when
212
+ * the wallet holds a non-zero allowance that must be reset to 0 first
213
+ * (USDT-style tokens reject non-zero -> non-zero `approve()`).
214
+ */
215
+ type TokenSaleExecutionPhase = 'checking-allowance' | 'resetting-allowance' | 'confirming-allowance-reset' | 'approving' | 'confirming-approval' | 'recording-participation';
216
+ /**
217
+ * A step-tagged reason {@link executeTokenSale} failed.
218
+ *
219
+ * The reset-to-zero step (`allowance-reset` / `allowance-reset-reverted`) is
220
+ * reported distinctly from the main approval (`approval` /
221
+ * `approval-reverted`, reused from {@link Erc20ApprovalError}) so consumers can
222
+ * tell which of the two wallet prompts the user rejected or which approval
223
+ * reverted on-chain.
224
+ */
225
+ type TokenSaleExecutionError = {
226
+ step: 'allowance-check';
227
+ } | {
228
+ step: 'allowance-reset';
229
+ cause: WalletError;
230
+ } | {
231
+ step: 'allowance-reset-reverted';
232
+ } | Erc20ApprovalError | {
233
+ step: 'participation';
234
+ approvalTxHash: Hash;
235
+ };
236
+ type TokenSaleExecutionResult = {
237
+ type: 'success';
238
+ participation: Participation;
239
+ approvalTxHash: Hash;
240
+ } | {
241
+ type: 'error';
242
+ error: TokenSaleExecutionError;
243
+ };
244
+ type ExecuteTokenSaleParams = {
245
+ erc20: CoinListErc20Namespace;
246
+ tokenSale: CoinListTokenSaleNamespace;
247
+ wallet: EvmWallet;
248
+ offerId: OfferId;
249
+ offerOptionId: OfferOptionId;
250
+ assetId: AssetId;
251
+ /** ERC-20 the user pays with (e.g. USDC/USDT). */
252
+ paymentTokenAddress: EvmContractAddress;
253
+ /** Contract that pulls the payment — the `approve()` spender. */
254
+ fundingContractAddress: EvmContractAddress;
255
+ chain: EthereumChain;
256
+ amount: BlockchainAmount;
257
+ onProgress?: (phase: TokenSaleExecutionPhase) => void;
258
+ };
259
+ /**
260
+ * Executes a token sale end-to-end: submits an ERC-20 `approve()` for the sale
261
+ * amount to the funding contract (resetting a stale non-zero allowance to 0
262
+ * first for USDT-style tokens), waits for it to mine, and records the
263
+ * participation with CoinList. An approval is submitted even when the existing
264
+ * allowance already covers the amount — the backend requires a fresh approval
265
+ * transaction hash for every participation and verifies it on-chain before
266
+ * confirming.
267
+ *
268
+ * Wallet selection/connection and chain switching are the app's responsibility —
269
+ * this operates on an already-connected {@link EvmWallet}. Progress is reported
270
+ * via `onProgress` for consumers rendering loading states.
271
+ */
272
+ declare function executeTokenSale(params: ExecuteTokenSaleParams): Promise<TokenSaleExecutionResult>;
273
+
274
+ /**
275
+ * The client-side token-sale namespace: everything the shared
276
+ * {@link CoinListTokenSaleNamespace} reads/writes over the API, plus the
277
+ * on-chain flow that drives a wallet (`executeTokenSale`). The flow takes the
278
+ * wallet and addresses per call; the namespace supplies the participation
279
+ * writes and (via the ERC-20 namespace) the allowance read it depends on.
280
+ */
281
+ interface CoinListClientTokenSaleNamespace extends CoinListTokenSaleNamespace {
282
+ /**
283
+ * Runs a token sale end-to-end against the given wallet: ERC-20
284
+ * allowance/approval (resetting a stale non-zero allowance first for
285
+ * USDT-style tokens), approval confirmation, and participation recording.
286
+ */
287
+ executeTokenSale(params: Omit<ExecuteTokenSaleParams, 'erc20' | 'tokenSale'>): Promise<TokenSaleExecutionResult>;
288
+ }
289
+ declare class ClientTokenSaleNamespaceImpl extends TokenSaleNamespaceImpl implements CoinListClientTokenSaleNamespace {
290
+ private readonly erc20;
291
+ constructor(ctx: SharedNamespaceContext, erc20: CoinListErc20Namespace);
292
+ executeTokenSale(params: Omit<ExecuteTokenSaleParams, 'erc20' | 'tokenSale'>): Promise<TokenSaleExecutionResult>;
293
+ }
5
294
 
6
295
  type AuthState = 'unknown' | 'logged-in' | 'logged-out';
7
296
  /** Discriminated error reasons from {@link CoinListClient.completeOAuth}. */
@@ -101,37 +390,43 @@ interface CoinListClient {
101
390
  */
102
391
  fetchOfferDetails(id: OfferId): Promise<OfferDetail>;
103
392
  /**
104
- * Fetches all participations by iterating through every paginated response.
393
+ * Creates a single-use wallet-ownership challenge for the given wallet and
394
+ * chain. The user signs the returned {@link WalletOwnershipChallenge.message}
395
+ * with their wallet, then passes the signature to
396
+ * {@link connectExternalWallet}.
105
397
  *
106
398
  * This method must be called only when the user's {@link AuthState} is
107
399
  * `'logged-in'`. If the user is not authenticated, it throws
108
400
  * {@link NotAuthenticatedError}.
109
401
  */
110
- fetchParticipations(offerId?: OfferId): Promise<Participation[]>;
402
+ createWalletOwnershipChallenge(params: CreateWalletOwnershipChallengeParams): Promise<WalletOwnershipChallenge>;
111
403
  /**
112
- * Fetches a single page of participations, optionally filtered by offer.
404
+ * Connects a proven external wallet to an offer option, using a signature of
405
+ * a challenge from {@link createWalletOwnershipChallenge}.
113
406
  *
114
407
  * This method must be called only when the user's {@link AuthState} is
115
408
  * `'logged-in'`. If the user is not authenticated, it throws
116
409
  * {@link NotAuthenticatedError}.
117
410
  */
118
- fetchParticipationsPage(params: ParticipationsPaginationParams): Promise<PaginatedResponse<Participation>>;
411
+ connectExternalWallet(offerId: OfferId, params: ConnectExternalWalletParams): Promise<OfferOptionAddress>;
119
412
  /**
120
- * Fetches a participation by id.
413
+ * Lists the user's proven wallet bindings for a single offer option (the
414
+ * bound address for `external_wallet`, or all whitelisted wallets for
415
+ * `whitelisted_wallet`).
121
416
  *
122
417
  * This method must be called only when the user's {@link AuthState} is
123
418
  * `'logged-in'`. If the user is not authenticated, it throws
124
419
  * {@link NotAuthenticatedError}.
125
420
  */
126
- fetchParticipation(id: ParticipationId): Promise<Participation>;
421
+ listOptionAddresses(offerId: OfferId, offerOptionId: OfferOptionId): Promise<OfferOptionAddress[]>;
127
422
  /**
128
- * Creates a participation.
423
+ * Removes one of the user's wallet bindings and returns the removed binding.
129
424
  *
130
425
  * This method must be called only when the user's {@link AuthState} is
131
426
  * `'logged-in'`. If the user is not authenticated, it throws
132
427
  * {@link NotAuthenticatedError}.
133
428
  */
134
- createParticipation(params: CreateParticipationParams): Promise<Participation>;
429
+ removeOptionAddress(offerId: OfferId, addressId: OfferOptionAddressId): Promise<OfferOptionAddress>;
135
430
  /**
136
431
  * Fetches the requirements for all options of a given offer, grouped by option ID.
137
432
  *
@@ -148,15 +443,85 @@ interface CoinListClient {
148
443
  * {@link NotAuthenticatedError}.
149
444
  */
150
445
  fetchRequirementStatuses(offerId: OfferId): Promise<RequirementStatusInfo[]>;
446
+ /**
447
+ * Fetches the current user's PII, used to pre-fill tax forms such as the
448
+ * W-8BEN. Fields the entity hasn't provided are `null`.
449
+ *
450
+ * This method must be called only when the user's {@link AuthState} is
451
+ * `'logged-in'`. If the user is not authenticated, it throws
452
+ * {@link NotAuthenticatedError}.
453
+ */
454
+ fetchPii(): Promise<Pii>;
455
+ /**
456
+ * Starts (or resumes) a document signing submission for the given type
457
+ * (currently only `tax_certification`, e.g. W-8BEN/W-8BEN-E). `fields` are
458
+ * signing-form values keyed by the document's DocuSeal field names,
459
+ * forwarded verbatim to Passport to pre-fill the document.
460
+ *
461
+ * This method must be called only when the user's {@link AuthState} is
462
+ * `'logged-in'`. If the user is not authenticated, it throws
463
+ * {@link NotAuthenticatedError}.
464
+ */
465
+ submitDocument(documentType: DocumentType, fields: Record<string, string>): Promise<DocumentSubmission>;
466
+ /**
467
+ * Creates a short-lived Sumsub WebSDK access token for the current user so an
468
+ * identity verification (KYC) flow can be started, e.g. by the
469
+ * `IdentityVerification` component. `levelName` selects the Sumsub
470
+ * verification level; defaults to the backend's standard level. `reset`
471
+ * resets the Sumsub applicant first, so an already-approved level can be
472
+ * executed again (e.g. to update stale PII) — pass the `kycReset` value
473
+ * from the requirement status, and never on mid-flow token refreshes.
474
+ *
475
+ * This method must be called only when the user's {@link AuthState} is
476
+ * `'logged-in'`. If the user is not authenticated, it throws
477
+ * {@link NotAuthenticatedError}.
478
+ */
479
+ createKycToken(levelName?: KycLevelName, reset?: boolean): Promise<KycToken>;
151
480
  /**
152
481
  * 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.
482
+ * For `jurisdiction` and `document` requirements there is no CTA, so this
483
+ * is a no-op — `document` requires the caller to use {@link fetchPii} and
484
+ * {@link submitDocument} directly (e.g. via a custom
485
+ * `onRequirementActionOverride`).
154
486
  */
155
487
  handleRequirement(requirement: Requirement): void;
156
488
  /**
157
489
  * Opens the CoinList support ticket page in a new tab.
158
490
  */
159
491
  contactSupport(): void;
492
+ /**
493
+ * Generic ERC-20 reads (token allowance and balance) shared across the
494
+ * on-chain flows — e.g. `coinlist.erc20.getTokenBalance({ ... })`.
495
+ *
496
+ * These reads must be called only when the user's {@link AuthState} is
497
+ * `'logged-in'`; if the user is not authenticated, they throw
498
+ * {@link NotAuthenticatedError}.
499
+ */
500
+ readonly erc20: CoinListErc20Namespace;
501
+ /**
502
+ * Token-sale operations: listing/reading/recording participations, plus the
503
+ * on-chain `executeTokenSale` flow that submits the ERC-20 approval and
504
+ * records the participation over a caller-supplied {@link EvmWallet} —
505
+ * e.g. `coinlist.tokenSale.executeTokenSale({ ... })`.
506
+ *
507
+ * The API-backed reads/writes must be called only when the user's
508
+ * {@link AuthState} is `'logged-in'`; if the user is not authenticated, they
509
+ * throw {@link NotAuthenticatedError}. `executeTokenSale` additionally drives
510
+ * a caller-supplied wallet and returns a step-tagged result rather than
511
+ * throwing.
512
+ */
513
+ readonly tokenSale: CoinListClientTokenSaleNamespace;
514
+ /**
515
+ * On-chain swap operations: quoting a swap, reading swap-contract state, and
516
+ * proving/allow-listing wallet ownership — e.g.
517
+ * `coinlist.swap.getOutputToken({ contractAddress, chain })`.
518
+ *
519
+ * The API-backed reads must be called only when the user's {@link AuthState}
520
+ * is `'logged-in'`; if the user is not authenticated, they throw
521
+ * {@link NotAuthenticatedError}. The on-chain flows (`executeSwap`,
522
+ * `authorizeWallet`) additionally drive a caller-supplied wallet.
523
+ */
524
+ readonly swap: CoinListClientSwapNamespace;
160
525
  }
161
526
  declare function createCoinListClient(config: ClientConfig): CoinListClient;
162
527
 
@@ -174,21 +539,22 @@ type CoinListProviderProps = {
174
539
  * React context: no DOM, no <style>, no .clco-sdk-root. Safe to mount app-wide:
175
540
  * it has zero visual impact on the host's own pages.
176
541
  *
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}.
542
+ * Use this when you need useCoinList() available app-wide. SDK visual components
543
+ * self-scope, so you do not need to wrap them in {@link CoinListStyleScope}.
544
+ * {@link CoinListProvider} is an identical alias with a friendlier name.
180
545
  */
181
546
  declare function CoinListContextProvider({ config, children, }: CoinListProviderProps): react_jsx_runtime.JSX.Element;
182
547
  /**
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}.
548
+ * The provider to mount once at your app root. Provides the CoinList client context
549
+ * to the whole tree with zero visual impact: it renders no DOM, no `<style>`, and no
550
+ * `.clco-sdk-root`, so it never styles the host's own pages. SDK visual components
551
+ * (CoinListSignInCard, OffersGrid, ...) style themselves, so you do not need to wrap
552
+ * them in {@link CoinListStyleScope}.
186
553
  *
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}.
554
+ * Alias of {@link CoinListContextProvider}: identical behavior, friendlier name for
555
+ * the common "mount one provider" case.
190
556
  */
191
- declare function CoinListProvider({ config, children }: CoinListProviderProps): react_jsx_runtime.JSX.Element;
557
+ declare const CoinListProvider: typeof CoinListContextProvider;
192
558
 
193
559
  type CoinListStyleScopeProps = {
194
560
  children: ReactNode;
@@ -198,9 +564,17 @@ type CoinListStyleScopeProps = {
198
564
  * with the self-contained, scoped <style> injection. Mount this around SDK visual
199
565
  * components only. Keep it off the host's own pages to avoid styling them.
200
566
  *
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.
567
+ * This component self-injects all SDK styles via a <style> tag so consumers do not
568
+ * need to import any CSS files or configure their bundler. Styles are fully scoped
569
+ * to .clco-sdk-root and do not leak to the host page. On React 19 the <style> is a
570
+ * hoistable resource (href + precedence), so it is lifted to <head> and deduplicated
571
+ * across every scope on the page; on React 18 the extra attributes are inert and it
572
+ * renders in place.
573
+ *
574
+ * The wrapper div uses `display: contents` so it generates no box of its own, so an
575
+ * SDK component wrapped by this stays the layout element in the host's flex/grid.
576
+ * Inheritance (font, color, custom-property tokens) and the scoped descendant reset
577
+ * (`.clco-sdk-root *`) still apply through it.
204
578
  */
205
579
  declare function CoinListStyleScope({ children }: CoinListStyleScopeProps): react_jsx_runtime.JSX.Element;
206
580
 
@@ -219,18 +593,20 @@ interface CoinListSignInCardProps {
219
593
  /**
220
594
  * Sign-in card that prompts the user to authenticate with CoinList.
221
595
  *
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.
596
+ * Use this component inside a tree wrapped by {@link CoinListProvider} (for the
597
+ * client context). By default, clicking the sign-in button starts the CoinList
598
+ * OAuth flow via {@link CoinListClient#startOAuth}; pass `onSignIn` to override.
599
+ *
600
+ * Self-scoped: renders fully styled on its own, no `CoinListStyleScope` needed.
225
601
  */
226
602
  declare function CoinListSignInCard({ state, onSignIn, onClose, className, }?: CoinListSignInCardProps): ReactNode;
227
603
 
228
604
  type OfferCardUi = {
229
- tagline: string | null;
230
- bannerUrl: string | null;
231
- logoUrl: string | null;
605
+ tagline: string;
606
+ bannerUrl: string;
607
+ logoUrl: string;
232
608
  formattedStartsAt: string;
233
- formattedEndsAt: string;
609
+ formattedEndsAt: string | null;
234
610
  };
235
611
  declare const OfferCardUi: {
236
612
  fromDomain: (offer: Offer) => OfferCardUi;
@@ -244,6 +620,10 @@ interface Props {
244
620
  /** Optional className applied to an outer wrapper element. */
245
621
  containerClassName?: string;
246
622
  }
623
+ /**
624
+ * Offer card. Self-scoped: renders fully styled on its own, with no
625
+ * `CoinListStyleScope` needed. Also composed by {@link OffersGrid}.
626
+ */
247
627
  declare function OfferCard({ offer, onClick, className, containerClassName, }: Props): ReactNode;
248
628
 
249
629
  interface OffersGridProps {
@@ -276,8 +656,239 @@ interface OffersGridProps {
276
656
  * Uses `loading` / `error` slots when provided; otherwise renders standard
277
657
  * fallback loading and error states. Offer cards are interactive only when
278
658
  * `onOfferClick` is provided.
659
+ *
660
+ * Self-scoped: renders fully styled on its own, with no `CoinListStyleScope` needed.
661
+ */
662
+ declare function OffersGrid(props?: OffersGridProps): ReactNode;
663
+
664
+ /** Load state of a connected-wallet list. */
665
+ type WalletsStatus = 'ready' | 'loading' | 'error';
666
+ /** A single connected wallet, shaped for display in {@link ConnectedWalletList}. */
667
+ type ConnectedWalletUi = {
668
+ /** Id of the connected wallet, used to remove it. */
669
+ id: string;
670
+ /** Full wallet address (for `title`/`aria`). */
671
+ address: string;
672
+ /** Shortened address for display (e.g. `0x1234…abcd`). */
673
+ shortAddress: string;
674
+ };
675
+ interface ConnectedWalletListProps {
676
+ wallets: ConnectedWalletUi[];
677
+ /**
678
+ * Removes a connected wallet by id. May return a promise; the row shows a
679
+ * removing state until it settles and surfaces an inline error on rejection.
680
+ * `null`/omitted hides the remove control (read-only list).
681
+ */
682
+ onRemove?: ((id: string) => void | Promise<void>) | null;
683
+ /** Opens the connect flow to add another wallet. Omit to hide the button. */
684
+ onAdd?: (() => void) | null;
685
+ /** Label for the add button. Defaults to "Add wallet". */
686
+ addLabel?: string;
687
+ /**
688
+ * Load state of the wallets. `'loading'`/`'error'` render a status message so
689
+ * a failed or in-flight fetch is not mistaken for a genuinely empty list.
690
+ * Defaults to `'ready'`.
691
+ */
692
+ status?: WalletsStatus;
693
+ className?: string;
694
+ }
695
+ /**
696
+ * Presentational list of a user's connected wallets for a whitelisted-wallet
697
+ * requirement: each row shows the address with an inline two-step remove
698
+ * confirmation, plus an optional "Add wallet" button. Self-contained — holds
699
+ * only per-row confirm/removing/error UI state; all data and side effects come
700
+ * from props.
701
+ */
702
+ declare function ConnectedWalletList({ wallets, onRemove, onAdd, addLabel, status, className, }: ConnectedWalletListProps): ReactNode;
703
+
704
+ /**
705
+ * The host-provided wallet the ownership flow signs with: the signing-only
706
+ * {@link EvmSigner} plus the {@link EthereumChain} the signature is proven on.
707
+ * `chain` rides on the wallet (not the offer) because the binding is
708
+ * chain-agnostic: it only records which EVM chain signed.
709
+ */
710
+ type ConnectWallet = EvmSigner & {
711
+ readonly chain: EthereumChain;
712
+ };
713
+ /**
714
+ * Machine-readable classification of a connect-wallet failure, so consumers can
715
+ * branch on the cause (e.g. hide a retry affordance on a terminal failure)
716
+ * instead of pattern-matching a display string.
717
+ */
718
+ type ConnectWalletErrorCode = 'not_authenticated' | 'user_rejected' | 'wallet_not_whitelisted' | 'max_wallets_reached' | 'unknown';
719
+ /**
720
+ * A classified connect-wallet failure. `retryable` is false when re-signing the
721
+ * same wallet can never succeed (a terminal binding rejection, or no signed-in
722
+ * session). Display copy is the caller's concern, kept out of the core.
723
+ */
724
+ type ConnectWalletFlowError = {
725
+ code: ConnectWalletErrorCode;
726
+ retryable: boolean;
727
+ };
728
+ /** Progress phases emitted by {@link connectExternalWalletFlow}, in order. */
729
+ type ConnectWalletPhase = 'requesting-challenge' | 'signing-message' | 'submitting-signature';
730
+ type ConnectWalletFlowResult = {
731
+ type: 'success';
732
+ binding: OfferOptionAddress;
733
+ } | {
734
+ type: 'cancelled';
735
+ } | {
736
+ type: 'error';
737
+ error: ConnectWalletFlowError;
738
+ };
739
+ /** The client surface the flow actually depends on, kept narrow on purpose. */
740
+ type ConnectWalletClient = Pick<CoinListClient, 'createWalletOwnershipChallenge' | 'connectExternalWallet'>;
741
+ type ConnectWalletFlowParams = {
742
+ coinlist: ConnectWalletClient;
743
+ /** Host-provided connected wallet the flow signs with. */
744
+ wallet: ConnectWallet;
745
+ offerId: OfferId;
746
+ offerOptionId: OfferOptionId;
747
+ /** Challenge framing; defaults to `'siwe'`. */
748
+ challengeType?: WalletChallengeType;
749
+ /** SIWE statement shown in the signing prompt. */
750
+ statement?: string;
751
+ /**
752
+ * Polled before each irreversible step (signing, binding). Return true to
753
+ * abandon a superseded attempt; the flow then resolves `{ type: 'cancelled' }`
754
+ * without prompting the wallet or binding. React hooks pass a generation
755
+ * check here; non-React callers can omit it.
756
+ */
757
+ isCancelled?: () => boolean;
758
+ /** Reports flow progress for consumers rendering loading states. */
759
+ onProgress?: (phase: ConnectWalletPhase) => void;
760
+ };
761
+ /**
762
+ * Drives the external-wallet ownership flow for one offer option:
763
+ * `createWalletOwnershipChallenge` -> host `wallet.signMessage` ->
764
+ * `connectExternalWallet`. Pure and signer-agnostic: signing is delegated to
765
+ * the host-provided {@link ConnectWallet}, and no React is involved so non-hook
766
+ * consumers can drive it directly (mirrors `authorizeWallet` in `swap-flows`).
767
+ * EVM / EOA + SIWE only for now.
768
+ */
769
+ declare function connectExternalWalletFlow(params: ConnectWalletFlowParams): Promise<ConnectWalletFlowResult>;
770
+
771
+ /**
772
+ * A display-ready connect-wallet error: the core flow's machine-readable code
773
+ * and `retryable` flag, plus copy for the UI.
774
+ */
775
+ type ConnectWalletError = {
776
+ code: ConnectWalletErrorCode;
777
+ message: string;
778
+ retryable: boolean;
779
+ };
780
+ /**
781
+ * Signing-in-flight and the last sign error live inside the READY state (not a
782
+ * separate top-level state) so a failed attempt returns to READY and can be
783
+ * retried without re-opening the modal. Mirrors {@link SignState} in
784
+ * `useTaxDocument`.
785
+ */
786
+ type ConnectWalletSignState = {
787
+ type: 'idle';
788
+ error: ConnectWalletError | null;
789
+ } | {
790
+ type: 'signing';
791
+ };
792
+ type ConnectWalletState = {
793
+ type: 'NEEDS_WALLET';
794
+ } | {
795
+ type: 'READY';
796
+ address: EvmWalletAddress;
797
+ sign: ConnectWalletSignState;
798
+ } | {
799
+ type: 'CONNECTED';
800
+ binding: OfferOptionAddress;
801
+ };
802
+ interface UseConnectWalletOptions {
803
+ /** The hook resets state each time it opens. */
804
+ isOpen: boolean;
805
+ offerId: OfferId;
806
+ offerOptionId: OfferOptionId;
807
+ /** Host-provided connected wallet, or `null` while none is connected. */
808
+ wallet: ConnectWallet | null;
809
+ /** Challenge framing; defaults to `'siwe'`. */
810
+ challengeType?: WalletChallengeType;
811
+ /** SIWE statement shown in the signing prompt. */
812
+ statement?: string;
813
+ }
814
+ interface UseConnectWalletResult {
815
+ state: ConnectWalletState;
816
+ /** READY.sign: idle -> signing -> (CONNECTED | idle w/ error). No-op otherwise. */
817
+ onSign: () => void;
818
+ }
819
+ /**
820
+ * React wrapper over {@link connectExternalWalletFlow}. Owns the modal state
821
+ * machine, the display copy, and generation-token supersession (so a wallet
822
+ * switch, modal close, or unmount cancels an in-flight attempt). The flow logic
823
+ * itself lives in the framework-free core so non-React consumers can drive it.
824
+ */
825
+ declare function useConnectWallet({ isOpen, offerId, offerOptionId, wallet, challengeType, statement, }: UseConnectWalletOptions): UseConnectWalletResult;
826
+
827
+ interface ConnectWalletModalProps {
828
+ isOpen: boolean;
829
+ onClose: () => void;
830
+ offerId: OfferId;
831
+ optionId: OfferOptionId;
832
+ /** Host-provided connected wallet, or `null` while none is connected. */
833
+ wallet: ConnectWallet | null;
834
+ /** Called once the wallet is bound to the offer option, right before close. */
835
+ onConnected?: (binding: OfferOptionAddress) => void;
836
+ /**
837
+ * Called when the user asks to connect (or switch) a wallet: the "Connect
838
+ * wallet" button when none is connected, and "Use a different wallet" when one
839
+ * already is.
840
+ */
841
+ onRequestConnect?: () => void;
842
+ /** Challenge framing; defaults to `'siwe'`. */
843
+ challengeType?: WalletChallengeType;
844
+ /** SIWE statement shown in the signing prompt. */
845
+ statement?: string;
846
+ className?: string;
847
+ }
848
+ /**
849
+ * Batteries-included modal for the external-wallet ownership flow. Drives
850
+ * {@link useConnectWallet} (challenge -> host signs -> connect) for one offer
851
+ * option. Signing is delegated to the host-provided {@link ConnectWallet}: the SDK
852
+ * never bundles a wallet stack.
853
+ *
854
+ * No portal is used — rendering stays inside the normal DOM tree so it keeps
855
+ * this SDK's `.clco-sdk-root`-scoped theme tokens without needing its own
856
+ * {@link CoinListStyleScope}.
279
857
  */
280
- declare function OffersGrid({ data, maxColumns, className, containerClassName, loading, error, emptyState, onOfferClick, }?: OffersGridProps): ReactNode;
858
+ declare function ConnectWalletModal({ isOpen, onClose, offerId, optionId, wallet, onConnected, onRequestConnect, challengeType, statement, className, }: ConnectWalletModalProps): react_jsx_runtime.JSX.Element | null;
859
+
860
+ interface IdentityVerificationProps {
861
+ /**
862
+ * Sumsub verification level to start. Determines which screens the Sumsub
863
+ * WebSDK shows. Defaults to the backend's standard identity level.
864
+ */
865
+ levelName?: KycLevelName;
866
+ /**
867
+ * Reset the Sumsub applicant before starting the flow, so an
868
+ * already-approved level can be executed again (e.g. to update stale PII).
869
+ * Pass the `kycReset` value from the requirement status. Applies only to
870
+ * the initial token — mid-flow token refreshes never repeat the reset.
871
+ */
872
+ reset?: boolean;
873
+ /** BCP-47 language tag for the Sumsub UI, e.g. `'en'`. */
874
+ locale?: string;
875
+ /**
876
+ * Called when the user submits their data in the Sumsub flow. Verification
877
+ * continues asynchronously on the backend, so the requirement typically moves
878
+ * to a pending state first — refetch requirement statuses to observe it.
879
+ */
880
+ onSubmitted?: () => void;
881
+ /** Called when the Sumsub flow reports an error. */
882
+ onError?: (error: Error) => void;
883
+ className?: string;
884
+ }
885
+ /**
886
+ * Inline Sumsub identity verification flow. Fetches a Sumsub WebSDK access
887
+ * token via {@link useKycToken} and renders the Sumsub iframe. Used by
888
+ * `RequirementsChecklist` for `identity_verified` requirements, and can also
889
+ * be rendered standalone.
890
+ */
891
+ declare function IdentityVerification({ levelName, reset, locale, onSubmitted, onError, className, }: IdentityVerificationProps): ReactNode;
281
892
 
282
893
  type RequirementVariant = 'verification' | 'wallet';
283
894
  declare const RequirementVariant: {
@@ -296,21 +907,40 @@ type RequirementItemUi = {
296
907
  status: RequirementStatus;
297
908
  label: string;
298
909
  description: string;
299
- walletAddress?: string;
910
+ /**
911
+ * `true` for `whitelisted_wallet` (a multi-slot list the user can add to and
912
+ * remove from), `false` for the single-slot `external_wallet`.
913
+ */
914
+ multiWallet: boolean;
915
+ /** The user's connected wallets. Single-slot requirements have at most one. */
916
+ connectedWallets: ConnectedWalletUi[];
300
917
  };
301
918
  declare const RequirementItemUi: {
302
- fromDomain(req: Requirement, statusInfo: RequirementStatusInfo | undefined, label: string, description: string, walletAddress?: string): RequirementItemUi;
919
+ fromDomain(req: Requirement, statusInfo: RequirementStatusInfo | undefined, label: string, description: string, addresses?: OfferOptionAddress[]): RequirementItemUi;
303
920
  };
304
921
  interface RequirementItemProps {
305
922
  ui: RequirementItemUi;
306
923
  expanded?: boolean;
307
924
  onToggle?: () => void;
308
- walletAddress?: string;
309
925
  className?: string;
926
+ /** Connect / change / add-wallet action (opens the connect flow). */
310
927
  onAction?: (() => void) | null;
928
+ /** Removes a bound wallet by id (whitelisted-wallet requirements only). */
929
+ onRemoveWallet?: ((id: string) => void | Promise<void>) | null;
930
+ /**
931
+ * Load state of {@link RequirementItemUi.connectedWallets}. `'loading'` /
932
+ * `'error'` keep a fetch-in-flight or failed fetch from looking like an empty
933
+ * wallet list. Defaults to `'ready'`.
934
+ */
935
+ walletsStatus?: WalletsStatus;
311
936
  onContactSupport?: (() => void) | null;
312
937
  }
313
- declare function RequirementItem({ ui, expanded, onToggle, className, onAction, onContactSupport, }: RequirementItemProps): react_jsx_runtime.JSX.Element;
938
+ /**
939
+ * A single requirement row (label, status, expandable action zone). Self-scoped:
940
+ * renders fully styled on its own, with no `CoinListStyleScope` needed. Also composed
941
+ * by {@link RequirementsChecklist}.
942
+ */
943
+ declare function RequirementItem({ ui, expanded, onToggle, className, onAction, onRemoveWallet, walletsStatus, onContactSupport, }: RequirementItemProps): react_jsx_runtime.JSX.Element;
314
944
 
315
945
  type LoadRequirementsReason = 'not-authenticated' | 'generic-error';
316
946
  type LoadRequirementsState = {
@@ -351,7 +981,9 @@ interface UseRequirementsResult {
351
981
  * Returns `LOADING` while CoinList is initializing or while data is being fetched.
352
982
  * Returns `CONTENT` with requirements and statuses on success.
353
983
  * Returns `ERROR` with:
354
- * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
984
+ * - `not-authenticated` immediately (no request is sent) when the client's
985
+ * auth state is logged-out, or when fetching fails with
986
+ * {@link NotAuthenticatedError}
355
987
  * - `generic-error` for any other failure
356
988
  */
357
989
  declare function useRequirements(offerId: OfferId, options?: UseRequirementsOptions): UseRequirementsResult;
@@ -365,8 +997,9 @@ interface RequirementsChecklistProps {
365
997
  /**
366
998
  * Called when the user clicks a requirement's action button (e.g. "Continue", "Connect wallet").
367
999
  * 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.
1000
+ * page in a new tab — except for `document` requirements, which default to opening a built-in
1001
+ * {@link TaxDocumentModal} instead. Pass your own handler to override this behavior for all
1002
+ * requirement types, or pass `null` to disable the action button entirely.
370
1003
  */
371
1004
  onRequirementActionOverride?: ((requirement: Requirement) => void) | null;
372
1005
  /**
@@ -376,21 +1009,54 @@ interface RequirementsChecklistProps {
376
1009
  * the contact support button entirely.
377
1010
  */
378
1011
  onContactSupportOverride?: ((requirement: Requirement) => void) | null;
1012
+ /**
1013
+ * Host-provided connected wallet used to satisfy `external_wallet` /
1014
+ * `whitelisted_wallet` requirements in-app via {@link ConnectWalletModal}.
1015
+ * Pass a connected {@link ConnectWallet}, or `null` while none is connected
1016
+ * (then also pass {@link onRequestConnect} so the modal can prompt to
1017
+ * connect). When this prop is omitted entirely — or is `null` without an
1018
+ * {@link onRequestConnect} to act on — wallet requirements fall back to
1019
+ * {@link CoinListClient#handleRequirement} (opening CoinList in a new tab).
1020
+ */
1021
+ wallet?: ConnectWallet | null;
1022
+ /** Called when the user asks to connect a wallet and none is connected yet. */
1023
+ onRequestConnect?: () => void;
379
1024
  /** Override the default label for a requirement type. */
380
1025
  getLabel?: (requirement: Requirement) => string;
381
1026
  /** Override the default description for a requirement type. */
382
1027
  getDescription?: (requirement: Requirement) => string | null;
383
1028
  /** Optional loading slot. */
384
1029
  loading?: ReactNode;
1030
+ /**
1031
+ * Rendered when the user is not authenticated. Defaults to
1032
+ * {@link CoinListSignInCard}, which prompts the user to sign in and starts
1033
+ * the CoinList OAuth flow. Pass `null` to render nothing.
1034
+ */
1035
+ unauthenticatedState?: ReactNode;
385
1036
  /** Optional error slot. */
386
1037
  error?: ReactNode;
387
1038
  className?: string;
388
1039
  /**
389
1040
  * Requirements data pre-fetched on the server (e.g. via
390
1041
  * `CoinListServer.fetchOfferRequirements()` + `fetchRequirementStatuses()`).
391
- * When provided, the component uses this data as-is and skips the client-side fetch entirely.
1042
+ * When provided, the component uses this data as-is and skips the client-side
1043
+ * requirements fetch. Connected-wallet addresses for wallet requirements are
1044
+ * not part of `RequirementsData` and are always fetched client-side.
392
1045
  */
393
1046
  data?: RequirementsData;
1047
+ /**
1048
+ * Options for the inline Sumsub verification flow shown for KYC-backed
1049
+ * requirements (`identity_verified`, `kyc_approved`, `accreditation`) when
1050
+ * `onRequirementActionOverride` is not provided. The flow starts whenever
1051
+ * the requirement's status carries a `kycLevel` — the backend prescribes
1052
+ * both the level and whether the applicant must be reset first.
1053
+ */
1054
+ identityVerificationOptions?: {
1055
+ /** Override the backend-prescribed Sumsub verification level. */
1056
+ levelName?: KycLevelName;
1057
+ /** BCP-47 language tag for the Sumsub UI, e.g. `'en'`. */
1058
+ locale?: string;
1059
+ };
394
1060
  }
395
1061
  /**
396
1062
  * Connected requirements checklist that fetches requirements and statuses
@@ -400,13 +1066,142 @@ interface RequirementsChecklistProps {
400
1066
  * Pass `data` (pre-fetched server-side) to use that data as-is and skip
401
1067
  * the initial client-side fetch.
402
1068
  *
1069
+ * When the user is not authenticated, renders a sign-in card instead of the
1070
+ * checklist — without fetching. Pass `unauthenticatedState` to customize
1071
+ * that state, or `null` to render nothing.
1072
+ *
403
1073
  * By default, clicking a requirement's action button opens the corresponding
404
1074
  * CoinList page in a new tab via {@link CoinListClient#handleRequirement}. It is
405
1075
  * recommended to omit `onRequirementActionOverride` and rely on this default.
406
1076
  * Pass your own handler only if you need custom navigation behavior, or `null`
407
1077
  * to disable the action button entirely.
1078
+ *
1079
+ * Self-scoped: renders fully styled on its own, with no `CoinListStyleScope` needed.
1080
+ */
1081
+ declare function RequirementsChecklist(props: RequirementsChecklistProps): ReactNode;
1082
+
1083
+ interface TaxDocumentModalProps {
1084
+ isOpen: boolean;
1085
+ onClose: () => void;
1086
+ /** Called once the document is successfully signed, right before the modal closes. */
1087
+ onSubmitted?: (submission: DocumentSubmission) => void;
1088
+ className?: string;
1089
+ }
1090
+ /**
1091
+ * Batteries-included modal for the tax document (W-8BEN / W-8BEN-E) signing
1092
+ * flow. Fetches the current user's PII via {@link useTaxDocument}, lets
1093
+ * individuals review/edit it before signing; company/trust entities see a
1094
+ * plain sign screen with no fields.
1095
+ *
1096
+ * No portal is used — rendering stays inside the normal DOM tree so it keeps
1097
+ * this SDK's `.clco-sdk-root`-scoped theme tokens without needing its own
1098
+ * {@link CoinListStyleScope}.
1099
+ */
1100
+ declare function TaxDocumentModal({ isOpen, onClose, onSubmitted, className, }: TaxDocumentModalProps): react_jsx_runtime.JSX.Element | null;
1101
+
1102
+ /**
1103
+ * The state of the swap's output-token fetch.
1104
+ *
1105
+ * - `LOADING`: no result yet (initial, or provider still initializing).
1106
+ * - `ERROR`: the fetch failed.
1107
+ * - `CONTENT`: the output token was fetched successfully.
1108
+ *
1109
+ * A stale `CONTENT`/`ERROR` is preserved while a re-fetch (e.g. after a chain
1110
+ * change) is in flight, rather than flipping back to `LOADING`.
1111
+ */
1112
+ type SwapOutputTokenState = {
1113
+ type: 'LOADING';
1114
+ } | {
1115
+ type: 'ERROR';
1116
+ } | {
1117
+ type: 'CONTENT';
1118
+ outputToken: Erc20Asset;
1119
+ };
1120
+ interface UseSwapOutputTokenOptions {
1121
+ /** The swap contract whose output token to read. */
1122
+ contractAddress: EvmContractAddress;
1123
+ chain: EthereumChain;
1124
+ /** When false, the hook does not fetch. */
1125
+ enabled: boolean;
1126
+ }
1127
+ interface UseSwapOutputTokenResult {
1128
+ outputTokenState: SwapOutputTokenState;
1129
+ }
1130
+ /**
1131
+ * Fetches the ERC-20 output token a swap contract pays out. Re-fetches when the
1132
+ * contract or chain changes, and once when `enabled` flips to true.
1133
+ *
1134
+ * The fetch runs once (no polling); a failure resolves to `ERROR` and is not
1135
+ * retried until a dependency changes.
1136
+ */
1137
+ declare function useSwapOutputToken(options: UseSwapOutputTokenOptions): UseSwapOutputTokenResult;
1138
+
1139
+ interface UseSwapQuoteOptions {
1140
+ /** The swap contract to quote against. */
1141
+ contractAddress: EvmContractAddress;
1142
+ chain: EthereumChain;
1143
+ /** The gross amount the user wants to swap, in the input token. */
1144
+ inputAmount: BlockchainAmount;
1145
+ inputTokenAddress: EvmContractAddress;
1146
+ /**
1147
+ * Decimals of the swap's output token. Quoting is skipped until this is
1148
+ * known (e.g. while {@link useSwapOutputToken} is still loading).
1149
+ */
1150
+ outputTokenDecimals: AssetDecimals | null;
1151
+ /** How often to refresh the quote. Defaults to {@link SWAP_POLL_INTERVAL_MS}. */
1152
+ pollIntervalMs?: number;
1153
+ /** When false, the hook neither fetches nor polls. */
1154
+ enabled: boolean;
1155
+ }
1156
+ interface UseSwapQuoteResult {
1157
+ /** The latest quote, or `null` until the first successful fetch. */
1158
+ quote: SwapQuote | null;
1159
+ /** True until the first quote arrives; false while the hook is disabled. */
1160
+ isLoading: boolean;
1161
+ /** True while a background poll refreshes an existing quote. */
1162
+ isRefreshing: boolean;
1163
+ }
1164
+ /**
1165
+ * Polls a read-only swap quote for `inputAmount` of `inputTokenAddress` against
1166
+ * the given swap contract, refreshing every `pollIntervalMs`.
1167
+ *
1168
+ * A failed fetch is swallowed: the last quote stays visible and the next poll
1169
+ * tick retries. Quoting is skipped while `outputTokenDecimals` is `null` or
1170
+ * `enabled` is false, and until the CoinList provider finishes initializing.
408
1171
  */
409
- declare function RequirementsChecklist({ offerId, optionId, title, description, onContinue, onRequirementActionOverride, onContactSupportOverride, getLabel, getDescription, loading, error, className, data, }: RequirementsChecklistProps): ReactNode;
1172
+ declare function useSwapQuote(options: UseSwapQuoteOptions): UseSwapQuoteResult;
1173
+
1174
+ interface UseSwapTokenBalancesOptions {
1175
+ address: EvmWalletAddress;
1176
+ chain: EthereumChain;
1177
+ /** The assets to read balances for. */
1178
+ assets: NonEmptyArray<StablecoinSymbol>;
1179
+ /** How often to refresh balances. Defaults to {@link SWAP_POLL_INTERVAL_MS}. */
1180
+ pollIntervalMs?: number;
1181
+ /** When false, the hook neither fetches nor polls. */
1182
+ enabled: boolean;
1183
+ }
1184
+ interface UseSwapTokenBalancesResult {
1185
+ /**
1186
+ * Raw on-chain balances keyed by the requested assets. Each requested asset
1187
+ * has an entry once a fetch settles (`bigint` on success, `null` on failure).
1188
+ * Assets that were not requested are absent.
1189
+ */
1190
+ balances: Map<StablecoinSymbol, bigint | null>;
1191
+ isLoading: boolean;
1192
+ }
1193
+ /**
1194
+ * Polls the raw ERC-20 balances of `assets` for `address`, refreshing every
1195
+ * `pollIntervalMs`. Each asset settles independently: a failed read maps to
1196
+ * `null` while the others still resolve. A poll tick is skipped while the
1197
+ * previous fetch is still in flight. `assets` is compared by contents, so
1198
+ * passing an inline array does not restart the poll.
1199
+ *
1200
+ * `isLoading` is true only until the first poll settles; background polls do
1201
+ * not toggle it. Disabled (or before the provider is ready) the hook does not
1202
+ * fetch.
1203
+ */
1204
+ declare function useSwapTokenBalances(options: UseSwapTokenBalancesOptions): UseSwapTokenBalancesResult;
410
1205
 
411
1206
  interface UseCoinListResult {
412
1207
  /**
@@ -462,6 +1257,47 @@ interface UseCompleteOAuthOptions {
462
1257
  */
463
1258
  declare function useCompleteOAuth(options: UseCompleteOAuthOptions): void;
464
1259
 
1260
+ type KycTokenErrorReason = 'not-authenticated' | 'generic-error';
1261
+ type KycTokenState = {
1262
+ type: 'IDLE';
1263
+ } | {
1264
+ type: 'LOADING';
1265
+ } | {
1266
+ type: 'ERROR';
1267
+ reason: KycTokenErrorReason;
1268
+ } | {
1269
+ type: 'CONTENT';
1270
+ token: string;
1271
+ };
1272
+ interface UseKycTokenResult {
1273
+ kycTokenState: KycTokenState;
1274
+ /**
1275
+ * Fetches a fresh Sumsub WebSDK access token and transitions the state to
1276
+ * `CONTENT` (or `ERROR`). Resolves with the token so it can also be used
1277
+ * imperatively, e.g. as the Sumsub `expirationHandler`. Rejects on failure.
1278
+ */
1279
+ fetchToken: () => Promise<string>;
1280
+ }
1281
+ /**
1282
+ * Creates Sumsub WebSDK access tokens for the current user via
1283
+ * {@link CoinListClient#createKycToken}, exposed as a state machine.
1284
+ *
1285
+ * Starts in `IDLE`; call `fetchToken()` to request a token. Returns `LOADING`
1286
+ * while the request is in flight, then `CONTENT` with the token on success, or
1287
+ * `ERROR` with:
1288
+ * - `not-authenticated` when fetching fails with {@link NotAuthenticatedError}
1289
+ * - `generic-error` for any other failure
1290
+ *
1291
+ * `reset` (resetting the Sumsub applicant so an already-approved level can be
1292
+ * redone) tracks the latest value of the `reset` argument up until a token is
1293
+ * first issued; after that, mid-flow token refreshes must never repeat the
1294
+ * reset, or the user's in-progress submission would be wiped, so further
1295
+ * changes to `reset` are ignored. Failed attempts keep the reset pending, so
1296
+ * retrying still requests it — the backend treats resetting an
1297
+ * already-pristine applicant as a no-op.
1298
+ */
1299
+ declare function useKycToken(levelName?: KycLevelName, reset?: boolean): UseKycTokenResult;
1300
+
465
1301
  type LoadOfferDetailsReason = 'not-authenticated' | 'generic-error';
466
1302
  type LoadOfferDetailsState = {
467
1303
  type: 'LOADING';
@@ -530,6 +1366,51 @@ interface UseOffersResult {
530
1366
  */
531
1367
  declare function useOffers(options?: UseOffersOptions): UseOffersResult;
532
1368
 
1369
+ type LoadOptionAddressesReason = 'not-authenticated' | 'generic-error';
1370
+ type LoadOptionAddressesState = {
1371
+ type: 'LOADING';
1372
+ } | {
1373
+ type: 'ERROR';
1374
+ reason: LoadOptionAddressesReason;
1375
+ } | {
1376
+ type: 'CONTENT';
1377
+ addresses: OfferOptionAddress[];
1378
+ };
1379
+ interface UseOptionAddressesOptions {
1380
+ /**
1381
+ * Bound wallets pre-fetched on the server (e.g. via
1382
+ * `CoinListServer.listOptionAddresses()`). When provided, the hook uses this
1383
+ * data as-is and skips the initial client-side fetch. Calling `refetch()`
1384
+ * (or `disconnect()`) will still trigger a fresh fetch.
1385
+ */
1386
+ data?: OfferOptionAddress[];
1387
+ /**
1388
+ * When `false`, the hook stays in a benign empty `CONTENT` state and sends no
1389
+ * request. Use it to avoid fetching bindings for options with no wallet
1390
+ * requirement. Defaults to `true`.
1391
+ */
1392
+ enabled?: boolean;
1393
+ }
1394
+ interface UseOptionAddressesResult {
1395
+ addressesState: LoadOptionAddressesState;
1396
+ /** Triggers a re-fetch of the bound wallets. */
1397
+ refetch: () => void;
1398
+ /**
1399
+ * Removes a bound wallet, then refetches the list. Rejects (without
1400
+ * refetching) if the removal fails, so callers can surface the error.
1401
+ */
1402
+ disconnect: (addressId: OfferOptionAddressId) => Promise<void>;
1403
+ }
1404
+ /**
1405
+ * Loads the user's proven wallet bindings for a single offer option, then
1406
+ * exposes them via a state machine plus `refetch`/`disconnect` actions.
1407
+ *
1408
+ * Returns `LOADING` while CoinList is initializing or data is being fetched,
1409
+ * `CONTENT` with the bindings on success, and `ERROR` with `not-authenticated`
1410
+ * (logged-out, or a {@link NotAuthenticatedError}) or `generic-error` otherwise.
1411
+ */
1412
+ declare function useOptionAddresses(offerId: OfferId, offerOptionId: OfferOptionId, options?: UseOptionAddressesOptions): UseOptionAddressesResult;
1413
+
533
1414
  type LoadParticipationsReason = 'not-authenticated' | 'generic-error';
534
1415
  type LoadParticipationsState = {
535
1416
  type: 'LOADING';
@@ -565,4 +1446,78 @@ interface UseParticipationsResult {
565
1446
  */
566
1447
  declare function useParticipations(offerId?: OfferId, options?: UseParticipationsOptions): UseParticipationsResult;
567
1448
 
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 };
1449
+ type TaxDocumentFields = {
1450
+ fullLegalName: string;
1451
+ dob: string;
1452
+ countryOfCitizenship: string;
1453
+ taxId: string;
1454
+ permanentAddress: string;
1455
+ };
1456
+ /**
1457
+ * Structured address pieces sent split in the payload (`City` / `Country`).
1458
+ * Kept separate from the single editable `permanentAddress` display string
1459
+ * and never shown or edited in the form.
1460
+ */
1461
+ type TaxDocumentAddressParts = {
1462
+ city: string;
1463
+ country: string;
1464
+ };
1465
+ /**
1466
+ * Signing-in-flight and the last sign error live inside the REVIEW/SIGN_ONLY
1467
+ * state (not a separate top-level state) so a failed sign never loses the
1468
+ * user's fields.
1469
+ */
1470
+ type SignState = {
1471
+ type: 'idle';
1472
+ error: string | null;
1473
+ } | {
1474
+ type: 'signing';
1475
+ };
1476
+ type TaxDocumentState = {
1477
+ type: 'LOADING';
1478
+ } | {
1479
+ type: 'REVIEW';
1480
+ fields: TaxDocumentFields;
1481
+ addressParts: TaxDocumentAddressParts;
1482
+ piiUnavailable: boolean;
1483
+ sign: SignState;
1484
+ } | {
1485
+ type: 'EDITING';
1486
+ fields: TaxDocumentFields;
1487
+ original: TaxDocumentFields;
1488
+ addressParts: TaxDocumentAddressParts;
1489
+ piiUnavailable: boolean;
1490
+ } | {
1491
+ type: 'SIGN_ONLY';
1492
+ sign: SignState;
1493
+ } | {
1494
+ type: 'ERROR';
1495
+ reason: 'not-authenticated' | 'generic-error';
1496
+ } | {
1497
+ type: 'SUBMITTED';
1498
+ submission: DocumentSubmission;
1499
+ };
1500
+ interface UseTaxDocumentOptions {
1501
+ /** The hook only fetches PII while open; state resets each time it opens. */
1502
+ isOpen: boolean;
1503
+ }
1504
+ interface UseTaxDocumentResult {
1505
+ state: TaxDocumentState;
1506
+ onEditField: (field: keyof TaxDocumentFields, value: string) => void;
1507
+ onStartEdit: () => void;
1508
+ onSaveEdit: () => void;
1509
+ onCancelEdit: () => void;
1510
+ onSign: () => void;
1511
+ }
1512
+ /**
1513
+ * Loads the current user's PII and drives the tax document (W-8BEN /
1514
+ * W-8BEN-E) signing flow.
1515
+ *
1516
+ * Always fetches PII once per open — it's the only way to learn the
1517
+ * entity's `kind`. For company/trust entities the fetched values are
1518
+ * discarded (only `kind` is used) and the form is skipped entirely
1519
+ * (`SIGN_ONLY`); for individuals they seed the review screen (`REVIEW`).
1520
+ */
1521
+ declare function useTaxDocument({ isOpen, }: UseTaxDocumentOptions): UseTaxDocumentResult;
1522
+
1523
+ export { type AuthState, type AuthorizeWalletParams, type BroadcastTxParams, ChecklistStatus, type ClientConfig, ClientSwapNamespaceImpl, ClientTokenSaleNamespaceImpl, type CoinListClient, CoinListClientInitializationError, type CoinListClientSwapNamespace, type CoinListClientTokenSaleNamespace, 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, type ExecuteTokenSaleParams, 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 TokenSaleExecutionError, type TokenSaleExecutionPhase, type TokenSaleExecutionResult, 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, executeTokenSale, useCoinList, useCompleteOAuth, useConnectWallet, useKycToken, useOfferDetails, useOffers, useOptionAddresses, useParticipations, useRequirements, useSwapOutputToken, useSwapQuote, useSwapTokenBalances, useTaxDocument };