@coinlist-co/react 0.5.1 → 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 (40) 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-N3WBC2VS.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 +3360 -556
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +921 -18
  12. package/dist/client/index.d.ts +921 -18
  13. package/dist/client/index.js +2275 -392
  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 +1264 -42
  26. package/dist/shared/index.cjs.map +1 -1
  27. package/dist/shared/index.d.cts +644 -3
  28. package/dist/shared/index.d.ts +644 -3
  29. package/dist/shared/index.js +220 -5
  30. package/dist/shared/index.js.map +1 -1
  31. package/package.json +33 -30
  32. package/dist/chunk-5E3P7AMH.js.map +0 -1
  33. package/dist/chunk-CRACFEJ4.js +0 -17
  34. package/dist/chunk-CRACFEJ4.js.map +0 -1
  35. package/dist/chunk-N3WBC2VS.js.map +0 -1
  36. package/dist/chunk-V6WO67RO.js +0 -311
  37. package/dist/chunk-V6WO67RO.js.map +0 -1
  38. package/dist/client/styles.css +0 -2
  39. package/dist/requirement-BEO42QOr.d.cts +0 -387
  40. package/dist/requirement-BEO42QOr.d.ts +0 -387
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  API_VERSION,
3
- Attributes,
4
3
  AuthenticatedApiClient,
5
4
  COINLIST_BASE_URL,
6
5
  HEADER_USER_AGENT,
@@ -13,6 +12,7 @@ import {
13
12
  VERIFY_IDENTITY_SOURCE_OF_FUNDS_PATH,
14
13
  VERIFY_IDENTITY_VERIFIED_PATH,
15
14
  WALLET_PATH,
15
+ createKycToken,
16
16
  createParticipation,
17
17
  fetchOfferDetails,
18
18
  fetchOfferRequirements,
@@ -21,22 +21,38 @@ import {
21
21
  fetchParticipation,
22
22
  fetchParticipations,
23
23
  fetchParticipationsPage,
24
- fetchRequirementStatuses
25
- } from "../chunk-N3WBC2VS.js";
24
+ fetchPii,
25
+ fetchRequirementStatuses,
26
+ submitDocument
27
+ } from "../chunk-MKCOK3DF.js";
26
28
  import {
27
29
  AuthorizationCode,
28
- CodeVerifier
29
- } from "../chunk-CRACFEJ4.js";
30
+ CodeVerifier,
31
+ ERC20_ABI,
32
+ SUPERSTATE_SWAP_ABI,
33
+ SWAP_POLL_INTERVAL_MS,
34
+ SwapQuote,
35
+ TOKEN_REGISTRY,
36
+ computePrice,
37
+ computeSlip,
38
+ decodeSwappedOutputAmount,
39
+ generatePKCEParams,
40
+ isStopped,
41
+ shortenAddress
42
+ } from "../chunk-I5YTJ5SL.js";
30
43
  import {
44
+ Attributes,
45
+ BlockchainAmount,
46
+ KycToken,
31
47
  NotAuthenticatedError,
32
- arrayBufferToBase64Url,
33
- generateSecureRandomBase64Url,
48
+ OfferOptionAddressId,
49
+ SwapNamespaceImpl,
50
+ connectExternalWallet,
51
+ createWalletOwnershipChallenge,
34
52
  getUUIDv4,
35
- sha256
36
- } from "../chunk-V6WO67RO.js";
37
-
38
- // src/client/index.ts
39
- import "./styles.css";
53
+ listOptionAddresses,
54
+ removeOptionAddress
55
+ } from "../chunk-Z2HAA2TI.js";
40
56
 
41
57
  // src/client/CoinListProvider.tsx
42
58
  import {
@@ -93,6 +109,286 @@ var ApiClient = class {
93
109
  }
94
110
  };
95
111
 
112
+ // src/client/core/blockchain/wallet-error.ts
113
+ import {
114
+ ContractFunctionRevertedError,
115
+ InsufficientFundsError,
116
+ UserRejectedRequestError,
117
+ WaitForTransactionReceiptTimeoutError
118
+ } from "viem";
119
+ function classifyWalletError(error, ctx) {
120
+ if (error instanceof UserRejectedRequestError) {
121
+ return { type: "user_rejected" };
122
+ }
123
+ if (error instanceof InsufficientFundsError) {
124
+ return { type: "insufficient_funds" };
125
+ }
126
+ if (error instanceof ContractFunctionRevertedError) {
127
+ return { type: "contract_reverted", reason: error.reason ?? error.message };
128
+ }
129
+ if (error instanceof WaitForTransactionReceiptTimeoutError && ctx?.hash) {
130
+ return { type: "timeout", hash: ctx.hash };
131
+ }
132
+ return { type: "unknown", cause: error };
133
+ }
134
+
135
+ // src/client/core/blockchain/swap-flows.ts
136
+ async function executeSwap(params) {
137
+ const {
138
+ swap,
139
+ wallet,
140
+ contractAddress,
141
+ chain,
142
+ inputTokenAddress,
143
+ quote,
144
+ slippageBps,
145
+ onProgress
146
+ } = params;
147
+ const emit = (phase) => onProgress?.(phase);
148
+ const inputAmount = BlockchainAmount.add(
149
+ quote.inputTokenAmount,
150
+ quote.fee
151
+ ).raw;
152
+ const minOut = computeSlip(quote.outputTokenAmount, slippageBps).raw;
153
+ emit("checking-status");
154
+ let status;
155
+ try {
156
+ status = await swap.getStatus({ contractAddress, chain });
157
+ } catch {
158
+ return { type: "error", error: { step: "status-check" } };
159
+ }
160
+ if (isStopped(status)) {
161
+ return { type: "error", error: { step: "swap-stopped" } };
162
+ }
163
+ emit("checking-allowance");
164
+ let allowance;
165
+ try {
166
+ allowance = await swap.getTokenAllowance({
167
+ tokenAddress: inputTokenAddress,
168
+ owner: wallet.address,
169
+ spender: contractAddress,
170
+ chain
171
+ });
172
+ } catch {
173
+ return { type: "error", error: { step: "allowance-check" } };
174
+ }
175
+ if (allowance.allowance < inputAmount) {
176
+ if (allowance.allowance > 0n) {
177
+ const reset = await submitApproval({
178
+ wallet,
179
+ tokenAddress: inputTokenAddress,
180
+ spender: contractAddress,
181
+ chain,
182
+ value: 0n,
183
+ pending: "resetting-allowance",
184
+ confirming: "confirming-allowance-reset",
185
+ emit
186
+ });
187
+ if (reset.type === "error") return { type: "error", error: reset.error };
188
+ }
189
+ const approve = await submitApproval({
190
+ wallet,
191
+ tokenAddress: inputTokenAddress,
192
+ spender: contractAddress,
193
+ chain,
194
+ value: inputAmount,
195
+ pending: "approving",
196
+ confirming: "confirming-approval",
197
+ emit
198
+ });
199
+ if (approve.type === "error")
200
+ return { type: "error", error: approve.error };
201
+ }
202
+ emit("swapping");
203
+ let swapTxHash;
204
+ try {
205
+ swapTxHash = await wallet.writeContract({
206
+ abi: SUPERSTATE_SWAP_ABI,
207
+ address: contractAddress,
208
+ functionName: "swap",
209
+ args: [inputTokenAddress, inputAmount, minOut],
210
+ chain
211
+ });
212
+ } catch (error) {
213
+ return {
214
+ type: "error",
215
+ error: { step: "swap", cause: classifyWalletError(error) }
216
+ };
217
+ }
218
+ emit("confirming-swap");
219
+ let receipt;
220
+ try {
221
+ receipt = await wallet.awaitTx(swapTxHash, chain);
222
+ } catch (error) {
223
+ return {
224
+ type: "error",
225
+ error: {
226
+ step: "swap",
227
+ cause: classifyWalletError(error, { hash: swapTxHash })
228
+ }
229
+ };
230
+ }
231
+ if (receipt.status !== "success") {
232
+ return { type: "error", error: { step: "swap-reverted" } };
233
+ }
234
+ const outputAmount = decodeSwappedOutputAmount(receipt, quote.outputTokenAmount.decimals) ?? quote.outputTokenAmount;
235
+ const finalQuote = { ...quote, outputTokenAmount: outputAmount };
236
+ return {
237
+ type: "success",
238
+ swapTxHash,
239
+ inputAmount: finalQuote.inputTokenAmount,
240
+ fee: finalQuote.fee,
241
+ outputAmount,
242
+ pricePerShare: computePrice(finalQuote),
243
+ recipientAddress: wallet.address
244
+ };
245
+ }
246
+ async function submitApproval(args) {
247
+ const {
248
+ wallet,
249
+ tokenAddress,
250
+ spender,
251
+ chain,
252
+ value,
253
+ pending,
254
+ confirming,
255
+ emit
256
+ } = args;
257
+ emit(pending);
258
+ let hash;
259
+ try {
260
+ hash = await wallet.writeContract({
261
+ abi: ERC20_ABI,
262
+ address: tokenAddress,
263
+ functionName: "approve",
264
+ args: [spender, value],
265
+ chain
266
+ });
267
+ } catch (error) {
268
+ return {
269
+ type: "error",
270
+ error: { step: "approval", cause: classifyWalletError(error) }
271
+ };
272
+ }
273
+ emit(confirming);
274
+ let receipt;
275
+ try {
276
+ receipt = await wallet.awaitTx(hash, chain);
277
+ } catch (error) {
278
+ return {
279
+ type: "error",
280
+ error: { step: "approval", cause: classifyWalletError(error, { hash }) }
281
+ };
282
+ }
283
+ if (receipt.status !== "success") {
284
+ return { type: "error", error: { step: "approval-reverted" } };
285
+ }
286
+ return { type: "ok" };
287
+ }
288
+ async function authorizeWallet(params) {
289
+ const { swap, wallet, offerId, contractAddress, chain, onProgress } = params;
290
+ const emit = (phase) => onProgress?.(phase);
291
+ const walletAddress = wallet.address;
292
+ emit("checking-authorization");
293
+ let auth;
294
+ try {
295
+ auth = await swap.getAuthorization({
296
+ walletAddress,
297
+ contractAddress,
298
+ chain
299
+ });
300
+ } catch {
301
+ return { type: "error", error: { step: "authorization-check" } };
302
+ }
303
+ if (auth.authorized) return { type: "success" };
304
+ emit("requesting-challenge");
305
+ let challenge;
306
+ try {
307
+ challenge = await swap.requestWalletOwnershipChallenge({
308
+ walletAddress,
309
+ chain,
310
+ challengeType: "plain"
311
+ });
312
+ } catch {
313
+ return { type: "error", error: { step: "challenge-request" } };
314
+ }
315
+ emit("signing-message");
316
+ let signature;
317
+ try {
318
+ signature = await wallet.signMessage(challenge.message);
319
+ } catch (error) {
320
+ return {
321
+ type: "error",
322
+ error: { step: "signing", cause: classifyWalletError(error) }
323
+ };
324
+ }
325
+ emit("submitting-signature");
326
+ let allow;
327
+ try {
328
+ allow = await swap.allowWallet({
329
+ offerId,
330
+ walletAddress,
331
+ chain,
332
+ signature
333
+ });
334
+ } catch {
335
+ return { type: "error", error: { step: "allow-wallet" } };
336
+ }
337
+ if (allow.action === "broadcast_transaction") {
338
+ emit("broadcasting-transaction");
339
+ let hash;
340
+ try {
341
+ hash = await wallet.broadcastRawTx({
342
+ to: allow.to,
343
+ data: allow.data,
344
+ chain
345
+ });
346
+ } catch (error) {
347
+ return {
348
+ type: "error",
349
+ error: { step: "broadcast", cause: classifyWalletError(error) }
350
+ };
351
+ }
352
+ emit("awaiting-confirmation");
353
+ try {
354
+ await wallet.awaitTx(hash, chain);
355
+ } catch (error) {
356
+ return {
357
+ type: "error",
358
+ error: {
359
+ step: "broadcast",
360
+ cause: classifyWalletError(error, { hash })
361
+ }
362
+ };
363
+ }
364
+ }
365
+ emit("verifying-authorization");
366
+ let reCheck;
367
+ try {
368
+ reCheck = await swap.getAuthorization({
369
+ walletAddress,
370
+ contractAddress,
371
+ chain
372
+ });
373
+ } catch {
374
+ return { type: "error", error: { step: "authorization-check" } };
375
+ }
376
+ if (!reCheck.authorized) {
377
+ return { type: "error", error: { step: "not-authorized" } };
378
+ }
379
+ return { type: "success" };
380
+ }
381
+
382
+ // src/client/core/client-swap-namespace.ts
383
+ var ClientSwapNamespaceImpl = class extends SwapNamespaceImpl {
384
+ executeSwap(params) {
385
+ return executeSwap({ ...params, swap: this });
386
+ }
387
+ authorizeWallet(params) {
388
+ return authorizeWallet({ ...params, swap: this });
389
+ }
390
+ };
391
+
96
392
  // src/client/core/coinlist-client.ts
97
393
  var OAUTH_STATE_KEY = "coinlist.oauth_state";
98
394
  var OAUTH_CODE_VERIFIER_KEY = "coinlist.oauth_code_verifier";
@@ -108,6 +404,10 @@ var CoinListClientImpl = class {
108
404
  },
109
405
  this.fetchAccessToken.bind(this)
110
406
  );
407
+ this.swap = new ClientSwapNamespaceImpl({
408
+ api: this.api,
409
+ ensureUserAuthenticated: async () => this.ensureAuthenticated()
410
+ });
111
411
  }
112
412
  async init() {
113
413
  await this.fetchAccessToken(true);
@@ -131,19 +431,16 @@ var CoinListClientImpl = class {
131
431
  return this._accessToken !== null ? "logged-in" : "logged-out";
132
432
  }
133
433
  async startOAuth() {
134
- const state = generateSecureRandomBase64Url(32);
135
- const codeVerifier = generateSecureRandomBase64Url(32);
136
- const codeChallengeRaw = await sha256(codeVerifier);
137
- const codeChallenge = arrayBufferToBase64Url(codeChallengeRaw, false);
138
- sessionStorage.setItem(OAUTH_STATE_KEY, state);
139
- sessionStorage.setItem(OAUTH_CODE_VERIFIER_KEY, codeVerifier);
434
+ const pkceParams = await generatePKCEParams(this._config);
435
+ sessionStorage.setItem(OAUTH_STATE_KEY, pkceParams.state);
436
+ sessionStorage.setItem(OAUTH_CODE_VERIFIER_KEY, pkceParams.codeVerifier);
140
437
  const params = new URLSearchParams({
141
- client_id: this._config.clientId,
142
- response_type: "code",
143
- redirect_uri: this._config.redirectUri,
144
- code_challenge: codeChallenge,
145
- code_challenge_method: "S256",
146
- state
438
+ client_id: pkceParams.clientId,
439
+ response_type: pkceParams.responseType,
440
+ redirect_uri: pkceParams.redirectUri,
441
+ code_challenge: pkceParams.codeChallenge,
442
+ code_challenge_method: pkceParams.codeChallengeMethod,
443
+ state: pkceParams.state
147
444
  });
148
445
  const baseUrl = this._config.coinlistBaseUrl ?? COINLIST_BASE_URL;
149
446
  const url = `${baseUrl}${OAUTH_PAGE_PATH}?${params.toString()}`;
@@ -182,15 +479,15 @@ var CoinListClientImpl = class {
182
479
  }
183
480
  async fetchOffers() {
184
481
  this.ensureAuthenticated();
185
- return fetchOffers(this.api);
482
+ return fetchOffers(this.api, void 0);
186
483
  }
187
484
  async fetchOffersPage(params) {
188
485
  this.ensureAuthenticated();
189
- return fetchOffersPage(this.api, params);
486
+ return fetchOffersPage(this.api, params, void 0);
190
487
  }
191
488
  async fetchOfferDetails(id) {
192
489
  this.ensureAuthenticated();
193
- return fetchOfferDetails(this.api, id);
490
+ return fetchOfferDetails(this.api, id, void 0);
194
491
  }
195
492
  async fetchParticipations(offerId) {
196
493
  this.ensureAuthenticated();
@@ -208,14 +505,46 @@ var CoinListClientImpl = class {
208
505
  this.ensureAuthenticated();
209
506
  return createParticipation(this.api, params);
210
507
  }
508
+ async createWalletOwnershipChallenge(params) {
509
+ this.ensureAuthenticated();
510
+ return createWalletOwnershipChallenge(this.api, params);
511
+ }
512
+ async connectExternalWallet(offerId, params) {
513
+ this.ensureAuthenticated();
514
+ return connectExternalWallet(this.api, offerId, params);
515
+ }
516
+ async listOptionAddresses(offerId, offerOptionId) {
517
+ this.ensureAuthenticated();
518
+ return listOptionAddresses(
519
+ this.api,
520
+ offerId,
521
+ offerOptionId
522
+ );
523
+ }
524
+ async removeOptionAddress(offerId, addressId) {
525
+ this.ensureAuthenticated();
526
+ return removeOptionAddress(this.api, offerId, addressId);
527
+ }
211
528
  async fetchOfferRequirements(offerId) {
212
529
  this.ensureAuthenticated();
213
- return fetchOfferRequirements(this.api, offerId);
530
+ return fetchOfferRequirements(this.api, offerId, void 0);
214
531
  }
215
532
  async fetchRequirementStatuses(offerId) {
216
533
  this.ensureAuthenticated();
217
534
  return fetchRequirementStatuses(this.api, offerId);
218
535
  }
536
+ async fetchPii() {
537
+ this.ensureAuthenticated();
538
+ return fetchPii(this.api);
539
+ }
540
+ async submitDocument(documentType, fields) {
541
+ this.ensureAuthenticated();
542
+ return submitDocument(this.api, documentType, fields);
543
+ }
544
+ async createKycToken(levelName, reset) {
545
+ this.ensureAuthenticated();
546
+ return createKycToken(this.api, levelName, reset);
547
+ }
219
548
  handleRequirement(requirement) {
220
549
  const baseUrl = this._config.coinlistBaseUrl ?? COINLIST_BASE_URL;
221
550
  let path;
@@ -242,6 +571,9 @@ var CoinListClientImpl = class {
242
571
  case "jurisdiction":
243
572
  path = null;
244
573
  break;
574
+ case "document":
575
+ path = null;
576
+ break;
245
577
  default: {
246
578
  const _exhaustive = requirement.type;
247
579
  return;
@@ -269,7 +601,10 @@ function createCoinListClient(config) {
269
601
  // src/client/CoinListProvider.tsx
270
602
  import { jsx } from "react/jsx-runtime";
271
603
  var CoinListContext = createContext(null);
272
- function CoinListProvider({ config, children }) {
604
+ function CoinListContextProvider({
605
+ config,
606
+ children
607
+ }) {
273
608
  const [isReady, setIsReady] = useState(false);
274
609
  const coinlist = useMemo(() => createCoinListClient(config), [config]);
275
610
  useEffect(() => {
@@ -294,23 +629,69 @@ function CoinListProvider({ config, children }) {
294
629
  const value = useMemo(() => ({ coinlist, isReady }), [coinlist, isReady]);
295
630
  return /* @__PURE__ */ jsx(CoinListContext.Provider, { value, children });
296
631
  }
632
+ var CoinListProvider = CoinListContextProvider;
633
+
634
+ // src/client/CoinListStyleScope.tsx
635
+ import { createContext as createContext2, useContext } from "react";
636
+
637
+ // src/client/generated/sdk-styles.gen.ts
638
+ var sdkStyles = '/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-ease:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0}}}@layer theme{:root,:host{--clcosdk-font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--clcosdk-color-red-400:oklch(70.4% .191 22.216);--clcosdk-color-red-500:oklch(63.7% .237 25.331);--clcosdk-color-amber-500:oklch(76.9% .188 70.08);--clcosdk-color-gray-400:oklch(70.7% .022 261.325);--clcosdk-color-gray-500:oklch(55.1% .027 264.364);--clcosdk-color-black:#000;--clcosdk-spacing:.25rem;--clcosdk-container-md:28rem;--clcosdk-text-xs:.75rem;--clcosdk-text-xs--line-height:calc(1 / .75);--clcosdk-text-sm:.875rem;--clcosdk-text-sm--line-height:calc(1.25 / .875);--clcosdk-text-base:1rem;--clcosdk-text-base--line-height:calc(1.5 / 1);--clcosdk-font-weight-normal:400;--clcosdk-font-weight-medium:500;--clcosdk-font-weight-semibold:600;--clcosdk-radius-md:.375rem;--clcosdk-radius-lg:.5rem;--clcosdk-radius-2xl:1rem;--clcosdk-ease-in-out:cubic-bezier(.4, 0, .2, 1);--clcosdk-animate-spin:spin 1s linear infinite;--clcosdk-animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--clcosdk-color-brand-primary:var(--color-brand-primary)}}@layer base{.clco-sdk-root{--color-brand-primary:#171717;--color-brand-primary-hover:#000;--color-brand-primary-active:#262626;--color-accent:#2b3a67;--color-accent-hover:#243057;--color-accent-active:#1d2647;--color-accent-focus:#c7d2fe;--color-accent-subtle:#eef1f8;--color-text-primary:#171717;--color-text-secondary:#8c8c8c;--color-text-disabled:#d1d1d1;--color-text-inverse:#fff;--color-background:#fff;--color-surface:#fafafa;--color-section:#f3f3f3;--color-border:#e5e5e5;--color-divider:#ececec;--color-overlay:#00000080;--color-positive:#15803d;--color-positive-subtle:#dcfce7;--color-negative:#dc2626;--color-negative-subtle:#fee2e2;--color-success:#2c8c5e;--color-success-subtle:#e6f4ec;--color-success-border:#cfe9db;--color-warning:#e08a1a;--color-warning-subtle:#fff7ed;--color-warning-border:#fdead7;--color-error:#b42318;--color-error-subtle:#feeceb;--color-error-border:#fbd5d2}@media (prefers-color-scheme:dark){.clco-sdk-root:not(:where(.light,.light *)){--color-brand-primary:#f4f4f5;--color-brand-primary-hover:#fff;--color-brand-primary-active:#e4e4e7;--color-accent:#6f82c9;--color-accent-hover:#8799e4;--color-accent-active:#5b6fcf;--color-accent-focus:#6f82c959;--color-accent-subtle:#6f82c91f;--color-text-primary:#f4f4f5;--color-text-secondary:#a1a1aa;--color-text-disabled:#71717a;--color-text-inverse:#171717;--color-background:#0f1115;--color-surface:#161a20;--color-section:#20242c;--color-border:#2a2e35;--color-divider:#323741;--color-overlay:#000000b3;--color-positive:#22c55e;--color-positive-subtle:#22c55e1f;--color-negative:#ef4444;--color-negative-subtle:#ef44441f;--color-success:#34d399;--color-success-subtle:#34d3991f;--color-success-border:#34d39940;--color-warning:#f59e0b;--color-warning-subtle:#f59e0b1f;--color-warning-border:#f59e0b40;--color-error:#f87171;--color-error-subtle:#f871711f;--color-error-border:#f8717140}}:where(.dark,.dark *) .clco-sdk-root{--color-brand-primary:#f4f4f5;--color-brand-primary-hover:#fff;--color-brand-primary-active:#e4e4e7;--color-accent:#6f82c9;--color-accent-hover:#8799e4;--color-accent-active:#5b6fcf;--color-accent-focus:#6f82c959;--color-accent-subtle:#6f82c91f;--color-text-primary:#f4f4f5;--color-text-secondary:#a1a1aa;--color-text-disabled:#71717a;--color-text-inverse:#171717;--color-background:#0f1115;--color-surface:#161a20;--color-section:#20242c;--color-border:#2a2e35;--color-divider:#323741;--color-overlay:#000000b3;--color-positive:#22c55e;--color-positive-subtle:#22c55e1f;--color-negative:#ef4444;--color-negative-subtle:#ef44441f;--color-success:#34d399;--color-success-subtle:#34d3991f;--color-success-border:#34d39940;--color-warning:#f59e0b;--color-warning-subtle:#f59e0b1f;--color-warning-border:#f59e0b40;--color-error:#f87171;--color-error-subtle:#f871711f;--color-error-border:#f8717140}.clco-sdk-root{color:var(--color-text-primary);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,sans-serif}.clco-sdk-root *,.clco-sdk-root :before,.clco-sdk-root :after{box-sizing:border-box}.clco-sdk-root button{appearance:none;cursor:pointer;font:inherit;color:inherit;background:0 0;border:none;padding:0}.clco-sdk-root a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}.clco-sdk-root ul,.clco-sdk-root ol{margin:0;padding:0;list-style:none}.clco-sdk-root img,.clco-sdk-root svg{max-width:100%;display:block}}@layer components;@layer utilities{.clcosdk\\:absolute{position:absolute}.clcosdk\\:fixed{position:fixed}.clcosdk\\:relative{position:relative}.clcosdk\\:inset-0{inset:calc(var(--clcosdk-spacing) * 0)}.clcosdk\\:top-4{top:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:right-4{right:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:z-50{z-index:50}.clcosdk\\:mt-4{margin-top:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:mb-6{margin-bottom:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:flex{display:flex}.clcosdk\\:grid{display:grid}.clcosdk\\:inline-flex{display:inline-flex}.clcosdk\\:h-3{height:calc(var(--clcosdk-spacing) * 3)}.clcosdk\\:h-4{height:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:h-5{height:calc(var(--clcosdk-spacing) * 5)}.clcosdk\\:h-6{height:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:h-9{height:calc(var(--clcosdk-spacing) * 9)}.clcosdk\\:h-11{height:calc(var(--clcosdk-spacing) * 11)}.clcosdk\\:h-12{height:calc(var(--clcosdk-spacing) * 12)}.clcosdk\\:h-14{height:calc(var(--clcosdk-spacing) * 14)}.clcosdk\\:h-28{height:calc(var(--clcosdk-spacing) * 28)}.clcosdk\\:h-52{height:calc(var(--clcosdk-spacing) * 52)}.clcosdk\\:h-\\[18px\\]{height:18px}.clcosdk\\:h-auto{height:auto}.clcosdk\\:h-full{height:100%}.clcosdk\\:max-h-\\[92vh\\]{max-height:92vh}.clcosdk\\:min-h-0{min-height:calc(var(--clcosdk-spacing) * 0)}.clcosdk\\:min-h-9{min-height:calc(var(--clcosdk-spacing) * 9)}.clcosdk\\:min-h-40{min-height:calc(var(--clcosdk-spacing) * 40)}.clcosdk\\:min-h-\\[2\\.4rem\\]{min-height:2.4rem}.clcosdk\\:w-4{width:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:w-5{width:calc(var(--clcosdk-spacing) * 5)}.clcosdk\\:w-6{width:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:w-12{width:calc(var(--clcosdk-spacing) * 12)}.clcosdk\\:w-16{width:calc(var(--clcosdk-spacing) * 16)}.clcosdk\\:w-28{width:calc(var(--clcosdk-spacing) * 28)}.clcosdk\\:w-48{width:calc(var(--clcosdk-spacing) * 48)}.clcosdk\\:w-80{width:calc(var(--clcosdk-spacing) * 80)}.clcosdk\\:w-96{width:calc(var(--clcosdk-spacing) * 96)}.clcosdk\\:w-\\[16px\\]{width:16px}.clcosdk\\:w-\\[448px\\]{width:448px}.clcosdk\\:w-auto{width:auto}.clcosdk\\:w-full{width:100%}.clcosdk\\:max-w-\\[22rem\\]{max-width:22rem}.clcosdk\\:max-w-\\[28rem\\]{max-width:28rem}.clcosdk\\:max-w-\\[30rem\\]{max-width:30rem}.clcosdk\\:max-w-\\[760px\\]{max-width:760px}.clcosdk\\:max-w-\\[1200px\\]{max-width:1200px}.clcosdk\\:max-w-md{max-width:var(--clcosdk-container-md)}.clcosdk\\:shrink-0{flex-shrink:0}.clcosdk\\:rotate-90{rotate:90deg}.clcosdk\\:animate-pulse{animation:var(--clcosdk-animate-pulse)}.clcosdk\\:animate-spin{animation:var(--clcosdk-animate-spin)}.clcosdk\\:cursor-pointer{cursor:pointer}.clcosdk\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.clcosdk\\:grid-rows-\\[0fr\\]{grid-template-rows:0fr}.clcosdk\\:grid-rows-\\[1fr\\]{grid-template-rows:1fr}.clcosdk\\:flex-col{flex-direction:column}.clcosdk\\:flex-wrap{flex-wrap:wrap}.clcosdk\\:items-center{align-items:center}.clcosdk\\:items-start{align-items:flex-start}.clcosdk\\:justify-between{justify-content:space-between}.clcosdk\\:justify-center{justify-content:center}.clcosdk\\:gap-1{gap:calc(var(--clcosdk-spacing) * 1)}.clcosdk\\:gap-2{gap:calc(var(--clcosdk-spacing) * 2)}.clcosdk\\:gap-3{gap:calc(var(--clcosdk-spacing) * 3)}.clcosdk\\:gap-4{gap:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:gap-5{gap:calc(var(--clcosdk-spacing) * 5)}.clcosdk\\:gap-6{gap:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:gap-8{gap:calc(var(--clcosdk-spacing) * 8)}.clcosdk\\:gap-\\[5px\\]{gap:5px}:where(.clcosdk\\:space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--clcosdk-spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--clcosdk-spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.clcosdk\\:space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--clcosdk-spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--clcosdk-spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.clcosdk\\:divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.clcosdk\\:divide-border>:not(:last-child)){border-color:var(--color-border)}:where(.clcosdk\\:divide-divider>:not(:last-child)){border-color:var(--color-divider)}.clcosdk\\:self-start{align-self:flex-start}.clcosdk\\:overflow-hidden{overflow:hidden}.clcosdk\\:overflow-y-auto{overflow-y:auto}.clcosdk\\:rounded{border-radius:.25rem}.clcosdk\\:rounded-2xl{border-radius:var(--clcosdk-radius-2xl)}.clcosdk\\:rounded-btn{border-radius:12px}.clcosdk\\:rounded-card{border-radius:24px}.clcosdk\\:rounded-full{border-radius:3.40282e38px}.clcosdk\\:rounded-lg{border-radius:var(--clcosdk-radius-lg)}.clcosdk\\:rounded-md{border-radius:var(--clcosdk-radius-md)}.clcosdk\\:border{border-style:var(--tw-border-style);border-width:1px}.clcosdk\\:border-2{border-style:var(--tw-border-style);border-width:2px}.clcosdk\\:border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.clcosdk\\:border-dashed{--tw-border-style:dashed;border-style:dashed}.clcosdk\\:border-accent\\/50{border-color:var(--color-accent)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:border-accent\\/50{border-color:color-mix(in oklab, var(--color-accent) 50%, transparent)}}.clcosdk\\:border-accent\\/60{border-color:var(--color-accent)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:border-accent\\/60{border-color:color-mix(in oklab, var(--color-accent) 60%, transparent)}}.clcosdk\\:border-amber-500{border-color:var(--clcosdk-color-amber-500)}.clcosdk\\:border-border,.clcosdk\\:border-border\\/60{border-color:var(--color-border)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:border-border\\/60{border-color:color-mix(in oklab, var(--color-border) 60%, transparent)}}.clcosdk\\:border-current{border-color:currentColor}.clcosdk\\:border-divider{border-color:var(--color-divider)}.clcosdk\\:border-error-border{border-color:var(--color-error-border)}.clcosdk\\:border-success-border{border-color:var(--color-success-border)}.clcosdk\\:border-warning-border{border-color:var(--color-warning-border)}.clcosdk\\:border-t-transparent{border-top-color:#0000}.clcosdk\\:bg-accent\\/10{background-color:var(--color-accent)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:bg-accent\\/10{background-color:color-mix(in oklab, var(--color-accent) 10%, transparent)}}.clcosdk\\:bg-background{background-color:var(--color-background)}.clcosdk\\:bg-black\\/50{background-color:var(--clcosdk-color-black)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:bg-black\\/50{background-color:color-mix(in oklab, var(--clcosdk-color-black) 50%, transparent)}}.clcosdk\\:bg-brand-primary{background-color:var(--color-brand-primary)}.clcosdk\\:bg-error-subtle{background-color:var(--color-error-subtle)}.clcosdk\\:bg-overlay{background-color:var(--color-overlay)}.clcosdk\\:bg-section{background-color:var(--color-section)}.clcosdk\\:bg-success-subtle{background-color:var(--color-success-subtle)}.clcosdk\\:bg-surface{background-color:var(--color-surface)}.clcosdk\\:bg-transparent{background-color:#0000}.clcosdk\\:bg-warning-subtle{background-color:var(--color-warning-subtle)}.clcosdk\\:bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.clcosdk\\:bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.clcosdk\\:from-surface{--tw-gradient-from:var(--color-surface);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.clcosdk\\:from-transparent{--tw-gradient-from:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.clcosdk\\:via-section{--tw-gradient-via:var(--color-section);--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.clcosdk\\:via-transparent{--tw-gradient-via:transparent;--tw-gradient-via-stops:var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.clcosdk\\:to-background{--tw-gradient-to:var(--color-background);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.clcosdk\\:to-overlay\\/35{--tw-gradient-to:var(--color-overlay)}@supports (color:color-mix(in lab, red, red)){.clcosdk\\:to-overlay\\/35{--tw-gradient-to:color-mix(in oklab, var(--color-overlay) 35%, transparent)}}.clcosdk\\:to-overlay\\/35{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.clcosdk\\:object-cover{object-fit:cover}.clcosdk\\:p-0{padding:calc(var(--clcosdk-spacing) * 0)}.clcosdk\\:p-1\\.5{padding:calc(var(--clcosdk-spacing) * 1.5)}.clcosdk\\:p-3{padding:calc(var(--clcosdk-spacing) * 3)}.clcosdk\\:p-4{padding:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:p-6{padding:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:p-8{padding:calc(var(--clcosdk-spacing) * 8)}.clcosdk\\:px-2{padding-inline:calc(var(--clcosdk-spacing) * 2)}.clcosdk\\:px-3{padding-inline:calc(var(--clcosdk-spacing) * 3)}.clcosdk\\:px-4{padding-inline:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:px-5{padding-inline:calc(var(--clcosdk-spacing) * 5)}.clcosdk\\:px-6{padding-inline:calc(var(--clcosdk-spacing) * 6)}.clcosdk\\:py-2{padding-block:calc(var(--clcosdk-spacing) * 2)}.clcosdk\\:pt-4{padding-top:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:pt-14{padding-top:calc(var(--clcosdk-spacing) * 14)}.clcosdk\\:pb-4{padding-bottom:calc(var(--clcosdk-spacing) * 4)}.clcosdk\\:text-center{text-align:center}.clcosdk\\:text-left{text-align:left}.clcosdk\\:font-mono{font-family:var(--clcosdk-font-mono)}.clcosdk\\:text-base{font-size:var(--clcosdk-text-base);line-height:var(--tw-leading,var(--clcosdk-text-base--line-height))}.clcosdk\\:text-sm{font-size:var(--clcosdk-text-sm);line-height:var(--tw-leading,var(--clcosdk-text-sm--line-height))}.clcosdk\\:text-xs{font-size:var(--clcosdk-text-xs);line-height:var(--tw-leading,var(--clcosdk-text-xs--line-height))}.clcosdk\\:text-\\[11px\\]{font-size:11px}.clcosdk\\:text-\\[12px\\]{font-size:12px}.clcosdk\\:text-\\[14px\\]{font-size:14px}.clcosdk\\:text-\\[16px\\]{font-size:16px}.clcosdk\\:text-\\[18px\\]{font-size:18px}.clcosdk\\:text-\\[22px\\]{font-size:22px}.clcosdk\\:text-\\[28px\\]{font-size:28px}.clcosdk\\:text-\\[36px\\]{font-size:36px}.clcosdk\\:text-\\[48px\\]{font-size:48px}.clcosdk\\:text-\\[64px\\]{font-size:64px}.clcosdk\\:leading-\\[110\\%\\]{--tw-leading:110%;line-height:110%}.clcosdk\\:leading-\\[120\\%\\]{--tw-leading:120%;line-height:120%}.clcosdk\\:leading-\\[125\\%\\]{--tw-leading:125%;line-height:125%}.clcosdk\\:leading-\\[130\\%\\]{--tw-leading:130%;line-height:130%}.clcosdk\\:leading-\\[135\\%\\]{--tw-leading:135%;line-height:135%}.clcosdk\\:leading-\\[140\\%\\]{--tw-leading:140%;line-height:140%}.clcosdk\\:leading-\\[150\\%\\]{--tw-leading:150%;line-height:150%}.clcosdk\\:font-medium{--tw-font-weight:var(--clcosdk-font-weight-medium);font-weight:var(--clcosdk-font-weight-medium)}.clcosdk\\:font-normal{--tw-font-weight:var(--clcosdk-font-weight-normal);font-weight:var(--clcosdk-font-weight-normal)}.clcosdk\\:font-semibold{--tw-font-weight:var(--clcosdk-font-weight-semibold);font-weight:var(--clcosdk-font-weight-semibold)}.clcosdk\\:tracking-\\[0\\.12px\\]{--tw-tracking:.12px;letter-spacing:.12px}.clcosdk\\:tracking-\\[0\\.28px\\]{--tw-tracking:.28px;letter-spacing:.28px}.clcosdk\\:whitespace-nowrap{white-space:nowrap}.clcosdk\\:text-error{color:var(--color-error)}.clcosdk\\:text-success{color:var(--color-success)}.clcosdk\\:text-text-inverse{color:var(--color-text-inverse)}.clcosdk\\:text-text-primary{color:var(--color-text-primary)}.clcosdk\\:text-text-secondary{color:var(--color-text-secondary)}.clcosdk\\:text-warning{color:var(--color-warning)}.clcosdk\\:capitalize{text-transform:capitalize}.clcosdk\\:underline{text-decoration-line:underline}.clcosdk\\:underline-offset-2{text-underline-offset:2px}.clcosdk\\:underline-offset-4{text-underline-offset:4px}.clcosdk\\:opacity-60{opacity:.6}.clcosdk\\:shadow-\\[0_0_0_3px_rgba\\(59\\,130\\,246\\,0\\.2\\)\\]{--tw-shadow:0 0 0 3px var(--tw-shadow-color,#3b82f633);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:shadow-\\[0_0_0_3px_rgba\\(59\\,130\\,246\\,0\\.18\\)\\]{--tw-shadow:0 0 0 3px var(--tw-shadow-color,#3b82f62e);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:shadow-\\[0_0_0_3px_rgba\\(59\\,130\\,246\\,0\\.25\\)\\]{--tw-shadow:0 0 0 3px var(--tw-shadow-color,#3b82f640);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:shadow-\\[0_10px_24px_rgba\\(0\\,0\\,0\\,0\\.18\\)\\]{--tw-shadow:0 10px 24px var(--tw-shadow-color,#0000002e);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:shadow-\\[0_10px_24px_rgba\\(0\\,0\\,0\\,0\\.25\\)\\]{--tw-shadow:0 10px 24px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:shadow-card{--tw-shadow:0px 4px 6px -2px var(--tw-shadow-color,#0000000d), 0px 12px 24px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:transition-\\[grid-template-rows\\]{transition-property:grid-template-rows;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.clcosdk\\:transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.clcosdk\\:transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.clcosdk\\:transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.clcosdk\\:duration-200{--tw-duration:.2s;transition-duration:.2s}.clcosdk\\:duration-300{--tw-duration:.3s;transition-duration:.3s}.clcosdk\\:ease-in-out{--tw-ease:var(--clcosdk-ease-in-out);transition-timing-function:var(--clcosdk-ease-in-out)}@media (hover:hover){.clcosdk\\:hover\\:-translate-y-0\\.5:hover{--tw-translate-y:calc(var(--clcosdk-spacing) * -.5);translate:var(--tw-translate-x) var(--tw-translate-y)}.clcosdk\\:hover\\:border-divider:hover{border-color:var(--color-divider)}.clcosdk\\:hover\\:bg-\\[\\#fcdcd8\\]:hover{background-color:#fcdcd8}.clcosdk\\:hover\\:bg-border:hover{background-color:var(--color-border)}.clcosdk\\:hover\\:bg-brand-primary:hover{background-color:var(--color-brand-primary)}.clcosdk\\:hover\\:bg-brand-primary-hover:hover{background-color:var(--color-brand-primary-hover)}.clcosdk\\:hover\\:bg-section:hover{background-color:var(--color-section)}.clcosdk\\:hover\\:bg-surface:hover{background-color:var(--color-surface)}.clcosdk\\:hover\\:text-\\[\\#8b1a13\\]:hover{color:#8b1a13}.clcosdk\\:hover\\:text-text-primary:hover{color:var(--color-text-primary)}.clcosdk\\:hover\\:underline:hover{text-decoration-line:underline}.clcosdk\\:hover\\:shadow-\\[0px_14px_32px_-10px_rgba\\(0\\,0\\,0\\,0\\.35\\)\\]:hover{--tw-shadow:0px 14px 32px -10px var(--tw-shadow-color,#00000059);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}}.clcosdk\\:focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.clcosdk\\:focus-visible\\:ring-accent-focus:focus-visible{--tw-ring-color:var(--color-accent-focus)}.clcosdk\\:focus-visible\\:ring-gray-400:focus-visible{--tw-ring-color:var(--clcosdk-color-gray-400)}.clcosdk\\:focus-visible\\:ring-gray-500:focus-visible{--tw-ring-color:var(--clcosdk-color-gray-500)}.clcosdk\\:focus-visible\\:ring-red-400:focus-visible{--tw-ring-color:var(--clcosdk-color-red-400)}.clcosdk\\:focus-visible\\:ring-red-500:focus-visible{--tw-ring-color:var(--clcosdk-color-red-500)}.clcosdk\\:focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.clcosdk\\:focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.clcosdk\\:active\\:bg-\\[\\#d9d9d9\\]:active{background-color:#d9d9d9}.clcosdk\\:active\\:bg-\\[\\#f9c9c4\\]:active{background-color:#f9c9c4}.clcosdk\\:active\\:bg-border:active{background-color:var(--color-border)}.clcosdk\\:active\\:bg-brand-primary-active:active{background-color:var(--color-brand-primary-active)}.clcosdk\\:active\\:text-\\[\\#6d1510\\]:active{color:#6d1510}.clcosdk\\:disabled\\:pointer-events-none:disabled{pointer-events:none}.clcosdk\\:disabled\\:opacity-40:disabled{opacity:.4}@media (prefers-color-scheme:dark){@media (hover:hover){.clcosdk\\:dark\\:hover\\:bg-\\[rgba\\(248\\,113\\,113\\,0\\.20\\)\\]:not(:where(.light,.light *)):hover{background-color:#f8717133}}}@media (hover:hover){.clcosdk\\:dark\\:hover\\:bg-\\[rgba\\(248\\,113\\,113\\,0\\.20\\)\\]:where(.dark,.dark *):hover{background-color:#f8717133}}@media (prefers-color-scheme:dark){@media (hover:hover){.clcosdk\\:dark\\:hover\\:text-\\[\\#fca5a5\\]:not(:where(.light,.light *)):hover{color:#fca5a5}}}@media (hover:hover){.clcosdk\\:dark\\:hover\\:text-\\[\\#fca5a5\\]:where(.dark,.dark *):hover{color:#fca5a5}}@media (prefers-color-scheme:dark){.clcosdk\\:dark\\:active\\:bg-\\[rgba\\(248\\,113\\,113\\,0\\.30\\)\\]:not(:where(.light,.light *)):active{background-color:#f871714d}}.clcosdk\\:dark\\:active\\:bg-\\[rgba\\(248\\,113\\,113\\,0\\.30\\)\\]:where(.dark,.dark *):active{background-color:#f871714d}@media (prefers-color-scheme:dark){.clcosdk\\:dark\\:active\\:bg-divider:not(:where(.light,.light *)):active{background-color:var(--color-divider)}}.clcosdk\\:dark\\:active\\:bg-divider:where(.dark,.dark *):active{background-color:var(--color-divider)}@media (prefers-color-scheme:dark){.clcosdk\\:dark\\:active\\:text-\\[\\#f87171\\]:not(:where(.light,.light *)):active{color:#f87171}}.clcosdk\\:dark\\:active\\:text-\\[\\#f87171\\]:where(.dark,.dark *):active{color:#f87171}.clcosdk\\:\\[\\&_circle\\]\\:fill-error-subtle circle{fill:var(--color-error-subtle)}.clcosdk\\:\\[\\&_circle\\]\\:fill-success-subtle circle{fill:var(--color-success-subtle)}.clcosdk\\:\\[\\&_circle\\]\\:fill-warning-subtle circle{fill:var(--color-warning-subtle)}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}';
639
+
640
+ // src/client/CoinListStyleScope.tsx
641
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
642
+ var ScopeContext = createContext2(false);
643
+ function CoinListStyleScope({ children }) {
644
+ const alreadyScoped = useContext(ScopeContext);
645
+ if (alreadyScoped) {
646
+ return /* @__PURE__ */ jsx2(Fragment, { children });
647
+ }
648
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
649
+ /* @__PURE__ */ jsx2("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }),
650
+ /* @__PURE__ */ jsx2(
651
+ "link",
652
+ {
653
+ rel: "preconnect",
654
+ href: "https://fonts.gstatic.com",
655
+ crossOrigin: "anonymous"
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsx2(
659
+ "link",
660
+ {
661
+ rel: "stylesheet",
662
+ href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
663
+ }
664
+ ),
665
+ /* @__PURE__ */ jsxs("div", { className: "clco-sdk-root", style: { display: "contents" }, children: [
666
+ /* @__PURE__ */ jsx2(
667
+ "style",
668
+ {
669
+ href: "clco-sdk-styles",
670
+ precedence: "default",
671
+ dangerouslySetInnerHTML: { __html: sdkStyles }
672
+ }
673
+ ),
674
+ /* @__PURE__ */ jsx2(ScopeContext.Provider, { value: true, children })
675
+ ] })
676
+ ] });
677
+ }
297
678
 
298
679
  // src/client/components/design-system/AlertBanner.tsx
299
680
  import { CircleCheck, Info } from "lucide-react";
300
681
 
301
682
  // src/client/components/design-system/tokens/typography.ts
302
683
  var typeClasses = {
303
- hero: "text-[64px] leading-[110%] font-semibold",
304
- display: "text-[48px] leading-[110%] font-semibold",
305
- h1: "text-[36px] leading-[120%] font-semibold",
306
- h2: "text-[28px] leading-[125%] font-semibold",
307
- h3: "text-[22px] leading-[130%] font-semibold",
308
- h4: "text-[18px] leading-[135%] font-medium",
309
- body: "text-[16px] leading-[150%] font-normal",
310
- subhead: "text-[14px] leading-[150%] font-normal",
311
- caption: "text-[12px] leading-[140%] font-normal",
312
- label: "text-[14px] leading-[140%] font-medium",
313
- data: "text-[14px] leading-[140%] font-normal"
684
+ hero: "clcosdk:text-[64px] clcosdk:leading-[110%] clcosdk:font-semibold",
685
+ display: "clcosdk:text-[48px] clcosdk:leading-[110%] clcosdk:font-semibold",
686
+ h1: "clcosdk:text-[36px] clcosdk:leading-[120%] clcosdk:font-semibold",
687
+ h2: "clcosdk:text-[28px] clcosdk:leading-[125%] clcosdk:font-semibold",
688
+ h3: "clcosdk:text-[22px] clcosdk:leading-[130%] clcosdk:font-semibold",
689
+ h4: "clcosdk:text-[18px] clcosdk:leading-[135%] clcosdk:font-medium",
690
+ body: "clcosdk:text-[16px] clcosdk:leading-[150%] clcosdk:font-normal",
691
+ subhead: "clcosdk:text-[14px] clcosdk:leading-[150%] clcosdk:font-normal",
692
+ caption: "clcosdk:text-[12px] clcosdk:leading-[140%] clcosdk:font-normal",
693
+ label: "clcosdk:text-[14px] clcosdk:leading-[140%] clcosdk:font-medium",
694
+ data: "clcosdk:text-[14px] clcosdk:leading-[140%] clcosdk:font-normal"
314
695
  };
315
696
 
316
697
  // src/client/components/design-system/utils.ts
@@ -320,26 +701,26 @@ function cn(...inputs) {
320
701
  }
321
702
 
322
703
  // src/client/components/design-system/AlertBanner.tsx
323
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
704
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
324
705
  var variantConfig = {
325
706
  info: {
326
- container: "bg-surface border-border",
327
- text: "text-text-secondary",
707
+ container: "clcosdk:bg-surface clcosdk:border-border",
708
+ text: "clcosdk:text-text-secondary",
328
709
  Icon: Info
329
710
  },
330
711
  success: {
331
- container: "bg-success-subtle border-success-border",
332
- text: "text-success",
712
+ container: "clcosdk:bg-success-subtle clcosdk:border-success-border",
713
+ text: "clcosdk:text-success",
333
714
  Icon: CircleCheck
334
715
  },
335
716
  warning: {
336
- container: "bg-warning-subtle border-warning-border",
337
- text: "text-warning",
717
+ container: "clcosdk:bg-warning-subtle clcosdk:border-warning-border",
718
+ text: "clcosdk:text-warning",
338
719
  Icon: Info
339
720
  },
340
721
  error: {
341
- container: "bg-error-subtle border-error-border",
342
- text: "text-error",
722
+ container: "clcosdk:bg-error-subtle clcosdk:border-error-border",
723
+ text: "clcosdk:text-error",
343
724
  Icon: Info
344
725
  }
345
726
  };
@@ -350,18 +731,24 @@ function AlertBanner({
350
731
  className
351
732
  }) {
352
733
  const { container, text, Icon } = variantConfig[variant];
353
- return /* @__PURE__ */ jsxs(
734
+ return /* @__PURE__ */ jsxs2(
354
735
  "div",
355
736
  {
356
737
  className: cn(
357
- "flex items-center rounded-btn border p-3",
358
- icon && "gap-2",
738
+ "clcosdk:flex clcosdk:items-center clcosdk:rounded-btn clcosdk:border clcosdk:p-3",
739
+ icon && "clcosdk:gap-2",
359
740
  container,
360
741
  className
361
742
  ),
362
743
  children: [
363
- icon && /* @__PURE__ */ jsx2(Icon, { className: cn("h-4 w-4 shrink-0", text), "aria-hidden": "true" }),
364
- /* @__PURE__ */ jsx2("p", { className: cn(typeClasses.subhead, text), children: content })
744
+ icon && /* @__PURE__ */ jsx3(
745
+ Icon,
746
+ {
747
+ className: cn("clcosdk:h-4 clcosdk:w-4 clcosdk:shrink-0", text),
748
+ "aria-hidden": "true"
749
+ }
750
+ ),
751
+ /* @__PURE__ */ jsx3("p", { className: cn(typeClasses.subhead, text), children: content })
365
752
  ]
366
753
  }
367
754
  );
@@ -369,31 +756,31 @@ function AlertBanner({
369
756
 
370
757
  // src/client/components/design-system/CloseButton.tsx
371
758
  import { X } from "lucide-react";
372
- import { jsx as jsx3 } from "react/jsx-runtime";
759
+ import { jsx as jsx4 } from "react/jsx-runtime";
373
760
  function CloseButton({
374
761
  onClick,
375
762
  "aria-label": ariaLabel = "Close",
376
763
  className
377
764
  }) {
378
- return /* @__PURE__ */ jsx3(
765
+ return /* @__PURE__ */ jsx4(
379
766
  "button",
380
767
  {
381
768
  type: "button",
382
769
  onClick,
383
770
  "aria-label": ariaLabel,
384
771
  className: cn(
385
- "cursor-pointer rounded-full p-1.5 text-text-secondary transition-colors hover:bg-surface hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400 focus-visible:ring-offset-2",
772
+ "clcosdk:cursor-pointer clcosdk:rounded-full clcosdk:p-1.5 clcosdk:text-text-secondary clcosdk:transition-colors clcosdk:hover:bg-surface clcosdk:hover:text-text-primary clcosdk:focus-visible:outline-none clcosdk:focus-visible:ring-2 clcosdk:focus-visible:ring-gray-400 clcosdk:focus-visible:ring-offset-2",
386
773
  className
387
774
  ),
388
- children: /* @__PURE__ */ jsx3(X, { className: "h-5 w-5" })
775
+ children: /* @__PURE__ */ jsx4(X, { className: "clcosdk:h-5 clcosdk:w-5" })
389
776
  }
390
777
  );
391
778
  }
392
779
 
393
780
  // src/client/components/design-system/logos/CoinListWordmark.tsx
394
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
781
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
395
782
  function CoinListWordmark(props) {
396
- return /* @__PURE__ */ jsxs2(
783
+ return /* @__PURE__ */ jsxs3(
397
784
  "svg",
398
785
  {
399
786
  width: "638",
@@ -404,35 +791,35 @@ function CoinListWordmark(props) {
404
791
  "aria-hidden": "true",
405
792
  ...props,
406
793
  children: [
407
- /* @__PURE__ */ jsx4(
794
+ /* @__PURE__ */ jsx5(
408
795
  "path",
409
796
  {
410
797
  d: "M68.7724 123.966C74.8226 124.172 80.7696 123.519 86.5104 121.799C97.4076 118.531 106.551 112.58 113.942 103.947C113.942 103.947 113.942 103.947 113.908 103.912C111.879 102.193 109.851 100.507 107.857 98.8218L105.829 97.1363C105.451 96.8268 105.107 96.5172 104.729 96.2076C103.939 95.5197 103.148 94.8662 102.323 94.2126C95.3107 98.8562 87.6104 101.539 79.1196 101.883C74.8569 102.055 70.6287 101.642 66.5036 100.507C59.5252 98.6154 53.2688 95.1757 48.2499 89.7755C39.1059 79.938 35.8745 68.1743 38.2465 55.0348C39.7246 46.9172 43.4373 40.0723 49.1093 34.5C47.9061 34.7408 46.703 35.016 45.5342 35.3943C36.5277 38.2148 29.6181 43.6839 24.6336 51.6639C20.6803 58.0273 18.5834 64.9411 18.3428 72.4739C18.2053 76.7047 18.4803 80.7979 19.4428 84.7879C22.5023 97.2395 29.3087 107.043 39.6903 114.335H39.7934V114.403C40.1372 114.644 40.4809 114.919 40.8247 115.16C49.2812 120.801 58.5971 123.691 68.7724 124.035V123.966Z",
411
798
  fill: "currentColor"
412
799
  }
413
800
  ),
414
- /* @__PURE__ */ jsx4(
801
+ /* @__PURE__ */ jsx5(
415
802
  "path",
416
803
  {
417
804
  d: "M72.2101 89.052C68.1881 90.8406 64.0286 91.8725 59.6973 92.2165C64.9912 95.209 70.8007 96.688 77.1259 96.8256C79.1885 96.86 81.251 96.6536 83.4167 96.3097C88.745 95.4841 93.6951 93.5579 98.3015 90.7374C93.9358 87.057 89.6044 83.4109 85.2386 79.7305C81.526 83.6861 77.1946 86.8506 72.2101 89.052Z",
418
805
  fill: "currentColor"
419
806
  }
420
807
  ),
421
- /* @__PURE__ */ jsx4(
808
+ /* @__PURE__ */ jsx5(
422
809
  "path",
423
810
  {
424
811
  d: "M66.9848 0.241609C66.4348 0.276006 65.8848 0.344799 65.3691 0.413592C70.663 1.4111 75.7851 3.13093 80.6321 5.71068C90.0511 10.7326 97.3732 17.9215 102.289 27.4494L104.936 25.1448C107.652 22.7714 110.368 20.3981 113.084 18.0247C106.655 11.1797 99.092 6.15784 90.223 3.06214C84.3103 0.998336 78.2601 -0.0335643 72.0381 0.000832319C70.388 0.000832319 68.7036 0.138419 66.9848 0.241609Z",
425
812
  fill: "currentColor"
426
813
  }
427
814
  ),
428
- /* @__PURE__ */ jsx4(
815
+ /* @__PURE__ */ jsx5(
429
816
  "path",
430
817
  {
431
818
  d: "M25.4242 107.871C17.4833 97.9988 13.1519 86.7167 13.2207 73.818C13.2207 67.317 14.5614 61.0568 17.1396 55.1062C20.9553 46.2662 26.9711 39.1805 35.4276 34.2962C43.3685 29.687 51.9968 27.8984 61.1752 29.2743C70.5943 30.7189 78.3976 35.2249 84.6541 42.2762C84.6541 42.2762 84.8603 42.5514 84.9634 42.6546C85.8916 41.8291 86.8541 41.0035 87.7823 40.178L88.9167 39.1805C89.8792 38.355 90.8417 37.4951 91.8043 36.6696C94.0043 34.7434 96.17 32.8515 98.3357 30.9253C94.1075 22.223 87.7479 15.55 79.3258 10.8377C69.3911 5.26541 58.7002 3.54557 47.3904 5.02463C41.7528 5.78136 36.3557 7.50119 31.1993 10.0465C13.702 18.7145 1.08597 36.532 0.0546833 58.4771C-0.117196 62.0887 0.123436 65.7348 0.673452 69.3464C2.04849 78.3239 5.34859 86.4759 10.6425 93.8368C14.7676 99.5123 19.6834 104.19 25.3898 107.871H25.4242Z",
432
819
  fill: "currentColor"
433
820
  }
434
821
  ),
435
- /* @__PURE__ */ jsx4(
822
+ /* @__PURE__ */ jsx5(
436
823
  "path",
437
824
  {
438
825
  fillRule: "evenodd",
@@ -447,18 +834,18 @@ function CoinListWordmark(props) {
447
834
  }
448
835
 
449
836
  // src/client/components/design-system/PoweredByCoinList.tsx
450
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
837
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
451
838
  function PoweredByCoinList({ className }) {
452
- return /* @__PURE__ */ jsxs3(
839
+ return /* @__PURE__ */ jsxs4(
453
840
  "div",
454
841
  {
455
842
  className: cn(
456
- "flex items-center justify-center gap-[5px] text-text-secondary",
843
+ "clcosdk:flex clcosdk:items-center clcosdk:justify-center clcosdk:gap-[5px] clcosdk:text-text-secondary",
457
844
  className
458
845
  ),
459
846
  children: [
460
- /* @__PURE__ */ jsx5("span", { className: cn(typeClasses.caption, "tracking-[0.12px]"), children: "Powered by" }),
461
- /* @__PURE__ */ jsx5(CoinListWordmark, { className: "h-3 w-auto" })
847
+ /* @__PURE__ */ jsx6("span", { className: cn(typeClasses.caption, "clcosdk:tracking-[0.12px]"), children: "Powered by" }),
848
+ /* @__PURE__ */ jsx6(CoinListWordmark, { className: "clcosdk:h-3 clcosdk:w-auto" })
462
849
  ]
463
850
  }
464
851
  );
@@ -467,48 +854,48 @@ function PoweredByCoinList({ className }) {
467
854
  // src/client/components/design-system/ClButton.tsx
468
855
  import { cva } from "class-variance-authority";
469
856
  import { forwardRef } from "react";
470
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
857
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
471
858
  var buttonVariants = cva(
472
- "inline-flex items-center justify-center gap-3 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-40 cursor-pointer",
859
+ "clcosdk:inline-flex clcosdk:items-center clcosdk:justify-center clcosdk:gap-3 clcosdk:whitespace-nowrap clcosdk:transition-colors clcosdk:focus-visible:outline-none clcosdk:focus-visible:ring-2 clcosdk:focus-visible:ring-offset-2 clcosdk:disabled:pointer-events-none clcosdk:disabled:opacity-40 clcosdk:cursor-pointer",
473
860
  {
474
861
  variants: {
475
862
  variant: {
476
863
  // Primary — tokens auto-switch: dark bg (#171717) → light bg (#f4f4f5) in dark mode
477
- primary: "bg-brand-primary text-text-inverse font-semibold hover:bg-brand-primary-hover active:bg-brand-primary-active focus-visible:ring-gray-500",
864
+ primary: "clcosdk:bg-brand-primary clcosdk:text-text-inverse clcosdk:font-semibold clcosdk:hover:bg-brand-primary-hover clcosdk:active:bg-brand-primary-active clcosdk:focus-visible:ring-gray-500",
478
865
  // Secondary — bg-section, border-border, text-text-primary all auto-switch
479
866
  // hover maps to border token value; active has no token so uses dark: override
480
- secondary: "bg-section border border-border text-text-primary font-medium hover:bg-border active:bg-[#d9d9d9] dark:active:bg-divider focus-visible:ring-gray-400",
867
+ secondary: "clcosdk:bg-section clcosdk:border clcosdk:border-border clcosdk:text-text-primary clcosdk:font-medium clcosdk:hover:bg-border clcosdk:active:bg-[#d9d9d9] clcosdk:dark:active:bg-divider clcosdk:focus-visible:ring-gray-400",
481
868
  // Destructive — error tokens auto-switch to translucent dark equivalents
482
869
  // hover/active have no tokens so use dark: overrides
483
- destructive: "bg-error-subtle border border-error-border text-error font-semibold hover:bg-[#fcdcd8] active:bg-[#f9c9c4] dark:hover:bg-[rgba(248,113,113,0.20)] dark:active:bg-[rgba(248,113,113,0.30)] focus-visible:ring-red-400",
870
+ destructive: "clcosdk:bg-error-subtle clcosdk:border clcosdk:border-error-border clcosdk:text-error clcosdk:font-semibold clcosdk:hover:bg-[#fcdcd8] clcosdk:active:bg-[#f9c9c4] clcosdk:dark:hover:bg-[rgba(248,113,113,0.20)] clcosdk:dark:active:bg-[rgba(248,113,113,0.30)] clcosdk:focus-visible:ring-red-400",
484
871
  // Ghost — transparent base; hover/active use section/border tokens (auto-switch)
485
- ghost: "bg-transparent text-text-primary font-medium hover:bg-section active:bg-border focus-visible:ring-gray-400",
872
+ ghost: "clcosdk:bg-transparent clcosdk:text-text-primary clcosdk:font-medium clcosdk:hover:bg-section clcosdk:active:bg-border clcosdk:focus-visible:ring-gray-400",
486
873
  // Link — text-text-primary auto-switches light/dark
487
- link: "h-auto p-0 text-text-primary font-medium underline-offset-4 hover:underline focus-visible:ring-gray-500",
874
+ link: "clcosdk:h-auto clcosdk:p-0 clcosdk:text-text-primary clcosdk:font-medium clcosdk:underline-offset-4 clcosdk:hover:underline clcosdk:focus-visible:ring-gray-500",
488
875
  // Destructive link — text-error auto-switches (#b42318 → #f87171 in dark)
489
- destructiveLink: "h-auto p-0 text-error font-medium hover:text-[#8b1a13] active:text-[#6d1510] dark:hover:text-[#fca5a5] dark:active:text-[#f87171] focus-visible:ring-red-500"
876
+ destructiveLink: "clcosdk:h-auto clcosdk:p-0 clcosdk:text-error clcosdk:font-medium clcosdk:hover:text-[#8b1a13] clcosdk:active:text-[#6d1510] clcosdk:dark:hover:text-[#fca5a5] clcosdk:dark:active:text-[#f87171] clcosdk:focus-visible:ring-red-500"
490
877
  },
491
878
  size: {
492
- sm: "h-9 px-4 text-sm tracking-[0.28px]",
493
- default: "h-11 px-5 text-sm tracking-[0.28px]",
494
- lg: "h-14 px-6 text-base",
495
- icon: "h-12 w-12"
879
+ sm: "clcosdk:h-9 clcosdk:px-4 clcosdk:text-sm clcosdk:tracking-[0.28px]",
880
+ default: "clcosdk:h-11 clcosdk:px-5 clcosdk:text-sm clcosdk:tracking-[0.28px]",
881
+ lg: "clcosdk:h-14 clcosdk:px-6 clcosdk:text-base",
882
+ icon: "clcosdk:h-12 clcosdk:w-12"
496
883
  },
497
884
  radius: {
498
- pill: "rounded-full",
885
+ pill: "clcosdk:rounded-full",
499
886
  // compound variants below handle size-specific radii
500
887
  rounded: ""
501
888
  },
502
889
  fullWidth: {
503
- true: "w-full",
890
+ true: "clcosdk:w-full",
504
891
  false: ""
505
892
  }
506
893
  },
507
894
  compoundVariants: [
508
- { size: "sm", radius: "rounded", className: "rounded-lg" },
509
- { size: "default", radius: "rounded", className: "rounded-2xl" },
510
- { size: "lg", radius: "rounded", className: "rounded-2xl" },
511
- { size: "icon", radius: "rounded", className: "rounded-md" }
895
+ { size: "sm", radius: "rounded", className: "clcosdk:rounded-lg" },
896
+ { size: "default", radius: "rounded", className: "clcosdk:rounded-2xl" },
897
+ { size: "lg", radius: "rounded", className: "clcosdk:rounded-2xl" },
898
+ { size: "icon", radius: "rounded", className: "clcosdk:rounded-md" }
512
899
  ],
513
900
  defaultVariants: {
514
901
  variant: "primary",
@@ -534,7 +921,7 @@ var ClButton = forwardRef(
534
921
  ...props
535
922
  }, ref) => {
536
923
  const isDisabled = disabled || isLoading;
537
- return /* @__PURE__ */ jsxs4(
924
+ return /* @__PURE__ */ jsxs5(
538
925
  "button",
539
926
  {
540
927
  className: cn(
@@ -545,16 +932,16 @@ var ClButton = forwardRef(
545
932
  disabled: isDisabled,
546
933
  ...props,
547
934
  children: [
548
- isLoading && /* @__PURE__ */ jsx6(
935
+ isLoading && /* @__PURE__ */ jsx7(
549
936
  "span",
550
937
  {
551
- className: "h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent opacity-60",
938
+ className: "clcosdk:h-4 clcosdk:w-4 clcosdk:shrink-0 clcosdk:animate-spin clcosdk:rounded-full clcosdk:border-2 clcosdk:border-current clcosdk:border-t-transparent clcosdk:opacity-60",
552
939
  "aria-hidden": "true"
553
940
  }
554
941
  ),
555
- startIcon && !isLoading && /* @__PURE__ */ jsx6("span", { className: "shrink-0", children: startIcon }),
556
- /* @__PURE__ */ jsx6("span", { className: cn(isLoading && "opacity-60"), children: isLoading && loadingText ? loadingText : children }),
557
- endIcon && !isLoading && /* @__PURE__ */ jsx6("span", { className: "shrink-0", children: endIcon })
942
+ startIcon && !isLoading && /* @__PURE__ */ jsx7("span", { className: "clcosdk:shrink-0", children: startIcon }),
943
+ /* @__PURE__ */ jsx7("span", { className: cn(isLoading && "clcosdk:opacity-60"), children: isLoading && loadingText ? loadingText : children }),
944
+ endIcon && !isLoading && /* @__PURE__ */ jsx7("span", { className: "clcosdk:shrink-0", children: endIcon })
558
945
  ]
559
946
  }
560
947
  );
@@ -563,9 +950,9 @@ var ClButton = forwardRef(
563
950
  ClButton.displayName = "ClButton";
564
951
 
565
952
  // src/client/components/design-system/logos/CoinListLogomark.tsx
566
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
953
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
567
954
  function CoinListLogomark(props) {
568
- return /* @__PURE__ */ jsxs5(
955
+ return /* @__PURE__ */ jsxs6(
569
956
  "svg",
570
957
  {
571
958
  width: "115",
@@ -576,28 +963,28 @@ function CoinListLogomark(props) {
576
963
  "aria-hidden": "true",
577
964
  ...props,
578
965
  children: [
579
- /* @__PURE__ */ jsx7(
966
+ /* @__PURE__ */ jsx8(
580
967
  "path",
581
968
  {
582
969
  d: "M69.7519 123.966C75.8021 124.172 81.7491 123.519 87.4899 121.799C98.3871 118.531 107.531 112.58 114.922 103.947C114.922 103.947 114.922 103.947 114.888 103.912C112.859 102.193 110.831 100.507 108.837 98.8218L106.809 97.1363C106.431 96.8268 106.087 96.5172 105.709 96.2076C104.919 95.5197 104.128 94.8662 103.303 94.2126C96.2902 98.8562 88.5899 101.539 80.0991 101.883C75.8364 102.055 71.6082 101.642 67.4831 100.507C60.5047 98.6154 54.2483 95.1757 49.2294 89.7755C40.0854 79.938 36.854 68.1743 39.226 55.0348C40.7041 46.9172 44.4168 40.0723 50.0888 34.5C48.8856 34.7408 47.6825 35.016 46.5137 35.3943C37.5072 38.2148 30.5976 43.6839 25.6131 51.6639C21.6598 58.0273 19.5629 64.9411 19.3223 72.4739C19.1848 76.7047 19.4598 80.7979 20.4223 84.7879C23.4818 97.2395 30.2882 107.043 40.6698 114.335H40.7729V114.403C41.1167 114.644 41.4604 114.919 41.8042 115.16C50.2607 120.801 59.5766 123.691 69.7519 124.035V123.966Z",
583
970
  fill: "currentColor"
584
971
  }
585
972
  ),
586
- /* @__PURE__ */ jsx7(
973
+ /* @__PURE__ */ jsx8(
587
974
  "path",
588
975
  {
589
976
  d: "M73.1896 89.052C69.1676 90.8406 65.0081 91.8725 60.6768 92.2165C65.9707 95.209 71.7802 96.688 78.1054 96.8256C80.168 96.86 82.2305 96.6536 84.3962 96.3097C89.7245 95.4841 94.6746 93.5579 99.281 90.7374C94.9153 87.057 90.5839 83.4109 86.2181 79.7305C82.5055 83.6861 78.1741 86.8506 73.1896 89.052Z",
590
977
  fill: "currentColor"
591
978
  }
592
979
  ),
593
- /* @__PURE__ */ jsx7(
980
+ /* @__PURE__ */ jsx8(
594
981
  "path",
595
982
  {
596
983
  d: "M67.9643 0.241609C67.4143 0.276006 66.8643 0.344799 66.3486 0.413592C71.6425 1.4111 76.7646 3.13093 81.6116 5.71068C91.0306 10.7326 98.3527 17.9215 103.268 27.4494L105.915 25.1448C108.631 22.7714 111.347 20.3981 114.063 18.0247C107.634 11.1797 100.072 6.15784 91.2025 3.06214C85.2898 0.998336 79.2396 -0.0335643 73.0176 0.000832322C71.3675 0.000832322 69.6831 0.138419 67.9643 0.241609Z",
597
984
  fill: "currentColor"
598
985
  }
599
986
  ),
600
- /* @__PURE__ */ jsx7(
987
+ /* @__PURE__ */ jsx8(
601
988
  "path",
602
989
  {
603
990
  d: "M26.4037 107.871C18.4628 97.9988 14.1314 86.7167 14.2002 73.818C14.2002 67.317 15.5409 61.0568 18.1191 55.1062C21.9348 46.2662 27.9506 39.1805 36.4071 34.2962C44.348 29.687 52.9763 27.8984 62.1547 29.2743C71.5738 30.7189 79.3771 35.2249 85.6336 42.2762C85.6336 42.2762 85.8398 42.5514 85.9429 42.6546C86.8711 41.8291 87.8336 41.0035 88.7618 40.178L89.8962 39.1805C90.8587 38.355 91.8212 37.4951 92.7838 36.6696C94.9838 34.7434 97.1495 32.8515 99.3152 30.9253C95.087 22.223 88.7274 15.55 80.3053 10.8377C70.3706 5.26541 59.6797 3.54557 48.37 5.02463C42.7323 5.78136 37.3352 7.50119 32.1788 10.0465C14.6815 18.7145 2.06546 36.532 1.03418 58.4771C0.862296 62.0887 1.10293 65.7348 1.65294 69.3464C3.02798 78.3239 6.32808 86.4759 11.622 93.8368C15.7471 99.5123 20.6629 104.19 26.3693 107.871H26.4037Z",
@@ -610,7 +997,7 @@ function CoinListLogomark(props) {
610
997
  }
611
998
 
612
999
  // src/client/components/oauth/CoinListSignInButton.tsx
613
- import { jsx as jsx8 } from "react/jsx-runtime";
1000
+ import { jsx as jsx9 } from "react/jsx-runtime";
614
1001
  function CoinListSignInButton({
615
1002
  onClick,
616
1003
  disabled = false,
@@ -618,7 +1005,7 @@ function CoinListSignInButton({
618
1005
  isLoading = false,
619
1006
  className
620
1007
  }) {
621
- return /* @__PURE__ */ jsx8(
1008
+ return /* @__PURE__ */ jsx9(
622
1009
  ClButton,
623
1010
  {
624
1011
  variant: "primary",
@@ -628,7 +1015,7 @@ function CoinListSignInButton({
628
1015
  disabled,
629
1016
  isLoading,
630
1017
  loadingText: "Signing in...",
631
- startIcon: /* @__PURE__ */ jsx8(CoinListLogomark, { className: "h-[18px] w-[16px] shrink-0" }),
1018
+ startIcon: /* @__PURE__ */ jsx9(CoinListLogomark, { className: "clcosdk:h-[18px] clcosdk:w-[16px] clcosdk:shrink-0" }),
632
1019
  className,
633
1020
  children: "Continue with CoinList"
634
1021
  }
@@ -636,9 +1023,9 @@ function CoinListSignInButton({
636
1023
  }
637
1024
 
638
1025
  // src/client/hooks/useCoinList.ts
639
- import { useContext } from "react";
1026
+ import { useContext as useContext2 } from "react";
640
1027
  function useCoinList() {
641
- const context = useContext(CoinListContext);
1028
+ const context = useContext2(CoinListContext);
642
1029
  if (context === null) {
643
1030
  throw new CoinListClientInitializationError();
644
1031
  }
@@ -655,7 +1042,7 @@ var CoinListClientInitializationError = class extends Error {
655
1042
  };
656
1043
 
657
1044
  // src/client/components/oauth/CoinListSignInCard.tsx
658
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1045
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
659
1046
  function CoinListSignInCard({
660
1047
  state = "idle",
661
1048
  onSignIn,
@@ -666,37 +1053,50 @@ function CoinListSignInCard({
666
1053
  const handleSignIn = onSignIn ?? (() => {
667
1054
  void coinlist.startOAuth();
668
1055
  });
669
- return /* @__PURE__ */ jsxs6(
1056
+ return /* @__PURE__ */ jsx10(CoinListStyleScope, { children: /* @__PURE__ */ jsxs7(
670
1057
  "div",
671
1058
  {
672
1059
  className: cn(
673
- "relative flex w-full max-w-md flex-col rounded-card border border-border bg-background p-6 shadow-card",
1060
+ "clcosdk:relative clcosdk:flex clcosdk:w-full clcosdk:max-w-md clcosdk:flex-col clcosdk:rounded-card clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:p-6 clcosdk:shadow-card",
674
1061
  className
675
1062
  ),
676
1063
  children: [
677
- onClose && /* @__PURE__ */ jsx9(CloseButton, { onClick: onClose, className: "absolute right-4 top-4" }),
678
- /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-2", children: [
679
- /* @__PURE__ */ jsx9("h3", { className: cn(typeClasses.h3, "text-text-primary"), children: "Sign in to participate" }),
680
- /* @__PURE__ */ jsx9("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "A CoinList account is required to participate in this sale." })
1064
+ onClose && /* @__PURE__ */ jsx10(
1065
+ CloseButton,
1066
+ {
1067
+ onClick: onClose,
1068
+ className: "clcosdk:absolute clcosdk:right-4 clcosdk:top-4"
1069
+ }
1070
+ ),
1071
+ /* @__PURE__ */ jsxs7("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-2", children: [
1072
+ /* @__PURE__ */ jsx10("h3", { className: cn(typeClasses.h3, "clcosdk:text-text-primary"), children: "Sign in to participate" }),
1073
+ /* @__PURE__ */ jsx10("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "A CoinList account is required to participate in this sale." })
681
1074
  ] }),
682
- /* @__PURE__ */ jsx9(CoinListSignInButton, { onClick: handleSignIn, fullWidth: true, className: "mt-4" }),
683
- state === "error" && /* @__PURE__ */ jsx9(
1075
+ /* @__PURE__ */ jsx10(
1076
+ CoinListSignInButton,
1077
+ {
1078
+ onClick: handleSignIn,
1079
+ fullWidth: true,
1080
+ className: "clcosdk:mt-4"
1081
+ }
1082
+ ),
1083
+ state === "error" && /* @__PURE__ */ jsx10(
684
1084
  AlertBanner,
685
1085
  {
686
1086
  variant: "error",
687
1087
  icon: true,
688
1088
  content: "Error signing in. Please try again.",
689
- className: "mt-4"
1089
+ className: "clcosdk:mt-4"
690
1090
  }
691
1091
  ),
692
- /* @__PURE__ */ jsx9(PoweredByCoinList, { className: "mt-4" })
1092
+ /* @__PURE__ */ jsx10(PoweredByCoinList, { className: "clcosdk:mt-4" })
693
1093
  ]
694
1094
  }
695
- );
1095
+ ) });
696
1096
  }
697
1097
 
698
1098
  // src/client/components/offers/OfferCard.tsx
699
- import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1099
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
700
1100
  var monthYearFormatter = new Intl.DateTimeFormat("en-US", {
701
1101
  month: "short",
702
1102
  year: "numeric"
@@ -717,46 +1117,67 @@ function OfferCard({
717
1117
  containerClassName
718
1118
  }) {
719
1119
  const cardLabel = "Open offer details";
720
- const cardContent = /* @__PURE__ */ jsxs7(Fragment, { children: [
721
- /* @__PURE__ */ jsxs7("div", { className: "relative mb-6 h-52 w-full overflow-hidden rounded-2xl border border-border/60 bg-section", children: [
722
- offer.bannerUrl ? /* @__PURE__ */ jsx10(
1120
+ const cardContent = /* @__PURE__ */ jsxs8(Fragment2, { children: [
1121
+ /* @__PURE__ */ jsxs8("div", { className: "clcosdk:relative clcosdk:mb-6 clcosdk:h-52 clcosdk:w-full clcosdk:overflow-hidden clcosdk:rounded-2xl clcosdk:border clcosdk:border-border/60 clcosdk:bg-section", children: [
1122
+ offer.bannerUrl ? /* @__PURE__ */ jsx11(
723
1123
  "img",
724
1124
  {
725
1125
  src: offer.bannerUrl,
726
1126
  alt: "Offer banner",
727
- className: "h-full w-full object-cover"
1127
+ className: "clcosdk:h-full clcosdk:w-full clcosdk:object-cover"
728
1128
  }
729
- ) : /* @__PURE__ */ jsx10("div", { className: "h-full w-full bg-gradient-to-br from-surface via-section to-background" }),
730
- /* @__PURE__ */ jsx10("div", { className: "absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-overlay/35" }),
731
- /* @__PURE__ */ jsx10("div", { className: "absolute inset-0 flex items-center justify-center p-6", children: offer.logoUrl ? /* @__PURE__ */ jsx10(
1129
+ ) : /* @__PURE__ */ jsx11("div", { className: "clcosdk:h-full clcosdk:w-full clcosdk:bg-gradient-to-br clcosdk:from-surface clcosdk:via-section clcosdk:to-background" }),
1130
+ /* @__PURE__ */ jsx11("div", { className: "clcosdk:absolute clcosdk:inset-0 clcosdk:bg-gradient-to-b clcosdk:from-transparent clcosdk:via-transparent clcosdk:to-overlay/35" }),
1131
+ /* @__PURE__ */ jsx11("div", { className: "clcosdk:absolute clcosdk:inset-0 clcosdk:flex clcosdk:items-center clcosdk:justify-center clcosdk:p-6", children: offer.logoUrl ? /* @__PURE__ */ jsx11(
732
1132
  "img",
733
1133
  {
734
1134
  src: offer.logoUrl,
735
1135
  alt: "Offer logo",
736
- className: "h-28 w-28 rounded-full border border-border bg-surface object-cover shadow-[0_10px_24px_rgba(0,0,0,0.25)]"
1136
+ className: "clcosdk:h-28 clcosdk:w-28 clcosdk:rounded-full clcosdk:border clcosdk:border-border clcosdk:bg-surface clcosdk:object-cover clcosdk:shadow-[0_10px_24px_rgba(0,0,0,0.25)]"
737
1137
  }
738
- ) : /* @__PURE__ */ jsx10("div", { className: "h-28 w-28 rounded-full border border-border bg-surface shadow-[0_10px_24px_rgba(0,0,0,0.18)]" }) })
1138
+ ) : /* @__PURE__ */ jsx11("div", { className: "clcosdk:h-28 clcosdk:w-28 clcosdk:rounded-full clcosdk:border clcosdk:border-border clcosdk:bg-surface clcosdk:shadow-[0_10px_24px_rgba(0,0,0,0.18)]" }) })
739
1139
  ] }),
740
- offer.tagline && /* @__PURE__ */ jsx10("p", { className: cn(typeClasses.h3, "min-h-[2.4rem] text-text-primary"), children: offer.tagline }),
741
- /* @__PURE__ */ jsxs7("div", { className: "mt-4 grid grid-cols-2 gap-5 border-t border-divider pt-4", children: [
742
- /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
743
- /* @__PURE__ */ jsx10("span", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Starts" }),
744
- /* @__PURE__ */ jsx10("span", { className: cn(typeClasses.label, "text-text-primary"), children: offer.formattedStartsAt })
1140
+ offer.tagline && /* @__PURE__ */ jsx11(
1141
+ "p",
1142
+ {
1143
+ className: cn(
1144
+ typeClasses.h3,
1145
+ "clcosdk:min-h-[2.4rem] clcosdk:text-text-primary"
1146
+ ),
1147
+ children: offer.tagline
1148
+ }
1149
+ ),
1150
+ /* @__PURE__ */ jsxs8("div", { className: "clcosdk:mt-4 clcosdk:grid clcosdk:grid-cols-2 clcosdk:gap-5 clcosdk:border-t clcosdk:border-divider clcosdk:pt-4", children: [
1151
+ /* @__PURE__ */ jsxs8("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1", children: [
1152
+ /* @__PURE__ */ jsx11(
1153
+ "span",
1154
+ {
1155
+ className: cn(typeClasses.subhead, "clcosdk:text-text-secondary"),
1156
+ children: "Starts"
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsx11("span", { className: cn(typeClasses.label, "clcosdk:text-text-primary"), children: offer.formattedStartsAt })
745
1160
  ] }),
746
- /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
747
- /* @__PURE__ */ jsx10("span", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Ends" }),
748
- /* @__PURE__ */ jsx10("span", { className: cn(typeClasses.label, "text-text-primary"), children: offer.formattedEndsAt })
1161
+ /* @__PURE__ */ jsxs8("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1", children: [
1162
+ /* @__PURE__ */ jsx11(
1163
+ "span",
1164
+ {
1165
+ className: cn(typeClasses.subhead, "clcosdk:text-text-secondary"),
1166
+ children: "Ends"
1167
+ }
1168
+ ),
1169
+ /* @__PURE__ */ jsx11("span", { className: cn(typeClasses.label, "clcosdk:text-text-primary"), children: offer.formattedEndsAt })
749
1170
  ] })
750
1171
  ] })
751
1172
  ] });
752
1173
  const baseClasses = cn(
753
- "relative flex w-full max-w-[28rem] flex-col rounded-card border border-border bg-background p-6 text-left shadow-card transition-all duration-200",
1174
+ "clcosdk:relative clcosdk:flex clcosdk:w-full clcosdk:max-w-[28rem] clcosdk:flex-col clcosdk:rounded-card clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:p-6 clcosdk:text-left clcosdk:shadow-card clcosdk:transition-all clcosdk:duration-200",
754
1175
  className,
755
- onClick && "cursor-pointer hover:-translate-y-0.5 hover:border-divider hover:shadow-[0px_14px_32px_-10px_rgba(0,0,0,0.35)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-focus focus-visible:ring-offset-2"
1176
+ onClick && "clcosdk:cursor-pointer clcosdk:hover:-translate-y-0.5 clcosdk:hover:border-divider clcosdk:hover:shadow-[0px_14px_32px_-10px_rgba(0,0,0,0.35)] clcosdk:focus-visible:outline-none clcosdk:focus-visible:ring-2 clcosdk:focus-visible:ring-accent-focus clcosdk:focus-visible:ring-offset-2"
756
1177
  );
757
1178
  let cardNode;
758
1179
  if (onClick) {
759
- cardNode = /* @__PURE__ */ jsx10(
1180
+ cardNode = /* @__PURE__ */ jsx11(
760
1181
  "button",
761
1182
  {
762
1183
  type: "button",
@@ -767,12 +1188,10 @@ function OfferCard({
767
1188
  }
768
1189
  );
769
1190
  } else {
770
- cardNode = /* @__PURE__ */ jsx10("div", { className: baseClasses, children: cardContent });
771
- }
772
- if (!containerClassName) {
773
- return cardNode;
1191
+ cardNode = /* @__PURE__ */ jsx11("div", { className: baseClasses, children: cardContent });
774
1192
  }
775
- return /* @__PURE__ */ jsx10("div", { className: containerClassName, children: cardNode });
1193
+ const card = containerClassName ? /* @__PURE__ */ jsx11("div", { className: containerClassName, children: cardNode }) : cardNode;
1194
+ return /* @__PURE__ */ jsx11(CoinListStyleScope, { children: card });
776
1195
  }
777
1196
 
778
1197
  // src/client/hooks/useOffers.ts
@@ -818,9 +1237,9 @@ function useOffers(options = {}) {
818
1237
  }
819
1238
 
820
1239
  // src/client/components/offers/OffersGrid.tsx
821
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1240
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
822
1241
  var DEFAULT_MAX_COLUMNS = 3;
823
- function OffersGrid({
1242
+ function OffersGridBody({
824
1243
  data,
825
1244
  maxColumns,
826
1245
  className,
@@ -836,12 +1255,12 @@ function OffersGrid({
836
1255
  if (loading) {
837
1256
  return loading;
838
1257
  }
839
- return /* @__PURE__ */ jsx11(
1258
+ return /* @__PURE__ */ jsx12(
840
1259
  "output",
841
1260
  {
842
1261
  "aria-live": "polite",
843
- className: "flex min-h-40 items-center justify-center rounded-2xl border border-border bg-background p-6",
844
- children: /* @__PURE__ */ jsx11("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "Loading offers..." })
1262
+ className: "clcosdk:flex clcosdk:min-h-40 clcosdk:items-center clcosdk:justify-center clcosdk:rounded-2xl clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:p-6",
1263
+ children: /* @__PURE__ */ jsx12("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Loading offers..." })
845
1264
  }
846
1265
  );
847
1266
  }
@@ -850,13 +1269,13 @@ function OffersGrid({
850
1269
  return error;
851
1270
  }
852
1271
  const errorMessage = offersState.reason === "not-authenticated" ? "Sign in to view available offers." : "Unable to load offers right now. Please try again.";
853
- return /* @__PURE__ */ jsx11(
1272
+ return /* @__PURE__ */ jsx12(
854
1273
  AlertBanner,
855
1274
  {
856
1275
  variant: "error",
857
1276
  icon: true,
858
1277
  content: errorMessage,
859
- className: "w-full max-w-[28rem]"
1278
+ className: "clcosdk:w-full clcosdk:max-w-[28rem]"
860
1279
  }
861
1280
  );
862
1281
  }
@@ -864,103 +1283,671 @@ function OffersGrid({
864
1283
  if (emptyState) {
865
1284
  return emptyState;
866
1285
  }
867
- return /* @__PURE__ */ jsxs8(
1286
+ return /* @__PURE__ */ jsxs9(
868
1287
  "output",
869
1288
  {
870
1289
  "aria-live": "polite",
871
- className: "flex min-h-40 flex-col items-center justify-center rounded-2xl border border-border bg-background p-6 text-center",
1290
+ className: "clcosdk:flex clcosdk:min-h-40 clcosdk:flex-col clcosdk:items-center clcosdk:justify-center clcosdk:rounded-2xl clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:p-6 clcosdk:text-center",
872
1291
  children: [
873
- /* @__PURE__ */ jsx11("p", { className: cn(typeClasses.body, "font-medium text-text-primary"), children: "No offers available" }),
874
- /* @__PURE__ */ jsx11("p", { className: cn(typeClasses.subhead, "text-text-secondary"), children: "Check back soon for upcoming opportunities." })
1292
+ /* @__PURE__ */ jsx12(
1293
+ "p",
1294
+ {
1295
+ className: cn(
1296
+ typeClasses.body,
1297
+ "clcosdk:font-medium clcosdk:text-text-primary"
1298
+ ),
1299
+ children: "No offers available"
1300
+ }
1301
+ ),
1302
+ /* @__PURE__ */ jsx12("p", { className: cn(typeClasses.subhead, "clcosdk:text-text-secondary"), children: "Check back soon for upcoming opportunities." })
875
1303
  ]
876
1304
  }
877
1305
  );
878
1306
  }
879
1307
  const maxGridWidth = `calc(${cols} * 28rem + ${Math.max(cols - 1, 0)} * 1.5rem)`;
880
- return /* @__PURE__ */ jsx11("div", { className: cn("flex w-full justify-center", containerClassName), children: /* @__PURE__ */ jsx11(
1308
+ return /* @__PURE__ */ jsx12(
881
1309
  "div",
882
1310
  {
883
- className: cn("grid w-full gap-6", className),
884
- style: {
885
- gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 20rem), 1fr))",
886
- maxWidth: maxGridWidth
887
- },
888
- children: offersState.offers.map((offer) => /* @__PURE__ */ jsx11(
889
- OfferCard,
1311
+ className: cn(
1312
+ "clcosdk:flex clcosdk:w-full clcosdk:justify-center",
1313
+ containerClassName
1314
+ ),
1315
+ children: /* @__PURE__ */ jsx12(
1316
+ "div",
890
1317
  {
891
- offer: OfferCardUi.fromDomain(offer),
892
- onClick: onOfferClick ? () => onOfferClick(offer) : void 0
893
- },
894
- offer.id
895
- ))
1318
+ className: cn("clcosdk:grid clcosdk:w-full clcosdk:gap-6", className),
1319
+ style: {
1320
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 20rem), 1fr))",
1321
+ maxWidth: maxGridWidth
1322
+ },
1323
+ children: offersState.offers.map((offer) => /* @__PURE__ */ jsx12(
1324
+ OfferCard,
1325
+ {
1326
+ offer: OfferCardUi.fromDomain(offer),
1327
+ onClick: onOfferClick ? () => onOfferClick(offer) : void 0
1328
+ },
1329
+ offer.id
1330
+ ))
1331
+ }
1332
+ )
896
1333
  }
897
- ) });
1334
+ );
1335
+ }
1336
+ function OffersGrid(props = {}) {
1337
+ return /* @__PURE__ */ jsx12(CoinListStyleScope, { children: /* @__PURE__ */ jsx12(OffersGridBody, { ...props }) });
898
1338
  }
899
1339
 
900
- // src/client/components/requirements/RequirementItem.tsx
901
- import { ChevronRight } from "lucide-react";
902
-
903
- // src/client/components/design-system/StepStatusIcon.tsx
904
- import { CircleCheck as CircleCheck2, CircleX, Info as Info2 } from "lucide-react";
905
- import { jsx as jsx12 } from "react/jsx-runtime";
906
- var statusConfig = {
907
- required: {
908
- Icon: Info2,
909
- colorClass: "text-warning",
910
- fillClass: "[&_circle]:fill-warning-subtle"
911
- },
912
- pending: {
913
- Icon: Info2,
914
- colorClass: "text-warning",
915
- fillClass: "[&_circle]:fill-warning-subtle"
916
- },
917
- completed: {
918
- Icon: CircleCheck2,
919
- colorClass: "text-success",
920
- fillClass: "[&_circle]:fill-success-subtle"
921
- },
922
- rejected: {
923
- Icon: CircleX,
924
- colorClass: "text-error",
925
- fillClass: "[&_circle]:fill-error-subtle"
926
- }
927
- };
928
- function StepStatusIcon({ status, className }) {
929
- const { Icon, colorClass, fillClass } = statusConfig[status];
930
- return /* @__PURE__ */ jsx12(
931
- Icon,
1340
+ // src/client/components/requirements/ConnectedWalletList.tsx
1341
+ import { useState as useState3 } from "react";
1342
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1343
+ function ConnectedWalletRow({
1344
+ wallet,
1345
+ onRemove
1346
+ }) {
1347
+ const [state, setState] = useState3("idle");
1348
+ const remove = async () => {
1349
+ if (!onRemove) return;
1350
+ setState("removing");
1351
+ try {
1352
+ await onRemove(wallet.id);
1353
+ setState("idle");
1354
+ } catch {
1355
+ setState("error");
1356
+ }
1357
+ };
1358
+ return /* @__PURE__ */ jsxs10("div", { className: "clcosdk:flex clcosdk:min-h-9 clcosdk:flex-wrap clcosdk:items-center clcosdk:justify-between clcosdk:gap-2", children: [
1359
+ /* @__PURE__ */ jsx13(
1360
+ "span",
1361
+ {
1362
+ title: wallet.address,
1363
+ className: cn(
1364
+ typeClasses.body,
1365
+ "clcosdk:font-mono clcosdk:text-text-primary"
1366
+ ),
1367
+ children: wallet.shortAddress
1368
+ }
1369
+ ),
1370
+ onRemove != null && (state === "confirming" ? /* @__PURE__ */ jsxs10("div", { className: "clcosdk:flex clcosdk:items-center clcosdk:gap-2", children: [
1371
+ /* @__PURE__ */ jsx13(
1372
+ "span",
1373
+ {
1374
+ className: cn(typeClasses.subhead, "clcosdk:text-text-secondary"),
1375
+ children: "Remove?"
1376
+ }
1377
+ ),
1378
+ /* @__PURE__ */ jsx13(ClButton, { size: "sm", variant: "destructive", onClick: remove, children: "Remove" }),
1379
+ /* @__PURE__ */ jsx13(
1380
+ ClButton,
1381
+ {
1382
+ size: "sm",
1383
+ variant: "ghost",
1384
+ onClick: () => setState("idle"),
1385
+ children: "Cancel"
1386
+ }
1387
+ )
1388
+ ] }) : state === "error" ? /* @__PURE__ */ jsxs10("div", { className: "clcosdk:flex clcosdk:items-center clcosdk:gap-2", children: [
1389
+ /* @__PURE__ */ jsx13("span", { className: cn(typeClasses.subhead, "clcosdk:text-error"), children: "Couldn't remove." }),
1390
+ /* @__PURE__ */ jsx13(
1391
+ ClButton,
1392
+ {
1393
+ size: "sm",
1394
+ variant: "destructiveLink",
1395
+ onClick: () => setState("confirming"),
1396
+ children: "Try again"
1397
+ }
1398
+ )
1399
+ ] }) : /* @__PURE__ */ jsx13(
1400
+ ClButton,
1401
+ {
1402
+ size: "sm",
1403
+ variant: "destructiveLink",
1404
+ disabled: state === "removing",
1405
+ onClick: () => setState("confirming"),
1406
+ children: state === "removing" ? "Removing\u2026" : "Remove"
1407
+ }
1408
+ ))
1409
+ ] });
1410
+ }
1411
+ function ConnectedWalletList({
1412
+ wallets,
1413
+ onRemove,
1414
+ onAdd,
1415
+ addLabel = "Add wallet",
1416
+ status = "ready",
1417
+ className
1418
+ }) {
1419
+ return /* @__PURE__ */ jsxs10(
1420
+ "div",
932
1421
  {
933
- className: cn("h-6 w-6 shrink-0", colorClass, fillClass, className),
934
- "aria-hidden": "true"
1422
+ className: cn("clcosdk:flex clcosdk:flex-col clcosdk:gap-2", className),
1423
+ children: [
1424
+ status === "loading" ? /* @__PURE__ */ jsx13(AlertBanner, { variant: "info", content: "Loading your connected wallets\u2026" }) : status === "error" ? /* @__PURE__ */ jsx13(
1425
+ AlertBanner,
1426
+ {
1427
+ variant: "error",
1428
+ content: "Couldn't load your connected wallets."
1429
+ }
1430
+ ) : wallets.length === 0 ? /* @__PURE__ */ jsx13(AlertBanner, { variant: "info", content: "No wallets connected yet." }) : /* @__PURE__ */ jsx13("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:divide-y clcosdk:divide-divider", children: wallets.map((wallet) => /* @__PURE__ */ jsx13(
1431
+ ConnectedWalletRow,
1432
+ {
1433
+ wallet,
1434
+ onRemove
1435
+ },
1436
+ wallet.id
1437
+ )) }),
1438
+ onAdd != null && status !== "loading" && /* @__PURE__ */ jsx13(
1439
+ ClButton,
1440
+ {
1441
+ size: "sm",
1442
+ variant: "secondary",
1443
+ onClick: onAdd,
1444
+ className: "clcosdk:self-start",
1445
+ children: addLabel
1446
+ }
1447
+ )
1448
+ ]
935
1449
  }
936
1450
  );
937
1451
  }
938
1452
 
939
- // src/client/components/requirements/RequirementItem.tsx
940
- import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
941
- var RequirementVariant = {
942
- fromType(type) {
943
- switch (type) {
944
- case "external_wallet":
945
- case "whitelisted_wallet":
946
- return "wallet";
947
- default:
948
- return "verification";
1453
+ // src/client/components/requirements/ConnectWalletModal.tsx
1454
+ import { useEffect as useEffect4, useId } from "react";
1455
+
1456
+ // src/client/hooks/useConnectWallet.ts
1457
+ import { useCallback, useEffect as useEffect3, useRef, useState as useState4 } from "react";
1458
+
1459
+ // src/client/core/connect-external-wallet.ts
1460
+ var DEFAULT_STATEMENT = "Sign this message to prove you own this wallet. This does not cost gas or move funds.";
1461
+ var TERMINAL_CODES = /* @__PURE__ */ new Set([
1462
+ "not_authenticated",
1463
+ "wallet_not_whitelisted",
1464
+ "max_wallets_reached"
1465
+ ]);
1466
+ var API_ERROR_CODES = /* @__PURE__ */ new Set([
1467
+ "wallet_not_whitelisted",
1468
+ "max_wallets_reached"
1469
+ ]);
1470
+ function flowError(code) {
1471
+ return { code, retryable: !TERMINAL_CODES.has(code) };
1472
+ }
1473
+ function siweContext() {
1474
+ if (typeof window === "undefined") return { domain: "", uri: "" };
1475
+ return { domain: window.location.host, uri: window.location.origin };
1476
+ }
1477
+ function apiErrorCode(error) {
1478
+ if (typeof error !== "object" || error === null) return void 0;
1479
+ const body = error.response?.body;
1480
+ return typeof body?.code === "string" ? body.code : void 0;
1481
+ }
1482
+ function classifyApiError(error) {
1483
+ if (error instanceof NotAuthenticatedError) {
1484
+ return flowError("not_authenticated");
1485
+ }
1486
+ const code = apiErrorCode(error);
1487
+ if (code !== void 0 && API_ERROR_CODES.has(code)) {
1488
+ return flowError(code);
1489
+ }
1490
+ return flowError("unknown");
1491
+ }
1492
+ function challengeParams(wallet, challengeType, statement) {
1493
+ switch (challengeType) {
1494
+ case "siwe":
1495
+ return {
1496
+ walletAddress: wallet.address,
1497
+ chain: wallet.chain,
1498
+ challengeType: "siwe",
1499
+ ...siweContext(),
1500
+ statement: statement ?? DEFAULT_STATEMENT
1501
+ };
1502
+ case "plain":
1503
+ return {
1504
+ walletAddress: wallet.address,
1505
+ chain: wallet.chain,
1506
+ challengeType: "plain"
1507
+ };
1508
+ default: {
1509
+ const _exhaustive = challengeType;
1510
+ return _exhaustive;
949
1511
  }
950
1512
  }
951
- };
952
- var RequirementStatus = {
953
- fromStatusValue(status) {
954
- switch (status) {
955
- case "not_started":
956
- case "action_needed":
957
- return "required";
958
- case "in_progress":
959
- return "pending";
960
- case "completed":
961
- return "completed";
962
- case "rejected":
963
- return "rejected";
1513
+ }
1514
+ async function connectExternalWalletFlow(params) {
1515
+ const {
1516
+ coinlist,
1517
+ wallet,
1518
+ offerId,
1519
+ offerOptionId,
1520
+ challengeType,
1521
+ statement,
1522
+ isCancelled,
1523
+ onProgress
1524
+ } = params;
1525
+ const emit = (phase) => onProgress?.(phase);
1526
+ const cancelled = () => isCancelled?.() ?? false;
1527
+ emit("requesting-challenge");
1528
+ let challenge;
1529
+ try {
1530
+ challenge = await coinlist.createWalletOwnershipChallenge(
1531
+ challengeParams(wallet, challengeType ?? "siwe", statement)
1532
+ );
1533
+ } catch (error) {
1534
+ return { type: "error", error: classifyApiError(error) };
1535
+ }
1536
+ if (cancelled()) return { type: "cancelled" };
1537
+ emit("signing-message");
1538
+ let signature;
1539
+ try {
1540
+ signature = await wallet.signMessage(challenge.message);
1541
+ } catch (error) {
1542
+ if (classifyWalletError(error).type === "user_rejected") {
1543
+ return { type: "error", error: flowError("user_rejected") };
1544
+ }
1545
+ return { type: "error", error: flowError("unknown") };
1546
+ }
1547
+ if (cancelled()) return { type: "cancelled" };
1548
+ emit("submitting-signature");
1549
+ try {
1550
+ const binding = await coinlist.connectExternalWallet(offerId, {
1551
+ offerOptionId,
1552
+ walletAddress: wallet.address,
1553
+ chain: wallet.chain,
1554
+ signature
1555
+ });
1556
+ return { type: "success", binding };
1557
+ } catch (error) {
1558
+ return { type: "error", error: classifyApiError(error) };
1559
+ }
1560
+ }
1561
+
1562
+ // src/client/hooks/useConnectWallet.ts
1563
+ var IDLE_SIGN = { type: "idle", error: null };
1564
+ var ERROR_COPY = {
1565
+ not_authenticated: "Sign in to connect your wallet.",
1566
+ user_rejected: "Signature request declined. Approve it in your wallet to continue.",
1567
+ wallet_not_whitelisted: "This wallet is not approved for this offer.",
1568
+ max_wallets_reached: "You've reached the maximum number of wallets for this option.",
1569
+ unknown: "Unable to connect your wallet. Please try again."
1570
+ };
1571
+ function displayError(error) {
1572
+ return {
1573
+ code: error.code,
1574
+ message: ERROR_COPY[error.code],
1575
+ retryable: error.retryable
1576
+ };
1577
+ }
1578
+ function useConnectWallet({
1579
+ isOpen,
1580
+ offerId,
1581
+ offerOptionId,
1582
+ wallet,
1583
+ challengeType,
1584
+ statement
1585
+ }) {
1586
+ const { coinlist } = useCoinList();
1587
+ const [state, setState] = useState4(
1588
+ () => wallet ? { type: "READY", address: wallet.address, sign: IDLE_SIGN } : { type: "NEEDS_WALLET" }
1589
+ );
1590
+ const attempt = useRef(0);
1591
+ useEffect3(() => {
1592
+ attempt.current += 1;
1593
+ if (!isOpen) return;
1594
+ setState(
1595
+ wallet ? { type: "READY", address: wallet.address, sign: IDLE_SIGN } : { type: "NEEDS_WALLET" }
1596
+ );
1597
+ }, [isOpen, wallet?.address]);
1598
+ useEffect3(
1599
+ () => () => {
1600
+ attempt.current += 1;
1601
+ },
1602
+ []
1603
+ );
1604
+ const onSign = useCallback(() => {
1605
+ if (!wallet || state.type !== "READY") return;
1606
+ attempt.current += 1;
1607
+ const gen = attempt.current;
1608
+ setState({
1609
+ type: "READY",
1610
+ address: wallet.address,
1611
+ sign: { type: "signing" }
1612
+ });
1613
+ connectExternalWalletFlow({
1614
+ coinlist,
1615
+ wallet,
1616
+ offerId,
1617
+ offerOptionId,
1618
+ challengeType,
1619
+ statement,
1620
+ // Cancels the flow before signing / binding if this attempt is superseded.
1621
+ isCancelled: () => attempt.current !== gen
1622
+ }).then((result) => {
1623
+ if (attempt.current !== gen) return;
1624
+ if (result.type === "success") {
1625
+ setState({ type: "CONNECTED", binding: result.binding });
1626
+ } else if (result.type === "error") {
1627
+ setState({
1628
+ type: "READY",
1629
+ address: wallet.address,
1630
+ sign: { type: "idle", error: displayError(result.error) }
1631
+ });
1632
+ }
1633
+ });
1634
+ }, [
1635
+ wallet,
1636
+ state,
1637
+ coinlist,
1638
+ offerId,
1639
+ offerOptionId,
1640
+ challengeType,
1641
+ statement
1642
+ ]);
1643
+ return { state, onSign };
1644
+ }
1645
+
1646
+ // src/client/components/requirements/ConnectWalletModal.tsx
1647
+ import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1648
+ function ConnectWalletModal({
1649
+ isOpen,
1650
+ onClose,
1651
+ offerId,
1652
+ optionId,
1653
+ wallet,
1654
+ onConnected,
1655
+ onRequestConnect,
1656
+ challengeType,
1657
+ statement,
1658
+ className
1659
+ }) {
1660
+ const { state, onSign } = useConnectWallet({
1661
+ isOpen,
1662
+ offerId,
1663
+ offerOptionId: optionId,
1664
+ wallet,
1665
+ challengeType,
1666
+ statement
1667
+ });
1668
+ const titleId = useId();
1669
+ useEffect4(() => {
1670
+ if (state.type === "CONNECTED") {
1671
+ onConnected?.(state.binding);
1672
+ onClose();
1673
+ }
1674
+ }, [state]);
1675
+ if (!isOpen || state.type === "CONNECTED") return null;
1676
+ return /* @__PURE__ */ jsx14("div", { className: "clcosdk:fixed clcosdk:inset-0 clcosdk:z-50 clcosdk:flex clcosdk:items-center clcosdk:justify-center clcosdk:bg-black/50 clcosdk:p-4", children: /* @__PURE__ */ jsxs11(
1677
+ "div",
1678
+ {
1679
+ role: "dialog",
1680
+ "aria-modal": "true",
1681
+ "aria-labelledby": titleId,
1682
+ className: cn(
1683
+ "clcosdk:flex clcosdk:w-full clcosdk:max-w-md clcosdk:flex-col clcosdk:gap-6 clcosdk:rounded-2xl clcosdk:bg-background clcosdk:p-6",
1684
+ className
1685
+ ),
1686
+ children: [
1687
+ /* @__PURE__ */ jsxs11("div", { className: "clcosdk:flex clcosdk:items-start clcosdk:justify-between clcosdk:gap-4", children: [
1688
+ /* @__PURE__ */ jsx14(
1689
+ "h3",
1690
+ {
1691
+ id: titleId,
1692
+ className: cn(typeClasses.h3, "clcosdk:text-text-primary"),
1693
+ children: "Connect your wallet"
1694
+ }
1695
+ ),
1696
+ /* @__PURE__ */ jsx14(CloseButton, { onClick: onClose })
1697
+ ] }),
1698
+ state.type === "NEEDS_WALLET" && /* @__PURE__ */ jsx14(NeedsWalletScreen, { onRequestConnect }),
1699
+ state.type === "READY" && /* @__PURE__ */ jsx14(
1700
+ ReadyScreen,
1701
+ {
1702
+ address: state.address,
1703
+ sign: state.sign,
1704
+ onSign,
1705
+ onRequestConnect
1706
+ }
1707
+ )
1708
+ ]
1709
+ }
1710
+ ) });
1711
+ }
1712
+ function NeedsWalletScreen({
1713
+ onRequestConnect
1714
+ }) {
1715
+ return /* @__PURE__ */ jsxs11("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-6", children: [
1716
+ /* @__PURE__ */ jsx14("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Connect an external wallet to prove ownership and bind it to this offer." }),
1717
+ onRequestConnect && /* @__PURE__ */ jsx14(ClButton, { fullWidth: true, onClick: onRequestConnect, children: "Connect wallet" })
1718
+ ] });
1719
+ }
1720
+ function ReadyScreen({
1721
+ address,
1722
+ sign,
1723
+ onSign,
1724
+ onRequestConnect
1725
+ }) {
1726
+ const hasTerminalError = sign.type === "idle" && sign.error !== null && !sign.error.retryable;
1727
+ return /* @__PURE__ */ jsxs11("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-6", children: [
1728
+ /* @__PURE__ */ jsx14("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Sign a message with your connected wallet to prove you own it. This does not cost gas or move any funds." }),
1729
+ /* @__PURE__ */ jsxs11("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1 clcosdk:border-t clcosdk:border-divider clcosdk:pt-4", children: [
1730
+ /* @__PURE__ */ jsx14("span", { className: cn(typeClasses.label, "clcosdk:text-text-secondary"), children: "Wallet address" }),
1731
+ /* @__PURE__ */ jsx14("span", { className: cn(typeClasses.body, "clcosdk:text-text-primary"), children: shortenAddress(address) })
1732
+ ] }),
1733
+ sign.type === "idle" && sign.error && // Live region: the error is inserted async after a failed attempt, so
1734
+ // screen readers need it announced rather than silently rendered.
1735
+ /* @__PURE__ */ jsx14("div", { role: "alert", children: /* @__PURE__ */ jsx14(AlertBanner, { variant: "error", icon: true, content: sign.error.message }) }),
1736
+ /* @__PURE__ */ jsx14(
1737
+ ClButton,
1738
+ {
1739
+ fullWidth: true,
1740
+ onClick: onSign,
1741
+ isLoading: sign.type === "signing",
1742
+ loadingText: "Waiting for signature...",
1743
+ disabled: hasTerminalError,
1744
+ children: "Sign & connect"
1745
+ }
1746
+ ),
1747
+ onRequestConnect && /* @__PURE__ */ jsx14(
1748
+ ClButton,
1749
+ {
1750
+ fullWidth: true,
1751
+ variant: "secondary",
1752
+ onClick: onRequestConnect,
1753
+ disabled: sign.type === "signing",
1754
+ children: "Use a different wallet"
1755
+ }
1756
+ )
1757
+ ] });
1758
+ }
1759
+
1760
+ // src/client/components/requirements/IdentityVerification.tsx
1761
+ import SumsubWebSdk from "@sumsub/websdk-react";
1762
+ import { useEffect as useEffect6, useRef as useRef3 } from "react";
1763
+
1764
+ // src/client/hooks/useKycToken.ts
1765
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef2, useState as useState5 } from "react";
1766
+ var IDLE_STATE = { type: "IDLE" };
1767
+ var LOADING_STATE2 = { type: "LOADING" };
1768
+ function useKycToken(levelName, reset) {
1769
+ const { coinlist } = useCoinList();
1770
+ const [kycTokenState, setKycTokenState] = useState5(IDLE_STATE);
1771
+ const resetPendingRef = useRef2(reset === true);
1772
+ const tokenIssuedRef = useRef2(false);
1773
+ useEffect5(() => {
1774
+ if (!tokenIssuedRef.current) {
1775
+ resetPendingRef.current = reset === true;
1776
+ }
1777
+ }, [reset]);
1778
+ const fetchToken = useCallback2(async () => {
1779
+ setKycTokenState(LOADING_STATE2);
1780
+ try {
1781
+ const kycToken = await coinlist.createKycToken(
1782
+ levelName,
1783
+ resetPendingRef.current
1784
+ );
1785
+ resetPendingRef.current = false;
1786
+ tokenIssuedRef.current = true;
1787
+ setKycTokenState({ type: "CONTENT", token: kycToken.token });
1788
+ return kycToken.token;
1789
+ } catch (error) {
1790
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
1791
+ setKycTokenState({ type: "ERROR", reason });
1792
+ throw error;
1793
+ }
1794
+ }, [coinlist, levelName]);
1795
+ return { kycTokenState, fetchToken };
1796
+ }
1797
+
1798
+ // src/client/components/requirements/IdentityVerification.tsx
1799
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1800
+ function IdentityVerification({
1801
+ levelName,
1802
+ reset,
1803
+ locale,
1804
+ onSubmitted,
1805
+ onError,
1806
+ className
1807
+ }) {
1808
+ const { kycTokenState, fetchToken } = useKycToken(levelName, reset);
1809
+ const fetchedRef = useRef3(false);
1810
+ useEffect6(() => {
1811
+ if (fetchedRef.current) return;
1812
+ fetchedRef.current = true;
1813
+ fetchToken().catch(() => {
1814
+ });
1815
+ }, [fetchToken]);
1816
+ const handleMessage = (type, payload) => {
1817
+ switch (type) {
1818
+ case "idCheck.onApplicantSubmitted":
1819
+ onSubmitted?.();
1820
+ break;
1821
+ case "idCheck.onError": {
1822
+ const message = payload?.message ?? "Verification error";
1823
+ onError?.(new Error(message));
1824
+ break;
1825
+ }
1826
+ default:
1827
+ break;
1828
+ }
1829
+ };
1830
+ const handleError = (error) => {
1831
+ onError?.(error instanceof Error ? error : new Error("Verification error"));
1832
+ };
1833
+ if (kycTokenState.type === "IDLE" || kycTokenState.type === "LOADING") {
1834
+ return /* @__PURE__ */ jsx15(
1835
+ "output",
1836
+ {
1837
+ "aria-live": "polite",
1838
+ className: cn(
1839
+ "clcosdk:flex clcosdk:min-h-40 clcosdk:items-center clcosdk:justify-center",
1840
+ className
1841
+ ),
1842
+ children: /* @__PURE__ */ jsx15("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Loading identity verification..." })
1843
+ }
1844
+ );
1845
+ }
1846
+ if (kycTokenState.type === "ERROR") {
1847
+ const errorMessage = kycTokenState.reason === "not-authenticated" ? "Sign in to verify your identity." : "Unable to start identity verification right now. Please try again.";
1848
+ return /* @__PURE__ */ jsxs12(
1849
+ "div",
1850
+ {
1851
+ className: cn("clcosdk:flex clcosdk:flex-col clcosdk:gap-4", className),
1852
+ children: [
1853
+ /* @__PURE__ */ jsx15(AlertBanner, { variant: "error", icon: true, content: errorMessage }),
1854
+ kycTokenState.reason !== "not-authenticated" && /* @__PURE__ */ jsx15(
1855
+ ClButton,
1856
+ {
1857
+ size: "sm",
1858
+ onClick: () => {
1859
+ fetchToken().catch(() => {
1860
+ });
1861
+ },
1862
+ children: "Try again"
1863
+ }
1864
+ )
1865
+ ]
1866
+ }
1867
+ );
1868
+ }
1869
+ return /* @__PURE__ */ jsx15("div", { className: cn("clcosdk:w-full", className), children: /* @__PURE__ */ jsx15(
1870
+ SumsubWebSdk,
1871
+ {
1872
+ accessToken: kycTokenState.token,
1873
+ expirationHandler: fetchToken,
1874
+ config: locale === void 0 ? {} : { lang: locale },
1875
+ options: { addViewportTag: false, adaptIframeHeight: true },
1876
+ onMessage: handleMessage,
1877
+ onError: handleError
1878
+ }
1879
+ ) });
1880
+ }
1881
+
1882
+ // src/client/components/requirements/RequirementItem.tsx
1883
+ import { ChevronRight } from "lucide-react";
1884
+
1885
+ // src/client/components/design-system/StepStatusIcon.tsx
1886
+ import { CircleCheck as CircleCheck2, CircleX, Info as Info2 } from "lucide-react";
1887
+ import { jsx as jsx16 } from "react/jsx-runtime";
1888
+ var statusConfig = {
1889
+ required: {
1890
+ Icon: Info2,
1891
+ colorClass: "clcosdk:text-warning",
1892
+ fillClass: "clcosdk:[&_circle]:fill-warning-subtle"
1893
+ },
1894
+ pending: {
1895
+ Icon: Info2,
1896
+ colorClass: "clcosdk:text-warning",
1897
+ fillClass: "clcosdk:[&_circle]:fill-warning-subtle"
1898
+ },
1899
+ completed: {
1900
+ Icon: CircleCheck2,
1901
+ colorClass: "clcosdk:text-success",
1902
+ fillClass: "clcosdk:[&_circle]:fill-success-subtle"
1903
+ },
1904
+ rejected: {
1905
+ Icon: CircleX,
1906
+ colorClass: "clcosdk:text-error",
1907
+ fillClass: "clcosdk:[&_circle]:fill-error-subtle"
1908
+ }
1909
+ };
1910
+ function StepStatusIcon({ status, className }) {
1911
+ const { Icon, colorClass, fillClass } = statusConfig[status];
1912
+ return /* @__PURE__ */ jsx16(
1913
+ Icon,
1914
+ {
1915
+ className: cn(
1916
+ "clcosdk:h-6 clcosdk:w-6 clcosdk:shrink-0",
1917
+ colorClass,
1918
+ fillClass,
1919
+ className
1920
+ ),
1921
+ "aria-hidden": "true"
1922
+ }
1923
+ );
1924
+ }
1925
+
1926
+ // src/client/components/requirements/RequirementItem.tsx
1927
+ import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1928
+ var RequirementVariant = {
1929
+ fromType(type) {
1930
+ switch (type) {
1931
+ case "external_wallet":
1932
+ case "whitelisted_wallet":
1933
+ return "wallet";
1934
+ default:
1935
+ return "verification";
1936
+ }
1937
+ }
1938
+ };
1939
+ var RequirementStatus = {
1940
+ fromStatusValue(status) {
1941
+ switch (status) {
1942
+ case "not_started":
1943
+ case "action_needed":
1944
+ return "required";
1945
+ case "in_progress":
1946
+ return "pending";
1947
+ case "completed":
1948
+ return "completed";
1949
+ case "rejected":
1950
+ return "rejected";
964
1951
  }
965
1952
  }
966
1953
  };
@@ -972,13 +1959,18 @@ var ChecklistStatus = {
972
1959
  }
973
1960
  };
974
1961
  var RequirementItemUi = {
975
- fromDomain(req, statusInfo, label, description, walletAddress) {
1962
+ fromDomain(req, statusInfo, label, description, addresses = []) {
976
1963
  return {
977
1964
  variant: RequirementVariant.fromType(req.type),
978
1965
  status: statusInfo ? RequirementStatus.fromStatusValue(statusInfo.status) : "required",
979
1966
  label,
980
1967
  description,
981
- walletAddress
1968
+ multiWallet: req.type === "whitelisted_wallet",
1969
+ connectedWallets: addresses.map((address) => ({
1970
+ id: address.id,
1971
+ address: address.address,
1972
+ shortAddress: shortenAddress(address.address)
1973
+ }))
982
1974
  };
983
1975
  }
984
1976
  };
@@ -990,9 +1982,9 @@ function VerificationActionZone({
990
1982
  switch (status) {
991
1983
  case "required":
992
1984
  if (onAction === null) return null;
993
- return /* @__PURE__ */ jsx13(ClButton, { size: "sm", onClick: onAction, children: "Continue" });
1985
+ return /* @__PURE__ */ jsx17(ClButton, { size: "sm", onClick: onAction, children: "Continue" });
994
1986
  case "pending":
995
- return /* @__PURE__ */ jsx13(
1987
+ return /* @__PURE__ */ jsx17(
996
1988
  AlertBanner,
997
1989
  {
998
1990
  variant: "warning",
@@ -1000,21 +1992,21 @@ function VerificationActionZone({
1000
1992
  }
1001
1993
  );
1002
1994
  case "completed":
1003
- return /* @__PURE__ */ jsx13(AlertBanner, { variant: "success", content: "Identity verified." });
1995
+ return /* @__PURE__ */ jsx17(AlertBanner, { variant: "success", content: "Identity verified." });
1004
1996
  case "rejected":
1005
- return /* @__PURE__ */ jsx13(
1997
+ return /* @__PURE__ */ jsx17(
1006
1998
  AlertBanner,
1007
1999
  {
1008
2000
  variant: "error",
1009
- content: onContactSupport === null ? "Verification rejected." : /* @__PURE__ */ jsxs9(Fragment2, { children: [
2001
+ content: onContactSupport === null ? "Verification rejected." : /* @__PURE__ */ jsxs13(Fragment3, { children: [
1010
2002
  "Verification rejected.",
1011
2003
  " ",
1012
- /* @__PURE__ */ jsx13(
2004
+ /* @__PURE__ */ jsx17(
1013
2005
  "button",
1014
2006
  {
1015
2007
  type: "button",
1016
2008
  onClick: onContactSupport,
1017
- className: "underline",
2009
+ className: "clcosdk:underline",
1018
2010
  children: "Contact support"
1019
2011
  }
1020
2012
  ),
@@ -1026,25 +2018,61 @@ function VerificationActionZone({
1026
2018
  }
1027
2019
  function WalletActionZone({
1028
2020
  status,
1029
- walletAddress,
1030
- onAction
2021
+ multiWallet,
2022
+ connectedWallets,
2023
+ walletsStatus,
2024
+ onAction,
2025
+ onRemoveWallet
1031
2026
  }) {
2027
+ if (multiWallet) {
2028
+ return /* @__PURE__ */ jsxs13(Fragment3, { children: [
2029
+ /* @__PURE__ */ jsx17(
2030
+ ConnectedWalletList,
2031
+ {
2032
+ wallets: connectedWallets,
2033
+ onRemove: onRemoveWallet,
2034
+ onAdd: onAction ?? void 0,
2035
+ status: walletsStatus
2036
+ }
2037
+ ),
2038
+ status === "rejected" && /* @__PURE__ */ jsx17(
2039
+ AlertBanner,
2040
+ {
2041
+ variant: "error",
2042
+ content: "One of these wallets is not eligible for this sale."
2043
+ }
2044
+ )
2045
+ ] });
2046
+ }
1032
2047
  if (status === "completed") {
1033
- return /* @__PURE__ */ jsxs9(Fragment2, { children: [
1034
- onAction !== null && /* @__PURE__ */ jsx13(ClButton, { size: "sm", variant: "secondary", onClick: onAction, children: "Change wallet" }),
1035
- /* @__PURE__ */ jsx13(
2048
+ const connected = connectedWallets[0];
2049
+ return /* @__PURE__ */ jsxs13(Fragment3, { children: [
2050
+ onAction !== null && /* @__PURE__ */ jsx17(ClButton, { size: "sm", variant: "secondary", onClick: onAction, children: "Change wallet" }),
2051
+ walletsStatus === "loading" ? /* @__PURE__ */ jsx17(
2052
+ AlertBanner,
2053
+ {
2054
+ variant: "info",
2055
+ content: "Loading your connected wallet\u2026"
2056
+ }
2057
+ ) : walletsStatus === "error" ? /* @__PURE__ */ jsx17(
2058
+ AlertBanner,
2059
+ {
2060
+ variant: "error",
2061
+ content: "Couldn't load your connected wallet."
2062
+ }
2063
+ ) : /* @__PURE__ */ jsx17(
1036
2064
  AlertBanner,
1037
2065
  {
1038
2066
  variant: "success",
1039
- content: walletAddress ?? "Connected: 0xBa4b...9f2c"
2067
+ content: connected ? connected.shortAddress : "Wallet connected."
1040
2068
  }
1041
2069
  )
1042
2070
  ] });
1043
2071
  }
1044
2072
  if (status === "rejected") {
1045
- return /* @__PURE__ */ jsxs9(Fragment2, { children: [
1046
- onAction !== null && /* @__PURE__ */ jsx13(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" }),
1047
- /* @__PURE__ */ jsx13(
2073
+ return /* @__PURE__ */ jsxs13(Fragment3, { children: [
2074
+ onAction !== null && /* @__PURE__ */ jsx17(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" }),
2075
+ /* @__PURE__ */ jsx17(
1048
2076
  AlertBanner,
1049
2077
  {
1050
2078
  variant: "error",
@@ -1054,7 +2082,7 @@ function WalletActionZone({
1054
2082
  ] });
1055
2083
  }
1056
2084
  if (onAction === null) return null;
1057
- return /* @__PURE__ */ jsx13(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" });
2085
+ return /* @__PURE__ */ jsx17(ClButton, { size: "sm", onClick: onAction, children: "Connect wallet" });
1058
2086
  }
1059
2087
  function RequirementItem({
1060
2088
  ui,
@@ -1062,92 +2090,588 @@ function RequirementItem({
1062
2090
  onToggle,
1063
2091
  className,
1064
2092
  onAction,
2093
+ onRemoveWallet,
2094
+ walletsStatus,
1065
2095
  onContactSupport
1066
2096
  }) {
1067
- return /* @__PURE__ */ jsxs9("div", { className: cn("flex w-full flex-col", className), children: [
1068
- /* @__PURE__ */ jsxs9(
1069
- "button",
2097
+ return /* @__PURE__ */ jsx17(CoinListStyleScope, { children: /* @__PURE__ */ jsxs13(
2098
+ "div",
2099
+ {
2100
+ className: cn(
2101
+ "clcosdk:flex clcosdk:w-full clcosdk:flex-col",
2102
+ className
2103
+ ),
2104
+ children: [
2105
+ /* @__PURE__ */ jsxs13(
2106
+ "button",
2107
+ {
2108
+ type: "button",
2109
+ onClick: onToggle,
2110
+ "aria-expanded": expanded,
2111
+ className: "clcosdk:flex clcosdk:h-14 clcosdk:w-full clcosdk:cursor-pointer clcosdk:items-center clcosdk:justify-between clcosdk:rounded-2xl clcosdk:px-2 clcosdk:transition-colors clcosdk:hover:bg-surface",
2112
+ children: [
2113
+ /* @__PURE__ */ jsxs13("div", { className: "clcosdk:flex clcosdk:items-center clcosdk:gap-3", children: [
2114
+ /* @__PURE__ */ jsx17(StepStatusIcon, { status: ui.status }),
2115
+ /* @__PURE__ */ jsx17(
2116
+ "span",
2117
+ {
2118
+ className: cn(
2119
+ typeClasses.body,
2120
+ "clcosdk:whitespace-nowrap clcosdk:font-medium clcosdk:text-text-primary"
2121
+ ),
2122
+ children: ui.label
2123
+ }
2124
+ )
2125
+ ] }),
2126
+ /* @__PURE__ */ jsx17(
2127
+ ChevronRight,
2128
+ {
2129
+ className: cn(
2130
+ "clcosdk:h-6 clcosdk:w-6 clcosdk:shrink-0 clcosdk:text-text-secondary clcosdk:transition-transform clcosdk:duration-300",
2131
+ expanded && "clcosdk:rotate-90"
2132
+ ),
2133
+ "aria-hidden": "true"
2134
+ }
2135
+ )
2136
+ ]
2137
+ }
2138
+ ),
2139
+ /* @__PURE__ */ jsx17(
2140
+ "div",
2141
+ {
2142
+ className: cn(
2143
+ "clcosdk:grid clcosdk:transition-[grid-template-rows] clcosdk:duration-300 clcosdk:ease-in-out",
2144
+ expanded ? "clcosdk:grid-rows-[1fr]" : "clcosdk:grid-rows-[0fr]"
2145
+ ),
2146
+ children: /* @__PURE__ */ jsx17("div", { className: "clcosdk:min-h-0 clcosdk:overflow-hidden", children: /* @__PURE__ */ jsxs13("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-4 clcosdk:px-2 clcosdk:pb-4", children: [
2147
+ /* @__PURE__ */ jsx17(
2148
+ "p",
2149
+ {
2150
+ className: cn(
2151
+ typeClasses.subhead,
2152
+ "clcosdk:text-text-secondary"
2153
+ ),
2154
+ children: ui.description
2155
+ }
2156
+ ),
2157
+ ui.variant === "verification" ? /* @__PURE__ */ jsx17(
2158
+ VerificationActionZone,
2159
+ {
2160
+ status: ui.status,
2161
+ onAction,
2162
+ onContactSupport
2163
+ }
2164
+ ) : /* @__PURE__ */ jsx17(
2165
+ WalletActionZone,
2166
+ {
2167
+ status: ui.status,
2168
+ multiWallet: ui.multiWallet,
2169
+ connectedWallets: ui.connectedWallets,
2170
+ walletsStatus,
2171
+ onAction,
2172
+ onRemoveWallet
2173
+ }
2174
+ )
2175
+ ] }) })
2176
+ }
2177
+ )
2178
+ ]
2179
+ }
2180
+ ) });
2181
+ }
2182
+
2183
+ // src/client/components/requirements/RequirementsChecklist.tsx
2184
+ import { useEffect as useEffect11, useRef as useRef4, useState as useState9 } from "react";
2185
+
2186
+ // src/client/components/requirements/TaxDocumentModal.tsx
2187
+ import { useEffect as useEffect8 } from "react";
2188
+
2189
+ // src/client/hooks/useTaxDocument.ts
2190
+ import { useCallback as useCallback3, useEffect as useEffect7, useState as useState6 } from "react";
2191
+ var IDLE_SIGN2 = { type: "idle", error: null };
2192
+ var LOADING_STATE3 = { type: "LOADING" };
2193
+ var SIGN_FAILED_MESSAGE = "Unable to sign document. Please try again.";
2194
+ var DOB_INVALID_MESSAGE = "Enter your date of birth as MM/DD/YYYY.";
2195
+ var FIELD_KEYS = {
2196
+ fullLegalName: "Full Name",
2197
+ dob: "DOB",
2198
+ countryOfCitizenship: "Country Of Citizenship",
2199
+ taxId: "Foreign Tax Number",
2200
+ permanentAddress: "Permanent Address"
2201
+ };
2202
+ function formatUsDate(date) {
2203
+ const month = String(date.getMonth() + 1).padStart(2, "0");
2204
+ const day = String(date.getDate()).padStart(2, "0");
2205
+ const year = String(date.getFullYear()).padStart(4, "0");
2206
+ return `${month}/${day}/${year}`;
2207
+ }
2208
+ function formatIsoDateOnly(isoDate) {
2209
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
2210
+ if (!match) return "";
2211
+ const [, year, month, day] = match;
2212
+ return `${month}/${day}/${year}`;
2213
+ }
2214
+ function isValidUsDate(value) {
2215
+ const match = /^(\d{2})\/(\d{2})\/(\d{4})$/.exec(value);
2216
+ if (!match) return false;
2217
+ const [, mm, dd, yyyy] = match;
2218
+ const month = Number(mm);
2219
+ const day = Number(dd);
2220
+ const year = Number(yyyy);
2221
+ const date = new Date(year, month - 1, day);
2222
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
2223
+ }
2224
+ function mapPiiToFields(pii) {
2225
+ const { street, city, state, postalCode } = pii.permanentAddress;
2226
+ const cityLine = [postalCode, city].filter(Boolean).join(" ");
2227
+ const permanentAddress = [street, cityLine, state].filter(Boolean).join(", ");
2228
+ return {
2229
+ fullLegalName: pii.fullLegalName ?? "",
2230
+ // Date-only ISO from the backend, shown as MM/DD/YYYY. Blank until the
2231
+ // backend has a date of birth (the field is editable in the meantime).
2232
+ dob: formatIsoDateOnly(pii.dateOfBirth ?? ""),
2233
+ countryOfCitizenship: pii.jurisdiction?.name ?? "",
2234
+ taxId: pii.taxId ?? "",
2235
+ permanentAddress
2236
+ };
2237
+ }
2238
+ function addressPartsFromPii(pii) {
2239
+ return {
2240
+ city: pii.permanentAddress.city ?? "",
2241
+ country: pii.permanentAddress.country ?? ""
2242
+ };
2243
+ }
2244
+ function hasAnyFieldValue(fields) {
2245
+ return Object.values(fields).some((value) => value.trim() !== "");
2246
+ }
2247
+ function toSubmissionFields(fields, addressParts) {
2248
+ return {
2249
+ [FIELD_KEYS.fullLegalName]: fields.fullLegalName,
2250
+ [FIELD_KEYS.dob]: fields.dob,
2251
+ [FIELD_KEYS.countryOfCitizenship]: fields.countryOfCitizenship,
2252
+ [FIELD_KEYS.taxId]: fields.taxId,
2253
+ [FIELD_KEYS.permanentAddress]: fields.permanentAddress,
2254
+ City: addressParts.city,
2255
+ Country: addressParts.country,
2256
+ Signature: fields.fullLegalName,
2257
+ Date: formatUsDate(/* @__PURE__ */ new Date())
2258
+ };
2259
+ }
2260
+ function useTaxDocument({
2261
+ isOpen
2262
+ }) {
2263
+ const { coinlist, isReady } = useCoinList();
2264
+ const [state, setState] = useState6(LOADING_STATE3);
2265
+ useEffect7(() => {
2266
+ let isCancelled = false;
2267
+ if (!isOpen) {
2268
+ return () => {
2269
+ isCancelled = true;
2270
+ };
2271
+ }
2272
+ if (!isReady) {
2273
+ setState(LOADING_STATE3);
2274
+ return () => {
2275
+ isCancelled = true;
2276
+ };
2277
+ }
2278
+ setState(LOADING_STATE3);
2279
+ coinlist.fetchPii().then((pii) => {
2280
+ if (isCancelled) return;
2281
+ if (pii.kind === "company") {
2282
+ setState({ type: "SIGN_ONLY", sign: IDLE_SIGN2 });
2283
+ return;
2284
+ }
2285
+ const fields = mapPiiToFields(pii);
2286
+ setState({
2287
+ type: "REVIEW",
2288
+ fields,
2289
+ addressParts: addressPartsFromPii(pii),
2290
+ piiUnavailable: !hasAnyFieldValue(fields),
2291
+ sign: IDLE_SIGN2
2292
+ });
2293
+ }).catch((error) => {
2294
+ if (isCancelled) return;
2295
+ setState({
2296
+ type: "ERROR",
2297
+ reason: error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error"
2298
+ });
2299
+ });
2300
+ return () => {
2301
+ isCancelled = true;
2302
+ };
2303
+ }, [coinlist, isReady, isOpen]);
2304
+ const onEditField = useCallback3(
2305
+ (field, value) => {
2306
+ setState(
2307
+ (prev) => prev.type === "EDITING" ? { ...prev, fields: { ...prev.fields, [field]: value } } : prev
2308
+ );
2309
+ },
2310
+ []
2311
+ );
2312
+ const onStartEdit = useCallback3(() => {
2313
+ setState(
2314
+ (prev) => prev.type === "REVIEW" ? {
2315
+ type: "EDITING",
2316
+ fields: prev.fields,
2317
+ original: prev.fields,
2318
+ addressParts: prev.addressParts,
2319
+ piiUnavailable: prev.piiUnavailable
2320
+ } : prev
2321
+ );
2322
+ }, []);
2323
+ const onSaveEdit = useCallback3(() => {
2324
+ setState(
2325
+ (prev) => prev.type === "EDITING" ? {
2326
+ type: "REVIEW",
2327
+ fields: prev.fields,
2328
+ addressParts: prev.addressParts,
2329
+ piiUnavailable: prev.piiUnavailable,
2330
+ sign: IDLE_SIGN2
2331
+ } : prev
2332
+ );
2333
+ }, []);
2334
+ const onCancelEdit = useCallback3(() => {
2335
+ setState(
2336
+ (prev) => prev.type === "EDITING" ? {
2337
+ type: "REVIEW",
2338
+ fields: prev.original,
2339
+ addressParts: prev.addressParts,
2340
+ piiUnavailable: prev.piiUnavailable,
2341
+ sign: IDLE_SIGN2
2342
+ } : prev
2343
+ );
2344
+ }, []);
2345
+ const onSign = useCallback3(() => {
2346
+ if (state.type !== "REVIEW" && state.type !== "SIGN_ONLY") return;
2347
+ if (state.type === "REVIEW" && state.fields.dob.trim() !== "" && !isValidUsDate(state.fields.dob)) {
2348
+ setState({
2349
+ ...state,
2350
+ sign: { type: "idle", error: DOB_INVALID_MESSAGE }
2351
+ });
2352
+ return;
2353
+ }
2354
+ setState({ ...state, sign: { type: "signing" } });
2355
+ coinlist.submitDocument(
2356
+ "tax_certification",
2357
+ state.type === "REVIEW" ? toSubmissionFields(state.fields, state.addressParts) : {}
2358
+ ).then((submission) => {
2359
+ setState({ type: "SUBMITTED", submission });
2360
+ }).catch(() => {
2361
+ setState(
2362
+ (prev) => prev.type === "REVIEW" || prev.type === "SIGN_ONLY" ? { ...prev, sign: { type: "idle", error: SIGN_FAILED_MESSAGE } } : prev
2363
+ );
2364
+ });
2365
+ }, [state, coinlist]);
2366
+ return { state, onEditField, onStartEdit, onSaveEdit, onCancelEdit, onSign };
2367
+ }
2368
+
2369
+ // src/client/components/requirements/TaxDocumentModal.tsx
2370
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
2371
+ var FIELD_ROWS = [
2372
+ { field: "fullLegalName", label: "Full legal name" },
2373
+ { field: "dob", label: "Date of birth", placeholder: "MM/DD/YYYY" },
2374
+ { field: "countryOfCitizenship", label: "Country of citizenship" },
2375
+ { field: "taxId", label: "Tax ID" },
2376
+ { field: "permanentAddress", label: "Permanent address" }
2377
+ ];
2378
+ function TaxDocumentModal({
2379
+ isOpen,
2380
+ onClose,
2381
+ onSubmitted,
2382
+ className
2383
+ }) {
2384
+ const { state, onEditField, onStartEdit, onSaveEdit, onCancelEdit, onSign } = useTaxDocument({ isOpen });
2385
+ useEffect8(() => {
2386
+ if (state.type === "SUBMITTED") {
2387
+ onSubmitted?.(state.submission);
2388
+ onClose();
2389
+ }
2390
+ }, [state]);
2391
+ if (!isOpen || state.type === "SUBMITTED") return null;
2392
+ return /* @__PURE__ */ jsx18("div", { className: "clcosdk:fixed clcosdk:inset-0 clcosdk:z-50 clcosdk:flex clcosdk:items-center clcosdk:justify-center clcosdk:bg-black/50 clcosdk:p-4", children: /* @__PURE__ */ jsxs14(
2393
+ "div",
2394
+ {
2395
+ className: cn(
2396
+ "clcosdk:flex clcosdk:w-full clcosdk:max-w-md clcosdk:flex-col clcosdk:gap-6 clcosdk:rounded-2xl clcosdk:bg-background clcosdk:p-6",
2397
+ className
2398
+ ),
2399
+ children: [
2400
+ /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:items-start clcosdk:justify-between clcosdk:gap-4", children: [
2401
+ /* @__PURE__ */ jsx18(TaxDocumentModalHeading, { state }),
2402
+ /* @__PURE__ */ jsx18(CloseButton, { onClick: onClose })
2403
+ ] }),
2404
+ state.type === "LOADING" && /* @__PURE__ */ jsx18(LoadingScreen, {}),
2405
+ state.type === "ERROR" && /* @__PURE__ */ jsx18(ErrorScreen, { reason: state.reason }),
2406
+ state.type === "REVIEW" && /* @__PURE__ */ jsx18(
2407
+ ReviewScreen,
2408
+ {
2409
+ fields: state.fields,
2410
+ piiUnavailable: state.piiUnavailable,
2411
+ sign: state.sign,
2412
+ onStartEdit,
2413
+ onSign
2414
+ }
2415
+ ),
2416
+ state.type === "EDITING" && /* @__PURE__ */ jsx18(
2417
+ EditScreen,
2418
+ {
2419
+ fields: state.fields,
2420
+ onEditField,
2421
+ onSaveEdit,
2422
+ onCancelEdit
2423
+ }
2424
+ ),
2425
+ state.type === "SIGN_ONLY" && /* @__PURE__ */ jsx18(SignOnlyScreen, { sign: state.sign, onSign })
2426
+ ]
2427
+ }
2428
+ ) });
2429
+ }
2430
+ function TaxDocumentModalHeading({
2431
+ state
2432
+ }) {
2433
+ const title = state.type === "EDITING" ? "Edit your W-8BEN" : state.type === "REVIEW" ? "Confirm your W-8BEN" : state.type === "SIGN_ONLY" ? "Confirm your W-8BEN-E" : "Tax document";
2434
+ return /* @__PURE__ */ jsx18("h3", { className: cn(typeClasses.h3, "clcosdk:text-text-primary"), children: title });
2435
+ }
2436
+ function LoadingScreen() {
2437
+ return /* @__PURE__ */ jsx18(
2438
+ "output",
2439
+ {
2440
+ "aria-live": "polite",
2441
+ className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-4",
2442
+ children: FIELD_ROWS.map(({ field }) => /* @__PURE__ */ jsxs14(
2443
+ "div",
2444
+ {
2445
+ className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1",
2446
+ children: [
2447
+ /* @__PURE__ */ jsx18("div", { className: "clcosdk:h-3 clcosdk:w-28 clcosdk:animate-pulse clcosdk:rounded clcosdk:bg-surface" }),
2448
+ /* @__PURE__ */ jsx18("div", { className: "clcosdk:h-5 clcosdk:w-48 clcosdk:animate-pulse clcosdk:rounded clcosdk:bg-surface" })
2449
+ ]
2450
+ },
2451
+ field
2452
+ ))
2453
+ }
2454
+ );
2455
+ }
2456
+ function ErrorScreen({
2457
+ reason
2458
+ }) {
2459
+ return /* @__PURE__ */ jsx18(
2460
+ AlertBanner,
2461
+ {
2462
+ variant: "error",
2463
+ icon: true,
2464
+ content: reason === "not-authenticated" ? "Sign in to complete your tax document." : "Unable to load your tax information right now. Please try again."
2465
+ }
2466
+ );
2467
+ }
2468
+ function ReviewScreen({
2469
+ fields,
2470
+ piiUnavailable,
2471
+ sign,
2472
+ onStartEdit,
2473
+ onSign
2474
+ }) {
2475
+ return /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-6", children: [
2476
+ /* @__PURE__ */ jsx18("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Review your information below. If anything looks incorrect, edit before signing." }),
2477
+ piiUnavailable && /* @__PURE__ */ jsx18(
2478
+ AlertBanner,
1070
2479
  {
1071
- type: "button",
1072
- onClick: onToggle,
1073
- "aria-expanded": expanded,
1074
- className: "flex h-14 w-full cursor-pointer items-center justify-between rounded-2xl px-2 transition-colors hover:bg-surface",
2480
+ variant: "info",
2481
+ icon: true,
2482
+ content: "We don't have this information on file yet \u2014 please fill it in below."
2483
+ }
2484
+ ),
2485
+ /* @__PURE__ */ jsx18("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-4 clcosdk:border-t clcosdk:border-divider clcosdk:pt-4", children: FIELD_ROWS.map(({ field, label }) => /* @__PURE__ */ jsxs14(
2486
+ "div",
2487
+ {
2488
+ className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1",
1075
2489
  children: [
1076
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-3", children: [
1077
- /* @__PURE__ */ jsx13(StepStatusIcon, { status: ui.status }),
1078
- /* @__PURE__ */ jsx13(
1079
- "span",
1080
- {
1081
- className: cn(
1082
- typeClasses.body,
1083
- "whitespace-nowrap font-medium text-text-primary"
1084
- ),
1085
- children: ui.label
1086
- }
1087
- )
1088
- ] }),
1089
- /* @__PURE__ */ jsx13(
1090
- ChevronRight,
2490
+ /* @__PURE__ */ jsx18(
2491
+ "span",
1091
2492
  {
1092
- className: cn(
1093
- "h-6 w-6 shrink-0 text-text-secondary transition-transform duration-300",
1094
- expanded && "rotate-90"
1095
- ),
1096
- "aria-hidden": "true"
2493
+ className: cn(typeClasses.label, "clcosdk:text-text-secondary"),
2494
+ children: label
1097
2495
  }
1098
- )
2496
+ ),
2497
+ /* @__PURE__ */ jsx18("span", { className: cn(typeClasses.body, "clcosdk:text-text-primary"), children: fields[field] || "\u2014" })
1099
2498
  ]
1100
- }
1101
- ),
1102
- /* @__PURE__ */ jsx13(
1103
- "div",
2499
+ },
2500
+ field
2501
+ )) }),
2502
+ /* @__PURE__ */ jsx18("p", { className: cn(typeClasses.caption, "clcosdk:text-text-secondary"), children: "By signing, you certify that the information above is accurate and that you are the beneficial owner of the income this form relates to. This document cannot be edited once signed." }),
2503
+ sign.type === "idle" && sign.error && /* @__PURE__ */ jsx18(AlertBanner, { variant: "error", icon: true, content: sign.error }),
2504
+ /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-2", children: [
2505
+ /* @__PURE__ */ jsx18(
2506
+ ClButton,
2507
+ {
2508
+ fullWidth: true,
2509
+ onClick: onSign,
2510
+ isLoading: sign.type === "signing",
2511
+ loadingText: "Signing...",
2512
+ children: "Sign W-8BEN"
2513
+ }
2514
+ ),
2515
+ /* @__PURE__ */ jsx18(
2516
+ ClButton,
2517
+ {
2518
+ fullWidth: true,
2519
+ variant: "secondary",
2520
+ onClick: onStartEdit,
2521
+ disabled: sign.type === "signing",
2522
+ children: "Edit information"
2523
+ }
2524
+ )
2525
+ ] })
2526
+ ] });
2527
+ }
2528
+ function EditScreen({
2529
+ fields,
2530
+ onEditField,
2531
+ onSaveEdit,
2532
+ onCancelEdit
2533
+ }) {
2534
+ return /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-6", children: [
2535
+ /* @__PURE__ */ jsx18("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-4 clcosdk:border-t clcosdk:border-divider clcosdk:pt-4", children: FIELD_ROWS.map(({ field, label, placeholder }) => /* @__PURE__ */ jsxs14(
2536
+ "label",
1104
2537
  {
1105
- className: cn(
1106
- "grid transition-[grid-template-rows] duration-300 ease-in-out",
1107
- expanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
1108
- ),
1109
- children: /* @__PURE__ */ jsx13("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-4 px-2 pb-4", children: [
1110
- /* @__PURE__ */ jsx13("p", { className: cn(typeClasses.subhead, "text-text-secondary"), children: ui.description }),
1111
- ui.variant === "verification" ? /* @__PURE__ */ jsx13(
1112
- VerificationActionZone,
2538
+ className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-1",
2539
+ children: [
2540
+ /* @__PURE__ */ jsx18(
2541
+ "span",
1113
2542
  {
1114
- status: ui.status,
1115
- onAction,
1116
- onContactSupport
2543
+ className: cn(typeClasses.label, "clcosdk:text-text-secondary"),
2544
+ children: label
1117
2545
  }
1118
- ) : /* @__PURE__ */ jsx13(
1119
- WalletActionZone,
2546
+ ),
2547
+ /* @__PURE__ */ jsx18(
2548
+ "input",
1120
2549
  {
1121
- status: ui.status,
1122
- walletAddress: ui.walletAddress,
1123
- onAction
2550
+ type: "text",
2551
+ value: fields[field],
2552
+ placeholder,
2553
+ onChange: (e) => onEditField(field, e.target.value),
2554
+ className: cn(
2555
+ typeClasses.body,
2556
+ "clcosdk:rounded-lg clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:px-3 clcosdk:py-2 clcosdk:text-text-primary clcosdk:focus-visible:outline-none clcosdk:focus-visible:ring-2 clcosdk:focus-visible:ring-gray-400"
2557
+ )
1124
2558
  }
1125
2559
  )
1126
- ] }) })
2560
+ ]
2561
+ },
2562
+ field
2563
+ )) }),
2564
+ /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-2", children: [
2565
+ /* @__PURE__ */ jsx18(ClButton, { fullWidth: true, onClick: onSaveEdit, children: "Save & review" }),
2566
+ /* @__PURE__ */ jsx18(ClButton, { fullWidth: true, variant: "secondary", onClick: onCancelEdit, children: "Cancel" })
2567
+ ] })
2568
+ ] });
2569
+ }
2570
+ function SignOnlyScreen({
2571
+ sign,
2572
+ onSign
2573
+ }) {
2574
+ return /* @__PURE__ */ jsxs14("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-6", children: [
2575
+ /* @__PURE__ */ jsx18("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "As an entity investor, you must complete a W-8BEN-E to confirm your foreign tax status and claim applicable treaty benefits. You'll receive an email to complete the signing." }),
2576
+ sign.type === "idle" && sign.error && /* @__PURE__ */ jsx18(AlertBanner, { variant: "error", icon: true, content: sign.error }),
2577
+ /* @__PURE__ */ jsx18(
2578
+ ClButton,
2579
+ {
2580
+ fullWidth: true,
2581
+ onClick: onSign,
2582
+ isLoading: sign.type === "signing",
2583
+ loadingText: "Signing...",
2584
+ children: "Sign W-8BEN-E"
1127
2585
  }
1128
2586
  )
1129
2587
  ] });
1130
2588
  }
1131
2589
 
1132
- // src/client/components/requirements/RequirementsChecklist.tsx
1133
- import { useState as useState4 } from "react";
2590
+ // src/client/hooks/useOptionAddresses.ts
2591
+ import { useCallback as useCallback4, useEffect as useEffect9, useState as useState7 } from "react";
2592
+ var LOADING_STATE4 = { type: "LOADING" };
2593
+ var EMPTY_STATE = {
2594
+ type: "CONTENT",
2595
+ addresses: []
2596
+ };
2597
+ function useOptionAddresses(offerId, offerOptionId, options = {}) {
2598
+ const { coinlist, isReady } = useCoinList();
2599
+ const { data, enabled = true } = options;
2600
+ const [addressesState, setAddressesState] = useState7(
2601
+ data !== void 0 ? { type: "CONTENT", addresses: data } : LOADING_STATE4
2602
+ );
2603
+ const [fetchKey, setFetchKey] = useState7(0);
2604
+ const refetch = useCallback4(() => setFetchKey((k) => k + 1), []);
2605
+ const disconnect = useCallback4(
2606
+ async (addressId) => {
2607
+ await coinlist.removeOptionAddress(offerId, addressId);
2608
+ refetch();
2609
+ },
2610
+ [coinlist, offerId, refetch]
2611
+ );
2612
+ useEffect9(() => {
2613
+ let isCancelled = false;
2614
+ if (!enabled) {
2615
+ setAddressesState(
2616
+ data !== void 0 ? { type: "CONTENT", addresses: data } : EMPTY_STATE
2617
+ );
2618
+ return () => {
2619
+ isCancelled = true;
2620
+ };
2621
+ }
2622
+ if (!isReady) {
2623
+ if (data === void 0) {
2624
+ setAddressesState(LOADING_STATE4);
2625
+ }
2626
+ return () => {
2627
+ isCancelled = true;
2628
+ };
2629
+ }
2630
+ if (data !== void 0 && fetchKey === 0) {
2631
+ return () => {
2632
+ isCancelled = true;
2633
+ };
2634
+ }
2635
+ if (coinlist.getAuthState() === "logged-out") {
2636
+ setAddressesState({ type: "ERROR", reason: "not-authenticated" });
2637
+ return () => {
2638
+ isCancelled = true;
2639
+ };
2640
+ }
2641
+ setAddressesState(LOADING_STATE4);
2642
+ coinlist.listOptionAddresses(offerId, offerOptionId).then((addresses) => {
2643
+ if (!isCancelled) {
2644
+ setAddressesState({ type: "CONTENT", addresses });
2645
+ }
2646
+ }).catch((error) => {
2647
+ if (!isCancelled) {
2648
+ const reason = error instanceof NotAuthenticatedError ? "not-authenticated" : "generic-error";
2649
+ setAddressesState({ type: "ERROR", reason });
2650
+ }
2651
+ });
2652
+ return () => {
2653
+ isCancelled = true;
2654
+ };
2655
+ }, [coinlist, isReady, enabled, offerId, offerOptionId, fetchKey]);
2656
+ return { addressesState, refetch, disconnect };
2657
+ }
1134
2658
 
1135
2659
  // src/client/hooks/useRequirements.ts
1136
- import { useCallback, useEffect as useEffect3, useState as useState3 } from "react";
1137
- var LOADING_STATE2 = { type: "LOADING" };
2660
+ import { useCallback as useCallback5, useEffect as useEffect10, useState as useState8 } from "react";
2661
+ var LOADING_STATE5 = { type: "LOADING" };
1138
2662
  function useRequirements(offerId, options = {}) {
1139
2663
  const { coinlist, isReady } = useCoinList();
1140
2664
  const { data } = options;
1141
- const [requirementsState, setRequirementsState] = useState3(
1142
- data !== void 0 ? { type: "CONTENT", ...data } : LOADING_STATE2
2665
+ const [requirementsState, setRequirementsState] = useState8(
2666
+ data !== void 0 ? { type: "CONTENT", ...data } : LOADING_STATE5
1143
2667
  );
1144
- const [fetchKey, setFetchKey] = useState3(0);
1145
- const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
1146
- useEffect3(() => {
2668
+ const [fetchKey, setFetchKey] = useState8(0);
2669
+ const refetch = useCallback5(() => setFetchKey((k) => k + 1), []);
2670
+ useEffect10(() => {
1147
2671
  let isCancelled = false;
1148
2672
  if (!isReady) {
1149
2673
  if (data === void 0) {
1150
- setRequirementsState(LOADING_STATE2);
2674
+ setRequirementsState(LOADING_STATE5);
1151
2675
  }
1152
2676
  return () => {
1153
2677
  isCancelled = true;
@@ -1158,7 +2682,13 @@ function useRequirements(offerId, options = {}) {
1158
2682
  isCancelled = true;
1159
2683
  };
1160
2684
  }
1161
- setRequirementsState(LOADING_STATE2);
2685
+ if (coinlist.getAuthState() === "logged-out") {
2686
+ setRequirementsState({ type: "ERROR", reason: "not-authenticated" });
2687
+ return () => {
2688
+ isCancelled = true;
2689
+ };
2690
+ }
2691
+ setRequirementsState(LOADING_STATE5);
1162
2692
  Promise.all([
1163
2693
  coinlist.fetchOfferRequirements(offerId),
1164
2694
  coinlist.fetchRequirementStatuses(offerId)
@@ -1184,7 +2714,8 @@ function useRequirements(offerId, options = {}) {
1184
2714
  }
1185
2715
 
1186
2716
  // src/client/components/requirements/RequirementsChecklist.tsx
1187
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
2717
+ import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
2718
+ var isWalletRequirement = (requirement) => RequirementVariant.fromType(requirement.type) === "wallet";
1188
2719
  var DEFAULT_LABELS = {
1189
2720
  kyc_approved: "KYC Verification",
1190
2721
  identity_verified: "Identity Verification",
@@ -1193,7 +2724,8 @@ var DEFAULT_LABELS = {
1193
2724
  external_wallet: "External Wallet",
1194
2725
  whitelisted_wallet: "Whitelisted Wallet",
1195
2726
  jurisdiction: "Jurisdiction Check",
1196
- accreditation: "Accreditation"
2727
+ accreditation: "Accreditation",
2728
+ document: "Document"
1197
2729
  };
1198
2730
  var DEFAULT_DESCRIPTIONS = {
1199
2731
  kyc_approved: "Complete Know Your Customer verification.",
@@ -1203,9 +2735,39 @@ var DEFAULT_DESCRIPTIONS = {
1203
2735
  external_wallet: "Connect an external wallet.",
1204
2736
  whitelisted_wallet: "Connect a whitelisted wallet.",
1205
2737
  jurisdiction: "Confirm your jurisdiction eligibility.",
1206
- accreditation: "Verify your accreditation status."
2738
+ accreditation: "Verify your accreditation status.",
2739
+ document: "Confirm your Tax Documents."
1207
2740
  };
1208
- function RequirementsChecklist({
2741
+ var VERIFICATION_POLL_INTERVAL_MS = 5e3;
2742
+ var VERIFICATION_POLL_TIMEOUT_MS = 12e4;
2743
+ function sleep(ms) {
2744
+ return new Promise((resolve) => setTimeout(resolve, ms));
2745
+ }
2746
+ function VerificationOverlay({
2747
+ onClose,
2748
+ children
2749
+ }) {
2750
+ return /* @__PURE__ */ jsx19(
2751
+ "div",
2752
+ {
2753
+ role: "dialog",
2754
+ "aria-modal": "true",
2755
+ "aria-label": "Identity verification",
2756
+ className: "clcosdk:fixed clcosdk:inset-0 clcosdk:z-50 clcosdk:flex clcosdk:items-center clcosdk:justify-center clcosdk:bg-overlay clcosdk:p-4",
2757
+ children: /* @__PURE__ */ jsxs15("div", { className: "clcosdk:relative clcosdk:flex clcosdk:max-h-[92vh] clcosdk:w-full clcosdk:max-w-[760px] clcosdk:flex-col clcosdk:overflow-y-auto clcosdk:rounded-2xl clcosdk:bg-background clcosdk:p-6 clcosdk:pt-14", children: [
2758
+ /* @__PURE__ */ jsx19(
2759
+ CloseButton,
2760
+ {
2761
+ onClick: onClose,
2762
+ className: "clcosdk:absolute clcosdk:top-4 clcosdk:right-4"
2763
+ }
2764
+ ),
2765
+ children
2766
+ ] })
2767
+ }
2768
+ );
2769
+ }
2770
+ function RequirementsChecklistBody({
1209
2771
  offerId,
1210
2772
  optionId,
1211
2773
  title,
@@ -1213,39 +2775,107 @@ function RequirementsChecklist({
1213
2775
  onContinue,
1214
2776
  onRequirementActionOverride,
1215
2777
  onContactSupportOverride,
2778
+ wallet,
2779
+ onRequestConnect,
1216
2780
  getLabel,
1217
2781
  getDescription,
1218
2782
  loading,
2783
+ unauthenticatedState,
1219
2784
  error,
1220
2785
  className,
1221
- data
2786
+ data,
2787
+ identityVerificationOptions
1222
2788
  }) {
1223
2789
  const { coinlist } = useCoinList();
1224
- const { requirementsState } = useRequirements(offerId, { data });
1225
- const [expandedIndex, setExpandedIndex] = useState4(null);
1226
- const onRequirementAction = onRequirementActionOverride !== void 0 ? onRequirementActionOverride : (requirement) => coinlist.handleRequirement(requirement);
2790
+ const { requirementsState, refetch } = useRequirements(offerId, { data });
2791
+ const [expandedIndex, setExpandedIndex] = useState9(null);
2792
+ const [taxDocumentOpen, setTaxDocumentOpen] = useState9(false);
2793
+ const [walletConnectOpen, setConnectWalletOpen] = useState9(false);
2794
+ const [verifyingRequirementId, setVerifyingRequirementId] = useState9(null);
2795
+ const isMountedRef = useRef4(true);
2796
+ useEffect11(() => {
2797
+ isMountedRef.current = true;
2798
+ return () => {
2799
+ isMountedRef.current = false;
2800
+ };
2801
+ }, []);
2802
+ const [hasWalletRequirement, setHasWalletRequirement] = useState9(false);
2803
+ useEffect11(() => {
2804
+ if (requirementsState.type === "CONTENT") {
2805
+ setHasWalletRequirement(
2806
+ (requirementsState.requirements[optionId] ?? []).some(
2807
+ isWalletRequirement
2808
+ )
2809
+ );
2810
+ }
2811
+ }, [requirementsState, optionId]);
2812
+ const {
2813
+ addressesState,
2814
+ refetch: refetchAddresses,
2815
+ disconnect
2816
+ } = useOptionAddresses(offerId, optionId, {
2817
+ enabled: hasWalletRequirement
2818
+ });
2819
+ const optionAddresses = addressesState.type === "CONTENT" ? addressesState.addresses : [];
2820
+ const walletsStatus = addressesState.type === "ERROR" ? "error" : addressesState.type === "LOADING" ? "loading" : "ready";
2821
+ const onRemoveWallet = async (id) => {
2822
+ await disconnect(OfferOptionAddressId(id));
2823
+ refetch();
2824
+ };
2825
+ const onRequirementAction = onRequirementActionOverride !== void 0 ? onRequirementActionOverride : (requirement) => {
2826
+ if (requirement.type === "document") {
2827
+ setTaxDocumentOpen(true);
2828
+ return;
2829
+ }
2830
+ if ((requirement.type === "external_wallet" || requirement.type === "whitelisted_wallet") && wallet !== void 0 && (wallet !== null || onRequestConnect !== void 0)) {
2831
+ setConnectWalletOpen(true);
2832
+ return;
2833
+ }
2834
+ coinlist.handleRequirement(requirement);
2835
+ };
1227
2836
  const onContactSupport = onContactSupportOverride !== void 0 ? onContactSupportOverride : () => coinlist.contactSupport();
2837
+ const awaitVerificationResult = (requirementId, statusBeforeSubmit) => {
2838
+ void (async () => {
2839
+ const deadline = Date.now() + VERIFICATION_POLL_TIMEOUT_MS;
2840
+ while (Date.now() < deadline) {
2841
+ await sleep(VERIFICATION_POLL_INTERVAL_MS);
2842
+ if (!isMountedRef.current) return;
2843
+ try {
2844
+ const statuses2 = await coinlist.fetchRequirementStatuses(offerId);
2845
+ const current = statuses2.find((s) => s.id === requirementId)?.status;
2846
+ if (current !== void 0 && current !== statusBeforeSubmit) break;
2847
+ } catch (_) {
2848
+ }
2849
+ }
2850
+ if (isMountedRef.current) {
2851
+ setVerifyingRequirementId(null);
2852
+ refetch();
2853
+ }
2854
+ })();
2855
+ };
1228
2856
  if (requirementsState.type === "LOADING") {
1229
2857
  if (loading) return loading;
1230
- return /* @__PURE__ */ jsx14(
2858
+ return /* @__PURE__ */ jsx19(
1231
2859
  "output",
1232
2860
  {
1233
2861
  "aria-live": "polite",
1234
- className: "flex min-h-40 items-center justify-center rounded-2xl border border-border bg-background p-6",
1235
- children: /* @__PURE__ */ jsx14("p", { className: cn(typeClasses.body, "text-text-secondary"), children: "Loading requirements..." })
2862
+ className: "clcosdk:flex clcosdk:min-h-40 clcosdk:items-center clcosdk:justify-center clcosdk:rounded-2xl clcosdk:border clcosdk:border-border clcosdk:bg-background clcosdk:p-6",
2863
+ children: /* @__PURE__ */ jsx19("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: "Loading requirements..." })
1236
2864
  }
1237
2865
  );
1238
2866
  }
1239
2867
  if (requirementsState.type === "ERROR") {
2868
+ if (requirementsState.reason === "not-authenticated") {
2869
+ return unauthenticatedState !== void 0 ? unauthenticatedState : /* @__PURE__ */ jsx19(CoinListSignInCard, {});
2870
+ }
1240
2871
  if (error) return error;
1241
- const errorMessage = requirementsState.reason === "not-authenticated" ? "Sign in to view requirements." : "Unable to load requirements right now. Please try again.";
1242
- return /* @__PURE__ */ jsx14(
2872
+ return /* @__PURE__ */ jsx19(
1243
2873
  AlertBanner,
1244
2874
  {
1245
2875
  variant: "error",
1246
2876
  icon: true,
1247
- content: errorMessage,
1248
- className: "w-full max-w-[28rem]"
2877
+ content: "Unable to load requirements right now. Please try again.",
2878
+ className: "clcosdk:w-full clcosdk:max-w-[28rem]"
1249
2879
  }
1250
2880
  );
1251
2881
  }
@@ -1255,76 +2885,310 @@ function RequirementsChecklist({
1255
2885
  const optionStatuses = statuses.filter((s) => requirementIds.has(s.id));
1256
2886
  const statusByRequirementId = new Map(optionStatuses.map((s) => [s.id, s]));
1257
2887
  const checklistStatus = ChecklistStatus.derive(optionStatuses);
1258
- return /* @__PURE__ */ jsxs10("div", { className: cn("flex w-full flex-col gap-4", className), children: [
1259
- /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1260
- /* @__PURE__ */ jsx14("h3", { className: cn(typeClasses.h3, "text-text-primary"), children: title }),
1261
- /* @__PURE__ */ jsx14("p", { className: cn(typeClasses.body, "text-text-secondary"), children: description })
1262
- ] }),
1263
- /* @__PURE__ */ jsx14("div", { className: "flex flex-col divide-y divide-divider", children: requirements.map((req, index) => {
1264
- const statusInfo = statusByRequirementId.get(req.id);
1265
- const label = getLabel ? getLabel(req) : DEFAULT_LABELS[req.type];
1266
- const itemDescription = (getDescription ? getDescription(req) : null) ?? DEFAULT_DESCRIPTIONS[req.type];
1267
- let onActionBound;
1268
- if (onRequirementAction === null) {
1269
- onActionBound = null;
1270
- } else if (onRequirementAction) {
1271
- onActionBound = () => onRequirementAction(req);
2888
+ return /* @__PURE__ */ jsxs15(
2889
+ "div",
2890
+ {
2891
+ className: cn(
2892
+ "clcosdk:flex clcosdk:w-full clcosdk:flex-col clcosdk:gap-4",
2893
+ className
2894
+ ),
2895
+ children: [
2896
+ /* @__PURE__ */ jsxs15("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:gap-2", children: [
2897
+ /* @__PURE__ */ jsx19("h3", { className: cn(typeClasses.h3, "clcosdk:text-text-primary"), children: title }),
2898
+ /* @__PURE__ */ jsx19("p", { className: cn(typeClasses.body, "clcosdk:text-text-secondary"), children: description })
2899
+ ] }),
2900
+ /* @__PURE__ */ jsx19("div", { className: "clcosdk:flex clcosdk:flex-col clcosdk:divide-y clcosdk:divide-divider", children: requirements.map((req, index) => {
2901
+ const statusInfo = statusByRequirementId.get(req.id);
2902
+ const label = getLabel ? getLabel(req) : DEFAULT_LABELS[req.type];
2903
+ const itemDescription = (getDescription ? getDescription(req) : null) ?? DEFAULT_DESCRIPTIONS[req.type];
2904
+ const inlineVerification = statusInfo?.kycLevel !== void 0 && onRequirementActionOverride === void 0;
2905
+ let onActionBound;
2906
+ if (onRequirementAction === null) {
2907
+ onActionBound = null;
2908
+ } else if (inlineVerification) {
2909
+ onActionBound = () => setVerifyingRequirementId(req.id);
2910
+ } else if (onRequirementAction) {
2911
+ onActionBound = () => onRequirementAction(req);
2912
+ }
2913
+ let onContactSupportBound;
2914
+ if (onContactSupport === null) {
2915
+ onContactSupportBound = null;
2916
+ } else if (onContactSupport) {
2917
+ onContactSupportBound = () => onContactSupport(req);
2918
+ }
2919
+ const walletRequirement = isWalletRequirement(req);
2920
+ return /* @__PURE__ */ jsx19(
2921
+ RequirementItem,
2922
+ {
2923
+ ui: RequirementItemUi.fromDomain(
2924
+ req,
2925
+ statusInfo,
2926
+ label,
2927
+ itemDescription,
2928
+ walletRequirement ? optionAddresses : []
2929
+ ),
2930
+ expanded: expandedIndex === index,
2931
+ onToggle: () => setExpandedIndex((prev) => prev === index ? null : index),
2932
+ onAction: onActionBound,
2933
+ onRemoveWallet: walletRequirement ? onRemoveWallet : void 0,
2934
+ walletsStatus: walletRequirement ? walletsStatus : void 0,
2935
+ onContactSupport: onContactSupportBound
2936
+ },
2937
+ req.id
2938
+ );
2939
+ }) }),
2940
+ checklistStatus === "ineligible" && /* @__PURE__ */ jsx19(
2941
+ AlertBanner,
2942
+ {
2943
+ variant: "error",
2944
+ icon: true,
2945
+ content: "You are not eligible to participate."
2946
+ }
2947
+ ),
2948
+ onContinue && /* @__PURE__ */ jsx19(
2949
+ ClButton,
2950
+ {
2951
+ fullWidth: true,
2952
+ disabled: checklistStatus !== "eligible",
2953
+ onClick: onContinue,
2954
+ children: "Continue"
2955
+ }
2956
+ ),
2957
+ /* @__PURE__ */ jsx19(
2958
+ TaxDocumentModal,
2959
+ {
2960
+ isOpen: taxDocumentOpen,
2961
+ onClose: () => setTaxDocumentOpen(false),
2962
+ onSubmitted: () => refetch()
2963
+ }
2964
+ ),
2965
+ verifyingRequirementId !== null && /* @__PURE__ */ jsx19(VerificationOverlay, { onClose: () => setVerifyingRequirementId(null), children: /* @__PURE__ */ jsx19(
2966
+ IdentityVerification,
2967
+ {
2968
+ levelName: identityVerificationOptions?.levelName ?? statusByRequirementId.get(verifyingRequirementId)?.kycLevel,
2969
+ reset: statusByRequirementId.get(verifyingRequirementId)?.kycReset ?? false,
2970
+ locale: identityVerificationOptions?.locale,
2971
+ onSubmitted: () => awaitVerificationResult(
2972
+ verifyingRequirementId,
2973
+ statusByRequirementId.get(verifyingRequirementId)?.status ?? "not_started"
2974
+ )
2975
+ }
2976
+ ) }),
2977
+ /* @__PURE__ */ jsx19(
2978
+ ConnectWalletModal,
2979
+ {
2980
+ isOpen: walletConnectOpen,
2981
+ onClose: () => setConnectWalletOpen(false),
2982
+ offerId,
2983
+ optionId,
2984
+ wallet: wallet ?? null,
2985
+ onRequestConnect,
2986
+ onConnected: () => {
2987
+ refetch();
2988
+ refetchAddresses();
2989
+ }
2990
+ }
2991
+ )
2992
+ ]
2993
+ }
2994
+ );
2995
+ }
2996
+ function RequirementsChecklist(props) {
2997
+ return /* @__PURE__ */ jsx19(CoinListStyleScope, { children: /* @__PURE__ */ jsx19(RequirementsChecklistBody, { ...props }) });
2998
+ }
2999
+
3000
+ // src/client/hooks/swap/useSwapOutputToken.ts
3001
+ import { useEffect as useEffect12, useRef as useRef5, useState as useState10 } from "react";
3002
+ var LOADING_STATE6 = { type: "LOADING" };
3003
+ function useSwapOutputToken(options) {
3004
+ const { contractAddress, chain, enabled } = options;
3005
+ const { coinlist, isReady } = useCoinList();
3006
+ const [outputTokenState, setOutputTokenState] = useState10(LOADING_STATE6);
3007
+ const fetchIdRef = useRef5(0);
3008
+ useEffect12(() => {
3009
+ if (!enabled || !isReady) return;
3010
+ const currentFetchId = ++fetchIdRef.current;
3011
+ const load = async () => {
3012
+ try {
3013
+ const outputToken = await coinlist.swap.getOutputToken({
3014
+ contractAddress,
3015
+ chain
3016
+ });
3017
+ if (currentFetchId !== fetchIdRef.current) return;
3018
+ setOutputTokenState({ type: "CONTENT", outputToken });
3019
+ } catch {
3020
+ if (currentFetchId !== fetchIdRef.current) return;
3021
+ setOutputTokenState({ type: "ERROR" });
1272
3022
  }
1273
- let onContactSupportBound;
1274
- if (onContactSupport === null) {
1275
- onContactSupportBound = null;
1276
- } else if (onContactSupport) {
1277
- onContactSupportBound = () => onContactSupport(req);
3023
+ };
3024
+ load();
3025
+ return () => {
3026
+ ++fetchIdRef.current;
3027
+ };
3028
+ }, [coinlist, isReady, contractAddress, chain, enabled]);
3029
+ return { outputTokenState };
3030
+ }
3031
+
3032
+ // src/client/hooks/swap/useSwapQuote.ts
3033
+ import { useEffect as useEffect13, useRef as useRef6, useState as useState11 } from "react";
3034
+ function useSwapQuote(options) {
3035
+ const {
3036
+ contractAddress,
3037
+ chain,
3038
+ inputAmount,
3039
+ inputTokenAddress,
3040
+ outputTokenDecimals,
3041
+ pollIntervalMs = SWAP_POLL_INTERVAL_MS,
3042
+ enabled
3043
+ } = options;
3044
+ const { coinlist, isReady } = useCoinList();
3045
+ const [quote, setQuote] = useState11(null);
3046
+ const [isLoading, setIsLoading] = useState11(true);
3047
+ const [isRefreshing, setIsRefreshing] = useState11(false);
3048
+ const isFetchingRef = useRef6(false);
3049
+ const hasQuoteRef = useRef6(false);
3050
+ const fetchIdRef = useRef6(0);
3051
+ useEffect13(() => {
3052
+ if (!isReady) return;
3053
+ if (!enabled) {
3054
+ setIsLoading(false);
3055
+ return;
3056
+ }
3057
+ const currentFetchId = ++fetchIdRef.current;
3058
+ isFetchingRef.current = false;
3059
+ const loadQuote = async () => {
3060
+ if (outputTokenDecimals === null) return;
3061
+ if (isFetchingRef.current) return;
3062
+ isFetchingRef.current = true;
3063
+ if (hasQuoteRef.current) {
3064
+ setIsRefreshing(true);
1278
3065
  }
1279
- return /* @__PURE__ */ jsx14(
1280
- RequirementItem,
1281
- {
1282
- ui: RequirementItemUi.fromDomain(
1283
- req,
1284
- statusInfo,
1285
- label,
1286
- itemDescription
1287
- ),
1288
- expanded: expandedIndex === index,
1289
- onToggle: () => setExpandedIndex((prev) => prev === index ? null : index),
1290
- onAction: onActionBound,
1291
- onContactSupport: onContactSupportBound
1292
- },
1293
- req.id
1294
- );
1295
- }) }),
1296
- checklistStatus === "ineligible" && /* @__PURE__ */ jsx14(
1297
- AlertBanner,
1298
- {
1299
- variant: "error",
1300
- icon: true,
1301
- content: "You are not eligible to participate."
3066
+ try {
3067
+ const preview = await coinlist.swap.getPreview({
3068
+ contractAddress,
3069
+ chain,
3070
+ inputToken: inputTokenAddress,
3071
+ amount: inputAmount.raw
3072
+ });
3073
+ if (currentFetchId !== fetchIdRef.current) return;
3074
+ setQuote(
3075
+ SwapQuote.fromPreview(
3076
+ preview,
3077
+ inputAmount.decimals,
3078
+ outputTokenDecimals
3079
+ )
3080
+ );
3081
+ if (!hasQuoteRef.current) {
3082
+ hasQuoteRef.current = true;
3083
+ setIsLoading(false);
3084
+ }
3085
+ } catch {
3086
+ } finally {
3087
+ if (currentFetchId === fetchIdRef.current) {
3088
+ isFetchingRef.current = false;
3089
+ setIsRefreshing(false);
3090
+ }
1302
3091
  }
1303
- ),
1304
- onContinue && /* @__PURE__ */ jsx14(
1305
- ClButton,
1306
- {
1307
- fullWidth: true,
1308
- disabled: checklistStatus !== "eligible",
1309
- onClick: onContinue,
1310
- children: "Continue"
3092
+ };
3093
+ loadQuote();
3094
+ const interval = setInterval(loadQuote, pollIntervalMs);
3095
+ return () => {
3096
+ clearInterval(interval);
3097
+ ++fetchIdRef.current;
3098
+ setIsRefreshing(false);
3099
+ };
3100
+ }, [
3101
+ coinlist,
3102
+ isReady,
3103
+ contractAddress,
3104
+ chain,
3105
+ inputAmount.raw,
3106
+ inputAmount.decimals,
3107
+ inputTokenAddress,
3108
+ outputTokenDecimals,
3109
+ pollIntervalMs,
3110
+ enabled
3111
+ ]);
3112
+ return { quote, isLoading, isRefreshing };
3113
+ }
3114
+
3115
+ // src/client/hooks/swap/useSwapTokenBalances.ts
3116
+ import { useEffect as useEffect14, useRef as useRef7, useState as useState12 } from "react";
3117
+ function useSwapTokenBalances(options) {
3118
+ const {
3119
+ address,
3120
+ chain,
3121
+ assets,
3122
+ pollIntervalMs = SWAP_POLL_INTERVAL_MS,
3123
+ enabled
3124
+ } = options;
3125
+ const { coinlist, isReady } = useCoinList();
3126
+ const [balances, setBalances] = useState12(/* @__PURE__ */ new Map());
3127
+ const [isLoading, setIsLoading] = useState12(true);
3128
+ const fetchIdRef = useRef7(0);
3129
+ const isFetchingRef = useRef7(false);
3130
+ const assetsKey = assets.join(",");
3131
+ useEffect14(() => {
3132
+ if (!isReady) return;
3133
+ if (!enabled) {
3134
+ setIsLoading(false);
3135
+ return;
3136
+ }
3137
+ const symbols = assetsKey.split(",");
3138
+ const currentFetchId = ++fetchIdRef.current;
3139
+ isFetchingRef.current = false;
3140
+ let isFirstFetch = true;
3141
+ async function fetchBalances() {
3142
+ if (isFetchingRef.current) return;
3143
+ isFetchingRef.current = true;
3144
+ if (isFirstFetch) {
3145
+ setIsLoading(true);
1311
3146
  }
1312
- )
1313
- ] });
3147
+ const results = await Promise.allSettled(
3148
+ symbols.map(
3149
+ (symbol) => coinlist.swap.getTokenBalance({
3150
+ tokenAddress: TOKEN_REGISTRY.contractAddress(symbol, chain),
3151
+ owner: address,
3152
+ chain
3153
+ })
3154
+ )
3155
+ );
3156
+ if (currentFetchId !== fetchIdRef.current) return;
3157
+ isFetchingRef.current = false;
3158
+ const next = /* @__PURE__ */ new Map();
3159
+ symbols.forEach((symbol, i) => {
3160
+ const result = results[i];
3161
+ next.set(
3162
+ symbol,
3163
+ result.status === "fulfilled" ? result.value.balance : null
3164
+ );
3165
+ });
3166
+ setBalances(next);
3167
+ setIsLoading(false);
3168
+ isFirstFetch = false;
3169
+ }
3170
+ fetchBalances();
3171
+ const interval = setInterval(fetchBalances, pollIntervalMs);
3172
+ return () => {
3173
+ clearInterval(interval);
3174
+ ++fetchIdRef.current;
3175
+ };
3176
+ }, [coinlist, isReady, address, chain, assetsKey, pollIntervalMs, enabled]);
3177
+ return { balances, isLoading };
1314
3178
  }
1315
3179
 
1316
3180
  // src/client/hooks/useCompleteOAuth.ts
1317
- import { useEffect as useEffect4, useRef } from "react";
3181
+ import { useEffect as useEffect15, useRef as useRef8 } from "react";
1318
3182
  function useCompleteOAuth(options) {
1319
3183
  const { coinlist } = useCoinList();
1320
- const postOAuthCompleteRef = useRef(options.postOAuthComplete);
1321
- const onFailureRef = useRef(options.onFailure);
1322
- const onSuccessRef = useRef(options.onSuccess);
3184
+ const postOAuthCompleteRef = useRef8(options.postOAuthComplete);
3185
+ const onFailureRef = useRef8(options.onFailure);
3186
+ const onSuccessRef = useRef8(options.onSuccess);
1323
3187
  postOAuthCompleteRef.current = options.postOAuthComplete;
1324
3188
  onFailureRef.current = options.onFailure;
1325
3189
  onSuccessRef.current = options.onSuccess;
1326
- const hasStartedExchangeRef = useRef(false);
1327
- useEffect4(() => {
3190
+ const hasStartedExchangeRef = useRef8(false);
3191
+ useEffect15(() => {
1328
3192
  if (typeof window === "undefined") return;
1329
3193
  const params = new URLSearchParams(window.location.search);
1330
3194
  if (!params.has("code") && !params.has("error")) return;
@@ -1368,19 +3232,19 @@ function useCompleteOAuth(options) {
1368
3232
  }
1369
3233
 
1370
3234
  // src/client/hooks/useOfferDetails.ts
1371
- import { useEffect as useEffect5, useState as useState5 } from "react";
1372
- var LOADING_STATE3 = { type: "LOADING" };
3235
+ import { useEffect as useEffect16, useState as useState13 } from "react";
3236
+ var LOADING_STATE7 = { type: "LOADING" };
1373
3237
  function useOfferDetails(offerId, options = {}) {
1374
3238
  const { coinlist, isReady } = useCoinList();
1375
3239
  const { data } = options;
1376
- const [offerDetailsState, setOfferDetailsState] = useState5(
1377
- data !== void 0 ? { type: "CONTENT", offerDetail: data } : LOADING_STATE3
3240
+ const [offerDetailsState, setOfferDetailsState] = useState13(
3241
+ data !== void 0 ? { type: "CONTENT", offerDetail: data } : LOADING_STATE7
1378
3242
  );
1379
- useEffect5(() => {
3243
+ useEffect16(() => {
1380
3244
  let isCancelled = false;
1381
3245
  if (!isReady) {
1382
3246
  if (data === void 0) {
1383
- setOfferDetailsState(LOADING_STATE3);
3247
+ setOfferDetailsState(LOADING_STATE7);
1384
3248
  }
1385
3249
  return () => {
1386
3250
  isCancelled = true;
@@ -1391,7 +3255,7 @@ function useOfferDetails(offerId, options = {}) {
1391
3255
  isCancelled = true;
1392
3256
  };
1393
3257
  }
1394
- setOfferDetailsState(LOADING_STATE3);
3258
+ setOfferDetailsState(LOADING_STATE7);
1395
3259
  coinlist.fetchOfferDetails(offerId).then((offerDetail) => {
1396
3260
  if (!isCancelled) {
1397
3261
  setOfferDetailsState({ type: "CONTENT", offerDetail });
@@ -1410,19 +3274,19 @@ function useOfferDetails(offerId, options = {}) {
1410
3274
  }
1411
3275
 
1412
3276
  // src/client/hooks/useParticipations.ts
1413
- import { useEffect as useEffect6, useState as useState6 } from "react";
1414
- var LOADING_STATE4 = { type: "LOADING" };
3277
+ import { useEffect as useEffect17, useState as useState14 } from "react";
3278
+ var LOADING_STATE8 = { type: "LOADING" };
1415
3279
  function useParticipations(offerId, options = {}) {
1416
3280
  const { coinlist, isReady } = useCoinList();
1417
3281
  const { data } = options;
1418
- const [participationsState, setParticipationsState] = useState6(
1419
- data !== void 0 ? { type: "CONTENT", participations: data } : LOADING_STATE4
3282
+ const [participationsState, setParticipationsState] = useState14(
3283
+ data !== void 0 ? { type: "CONTENT", participations: data } : LOADING_STATE8
1420
3284
  );
1421
- useEffect6(() => {
3285
+ useEffect17(() => {
1422
3286
  let isCancelled = false;
1423
3287
  if (!isReady) {
1424
3288
  if (data === void 0) {
1425
- setParticipationsState(LOADING_STATE4);
3289
+ setParticipationsState(LOADING_STATE8);
1426
3290
  }
1427
3291
  return () => {
1428
3292
  isCancelled = true;
@@ -1433,7 +3297,7 @@ function useParticipations(offerId, options = {}) {
1433
3297
  isCancelled = true;
1434
3298
  };
1435
3299
  }
1436
- setParticipationsState(LOADING_STATE4);
3300
+ setParticipationsState(LOADING_STATE8);
1437
3301
  coinlist.fetchParticipations(offerId).then((participations) => {
1438
3302
  if (!isCancelled) {
1439
3303
  setParticipationsState({ type: "CONTENT", participations });
@@ -1452,10 +3316,17 @@ function useParticipations(offerId, options = {}) {
1452
3316
  }
1453
3317
  export {
1454
3318
  ChecklistStatus,
3319
+ ClientSwapNamespaceImpl,
1455
3320
  CoinListClientInitializationError,
1456
3321
  CoinListContext,
3322
+ CoinListContextProvider,
1457
3323
  CoinListProvider,
1458
3324
  CoinListSignInCard,
3325
+ CoinListStyleScope,
3326
+ ConnectWalletModal,
3327
+ ConnectedWalletList,
3328
+ IdentityVerification,
3329
+ KycToken,
1459
3330
  OAUTH_CODE_VERIFIER_KEY,
1460
3331
  OAUTH_STATE_KEY,
1461
3332
  OfferCard,
@@ -1466,12 +3337,24 @@ export {
1466
3337
  RequirementStatus,
1467
3338
  RequirementVariant,
1468
3339
  RequirementsChecklist,
3340
+ TaxDocumentModal,
3341
+ authorizeWallet,
3342
+ classifyWalletError,
3343
+ connectExternalWalletFlow,
1469
3344
  createCoinListClient,
3345
+ executeSwap,
1470
3346
  useCoinList,
1471
3347
  useCompleteOAuth,
3348
+ useConnectWallet,
3349
+ useKycToken,
1472
3350
  useOfferDetails,
1473
3351
  useOffers,
3352
+ useOptionAddresses,
1474
3353
  useParticipations,
1475
- useRequirements
3354
+ useRequirements,
3355
+ useSwapOutputToken,
3356
+ useSwapQuote,
3357
+ useSwapTokenBalances,
3358
+ useTaxDocument
1476
3359
  };
1477
3360
  //# sourceMappingURL=index.js.map