@chipi-stack/chipi-react 14.13.0 → 14.14.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.
package/dist/index.mjs CHANGED
@@ -71,6 +71,18 @@ function useChipiContext() {
71
71
  }
72
72
  return context;
73
73
  }
74
+
75
+ // src/utils/with-error-code.ts
76
+ function withErrorCode(wrapped, original) {
77
+ const code = original.code;
78
+ if (typeof code === "string") {
79
+ Object.defineProperty(wrapped, "code", { value: code, enumerable: true });
80
+ }
81
+ Object.defineProperty(wrapped, "cause", { value: original, enumerable: false });
82
+ return wrapped;
83
+ }
84
+
85
+ // src/hooks/useCreateWallet.ts
74
86
  function useCreateWallet() {
75
87
  const { chipiSDK } = useChipiContext();
76
88
  const mutation = useMutation({
@@ -101,7 +113,7 @@ function useCreateWallet() {
101
113
  }
102
114
  } catch (error) {
103
115
  if (error instanceof Error) {
104
- throw new Error(`Passkey creation failed: ${error.message}`);
116
+ throw withErrorCode(new Error(`Passkey creation failed: ${error.message}`), error);
105
117
  }
106
118
  throw new Error("Failed to create passkey for wallet");
107
119
  }
@@ -181,6 +193,17 @@ function useGetWallet(input) {
181
193
  fetchWallet
182
194
  };
183
195
  }
196
+
197
+ // src/utils/wallet-capabilities.ts
198
+ var SHHH_SESSION_SIGNER_KINDS = /* @__PURE__ */ new Set(["STARK", "ED25519"]);
199
+ function walletSupportsSessions(wallet) {
200
+ if (!wallet) return false;
201
+ if (wallet.walletType === "CHIPI") return true;
202
+ if (wallet.walletType === "SHHH") {
203
+ return !wallet.signerKind || SHHH_SESSION_SIGNER_KINDS.has(wallet.signerKind);
204
+ }
205
+ return false;
206
+ }
184
207
  function useChipiWallet(config3) {
185
208
  const { chipiSDK } = useChipiContext();
186
209
  const queryClient = useQueryClient();
@@ -261,7 +284,7 @@ function useChipiWallet(config3) {
261
284
  const data = walletQuery.data;
262
285
  return {
263
286
  ...data,
264
- supportsSessionKeys: data.walletType === "CHIPI",
287
+ supportsSessionKeys: walletSupportsSessions(data),
265
288
  shortAddress: formatAddress(data.publicKey)
266
289
  };
267
290
  }, [walletQuery.data, walletQuery.isLoading]);
@@ -318,6 +341,7 @@ function useChipiSession(config3) {
318
341
  const {
319
342
  wallet,
320
343
  encryptKey,
344
+ getEncryptKey,
321
345
  getBearerToken,
322
346
  storedSession,
323
347
  onSessionCreated,
@@ -331,10 +355,13 @@ function useChipiSession(config3) {
331
355
  useEffect(() => {
332
356
  setLocalSession(storedSession ?? null);
333
357
  }, [storedSession]);
334
- const supportsSession = useMemo(() => {
335
- if (!wallet) return false;
336
- return wallet.walletType === "CHIPI";
337
- }, [wallet]);
358
+ const supportsSession = useMemo(() => walletSupportsSessions(wallet), [wallet]);
359
+ const resolveEncryptKey = useCallback(async () => {
360
+ const key = encryptKey || (getEncryptKey ? await getEncryptKey() : void 0);
361
+ if (!key) throw new Error("Encryption key (PIN) is required");
362
+ return key;
363
+ }, [encryptKey, getEncryptKey]);
364
+ const [awaitingRegistrationUntil, setAwaitingRegistrationUntil] = useState(null);
338
365
  const sessionStatusQuery = useQuery({
339
366
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey],
340
367
  queryFn: async () => {
@@ -347,34 +374,41 @@ function useChipiSession(config3) {
347
374
  enabled: Boolean(
348
375
  autoCheckStatus && wallet?.publicKey && localSession?.publicKey && supportsSession
349
376
  ),
350
- staleTime: 3e4
377
+ staleTime: 3e4,
351
378
  // 30 seconds
379
+ // The registration hash comes back before the transaction is in a block, so
380
+ // right after registerSession the struct can still read as zero. Poll until
381
+ // it shows up (or for two minutes), then go back to on-demand reads.
382
+ refetchInterval: (query) => awaitingRegistrationUntil !== null && Date.now() < awaitingRegistrationUntil && (query.state.data?.validUntil ?? 0) === 0 ? 3e3 : false
352
383
  });
384
+ const [revokedSessionKey, setRevokedSessionKey] = useState(null);
353
385
  const sessionState = useMemo(() => {
354
386
  if (!localSession) return "none";
387
+ if (revokedSessionKey === localSession.publicKey) return "revoked";
388
+ if (localSession.validUntil * 1e3 < Date.now()) return "expired";
355
389
  const status = sessionStatusQuery.data;
356
390
  if (status) {
357
- if (!status.isActive) return "revoked";
391
+ if (status.validUntil === 0) return "created";
358
392
  if (status.validUntil * 1e3 < Date.now()) return "expired";
359
393
  if (status.remainingCalls <= 0) return "expired";
394
+ if (!status.isActive) return "revoked";
360
395
  return "active";
361
396
  }
362
- if (localSession.validUntil * 1e3 < Date.now()) return "expired";
363
397
  return "created";
364
- }, [localSession, sessionStatusQuery.data]);
398
+ }, [localSession, revokedSessionKey, sessionStatusQuery.data]);
365
399
  const hasActiveSession = sessionState === "active";
366
400
  const isSessionExpired = sessionState === "expired";
367
401
  const remainingCalls = sessionStatusQuery.data?.remainingCalls;
368
402
  const createSessionMutation = useMutation(
369
403
  {
370
404
  mutationFn: async (params = {}) => {
371
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
372
405
  if (!supportsSession)
373
406
  throw new Error(
374
- "Wallet does not support sessions. Only CHIPI wallets support session keys."
407
+ "Wallet does not support sessions. Session keys need a CHIPI wallet or a SHHH wallet with a STARK / ED25519 signer."
375
408
  );
409
+ const key = await resolveEncryptKey();
376
410
  const sessionData = chipiSDK.sessions.createSessionKey({
377
- encryptKey,
411
+ encryptKey: key,
378
412
  durationSeconds: params.durationSeconds ?? defaultDurationSeconds
379
413
  });
380
414
  return sessionData;
@@ -394,28 +428,38 @@ function useChipiSession(config3) {
394
428
  const registerSessionMutation = useMutation({
395
429
  mutationFn: async (sessionConfig = {}) => {
396
430
  if (!wallet) throw new Error("Wallet is required");
397
- if (!localSession) throw new Error("No session to register. Call createSession first.");
398
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
431
+ const sessionPublicKey = sessionConfig.sessionPublicKey ?? localSession?.publicKey;
432
+ const validUntil = sessionConfig.validUntil ?? localSession?.validUntil;
433
+ if (!sessionPublicKey || validUntil === void 0)
434
+ throw new Error(
435
+ "No session to register. Call createSession first, or pass sessionPublicKey and validUntil."
436
+ );
399
437
  if (!supportsSession) throw new Error("Wallet does not support sessions");
438
+ const key = await resolveEncryptKey();
400
439
  const bearerToken = await getBearerToken();
401
440
  if (!bearerToken) throw new Error("Bearer token is required");
402
- const txHash = await chipiSDK.sessions.addSessionKeyToContract(
403
- {
404
- encryptKey,
405
- wallet,
406
- sessionConfig: {
407
- sessionPublicKey: localSession.publicKey,
408
- validUntil: sessionConfig.validUntil ?? localSession.validUntil,
409
- maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
410
- allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
411
- }
412
- },
413
- bearerToken
414
- );
415
- return txHash;
441
+ const params = {
442
+ encryptKey: key,
443
+ wallet,
444
+ sessionConfig: {
445
+ sessionPublicKey,
446
+ validUntil,
447
+ maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
448
+ allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
449
+ }
450
+ };
451
+ if (sessionConfig.spendingPolicies?.length) {
452
+ return chipiSDK.sessions.setupSession(
453
+ { ...params, spendingPolicies: sessionConfig.spendingPolicies },
454
+ bearerToken
455
+ );
456
+ }
457
+ return chipiSDK.sessions.addSessionKeyToContract(params, bearerToken);
416
458
  },
417
459
  onSuccess: () => {
418
460
  setError(null);
461
+ if (localSession && revokedSessionKey === localSession.publicKey) setRevokedSessionKey(null);
462
+ setAwaitingRegistrationUntil(Date.now() + 12e4);
419
463
  queryClient.invalidateQueries({
420
464
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
421
465
  });
@@ -425,25 +469,28 @@ function useChipiSession(config3) {
425
469
  }
426
470
  });
427
471
  const revokeSessionMutation = useMutation({
428
- mutationFn: async () => {
472
+ mutationFn: async (sessionPublicKey) => {
429
473
  if (!wallet) throw new Error("Wallet is required");
430
- if (!localSession) throw new Error("No session to revoke");
431
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
474
+ const target = sessionPublicKey ?? localSession?.publicKey;
475
+ if (!target) throw new Error("No session to revoke");
432
476
  if (!supportsSession) throw new Error("Wallet does not support sessions");
477
+ const key = await resolveEncryptKey();
433
478
  const bearerToken = await getBearerToken();
434
479
  if (!bearerToken) throw new Error("Bearer token is required");
435
480
  const txHash = await chipiSDK.sessions.revokeSessionKey(
436
481
  {
437
- encryptKey,
482
+ encryptKey: key,
438
483
  wallet,
439
- sessionPublicKey: localSession.publicKey
484
+ sessionPublicKey: target
440
485
  },
441
486
  bearerToken
442
487
  );
443
488
  return txHash;
444
489
  },
445
- onSuccess: () => {
490
+ onSuccess: (_txHash, sessionPublicKey) => {
446
491
  setError(null);
492
+ const target = sessionPublicKey ?? localSession?.publicKey;
493
+ if (target) setRevokedSessionKey(target);
447
494
  queryClient.invalidateQueries({
448
495
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
449
496
  });
@@ -456,13 +503,13 @@ function useChipiSession(config3) {
456
503
  mutationFn: async (calls) => {
457
504
  if (!wallet) throw new Error("Wallet is required");
458
505
  if (!localSession) throw new Error("No active session");
459
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
460
506
  if (!hasActiveSession) throw new Error("Session is not active. Register the session first.");
507
+ const key = await resolveEncryptKey();
461
508
  const bearerToken = await getBearerToken();
462
509
  if (!bearerToken) throw new Error("Bearer token is required");
463
510
  const txHash = await chipiSDK.executeTransactionWithSession({
464
511
  params: {
465
- encryptKey,
512
+ encryptKey: key,
466
513
  wallet,
467
514
  session: localSession,
468
515
  calls
@@ -486,13 +533,13 @@ function useChipiSession(config3) {
486
533
  if (!wallet) throw new Error("Wallet is required");
487
534
  if (!localSession)
488
535
  throw new Error("No session to set policy on. Create and register a session first.");
489
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
490
536
  if (!supportsSession) throw new Error("Wallet does not support sessions");
537
+ const key = await resolveEncryptKey();
491
538
  const bearerToken = await getBearerToken();
492
539
  if (!bearerToken) throw new Error("Bearer token is required");
493
540
  const txHash = await chipiSDK.sessions.setSpendingPolicy(
494
541
  {
495
- encryptKey,
542
+ encryptKey: key,
496
543
  wallet,
497
544
  spendingPolicyConfig: {
498
545
  sessionPublicKey: localSession.publicKey,
@@ -514,13 +561,13 @@ function useChipiSession(config3) {
514
561
  mutationFn: async (token) => {
515
562
  if (!wallet) throw new Error("Wallet is required");
516
563
  if (!localSession) throw new Error("No session to remove policy from");
517
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
518
564
  if (!supportsSession) throw new Error("Wallet does not support sessions");
565
+ const key = await resolveEncryptKey();
519
566
  const bearerToken = await getBearerToken();
520
567
  if (!bearerToken) throw new Error("Bearer token is required");
521
568
  const txHash = await chipiSDK.sessions.removeSpendingPolicy(
522
569
  {
523
- encryptKey,
570
+ encryptKey: key,
524
571
  wallet,
525
572
  sessionPublicKey: localSession.publicKey,
526
573
  token
@@ -548,9 +595,14 @@ function useChipiSession(config3) {
548
595
  },
549
596
  [registerSessionMutation]
550
597
  );
551
- const revokeSession = useCallback(async () => {
552
- return revokeSessionMutation.mutateAsync();
553
- }, [revokeSessionMutation]);
598
+ const revokeSession = useCallback(
599
+ async (sessionPublicKey) => {
600
+ return revokeSessionMutation.mutateAsync(
601
+ typeof sessionPublicKey === "string" ? sessionPublicKey : void 0
602
+ );
603
+ },
604
+ [revokeSessionMutation]
605
+ );
554
606
  const executeWithSession = useCallback(
555
607
  async (calls) => {
556
608
  return executeWithSessionMutation.mutateAsync(calls);
@@ -684,7 +736,7 @@ function useMigrateWalletToPasskey() {
684
736
  };
685
737
  } catch (error) {
686
738
  if (error instanceof Error) {
687
- throw new Error(`Migration failed: ${error.message}`);
739
+ throw withErrorCode(new Error(`Migration failed: ${error.message}`), error);
688
740
  }
689
741
  throw new Error("Failed to migrate wallet to passkey");
690
742
  }
@@ -21237,6 +21289,21 @@ function ChipiInjector({ children, ...config3 }) {
21237
21289
  }, [walletAddr]);
21238
21290
  return children ?? null;
21239
21291
  }
21292
+ var DEFAULT_RECOVER_LABELS = {
21293
+ proposeAddDevice: "Propose new device",
21294
+ proposeRecovery: "Initiate recovery",
21295
+ executeAddDevice: "Execute add-owner",
21296
+ executeRecovery: "Finalize recovery",
21297
+ timelockAddDevice: "48-hour timelock before this device becomes active.",
21298
+ timelockRecovery: "7-day timelock before the recovered wallet becomes active.",
21299
+ submitting: "Submitting\u2026",
21300
+ secondsRemaining: (s) => `${s}s remaining`,
21301
+ cancel: "Cancel pending op",
21302
+ timelockElapsed: "Timelock elapsed.",
21303
+ cancelling: "Cancelling\u2026",
21304
+ doneAddDevice: "New device added.",
21305
+ doneRecovery: "Wallet recovered."
21306
+ };
21240
21307
  function deriveStaticPhase(pendingOp, executed, isReady, nowSecs) {
21241
21308
  if (executed) return "done";
21242
21309
  if (!pendingOp) return "form-idle";
@@ -21318,10 +21385,17 @@ function Recover(props) {
21318
21385
  setSubmittingAction(null);
21319
21386
  }
21320
21387
  }, [activePendingOp, onCancel]);
21388
+ const L = useMemo(
21389
+ () => ({
21390
+ ...DEFAULT_RECOVER_LABELS,
21391
+ ...Object.fromEntries(Object.entries(props.labels ?? {}).filter(([, v]) => v !== void 0))
21392
+ }),
21393
+ [props.labels]
21394
+ );
21321
21395
  const isAddDevice = mode === "add-device";
21322
- const proposeLabel = isAddDevice ? "Propose new device" : "Initiate recovery";
21323
- const executeLabel = isAddDevice ? "Execute add-owner" : "Finalize recovery";
21324
- const timelockDescription = isAddDevice ? "48-hour timelock before this device becomes active." : "7-day timelock before the recovered wallet becomes active.";
21396
+ const proposeLabel = isAddDevice ? L.proposeAddDevice : L.proposeRecovery;
21397
+ const executeLabel = isAddDevice ? L.executeAddDevice : L.executeRecovery;
21398
+ const timelockDescription = isAddDevice ? L.timelockAddDevice : L.timelockRecovery;
21325
21399
  return /* @__PURE__ */ jsxs(
21326
21400
  "div",
21327
21401
  {
@@ -21349,18 +21423,15 @@ function Recover(props) {
21349
21423
  }
21350
21424
  )
21351
21425
  ] }),
21352
- phase === "submitting-propose" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: "Submitting\u2026" }) }),
21426
+ phase === "submitting-propose" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: L.submitting }) }),
21353
21427
  phase === "awaiting-timelock" && activePendingOp && /* @__PURE__ */ jsxs("section", { "data-chipi-recover-section": "awaiting", children: [
21354
21428
  /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "awaiting", children: timelockDescription }),
21355
- /* @__PURE__ */ jsxs(
21429
+ /* @__PURE__ */ jsx(
21356
21430
  "p",
21357
21431
  {
21358
21432
  "data-chipi-recover-countdown": "",
21359
21433
  "data-chipi-recover-seconds-remaining": String(secondsRemaining),
21360
- children: [
21361
- secondsRemaining,
21362
- "s remaining"
21363
- ]
21434
+ children: L.secondsRemaining(secondsRemaining)
21364
21435
  }
21365
21436
  ),
21366
21437
  /* @__PURE__ */ jsx("p", { "data-chipi-recover-op-id": "", children: String(activePendingOp.opId) }),
@@ -21371,12 +21442,12 @@ function Recover(props) {
21371
21442
  "data-chipi-recover-action": "cancel",
21372
21443
  onClick: handleCancel,
21373
21444
  disabled: submittingAction !== null,
21374
- children: "Cancel pending op"
21445
+ children: L.cancel
21375
21446
  }
21376
21447
  )
21377
21448
  ] }),
21378
21449
  (phase === "ready-to-execute" || phase === "submitting-execute") && activePendingOp && /* @__PURE__ */ jsxs("section", { "data-chipi-recover-section": "execute", children: [
21379
- /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "ready", children: "Timelock elapsed." }),
21450
+ /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "ready", children: L.timelockElapsed }),
21380
21451
  /* @__PURE__ */ jsx(
21381
21452
  "button",
21382
21453
  {
@@ -21394,12 +21465,12 @@ function Recover(props) {
21394
21465
  "data-chipi-recover-action": "cancel",
21395
21466
  onClick: handleCancel,
21396
21467
  disabled: submittingAction !== null,
21397
- children: "Cancel pending op"
21468
+ children: L.cancel
21398
21469
  }
21399
21470
  )
21400
21471
  ] }),
21401
- phase === "submitting-cancel" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: "Cancelling\u2026" }) }),
21402
- phase === "done" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? "New device added." : "Wallet recovered." }) }),
21472
+ phase === "submitting-cancel" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "submitting", children: L.cancelling }) }),
21473
+ phase === "done" && /* @__PURE__ */ jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? L.doneAddDevice : L.doneRecovery }) }),
21403
21474
  error && /* @__PURE__ */ jsx("p", { "data-chipi-recover-error": "", role: "alert", children: error.message })
21404
21475
  ]
21405
21476
  }
@@ -21429,6 +21500,6 @@ pako/dist/pako.esm.mjs:
21429
21500
  (*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) *)
21430
21501
  */
21431
21502
 
21432
- export { ChipiInjector, ChipiProvider, Recover, createChipiWindowObject, derivePrivacyAccount, injectChipiWallet, netPrivateBalance, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, useChipiSession, useChipiWallet, useChipiWalletObject, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useGuardianRecovery, useMigrateWalletToPasskey, useMigrateWalletToShhh, usePrivateBalance, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useShhhSigner, useShield, useSignTypedData, useSyncOnChainTransfers, useThresholdSign, useTransfer, useUnshield, useUpdateWalletEncryption, useX402Payment };
21503
+ export { ChipiInjector, ChipiProvider, DEFAULT_RECOVER_LABELS, Recover, createChipiWindowObject, derivePrivacyAccount, injectChipiWallet, netPrivateBalance, useAddSessionKeyToContract, useApprove, useCallAnyContract, useChipiContext, useChipiSession, useChipiWallet, useChipiWalletObject, useCreateSessionKey, useCreateWallet, useExecuteWithSession, useGetSessionData, useGetSku, useGetSkuList, useGetSkuPurchase, useGetTokenBalance, useGetTransaction, useGetTransactionList, useGetTransactionStatus, useGetWallet, useGuardianRecovery, useMigrateWalletToPasskey, useMigrateWalletToShhh, usePrivateBalance, usePurchaseSku, useRecordSendTransaction, useRevokeSessionKey, useShhhSigner, useShield, useSignTypedData, useSyncOnChainTransfers, useThresholdSign, useTransfer, useUnshield, useUpdateWalletEncryption, useX402Payment, walletSupportsSessions };
21433
21504
  //# sourceMappingURL=index.mjs.map
21434
21505
  //# sourceMappingURL=index.mjs.map