@chipi-stack/chipi-react 14.13.1 → 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.js CHANGED
@@ -217,6 +217,17 @@ function useGetWallet(input) {
217
217
  fetchWallet
218
218
  };
219
219
  }
220
+
221
+ // src/utils/wallet-capabilities.ts
222
+ var SHHH_SESSION_SIGNER_KINDS = /* @__PURE__ */ new Set(["STARK", "ED25519"]);
223
+ function walletSupportsSessions(wallet) {
224
+ if (!wallet) return false;
225
+ if (wallet.walletType === "CHIPI") return true;
226
+ if (wallet.walletType === "SHHH") {
227
+ return !wallet.signerKind || SHHH_SESSION_SIGNER_KINDS.has(wallet.signerKind);
228
+ }
229
+ return false;
230
+ }
220
231
  function useChipiWallet(config3) {
221
232
  const { chipiSDK } = useChipiContext();
222
233
  const queryClient = reactQuery.useQueryClient();
@@ -297,7 +308,7 @@ function useChipiWallet(config3) {
297
308
  const data = walletQuery.data;
298
309
  return {
299
310
  ...data,
300
- supportsSessionKeys: data.walletType === "CHIPI",
311
+ supportsSessionKeys: walletSupportsSessions(data),
301
312
  shortAddress: formatAddress(data.publicKey)
302
313
  };
303
314
  }, [walletQuery.data, walletQuery.isLoading]);
@@ -354,6 +365,7 @@ function useChipiSession(config3) {
354
365
  const {
355
366
  wallet,
356
367
  encryptKey,
368
+ getEncryptKey,
357
369
  getBearerToken,
358
370
  storedSession,
359
371
  onSessionCreated,
@@ -367,10 +379,13 @@ function useChipiSession(config3) {
367
379
  React.useEffect(() => {
368
380
  setLocalSession(storedSession ?? null);
369
381
  }, [storedSession]);
370
- const supportsSession = React.useMemo(() => {
371
- if (!wallet) return false;
372
- return wallet.walletType === "CHIPI";
373
- }, [wallet]);
382
+ const supportsSession = React.useMemo(() => walletSupportsSessions(wallet), [wallet]);
383
+ const resolveEncryptKey = React.useCallback(async () => {
384
+ const key = encryptKey || (getEncryptKey ? await getEncryptKey() : void 0);
385
+ if (!key) throw new Error("Encryption key (PIN) is required");
386
+ return key;
387
+ }, [encryptKey, getEncryptKey]);
388
+ const [awaitingRegistrationUntil, setAwaitingRegistrationUntil] = React.useState(null);
374
389
  const sessionStatusQuery = reactQuery.useQuery({
375
390
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey],
376
391
  queryFn: async () => {
@@ -383,8 +398,12 @@ function useChipiSession(config3) {
383
398
  enabled: Boolean(
384
399
  autoCheckStatus && wallet?.publicKey && localSession?.publicKey && supportsSession
385
400
  ),
386
- staleTime: 3e4
401
+ staleTime: 3e4,
387
402
  // 30 seconds
403
+ // The registration hash comes back before the transaction is in a block, so
404
+ // right after registerSession the struct can still read as zero. Poll until
405
+ // it shows up (or for two minutes), then go back to on-demand reads.
406
+ refetchInterval: (query) => awaitingRegistrationUntil !== null && Date.now() < awaitingRegistrationUntil && (query.state.data?.validUntil ?? 0) === 0 ? 3e3 : false
388
407
  });
389
408
  const [revokedSessionKey, setRevokedSessionKey] = React.useState(null);
390
409
  const sessionState = React.useMemo(() => {
@@ -407,13 +426,13 @@ function useChipiSession(config3) {
407
426
  const createSessionMutation = reactQuery.useMutation(
408
427
  {
409
428
  mutationFn: async (params = {}) => {
410
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
411
429
  if (!supportsSession)
412
430
  throw new Error(
413
- "Wallet does not support sessions. Only CHIPI wallets support session keys."
431
+ "Wallet does not support sessions. Session keys need a CHIPI wallet or a SHHH wallet with a STARK / ED25519 signer."
414
432
  );
433
+ const key = await resolveEncryptKey();
415
434
  const sessionData = chipiSDK.sessions.createSessionKey({
416
- encryptKey,
435
+ encryptKey: key,
417
436
  durationSeconds: params.durationSeconds ?? defaultDurationSeconds
418
437
  });
419
438
  return sessionData;
@@ -433,29 +452,38 @@ function useChipiSession(config3) {
433
452
  const registerSessionMutation = reactQuery.useMutation({
434
453
  mutationFn: async (sessionConfig = {}) => {
435
454
  if (!wallet) throw new Error("Wallet is required");
436
- if (!localSession) throw new Error("No session to register. Call createSession first.");
437
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
455
+ const sessionPublicKey = sessionConfig.sessionPublicKey ?? localSession?.publicKey;
456
+ const validUntil = sessionConfig.validUntil ?? localSession?.validUntil;
457
+ if (!sessionPublicKey || validUntil === void 0)
458
+ throw new Error(
459
+ "No session to register. Call createSession first, or pass sessionPublicKey and validUntil."
460
+ );
438
461
  if (!supportsSession) throw new Error("Wallet does not support sessions");
462
+ const key = await resolveEncryptKey();
439
463
  const bearerToken = await getBearerToken();
440
464
  if (!bearerToken) throw new Error("Bearer token is required");
441
- const txHash = await chipiSDK.sessions.addSessionKeyToContract(
442
- {
443
- encryptKey,
444
- wallet,
445
- sessionConfig: {
446
- sessionPublicKey: localSession.publicKey,
447
- validUntil: sessionConfig.validUntil ?? localSession.validUntil,
448
- maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
449
- allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
450
- }
451
- },
452
- bearerToken
453
- );
454
- return txHash;
465
+ const params = {
466
+ encryptKey: key,
467
+ wallet,
468
+ sessionConfig: {
469
+ sessionPublicKey,
470
+ validUntil,
471
+ maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
472
+ allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
473
+ }
474
+ };
475
+ if (sessionConfig.spendingPolicies?.length) {
476
+ return chipiSDK.sessions.setupSession(
477
+ { ...params, spendingPolicies: sessionConfig.spendingPolicies },
478
+ bearerToken
479
+ );
480
+ }
481
+ return chipiSDK.sessions.addSessionKeyToContract(params, bearerToken);
455
482
  },
456
483
  onSuccess: () => {
457
484
  setError(null);
458
485
  if (localSession && revokedSessionKey === localSession.publicKey) setRevokedSessionKey(null);
486
+ setAwaitingRegistrationUntil(Date.now() + 12e4);
459
487
  queryClient.invalidateQueries({
460
488
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
461
489
  });
@@ -465,26 +493,28 @@ function useChipiSession(config3) {
465
493
  }
466
494
  });
467
495
  const revokeSessionMutation = reactQuery.useMutation({
468
- mutationFn: async () => {
496
+ mutationFn: async (sessionPublicKey) => {
469
497
  if (!wallet) throw new Error("Wallet is required");
470
- if (!localSession) throw new Error("No session to revoke");
471
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
498
+ const target = sessionPublicKey ?? localSession?.publicKey;
499
+ if (!target) throw new Error("No session to revoke");
472
500
  if (!supportsSession) throw new Error("Wallet does not support sessions");
501
+ const key = await resolveEncryptKey();
473
502
  const bearerToken = await getBearerToken();
474
503
  if (!bearerToken) throw new Error("Bearer token is required");
475
504
  const txHash = await chipiSDK.sessions.revokeSessionKey(
476
505
  {
477
- encryptKey,
506
+ encryptKey: key,
478
507
  wallet,
479
- sessionPublicKey: localSession.publicKey
508
+ sessionPublicKey: target
480
509
  },
481
510
  bearerToken
482
511
  );
483
512
  return txHash;
484
513
  },
485
- onSuccess: () => {
514
+ onSuccess: (_txHash, sessionPublicKey) => {
486
515
  setError(null);
487
- if (localSession) setRevokedSessionKey(localSession.publicKey);
516
+ const target = sessionPublicKey ?? localSession?.publicKey;
517
+ if (target) setRevokedSessionKey(target);
488
518
  queryClient.invalidateQueries({
489
519
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
490
520
  });
@@ -497,13 +527,13 @@ function useChipiSession(config3) {
497
527
  mutationFn: async (calls) => {
498
528
  if (!wallet) throw new Error("Wallet is required");
499
529
  if (!localSession) throw new Error("No active session");
500
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
501
530
  if (!hasActiveSession) throw new Error("Session is not active. Register the session first.");
531
+ const key = await resolveEncryptKey();
502
532
  const bearerToken = await getBearerToken();
503
533
  if (!bearerToken) throw new Error("Bearer token is required");
504
534
  const txHash = await chipiSDK.executeTransactionWithSession({
505
535
  params: {
506
- encryptKey,
536
+ encryptKey: key,
507
537
  wallet,
508
538
  session: localSession,
509
539
  calls
@@ -527,13 +557,13 @@ function useChipiSession(config3) {
527
557
  if (!wallet) throw new Error("Wallet is required");
528
558
  if (!localSession)
529
559
  throw new Error("No session to set policy on. Create and register a session first.");
530
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
531
560
  if (!supportsSession) throw new Error("Wallet does not support sessions");
561
+ const key = await resolveEncryptKey();
532
562
  const bearerToken = await getBearerToken();
533
563
  if (!bearerToken) throw new Error("Bearer token is required");
534
564
  const txHash = await chipiSDK.sessions.setSpendingPolicy(
535
565
  {
536
- encryptKey,
566
+ encryptKey: key,
537
567
  wallet,
538
568
  spendingPolicyConfig: {
539
569
  sessionPublicKey: localSession.publicKey,
@@ -555,13 +585,13 @@ function useChipiSession(config3) {
555
585
  mutationFn: async (token) => {
556
586
  if (!wallet) throw new Error("Wallet is required");
557
587
  if (!localSession) throw new Error("No session to remove policy from");
558
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
559
588
  if (!supportsSession) throw new Error("Wallet does not support sessions");
589
+ const key = await resolveEncryptKey();
560
590
  const bearerToken = await getBearerToken();
561
591
  if (!bearerToken) throw new Error("Bearer token is required");
562
592
  const txHash = await chipiSDK.sessions.removeSpendingPolicy(
563
593
  {
564
- encryptKey,
594
+ encryptKey: key,
565
595
  wallet,
566
596
  sessionPublicKey: localSession.publicKey,
567
597
  token
@@ -589,9 +619,14 @@ function useChipiSession(config3) {
589
619
  },
590
620
  [registerSessionMutation]
591
621
  );
592
- const revokeSession = React.useCallback(async () => {
593
- return revokeSessionMutation.mutateAsync();
594
- }, [revokeSessionMutation]);
622
+ const revokeSession = React.useCallback(
623
+ async (sessionPublicKey) => {
624
+ return revokeSessionMutation.mutateAsync(
625
+ typeof sessionPublicKey === "string" ? sessionPublicKey : void 0
626
+ );
627
+ },
628
+ [revokeSessionMutation]
629
+ );
595
630
  const executeWithSession = React.useCallback(
596
631
  async (calls) => {
597
632
  return executeWithSessionMutation.mutateAsync(calls);
@@ -21278,6 +21313,21 @@ function ChipiInjector({ children, ...config3 }) {
21278
21313
  }, [walletAddr]);
21279
21314
  return children ?? null;
21280
21315
  }
21316
+ var DEFAULT_RECOVER_LABELS = {
21317
+ proposeAddDevice: "Propose new device",
21318
+ proposeRecovery: "Initiate recovery",
21319
+ executeAddDevice: "Execute add-owner",
21320
+ executeRecovery: "Finalize recovery",
21321
+ timelockAddDevice: "48-hour timelock before this device becomes active.",
21322
+ timelockRecovery: "7-day timelock before the recovered wallet becomes active.",
21323
+ submitting: "Submitting\u2026",
21324
+ secondsRemaining: (s) => `${s}s remaining`,
21325
+ cancel: "Cancel pending op",
21326
+ timelockElapsed: "Timelock elapsed.",
21327
+ cancelling: "Cancelling\u2026",
21328
+ doneAddDevice: "New device added.",
21329
+ doneRecovery: "Wallet recovered."
21330
+ };
21281
21331
  function deriveStaticPhase(pendingOp, executed, isReady, nowSecs) {
21282
21332
  if (executed) return "done";
21283
21333
  if (!pendingOp) return "form-idle";
@@ -21359,10 +21409,17 @@ function Recover(props) {
21359
21409
  setSubmittingAction(null);
21360
21410
  }
21361
21411
  }, [activePendingOp, onCancel]);
21412
+ const L = React.useMemo(
21413
+ () => ({
21414
+ ...DEFAULT_RECOVER_LABELS,
21415
+ ...Object.fromEntries(Object.entries(props.labels ?? {}).filter(([, v]) => v !== void 0))
21416
+ }),
21417
+ [props.labels]
21418
+ );
21362
21419
  const isAddDevice = mode === "add-device";
21363
- const proposeLabel = isAddDevice ? "Propose new device" : "Initiate recovery";
21364
- const executeLabel = isAddDevice ? "Execute add-owner" : "Finalize recovery";
21365
- const timelockDescription = isAddDevice ? "48-hour timelock before this device becomes active." : "7-day timelock before the recovered wallet becomes active.";
21420
+ const proposeLabel = isAddDevice ? L.proposeAddDevice : L.proposeRecovery;
21421
+ const executeLabel = isAddDevice ? L.executeAddDevice : L.executeRecovery;
21422
+ const timelockDescription = isAddDevice ? L.timelockAddDevice : L.timelockRecovery;
21366
21423
  return /* @__PURE__ */ jsxRuntime.jsxs(
21367
21424
  "div",
21368
21425
  {
@@ -21390,18 +21447,15 @@ function Recover(props) {
21390
21447
  }
21391
21448
  )
21392
21449
  ] }),
21393
- phase === "submitting-propose" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: "Submitting\u2026" }) }),
21450
+ phase === "submitting-propose" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "propose", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: L.submitting }) }),
21394
21451
  phase === "awaiting-timelock" && activePendingOp && /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-chipi-recover-section": "awaiting", children: [
21395
21452
  /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "awaiting", children: timelockDescription }),
21396
- /* @__PURE__ */ jsxRuntime.jsxs(
21453
+ /* @__PURE__ */ jsxRuntime.jsx(
21397
21454
  "p",
21398
21455
  {
21399
21456
  "data-chipi-recover-countdown": "",
21400
21457
  "data-chipi-recover-seconds-remaining": String(secondsRemaining),
21401
- children: [
21402
- secondsRemaining,
21403
- "s remaining"
21404
- ]
21458
+ children: L.secondsRemaining(secondsRemaining)
21405
21459
  }
21406
21460
  ),
21407
21461
  /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-op-id": "", children: String(activePendingOp.opId) }),
@@ -21412,12 +21466,12 @@ function Recover(props) {
21412
21466
  "data-chipi-recover-action": "cancel",
21413
21467
  onClick: handleCancel,
21414
21468
  disabled: submittingAction !== null,
21415
- children: "Cancel pending op"
21469
+ children: L.cancel
21416
21470
  }
21417
21471
  )
21418
21472
  ] }),
21419
21473
  (phase === "ready-to-execute" || phase === "submitting-execute") && activePendingOp && /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-chipi-recover-section": "execute", children: [
21420
- /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "ready", children: "Timelock elapsed." }),
21474
+ /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "ready", children: L.timelockElapsed }),
21421
21475
  /* @__PURE__ */ jsxRuntime.jsx(
21422
21476
  "button",
21423
21477
  {
@@ -21435,12 +21489,12 @@ function Recover(props) {
21435
21489
  "data-chipi-recover-action": "cancel",
21436
21490
  onClick: handleCancel,
21437
21491
  disabled: submittingAction !== null,
21438
- children: "Cancel pending op"
21492
+ children: L.cancel
21439
21493
  }
21440
21494
  )
21441
21495
  ] }),
21442
- phase === "submitting-cancel" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: "Cancelling\u2026" }) }),
21443
- phase === "done" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? "New device added." : "Wallet recovered." }) }),
21496
+ phase === "submitting-cancel" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "cancel", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "submitting", children: L.cancelling }) }),
21497
+ phase === "done" && /* @__PURE__ */ jsxRuntime.jsx("section", { "data-chipi-recover-section": "done", children: /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-status": "done", children: isAddDevice ? L.doneAddDevice : L.doneRecovery }) }),
21444
21498
  error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-chipi-recover-error": "", role: "alert", children: error.message })
21445
21499
  ]
21446
21500
  }
@@ -21480,6 +21534,7 @@ Object.defineProperty(exports, "ChainToken", {
21480
21534
  });
21481
21535
  exports.ChipiInjector = ChipiInjector;
21482
21536
  exports.ChipiProvider = ChipiProvider;
21537
+ exports.DEFAULT_RECOVER_LABELS = DEFAULT_RECOVER_LABELS;
21483
21538
  exports.Recover = Recover;
21484
21539
  exports.createChipiWindowObject = createChipiWindowObject;
21485
21540
  exports.derivePrivacyAccount = derivePrivacyAccount;
@@ -21520,5 +21575,6 @@ exports.useTransfer = useTransfer;
21520
21575
  exports.useUnshield = useUnshield;
21521
21576
  exports.useUpdateWalletEncryption = useUpdateWalletEncryption;
21522
21577
  exports.useX402Payment = useX402Payment;
21578
+ exports.walletSupportsSessions = walletSupportsSessions;
21523
21579
  //# sourceMappingURL=index.js.map
21524
21580
  //# sourceMappingURL=index.js.map