@cavos/kit 0.0.1 → 0.0.3

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.
@@ -0,0 +1,994 @@
1
+ import { Account } from 'starknet';
2
+ import { PublicKey, Connection, TransactionInstruction, Keypair } from '@solana/web3.js';
3
+ import { Keypair as Keypair$1 } from '@stellar/stellar-sdk';
4
+
5
+ /**
6
+ * Identity for a Cavos wallet. Login (email / social / OTP) only ever produces a
7
+ * stable `userId`; that's all the wallet needs to derive its address. Auth never
8
+ * touches signing — the device key does that, silently.
9
+ *
10
+ * Privy-style UX: the user "logs in" and the wallet is provisioned behind the
11
+ * scenes (device key + auto-deployed smart account). The app never handles keys.
12
+ */
13
+ interface Identity {
14
+ /** Stable, backend-managed user identifier. */
15
+ userId: string;
16
+ /** Optional metadata (email, provider) for display only. */
17
+ email?: string;
18
+ provider?: "google" | "apple" | "email" | "otp" | string;
19
+ }
20
+ /**
21
+ * Authenticates a user and returns their stable identity. Implementations:
22
+ * - `CavosAuth` (hosted, mirrors `@cavos/react`: Google/Apple/email/OTP via the
23
+ * Cavos backend) — the default, Privy-like experience.
24
+ * - any custom provider (the app already authenticated the user elsewhere).
25
+ */
26
+ interface AuthProvider {
27
+ authenticate(): Promise<Identity>;
28
+ }
29
+ /** Trivial provider when the app already has the user's stable id. */
30
+ declare class StaticIdentity implements AuthProvider {
31
+ private readonly identity;
32
+ constructor(identity: Identity);
33
+ authenticate(): Promise<Identity>;
34
+ }
35
+
36
+ /** secp256r1 (P-256) public key of a device signer. */
37
+ interface DevicePublicKey {
38
+ x: bigint;
39
+ y: bigint;
40
+ }
41
+ /** A raw secp256r1 signature over `sha256(tx_hash)`. */
42
+ interface DeviceSignature {
43
+ r: bigint;
44
+ s: bigint;
45
+ /** Recovery parity of the emitted (r, s); the contract normalizes high-s. */
46
+ yParity: boolean;
47
+ }
48
+ /**
49
+ * A silent, device-bound signer. The private key is a secp256r1 key generated
50
+ * and kept on the device (non-extractable WebCrypto key on web, Secure Enclave
51
+ * on native) — it never leaves the device and signs WITHOUT any user-visible
52
+ * prompt (no passkey, no biometrics). OAuth / email only derive identity; they
53
+ * are never involved in signing.
54
+ *
55
+ * `WebCryptoSigner` is the browser implementation. React Native and other
56
+ * platforms provide their own implementation of this interface.
57
+ */
58
+ interface DeviceSigner {
59
+ /** secp256r1 public key of this device signer. */
60
+ getPublicKey(): Promise<DevicePublicKey>;
61
+ /**
62
+ * Sign a transaction hash silently. `txHash` is the 32-byte big-endian tx
63
+ * hash; the signer signs `sha256(txHash)` and returns the raw (r, s, parity).
64
+ */
65
+ sign(txHash: Uint8Array): Promise<DeviceSignature>;
66
+ }
67
+
68
+ /**
69
+ * Off-chain map of `user_id -> wallet`. Because the account address is
70
+ * `f(identity, first_device_pubkey)` (unforgeable, secure), it is NOT derivable
71
+ * from the identity alone on a new device. The backend is the source of truth
72
+ * for "does this user already have a wallet?" — enabling multi-device:
73
+ *
74
+ * - First device, unknown user -> deploy a new wallet, then `register`.
75
+ * - Same user, new device -> `lookup` returns the existing wallet; the
76
+ * new device is added as a signer (recovery
77
+ * approval), NOT a new wallet.
78
+ *
79
+ * The backend implements this (it already manages the user<->address binding).
80
+ */
81
+ interface WalletRegistry {
82
+ /** The user's existing wallet, or null if they don't have one yet. */
83
+ lookup(userId: string): Promise<RegisteredWallet | null>;
84
+ /** Record a freshly deployed wallet for the user (first device). */
85
+ register(params: {
86
+ userId: string;
87
+ address: string;
88
+ initialSigner: DevicePublicKey;
89
+ }): Promise<void>;
90
+ /** Note an additional device signer for the user's wallet (after approval). */
91
+ addDevice?(params: {
92
+ userId: string;
93
+ address: string;
94
+ signer: DevicePublicKey;
95
+ }): Promise<void>;
96
+ }
97
+ interface RegisteredWallet {
98
+ address: string;
99
+ /** Public keys of the devices registered on this wallet (if tracked). */
100
+ devices?: DevicePublicKey[];
101
+ }
102
+ /** Simple in-memory registry for demos / tests. */
103
+ declare class InMemoryWalletRegistry implements WalletRegistry {
104
+ private wallets;
105
+ lookup(userId: string): Promise<RegisteredWallet | null>;
106
+ register(params: {
107
+ userId: string;
108
+ address: string;
109
+ initialSigner: DevicePublicKey;
110
+ }): Promise<void>;
111
+ addDevice(params: {
112
+ userId: string;
113
+ address: string;
114
+ signer: DevicePublicKey;
115
+ }): Promise<void>;
116
+ }
117
+
118
+ /** A parsed passkey assertion, chain-agnostic. */
119
+ interface PasskeyAssertion {
120
+ authenticatorData: Uint8Array;
121
+ clientDataJSON: Uint8Array;
122
+ /** ECDSA signature components (raw, as returned by the authenticator). */
123
+ r: bigint;
124
+ s: bigint;
125
+ /** Byte index where the base64url challenge starts inside clientDataJSON. */
126
+ challengeOffset: number;
127
+ }
128
+ /** base64url (no padding) of a byte array. */
129
+ declare function base64urlEncode(bytes: Uint8Array): string;
130
+ /**
131
+ * Batch challenge over ordered per-chain leaves: `sha256(leaf_0 ‖ … ‖ leaf_n)`.
132
+ * A single passkey assertion over this challenge authorizes `add_signer` on every
133
+ * chain in the batch (each chain verifies only its own leaf sits at its index).
134
+ * For a single chain the batch is one leaf, so the challenge is `sha256(leaf)`.
135
+ */
136
+ declare function batchChallenge(leaves: Uint8Array[]): Uint8Array;
137
+ /** The WebAuthn signed digest: `sha256(authenticatorData || sha256(clientDataJSON))`. */
138
+ declare function webauthnDigest(authenticatorData: Uint8Array, clientDataJSON: Uint8Array): Uint8Array;
139
+ /** Both candidate public keys recoverable from `(r, s)` over `digest`, tagged
140
+ * with their y-parity. On a fresh browser the assertion does not carry the
141
+ * pubkey, so the caller identifies the real one via the on-chain `is_approver`
142
+ * view. */
143
+ declare function recoverCandidatePublicKeys(r: bigint, s: bigint, digest: Uint8Array): {
144
+ publicKey: DevicePublicKey;
145
+ yParity: boolean;
146
+ }[];
147
+ /** Normalize `s` to the low half of the curve order (required by the Stellar
148
+ * and Solana verifiers, which do not normalize on-chain). */
149
+ declare function lowS(s: bigint): bigint;
150
+
151
+ /** An account meta candidate for a CPI instruction inside `execute`. Mirrors the
152
+ * on-chain `AccountMetaCandidate` (Borsh). */
153
+ interface InstructionAccount {
154
+ pubkey: string;
155
+ isSigner: boolean;
156
+ isWritable: boolean;
157
+ }
158
+ /** A CPI instruction the device key authorizes via `execute`. Mirrors the
159
+ * on-chain `InstructionData` (Borsh). Serialized canonically and hashed before
160
+ * signing, so a signature binds exactly this instruction set. */
161
+ interface InstructionData {
162
+ programId: string;
163
+ accounts: InstructionAccount[];
164
+ data: Uint8Array;
165
+ }
166
+ interface SolanaAdapterOptions {
167
+ /** Cavos device-account program id (defaults to the deployed one). */
168
+ programId?: string;
169
+ /** RPC connection for reads (`isAuthorizedSigner`) and nonce fetch. */
170
+ connection?: Connection;
171
+ /** Device signer used to authorize guarded actions. */
172
+ signer?: DeviceSigner;
173
+ }
174
+ /**
175
+ * Solana adapter for the Cavos device-signer account. Unlike Starknet (where the
176
+ * account contract verifies the P-256 signature in `__validate__`), Solana
177
+ * verifies it natively via the secp256r1 precompile, so every guarded action is
178
+ * a two-instruction bundle: `[secp256r1 precompile ix, program ix]`. This adapter
179
+ * derives the account PDA, builds those bundles, and reuses the same
180
+ * `DeviceSigner` (P-256 / WebCrypto) as every other chain.
181
+ */
182
+ declare class SolanaAdapter {
183
+ private readonly opts;
184
+ readonly chain: "solana";
185
+ readonly programId: PublicKey;
186
+ constructor(opts?: SolanaAdapterOptions);
187
+ /** Deterministic account address: PDA of [seed, address_seed, initial_signer_x]. */
188
+ computeAddress(addressSeed: Uint8Array, initialSigner: DevicePublicKey): string;
189
+ private pda;
190
+ /** `initialize` instruction creating the account with its first device signer. */
191
+ buildInitialize(addressSeed: Uint8Array, payer: string, initialSigner: DevicePublicKey): TransactionInstruction;
192
+ /** `[precompile, add_signer]` bundle, authorized by an existing device signer. */
193
+ buildAddSigner(account: string, newSigner: DevicePublicKey): Promise<TransactionInstruction[]>;
194
+ /** `[precompile, remove_signer]` bundle, authorized by an existing device signer. */
195
+ buildRemoveSigner(account: string, signer: DevicePublicKey): Promise<TransactionInstruction[]>;
196
+ /** `[precompile, add_approver]` bundle enrolling a passkey approver (device-signed). */
197
+ buildAddApprover(account: string, passkey: DevicePublicKey): Promise<TransactionInstruction[]>;
198
+ /** `[precompile, remove_approver]` bundle (device-signed). */
199
+ buildRemoveApprover(account: string, passkey: DevicePublicKey): Promise<TransactionInstruction[]>;
200
+ /** This chain's leaf for approving `add_signer(newSigner)` at `nonce`:
201
+ * `sha256(compressed(new_signer) || passkey_nonce_le8)`. The batch challenge the
202
+ * passkey signs is `sha256(concat(leaves))` across chains. */
203
+ passkeyLeaf(newSigner: DevicePublicKey, nonce: bigint): Uint8Array;
204
+ /**
205
+ * `[precompile(passkey), add_signer_via_passkey]` bundle. The precompile ix
206
+ * verifies the PASSKEY's WebAuthn assertion over `authData || sha256(clientDataJSON)`;
207
+ * the program ix binds the challenge to `newSigner` + the passkey nonce and adds
208
+ * the signer. No device signature — a gasless relayer can submit it.
209
+ */
210
+ buildAddSignerViaPasskey(account: string, newSigner: DevicePublicKey, passkey: DevicePublicKey, leaves: Uint8Array[], leafIndex: number, assertion: PasskeyAssertion): TransactionInstruction[];
211
+ /** Read whether `passkey` is a registered approver. */
212
+ isApprover(account: string, passkey: DevicePublicKey): Promise<boolean>;
213
+ /** Read the current passkey-approval nonce. */
214
+ passkeyNonce(account: string): Promise<bigint>;
215
+ /** `[precompile, execute_transfer]` bundle moving lamports out of the account. */
216
+ buildExecuteTransfer(account: string, destination: string, amount: bigint): Promise<TransactionInstruction[]>;
217
+ /**
218
+ * `[precompile, execute]` bundle running arbitrary CPI instructions with the
219
+ * account PDA as signer. The device key signs over
220
+ * `DOMAIN_EXECUTE || account || sha256(canonical(instructions)) || nonce`, so
221
+ * the signature commits to the EXACT instruction set the program will invoke —
222
+ * no account/data substitution is possible after signing.
223
+ *
224
+ * The instructions' accounts are passed to the program via `remaining_accounts`
225
+ * (flattened, in order); the program enforces an exact, ordered mapping.
226
+ */
227
+ buildExecute(account: string, instructions: InstructionData[]): Promise<TransactionInstruction[]>;
228
+ /** Read whether `signer` is currently an authorized signer of `account`. */
229
+ isAuthorizedSigner(account: string, signer: DevicePublicKey): Promise<boolean>;
230
+ private guardedKeys;
231
+ /** Sign `message` with the device key and build the matching precompile ix. */
232
+ private signToPrecompile;
233
+ private fetchNonce;
234
+ private fetchSigners;
235
+ private fetchApprovers;
236
+ private requireConnection;
237
+ }
238
+ /** Compressed SEC1 P-256 pubkey (33 bytes) from {x, y}. */
239
+ declare function compressedPubkey(pk: DevicePublicKey): Uint8Array;
240
+ /** Encode (r, s) as raw 64-byte r‖s, normalized to low-S (precompile requires it). */
241
+ declare function encodeLowSSignature(r: bigint, s: bigint): Uint8Array;
242
+ /** Build the native secp256r1 precompile instruction (single self-contained sig). */
243
+ declare function buildSecp256r1Instruction(compressed: Uint8Array, signature: Uint8Array, message: Uint8Array): TransactionInstruction;
244
+ /** Anchor instruction discriminator = sha256("global:<name>")[..8]. */
245
+ declare function anchorDiscriminator(name: string): Buffer;
246
+ /** Serialize the full instruction set — the bytes `hash_instructions` hashes. */
247
+ declare function serializeInstructions(instructions: InstructionData[]): Buffer;
248
+
249
+ /** Cavos device-account program + Solana primitives. */
250
+ /** Deployed `cavos-device-account` program id (see account-contracts/solana). */
251
+ declare const DEVICE_ACCOUNT_PROGRAM_ID = "FHnoYNfYAmFrwt18gcBGG7G1S5q3RAbCBvrV2D29izNJ";
252
+ /** Native secp256r1 signature-verify precompile (SIMD-0075). */
253
+ declare const SECP256R1_PROGRAM_ID = "Secp256r1SigVerify1111111111111111111111111";
254
+ declare const SOLANA_NETWORKS: {
255
+ readonly "solana-devnet": "https://api.devnet.solana.com";
256
+ readonly "solana-mainnet": "https://api.mainnet-beta.solana.com";
257
+ readonly "solana-localnet": "http://127.0.0.1:8899";
258
+ };
259
+ type SolanaNetwork = keyof typeof SOLANA_NETWORKS;
260
+
261
+ interface SolanaRelayerOptions {
262
+ /** Base URL of the Cavos backend exposing /api/solana/relay. */
263
+ baseUrl: string;
264
+ /** Cavos App ID (authorizes the sponsored request). */
265
+ appId: string;
266
+ network: SolanaNetwork;
267
+ /** Connection used only to fetch a recent blockhash before serializing. */
268
+ connection: Connection;
269
+ }
270
+ /**
271
+ * Client for the Cavos Solana sponsoring relayer. Lets the SDK submit
272
+ * device-account transactions WITHOUT the integrator holding a fee-payer
273
+ * keypair: the relayer co-signs as fee payer and pays the fee/rent. The relayer
274
+ * only pays — the device signature inside the instructions (verified by the
275
+ * secp256r1 precompile) is what authorizes the action, and it does not bind the
276
+ * fee payer, so sponsorship needs no re-signing.
277
+ */
278
+ declare class SolanaRelayer {
279
+ private readonly opts;
280
+ private feePayer?;
281
+ constructor(opts: SolanaRelayerOptions);
282
+ /** The relayer's fee-payer pubkey (fetched + cached from the backend). */
283
+ getFeePayer(): Promise<PublicKey>;
284
+ /**
285
+ * Build a tx with the relayer as fee payer, serialize it unsigned, and POST it
286
+ * to the relayer to co-sign + submit. Returns the confirmed signature.
287
+ */
288
+ send(instructions: TransactionInstruction[]): Promise<string>;
289
+ }
290
+
291
+ interface PasskeySignerOptions {
292
+ /** Relying-Party id (usually the eTLD+1). Defaults to `window.location.hostname`. */
293
+ rpId?: string;
294
+ /** Human-readable RP name shown in the OS passkey UI. */
295
+ rpName?: string;
296
+ }
297
+ interface PasskeyEnrollParams {
298
+ /** Stable user handle for the credential (e.g. the account address or userId). */
299
+ userId: string;
300
+ /** Account name shown in the OS passkey UI (e.g. an email). */
301
+ userName: string;
302
+ displayName?: string;
303
+ }
304
+ interface EnrolledPasskey {
305
+ publicKey: DevicePublicKey;
306
+ credentialId: Uint8Array;
307
+ }
308
+ /**
309
+ * Browser passkey signer. Creates a discoverable (resident) secp256r1 credential
310
+ * that syncs across the user's devices (iCloud Keychain / Google Password
311
+ * Manager), and produces WebAuthn assertions used to authorize `add_signer`.
312
+ *
313
+ * This is a step-up primitive: the app decides WHEN to enroll (e.g. after
314
+ * onboarding, as a "turn on device approvals" / 2FA moment). Enrollment
315
+ * registers the passkey on-chain as an approver; later, from any browser, an
316
+ * assertion approves adding that browser's new device key.
317
+ */
318
+ declare class PasskeySigner {
319
+ private readonly rpId;
320
+ private readonly rpName;
321
+ constructor(opts?: PasskeySignerOptions);
322
+ /** True if this platform advertises a usable passkey (platform authenticator). */
323
+ static isSupported(): Promise<boolean>;
324
+ /** Create a new synced passkey and return its P-256 public key. */
325
+ enroll(params: PasskeyEnrollParams): Promise<EnrolledPasskey>;
326
+ /**
327
+ * Produce a WebAuthn assertion over `challenge` (a 32-byte value the caller
328
+ * derives from the signer being added + the on-chain nonce). Uses discoverable
329
+ * credentials — no `allowCredentials` — so it works on a brand-new browser.
330
+ */
331
+ assert(challenge: Uint8Array): Promise<PasskeyAssertion>;
332
+ }
333
+
334
+ interface ConnectSolanaOptions {
335
+ network: SolanaNetwork;
336
+ /** Authenticated user (pass `identity` directly, or an `auth` provider). */
337
+ auth?: AuthProvider;
338
+ identity?: Identity;
339
+ appSalt: string;
340
+ appId?: string;
341
+ backendUrl?: string;
342
+ registry?: WalletRegistry;
343
+ /** RPC override (else the network default). */
344
+ rpcUrl?: string;
345
+ /** Cavos device-account program id override. */
346
+ programId?: string;
347
+ /** Override the device signer factory (native / tests); default WebCrypto. */
348
+ createSigner?: (keyId: string) => Promise<DeviceSigner>;
349
+ /**
350
+ * Gasless sponsorship via the Cavos relayer. When set (or when `appId` +
351
+ * `backendUrl` are given), transactions are co-signed + paid by the Cavos
352
+ * relayer, so the integrator needs NO fee-payer keypair — the user's silent
353
+ * device key (which holds no SOL) gets a seedless, gasless experience.
354
+ */
355
+ relayer?: SolanaRelayer;
356
+ /**
357
+ * Self-funded fallback: a fee-payer keypair the integrator funds. Used only
358
+ * when no `relayer` is configured (tests / advanced). Sponsored relaying is
359
+ * the default path when `appId` is provided.
360
+ */
361
+ feePayer?: Keypair;
362
+ }
363
+ type ConnectStatus$2 = "ready" | "needs-device-approval";
364
+ /**
365
+ * Options for recovering a Solana account after losing every device signer.
366
+ * Mirrors `RecoveryOptions` (Starknet), adapted to the Solana path: the backup
367
+ * key signs the `add_signer` bundle via the secp256r1 precompile and the Cavos
368
+ * relayer sponsors it (no fee-payer keypair needed).
369
+ */
370
+ interface RecoverSolanaOptions {
371
+ /** The recovery code the user stored when they ran setupRecovery. */
372
+ code: string;
373
+ /** Authenticated identity (same user who owns the account). */
374
+ identity: Identity;
375
+ /** Solana network the account lives on. */
376
+ network: SolanaNetwork;
377
+ appSalt: string;
378
+ appId?: string;
379
+ backendUrl?: string;
380
+ registry?: WalletRegistry;
381
+ /** RPC override (else the network default). */
382
+ rpcUrl?: string;
383
+ /** Cavos device-account program id override. */
384
+ programId?: string;
385
+ /** Override the new device's signer (native / tests); default WebCrypto. */
386
+ createSigner?: (keyId: string) => Promise<DeviceSigner>;
387
+ /** Gasless sponsorship via the Cavos relayer (defaults to hosted when appId set). */
388
+ relayer?: SolanaRelayer;
389
+ /** Self-funded fallback when no relayer is configured (tests / advanced). */
390
+ feePayer?: Keypair;
391
+ }
392
+ /**
393
+ * High-level Solana entry — the Solana analogue of `Cavos.connect`. One call
394
+ * derives the deterministic device-bound account, deploys it (PDA `initialize`)
395
+ * if needed, registers it for cross-device recognition, and returns a ready
396
+ * handle whose silent P-256 device key authorizes every action through the
397
+ * native secp256r1 precompile.
398
+ *
399
+ * const cavos = await CavosSolana.connect({ network: "solana-devnet", identity, appSalt, feePayer });
400
+ * if (cavos.status === "ready") await cavos.execute(amount, dest);
401
+ *
402
+ * Gasless by default: when an `appId` is provided the Cavos relayer co-signs +
403
+ * pays (no fee-payer keypair needed). `feePayer` is the self-funded fallback.
404
+ */
405
+ declare class CavosSolana {
406
+ readonly identity: Identity;
407
+ readonly address: string;
408
+ readonly status: ConnectStatus$2;
409
+ readonly connection: Connection;
410
+ private readonly adapter;
411
+ private readonly devicePubkey;
412
+ private readonly relayer?;
413
+ private readonly feePayer?;
414
+ /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
415
+ readonly chain: "solana";
416
+ private constructor();
417
+ get publicKey(): DevicePublicKey;
418
+ static connect(opts: ConnectSolanaOptions): Promise<CavosSolana>;
419
+ /** Authorize an additional device signer (device-signed via precompile). */
420
+ addSigner(pubkey: DevicePublicKey): Promise<string>;
421
+ /**
422
+ * Enroll a passkey as an approver (2FA-style step-up). Device-signed + gasless;
423
+ * requires a ready device. Idempotent. Returns the passkey pubkey + tx hash.
424
+ */
425
+ enrollPasskey(passkey: PasskeySigner, params: PasskeyEnrollParams): Promise<{
426
+ publicKey: DevicePublicKey;
427
+ transactionHash?: string;
428
+ }>;
429
+ /** Register an already-enrolled passkey pubkey as an approver (gasless).
430
+ * Idempotent. Lets one passkey be registered across chains without re-prompting. */
431
+ addApprover(pubkey: DevicePublicKey): Promise<{
432
+ transactionHash?: string;
433
+ }>;
434
+ /**
435
+ * From a fresh browser (status `needs-device-approval`), approve adding THIS
436
+ * device with the user's synced passkey. Gasless via the relayer — the bundle
437
+ * carries the passkey's WebAuthn assertion, so no device signature is needed.
438
+ */
439
+ approveThisDeviceWithPasskey(passkey: PasskeySigner): Promise<string>;
440
+ /** This device's leaf + passkey nonce for a (possibly multi-chain) batch. */
441
+ passkeyLeafForThisDevice(): Promise<{
442
+ leaf: Uint8Array;
443
+ nonce: bigint;
444
+ }>;
445
+ /** Submit `add_signer_via_passkey` given a shared assertion + batch position.
446
+ * Used by `approveThisDeviceWithPasskey` and `approveDeviceEverywhere`. */
447
+ submitPasskeyApproval(assertion: PasskeyAssertion, leaves: Uint8Array[], leafIndex: number, _nonce: bigint): Promise<{
448
+ transactionHash: string;
449
+ }>;
450
+ /** Move `amount` lamports out of the account to `destination` (device-signed). */
451
+ execute(amount: bigint, destination: string): Promise<string>;
452
+ /**
453
+ * Run arbitrary CPI `instructions` with the account PDA as signer (device-
454
+ * signed). The signature commits to sha256 of the canonical Borsh
455
+ * serialization of the instructions, so it binds exactly the operations the
456
+ * program will invoke. Unlocks SPL transfers, swaps, staking, etc.
457
+ *
458
+ * What the relayer will sponsor is constrained by the app's Solana program
459
+ * allowlist (configured in the dashboard) — programs outside the allowlist are
460
+ * rejected before co-signing.
461
+ */
462
+ executeInstructions(instructions: InstructionData[]): Promise<string>;
463
+ /**
464
+ * Register the backup signer derived from `code` as an authorized signer of this
465
+ * account (device-signed via precompile). Idempotent: returns without a tx if
466
+ * the backup signer is already registered. The code never leaves the device —
467
+ * only the derived public key travels on-chain.
468
+ *
469
+ * Self-custodial: anyone who can re-derive the backup key from the code (i.e.
470
+ * the rightful owner) can later recover the account with `CavosSolana.recover`.
471
+ * Run this once, on a registered device, and have the user store the code.
472
+ */
473
+ setupRecovery(code: string): Promise<string | undefined>;
474
+ /**
475
+ * Recover an account after losing every device signer. Derives the backup key
476
+ * from `code`, uses it (not the new device key) to sign an `add_signer` for the
477
+ * new device, and returns a ready CavosSolana bound to the new device. The
478
+ * account address is unchanged.
479
+ *
480
+ * Self-custodial: only someone holding the code (i.e. the rightful owner) can
481
+ * re-derive the backup key. The backend never sees the code.
482
+ *
483
+ * This mirrors `Cavos.recover` (Starknet): the backup key is just another
484
+ * authorized signer, so recovery is an `add_signer(newDevice)` bundle signed by
485
+ * the backup key. The on-chain program needs no recovery-specific entrypoint.
486
+ */
487
+ static recover(opts: RecoverSolanaOptions): Promise<CavosSolana>;
488
+ private send;
489
+ }
490
+
491
+ /** Cavos device-account primitives on Stellar / Soroban. */
492
+ /**
493
+ * Deployed `cavos-account-factory` contract id per network (see
494
+ * account-contracts/stellar/deployments). The factory is the fixed deployer that
495
+ * makes account addresses a deterministic function of (identity, device pubkey).
496
+ */
497
+ declare const FACTORY_CONTRACT_ID: {
498
+ readonly "stellar-testnet": "CBCJIODXIEBOXXD66KCUCF7ZDYJARKI4ZIVQOVWPULOBH5XGNCDP6W3I";
499
+ readonly "stellar-mainnet": "";
500
+ };
501
+ /** Uploaded Wasm hash of `cavos-device-account` (informational / verification). */
502
+ declare const DEVICE_ACCOUNT_WASM_HASH: {
503
+ readonly "stellar-testnet": "2671b085578e59a385ef5a5664e42f0450322fe3249539f588e1263ed5a31dce";
504
+ readonly "stellar-mainnet": "";
505
+ };
506
+ declare const STELLAR_NETWORKS: {
507
+ readonly "stellar-testnet": {
508
+ readonly rpcUrl: "https://soroban-testnet.stellar.org";
509
+ readonly passphrase: "Test SDF Network ; September 2015";
510
+ };
511
+ readonly "stellar-mainnet": {
512
+ readonly rpcUrl: "https://soroban-rpc.mainnet.stellar.gateway.fm";
513
+ readonly passphrase: "Public Global Stellar Network ; September 2015";
514
+ };
515
+ };
516
+ type StellarNetwork = keyof typeof STELLAR_NETWORKS;
517
+ /** Native XLM Stellar Asset Contract (SAC) id per network — the token the demo
518
+ * moves. Any SEP-41 token contract works; this is a convenience default. */
519
+ declare const NATIVE_SAC_ID: {
520
+ readonly "stellar-testnet": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
521
+ readonly "stellar-mainnet": "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA";
522
+ };
523
+
524
+ interface StellarRelayerOptions {
525
+ /** Base URL of the Cavos backend exposing /api/stellar/relay. */
526
+ baseUrl: string;
527
+ /** Cavos App ID (authorizes the sponsored request). */
528
+ appId: string;
529
+ network: StellarNetwork;
530
+ }
531
+ /**
532
+ * Client for the Cavos Stellar sponsoring relayer. On Stellar the account is a
533
+ * *contract*, which cannot be a transaction source — so the relayer's own
534
+ * G-account is the transaction source AND fee payer. The user's silent device
535
+ * key never pays: it only signs the Soroban *authorization entry* (verified by
536
+ * the account's `__check_auth`), which is independent of who submits or pays.
537
+ * That gives a seedless, gasless experience with no fee-payer keypair on the
538
+ * integrator side — the same "relayer pays, device authorizes" split as Solana.
539
+ *
540
+ * The SDK builds the fully-assembled transaction (source = relayer, Soroban auth
541
+ * entries already device-signed) and hands its unsigned XDR to the relayer, which
542
+ * validates it against its allowlist, signs the envelope and submits.
543
+ */
544
+ declare class StellarRelayer {
545
+ private readonly opts;
546
+ private source?;
547
+ constructor(opts: StellarRelayerOptions);
548
+ /** The relayer's source/fee-payer G-account (fetched + cached from the backend). */
549
+ getSource(): Promise<string>;
550
+ /**
551
+ * POST the assembled, device-authorized transaction XDR to the relayer to sign
552
+ * the envelope + submit. Returns the confirmed transaction hash.
553
+ */
554
+ submit(transactionXdr: string): Promise<string>;
555
+ }
556
+
557
+ interface ConnectStellarOptions {
558
+ network: StellarNetwork;
559
+ /** Authenticated user (pass `identity` directly, or an `auth` provider). */
560
+ auth?: AuthProvider;
561
+ identity?: Identity;
562
+ appSalt: string;
563
+ appId?: string;
564
+ backendUrl?: string;
565
+ registry?: WalletRegistry;
566
+ /** RPC override (else the network default). */
567
+ rpcUrl?: string;
568
+ /** Factory contract id override (else the per-network default). */
569
+ factoryId?: string;
570
+ /** Override the device signer factory (native / tests); default WebCrypto. */
571
+ createSigner?: (keyId: string) => Promise<DeviceSigner>;
572
+ /**
573
+ * Gasless sponsorship via the Cavos relayer. When set (or when `appId` +
574
+ * `backendUrl` are given) the relayer is the transaction source + fee payer, so
575
+ * the integrator needs NO Stellar keypair — the silent device key (which holds
576
+ * no XLM) gets a seedless, gasless experience.
577
+ */
578
+ relayer?: StellarRelayer;
579
+ /**
580
+ * Self-funded fallback: a Stellar `Keypair` that is the transaction source +
581
+ * fee payer. Used only when no `relayer` is configured (tests / advanced).
582
+ */
583
+ sourceKeypair?: Keypair$1;
584
+ }
585
+ interface RecoverStellarOptions extends Omit<ConnectStellarOptions, "auth"> {
586
+ /** The recovery code the user stored when they ran setupRecovery. */
587
+ code: string;
588
+ /** Authenticated identity (same user who owns the account). */
589
+ identity: Identity;
590
+ }
591
+ type ConnectStatus$1 = "ready" | "needs-device-approval";
592
+ /**
593
+ * High-level Stellar entry — the Soroban analogue of `Cavos.connect` /
594
+ * `CavosSolana.connect`. One call derives the deterministic device-bound account,
595
+ * deploys it via the factory if needed, registers it for cross-device
596
+ * recognition, and returns a ready handle whose silent P-256 device key
597
+ * authorizes every action through the account's `__check_auth`.
598
+ *
599
+ * const cavos = await CavosStellar.connect({ network: "stellar-testnet", identity, appSalt, relayer });
600
+ * if (cavos.status === "ready") await cavos.execute(10_000_000n, dest); // 1 XLM
601
+ *
602
+ * Gasless by default: with an `appId` the Cavos relayer is the tx source + fee
603
+ * payer. `sourceKeypair` is the self-funded fallback.
604
+ */
605
+ declare class CavosStellar {
606
+ readonly identity: Identity;
607
+ readonly address: string;
608
+ readonly status: ConnectStatus$1;
609
+ readonly network: StellarNetwork;
610
+ private readonly adapter;
611
+ private readonly devicePubkey;
612
+ private readonly relayer?;
613
+ private readonly sourceKeypair?;
614
+ /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
615
+ readonly chain: "stellar";
616
+ private constructor();
617
+ get publicKey(): DevicePublicKey;
618
+ static connect(opts: ConnectStellarOptions): Promise<CavosStellar>;
619
+ /** Authorize an additional device signer (device-signed via `__check_auth`). */
620
+ addSigner(pubkey: DevicePublicKey): Promise<string>;
621
+ /**
622
+ * Enroll a passkey as an approver (2FA-style step-up). Device-signed + gasless;
623
+ * requires a ready device. Idempotent. Returns the passkey pubkey + tx hash.
624
+ */
625
+ enrollPasskey(passkey: PasskeySigner, params: PasskeyEnrollParams): Promise<{
626
+ publicKey: DevicePublicKey;
627
+ transactionHash?: string;
628
+ }>;
629
+ /** Register an already-enrolled passkey pubkey as an approver (gasless).
630
+ * Idempotent. Lets one passkey be registered across chains without re-prompting. */
631
+ addApprover(pubkey: DevicePublicKey): Promise<{
632
+ transactionHash?: string;
633
+ }>;
634
+ /**
635
+ * From a fresh browser (status `needs-device-approval`), approve adding THIS
636
+ * device using the user's synced passkey. Gasless via the relayer — the call
637
+ * carries the WebAuthn assertion, so no device signature is needed. Returns the
638
+ * tx hash. No trip back to an already-authorized device.
639
+ */
640
+ approveThisDeviceWithPasskey(passkey: PasskeySigner): Promise<string>;
641
+ /** This device's leaf + passkey nonce for a (possibly multi-chain) batch. */
642
+ passkeyLeafForThisDevice(): Promise<{
643
+ leaf: Uint8Array;
644
+ nonce: bigint;
645
+ }>;
646
+ /** Submit `add_signer_via_passkey` given a shared assertion + batch position.
647
+ * No device auth entry — authorized purely by the passkey assertion. */
648
+ submitPasskeyApproval(assertion: PasskeyAssertion, leaves: Uint8Array[], leafIndex: number, nonce: bigint): Promise<{
649
+ transactionHash: string;
650
+ }>;
651
+ /** Move `amount` stroops of native XLM to `destination` (device-signed). */
652
+ execute(amount: bigint, destination: string): Promise<string>;
653
+ /** Read this account's balance of `tokenId` (defaults to native XLM), in stroops. */
654
+ balance(tokenId?: string): Promise<bigint>;
655
+ /** Transfer `amount` of any SEP-41 token out of the account (device-signed). */
656
+ executeTransfer(tokenId: string, amount: bigint, destination: string): Promise<string>;
657
+ /**
658
+ * Register the backup signer derived from `code` as an authorized signer of
659
+ * this account (device-signed). Idempotent. The code never leaves the device —
660
+ * only the derived public key travels on-chain. Mirrors the other chains.
661
+ */
662
+ setupRecovery(code: string): Promise<string | undefined>;
663
+ /**
664
+ * Recover an account after losing every device signer: derive the backup key
665
+ * from `code`, use it (not the new device) to authorize `add_signer(newDevice)`,
666
+ * and return a ready handle bound to the new device. The address is unchanged.
667
+ */
668
+ static recover(opts: RecoverStellarOptions): Promise<CavosStellar>;
669
+ /** The transaction source/fee-payer G-address (relayer or self-funded). */
670
+ private resolveSource;
671
+ /**
672
+ * Build → simulate → device-sign auth → assemble → submit an invoke-contract
673
+ * host function. `authAccount` is the account whose `__check_auth` must sign the
674
+ * operation's Soroban auth entry (undefined for a plain factory deploy).
675
+ */
676
+ private submitHostFunction;
677
+ /** Submit a signed tx via RPC and poll to confirmation. Returns the hash. */
678
+ private sendAndConfirm;
679
+ }
680
+
681
+ /** A chain-native contract call (Starknet `Call`-shaped; generic for portability). */
682
+ interface ChainCall {
683
+ contractAddress: string;
684
+ entrypoint: string;
685
+ calldata: string[];
686
+ }
687
+ interface ComputeAddressParams {
688
+ addressSeed: bigint;
689
+ /** First device signer — part of the address, making it unforgeable. */
690
+ initialSigner: DevicePublicKey;
691
+ /** Defaults to `addressSeed` when omitted. */
692
+ salt?: bigint;
693
+ }
694
+ /**
695
+ * Per-chain implementation surface. Phase 1 ships only Starknet, but the kit is
696
+ * designed so Stellar and Solana adapters drop in behind the same interface.
697
+ */
698
+ interface ChainAdapter {
699
+ readonly chain: "starknet" | "stellar" | "solana";
700
+ /** Deterministic address from identity seed + the first device signer. */
701
+ computeAddress(params: ComputeAddressParams): string;
702
+ /** Call(s) to deploy the account with its first device signer (UDC). */
703
+ buildDeploy(params: ComputeAddressParams): ChainCall[];
704
+ buildAddSigner(accountAddress: string, signer: DevicePublicKey): ChainCall;
705
+ buildRemoveSigner(accountAddress: string, signer: DevicePublicKey): ChainCall;
706
+ /** Read whether a pubkey is a currently-authorized signer of the account. */
707
+ isAuthorizedSigner(accountAddress: string, signer: DevicePublicKey): Promise<boolean>;
708
+ /**
709
+ * Compute the signature payload for an outgoing transaction: given the chain's
710
+ * tx hash, obtain a device assertion and serialize it to the chain's expected
711
+ * signature encoding.
712
+ */
713
+ buildSignature(txHash: bigint): Promise<string[]>;
714
+ }
715
+
716
+ /**
717
+ * Multi-device / recovery flow (roadmap §1.2). A new device requests addition;
718
+ * the backend emails an approval prompt styled as a login request; the user
719
+ * approves on an ALREADY-registered device, which signs `add_signer` for the
720
+ * new pubkey. There is no legacy JWT path — recovery is purely device-signer
721
+ * based.
722
+ *
723
+ * The backend lives outside this repo; this is the client contract the kit
724
+ * speaks to. Provide an implementation (HTTP, etc.) when wiring an app.
725
+ */
726
+ interface RecoveryClient {
727
+ /**
728
+ * Step 1 (new device): request that this pubkey be added to the user's
729
+ * account. Triggers the approval email. Returns a request id to poll.
730
+ */
731
+ requestDeviceAddition(params: {
732
+ userId: string;
733
+ accountAddress: string;
734
+ newSigner: DevicePublicKey;
735
+ /** Owner email to send the approval link to (the SDK has it from login). */
736
+ email?: string;
737
+ /** Optional device label (browser/UA) shown in the approval email. */
738
+ deviceLabel?: string;
739
+ }): Promise<{
740
+ requestId: string;
741
+ }>;
742
+ /**
743
+ * Step 3-4 (existing device): fetch a pending addition request so the
744
+ * registered device can approve it by signing `add_signer`.
745
+ */
746
+ getPendingRequest(requestId: string): Promise<PendingDeviceRequest | null>;
747
+ /** Mark a request approved after the `add_signer` tx is submitted. */
748
+ confirmDeviceAddition(params: {
749
+ requestId: string;
750
+ txHash: string;
751
+ }): Promise<void>;
752
+ }
753
+ interface PendingDeviceRequest {
754
+ requestId: string;
755
+ appId?: string;
756
+ userId: string;
757
+ accountAddress: string;
758
+ newSigner: DevicePublicKey;
759
+ createdAt: string;
760
+ status: "pending" | "approved" | "expired";
761
+ }
762
+
763
+ /** The chains the unified `Cavos.connect` can target. */
764
+ type Chain = "starknet" | "solana" | "stellar";
765
+ /**
766
+ * Environment selector. `Cavos.connect` resolves it to the chain's concrete
767
+ * network: starknet → sepolia/mainnet, solana → solana-devnet/solana-mainnet.
768
+ */
769
+ type NetworkEnv = "mainnet" | "testnet";
770
+ /** A connected wallet: discriminated by `chain`, so `execute()` stays native. */
771
+ type CavosWallet = Cavos | CavosSolana | CavosStellar;
772
+ interface ConnectOptions {
773
+ /** Target chain. The returned wallet is discriminated by this same value. */
774
+ chain: Chain;
775
+ /** Environment. Resolved to sepolia/devnet (testnet) or mainnet per chain. */
776
+ network: NetworkEnv;
777
+ /** Authenticated user (pass `identity` directly, or an `auth` provider). */
778
+ auth?: AuthProvider;
779
+ identity?: Identity;
780
+ appSalt: string;
781
+ /**
782
+ * Cavos App ID. When set (with `backendUrl`), the kit uses the hosted
783
+ * WalletRegistry + RecoveryClient by default for real multi-device support.
784
+ */
785
+ appId?: string;
786
+ /** Cavos backend base URL. Defaults to https://cavos.xyz. */
787
+ backendUrl?: string;
788
+ /**
789
+ * Off-chain user_id -> wallet map. Defaults to the hosted HttpWalletRegistry
790
+ * when `appId` is set, else an in-memory registry (single-device only).
791
+ */
792
+ registry?: WalletRegistry;
793
+ /**
794
+ * Device-approval relay (Starknet). Defaults to HttpRecoveryClient when
795
+ * `appId` is set; used to request addition of this device when it isn't a
796
+ * signer yet.
797
+ */
798
+ recovery?: RecoveryClient;
799
+ rpcUrl?: string;
800
+ /** Override the device signer factory (native / tests); default WebCrypto. */
801
+ createSigner?: (keyId: string) => Promise<DeviceSigner>;
802
+ /** Cavos paymaster API key (sponsors deploy + execute). Required for Starknet. */
803
+ paymasterApiKey?: string;
804
+ paymasterUrl?: string;
805
+ classHash?: string;
806
+ /** Cavos device-account program id override. */
807
+ programId?: string;
808
+ /** Gasless sponsorship relayer (defaults to the hosted one when `appId` set). */
809
+ relayer?: SolanaRelayer;
810
+ /** Self-funded fee-payer fallback when no relayer is configured. */
811
+ feePayer?: Keypair;
812
+ /** Gasless sponsorship relayer (defaults to the hosted one when `appId` set). */
813
+ stellarRelayer?: StellarRelayer;
814
+ /** Self-funded source/fee-payer Stellar keypair when no relayer is configured. */
815
+ stellarSourceKeypair?: Keypair$1;
816
+ /** Factory contract id override (else the per-network default). */
817
+ factoryId?: string;
818
+ }
819
+ /** Whether this device can already operate the wallet, or needs to be added. */
820
+ type ConnectStatus = "ready" | "needs-device-approval";
821
+ /** Options for recovering an account after losing every device signer. */
822
+ interface RecoveryOptions {
823
+ /** The recovery code the user stored when they ran setupRecovery. */
824
+ code: string;
825
+ /** Authenticated identity (same user who owns the account). */
826
+ identity: Identity;
827
+ /** Environment (recovery is Starknet-only): testnet → sepolia, mainnet. */
828
+ network: NetworkEnv;
829
+ appSalt: string;
830
+ paymasterApiKey: string;
831
+ appId?: string;
832
+ backendUrl?: string;
833
+ rpcUrl?: string;
834
+ paymasterUrl?: string;
835
+ classHash?: string;
836
+ /** Off-chain user_id -> wallet map. Defaults to the hosted registry. */
837
+ registry?: WalletRegistry;
838
+ /** Override the new device's signer (native / tests); default WebCrypto. */
839
+ createSigner?: (keyId: string) => Promise<DeviceSigner>;
840
+ }
841
+ /**
842
+ * High-level Cavos wallet. One call logs the user in and returns a ready, gas-
843
+ * sponsored smart account controlled by a silent device key.
844
+ *
845
+ * const cavos = await Cavos.connect({ network, identity, appSalt, registry, paymasterApiKey });
846
+ * if (cavos.status === "ready") await cavos.execute(calls);
847
+ *
848
+ * The account address is `f(identity, device_pubkey)` — unforgeable, so it can't
849
+ * be hijacked. The `registry` recognizes returning users across devices: a new
850
+ * device on an existing account is flagged `needs-device-approval` (add it via
851
+ * an already-registered device) instead of creating a second wallet.
852
+ */
853
+ declare class Cavos {
854
+ readonly identity: Identity;
855
+ readonly address: string;
856
+ readonly status: ConnectStatus;
857
+ readonly account: Account;
858
+ private readonly adapter;
859
+ private readonly devicePubkey;
860
+ /** Paymaster URL + API key, for the sponsored passkey-approval path. */
861
+ private readonly paymaster?;
862
+ /** Discriminant for the `CavosWallet` union — narrows `execute()` per chain. */
863
+ readonly chain: "starknet";
864
+ /** Request id of the pending device-addition, when status is needs-device-approval. */
865
+ pendingRequestId: string | null;
866
+ private constructor();
867
+ /**
868
+ * Unified entry point. Pick a `chain` and an `network` environment; the kit
869
+ * resolves the concrete network (sepolia/devnet for testnet, mainnet for
870
+ * mainnet) and returns a chain-native wallet. The result is a discriminated
871
+ * union (`wallet.chain`), so `execute()` keeps each chain's native signature:
872
+ *
873
+ * const wallet = await Cavos.connect({ chain: "solana", network: "testnet", identity, appSalt, appId });
874
+ * if (wallet.chain === "starknet") await wallet.execute(calls);
875
+ * else await wallet.execute(amount, dest);
876
+ */
877
+ static connect(opts: ConnectOptions): Promise<CavosWallet>;
878
+ private static connectStarknet;
879
+ /** This device's public key (e.g. to request addition to an existing wallet). */
880
+ get publicKey(): DevicePublicKey;
881
+ /** Execute a sponsored (gasless) multicall, signed silently by the device. */
882
+ execute(calls: ChainCall[]): Promise<{
883
+ transactionHash: string;
884
+ }>;
885
+ /** Authorize an additional device signer (sponsored). Self-submitted. */
886
+ addSigner(pubkey: DevicePublicKey): Promise<{
887
+ transactionHash: string;
888
+ }>;
889
+ /**
890
+ * Enroll a passkey as an APPROVER so the user can later add devices from any
891
+ * browser (2FA-style step-up). Requires a ready device (the enrollment call is
892
+ * device-signed and gasless). Idempotent: a no-op if the passkey is already an
893
+ * approver. Call this whenever the app decides to prompt "turn on device
894
+ * approvals". Returns the passkey's public key + the enrollment tx hash.
895
+ */
896
+ enrollPasskey(passkey: PasskeySigner, params: PasskeyEnrollParams): Promise<{
897
+ publicKey: DevicePublicKey;
898
+ transactionHash?: string;
899
+ }>;
900
+ /**
901
+ * Register an ALREADY-enrolled passkey public key as an approver (gasless,
902
+ * device-signed). Idempotent. Use this to register ONE passkey across multiple
903
+ * chains without re-prompting `passkey.enroll()` on each: enroll once, then
904
+ * call `addApprover(pubkey)` on each chain's wallet.
905
+ */
906
+ addApprover(pubkey: DevicePublicKey): Promise<{
907
+ transactionHash?: string;
908
+ }>;
909
+ /**
910
+ * From a brand-new browser (status `needs-device-approval`), use the user's
911
+ * synced passkey to authorize adding THIS device — no trip back to an already-
912
+ * authorized device.
913
+ *
914
+ * `add_signer_via_passkey` is a public external authorized by the embedded
915
+ * WebAuthn assertion (no device signature), so by default we sponsor it through
916
+ * the Cavos paymaster's `paymaster_executeDirectTransaction` (the forwarder's
917
+ * `execute_sponsored` runs a generic call — it does NOT require SNIP-9). Pass a
918
+ * custom `submit` to route it through your own relayer instead. Returns the tx.
919
+ */
920
+ approveThisDeviceWithPasskey(opts: {
921
+ passkey: PasskeySigner;
922
+ submit?: (call: ChainCall) => Promise<{
923
+ transactionHash: string;
924
+ }>;
925
+ }): Promise<{
926
+ transactionHash: string;
927
+ }>;
928
+ /** This device's leaf + the current passkey nonce, for a (possibly multi-chain)
929
+ * passkey approval batch. See `approveDeviceEverywhere`. */
930
+ passkeyLeafForThisDevice(): Promise<{
931
+ leaf: Uint8Array;
932
+ nonce: bigint;
933
+ }>;
934
+ /** Submit `add_signer_via_passkey` given a (shared) assertion + this chain's
935
+ * position in the batch. The assertion doesn't carry the passkey pubkey, so we
936
+ * recover both candidates and pick the enrolled approver via the on-chain view
937
+ * (no backend). Defaults to sponsoring through the paymaster. */
938
+ submitPasskeyApproval(assertion: PasskeyAssertion, leaves: Uint8Array[], leafIndex: number, nonce: bigint, submit?: (call: ChainCall) => Promise<{
939
+ transactionHash: string;
940
+ }>): Promise<{
941
+ transactionHash: string;
942
+ }>;
943
+ /**
944
+ * Register a self-custodial backup signer derived from `code`, so the account
945
+ * can be recovered after the user loses every device. Idempotent: if the
946
+ * derived backup key is already an authorised signer, this is a no-op.
947
+ *
948
+ * The code never leaves the device — only its deterministic public key is
949
+ * added on-chain as an ordinary signer. Sponsor this like any other
950
+ * add_signer (gasless). Returns the transaction hash (or undefined when the
951
+ * backup was already set up).
952
+ */
953
+ setupRecovery(code: string): Promise<{
954
+ transactionHash: string;
955
+ } | undefined>;
956
+ /**
957
+ * Recover an account after losing every device signer. Derives the backup key
958
+ * from `code`, uses it (not the new device key) to sign an `add_signer` for
959
+ * the new device, and returns a ready Cavos bound to the new device. The
960
+ * account address is unchanged.
961
+ *
962
+ * Self-custodial: only someone holding the code (i.e. the rightful owner) can
963
+ * re-derive the backup key. The backend never sees the code.
964
+ */
965
+ static recover(opts: RecoveryOptions): Promise<Cavos>;
966
+ }
967
+ /** A chain wallet that can approve THIS device via a passkey (implemented by
968
+ * `Cavos`, `CavosSolana`, `CavosStellar`). */
969
+ interface PasskeyApprovable {
970
+ readonly chain: string;
971
+ readonly status: string;
972
+ passkeyLeafForThisDevice(): Promise<{
973
+ leaf: Uint8Array;
974
+ nonce: bigint;
975
+ }>;
976
+ submitPasskeyApproval(assertion: PasskeyAssertion, leaves: Uint8Array[], leafIndex: number, nonce: bigint): Promise<{
977
+ transactionHash: string;
978
+ }>;
979
+ }
980
+ /**
981
+ * Approve THIS device across several chains with a SINGLE passkey prompt. Each
982
+ * chain is a separate account, so the device must be added per chain — but one
983
+ * WebAuthn assertion over the batch challenge (`sha256(concat(leaves))`) suffices
984
+ * for all of them. Only wallets whose status is `needs-device-approval` are
985
+ * touched. Returns the per-chain tx hashes.
986
+ *
987
+ * await approveDeviceEverywhere([starknet, solana, stellar], passkey);
988
+ */
989
+ declare function approveDeviceEverywhere(wallets: PasskeyApprovable[], passkey: PasskeySigner): Promise<{
990
+ chain: string;
991
+ transactionHash: string;
992
+ }[]>;
993
+
994
+ 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 };