@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/hooks.mjs CHANGED
@@ -23,6 +23,18 @@ function useChipiContext() {
23
23
  }
24
24
  return context;
25
25
  }
26
+
27
+ // src/utils/with-error-code.ts
28
+ function withErrorCode(wrapped, original) {
29
+ const code = original.code;
30
+ if (typeof code === "string") {
31
+ Object.defineProperty(wrapped, "code", { value: code, enumerable: true });
32
+ }
33
+ Object.defineProperty(wrapped, "cause", { value: original, enumerable: false });
34
+ return wrapped;
35
+ }
36
+
37
+ // src/hooks/useCreateWallet.ts
26
38
  function useCreateWallet() {
27
39
  const { chipiSDK } = useChipiContext();
28
40
  const mutation = useMutation({
@@ -53,7 +65,7 @@ function useCreateWallet() {
53
65
  }
54
66
  } catch (error) {
55
67
  if (error instanceof Error) {
56
- throw new Error(`Passkey creation failed: ${error.message}`);
68
+ throw withErrorCode(new Error(`Passkey creation failed: ${error.message}`), error);
57
69
  }
58
70
  throw new Error("Failed to create passkey for wallet");
59
71
  }
@@ -133,6 +145,17 @@ function useGetWallet(input) {
133
145
  fetchWallet
134
146
  };
135
147
  }
148
+
149
+ // src/utils/wallet-capabilities.ts
150
+ var SHHH_SESSION_SIGNER_KINDS = /* @__PURE__ */ new Set(["STARK", "ED25519"]);
151
+ function walletSupportsSessions(wallet) {
152
+ if (!wallet) return false;
153
+ if (wallet.walletType === "CHIPI") return true;
154
+ if (wallet.walletType === "SHHH") {
155
+ return !wallet.signerKind || SHHH_SESSION_SIGNER_KINDS.has(wallet.signerKind);
156
+ }
157
+ return false;
158
+ }
136
159
  function useChipiWallet(config3) {
137
160
  const { chipiSDK } = useChipiContext();
138
161
  const queryClient = useQueryClient();
@@ -213,7 +236,7 @@ function useChipiWallet(config3) {
213
236
  const data = walletQuery.data;
214
237
  return {
215
238
  ...data,
216
- supportsSessionKeys: data.walletType === "CHIPI",
239
+ supportsSessionKeys: walletSupportsSessions(data),
217
240
  shortAddress: formatAddress(data.publicKey)
218
241
  };
219
242
  }, [walletQuery.data, walletQuery.isLoading]);
@@ -270,6 +293,7 @@ function useChipiSession(config3) {
270
293
  const {
271
294
  wallet,
272
295
  encryptKey,
296
+ getEncryptKey,
273
297
  getBearerToken,
274
298
  storedSession,
275
299
  onSessionCreated,
@@ -283,10 +307,13 @@ function useChipiSession(config3) {
283
307
  useEffect(() => {
284
308
  setLocalSession(storedSession ?? null);
285
309
  }, [storedSession]);
286
- const supportsSession = useMemo(() => {
287
- if (!wallet) return false;
288
- return wallet.walletType === "CHIPI";
289
- }, [wallet]);
310
+ const supportsSession = useMemo(() => walletSupportsSessions(wallet), [wallet]);
311
+ const resolveEncryptKey = useCallback(async () => {
312
+ const key = encryptKey || (getEncryptKey ? await getEncryptKey() : void 0);
313
+ if (!key) throw new Error("Encryption key (PIN) is required");
314
+ return key;
315
+ }, [encryptKey, getEncryptKey]);
316
+ const [awaitingRegistrationUntil, setAwaitingRegistrationUntil] = useState(null);
290
317
  const sessionStatusQuery = useQuery({
291
318
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey],
292
319
  queryFn: async () => {
@@ -299,34 +326,41 @@ function useChipiSession(config3) {
299
326
  enabled: Boolean(
300
327
  autoCheckStatus && wallet?.publicKey && localSession?.publicKey && supportsSession
301
328
  ),
302
- staleTime: 3e4
329
+ staleTime: 3e4,
303
330
  // 30 seconds
331
+ // The registration hash comes back before the transaction is in a block, so
332
+ // right after registerSession the struct can still read as zero. Poll until
333
+ // it shows up (or for two minutes), then go back to on-demand reads.
334
+ refetchInterval: (query) => awaitingRegistrationUntil !== null && Date.now() < awaitingRegistrationUntil && (query.state.data?.validUntil ?? 0) === 0 ? 3e3 : false
304
335
  });
336
+ const [revokedSessionKey, setRevokedSessionKey] = useState(null);
305
337
  const sessionState = useMemo(() => {
306
338
  if (!localSession) return "none";
339
+ if (revokedSessionKey === localSession.publicKey) return "revoked";
340
+ if (localSession.validUntil * 1e3 < Date.now()) return "expired";
307
341
  const status = sessionStatusQuery.data;
308
342
  if (status) {
309
- if (!status.isActive) return "revoked";
343
+ if (status.validUntil === 0) return "created";
310
344
  if (status.validUntil * 1e3 < Date.now()) return "expired";
311
345
  if (status.remainingCalls <= 0) return "expired";
346
+ if (!status.isActive) return "revoked";
312
347
  return "active";
313
348
  }
314
- if (localSession.validUntil * 1e3 < Date.now()) return "expired";
315
349
  return "created";
316
- }, [localSession, sessionStatusQuery.data]);
350
+ }, [localSession, revokedSessionKey, sessionStatusQuery.data]);
317
351
  const hasActiveSession = sessionState === "active";
318
352
  const isSessionExpired = sessionState === "expired";
319
353
  const remainingCalls = sessionStatusQuery.data?.remainingCalls;
320
354
  const createSessionMutation = useMutation(
321
355
  {
322
356
  mutationFn: async (params = {}) => {
323
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
324
357
  if (!supportsSession)
325
358
  throw new Error(
326
- "Wallet does not support sessions. Only CHIPI wallets support session keys."
359
+ "Wallet does not support sessions. Session keys need a CHIPI wallet or a SHHH wallet with a STARK / ED25519 signer."
327
360
  );
361
+ const key = await resolveEncryptKey();
328
362
  const sessionData = chipiSDK.sessions.createSessionKey({
329
- encryptKey,
363
+ encryptKey: key,
330
364
  durationSeconds: params.durationSeconds ?? defaultDurationSeconds
331
365
  });
332
366
  return sessionData;
@@ -346,28 +380,38 @@ function useChipiSession(config3) {
346
380
  const registerSessionMutation = useMutation({
347
381
  mutationFn: async (sessionConfig = {}) => {
348
382
  if (!wallet) throw new Error("Wallet is required");
349
- if (!localSession) throw new Error("No session to register. Call createSession first.");
350
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
383
+ const sessionPublicKey = sessionConfig.sessionPublicKey ?? localSession?.publicKey;
384
+ const validUntil = sessionConfig.validUntil ?? localSession?.validUntil;
385
+ if (!sessionPublicKey || validUntil === void 0)
386
+ throw new Error(
387
+ "No session to register. Call createSession first, or pass sessionPublicKey and validUntil."
388
+ );
351
389
  if (!supportsSession) throw new Error("Wallet does not support sessions");
390
+ const key = await resolveEncryptKey();
352
391
  const bearerToken = await getBearerToken();
353
392
  if (!bearerToken) throw new Error("Bearer token is required");
354
- const txHash = await chipiSDK.sessions.addSessionKeyToContract(
355
- {
356
- encryptKey,
357
- wallet,
358
- sessionConfig: {
359
- sessionPublicKey: localSession.publicKey,
360
- validUntil: sessionConfig.validUntil ?? localSession.validUntil,
361
- maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
362
- allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
363
- }
364
- },
365
- bearerToken
366
- );
367
- return txHash;
393
+ const params = {
394
+ encryptKey: key,
395
+ wallet,
396
+ sessionConfig: {
397
+ sessionPublicKey,
398
+ validUntil,
399
+ maxCalls: sessionConfig.maxCalls ?? defaultMaxCalls,
400
+ allowedEntrypoints: sessionConfig.allowedEntrypoints ?? []
401
+ }
402
+ };
403
+ if (sessionConfig.spendingPolicies?.length) {
404
+ return chipiSDK.sessions.setupSession(
405
+ { ...params, spendingPolicies: sessionConfig.spendingPolicies },
406
+ bearerToken
407
+ );
408
+ }
409
+ return chipiSDK.sessions.addSessionKeyToContract(params, bearerToken);
368
410
  },
369
411
  onSuccess: () => {
370
412
  setError(null);
413
+ if (localSession && revokedSessionKey === localSession.publicKey) setRevokedSessionKey(null);
414
+ setAwaitingRegistrationUntil(Date.now() + 12e4);
371
415
  queryClient.invalidateQueries({
372
416
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
373
417
  });
@@ -377,25 +421,28 @@ function useChipiSession(config3) {
377
421
  }
378
422
  });
379
423
  const revokeSessionMutation = useMutation({
380
- mutationFn: async () => {
424
+ mutationFn: async (sessionPublicKey) => {
381
425
  if (!wallet) throw new Error("Wallet is required");
382
- if (!localSession) throw new Error("No session to revoke");
383
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
426
+ const target = sessionPublicKey ?? localSession?.publicKey;
427
+ if (!target) throw new Error("No session to revoke");
384
428
  if (!supportsSession) throw new Error("Wallet does not support sessions");
429
+ const key = await resolveEncryptKey();
385
430
  const bearerToken = await getBearerToken();
386
431
  if (!bearerToken) throw new Error("Bearer token is required");
387
432
  const txHash = await chipiSDK.sessions.revokeSessionKey(
388
433
  {
389
- encryptKey,
434
+ encryptKey: key,
390
435
  wallet,
391
- sessionPublicKey: localSession.publicKey
436
+ sessionPublicKey: target
392
437
  },
393
438
  bearerToken
394
439
  );
395
440
  return txHash;
396
441
  },
397
- onSuccess: () => {
442
+ onSuccess: (_txHash, sessionPublicKey) => {
398
443
  setError(null);
444
+ const target = sessionPublicKey ?? localSession?.publicKey;
445
+ if (target) setRevokedSessionKey(target);
399
446
  queryClient.invalidateQueries({
400
447
  queryKey: ["chipi-session-status", wallet?.publicKey, localSession?.publicKey]
401
448
  });
@@ -408,13 +455,13 @@ function useChipiSession(config3) {
408
455
  mutationFn: async (calls) => {
409
456
  if (!wallet) throw new Error("Wallet is required");
410
457
  if (!localSession) throw new Error("No active session");
411
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
412
458
  if (!hasActiveSession) throw new Error("Session is not active. Register the session first.");
459
+ const key = await resolveEncryptKey();
413
460
  const bearerToken = await getBearerToken();
414
461
  if (!bearerToken) throw new Error("Bearer token is required");
415
462
  const txHash = await chipiSDK.executeTransactionWithSession({
416
463
  params: {
417
- encryptKey,
464
+ encryptKey: key,
418
465
  wallet,
419
466
  session: localSession,
420
467
  calls
@@ -438,13 +485,13 @@ function useChipiSession(config3) {
438
485
  if (!wallet) throw new Error("Wallet is required");
439
486
  if (!localSession)
440
487
  throw new Error("No session to set policy on. Create and register a session first.");
441
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
442
488
  if (!supportsSession) throw new Error("Wallet does not support sessions");
489
+ const key = await resolveEncryptKey();
443
490
  const bearerToken = await getBearerToken();
444
491
  if (!bearerToken) throw new Error("Bearer token is required");
445
492
  const txHash = await chipiSDK.sessions.setSpendingPolicy(
446
493
  {
447
- encryptKey,
494
+ encryptKey: key,
448
495
  wallet,
449
496
  spendingPolicyConfig: {
450
497
  sessionPublicKey: localSession.publicKey,
@@ -466,13 +513,13 @@ function useChipiSession(config3) {
466
513
  mutationFn: async (token) => {
467
514
  if (!wallet) throw new Error("Wallet is required");
468
515
  if (!localSession) throw new Error("No session to remove policy from");
469
- if (!encryptKey) throw new Error("Encryption key (PIN) is required");
470
516
  if (!supportsSession) throw new Error("Wallet does not support sessions");
517
+ const key = await resolveEncryptKey();
471
518
  const bearerToken = await getBearerToken();
472
519
  if (!bearerToken) throw new Error("Bearer token is required");
473
520
  const txHash = await chipiSDK.sessions.removeSpendingPolicy(
474
521
  {
475
- encryptKey,
522
+ encryptKey: key,
476
523
  wallet,
477
524
  sessionPublicKey: localSession.publicKey,
478
525
  token
@@ -500,9 +547,14 @@ function useChipiSession(config3) {
500
547
  },
501
548
  [registerSessionMutation]
502
549
  );
503
- const revokeSession = useCallback(async () => {
504
- return revokeSessionMutation.mutateAsync();
505
- }, [revokeSessionMutation]);
550
+ const revokeSession = useCallback(
551
+ async (sessionPublicKey) => {
552
+ return revokeSessionMutation.mutateAsync(
553
+ typeof sessionPublicKey === "string" ? sessionPublicKey : void 0
554
+ );
555
+ },
556
+ [revokeSessionMutation]
557
+ );
506
558
  const executeWithSession = useCallback(
507
559
  async (calls) => {
508
560
  return executeWithSessionMutation.mutateAsync(calls);
@@ -636,7 +688,7 @@ function useMigrateWalletToPasskey() {
636
688
  };
637
689
  } catch (error) {
638
690
  if (error instanceof Error) {
639
- throw new Error(`Migration failed: ${error.message}`);
691
+ throw withErrorCode(new Error(`Migration failed: ${error.message}`), error);
640
692
  }
641
693
  throw new Error("Failed to migrate wallet to passkey");
642
694
  }