import { Keypair, xdr } from '@stellar/stellar-sdk'; import { I as Identity, A as AuthProvider, S as SocialRecoveryCredential, a as SocialRecoveryClient, E as EnclaveDekPort, D as DeviceFactor, b as WrapStore, W as WalletRegistry, d as PasskeyPrfProvider, V as VaultClient } from './VaultClient-CkzLCyXH.mjs'; import { D as DeviceUnwrapKey } from './DeviceUnwrapKey-Ct-JUQrB.mjs'; import { E as ExecuteOptions, M as MessageSignature, a as StellarSignedTransaction } from './signing-BC_pkVbS.mjs'; /** Classic-Stellar (`G…`) network configuration. * * This is now THE Stellar implementation in the kit (the Soroban `C…` * device-account path was removed — `G…` classic multisig is the default). Classic * Stellar uses Horizon (not the Soroban RPC) to load account state and submit * transactions. */ declare const STELLAR_NETWORKS: { readonly "stellar-testnet": { readonly passphrase: "Test SDF Network ; September 2015"; }; readonly "stellar-mainnet": { readonly passphrase: "Public Global Stellar Network ; September 2015"; }; }; type StellarNetwork = keyof typeof STELLAR_NETWORKS; declare const HORIZON_URL: { readonly "stellar-testnet": "https://horizon-testnet.stellar.org"; readonly "stellar-mainnet": "https://horizon.stellar.org"; }; /** Soroban RPC endpoints — used only for contract invocation (simulate, footprint, * resource fees, submit). Classic account state/pay still goes through Horizon. */ declare const SOROBAN_RPC_URL: { readonly "stellar-testnet": "https://soroban-testnet.stellar.org"; readonly "stellar-mainnet": "https://mainnet.sorobanrpc.com"; }; /** Native XLM has 7 decimals (stroops). */ declare const XLM_DECIMALS = 7; interface StellarRelayerOptions { /** Base URL of the Cavos backend exposing /api/stellar/relay. */ baseUrl: string; /** Cavos App ID (authorizes the sponsored request). */ appId: string; /** Optional Cavos console environment. Omitted means production. */ environment?: "development" | "production"; network: StellarNetwork; } /** What the transaction is, so the backend applies the right validation gate. * - `create` sponsored account creation (relayer = source + sponsor) * - `fee-bump` a control-signed payment wrapped in a relayer fee-bump * - `sponsored-data` a control-signed data write (add factor/device slot) whose * new subentry reserves the relayer sponsors * - `trustline` a control-signed `changeTrust` whose new subentry reserve * the relayer sponsors * - `soroban` a control-signed Soroban contract invocation wrapped in a * relayer fee-bump (the relayer pays the resource + inclusion * fees; the account's control key signs the auth entries) */ type StellarRelayKind = "create" | "fee-bump" | "sponsored-data" | "trustline" | "soroban"; /** * Client for the classic-G sponsoring relayer. Unlike the Soroban relayer (which * is the tx *source*), the classic relayer plays two roles: * - **create**: it is the tx source + fee payer AND sponsors the new account's * reserves (`begin/endSponsoringFutureReserves`), so the user locks no XLM. * The SDK sends a master-signed create tx; the relayer co-signs + submits. * - **fee-bump**: the user's control-signed inner tx (source = their `G…`) is * wrapped in a fee-bump whose fee source is the relayer. The relayer signs the * outer envelope only — it pays the fee, never moves the user's funds. * * Either way the relayer is a fee payer / reserve sponsor, never a custodian. */ declare class StellarRelayer { private readonly opts; private source?; constructor(opts: StellarRelayerOptions); /** The relayer's source/fee-payer/sponsor G-account (fetched + cached). */ getSource(): Promise; /** * The relayer account together with its CURRENT sequence number, straight from * the server. Sponsored writes are sourced by the relayer, so building one * needs its sequence — and reading that from this device's own Horizon is * reading a stale view of an account the server owns, which the network then * rejects as `tx_bad_seq`. The sequence is deliberately NOT cached. * * `sequence` is undefined on older backends, in which case the caller falls * back to loading the account itself. */ fetchSourceAccount(): Promise<{ address: string; sequence?: string; }>; /** POST a (partially) signed transaction XDR for the relayer to co-sign + submit. * `kind` selects the validation gate. Returns the confirmed transaction hash. */ submit(kind: StellarRelayKind, transactionXdr: string): Promise; } interface ConnectStellarOptions { network: StellarNetwork; /** Authenticated user (pass `identity` directly, or an `auth` provider). */ auth?: AuthProvider; identity?: Identity; appSalt: string; /** This device's P-256 ECDH unwrap key (provisioned + persisted per device). */ deviceKey?: DeviceUnwrapKey; /** * Gasless sponsorship via the Cavos classic relayer. When set (or when `appId` + * `backendUrl` are given) the relayer is the tx source + fee payer AND sponsors * the account's reserves — the user locks no XLM and pays no fees. */ relayer?: StellarRelayer; /** Cavos App ID — enables the default relayer when no `relayer` is passed. */ appId?: string; /** Cavos console environment. Defaults to production when omitted. */ environment?: "development" | "production"; /** Cavos backend base URL (default https://cavos.xyz). */ backendUrl?: string; /** * Self-funded funder + fee payer: creates + submits classic transactions * directly (the account pays its own reserves + fees). The advanced / * self-hosted fallback used when no relayer is configured. */ sourceKeypair?: Keypair; /** Horizon URL override. */ horizonUrl?: string; /** Starting balance for a fresh account, in stroops. */ startingBalance?: bigint; credential?: SocialRecoveryCredential; socialRecovery?: SocialRecoveryClient; recovery?: EnclaveDekPort; factor?: DeviceFactor; wrapStore?: WrapStore; registry?: WalletRegistry; passkey?: PasskeyPrfProvider; /** Keep the key in the Cavos vault instead of this page's storage. */ vault?: VaultClient; } /** * Chain status for Stellar accounts. * - `undeployed`: Address derived but no on-chain account exists yet. First execute will create. * - `ready`: Account exists and this device can sign (control key unlocked). * - `needs-device-approval`: Account exists but this device is not yet authorized. */ type StellarConnectStatus = "undeployed" | "ready" | "needs-device-approval"; /** * High-level entry for the classic-Stellar (`G…`) multisig account — the classic * analogue of `CavosStellar` (Soroban). One `connect` derives the deterministic * `G…` address and on a known device unlocks the control key from the on-chain * envelope so `execute` signs silently. * * **Lazy deploy**: Connect NEVER creates the account. The first `execute` call * on an undeployed account creates the account + performs the operation. * * Multiple unlock **factors** all wrap the same DEK, so opening any one yields the * control key: * - **device** (P-256 ECIES): silent daily signing, per-device, non-syncable; * - **passkey** (WebAuthn PRF): synced anchor to approve a new device / recover; * - **recovery code**: offline backup (optional). * * Self-custodial: the address is a pure function of identity and the control key * lives only in the account's own data entries. Creation needs neither an org API * key nor a relayer, and the optional relayer is only a fee payer + reserve sponsor * (never a custodian or identity authority), so a bad/absent relayer can cost fees * but can never move funds or squat an address. When an `appId` is provided we also * record the created address in the Cavos backend `wallets` table (best-effort) so * it counts toward billing — this is pure bookkeeping and never drives address * resolution, custody, or signing. */ declare class CavosStellar { readonly identity: Identity; readonly address: string; readonly network: StellarNetwork; private readonly adapter; private control; private readonly relayer; readonly chain: "stellar"; isNewAccount: boolean; /** * Native MasterDEK wallet. A synced passkey unwraps the DEK onto this device; * it is not added as a Horizon extra signer. */ nativeDek: boolean; /** Native accounts only: a passkey the user added can restore this account. */ passkeyRestore: boolean; private statusValue; /** Track whether account is created on-chain (for lazy deploy). */ private _isDeployed; /** Pending passkey PRF output to include in first create. */ private _pendingPasskeyPrf; /** * Asked for the passkey secret that wraps this account's DEK, while creating * the account. * * The same shape as `Cavos.approverForDeploy`, and here it is not merely * tidier: this secret opens the account, so it must never be written down. * Deriving it from the passkey at the moment of the create is the only * handling that leaves no copy behind. */ passkeyFactorForCreate?: () => Promise; /** Pending recovery code to include in first create. */ private _pendingRecoveryCode; /** Starting balance for account creation. */ private readonly startingBalance; /** Source keypair for self-funded creation. */ private readonly sourceKeypair?; private constructor(); get status(): StellarConnectStatus; /** Whether this account is deployed/created on-chain. */ get isDeployed(): boolean; static connect(opts: ConnectStellarOptions): Promise; /** * Listeners for status changes. * * The status moves when the first execute deploys the account, and it moves * by mutating this object — so nothing holding a reference re-renders, and an * effect keyed on the wallet never re-runs. That is how recovery enrolment * came to be skipped entirely: the wallet turned ready and nobody was told. */ private readonly statusListeners; /** Subscribe to status changes. Returns an unsubscribe. */ onStatusChange(listener: () => void): () => void; private setStatus; /** * Called when an action needs this device to hold the control key and it does * not. The refusal lives here, where the action is, because integrators call * `wallet.execute` directly — a wrapper in the React provider never sees it. */ onAuthorizationNeeded?: () => Promise; /** Native XLM balance of the account, in stroops. */ balance(): Promise; /** * True if the account has a passkey factor enrolled (`cv:wp`), so a new device * can be approved with the passkey instead of a recovery code. Mirrors the * other chains' `hasPasskey()` for the React provider. * * Returns true for undeployed accounts if a passkey is pending enrollment. */ hasPasskey(): Promise; /** * Whether the control key is unlocked on this device (status ready). Classic * approvals land synchronously via Horizon, so this reflects state immediately * (no indexing delay to poll for). * * For undeployed accounts, returns false (not yet on-chain). */ isReady(): Promise; /** * Move `amount` stroops of native XLM to `destination`, signed by the control * key. * * **Lazy deploy**: If the account is undeployed, the first execute creates the * account first (sponsored, 0 XLM cost), then performs the payment in a follow-up * transaction. Native XLM cannot ride the create tx (the account needs to exist * to send FROM it). * * Sponsored by default (the relayer fee-bumps and pays the fee); pass * `{ sponsored: false }` to submit directly — the account pays its own (tiny) * fee from its XLM balance. The control key signs identically in both modes; * only the fee payer differs. */ execute(amount: bigint, destination: string, opts?: ExecuteOptions): Promise; /** * Create the account first (sponsored), then perform the payment. * Called by execute() when status is "undeployed". */ private _createAndExecute; /** * Create the Stellar account on-chain. Returns the control key and DEK. * Uses the pre-generated control key from connect() if available, otherwise * generates a new one. * * The control seed is loaded from pendingControl at the moment of creation, * used only to write the encrypted cv:ct entry, then wiped immediately. The * signing is done via the non-extractable WebCryptoControlKey, so the seed * never sits in memory on the hot spend path. */ private _createAccount; /** * Invoke a Soroban contract method, authorized by this account's control key. * * The full flow: build + simulate the invocation (footprint, resource fees, and * the required `SorobanAuthorizationEntry`s come back from the RPC), then for * every auth entry whose credential address is THIS account's `G…`, re-sign it * with the control key (`authorizeEntry`). Finally sign the tx envelope and * submit via the Soroban RPC (or, when sponsored, fee-bump through the relayer). * * This is what lets a Cavos account act as a `require_auth(role)` signer in * contracts like Trustless Work's escrow (approve/release/dispute/…). `args` * accepts native JS values (converted via `nativeToScVal`) or ready `xdr.ScVal`s. */ invokeContract(params: { contractId: string; method: string; args?: (xdr.ScVal | unknown)[]; opts?: ExecuteOptions; }): Promise; /** * Open a trustline to a classic asset (e.g. USDC) so the account can hold / * receive it — required before funding a Trustless Work escrow in USDC. A * trustline creates a new subentry (reserve), so when sponsored the relayer * pays it (begin/endSponsoringFutureReserves); `{ sponsored: false }` makes the * account pay its own reserve. Returns the confirmed tx hash. */ addTrustline(asset: { code: string; issuer: string; }, opts?: ExecuteOptions & { limit?: string; }): Promise; /** This account's balance of a classic token (e.g. USDC) as a 7-dp string, or * "0" if no trustline exists. Read-only; needs no unlock. */ tokenBalance(asset: { code: string; issuer: string; }): Promise; /** * Sign an externally-built transaction XDR with the control key and return the * signed XDR (does NOT submit). This is the wallet-adapter seam: it mirrors a * classic wallet's `signTransaction(unsignedXdr) → signedXdr`, so apps that * build the tx server-side (e.g. Trustless Work's REST API returns an unsigned * XDR) can use a Cavos account as a drop-in signer. * * Handles both auth models: for Soroban invocations whose auth entries name * THIS account it re-signs those entries (`authorizeEntry`); for source-account * auth (and classic txs) the control-key envelope signature is what satisfies * the account. Entries authorizing other addresses are left untouched. */ signXdr(unsignedXdr: string): Promise; /** * Sign an arbitrary message off-chain with the control key. Nothing is * submitted. Stellar's model differs from Starknet/Solana: the signing key is * the ed25519 **control key** (not a P-256 device key), so `curve` is * `"ed25519"` and `publicKey` is the control key's `G…` address. * * A verifier calls `Keypair.fromPublicKey(controlAddress).verify(messageBytes, * signature)` — standard ed25519 math. The message is prefixed with the Cavos * domain prefix (`"Cavos Signed Message:\n\n"`) before signing. */ signMessage(message: string | Uint8Array): Promise; /** * Build + sign a native XLM payment WITHOUT submitting it. Returns the signed * inner Transaction as base64 XDR. A relayer can fee-bump it (the control * signature stays valid through the fee-bump wrap); or the caller can submit * it directly via Horizon. * * The signature binds to the account's sequence number and the tx has a 180s * timeout, so it is single-use — submit (or fee-bump) promptly. */ signTransaction(amount: bigint, destination: string): Promise; /** * Enroll a passkey as an unlock factor: wrap the DEK under the passkey's PRF * output and write the `cv:wp` entry. This is the synced anchor used to approve * a new device or recover — it survives device loss. Idempotent-ish: writing it * again just overwrites the wrap of the same DEK. * * **Undeployed accounts**: The passkey PRF output is stored pending and will be * included in the first account creation. No on-chain write happens until execute(). */ enrollPasskey(prfOutput: Uint8Array): Promise; /** * Set up a recovery code as an unlock factor: wrap the DEK under the code's KEK * and write the `cv:wr` entry. Optional in v1 — the integrating app decides when * to surface it. The code never leaves the device; only the wrap goes on-chain. * * **Undeployed accounts**: The recovery code is stored pending and will be * included in the first account creation. No on-chain write happens until execute(). */ setupRecovery(code: string): Promise; /** * From a new browser/device (`needs-device-approval`), approve THIS device using * the user's synced passkey. Reconstructs the passkey extra signer, then * `setOptions` adds this device. Flips status to `ready`. */ approveThisDeviceWithPasskey(prfOutput: Uint8Array): Promise; /** Approve THIS device using the recovery code (same as the passkey path, for * the backup factor). */ approveThisDeviceWithRecovery(code: string): Promise; /** * Slot ids of every device currently able to unlock this account, newest-first * order not guaranteed. This device's slot is `deviceKey.slotId()`. Feed these * to `removeDevice` to build a device-management UI. */ listDevices(): Promise; removeDevice(params: { slotId: string; passkeyPrfOutput?: Uint8Array; recoveryCode?: string; opts?: ExecuteOptions; }): Promise<{ transactionHash: string; controlAddress: string; evictedSlots: string[]; }>; /** The control key's public G address (the weight-1 real signer), for display. */ get controlAddress(): string | undefined; private addThisDeviceWith; /** * Sign an inner (account-sourced) payment tx with the control key and submit it: * - sponsored (default) → with a relayer, wrap in a fee-bump (relayer pays * the fee) and POST; falls back to self-funded if no relayer; * - `{ sponsored: false }` → submit directly (the account pays its own fee). * Payments add no subentries, so no reserve sponsorship is needed here. */ /** * Re-sign every Soroban auth entry whose credential address is this account * with the control key, then re-assemble the tx. Entries authorizing OTHER * addresses (e.g. a different escrow role) are left untouched — each party * signs their own. Requires rebuilding the invoke op with the signed entries. */ private signSorobanAuth; /** Sign the tx envelope with the control key and submit the Soroban tx: sponsored * (default) → fee-bump through the relayer; else submit directly via the RPC. */ private submitSoroban; private submitInner; /** * Write data entries (add a factor / device slot) — which create NEW subentries * that each need ~0.5 XLM of reserve. A relayer-sponsored account holds no XLM, * so the write must be sponsored by the relayer (source + sponsor), exactly like * account creation — a plain fee-bump would fail with `op_low_reserve`. * - sponsored (default) → with a relayer, build a sponsored write (relayer * source + begin/end sponsoring), control-sign the account ops, relay * co-signs + submits; falls back to self-funded if no relayer; * - `{ sponsored: false }` → the account writes directly (it must hold its * own reserve for the new subentries). */ private submitDataWrite; /** * Get the control key for signing operations. Works for both ready AND * undeployed accounts (undeployed accounts have a pre-generated control key). * Only throws for needs-device-approval status where no control key exists. */ private requireControl; /** * Get the control key, but only for on-chain operations that require ready status. * Use requireControl() for off-chain signing that can work while undeployed. */ private requireReadyControl; private requireUnlocked; } export { CavosStellar as C, HORIZON_URL as H, StellarRelayer as S, XLM_DECIMALS as X, type ConnectStellarOptions as a, SOROBAN_RPC_URL as b, STELLAR_NETWORKS as c, type StellarConnectStatus as d, type StellarNetwork as e, type StellarRelayKind as f, type StellarRelayerOptions as g };