@cavos/kit 0.0.3 → 0.0.4

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.
@@ -209,6 +209,8 @@ declare class SolanaAdapter {
209
209
  */
210
210
  buildAddSignerViaPasskey(account: string, newSigner: DevicePublicKey, passkey: DevicePublicKey, leaves: Uint8Array[], leafIndex: number, assertion: PasskeyAssertion): TransactionInstruction[];
211
211
  /** Read whether `passkey` is a registered approver. */
212
+ /** True if the account has at least one passkey registered as an approver. */
213
+ hasPasskeyApprover(account: string): Promise<boolean>;
212
214
  isApprover(account: string, passkey: DevicePublicKey): Promise<boolean>;
213
215
  /** Read the current passkey-approval nonce. */
214
216
  passkeyNonce(account: string): Promise<bigint>;
@@ -413,6 +415,8 @@ declare class CavosSolana {
413
415
  private readonly feePayer?;
414
416
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
415
417
  readonly chain: "solana";
418
+ /** True when this connect just created a brand-new account (first sign-up). */
419
+ isNewAccount: boolean;
416
420
  private constructor();
417
421
  get publicKey(): DevicePublicKey;
418
422
  static connect(opts: ConnectSolanaOptions): Promise<CavosSolana>;
@@ -431,6 +435,12 @@ declare class CavosSolana {
431
435
  addApprover(pubkey: DevicePublicKey): Promise<{
432
436
  transactionHash?: string;
433
437
  }>;
438
+ /** True if this account already has a passkey enrolled as an approver, so a
439
+ * new device can be approved with the passkey instead of the email flow. */
440
+ hasPasskey(): Promise<boolean>;
441
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
442
+ * Used to poll for readiness after a passkey approval before it's indexed. */
443
+ isReady(): Promise<boolean>;
434
444
  /**
435
445
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
436
446
  * device with the user's synced passkey. Gasless via the relayer — the bundle
@@ -613,6 +623,8 @@ declare class CavosStellar {
613
623
  private readonly sourceKeypair?;
614
624
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
615
625
  readonly chain: "stellar";
626
+ /** True when this connect just created a brand-new account (first sign-up). */
627
+ isNewAccount: boolean;
616
628
  private constructor();
617
629
  get publicKey(): DevicePublicKey;
618
630
  static connect(opts: ConnectStellarOptions): Promise<CavosStellar>;
@@ -631,6 +643,12 @@ declare class CavosStellar {
631
643
  addApprover(pubkey: DevicePublicKey): Promise<{
632
644
  transactionHash?: string;
633
645
  }>;
646
+ /** True if this account already has a passkey enrolled as an approver, so a
647
+ * new device can be approved with the passkey instead of the email flow. */
648
+ hasPasskey(): Promise<boolean>;
649
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
650
+ * Used to poll for readiness after a passkey approval before it's indexed. */
651
+ isReady(): Promise<boolean>;
634
652
  /**
635
653
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
636
654
  * device using the user's synced passkey. Gasless via the relayer — the call
@@ -863,6 +881,9 @@ declare class Cavos {
863
881
  readonly chain: "starknet";
864
882
  /** Request id of the pending device-addition, when status is needs-device-approval. */
865
883
  pendingRequestId: string | null;
884
+ /** True when this connect just created & deployed a brand-new account (first
885
+ * sign-up), so the UI can offer a one-time "secure your account" step. */
886
+ isNewAccount: boolean;
866
887
  private constructor();
867
888
  /**
868
889
  * Unified entry point. Pick a `chain` and an `network` environment; the kit
@@ -906,6 +927,13 @@ declare class Cavos {
906
927
  addApprover(pubkey: DevicePublicKey): Promise<{
907
928
  transactionHash?: string;
908
929
  }>;
930
+ /** True if this account already has a passkey enrolled as an approver, so a
931
+ * new device can be approved with the passkey instead of the email flow. */
932
+ hasPasskey(): Promise<boolean>;
933
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
934
+ * Cheap and side-effect free — used to poll for readiness after a passkey /
935
+ * device approval submits, before the new signer is indexed. */
936
+ isReady(): Promise<boolean>;
909
937
  /**
910
938
  * From a brand-new browser (status `needs-device-approval`), use the user's
911
939
  * synced passkey to authorize adding THIS device — no trip back to an already-
@@ -988,7 +1016,8 @@ interface PasskeyApprovable {
988
1016
  */
989
1017
  declare function approveDeviceEverywhere(wallets: PasskeyApprovable[], passkey: PasskeySigner): Promise<{
990
1018
  chain: string;
991
- transactionHash: string;
1019
+ transactionHash?: string;
1020
+ error?: string;
992
1021
  }[]>;
993
1022
 
994
1023
  export { batchChallenge as $, type AuthProvider as A, type RecoverStellarOptions as B, type ChainAdapter as C, type DevicePublicKey as D, type EnrolledPasskey as E, FACTORY_CONTRACT_ID as F, type RecoveryOptions as G, SECP256R1_PROGRAM_ID as H, type Identity as I, SOLANA_NETWORKS as J, STELLAR_NETWORKS as K, SolanaAdapter as L, type SolanaAdapterOptions as M, NATIVE_SAC_ID as N, type SolanaNetwork as O, type PendingDeviceRequest as P, SolanaRelayer as Q, type RegisteredWallet as R, type StellarNetwork as S, type SolanaRelayerOptions as T, StaticIdentity as U, StellarRelayer as V, type WalletRegistry as W, type StellarRelayerOptions as X, anchorDiscriminator as Y, approveDeviceEverywhere as Z, base64urlEncode as _, type RecoveryClient as a, buildSecp256r1Instruction as a0, compressedPubkey as a1, encodeLowSSignature as a2, lowS as a3, recoverCandidatePublicKeys as a4, serializeInstructions as a5, webauthnDigest as a6, type DeviceSigner as b, type DeviceSignature as c, type ComputeAddressParams as d, type ChainCall as e, type PasskeyAssertion as f, Cavos as g, CavosSolana as h, CavosStellar as i, type CavosWallet as j, type Chain as k, type ConnectOptions as l, type ConnectSolanaOptions as m, type ConnectStatus as n, type ConnectStellarOptions as o, DEVICE_ACCOUNT_PROGRAM_ID as p, DEVICE_ACCOUNT_WASM_HASH as q, InMemoryWalletRegistry as r, type InstructionAccount as s, type InstructionData as t, type NetworkEnv as u, type PasskeyApprovable as v, type PasskeyEnrollParams as w, PasskeySigner as x, type PasskeySignerOptions as y, type RecoverSolanaOptions as z };
@@ -209,6 +209,8 @@ declare class SolanaAdapter {
209
209
  */
210
210
  buildAddSignerViaPasskey(account: string, newSigner: DevicePublicKey, passkey: DevicePublicKey, leaves: Uint8Array[], leafIndex: number, assertion: PasskeyAssertion): TransactionInstruction[];
211
211
  /** Read whether `passkey` is a registered approver. */
212
+ /** True if the account has at least one passkey registered as an approver. */
213
+ hasPasskeyApprover(account: string): Promise<boolean>;
212
214
  isApprover(account: string, passkey: DevicePublicKey): Promise<boolean>;
213
215
  /** Read the current passkey-approval nonce. */
214
216
  passkeyNonce(account: string): Promise<bigint>;
@@ -413,6 +415,8 @@ declare class CavosSolana {
413
415
  private readonly feePayer?;
414
416
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
415
417
  readonly chain: "solana";
418
+ /** True when this connect just created a brand-new account (first sign-up). */
419
+ isNewAccount: boolean;
416
420
  private constructor();
417
421
  get publicKey(): DevicePublicKey;
418
422
  static connect(opts: ConnectSolanaOptions): Promise<CavosSolana>;
@@ -431,6 +435,12 @@ declare class CavosSolana {
431
435
  addApprover(pubkey: DevicePublicKey): Promise<{
432
436
  transactionHash?: string;
433
437
  }>;
438
+ /** True if this account already has a passkey enrolled as an approver, so a
439
+ * new device can be approved with the passkey instead of the email flow. */
440
+ hasPasskey(): Promise<boolean>;
441
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
442
+ * Used to poll for readiness after a passkey approval before it's indexed. */
443
+ isReady(): Promise<boolean>;
434
444
  /**
435
445
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
436
446
  * device with the user's synced passkey. Gasless via the relayer — the bundle
@@ -613,6 +623,8 @@ declare class CavosStellar {
613
623
  private readonly sourceKeypair?;
614
624
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
615
625
  readonly chain: "stellar";
626
+ /** True when this connect just created a brand-new account (first sign-up). */
627
+ isNewAccount: boolean;
616
628
  private constructor();
617
629
  get publicKey(): DevicePublicKey;
618
630
  static connect(opts: ConnectStellarOptions): Promise<CavosStellar>;
@@ -631,6 +643,12 @@ declare class CavosStellar {
631
643
  addApprover(pubkey: DevicePublicKey): Promise<{
632
644
  transactionHash?: string;
633
645
  }>;
646
+ /** True if this account already has a passkey enrolled as an approver, so a
647
+ * new device can be approved with the passkey instead of the email flow. */
648
+ hasPasskey(): Promise<boolean>;
649
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
650
+ * Used to poll for readiness after a passkey approval before it's indexed. */
651
+ isReady(): Promise<boolean>;
634
652
  /**
635
653
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
636
654
  * device using the user's synced passkey. Gasless via the relayer — the call
@@ -863,6 +881,9 @@ declare class Cavos {
863
881
  readonly chain: "starknet";
864
882
  /** Request id of the pending device-addition, when status is needs-device-approval. */
865
883
  pendingRequestId: string | null;
884
+ /** True when this connect just created & deployed a brand-new account (first
885
+ * sign-up), so the UI can offer a one-time "secure your account" step. */
886
+ isNewAccount: boolean;
866
887
  private constructor();
867
888
  /**
868
889
  * Unified entry point. Pick a `chain` and an `network` environment; the kit
@@ -906,6 +927,13 @@ declare class Cavos {
906
927
  addApprover(pubkey: DevicePublicKey): Promise<{
907
928
  transactionHash?: string;
908
929
  }>;
930
+ /** True if this account already has a passkey enrolled as an approver, so a
931
+ * new device can be approved with the passkey instead of the email flow. */
932
+ hasPasskey(): Promise<boolean>;
933
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
934
+ * Cheap and side-effect free — used to poll for readiness after a passkey /
935
+ * device approval submits, before the new signer is indexed. */
936
+ isReady(): Promise<boolean>;
909
937
  /**
910
938
  * From a brand-new browser (status `needs-device-approval`), use the user's
911
939
  * synced passkey to authorize adding THIS device — no trip back to an already-
@@ -988,7 +1016,8 @@ interface PasskeyApprovable {
988
1016
  */
989
1017
  declare function approveDeviceEverywhere(wallets: PasskeyApprovable[], passkey: PasskeySigner): Promise<{
990
1018
  chain: string;
991
- transactionHash: string;
1019
+ transactionHash?: string;
1020
+ error?: string;
992
1021
  }[]>;
993
1022
 
994
1023
  export { batchChallenge as $, type AuthProvider as A, type RecoverStellarOptions as B, type ChainAdapter as C, type DevicePublicKey as D, type EnrolledPasskey as E, FACTORY_CONTRACT_ID as F, type RecoveryOptions as G, SECP256R1_PROGRAM_ID as H, type Identity as I, SOLANA_NETWORKS as J, STELLAR_NETWORKS as K, SolanaAdapter as L, type SolanaAdapterOptions as M, NATIVE_SAC_ID as N, type SolanaNetwork as O, type PendingDeviceRequest as P, SolanaRelayer as Q, type RegisteredWallet as R, type StellarNetwork as S, type SolanaRelayerOptions as T, StaticIdentity as U, StellarRelayer as V, type WalletRegistry as W, type StellarRelayerOptions as X, anchorDiscriminator as Y, approveDeviceEverywhere as Z, base64urlEncode as _, type RecoveryClient as a, buildSecp256r1Instruction as a0, compressedPubkey as a1, encodeLowSSignature as a2, lowS as a3, recoverCandidatePublicKeys as a4, serializeInstructions as a5, webauthnDigest as a6, type DeviceSigner as b, type DeviceSignature as c, type ComputeAddressParams as d, type ChainCall as e, type PasskeyAssertion as f, Cavos as g, CavosSolana as h, CavosStellar as i, type CavosWallet as j, type Chain as k, type ConnectOptions as l, type ConnectSolanaOptions as m, type ConnectStatus as n, type ConnectStellarOptions as o, DEVICE_ACCOUNT_PROGRAM_ID as p, DEVICE_ACCOUNT_WASM_HASH as q, InMemoryWalletRegistry as r, type InstructionAccount as s, type InstructionData as t, type NetworkEnv as u, type PasskeyApprovable as v, type PasskeyEnrollParams as w, PasskeySigner as x, type PasskeySignerOptions as y, type RecoverSolanaOptions as z };
@@ -254,6 +254,17 @@ var StarknetAdapter = class {
254
254
  });
255
255
  return BigInt(res[0] ?? 0) !== 0n;
256
256
  }
257
+ /** True if the account has at least one passkey registered as an approver.
258
+ * Lets the UI decide whether to offer passkey approval before prompting. */
259
+ async hasPasskeyApprover(accountAddress) {
260
+ if (!this.opts.provider) throw new Error("kit/starknet: provider required for reads");
261
+ const res = await this.opts.provider.callContract({
262
+ contractAddress: accountAddress,
263
+ entrypoint: "get_approver_count",
264
+ calldata: []
265
+ });
266
+ return BigInt(res[0] ?? 0) > 0n;
267
+ }
257
268
  async getPasskeyNonce(accountAddress) {
258
269
  if (!this.opts.provider) throw new Error("kit/starknet: provider required for reads");
259
270
  const res = await this.opts.provider.callContract({
@@ -590,6 +601,11 @@ var SolanaAdapter = class {
590
601
  return [precompileIx, ix];
591
602
  }
592
603
  /** Read whether `passkey` is a registered approver. */
604
+ /** True if the account has at least one passkey registered as an approver. */
605
+ async hasPasskeyApprover(account) {
606
+ const approvers = await this.fetchApprovers(new PublicKey(account));
607
+ return approvers.length > 0;
608
+ }
593
609
  async isApprover(account, passkey) {
594
610
  const approvers = await this.fetchApprovers(new PublicKey(account));
595
611
  const target = Buffer.from(compressedPubkey(passkey)).toString("hex");
@@ -808,8 +824,8 @@ function u64le(n) {
808
824
  new DataView(b.buffer, b.byteOffset, 8).setBigUint64(0, BigInt(n), true);
809
825
  return b;
810
826
  }
811
- function readU64le(buf, offset) {
812
- return new DataView(buf.buffer, buf.byteOffset, buf.length).getBigUint64(
827
+ function readU64le(buf2, offset) {
828
+ return new DataView(buf2.buffer, buf2.byteOffset, buf2.length).getBigUint64(
813
829
  offset,
814
830
  true
815
831
  );
@@ -1280,6 +1296,8 @@ var CavosSolana = class _CavosSolana {
1280
1296
  this.feePayer = feePayer;
1281
1297
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
1282
1298
  this.chain = "solana";
1299
+ /** True when this connect just created a brand-new account (first sign-up). */
1300
+ this.isNewAccount = false;
1283
1301
  }
1284
1302
  get publicKey() {
1285
1303
  return this.devicePubkey;
@@ -1330,7 +1348,7 @@ var CavosSolana = class _CavosSolana {
1330
1348
  }
1331
1349
  await registry.register({ userId: identity.userId, address, initialSigner: devicePubkey });
1332
1350
  const isSigner = await adapter.isAuthorizedSigner(address, devicePubkey);
1333
- return new _CavosSolana(
1351
+ const wallet = new _CavosSolana(
1334
1352
  identity,
1335
1353
  address,
1336
1354
  isSigner ? "ready" : "needs-device-approval",
@@ -1340,6 +1358,8 @@ var CavosSolana = class _CavosSolana {
1340
1358
  relayer,
1341
1359
  opts.feePayer
1342
1360
  );
1361
+ wallet.isNewAccount = !deployed && isSigner;
1362
+ return wallet;
1343
1363
  }
1344
1364
  /** Authorize an additional device signer (device-signed via precompile). */
1345
1365
  async addSigner(pubkey) {
@@ -1366,6 +1386,16 @@ var CavosSolana = class _CavosSolana {
1366
1386
  const transactionHash = await this.send(ixs);
1367
1387
  return { transactionHash };
1368
1388
  }
1389
+ /** True if this account already has a passkey enrolled as an approver, so a
1390
+ * new device can be approved with the passkey instead of the email flow. */
1391
+ async hasPasskey() {
1392
+ return this.adapter.hasPasskeyApprover(this.address);
1393
+ }
1394
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
1395
+ * Used to poll for readiness after a passkey approval before it's indexed. */
1396
+ async isReady() {
1397
+ return this.adapter.isAuthorizedSigner(this.address, this.devicePubkey);
1398
+ }
1369
1399
  /**
1370
1400
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
1371
1401
  * device with the user's synced passkey. Gasless via the relayer — the bundle
@@ -1655,6 +1685,25 @@ var StellarAdapter = class {
1655
1685
  ]);
1656
1686
  }
1657
1687
  /** Read whether `passkey` is a registered approver (read-only simulation). */
1688
+ /** True if the account has at least one passkey registered as an approver.
1689
+ * Reads the contract's `approvers` view and checks the list is non-empty. */
1690
+ async hasPasskeyApprover(accountAddress, readSource) {
1691
+ if (!await this.isDeployed(accountAddress)) return false;
1692
+ const { Account: Account3, TransactionBuilder: TransactionBuilder2, BASE_FEE: BASE_FEE2 } = await import('@stellar/stellar-sdk');
1693
+ const src = new Account3(readSource, "0");
1694
+ const op = Operation.invokeHostFunction({
1695
+ func: invokeFunc(accountAddress, "approvers", []),
1696
+ auth: []
1697
+ });
1698
+ const tx2 = new TransactionBuilder2(src, { fee: BASE_FEE2, networkPassphrase: this.passphrase }).addOperation(op).setTimeout(30).build();
1699
+ const sim = await this.server().simulateTransaction(tx2);
1700
+ if (rpc.Api.isSimulationError(sim)) {
1701
+ throw new Error(`kit/stellar: approvers simulation failed: ${sim.error}`);
1702
+ }
1703
+ if (!sim.result?.retval) return false;
1704
+ const list = scValToNative(sim.result.retval);
1705
+ return Array.isArray(list) && list.length > 0;
1706
+ }
1658
1707
  async isApprover(accountAddress, passkey, readSource) {
1659
1708
  if (!await this.isDeployed(accountAddress)) return false;
1660
1709
  const { Account: Account3, TransactionBuilder: TransactionBuilder2, BASE_FEE: BASE_FEE2 } = await import('@stellar/stellar-sdk');
@@ -1857,6 +1906,8 @@ var CavosStellar = class _CavosStellar {
1857
1906
  this.sourceKeypair = sourceKeypair;
1858
1907
  /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
1859
1908
  this.chain = "stellar";
1909
+ /** True when this connect just created a brand-new account (first sign-up). */
1910
+ this.isNewAccount = false;
1860
1911
  }
1861
1912
  get publicKey() {
1862
1913
  return this.devicePubkey;
@@ -1885,13 +1936,16 @@ var CavosStellar = class _CavosStellar {
1885
1936
  return build(existing.address, isSigner2 ? "ready" : "needs-device-approval");
1886
1937
  }
1887
1938
  const address = adapter.computeAddress(addressSeed, devicePubkey);
1888
- if (!await adapter.isDeployed(address)) {
1939
+ const wasDeployed = await adapter.isDeployed(address);
1940
+ if (!wasDeployed) {
1889
1941
  const func = adapter.buildDeploy(addressSeed, devicePubkey);
1890
1942
  await self.submitHostFunction(func, void 0);
1891
1943
  }
1892
1944
  await registry.register({ userId: identity.userId, address, initialSigner: devicePubkey });
1893
1945
  const isSigner = await adapter.isAuthorizedSigner(address, devicePubkey, readSource);
1894
- return build(address, isSigner ? "ready" : "needs-device-approval");
1946
+ const wallet = build(address, isSigner ? "ready" : "needs-device-approval");
1947
+ wallet.isNewAccount = !wasDeployed && isSigner;
1948
+ return wallet;
1895
1949
  }
1896
1950
  /** Authorize an additional device signer (device-signed via `__check_auth`). */
1897
1951
  async addSigner(pubkey) {
@@ -1919,6 +1973,18 @@ var CavosStellar = class _CavosStellar {
1919
1973
  const transactionHash = await this.submitHostFunction(func, this.address);
1920
1974
  return { transactionHash };
1921
1975
  }
1976
+ /** True if this account already has a passkey enrolled as an approver, so a
1977
+ * new device can be approved with the passkey instead of the email flow. */
1978
+ async hasPasskey() {
1979
+ const readSource = await this.resolveSource();
1980
+ return this.adapter.hasPasskeyApprover(this.address, readSource);
1981
+ }
1982
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
1983
+ * Used to poll for readiness after a passkey approval before it's indexed. */
1984
+ async isReady() {
1985
+ const readSource = await this.resolveSource();
1986
+ return this.adapter.isAuthorizedSigner(this.address, this.devicePubkey, readSource);
1987
+ }
1922
1988
  /**
1923
1989
  * From a fresh browser (status `needs-device-approval`), approve adding THIS
1924
1990
  * device using the user's synced passkey. Gasless via the relayer — the call
@@ -2226,6 +2292,9 @@ var Cavos = class _Cavos {
2226
2292
  this.chain = "starknet";
2227
2293
  /** Request id of the pending device-addition, when status is needs-device-approval. */
2228
2294
  this.pendingRequestId = null;
2295
+ /** True when this connect just created & deployed a brand-new account (first
2296
+ * sign-up), so the UI can offer a one-time "secure your account" step. */
2297
+ this.isNewAccount = false;
2229
2298
  }
2230
2299
  /**
2231
2300
  * Unified entry point. Pick a `chain` and an `network` environment; the kit
@@ -2321,7 +2390,7 @@ var Cavos = class _Cavos {
2321
2390
  if (existing) {
2322
2391
  const account2 = makeAccount(existing.address);
2323
2392
  const isSigner2 = await adapter.isAuthorizedSigner(existing.address, devicePubkey);
2324
- const cavos = new _Cavos(
2393
+ const cavos2 = new _Cavos(
2325
2394
  identity,
2326
2395
  existing.address,
2327
2396
  isSigner2 ? "ready" : "needs-device-approval",
@@ -2335,7 +2404,7 @@ var Cavos = class _Cavos {
2335
2404
  const fresh = dedup && Date.now() - dedup.requestedAt < DEVICE_REQUEST_DEDUP_MS;
2336
2405
  try {
2337
2406
  if (fresh) {
2338
- cavos.pendingRequestId = dedup.requestId;
2407
+ cavos2.pendingRequestId = dedup.requestId;
2339
2408
  } else {
2340
2409
  const { requestId } = await recovery.requestDeviceAddition({
2341
2410
  userId: identity.userId,
@@ -2343,14 +2412,14 @@ var Cavos = class _Cavos {
2343
2412
  newSigner: devicePubkey,
2344
2413
  ...identity.email ? { email: identity.email } : {}
2345
2414
  });
2346
- cavos.pendingRequestId = requestId;
2415
+ cavos2.pendingRequestId = requestId;
2347
2416
  lastDeviceRequest.set(identity.userId, { requestId, requestedAt: Date.now() });
2348
2417
  }
2349
2418
  } catch (e) {
2350
2419
  console.warn("[Cavos] requestDeviceAddition failed:", e);
2351
2420
  }
2352
2421
  }
2353
- return cavos;
2422
+ return cavos2;
2354
2423
  }
2355
2424
  const address = adapter.computeAddress({ addressSeed, initialSigner: devicePubkey });
2356
2425
  const account = makeAccount(address);
@@ -2381,7 +2450,7 @@ var Cavos = class _Cavos {
2381
2450
  console.warn("[Cavos] isAuthorizedSigner read failed:", e);
2382
2451
  isSigner = !alreadyDeployed;
2383
2452
  }
2384
- return new _Cavos(
2453
+ const cavos = new _Cavos(
2385
2454
  identity,
2386
2455
  address,
2387
2456
  isSigner ? "ready" : "needs-device-approval",
@@ -2390,6 +2459,8 @@ var Cavos = class _Cavos {
2390
2459
  devicePubkey,
2391
2460
  paymasterConfig
2392
2461
  );
2462
+ cavos.isNewAccount = !alreadyDeployed && isSigner;
2463
+ return cavos;
2393
2464
  }
2394
2465
  /** This device's public key (e.g. to request addition to an existing wallet). */
2395
2466
  get publicKey() {
@@ -2435,8 +2506,24 @@ var Cavos = class _Cavos {
2435
2506
  const { transactionHash } = await this.execute([
2436
2507
  this.adapter.buildAddApprover(this.address, pubkey)
2437
2508
  ]);
2509
+ try {
2510
+ await this.account.waitForTransaction(transactionHash);
2511
+ } catch (e) {
2512
+ console.warn("[Cavos] add_approver receipt wait failed:", e);
2513
+ }
2438
2514
  return { transactionHash };
2439
2515
  }
2516
+ /** True if this account already has a passkey enrolled as an approver, so a
2517
+ * new device can be approved with the passkey instead of the email flow. */
2518
+ async hasPasskey() {
2519
+ return this.adapter.hasPasskeyApprover(this.address);
2520
+ }
2521
+ /** Re-read (from chain) whether THIS device is now an authorized signer.
2522
+ * Cheap and side-effect free — used to poll for readiness after a passkey /
2523
+ * device approval submits, before the new signer is indexed. */
2524
+ async isReady() {
2525
+ return this.adapter.isAuthorizedSigner(this.address, this.devicePubkey);
2526
+ }
2440
2527
  /**
2441
2528
  * From a brand-new browser (status `needs-device-approval`), use the user's
2442
2529
  * synced passkey to authorize adding THIS device — no trip back to an already-
@@ -2588,17 +2675,15 @@ async function approveDeviceEverywhere(wallets, passkey) {
2588
2675
  const infos = await Promise.all(targets.map((w) => w.passkeyLeafForThisDevice()));
2589
2676
  const leaves = infos.map((i) => i.leaf);
2590
2677
  const assertion = await passkey.assert(batchChallenge(leaves));
2591
- const out = [];
2592
- for (let i = 0; i < targets.length; i++) {
2593
- const { transactionHash } = await targets[i].submitPasskeyApproval(
2594
- assertion,
2595
- leaves,
2596
- i,
2597
- infos[i].nonce
2598
- );
2599
- out.push({ chain: targets[i].chain, transactionHash });
2600
- }
2601
- return out;
2678
+ const settled = await Promise.allSettled(
2679
+ targets.map((w, i) => w.submitPasskeyApproval(assertion, leaves, i, infos[i].nonce))
2680
+ );
2681
+ return settled.map(
2682
+ (r, i) => r.status === "fulfilled" ? { chain: targets[i].chain, transactionHash: r.value.transactionHash } : {
2683
+ chain: targets[i].chain,
2684
+ error: r.reason instanceof Error ? r.reason.message : String(r.reason)
2685
+ }
2686
+ );
2602
2687
  }
2603
2688
  async function paymasterExecuteDirect(paymaster, userAddress, call) {
2604
2689
  const body = {
@@ -2771,7 +2856,91 @@ function bytesToChunks(bytes) {
2771
2856
  for (const b of bytes.subarray(0, 31)) w = w << 8n | BigInt(b);
2772
2857
  return w;
2773
2858
  }
2859
+ var PasskeySigner = class {
2860
+ constructor(opts = {}) {
2861
+ if (typeof window === "undefined" || !navigator.credentials) {
2862
+ throw new Error("kit/passkey: WebAuthn is only available in a browser");
2863
+ }
2864
+ this.rpId = opts.rpId ?? window.location.hostname;
2865
+ this.rpName = opts.rpName ?? this.rpId;
2866
+ if (isIpAddress(this.rpId)) {
2867
+ throw new Error(
2868
+ `kit/passkey: passkeys can't use an IP address as the domain ("${this.rpId}"). Use http://localhost, a real HTTPS domain, or a tunnel (cloudflared/ngrok) \u2014 or pass an explicit \`rpId\`. (The silent device key works over an IP; passkeys don't.)`
2869
+ );
2870
+ }
2871
+ }
2872
+ /** True if this platform advertises a usable passkey (platform authenticator). */
2873
+ static async isSupported() {
2874
+ if (typeof window === "undefined" || !window.PublicKeyCredential) return false;
2875
+ try {
2876
+ return await window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
2877
+ } catch {
2878
+ return false;
2879
+ }
2880
+ }
2881
+ /** Create a new synced passkey and return its P-256 public key. */
2882
+ async enroll(params) {
2883
+ const challenge = crypto.getRandomValues(new Uint8Array(32));
2884
+ const cred = await navigator.credentials.create({
2885
+ publicKey: {
2886
+ challenge: buf(challenge),
2887
+ rp: { id: this.rpId, name: this.rpName },
2888
+ user: {
2889
+ id: buf(userHandle(params.userId)),
2890
+ name: params.userName,
2891
+ displayName: params.displayName ?? params.userName
2892
+ },
2893
+ pubKeyCredParams: [{ type: "public-key", alg: -7 }],
2894
+ // ES256 (P-256)
2895
+ authenticatorSelection: {
2896
+ residentKey: "required",
2897
+ requireResidentKey: true,
2898
+ userVerification: "preferred"
2899
+ },
2900
+ attestation: "none"
2901
+ }
2902
+ });
2903
+ if (!cred) throw new Error("kit/passkey: enrollment cancelled");
2904
+ const response = cred.response;
2905
+ const spki = new Uint8Array(response.getPublicKey());
2906
+ return { publicKey: spkiToPublicKey(spki), credentialId: new Uint8Array(cred.rawId) };
2907
+ }
2908
+ /**
2909
+ * Produce a WebAuthn assertion over `challenge` (a 32-byte value the caller
2910
+ * derives from the signer being added + the on-chain nonce). Uses discoverable
2911
+ * credentials — no `allowCredentials` — so it works on a brand-new browser.
2912
+ */
2913
+ async assert(challenge) {
2914
+ const cred = await navigator.credentials.get({
2915
+ publicKey: {
2916
+ challenge: buf(challenge),
2917
+ rpId: this.rpId,
2918
+ allowCredentials: [],
2919
+ userVerification: "preferred"
2920
+ }
2921
+ });
2922
+ if (!cred) throw new Error("kit/passkey: assertion cancelled");
2923
+ const response = cred.response;
2924
+ const authenticatorData = new Uint8Array(response.authenticatorData);
2925
+ const clientDataJSON = new Uint8Array(response.clientDataJSON);
2926
+ const { r, s } = derToRs(new Uint8Array(response.signature));
2927
+ const challengeOffset = challengeOffsetOf(clientDataJSON, base64urlEncode(challenge));
2928
+ return { authenticatorData, clientDataJSON, r, s, challengeOffset };
2929
+ }
2930
+ };
2931
+ function isIpAddress(host) {
2932
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return true;
2933
+ if (host.includes(":")) return true;
2934
+ return false;
2935
+ }
2936
+ function userHandle(userId) {
2937
+ const bytes = new TextEncoder().encode(userId);
2938
+ return bytes.length <= 64 ? bytes : sha256(bytes);
2939
+ }
2940
+ function buf(bytes) {
2941
+ return bytes.slice();
2942
+ }
2774
2943
 
2775
- export { BackupSigner, Cavos, CavosAuth, CavosSolana, CavosStellar, DEVICE_ACCOUNT_CLASS_HASH, DEVICE_ACCOUNT_PROGRAM_ID, DEVICE_ACCOUNT_WASM_HASH, FACTORY_CONTRACT_ID, HttpRecoveryClient, HttpWalletRegistry, InMemoryWalletRegistry, NATIVE_SAC_ID, SECP256R1_PROGRAM_ID, SOLANA_NETWORKS, STARKNET_NETWORKS, STELLAR_NETWORKS, SolanaAdapter, SolanaRelayer, StarknetAdapter, StarknetDeviceSigner, StellarAdapter, StellarRelayer, UDC_ADDRESS, WebCryptoSigner, anchorDiscriminator, approveDeviceEverywhere, base64urlEncode, batchChallenge, bigIntTo32Bytes, buildSecp256r1Instruction, bytesToBigInt, bytesToHex, challengeOffsetOf, compressedPubkey, derToRs, deriveAddressSeed, deriveAddressSeedSolana, deriveAddressSeedStellar, deriveBackupKey, deviceSignatureScVal, encodeLowSSignature, encodeLowSSignature2, generateRecoveryCode, hexToBytes, lowS, recoverCandidatePublicKeys, recoverYParity, sec1Pubkey, serializeInstructions, signatureToFelts, spkiToPublicKey, u256ToFelts, webauthnDigest };
2776
- //# sourceMappingURL=chunk-BNGLH3Q3.mjs.map
2777
- //# sourceMappingURL=chunk-BNGLH3Q3.mjs.map
2944
+ export { BackupSigner, Cavos, CavosAuth, CavosSolana, CavosStellar, DEVICE_ACCOUNT_CLASS_HASH, DEVICE_ACCOUNT_PROGRAM_ID, DEVICE_ACCOUNT_WASM_HASH, FACTORY_CONTRACT_ID, HttpRecoveryClient, HttpWalletRegistry, InMemoryWalletRegistry, NATIVE_SAC_ID, PasskeySigner, SECP256R1_PROGRAM_ID, SOLANA_NETWORKS, STARKNET_NETWORKS, STELLAR_NETWORKS, SolanaAdapter, SolanaRelayer, StarknetAdapter, StarknetDeviceSigner, StellarAdapter, StellarRelayer, UDC_ADDRESS, WebCryptoSigner, anchorDiscriminator, approveDeviceEverywhere, base64urlEncode, batchChallenge, bigIntTo32Bytes, buildSecp256r1Instruction, bytesToBigInt, bytesToHex, compressedPubkey, deriveAddressSeed, deriveAddressSeedSolana, deriveAddressSeedStellar, deriveBackupKey, deviceSignatureScVal, encodeLowSSignature, encodeLowSSignature2, generateRecoveryCode, hexToBytes, lowS, recoverCandidatePublicKeys, recoverYParity, sec1Pubkey, serializeInstructions, signatureToFelts, u256ToFelts, webauthnDigest };
2945
+ //# sourceMappingURL=chunk-F2J25XSL.mjs.map
2946
+ //# sourceMappingURL=chunk-F2J25XSL.mjs.map