import { AxiosRequestConfig } from 'axios'; import { PublicClient, Address, Abi, Hex, WalletClient, Hash } from 'viem'; import { UserOperation, PackedUserOperation, BLSSignatureData, TierLevel as TierLevel$1, TierConfig, GuardStatus, PreCheckResult } from './airaccount.js'; export { ALG_BLS, ALG_CUMULATIVE_T2, ALG_CUMULATIVE_T2_WA, ALG_CUMULATIVE_T3, ALG_CUMULATIVE_T3_WA, ALG_ECDSA, ALG_P256, AccountCall, AccountTierProfile, AirAccountClient, AirAccountConfig, AlgId, AuthenticationResponseJSON, BLSConfig, BLSManager, BLSNode, BeginAuthenticationResponse, BeginBindingResult, BeginRegistrationResponse, BeginTransactionVerificationResponse, ConfirmationState, ConfirmationStatus, ContactBindingClient, ContactBindingClientOptions, ContactChannel, ContactRecord, CumulativeT2SignatureData, CumulativeT3SignatureData, DEFAULT_PASSKEY_ROUTES, DEFAULT_WEIGHT_CONFIG, ERC4337Utils, GasEstimate, KmsWebAuthn, OwnerCeremony, PasskeyAuthenticationParams, PasskeyInfo, PasskeyManager, PasskeyRegistrationParams, PasskeyRoutes, PollConfirmationOptions, ProfileName, RequiredSigs, ResolveTransferParams, TIER_PROFILES, TierWeightConfig, TransactionVerificationParams, TransferLimits, TransferResolution, UserOpBuilder, YAAAClient, YAAAConfig, algIdForTier, confirmationCredentialRequest, createContactBindingClient, encodeModifyTierLimitsWithGuardians, encodeSetTierLimits, encodeSetWeightConfig, getDvtConfirmationStatus, modifyTierLimitsGuardianDigest, modifyTierLimitsGuardianDigestFromChain, packBlsPayload, packCommitteeBlsPayload, packCumulativeT2WA, packCumulativeT3WA, packWebAuthnBlob, pollDvtConfirmation, profileSetupCalls, requestSignal, resolveTier, resolveTokenTier, resolveTransfer, sigsForTier, submitDvtConfirmation } from './airaccount.js'; import { T as TokenConfig, I as InitConfig, G as GuardianSpec } from './initConfig-DajRbAVR.js'; import './dvtWire-DxsrbfDe.js'; /** * Account record stored by the SDK. */ interface AccountRecord { userId: string; address: string; signerAddress: string; /** * CREATE2 salt. Canonically persisted as a DECIMAL STRING (lossless, like dailyLimit) — the * full-config / P-256 path (#118 M2) writes it this way so a large salt (> 2^53) neither truncates * as a JS number nor fails JSON serialization as a bigint. The deploy-time rebuild reconstructs it * with `BigInt(account.salt)`, which MUST match the salt used to predict the address * (`_getSalt(owner, salt, configHash)`) or funds sent to the predicted address are stranded. * `number | bigint` retained for back-compat with the legacy create paths. */ salt: string | number | bigint; deployed: boolean; deploymentTxHash: string | null; validatorAddress: string; entryPointVersion: string; factoryAddress: string; createdAt: string; /** * Daily transfer limit in wei, stored as a decimal string (bigint serialization). * "0" or undefined means no guard / no limit. * Written into the factory config at account creation time. */ dailyLimit?: string; /** * Guardian addresses and their acceptance signatures. * Present only for accounts created via createAccountWithGuardians(). * Required by transfer-manager to reconstruct initCode using createAccountWithDefaults. */ guardian1?: string; guardian1Sig?: string; guardian2?: string; guardian2Sig?: string; /** * Full-config (8-field InitConfig) guardian slots — each is either an ECDSA address or a * P-256 (passkey) public key (x, y). Present ONLY for accounts created via * createAccountWithP256Guardians() (the factory's createAccount(owner, salt, config) path). * transfer-manager rebuilds the byte-identical InitConfig from these at first-UserOp deploy * time so the deployed CREATE2 address matches the create-time prediction. */ guardianSpecs?: Array<{ ecdsa: string; } | { p256: { x: string; y: string; }; }>; /** * Resolved approvedAlgIds written into the init config (full-config path). Persisted so the * deploy-time InitConfig is reconstructed EXACTLY (no re-defaulting). Paired with guardianSpecs. */ approvedAlgIds?: number[]; /** * minDailyLimit floor (wei, decimal string) written into the init config (full-config path). * Paired with guardianSpecs for exact deploy-time reconstruction. */ minDailyLimit?: string; } /** * Transfer record stored by the SDK. */ interface TransferRecord { id: string; userId: string; from: string; to: string; amount: string; data?: string; userOpHash: string; bundlerUserOpHash?: string; transactionHash?: string; status: "pending" | "submitted" | "completed" | "failed"; error?: string; nodeIndices: number[]; tokenAddress?: string; tokenSymbol?: string; createdAt: string; submittedAt?: string; completedAt?: string; failedAt?: string; } /** * Paymaster configuration record. */ interface PaymasterRecord { id?: string; name: string; address: string; apiKey?: string; type: "pimlico" | "stackup" | "alchemy" | "custom"; endpoint?: string; createdAt?: string; } /** * BLS configuration record. */ interface BlsConfigRecord { signerNodes?: { nodes: Array<{ nodeId: string; nodeName: string; apiEndpoint: string; status: string; lastSeen?: string; }>; }; discovery?: { seedNodes?: Array<{ endpoint: string; }>; discoveryTimeout?: number; }; } /** * Pluggable storage adapter — replaces NestJS DatabaseService. * SDK only manages accounts, transfers, paymasters, and BLS config. * User authentication is NOT handled by the SDK. */ interface IStorageAdapter { getAccounts(): Promise; saveAccount(account: AccountRecord): Promise; findAccountByUserId(userId: string): Promise; updateAccount(userId: string, updates: Partial): Promise; saveTransfer(transfer: TransferRecord): Promise; findTransfersByUserId(userId: string): Promise; findTransferById(id: string): Promise; updateTransfer(id: string, updates: Partial): Promise; getPaymasters(userId: string): Promise; savePaymaster(userId: string, paymaster: PaymasterRecord): Promise; removePaymaster(userId: string, name: string): Promise; getBlsConfig(): Promise; updateSignerNodesCache(nodes: unknown[]): Promise; } /** * Optional logger interface for server SDK. * Implement this to integrate with your application's logging framework. */ interface ILogger { debug(message: string, ...args: unknown[]): void; log(message: string, ...args: unknown[]): void; warn(message: string, ...args: unknown[]): void; error(message: string, ...args: unknown[]): void; } /** * Default console logger used when no custom logger is provided. */ declare class ConsoleLogger implements ILogger { private readonly prefix; constructor(prefix?: string); debug(message: string, ...args: unknown[]): void; log(message: string, ...args: unknown[]): void; warn(message: string, ...args: unknown[]): void; error(message: string, ...args: unknown[]): void; } /** * Silent logger that suppresses all output. */ declare class SilentLogger implements ILogger { debug(): void; log(): void; warn(): void; error(): void; } /** * Canonical production KMS endpoint (IMX93 TEE behind Cloudflare Tunnel). * v0.20.0 (Beta2) — see AirAccount/kms/CHANGELOG.md. */ declare const DEFAULT_KMS_ENDPOINT = "https://kms.aastar.io"; interface KmsHttpClientOptions { kmsEndpoint?: string; kmsEnabled?: boolean; kmsApiKey?: string; logger?: ILogger; } /** * Shared low-level HTTP transport for all KMS service classes. * * Centralises axios setup (baseURL, x-api-key), the `enabled` gate, and the three * request flavours the KMS uses: * - plain JSON → `post` / `get` * - AWS-KMS framed → `amzPost` (adds x-amz-target + x-amz-json-1.1 content type) * - agent/session JWT → `postWithBearer` (Authorization: Bearer ) * * KmsManager and the composed services (agent / session / payment / monitor) all share * one instance so they reuse the same connection config and auth headers. */ declare class KmsHttpClient { readonly endpoint: string; readonly enabled: boolean; readonly logger: ILogger; private readonly apiKey?; private readonly http; constructor(options: KmsHttpClientOptions); /** Throw if KMS is not enabled — every operation must call this first. */ ensureEnabled(): void; /** * Plain JSON POST. The axios `config` arg is only forwarded when defined, so a * config-less call results in `http.post(path, body)` (2 args) — preserving the * exact call shape the existing unit tests assert against. */ post(path: string, body?: unknown, config?: AxiosRequestConfig): Promise; /** Plain JSON GET. */ get(path: string, config?: AxiosRequestConfig): Promise; /** POST with AWS-KMS framing (x-amz-target header) — required for wallet/signing ops. */ amzPost(path: string, target: string, body: unknown): Promise; /** POST authenticated with a TEE-issued agent/session JWT (Authorization: Bearer). */ postWithBearer(path: string, body: unknown, jwt: string): Promise; } /** * RP id the TA verifies against. The TA hardcodes * `EXPECTED_RP_ID_HASH = SHA-256("aastar.io")` (AirAccount PR#44 / Issue #39); * any other rpId makes the TA reject the assertion with "rpId hash mismatch". */ declare const DEFAULT_RP_ID = "aastar.io"; /** Origin embedded in clientDataJSON — must be the RP origin the TA expects. */ declare const DEFAULT_ORIGIN = "https://aastar.io"; /** * Placeholder credential id (base64url of "test-credential") matching the * reference ceremony fixtures. Production callers SHOULD pass the credential id * returned by CompleteRegistration for the registered passkey. */ declare const DEFAULT_CREDENTIAL_ID = "dGVzdC1jcmVkZW50aWFs"; declare function base64UrlEncode(bytes: Uint8Array): string; declare function base64UrlDecode(value: string): Uint8Array; /** * WebAuthn AuthenticationResponseJSON (the subset the KMS verifies). This is the * value placed in `WebAuthnAssertion.Credential`. */ interface WebAuthnAuthenticationCredential { id: string; rawId: string; type: "public-key"; response: { clientDataJSON: string; authenticatorData: string; signature: string; userHandle?: string; }; clientExtensionResults?: Record; } /** * Pluggable passkey signer. The ceremony helper builds clientDataJSON + * authenticatorData and computes the WebAuthn message * (authenticatorData || SHA-256(clientDataJSON)); this signer turns that message * into an ES256 (ECDSA P-256 over SHA-256) DER signature. * * Browser callers back this with the platform authenticator; server/test callers * use {@link P256PasskeySigner}. */ interface PasskeyCeremonySigner { /** base64url credential id registered with the KMS for this passkey. */ readonly credentialId: string; /** * Sign the WebAuthn message (authenticatorData || SHA-256(clientDataJSON)). * MUST return a DER-encoded ES256 signature (ECDSA P-256 with SHA-256 applied * to the message), matching the WebAuthn wire format. */ sign(message: Uint8Array): Uint8Array | Promise; } /** * Server/test {@link PasskeyCeremonySigner} backed by a raw P-256 private key * (the passkey bound to the KMS key). Mirrors `p256_helper.py`'s * `make_ceremony_assertion`: ES256 DER signature over the WebAuthn message. */ declare class P256PasskeySigner implements PasskeyCeremonySigner { readonly credentialId: string; private readonly privateKey; /** * @param privateKey raw 32-byte P-256 scalar (Uint8Array or hex, 0x optional). * @param credentialId base64url credential id (defaults to the reference fixture). */ constructor(privateKey: Uint8Array | string, credentialId?: string); /** * Uncompressed (0x04…, 65-byte) P-256 public key hex. Register this with the * KMS via CreateKey `PasskeyPublicKey` (or ChangePasskey) so the TA can verify * assertions produced by this signer. */ get publicKeyHex(): string; sign(message: Uint8Array): Uint8Array; } /** * Build the clientDataJSON bytes embedding the TA-issued one-time challenge. * * Compact JSON (no whitespace) with field order `type, challenge, origin`, * mirroring the reference ceremony. The KMS parses this and asserts the * `challenge` field equals the stored nonce before verifying the signature over * (authenticatorData || SHA-256(clientDataJSON)). */ declare function buildClientDataJSON(challenge: string, origin?: string): Uint8Array; /** * Build authenticatorData = rpIdHash(32) || flags(1) || signCount(4, big-endian). * flags = 0x05 (UP | UV). `signCount` must strictly increase across ceremonies * for the same wallet (anti-clone check); callers performing multiple sequential * signs should pass an incrementing value. */ declare function buildAuthenticatorData(rpId?: string, signCount?: number): Uint8Array; interface BuildCredentialOptions { /** The base64url challenge returned by the begin endpoint. */ challenge: string; signer: PasskeyCeremonySigner; rpId?: string; origin?: string; signCount?: number; } /** * Build a complete WebAuthn AuthenticationResponseJSON for a dynamic TA * challenge: construct clientDataJSON (embedding the challenge) + authenticatorData, * then sign (authenticatorData || SHA-256(clientDataJSON)). */ declare function buildAuthenticationCredential(opts: BuildCredentialOptions): Promise; /** Minimal shape returned by BeginAuthentication / begin-grant-session-auth. */ interface BeginCeremonyResponse { ChallengeId: string; Options: { challenge: string; }; } interface RunCeremonyOptions { signer: PasskeyCeremonySigner; rpId?: string; origin?: string; signCount?: number; /** * The 32-byte payload digest being authorized (e.g. the SignHash hash). When set, * the WebAuthn challenge is bound to it as `SHA-256(nonce ‖ payload)` instead of the * raw nonce — this is the "what you see is what you sign" (WYSIWYS) commitment the * TA verifies (AirAccount #68). REQUIRED once the KMS runs in strict mode * (`--features strict-challenge`); in the default transition mode the raw nonce is * still accepted. Omit only for non-signing ceremonies (none today). Accepts a * Uint8Array or a `0x` hex string. */ payload?: Uint8Array | `0x${string}`; } /** * Compute the WYSIWYS-bound WebAuthn challenge for a signing ceremony: * `base64url( SHA-256( decode(nonce) ‖ payload ) )`. * * `nonce` is the base64url challenge from BeginAuthentication; `payload` is the 32-byte * digest about to be signed (the SignHash hash). The KMS/TA recomputes this exact value * and rejects the signature if it doesn't match (AirAccount #68). Use this in a browser * frontend that builds its own WebAuthn assertion for a device passkey, so the per-call * `webAuthnAssertion` it sends commits to the operation hash. */ declare function commitChallenge(nonceBase64Url: string, payload: Uint8Array | `0x${string}`): string; /** * Run a full WebAuthn challenge-binding ceremony (AirAccount #49): * 1. fetch a one-time TA challenge from the `begin` endpoint, * 2. embed it in clientDataJSON, * 3. build + sign the assertion, * 4. return `{ ChallengeId, Credential }` for the KMS `WebAuthn` / * `webAuthnAssertion` field. * * `begin` is injected so the same helper serves both the generic * (purpose="authentication") and grant-session (purpose="grant-session") * challenge endpoints. */ declare function runWebAuthnCeremony(begin: () => Promise, options: RunCeremonyOptions): Promise; /** Fetch a generic authentication challenge (purpose="authentication"). */ declare function beginAuthenticationChallenge(http: KmsHttpClient, keyId: string): Promise; /** Fetch a grant-session challenge (purpose="grant-session"). */ declare function beginGrantSessionChallenge(http: KmsHttpClient, keyId: string): Promise; /** * Convenience: run a generic authentication ceremony over an {@link KmsHttpClient}. * Covers DeriveAddress / Sign / SignHash / SignTypedData / agent-key / * p256-session signing paths. */ declare function runAuthenticationCeremony(http: KmsHttpClient, keyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Convenience: run a grant-session ceremony over an {@link KmsHttpClient}. * Required by sign-grant-session / sign-p256-grant-session, which reject the * generic 'authentication' challenge for cross-op replay safety. */ declare function runGrantSessionCeremony(http: KmsHttpClient, keyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; interface LegacyPasskeyAssertion { AuthenticatorData: string; ClientDataHash: string; Signature: string; } interface WebAuthnAssertion { ChallengeId: string; Credential: unknown; } interface KmsCreateKeyRequest { Description: string; KeyUsage?: string; KeySpec?: string; Origin?: string; PasskeyPublicKey: string; } interface KmsCreateKeyResponse { KeyMetadata: { KeyId: string; Arn: string; CreationDate: string; Enabled: boolean; Description: string; KeyUsage: string; KeySpec: string; Origin: string; Address?: string; }; Mnemonic: string; Address?: string; Status?: string; } interface KmsSignHashResponse { Signature: string; } interface KmsBeginRegistrationRequest { Description?: string; UserName?: string; UserDisplayName?: string; } interface KmsBeginRegistrationResponse { ChallengeId: string; Options: PublicKeyCredentialCreationOptions; } interface KmsCompleteRegistrationRequest { ChallengeId: string; Credential: unknown; Description?: string; } interface KmsCompleteRegistrationResponse { KeyId: string; CredentialId: string; Status: string; } interface KmsBeginAuthenticationRequest { Address?: string; KeyId?: string; } interface KmsBeginAuthenticationResponse { ChallengeId: string; Options: PublicKeyCredentialRequestOptions; } interface KmsEip712Domain { name?: string; version?: string; chainId?: number; verifyingContract?: string; } /** One entry in a `types` definition: a struct name and its ordered fields. */ interface KmsEip712TypeDef { name: string; fields: Array<{ name: string; type: string; }>; } /** One field value for the primary type's message. */ interface KmsEip712FieldValue { name: string; value: unknown; } /** * Compute the standard EIP-712 digest for a KMS typed-data request — the same value the * KMS hashes host-side, and the payload to commit to in the WebAuthn ceremony (WYSIWYS, * AirAccount #68). Converts the KMS wire format (`types` = array of struct defs, `message` * = array of `{name,value}`) into viem's `hashTypedData` input. `EIP712Domain` is dropped * from `types` (viem derives it from `domain`). */ declare function eip712Digest(params: { domain: KmsEip712Domain; primaryType: string; types: KmsEip712TypeDef[]; message: KmsEip712FieldValue[]; }): `0x${string}`; /** * Compute the KMS "mint" digest (v2) — the WYSIWYS commitment payload for the key-minting * ceremonies (AirAccount #115, KMS v0.26.0). Mirrors the TA byte-for-byte; verified against * the locked test vectors on aastar-sdk#135 (`kms/docs/test-vectors/compute_vectors.py`): * * create-agent : SHA-256("AA-AGENT-MINT-v2" ‖ walletId[16B] ‖ SHA-256(label)) * create-p256 : SHA-256("AA-P256-SESSION-MINT-v2" ‖ walletId[16B] ‖ SHA-256(label)) * refresh-agent: SHA-256("AA-AGENT-REFRESH-v2" ‖ walletId[16B] ‖ agentIndex[u32 BE]) * * `walletId[16]` = the human key id parsed as a UUID (`Uuid::as_bytes()`). `label` is the * caller-supplied create label (so create no longer depends on the server-assigned index). * create vs refresh use DIFFERENT tags and are not interchangeable (a refresh gesture cannot * be replayed as a create). Pass the result as the ceremony `payload` — the ceremony binds * `challenge = SHA-256(nonce ‖ mint_digest)` via {@link commitChallenge}. */ declare function mintDigest(p: { kind: "create-agent" | "create-p256"; walletId: string; label: string; } | { kind: "refresh-agent"; walletId: string; agentIndex: number; }): `0x${string}`; /** * Compute the grant-session `final_hash` — the value the TA signs and the WYSIWYS commitment * payload for the grant ceremony (AirAccount #112). Equals the contract's `buildGrantHash()` / * `buildP256GrantHash()` output byte-for-byte (`SessionKeyValidator._buildGrantHash` already * applies `inner.toEthSignedMessageHash()`); verified against the live contract (E2E oracle). * `inner = keccak256(abi.encode(domainTag, chainId, * verifyingContract, account, , expiry, contractScope, selectorScope, * velocityLimit, velocityWindow, callTargetsHash, selectorsHash, nonce))` with * `callTargetsHash = keccak256(abi.encodePacked(callTargets))`, * `selectorsHash = keccak256(abi.encodePacked(selectorAllowlist))`; then EIP-191-prefixed. */ declare function grantSessionFinalHash(p: { chainId: number; verifyingContract: string; account: string; expiry: number; contractScope: string; selectorScope: string; velocityLimit: number; velocityWindow: number; callTargets: string[]; selectorAllowlist: string[]; nonce: number | bigint | string; } & ({ sessionKey: string; } | { keyX: string; keyY: string; })): `0x${string}`; interface KmsSignTypedDataRequest { keyId: string; hdPath?: string; domain: KmsEip712Domain; primaryType: string; types: KmsEip712TypeDef[]; message: KmsEip712FieldValue[]; /** Required unless a Bearer agent JWT is supplied. Legacy passkeyAssertion is rejected. */ webAuthnAssertion?: WebAuthnAssertion; } interface KmsSignTypedDataResponse { keyId: string; signature: string; } interface KmsBeginGrantSessionAuthRequest { keyId: string; } interface KmsBeginGrantSessionAuthResponse { ChallengeId: string; Options: PublicKeyCredentialRequestOptions; } interface KmsSignGrantSessionRequest { keyId: string; hdPath?: string; chainId: number; verifyingContract: string; account: string; sessionKey: string; expiry: number; contractScope: string; selectorScope: string; velocityLimit: number; velocityWindow: number; callTargets: string[]; selectorAllowlist: string[]; nonce: number; webAuthnAssertion: WebAuthnAssertion; } interface KmsSignGrantSessionResponse { keyId: string; signature: string; } interface KmsSignP256GrantSessionRequest { keyId: string; hdPath?: string; chainId: number; verifyingContract: string; account: string; keyX: string; keyY: string; expiry: number; contractScope: string; selectorScope: string; velocityLimit: number; velocityWindow: number; callTargets: string[]; selectorAllowlist: string[]; nonce: number; webAuthnAssertion: WebAuthnAssertion; } interface KmsKeyStatusResponse { KeyId: string; Status: "creating" | "deriving" | "ready" | "error"; Address?: string; PublicKey?: string; DerivationPath?: string; Error?: string; } interface KmsDescribeKeyResponse { KeyMetadata: { KeyId: string; Address?: string; PublicKey?: string; DerivationPath?: string; PasskeyPublicKey?: string; Arn?: string; CreationDate?: string; Enabled?: boolean; Description?: string; KeyUsage?: string; KeySpec?: string; Origin?: string; }; } interface KmsEthereumTransaction { chainId: number; nonce: number; to: string; value: string; gasPrice: string; gas: number; data: string; } interface KmsSignRequest { KeyId?: string; Address?: string; DerivationPath?: string; /** Provide exactly one of Message or Transaction. */ Message?: string; Transaction?: KmsEthereumTransaction; SigningAlgorithm?: string; WebAuthn?: WebAuthnAssertion; Passkey?: LegacyPasskeyAssertion; } interface KmsSignResponse { Signature: string; TransactionHash?: string; } interface KmsGetPublicKeyResponse { KeyId: string; PublicKey: string; Address?: string; KeyUsage?: string; KeySpec?: string; } interface KmsDeriveAddressResponse { Address: string; PublicKey?: string; } interface KmsListKeysResponse { Keys: Array<{ KeyId: string; KeyArn?: string; }>; Truncated?: boolean; NextMarker?: string; } interface KmsDeleteKeyResponse { KeyId: string; DeletionDate?: string; } interface KmsChangePasskeyResponse { KeyId: string; Changed: boolean; } interface KmsUnfreezeKeyResponse { KeyId: string; LifecycleStatus: string; } /** * KMS service for remote key management with WebAuthn/Passkey integration. * * Targets the AAStar TEE KMS (v0.20.0, kms.aastar.io). WebAuthn registration / * authentication ceremonies are handled by the KMS directly; signing operations * require a Passkey assertion (Legacy hex) or a one-time WebAuthn ceremony. * * Wraps a shared {@link KmsHttpClient}; the composed services (agent / session / * payment / monitor) reuse the same client via {@link KmsManager.httpClient}. */ declare class KmsManager { private readonly client; readonly logger: ILogger; constructor(options: { kmsEndpoint?: string; kmsEnabled?: boolean; kmsApiKey?: string; logger?: ILogger; }); isKmsEnabled(): boolean; /** Shared HTTP transport — pass to KmsAgentService / KmsSessionService / etc. */ get httpClient(): KmsHttpClient; private ensureEnabled; /** POST with x-amz-target header (required for wallet/signing operations). */ private amzPost; createKey(description: string, passkeyPublicKey: string): Promise; getKeyStatus(keyId: string): Promise; describeKey(keyId: string): Promise; /** Get a key's public key (uncompressed). Not WebAuthn-gated. */ getPublicKey(target: { KeyId?: string; Address?: string; }): Promise; /** * Derive an Ethereum address at a BIP-44 path (WebAuthn-gated). * Provide a WebAuthn ceremony assertion (preferred) or a Legacy passkey assertion. */ deriveAddress(params: { KeyId: string; DerivationPath: string; WebAuthn?: WebAuthnAssertion; Passkey?: LegacyPasskeyAssertion; }): Promise; /** List keys (paginated). Not WebAuthn-gated. */ listKeys(params?: { Limit?: number; Marker?: string; }): Promise; /** * Schedule key deletion (AWS-KMS action ScheduleKeyDeletion; WebAuthn-gated). * RPMB-bound on the TEE — requires a passkey/WebAuthn assertion on the normal path. */ deleteKey(params: { KeyId: string; PendingWindowInDays?: number; WebAuthn?: WebAuthnAssertion; Passkey?: LegacyPasskeyAssertion; }): Promise; /** * Unfreeze a dormant (frozen) key (issue #42; WebAuthn-gated). * A key auto-frozen by the dormant-key sweep rejects signing until unfrozen. * The TEE verifies the owner via the same strict WebAuthn ceremony as * {@link deleteKey}; ownership is checked even when the key is already active, * so this cannot be used as an unauthenticated key-state probe. Unlike DeleteKey * this endpoint takes no `x-amz-target` header — it authenticates via the default * API key plus the WebAuthn assertion in the body. */ unfreezeKey(params: { KeyId: string; WebAuthn?: WebAuthnAssertion; }): Promise; /** * Rotate the WebAuthn passkey bound to a key (WebAuthn-gated, RPMB-bound). * `PasskeyPublicKey` is the NEW P-256 public key (0x04… 65-byte uncompressed). */ changePasskey(params: { KeyId: string; PasskeyPublicKey: string; WebAuthn?: WebAuthnAssertion; Passkey?: LegacyPasskeyAssertion; }): Promise; /** Schedule key deletion, running the WebAuthn ceremony internally (raw-nonce). */ deleteKeyWithCeremony(params: { KeyId: string; PendingWindowInDays?: number; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** Unfreeze a dormant key, running the WebAuthn ceremony internally (raw-nonce). */ unfreezeKeyWithCeremony(params: { KeyId: string; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** Rotate the bound passkey, running the WebAuthn ceremony internally (raw-nonce). */ changePasskeyWithCeremony(params: { KeyId: string; PasskeyPublicKey: string; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign a message or an EIP-155 transaction (WebAuthn-gated). * Provide exactly one of `Message` (hex) or `Transaction`. For a raw 32-byte * digest use {@link signHash} / {@link signHashWithWebAuthn} instead. */ sign(params: KmsSignRequest): Promise; /** * Poll KeyStatus until the key is ready (address derived) or timeout. * STM32 key derivation takes 60-75 seconds on first creation. */ pollUntilReady(keyId: string, timeoutMs?: number, intervalMs?: number): Promise; /** * Sign a hash using Legacy Passkey assertion (reusable for BLS dual-signing). */ signHash(hash: string, assertion: LegacyPasskeyAssertion, target: { Address?: string; KeyId?: string; }): Promise; /** * Sign a hash using a WebAuthn ceremony assertion (one-time use). */ signHashWithWebAuthn(hash: string, challengeId: string, credential: unknown, target: { Address?: string; KeyId?: string; }): Promise; /** * Sign arbitrary EIP-712 typed data via `POST /kms/SignTypedData` (v0.20.0). * * The KMS hashes the typed data host-side, so the FULL EIP-712 structure * (domain / primaryType / types / message) is sent — not a pre-hashed * domainSeparator/structHash. The `webAuthnAssertion` challenge comes from a * generic {@link beginAuthentication} ceremony (purpose="authentication"). * * Alternatively, agents authenticate with a Bearer JWT — see KmsAgentService. */ signTypedDataWithWebAuthn(params: KmsSignTypedDataRequest): Promise; /** * Begin a grant-session WebAuthn challenge. * The returned challengeId can ONLY be used with sign-grant-session, not sign-typed-data. */ beginGrantSessionAuth(params: KmsBeginGrantSessionAuthRequest): Promise; /** * Sign a GRANT_SESSION_V2 hash off-chain inside the TEE (secp256k1 session key). * Returns a 65-byte signature (R||S||V, V=27/28) for use in grantSessionWithSig(). */ signGrantSession(params: KmsSignGrantSessionRequest): Promise; /** * Sign a GRANT_P256_SESSION_V2 hash off-chain inside the TEE (P256 session key). * Returns a 65-byte signature for use in grantP256SessionWithSig(). */ signP256GrantSession(params: KmsSignP256GrantSessionRequest): Promise; /** * Run a generic authentication ceremony (purpose="authentication") bound to a * fresh TA challenge. The returned assertion is valid for DeriveAddress / Sign * / SignHash / SignTypedData / agent-key / p256-session signing. */ runAuthenticationCeremony(keyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Run a grant-session ceremony (purpose="grant-session") bound to a fresh TA * challenge — required by {@link signGrantSession} / {@link signP256GrantSession} * (the generic 'authentication' challenge is rejected there for replay safety). */ runGrantSessionCeremony(keyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** Derive an address, running the challenge-binding ceremony internally. */ deriveAddressWithCeremony(params: { KeyId: string; DerivationPath: string; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign a message or EIP-155 transaction via `/Sign`, running the ceremony internally. * `params.KeyId` is required. * * ⚠️ STRICT MODE: unlike {@link signHashWithCeremony} / {@link signTypedDataWithCeremony}, * this does NOT auto-bind a payload commitment, because the TA derives the signed digest * from `Message` / `Transaction` host-side (EIP-191 / RLP) and the SDK can't reproduce it * byte-exactly for every input. So it sends the RAW nonce by default — which the KMS will * REJECT once strict mode (#63) is on. For strict-safe signing either: * - pass `options.payload` = the exact digest the TA will sign (you computed it), or * - prefer {@link signHashWithCeremony} (commits to a known 32-byte hash). */ signWithCeremony(params: Omit & { KeyId: string; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign a 32-byte digest, running the challenge-binding ceremony internally. * Binds the challenge to `hash` (WYSIWYS commitment, #68) by default — pass an * explicit `options.payload` only to override. */ signHashWithCeremony(hash: string, target: { KeyId: string; }, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign EIP-712 typed data, running the challenge-binding ceremony internally. * Auto-binds the WYSIWYS commitment (#68): the ceremony challenge is * `SHA-256(nonce ‖ eip712Digest)`, where `eip712Digest` is the standard EIP-712 * digest the KMS hashes host-side — computed here via {@link eip712Digest} so the * user's signature commits to the exact typed-data payload. Pass an explicit * `options.payload` only to override. */ signTypedDataWithCeremony(params: Omit, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign a GRANT_SESSION_V2 hash, running the grant-session ceremony internally * (uses the purpose-bound `begin-grant-session-auth` challenge). */ signGrantSessionWithCeremony(params: Omit, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Sign a GRANT_P256_SESSION_V2 hash, running the grant-session ceremony * internally (uses the purpose-bound `begin-grant-session-auth` challenge). */ signP256GrantSessionWithCeremony(params: Omit, signer: PasskeyCeremonySigner, options?: Omit): Promise; beginRegistration(params: KmsBeginRegistrationRequest): Promise; completeRegistration(params: KmsCompleteRegistrationRequest): Promise; beginAuthentication(params: KmsBeginAuthenticationRequest): Promise; /** * Begin a generic WebAuthn authentication ceremony for a key, returning a * challenge usable for SignHash / SignTypedData (purpose="authentication"). * * NOTE: there is no dedicated `begin-webauthn-auth` endpoint — this delegates * to `POST /BeginAuthentication`. (Grant-session signing needs a purpose-bound * challenge from {@link beginGrantSessionAuth} instead.) */ beginWebAuthnAuth(keyId: string): Promise; /** * Create a KMS signer that authorizes each signature with a LEGACY raw passkey * assertion (reusable, no challenge consumption). * * @deprecated The KMS (v0.20.0+) rejects legacy raw passkey assertions for * signing/mutating operations (`/SignHash` → 400, "no challenge binding — * replayable"), unless `KMS_ALLOW_LEGACY_PASSKEY=1` is set on the KMS (test * only). Prefer {@link createKmsSignerWithCeremony}, which runs a one-time * challenge-bound WebAuthn ceremony per signature. */ createKmsSigner(keyId: string, address: string, assertionProvider: () => Promise): KmsSigner; /** * Create a KMS signer that authorizes each signature with a one-time, * challenge-bound WebAuthn ceremony (production-safe; replay-protected). * * Every `signMessage` call runs a FRESH ceremony (BeginAuthentication → * authenticator assertion → `/SignHash` with the `WebAuthn` field), because the * KMS consumes the challenge atomically (one challenge ⇒ one signature). A * Tier-2/3 BLS transfer that needs N owner signatures therefore triggers N * ceremonies — see {@link BLSSignatureService} (which now skips the unused * userOpHash owner-ECDSA for tiered signatures, so Tier-2 needs only one). * * @param ceremonySigner authenticator that signs the WebAuthn challenge * (a browser passkey on the client, or {@link P256PasskeySigner} server-side). */ createKmsSignerWithCeremony(keyId: string, address: string, ceremonySigner: PasskeyCeremonySigner, ceremonyOptions?: Omit, commitPayload?: boolean): KmsSigner; } /** How a {@link KmsSigner} authorizes each `/SignHash` call. */ type KmsSignerAuth = { mode: "legacy"; assertionProvider: () => Promise; } | { mode: "ceremony"; ceremonySigner: PasskeyCeremonySigner; ceremonyOptions?: Omit; /** * Bind each ceremony challenge to the payload via `SHA-256(nonce ‖ hash)` * (WYSIWYS, AirAccount #68). DEFAULT `true` — verified end-to-end against the live * KMS (kms.aastar.io) once AirAccount#110 (host/TA challenge alignment) shipped; the * KMS transition mode accepts it now and strict mode (#63) will REQUIRE it. Set * `false` only to force the legacy raw-nonce challenge (not strict-safe). */ commitPayload?: boolean; }; /** * KMS-backed signer (EIP-191 personal-sign over a digest). * * Two authorization modes (see {@link KmsSignerAuth}): * - `ceremony` (preferred): each signature runs a fresh one-time WebAuthn * ceremony and calls KMS `SignHash` with the challenge-bound `WebAuthn` field * (replay-safe; what the KMS now requires). * - `legacy` (deprecated): each signature reuses a raw passkey assertion via * KMS `SignHash` `Passkey` field — rejected by KMS unless * `KMS_ALLOW_LEGACY_PASSKEY=1` (test only). * * Narrowed during the ethers -> viem migration: only the EIP-191 personal-sign * and address-read behaviour is consumed by the SDK. */ declare class KmsSigner { private readonly keyId; private readonly _address; private readonly kmsManager; private readonly auth; constructor(keyId: string, _address: string, kmsManager: KmsManager, auth: KmsSignerAuth); getAddress(): Promise; /** * EIP-191 personal-sign over a digest. A string is hashed as UTF-8 text, a byte * array as raw bytes — byte-identical to ethers `hashMessage`. * * @param webAuthnAssertion OPTIONAL pre-built, one-time ceremony assertion. Use * this in server flows where the passkey lives on the USER's device: the * frontend runs the BeginAuthentication ceremony and the backend forwards the * resulting `{ ChallengeId, Credential }` here. When supplied it takes * precedence over the signer's baked-in auth mode. Each assertion is one-time * (the KMS consumes the challenge), so a caller that needs N signatures must * supply N distinct assertions. * * WYSIWYS (AirAccount #68): the frontend MUST build the assertion over the * payload-committed challenge `commitChallenge(nonce, hashOf(message))`, not the * raw nonce — otherwise a compromised host could swap the signed payload. The * raw-nonce assertion only works while the KMS runs in transition mode. (The * signer's own ceremony mode does this automatically.) */ signMessage(message: string | Uint8Array, webAuthnAssertion?: WebAuthnAssertion): Promise; } /** * Context for passing a LEGACY raw passkey assertion through the signing chain. * * @deprecated KMS v0.20.0+ rejects legacy raw passkey assertions for signing * (no challenge binding → replayable). Prefer {@link WebAuthnCeremonyContext}. */ interface PasskeyAssertionContext { assertion: LegacyPasskeyAssertion; } /** * Context carrying a one-time, challenge-bound WebAuthn ceremony assertion * (the replay-safe path the KMS now requires). In server transfer flows the * passkey lives on the USER's device: the frontend runs the BeginAuthentication * ceremony and the backend forwards the resulting `{ ChallengeId, Credential }`. * Each assertion is one-time — a flow needing N signatures supplies N of them. */ interface WebAuthnCeremonyContext { webAuthnAssertion: WebAuthnAssertion; } /** Either auth context accepted by a KMS-backed signer. */ type SignerAuthContext = PasskeyAssertionContext | WebAuthnCeremonyContext; /** * Pluggable signer adapter — replaces NestJS AuthService wallet management. * Implement this to provide signing capabilities from your key management system. * * Narrow by design: the only operations the SDK performs are EOA address * lookup and EIP-191 personal-sign over a digest. There is no transaction * signing / provider connection — that lives in the bundler/UserOp path. */ interface ISignerAdapter { /** Get the EOA address for a given user. */ getAddress(userId: string): Promise<`0x${string}`>; /** * Sign a message for a given user, applying EIP-191 personal-sign semantics * (equivalent to ethers `signer.signMessage(bytes)` / viem * `account.signMessage({ raw: bytes })`). A `Uint8Array` (or raw `0x` hex) is * signed as raw bytes — callers pass a 32-byte digest, NOT UTF-8 text. * * @param ctx optional auth context for KMS-backed signers — a one-time * {@link WebAuthnCeremonyContext} (preferred) or a legacy * {@link PasskeyAssertionContext}. */ signMessage(userId: string, message: `0x${string}` | Uint8Array, ctx?: SignerAuthContext): Promise<`0x${string}`>; /** * Ensure a signer exists for the user (create on demand if needed). * Returns the signer's address. */ ensureSigner(userId: string): Promise<{ address: `0x${string}`; }>; /** * Begin a challenge-bound ceremony for a payload the FRONTEND will sign with the * user's device passkey (the two-phase / "case B" strict path). The adapter: * 1. starts a KMS BeginAuthentication ceremony for the user's key, * 2. computes the WYSIWYS commitment `challenge = SHA-256(nonce ‖ sign-digest(message))` * — the SAME digest {@link signMessage} would sign — so the SDK owns the payload and * the frontend never guesses it, * 3. returns the credential-request options with `challenge` already set to that commitment. * * The frontend runs `navigator.credentials.get(publicKeyOptions)`; the resulting assertion is * passed back via {@link WebAuthnCeremonyContext} to {@link signMessage} (whose digest matches * the committed one, so the KMS accepts it under strict mode). * * Optional: only KMS-backed adapters that support the strict device-passkey path implement it. */ beginCeremony?(userId: string, message: `0x${string}` | Uint8Array): Promise<{ challengeId: string; publicKeyOptions: PublicKeyCredentialRequestOptions; }>; } /** * Per-version EntryPoint configuration. */ interface EntryPointVersionConfig { entryPointAddress: string; factoryAddress: string; validatorAddress: string; } /** * Server SDK configuration — replaces NestJS ConfigService. */ interface ServerConfig { /** Main network RPC URL. */ rpcUrl: string; /** Bundler RPC URL (e.g. Pimlico, StackUp). */ bundlerRpcUrl: string; /** Chain ID of the target network. */ chainId: number; /** EntryPoint configurations — at least one version must be provided. */ entryPoints: { v06?: EntryPointVersionConfig; v07?: EntryPointVersionConfig; v08?: EntryPointVersionConfig; }; /** Default EntryPoint version to use when not specified. */ defaultVersion?: "0.6" | "0.7" | "0.8"; /** * Safety buffer (percent) added on top of the bundler's gas estimate for * callGasLimit / verificationGasLimit. Execution-time gas can exceed the * simulated estimate (cold storage, BLS verification variance), so a small * margin avoids out-of-gas reverts. preVerificationGas is left untouched * (calldata cost is deterministic). Defaults to 10. Set 0 to disable. * Fractional values are rounded to the nearest integer percent (e.g. 10.7 → 11). */ gasEstimateBufferPercent?: number; /** * Static fallback gas limits (hex strings) used ONLY when the bundler's * eth_estimateUserOperationGas call fails. The previous hard-coded 4M * verificationGasLimit is kept as the default because AirAccount's BLS * verification + factory deployment are genuinely gas-heavy and some bundlers * cannot simulate them — but a failed estimate is now logged loudly (it used * to be swallowed) and these values can be overridden per deployment. */ fallbackGasLimits?: { callGasLimit?: string; verificationGasLimit?: string; preVerificationGas?: string; }; /** BLS signer seed nodes for gossip discovery. */ blsSeedNodes?: string[]; /** Timeout for BLS node discovery in ms. */ blsDiscoveryTimeout?: number; /** KMS endpoint URL (optional, for KMS-based signing). */ kmsEndpoint?: string; /** Whether KMS signing is enabled. */ kmsEnabled?: boolean; /** KMS API key for authenticated requests. */ kmsApiKey?: string; /** Storage adapter (required). */ storage: IStorageAdapter; /** Signer adapter (required). */ signer: ISignerAdapter; /** Logger (optional, defaults to ConsoleLogger). */ logger?: ILogger; } /** AirAccount contract version selection. * - "M7" — r4 audit-final (default). Use for all new account creation. * - "M7r6" — r6 deployment (2026-03-29, superseded). Use ONLY to recover existing r6-deployed accounts. * - "M5" — legacy 6-field InitConfig deployment. */ type AirAccountVersion = "M5" | "M7" | "M7r6"; /** * Build a pre-configured EntryPointVersionConfig for Sepolia using a known AirAccount deployment. * Eliminates the need to look up contract addresses manually. * * @example * // Use M7 r4 audit-final (default) * const config = { entryPoints: { v07: sepoliaV07Config() }, ... }; * * // Recover an existing r6-deployed account (do NOT use for new accounts) * const config = { entryPoints: { v07: sepoliaV07Config("M7r6") }, ... }; * * // Use M5 legacy * const config = { entryPoints: { v07: sepoliaV07Config("M5") }, ... }; */ declare function sepoliaV07Config(version?: AirAccountVersion): EntryPointVersionConfig; /** * Validate a ServerConfig and throw descriptive errors for missing fields. */ declare function validateConfig(config: ServerConfig): void; declare enum EntryPointVersion { V0_6 = "0.6", V0_7 = "0.7", V0_8 = "0.8" } interface EntryPointConfig { version: EntryPointVersion; address: string; factoryAddress: string; validatorAddress: string; } /** Default EntryPoint addresses (same on Sepolia, Mainnet, and OP Mainnet). */ declare const ENTRYPOINT_ADDRESSES: { "0.6": { sepolia: string; mainnet: string; optimism: string; }; "0.7": { sepolia: string; mainnet: string; optimism: string; }; "0.8": { sepolia: string; mainnet: string; optimism: string; }; }; declare const ENTRYPOINT_ABI_V6: string[]; declare const ENTRYPOINT_ABI_V7_V8: string[]; declare const FACTORY_ABI_V6: string[]; declare const FACTORY_ABI_V7_V8: string[]; declare const ACCOUNT_ABI: string[]; declare const VALIDATOR_ABI: string[]; declare const AIRACCOUNT_ADDRESSES: { sepolia: { factoryM4: string; factoryM5: string; /** @deprecated defaultCommunityGuardian was address(0); superseded by r6 and r4. Do not use for new accounts. */ factoryM7r5Prev: string; /** @deprecated Use {@link factory} (r4 audit-final) for new accounts. */ factoryM7r6: string; /** @deprecated Use {@link accountImpl} (r4 audit-final). */ accountImplM7r6: string; /** @deprecated Use {@link compositeValidator} (r4 audit-final). */ compositeValidatorM7r6: string; /** @deprecated Use {@link tierGuardHook} (r4 audit-final). */ tierGuardHookM7r6: string; /** @deprecated Use {@link agentSessionKeyValidator} (r4 audit-final). */ agentSessionKeyValidatorM7r6: string; /** @deprecated Use factory (beta.4) for new accounts. */ factoryM7r4: string; /** @deprecated */ accountImplM7r4: string; /** @deprecated Use validatorRouter. */ validatorRouterM7r4: string; /** @deprecated */ compositeValidatorM7r4: string; /** @deprecated */ tierGuardHookM7r4: string; /** @deprecated */ agentSessionKeyValidatorM7r4: string; factory: "0x25C1E9F9120a406581f93bA82f7Cfd6805512791"; factoryM7: "0x25C1E9F9120a406581f93bA82f7Cfd6805512791"; accountImpl: "0x4873b7C1c07BE1b52d6583A64F5E902e593BDdad"; validatorRouter: "0xA15127e8601e77De7C655bf04ca75cccD8C968f0"; blsAlgorithm: "0x1A8Db639b5d8Bd5742edB083656EDD56f416cd64"; blsAggregator: "0x35775df9a4f4dB42Ea0C46118a12dDd0cEc70609"; superPaymaster: "0x09DF0d2e3722EC0e401fE3819E64278a42ae4DE9"; sessionKeyValidator: "0x6b044fB27B4763Fd30D02e41EDF2c62af4Aa946f"; forceExitModule: "0x3fDe77868b74a7979A40a2293a1CD265fbe66EEc"; airAccountDelegate: "0xd2735E54C5f5f2BF523b8a9ddd0E183624c3f2c0"; airAccountExtension: "0x79b90Ed6CB97ec48cfDA86399752C58Bbc59D90a"; agentRegistry: "0x37fc74EaeC81fEdD92876c8713405118Ebc0306e"; calldataParserRegistry: "0x7dEea4544446826601014bD94d0F6432A67496F5"; uniswapV3Parser: string; }; }; declare const AIRACCOUNT_ABI: string[]; declare const AIRACCOUNT_FACTORY_ABI: string[]; declare const GLOBAL_GUARD_ABI: string[]; declare const ERC20_ABI: string[]; /** * @deprecated NO `AgentSessionKeyValidator` contract is deployed. airaccount-contract v0.27.0 * confirmed (Seeder CC-16 / #282): there is no `AgentSessionKeyValidator.sol`, no `grantAgentSession()`, * and no distinct agent-session algId — agent sessions reuse `SessionKeyValidator` (algId `0x08`) with * a scoped `Session`. Every function described here (`grantAgentSession`/`delegateSession`/`agentSessions`/…) * is phantom: a call reverts on-chain. Use the M6 session methods on {@link SESSION_KEY_VALIDATOR_ABI} * instead. Retained (deprecated) for one minor to avoid breaking imports; scheduled for removal in the * next major (#282). */ declare const AGENT_SESSION_KEY_VALIDATOR_ABI: string[]; declare const TIER_GUARD_HOOK_ABI: string[]; declare const AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI: string[]; declare const FORCE_EXIT_MODULE_ABI: string[]; declare const MODULE_TYPE: { readonly VALIDATOR: 1; readonly EXECUTOR: 2; readonly FALLBACK: 3; readonly HOOK: 4; }; declare const ALG_ID: { readonly BLS: 1; readonly ECDSA: 2; readonly P256: 3; readonly CUMULATIVE_T2: 4; readonly CUMULATIVE_T3: 5; readonly COMBINED_T1: 6; readonly WEIGHTED: 7; readonly SESSION_KEY: 8; readonly CUMULATIVE_T2_WA: 9; readonly CUMULATIVE_T3_WA: 10; }; declare const SESSION_KEY_VALIDATOR_ABI: string[]; declare const CALLDATA_PARSER_REGISTRY_ABI: string[]; declare const AIR_ACCOUNT_DELEGATE_ABI: string[]; type ViemContractMethods = Record Promise>; interface ViemContract { address: Address; abi: Abi; /** Read (view/pure) calls: `contract.read.fnName([...args])`. */ read: ViemContractMethods; /** State-changing calls (requires a wallet client — not provided by this read-only hub). */ write: ViemContractMethods; estimateGas: ViemContractMethods; simulate: ViemContractMethods; getEvents: ViemContractMethods; } /** * Unified Ethereum provider — replaces NestJS EthereumService. * Manages RPC + Bundler clients (viem) and contract interactions. */ declare class EthereumProvider { /** Main-network read client. Pass to viem getContract / readContract calls. */ private readonly provider; /** Bundler client — used only for raw eth_ / pimlico_ userOp JSON-RPC. */ private readonly bundlerProvider; private readonly config; private readonly logger; constructor(config: ServerConfig); /** Returns the viem PublicClient for the main network RPC. */ getProvider(): PublicClient; /** Returns the viem PublicClient bound to the bundler RPC (raw .request only). */ getBundlerProvider(): PublicClient; /** EVM chain id from the validated ServerConfig (deterministic — no RPC round-trip). */ getChainId(): number; /** * Raw bundler JSON-RPC call. The bundler exposes non-standard methods * (eth_sendUserOperation, pimlico_getUserOperationGasPrice, ...) that are not in * viem's typed RPC schema, so we go through the transport's request fn untyped. */ private bundlerRequest; private getVersionConfig; getEntryPointAddress(version: EntryPointVersion): string; getFactoryAddress(version: EntryPointVersion): string; getValidatorAddress(version: EntryPointVersion): string; getDefaultVersion(): EntryPointVersion; /** Build a read-only viem contract bound to the main-network PublicClient. */ private contractAt; getFactoryContract(version?: EntryPointVersion): ViemContract; getEntryPointContract(version?: EntryPointVersion): ViemContract; getValidatorContract(version?: EntryPointVersion): ViemContract; getAccountContract(address: string): ViemContract; /** * @deprecated No `AgentSessionKeyValidator` contract is deployed (airaccount-contract v0.27.0, #282). * Its ABI is phantom — every function reverts on-chain — so this now FAILS CLOSED (throws) instead of * handing back a contract whose reads/calls revert. For the real `SessionKeyValidator` (algId 0x08), * use {@link SessionKeyService} (M6 `grantSession`/`grantP256Session`), or bind `SESSION_KEY_VALIDATOR_ABI` * directly with viem's `getContract`. Signature retained for one minor; removed next major. */ getAgentSessionKeyValidatorContract(_address?: string): ViemContract; getTierGuardHookContract(address?: string): ViemContract; getCompositeValidatorContract(address?: string): ViemContract; getForceExitModuleContract(address: string): ViemContract; getBalance(address: string): Promise; getNonce(accountAddress: string, key?: number, version?: EntryPointVersion): Promise; getUserOpHash(userOp: UserOperation | PackedUserOperation, version?: EntryPointVersion): Promise; estimateUserOperationGas(userOp: unknown, version?: EntryPointVersion): Promise<{ callGasLimit: string; verificationGasLimit: string; preVerificationGas: string; }>; sendUserOperation(userOp: unknown, version?: EntryPointVersion): Promise; getUserOperationReceipt(userOpHash: string): Promise; waitForUserOp(userOpHash: string, maxAttempts?: number): Promise; getUserOperationGasPrice(): Promise<{ maxFeePerGas: string; maxPriorityFeePerGas: string; }>; } /** * Shared helpers for the FULL-config (8-field `InitConfig`) account-creation path — * the only factory path that can install P-256 (passkey) guardian keys at deploy time * (airaccount-contract v0.20.0 / #120, #118). * * ## Why a dedicated path (not `createAccountWithDefaults`) * The factory exposes two account-creation entrypoints with DIFFERENT salt + acceptance * semantics (verified against `AAStarAirAccountFactoryV7.sol`): * * - `createAccountWithDefaults(owner, salt, g1, g1Sig, g2, g2Sig, dailyLimit)` — ECDSA-only. * CREATE2 salt = `keccak256(owner, salt)` (does NOT bind the config), so the contract * REQUIRES each ECDSA guardian's `ACCEPT_GUARDIAN` acceptance signature to stop a * front-runner from seizing the counterfactual address with different guardians. This is * the existing `AccountManager.createAccountWithGuardians` path; it has no InitConfig and * thus no way to set `guardianP256X/Y`. * * - `createAccount(owner, salt, config)` — full 8-field `InitConfig`. CREATE2 salt = * `keccak256(owner, salt, keccak256(InitConfig))` (`_getSalt` over `_getConfigHash`), * so the address is BOUND to the exact config. Because any config change yields a * different address, the contract performs NO guardian-acceptance check on this path — * for either ECDSA or P-256 guardians (`_initAccount` installs every slot directly). * P-256 guardian bootstrap is owner-only and acceptance-sig-free by design (#110④): * a single guardian cannot form a recovery quorum, so no consent ceremony is needed. * * Consequence: the deploy-time initCode MUST embed the BYTE-IDENTICAL `InitConfig` used to * predict the address, or the deployed account lands at a different CREATE2 address. These * helpers build the config once (via the core `buildInitConfig`) and reconstruct it * deterministically from the persisted record at first-UserOp deploy time so the two match. */ /** A P-256 (passkey) guardian public key — SEC1 affine coordinates, each a 32-byte hex word. */ interface P256GuardianKey { x: Hex; y: Hex; } /** Inputs for the full-config (8-field `InitConfig`) account-creation path. */ interface FullConfigGuardianParams { /** P-256 (passkey) guardians installed at deploy time. Owner-bootstrap — NO acceptance sig. */ p256Guardians: P256GuardianKey[]; /** * Optional ECDSA guardians, installed via the SAME full-config path. NOTE: on this path the * contract does not verify ECDSA acceptance signatures (the config-hash-in-salt binding stands * in for them), so none are required or accepted here. */ ecdsaGuardians?: Address[]; /** Daily spend limit (wei). MUST be > 0 — a guardian set enables the on-chain GUARD. */ dailyLimit: bigint; /** Validator algorithm ids approved at init. Defaults (in buildInitConfig) to ECDSA (+P-256). */ approvedAlgIds?: number[]; /** Floor the daily limit may be lowered to via the guard. Defaults to 0. */ minDailyLimit?: bigint; /** * ERC-20 tokens to pre-register with the guard at birth (index-aligned with `initialTokenConfigs`). * NOTE: these are per-TOKEN spend limits — they do NOT set the account's NATIVE-ETH tier1/tier2 * (those live in account storage slots 10/11, set via `setTierLimits`, not in InitConfig). #266. */ initialTokens?: readonly Address[]; /** Per-token `{ tier1Limit, tier2Limit, dailyLimit }` (wei), 1:1 with `initialTokens`. */ initialTokenConfigs?: readonly TokenConfig[]; } /** A guardian slot serialized for JSON persistence on the {@link AccountRecord}. */ type SerializedGuardianSpec = { ecdsa: string; } | { p256: { x: string; y: string; }; }; /** * Map the public params to core {@link GuardianSpec}s in a DETERMINISTIC order * (ECDSA slots first, then P-256). Order is consensus-critical: it determines both the * predicted CREATE2 address and the guardian slot index each key occupies on-chain. */ declare function toGuardianSpecs(p: FullConfigGuardianParams): GuardianSpec[]; /** * Build the full 8-field `InitConfig` for the create path. Delegates to the core * `buildInitConfig` (the 0.22.0 builder) so the P-256 slots, sentinel handling, and * approvedAlgId defaulting are produced by ONE audited implementation — never hand-rolled. */ declare function buildFullInitConfig(p: FullConfigGuardianParams): InitConfig; /** * Flatten a typed {@link InitConfig} into the POSITIONAL tuple the local human-readable * factory ABI (`AIRACCOUNT_FACTORY_ABI`, fed through viem `parseAbi`) expects as the * `config` argument of `getAddress` / `createAccount`. Field order is consensus-critical * and matches `AAStarAirAccountBase.InitConfig` exactly. */ declare function initConfigToTuple(c: InitConfig): readonly unknown[]; /** Serialize core {@link GuardianSpec}s for JSON storage on the account record. */ declare function serializeGuardianSpecs(specs: readonly GuardianSpec[]): SerializedGuardianSpec[]; /** * Reconstruct the BYTE-IDENTICAL `InitConfig` from a persisted record at deploy time. * * Re-derivation is exact because the record persists the RESOLVED `approvedAlgIds`, * `minDailyLimit`, and `dailyLimit` (not just the create-time inputs), and `buildInitConfig` * is a pure function of its arguments. The resulting config therefore hashes to the same * `_getConfigHash`, yielding the same CREATE2 address that was predicted at create time. * * @throws if the record carries no `guardianSpecs` (i.e. it is not a full-config account). */ declare function initConfigFromRecord(record: AccountRecord): InitConfig; /** * Result of {@link AccountManager.ensureValidatorRouter}. * `set: true` only when an on-chain `setValidator(router)` tx was actually sent (`tx` * carries the hash). `set: false` is a no-op with a `reason` explaining the decision. */ interface EnsureValidatorRouterResult { set: boolean; reason?: string; tx?: Hash; router?: Address; } /** Parameters shared by the inline ({@link AccountManager.createAccountWithPasskey}) and two-phase * ({@link AccountManager.prepareCreateAccountWithPasskey}) KMS passkey-at-birth create paths (#249). */ interface PasskeyCreateParams { /** Owner device WebAuthn passkey public key (each bytes32) — injected at birth, NOT a guardian. */ ownerP256X: Hex; ownerP256Y: Hex; /** Optional P-256 (passkey) guardians installed at deploy time. */ p256Guardians?: P256GuardianKey[]; /** Optional ECDSA guardians installed via the full-config path (no acceptance sig). */ ecdsaGuardians?: Address[]; /** Daily spend limit in wei. MUST be > 0 (enables the on-chain GUARD). */ dailyLimit: bigint; /** Validator algorithm ids approved at init (e.g. [0x0a] for device-passkey Tier-3). */ approvedAlgIds?: number[]; minDailyLimit?: bigint; /** * ERC-20 tokens to pre-register with the guard at birth (index-aligned with `initialTokenConfigs`), * baked into the InitConfig the owner signature already covers (pure plumbing, no protocol change). #266. * * ⚠️ These are per-ERC-20-TOKEN spend limits. They do NOT set the account's NATIVE-ETH tier1/tier2 * (which are account storage slots 10/11, NOT in InitConfig). To bake a native-ETH tier profile at * birth, call `setTierLimits(tier1, tier2)` after deploy (onlyOwnerOrSelf) — see #266. */ initialTokens?: readonly Address[]; /** Per-token `{ tier1Limit, tier2Limit, dailyLimit }` (wei), 1:1 with `initialTokens`. */ initialTokenConfigs?: readonly TokenConfig[]; salt?: number | bigint; entryPointVersion?: EntryPointVersion; /** ownerSig validity window in seconds from now. Default 3600. */ deadlineSeconds?: number; } /** Result of {@link AccountManager.prepareCreateAccountWithPasskey} — the frontend runs a WebAuthn * ceremony over `challenge`, then calls {@link AccountManager.submitPreparedCreateAccount}. */ interface PreparedPasskeyCreate { /** Opaque handle for {@link AccountManager.submitPreparedCreateAccount}. */ createId: string; /** Counterfactual account address (deploys here). */ predictedAddress: Address; /** The CREATE_ACCOUNT digest the owner must sign (the WebAuthn ceremony challenge for KMS). 32-byte hex. */ challenge: Hex; /** Set on the KMS path: the begun ceremony id + credential-request options for navigator.credentials.get(). */ challengeId?: string; publicKeyOptions?: PublicKeyCredentialRequestOptions; nonce: bigint; deadline: bigint; /** True when the account is already deployed on-chain — no ceremony/submit needed. */ alreadyDeployed: boolean; } /** * Account manager — extracted from NestJS AccountService. * Creates and retrieves smart accounts without framework dependencies. */ declare class AccountManager { private readonly ethereum; private readonly storage; private readonly signer; private readonly logger; /** In-memory store for two-phase passkey creates (prepare → ceremony → submit). Single-process; * a multi-worker deployment needs a shared store. Entries are TTL-evicted (the challenge is short-lived). */ private readonly preparedCreates; private static readonly PREPARED_CREATE_TTL_MS; constructor(ethereum: EthereumProvider, storage: IStorageAdapter, signer: ISignerAdapter, logger?: ILogger); createAccount(userId: string, options?: { entryPointVersion?: EntryPointVersion; salt?: number | bigint; /** Daily transfer limit in wei. When > 0 the account is created with on-chain guard enforcement. */ dailyLimit?: bigint; /** * P-256 (passkey) guardians to install at deploy time. When present, the account is created * via the full-config createAccount(owner, salt, config) path (delegates to * {@link createAccountWithP256Guardians}); `dailyLimit` MUST be > 0 (guardians enable the guard). */ p256Guardians?: P256GuardianKey[]; /** Optional ECDSA guardians installed via the same full-config path (no acceptance sig required). */ ecdsaGuardians?: Address[]; /** Validator algorithm ids approved at init (full-config path). Defaults to ECDSA (+P-256). */ approvedAlgIds?: number[]; /** Floor the daily limit may be lowered to via the guard (full-config path). Defaults to 0. */ minDailyLimit?: bigint; }): Promise; getAccount(userId: string): Promise<(AccountRecord & { balance: string; nonce: string; }) | null>; getAccountAddress(userId: string): Promise; getAccountBalance(userId: string): Promise<{ address: string; balance: string; balanceInWei: string; }>; getAccountNonce(userId: string): Promise<{ address: string; nonce: string; }>; getAccountByUserId(userId: string): Promise; /** * Build the acceptance hash that guardian devices must sign before account creation. * * Encoding: keccak256(solidityPacked( * ["string","uint256","address","address","uint256","uint256"], * ["ACCEPT_GUARDIAN", chainId, factoryAddress, owner, salt, dailyLimit] * )) * * dailyLimit is bound in the hash (PR #47 / C-3) to prevent a front-runner from * replaying guardian sigs with a weaker limit on the same counterfactual address. * * Returns the RAW keccak256 hash (no EIP-191 prefix). * Guardians MUST sign via personal_sign / ethers.signMessage(ethers.getBytes(hash)). * Do NOT use eth_sign — the EIP-191 "\x19Ethereum Signed Message:\n32" prefix * is applied inside the contract (toEthSignedMessageHash) before ecrecover, not here. * * @returns raw hex keccak256 hash — encode this into the QR code shown to guardian devices */ buildGuardianAcceptanceHash(owner: string, salt: number | bigint, factoryAddress: string, chainId: number, dailyLimit: bigint): string; /** * Encode calldata for modifyTierLimitsWithGuardians() — guardian-gated tier-limit change (PR #43). * * Both tier1 and tier2 can be raised or lowered, subject to guardian approval. * Caller is responsible for building and submitting the resulting UserOp. * * @param tier1 New Tier-1 ceiling in wei (ECDSA-only spending; 0 = no limit) * @param tier2 New Tier-2 ceiling in wei (dual-factor; 0 = no limit) * @param deadline Unix timestamp — guardian sigs rejected after this * @param guardianSigs 65-byte EIP-191 hex signatures from required guardians */ encodeModifyTierLimits(tier1: bigint, tier2: bigint, deadline: bigint, guardianSigs: string[]): string; /** * Create an AirAccount with 3 on-chain guardians: * - guardian1 and guardian2: user's own devices (passkeys on phone 1 and phone 2) * - guardian3: team Safe multisig (defaultCommunityGuardian, set in factory at deploy time) * * Both guardian1 and guardian2 must sign the acceptance hash produced by * buildGuardianAcceptanceHash() before this method is called. * * Recovery: any 2-of-3 guardians can initiate social recovery after a 48h timelock. */ createAccountWithGuardians(userId: string, params: { guardian1: string; guardian1Sig: string; guardian2: string; guardian2Sig: string; dailyLimit: bigint; salt?: number | bigint; entryPointVersion?: EntryPointVersion; }): Promise; /** * Create an AirAccount with one or more P-256 (WebAuthn passkey) guardians installed at * DEPLOY time — the server-client path #118 adds for KMS-custodied / counterfactual accounts * (e.g. YAA) that cannot drive the viem extension layer for account creation. * * Uses the factory's full-config `createAccount(owner, salt, config)` path because it is the * ONLY entrypoint that accepts an 8-field `InitConfig` (and therefore `guardianP256X/Y`). The * 8-field config is built by the core `buildInitConfig` (0.22.0) — never hand-rolled — and the * address is predicted via the factory's full-config `getAddress(owner, salt, config)` (NOT * `getAddressWithDefaults`), binding the address to `keccak256(config)`. * * ### Acceptance-signature semantics (verified against AAStarAirAccountFactoryV7.sol) * On this path the contract performs NO guardian-acceptance signature check — for P-256 OR ECDSA * guardians. Front-run protection comes from `_getSalt(owner, salt, _getConfigHash(config))`: * any change to the guardian set (or any other config field) yields a different CREATE2 address, * so an attacker cannot collide on the victim's counterfactual address with a weaker config. * P-256 guardians are an owner-bootstrap (single guardian can't form a recovery quorum), so no * acceptance ceremony exists for them by design (#110④). This is why optional ECDSA guardians may * also be passed here WITHOUT signatures — distinct from createAccountWithGuardians(), which uses * the owner-only-salt `createAccountWithDefaults` path and DOES require ECDSA acceptance sigs. * * The deploy UserOp is still signed by the existing KMS owner-key path (unchanged): this method * only predicts the address and persists the full config; transfer-manager rebuilds the * byte-identical initCode (via {@link initConfigFromRecord}) at first-UserOp deploy time. * * @throws if no P-256 guardian is supplied, dailyLimit <= 0, or EntryPoint is v0.6. */ createAccountWithP256Guardians(userId: string, params: { /** P-256 (passkey) guardian public keys to install at deploy time (at least one required). */ p256Guardians: P256GuardianKey[]; /** Optional ECDSA guardians installed via the same full-config path (no acceptance sig). */ ecdsaGuardians?: Address[]; /** Daily spend limit in wei. MUST be > 0 — a guardian set enables the on-chain guard. */ dailyLimit: bigint; /** Validator algorithm ids approved at init. Defaults to ECDSA (+P-256 when a passkey is present). */ approvedAlgIds?: number[]; /** Floor the daily limit may be lowered to via the guard. Defaults to 0. */ minDailyLimit?: bigint; salt?: number | bigint; entryPointVersion?: EntryPointVersion; }): Promise; /** * KMS-style **passkey-at-birth** account creation via the v0.22.0 factory RELAY mode (#249) — INLINE * single-method form. Works for an EOA/local owner (signs the digest inline), OR a KMS owner IF you * already hold a ceremony assertion committed to the digest. For the STANDARD KMS flow where the user * runs the WebAuthn ceremony separately, use the two-phase {@link prepareCreateAccountWithPasskey} / * {@link submitPreparedCreateAccount} — the ceremony challenge must commit to the digest, which is only * known AFTER the nonce/deadline are resolved (so a single method can't hand the caller the challenge). * * The owner passkey (`ownerP256X/Y`) + validator are wired AT BIRTH in one `createAccount` tx. A KMS * owner key (TEE) can't send a raw tx, so authorization is an EIP-191 `ownerSig` over the SDK-built * CREATE_ACCOUNT digest ({@link buildCreateAccountHash} — never hand-rolled) and a funded * `deployerWallet` relays + pays gas. */ createAccountWithPasskey(userId: string, params: PasskeyCreateParams, opts: { deployerWallet: WalletClient; signerCtx?: SignerAuthContext; }): Promise; /** * Two-phase KMS passkey-at-birth — **PHASE 1** (#249). Resolves the account + computes the * CREATE_ACCOUNT digest (the only thing a KMS WebAuthn ceremony can commit its challenge to) and, for * KMS signers, BEGINS the ceremony. The frontend runs `navigator.credentials.get(publicKeyOptions)` * (or uses `challenge` directly) and passes the assertion to {@link submitPreparedCreateAccount}. * * Mirrors `prepareTransfer`/`submitPreparedTransfer` — necessary because the ceremony challenge must * commit to the digest, which depends on the internally-resolved nonce/deadline, so the caller cannot * precompute it (the chicken-and-egg the single-method form hits for separated-ceremony KMS owners). */ prepareCreateAccountWithPasskey(userId: string, params: PasskeyCreateParams): Promise; /** * Two-phase KMS passkey-at-birth — **PHASE 2** (#249). Signs the prepared digest with the user's * ceremony assertion (KMS owner key, via `opts.signerCtx`) and relays `createAccount` through * `deployerWallet`. Returns the deployed record. */ submitPreparedCreateAccount(createId: string, opts: { deployerWallet: WalletClient; signerCtx?: SignerAuthContext; }): Promise; private _factoryRead; private _passkeyCreateHash; private _evictExpiredCreates; /** Store a prepared-create entry: opportunistic sweep + a per-entry TTL timer so an entry that is * NEVER submitted (and with no later prepare to trigger the sweep) is still evicted. The timer is * `unref`'d so it never keeps the process alive (Codex §5 #249). */ private _storePrepared; /** Validate + build config + predict + idempotency. Throws on bad input; sets existing/alreadyDeployed. */ private _resolvePasskeyCreate; /** Relay createAccount via the deployer + persist the deployed record. */ private _relayPasskeyDeploy; private _persistPasskeyRecord; /** * Gap B — wire the validator router for an account that approved a ROUTER-DELEGATED signature * algorithm (BLS 0x01, cumulative T2 0x04, T3 0x05, weighted 0x07, session 0x08, ...). Such an * account's `_validateTripleSignature` / `_callBLSValidator` return `1` (FAIL) while * `validator() == address(0)`, so the algorithm is non-functional until the owner calls * `setValidator(router)` (onlyOwner, SET-ONCE). Inline algIds (ECDSA 0x02, P256 0x03, COMBINED_T1 * 0x06) need no router and are a no-op here. * * MUST be called AFTER the account is deployed (setValidator is onlyOwner and needs code) — the * lazy/counterfactual deploy path cannot setValidator at predict-time. Idempotent: re-running after * the validator is set is a no-op (`reason: 'validator already set'`). * * On-chain access matches the rest of this package: reads via the EthereumProvider's PublicClient * (`getAccountContract(...).read.validator()` and `getProvider().getCode()`); the state-changing * `setValidator` is sent through a caller-supplied `WalletClient` whose account is the owner — * the same convention used by `PaymasterManager.updatePrice` / `ForceExitService` (this manager's * narrow `ISignerAdapter` only EIP-191 personal-signs and cannot send transactions). * * @param userId the account owner's user id (storage key) * @param opts.router override the router address (defaults to the chain's canonical * `aaStarValidator`); pass to target a non-canonical router * @param opts.walletClient viem WalletClient signing as the account OWNER — REQUIRED to send the tx */ ensureValidatorRouter(userId: string, opts?: { router?: Address; walletClient?: WalletClient; }): Promise; /** * Gap B (complete auto-wiring): deploy a router-delegated account AND set its validator router in * ONE call, so a BLS / cumulative / session-key account is immediately functional — no separate * manual `ensureValidatorRouter` step. The factory's lazy first-UserOp deploy cannot bootstrap such * an account (its own algorithm can't validate until the router is wired), so this performs an * explicit `factory.createAccount(owner, salt, config)` deploy (if the account has no code yet), * waits for it, then wires `setValidator(router)`. Both txs go through the caller-supplied owner/ * deployer `WalletClient` (this manager holds no transaction signer). For inline algIds (ECDSA/P256/ * COMBINED_T1) the validator step is a documented no-op. * * @returns `{ deployTx?, validator }` — `deployTx` is undefined if the account was already deployed. */ deployAndWireValidator(userId: string, opts: { walletClient: WalletClient; router?: Address; }): Promise<{ deployTx?: Hash; validator: EnsureValidatorRouterResult; }>; } /** * Minimal guardian signer surface (was `ethers.Signer`): an external signer that * performs an EIP-191 personal-sign over raw bytes and returns a 0x-prefixed * 65-byte hex signature. Structural — any ethers/viem signer with this method fits. */ interface GuardianSigner { signMessage(message: Uint8Array): Promise; } /** * #257: transport payload the redeployed DVT (v1.7) requires on `/signature/sign`. `userOp` is the * PACKED ERC-4337 UserOperation in RPC (hex) form; `ownerAuth` is the owner's EIP-191 signature over * `userOpHash` (the DVT validates owner authorization before co-signing). Built by the SUBMIT flow and * threaded through the tiered-signature path as a pure transport credential — it is NOT part of the * on-chain composite signature. */ interface DvtSignRequest { userOp: Record; ownerAuth: string; } /** * Device WebAuthn assertion (the three `AuthenticatorAssertionResponse` fields the frontend gets * from `navigator.credentials.get()` with `challenge = userOpHash`). Used by the WebAuthn cumulative * Tier-2/3 path — the SDK derives the on-chain passkey factor (algId 0x09/0x0a) from it. */ interface DeviceWebAuthnAssertion { authenticatorData: `0x${string}` | Uint8Array; clientDataJSON: `0x${string}` | Uint8Array | string; signature: `0x${string}` | Uint8Array; } /** * Raised when a DVT node (aNode YetAnotherAA-Validator ≥ v1.3.0, running with * `CONFIRM_ENABLED=true`) withholds its co-signature on a high-value op pending * out-of-band approval. The node returns `{ status: "pending_confirmation", * userOpHash }` instead of a signature; the withheld co-sign is released by * `POST /signature/confirm { userOpHash, token }` once the user approves over an * independent channel (single-use token, TTL, fail-closed). The SDK surfaces this * as a typed error rather than silently dropping the node so callers can drive the * confirm flow. Default-off nodes never emit this (behaviour == v1.2.0). */ declare class DvtPendingConfirmationError extends Error { readonly userOpHash: string; readonly nodeEndpoint: string; constructor(userOpHash: string, nodeEndpoint: string); } /** * Type guard for a DVT v1.3.0 `/signature/sign` response that withheld its * co-signature pending out-of-band confirmation (`{ status: "pending_confirmation", * userOpHash }`). Used at every sign call site so a high-value-op withhold is * surfaced, not mistaken for a signature-less failure. Default-off nodes never * return this shape. */ declare function isPendingConfirmation(data: unknown): data is { status: "pending_confirmation"; userOpHash?: string; }; /** * BLS signature service — extracted from NestJS BlsService. * Uses lazy initialization instead of onModuleInit. */ declare class BLSSignatureService { private readonly config; private readonly ethereum; private readonly storage; private readonly signer; private blsManager; private readonly logger; constructor(config: ServerConfig, ethereum: EthereumProvider, storage: IStorageAdapter, signer: ISignerAdapter, logger?: ILogger); /** * Fail closed when the DVT validator MOUNTED FOR THIS ACCOUNT is in COMMITTEE mode (CC-98/CC-103). * * The cumulative packers CAN emit committee framing (FU-18), but this service always hands them * bare `nodeIds` — it has no committee-signer plumbing (slot + Merkle proof fetch, and the * one-time owner-only `enrollInCommitteeValidator()` tx it cannot send on the user's behalf). * Under `committeeActive() == true` the account decodes committee framing, so a legacy-framed * composite is guaranteed to be REJECTED on-chain. Throwing beats returning those bytes: the * whole point of FU-18 was that a wrong-framing payload fails as an opaque `validateUserOp != 0` * far from its cause. Full committee support for this path is FU-19. * * EVERYTHING here is derived from the CHAIN — never from the static address book, never from the * storage record (Codex review, rounds 2-3). Two earlier revisions were wrong in the same way, * trusting a local copy of state the chain owns: * - `CANONICAL_ADDRESSES` as both the validator to read and a "does this chain have committee * infra" precondition, so a non-canonical deployment WITH committee infra bypassed the guard; * - the persisted `account.validatorAddress`, which `AccountManager.ensureValidatorRouter()` * lets an owner legitimately outdate (it sends `setValidator` without writing back), so a * stale record could name a legacy router while the account really runs on a committee one. * The authoritative chain is therefore read end to end: * * account.validator() = the account's own set-once router (on-chain, not the record) * -> getAlgorithm(0x01) = the validator actually mounted for THIS account * -> committeeActive() = the framing it will decode * * Fail-closed boundary: allowing legacy signing to proceed is the dangerous direction, so it needs * a POSITIVE reason. Exactly one thing earns it — the contract answering, at the ABI level, that it * has no `committeeActive()` (a pre-committee validator, which every legacy deployment is, and for * which legacy framing is correct) — corroborated by `getCode` so that an empty response from a * codeless address, which decodes identically, cannot pass as that answer. Everything else — * transport faults, unrecognised errors, a stripped cause chain, an unresolvable router — refuses * to sign. Guessing legacy is precisely how a guaranteed-rejected signature gets produced. */ private resolveCommitteeFraming; /** * Fetch each contributing node's `slot` + Merkle proof so the aggregate can be committee-framed * (FU-19). Kept separate from {@link resolveCommitteeFraming} because it needs the nodeIds, which * only exist AFTER the DVT round-trip — whereas the framing decision must happen BEFORE it, so an * unenrolled account or an unreadable validator fails without burning three node calls. */ private fetchCommitteeSignersFor; /** Lazy-initialize BLSManager on first use. */ private ensureInitialized; getActiveSignerNodes(): Promise; generateBLSSignature(userId: string, userOpHash: string, ctx?: SignerAuthContext, options?: { /** * Skip the owner ECDSA over `userOpHash` (`aaSignature`). The cumulative * Tier-2 (algId 0x04) / Tier-3 (0x05) packings do NOT include it — they * carry only `messagePointSignature` (owner intent comes from the P256 * passkey signature) — so computing it there is a wasted owner signature. * Under the WebAuthn-ceremony KMS path that wasted signature is also a * wasted user gesture, so tiered callers set this to `true`. */ skipOwnerOpSignature?: boolean; /** * #257: the redeployed DVT (v1.7) validates OWNER AUTHORIZATION before co-signing, so the sign * request is `{ userOp, ownerAuth }` (ownerAuth = the owner's EIP-191 sig over userOpHash). * This is a TRANSPORT credential produced by the SUBMIT flow and threaded through — the composite * signature (P256 + BLS + guardian) does NOT include it and this method does NOT produce it. */ dvtRequest?: DvtSignRequest; }): Promise; /** * COORDINATION SEAM (#257 / P2P-migration). The SDK-coordinator BLS transport: POST the sign request * to each selected node, then aggregate. The redeployed DVT (v1.7) rejects the legacy `{ message }` * body and validates OWNER AUTHORIZATION before co-signing, so the body is `{ userOp, ownerAuth }` * (`ownerAuth` = the owner's EIP-191 sig over userOpHash, produced by the submit flow — this layer * only transports it). * * This is the ONLY place that talks to the DVT nodes. A future P2P deployment (nodes self-discover + * self-organize) provides an alternative implementation of this single method — a submit-once-to-the- * network transport that returns the same `{ nodeIds, signature }` — with NO change to the composite * signature assembly, the tiered packers, or the contract format. */ private _coordinateBlsAggregate; packSignature(blsData: BLSSignatureData): Promise; /** * Generate a tiered signature based on the required tier level. * * - Tier 1: algId 0x02 — single ECDSA ([0x02][r][s][v] = 66 bytes). airaccount-contract * v0.25.0 removed the raw-65 fallback, so the leading 0x02 is now REQUIRED (#273). This * matches the Ledger path (auth/hardware/ledger.ts) and the compositeValidator ECDSA path. * - Tier 2: algId 0x04 — P256 + BLS aggregate (contract #45: no messagePoint/mpSig) * - Tier 3: algId 0x05 — P256 + BLS aggregate + Guardian ECDSA (contract #45: no messagePoint/mpSig) * * @param tier - Required tier level (1, 2, or 3) * @param userId - User ID for account lookup * @param userOpHash - The UserOp hash to sign * @param p256Signature - P256 passkey signature (64 bytes, required for tier 2/3) * @param guardianSigner - Guardian signer (required for tier 3) * @param ctx - Optional passkey assertion context for KMS signing */ generateTieredSignature(params: { tier: TierLevel$1; userId: string; userOpHash: string; p256Signature?: string; guardianSigner?: GuardianSigner; ctx?: SignerAuthContext; /** #257 transport: { userOp, ownerAuth } for the DVT — produced by the submit flow, threaded through. */ dvtRequest?: DvtSignRequest; }): Promise; /** * Generate a WebAuthn cumulative Tier-2/3 signature (algId 0x09 / 0x0a) from a DEVICE passkey * assertion — the integrator-zero-packing path (#234). The frontend runs one WebAuthn ceremony * with `challenge = userOpHash`; the SDK derives the on-chain passkey factor from the assertion, * fetches + aggregates the DVT BLS co-signatures itself, and packs the composite. No KMS owner * signature is involved (the device passkey IS the owner factor; cumulative = P256 + BLS [+ guardian]). * * @param tier 2 or 3 (tier 1 is plain ECDSA — not this path). * @param deviceWebAuthn the `navigator.credentials.get()` response fields (challenge MUST be userOpHash). * @param guardianSigner required for tier 3. */ generateWebAuthnTieredSignature(params: { tier: TierLevel$1; userId: string; userOpHash: string; deviceWebAuthn: DeviceWebAuthnAssertion; guardianSigner?: GuardianSigner; /** * #257 transport: { userOp, ownerAuth } for the DVT — the owner authorization the redeployed nodes * validate before co-signing. Produced by the SUBMIT flow (where owner authorization belongs) and * threaded through; it is NOT part of the on-chain composite (this function stays a pure composite * assembler — P256 + BLS + guardian). */ dvtRequest?: DvtSignRequest; }): Promise; } /** * Pre-checks transactions against GlobalGuard before submitting on-chain. * Avoids wasted gas from predictable reverts. */ declare class GuardChecker { private readonly ethereum; private readonly logger; constructor(ethereum: EthereumProvider, logger?: ILogger); /** * Fetch tier limits from an AirAccount contract. */ fetchTierConfig(accountAddress: string): Promise; /** * Fetch guard status from the account's GlobalGuard. */ fetchGuardStatus(accountAddress: string): Promise; /** * Pre-check a transaction: determine tier, check guard limits and algorithm approval. * Returns errors array (empty = OK to proceed). */ preCheck(accountAddress: string, value: bigint, useWebAuthnPasskey?: boolean): Promise; } /** * Thrown when a paymaster's on-chain price cache is stale. * Caller should invoke `paymasterManager.updatePrice(paymasterAddress)` before retrying. */ declare class PaymasterPriceStalenessError extends Error { readonly paymasterAddress: string; readonly ageSeconds: number; readonly thresholdSeconds: number; constructor(paymasterAddress: string, ageSeconds: number, thresholdSeconds: number); } /** * Paymaster manager — extracted from NestJS PaymasterService. * Storage via IStorageAdapter instead of filesystem JSON files. */ declare class PaymasterManager { private readonly ethereum; private readonly storage; private readonly logger; constructor(ethereum: EthereumProvider, storage: IStorageAdapter, logger?: ILogger); getAvailablePaymasters(userId: string): Promise<{ name: string; address: string; configured: boolean; }[]>; addCustomPaymaster(userId: string, name: string, address: string, type?: "pimlico" | "stackup" | "alchemy" | "custom", apiKey?: string, endpoint?: string): Promise; removeCustomPaymaster(userId: string, name: string): Promise; /** * Check whether a paymaster's on-chain price cache is still fresh. * Returns `{ fresh, ageSeconds, thresholdSeconds }`. * Throws if the contract does not implement `cachedPriceTimestamp()` / `priceStalenessThreshold()`. */ checkPriceFreshness(paymasterAddress: string): Promise<{ fresh: boolean; ageSeconds: number; thresholdSeconds: number; }>; /** * Call `updatePrice()` on a paymaster contract (permissionless). * Useful when `checkPriceFreshness()` reports stale price. * * @param walletClient - A viem WalletClient (with an account) that will send * the transaction (must have gas). Replaces the former ethers Signer param. */ updatePrice(paymasterAddress: string, walletClient: WalletClient): Promise; getPaymasterData(userId: string, paymasterName: string, userOp: unknown, entryPoint: string, customAddress?: string, options?: { tokenAddress?: string; }): Promise; private getPimlicoPaymasterData; private getStackUpPaymasterData; private getAlchemyPaymasterData; } interface TokenInfo { address: string; symbol: string; name: string; decimals: number; } interface TokenBalance { token: TokenInfo; balance: string; formattedBalance: string; } /** * Token service — extracted from NestJS TokenService. * Only on-chain queries and calldata generation (no preset token list). */ declare class TokenService { private readonly ethereum; constructor(ethereum: EthereumProvider); getTokenInfo(tokenAddress: string): Promise; getTokenBalance(tokenAddress: string, walletAddress: string): Promise; getFormattedTokenBalance(tokenAddress: string, walletAddress: string): Promise; generateTransferCalldata(to: string, amount: string, decimals: number): string; validateToken(tokenAddress: string): Promise<{ isValid: boolean; token?: TokenInfo; error?: string; }>; } interface ExecuteTransferParams { to: string; amount: string; data?: string; tokenAddress?: string; usePaymaster?: boolean; paymasterAddress?: string; paymasterData?: string; /** ERC-20 token address for deposit-pull paymasters (e.g. PMv4) that require * the gas token address appended to paymasterData. Used when the paymaster * contract does not expose a public token() getter for auto-detection. */ paymasterTokenAddress?: string; /** * LEGACY raw passkey assertion for KMS signing. * @deprecated KMS v0.20.0+ rejects it (replayable). Use {@link webAuthnAssertion}. */ passkeyAssertion?: LegacyPasskeyAssertion; /** * One-time, challenge-bound WebAuthn ceremony assertion for KMS owner signing * (replay-safe; what the KMS now requires). The frontend runs the * BeginAuthentication ceremony with the user's device passkey and passes the * resulting `{ ChallengeId, Credential }` here. The challenge is consumed once, * so this authorizes exactly ONE owner signature — use the tiered path * (`useAirAccountTiering: true`), which needs a single owner signature. */ webAuthnAssertion?: WebAuthnAssertion; /** P256 passkey signature (64 bytes hex). Required for AirAccount Tier 2/3. */ p256Signature?: string; /** Guardian signer instance. Required for AirAccount Tier 3. */ guardianSigner?: GuardianSigner; /** Enable AirAccount tiered signature routing. Default: false (legacy BLS-only). */ useAirAccountTiering?: boolean; /** * Use the on-chain WebAuthn-passkey cumulative path (algId 0x09/0x0a) for Tier-2/3 instead of the * raw-P256 cumulative (0x04/0x05). Set this when the account's passkey is a real device WebAuthn * credential (the common case): the frontend runs ONE `navigator.credentials.get()` ceremony with * `challenge = the prepared userOpHash`, and submit derives the on-chain passkey factor from that * assertion (no KMS owner ceremony, no manual packing). Requires `useAirAccountTiering: true`. */ useWebAuthnPasskey?: boolean; /** * Wrap the execute()/executeBatch() callData with the `executeUserOp` selector * (v0.17.2-beta.4 bundler-compat). REQUIRED for guard-enabled accounts submitted * through a standard ERC-4337 bundler; the account re-derives the signature algId * in-frame. Default: false. No-guard accounts and owner-direct calls leave it off. */ wrapExecuteUserOp?: boolean; } interface EstimateGasParams { to: string; amount: string; data?: string; tokenAddress?: string; /** Match the executeUserOp wrapping used at submission so gas estimation is accurate (v0.17.2-beta.4). */ wrapExecuteUserOp?: boolean; } interface TransferResult { success: boolean; transferId: string; userOpHash: string; status: string; message: string; from: string; to: string; amount: string; } /** Phase-1 output of {@link TransferManager.prepareTransfer} (the strict device-passkey flow). */ interface PreparedTransfer { /** Opaque handle to pass back to {@link TransferManager.submitPreparedTransfer}. */ transferId: string; /** * KMS BeginAuthentication ChallengeId — pair it with the credential as the webAuthnAssertion. * Absent on the WebAuthn passkey path (`useWebAuthnPasskey`), which runs no KMS ceremony — there * the frontend uses `userOpHash` itself as the navigator.credentials.get() challenge. */ challengeId?: string; /** * Credential-request options to feed `navigator.credentials.get` / `startAuthentication`. * Its `challenge` is ALREADY the WYSIWYS commitment over the correct payload (SDK-computed). * Absent on the WebAuthn passkey path (use `userOpHash` as the challenge). */ publicKeyOptions?: PublicKeyCredentialRequestOptions; /** The UserOp hash (informational; the frontend does not need to sign it directly). */ userOpHash: string; /** * The resolved AirAccount tier for this transfer (1/2/3), or `null` for the ECDSA / legacy-BLS path. * Tier 3 (amount > tier2Limit) REQUIRES a guardian co-signature at submit — pass it via * `submitPreparedTransfer({ ..., guardianSigner })`, or the submit will fail-fast before any gas. */ tier: TierLevel$1 | null; /** Which signatures this transfer needs, so the UI knows whether to collect a guardian co-sign. */ requiredSigs: { passkey: boolean; bls: boolean; guardian: number; }; } /** * Transfer manager — extracted from NestJS TransferService. * No passkey verification: callers are responsible for their own auth. */ declare class TransferManager { private readonly ethereum; private readonly accountManager; private readonly blsService; private readonly paymasterManager; private readonly tokenService; private readonly storage; private readonly signer; private readonly logger; private readonly guardChecker; /** * In-memory store for two-phase transfers between prepareTransfer and submitPreparedTransfer. * Single-process only — a multi-worker deployment must back this with a shared store (the * prepared UserOp + its committed challenge must be retrievable by whichever worker submits). */ private readonly prepared; constructor(ethereum: EthereumProvider, accountManager: AccountManager, blsService: BLSSignatureService, paymasterManager: PaymasterManager, tokenService: TokenService, storage: IStorageAdapter, signer: ISignerAdapter, logger?: ILogger, guardChecker?: GuardChecker); executeTransfer(userId: string, params: ExecuteTransferParams): Promise; /** * Phase 1: build the UserOp + bind the WYSIWYS commitment, returning everything the frontend * ceremony needs. Requires a signer adapter implementing {@link ISignerAdapter.beginCeremony} * (e.g. {@link KmsSignerAdapter}) and the tiered path (`useAirAccountTiering: true`) or a plain * ECDSA account — the legacy non-tiered BLS path needs two owner signatures and can't be * single-assertion prepared. * * NOTE: the prepared UserOp is held in-memory keyed by `transferId` until * {@link submitPreparedTransfer} (single-process; a multi-worker deployment needs a shared store). */ prepareTransfer(userId: string, params: ExecuteTransferParams): Promise; /** * Phase 3: finish a {@link prepareTransfer} with the frontend's device-passkey assertion. * The committed digest matches what prepareTransfer bound, so the KMS accepts it under strict. * The prepared record is consumed (single-use). */ submitPreparedTransfer(userId: string, params: { transferId: string; /** KMS-ceremony assertion for the owner-signature paths. Not used by the WebAuthn passkey path. */ webAuthnAssertion?: WebAuthnAssertion; /** * Guardian co-signer, REQUIRED when the prepared transfer is Tier 3 (see {@link PreparedTransfer.tier}). * Collected at submit time (after the UI saw it was needed). If omitted for a Tier-3 transfer, * submit fail-fasts (no gas) instead of producing an incomplete signature that reverts on-chain. */ guardianSigner?: GuardianSigner; /** * Device-passkey P256 signature (64-byte `r‖s` hex) over the prepared `userOpHash`, for the * RAW-P256 cumulative path (algId 0x04/0x05). For real device WebAuthn passkeys use * `deviceWebAuthn` + `useWebAuthnPasskey` instead (the device can't produce a raw r‖s). */ p256Signature?: string; /** * The device WebAuthn assertion (`navigator.credentials.get()` response over `challenge = userOpHash`) * for the WebAuthn passkey cumulative path (algId 0x09/0x0a). Required when the prepared transfer * was created with `useWebAuthnPasskey: true`. The SDK derives the on-chain passkey factor + fetches * the DVT BLS aggregate + packs the composite — no manual packing. */ deviceWebAuthn?: DeviceWebAuthnAssertion; }): Promise; /** * Resolve the signature strategy ONCE (account detection + tier pre-check) so the committed * payload and the signed payload derive from the SAME decision — no re-derivation drift between * prepareTransfer's commitment and submitPreparedTransfer's signing (#143 Codex). * `tier === null` means "not the AirAccount tiered path" (ECDSA or legacy BLS). */ private resolveSignStrategy; /** * The exact message the single owner signature will sign (before the adapter's EIP-191 wrap), * for a resolved strategy — so prepareTransfer binds the strict-KMS ceremony challenge to the value * submit actually signs. For ALL owner-signed strategies that is now the `userOpHash`: * - ECDSA / Tier-1: the owner ECDSA over userOpHash. * - Tier-2/3: the ONLY owner signature is the DVT `ownerAuth` over userOpHash (#257). Tier-2/3 omit * both the owner ECDSA AND the messagePoint signature (#258 M1 — generateBLSSignature skips them * under skipOwnerOpSignature). #259: previously this returned `keccak256(messagePoint)`, so the * ceremony committed to messagePointHash while submit signed userOpHash → strict-KMS challenge * mismatch (400). Bind to userOpHash to match. */ private ownerMessageForStrategy; /** * Set `userOp.signature` for an ALREADY-RESOLVED strategy (ECDSA / AirAccount tiered / legacy * BLS). Shared by executeTransfer and submitPreparedTransfer so the signing logic never drifts; * taking the resolved strategy (not re-detecting) means submit signs exactly the committed payload. */ /** * #257: build the DVT sign request `{ userOp (packed, RPC hex), ownerAuth }`. `ownerAuth` = the * owner's EIP-191 signature over `userOpHash` — the owner-authorization the redeployed DVT (v1.7) * validates before co-signing. Produced HERE (the submit flow, where owner authorization belongs) and * threaded into the BLS coordination as a pure transport credential — it is NOT part of the on-chain * composite signature, and the composite-assembly functions do not produce it. */ private buildDvtRequest; private applySignature; /** Persist the transfer record and submit it to the bundler asynchronously. */ private finalizeAndSubmit; private processTransferAsync; estimateGas(userId: string, params: EstimateGasParams): Promise<{ callGasLimit: string; verificationGasLimit: string; preVerificationGas: string; validatorGasEstimate: string; totalGasEstimate: string; maxFeePerGas: string; maxPriorityFeePerGas: string; }>; getTransferStatus(userId: string, transferId: string): Promise>; getTransferHistory(userId: string, page?: number, limit?: number): Promise<{ transfers: TransferRecord[]; total: number; page: number; limit: number; totalPages: number; }>; private buildUserOperation; private formatUserOpForBundler; } /** * Thin wrapper around ISignerAdapter for consistent wallet access. */ declare class WalletManager { private readonly signer; constructor(signer: ISignerAdapter); getAddress(userId: string): Promise<`0x${string}`>; signMessage(userId: string, message: `0x${string}` | Uint8Array, ctx?: SignerAuthContext): Promise<`0x${string}`>; ensureSigner(userId: string): Promise<{ address: `0x${string}`; }>; } /** * Main facade for the YAAA Server SDK. * Wires all services together from a single config object. * * @example * ```ts * import { AirAccountServerClient, MemoryStorage, LocalWalletSigner } from '@aastar/airaccount/server'; * * const client = new AirAccountServerClient({ * rpcUrl: 'https://sepolia.infura.io/v3/...', * bundlerRpcUrl: 'https://api.pimlico.io/v2/11155111/rpc?apikey=...', * chainId: 11155111, * entryPoints: { * v06: { * entryPointAddress: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789', * factoryAddress: '0x...', * validatorAddress: '0x...', * }, * }, * storage: new MemoryStorage(), * signer: new LocalWalletSigner('0xPRIVATE_KEY'), * }); * * const account = await client.accounts.createAccount('user-123'); * ``` * * @example KMS-backed signing (production) — inject {@link KmsSignerAdapter} as the * `signer`. This is the wiring seam that carries a per-call WebAuthn ceremony * assertion (challenge-bound, replay-safe) from `executeTransfer` through to the * KMS `/SignHash`. The `userId → { keyId, address }` mapping is app-specific. * ```ts * import { AirAccountServerClient, KmsManager, KmsSignerAdapter } from '@aastar/airaccount/server'; * * const kms = new KmsManager({ kmsEndpoint, kmsApiKey, kmsEnabled: true }); * const client = new AirAccountServerClient({ * ...rest, * signer: new KmsSignerAdapter(kms, async (userId) => lookupUserKey(userId)), * }); * // Transfer with a one-time WebAuthn assertion (frontend ceremony) on the tiered path: * await client.transfers.executeTransfer(userId, { * ...params, * useAirAccountTiering: true, * webAuthnAssertion, // { ChallengeId, Credential } from BeginAuthentication * }); * ``` */ declare class AirAccountServerClient { readonly ethereum: EthereumProvider; readonly accounts: AccountManager; readonly transfers: TransferManager; readonly bls: BLSSignatureService; readonly paymaster: PaymasterManager; readonly tokens: TokenService; readonly wallets: WalletManager; constructor(config: ServerConfig); } /** * @deprecated Renamed to {@link AirAccountServerClient}. This alias is kept for * backward compatibility and will be removed in a future major version. */ declare const YAAAServerClient: typeof AirAccountServerClient; /** Per-ERC-20 tier ceilings (in the token's own units, e.g. 6-decimal USDC). */ interface TierProfileToken { address: Address; /** Tier-1 (passkey-only) ceiling. */ tier1: bigint; /** Tier-2 (+DVT/BLS) ceiling. Over `dailyLimit` is hard-blocked (needs a guardian). */ tier2: bigint; /** Hard daily cap for this token. */ dailyLimit: bigint; } /** A user-segment spending profile: native-ETH ceilings + optional per-ERC-20 ceilings. */ interface TierProfile { /** Native-ETH ceilings (wei). tier1 = passkey-only; tier2 = +DVT; over dailyLimit is hard-blocked. */ eth: { tier1: bigint; tier2: bigint; dailyLimit: bigint; }; /** Optional per-ERC-20 configs (USDC / USDT / any custom token). */ tokens?: readonly TierProfileToken[]; } /** The two halves a TierProfile resolves to: birth-bakeable InitConfig params + the native-ETH tier limits. */ interface ResolvedTierProfile { /** → `PasskeyCreateParams.dailyLimit` (native-ETH daily, baked in InitConfig at birth). */ dailyLimit: bigint; /** → `PasskeyCreateParams.initialTokens` (baked at birth). */ initialTokens: Address[]; /** → `PasskeyCreateParams.initialTokenConfigs` (baked at birth). */ initialTokenConfigs: TokenConfig[]; /** → `setTierLimits(tier1, tier2)` after deploy (native-ETH tier; folds into InitConfig once #161 lands). */ ethTierLimits: { tier1: bigint; tier2: bigint; }; } /** * Split a {@link TierProfile} into the birth-bakeable InitConfig params (native-ETH daily + all token * configs) and the native-ETH tier1/tier2 (applied via `setTierLimits` until airaccount-contract#161). * Validates each config against the contract's tiering rules. */ declare function resolveTierProfile(profile: TierProfile): ResolvedTierProfile; /** * Reference ETH-denominated profiles from aastar-sdk#266. These set only the native-ETH half; add * `tokens` (USDC/USDT/custom, in their own units) to bake per-token ceilings in the same profile. */ declare const REFERENCE_ETH_PROFILES: Readonly>; type ModuleTypeId = 1 | 2 | 3 | 4; interface InstallModuleParams { /** The deployed AirAccount address */ account: string; /** ERC-7579 module type: 1=Validator, 2=Executor, 3=Fallback, 4=Hook */ moduleTypeId: ModuleTypeId; /** Module contract address to install */ module: string; /** * Guardian slot indices (0..guardianCount-1), parallel to {@link guardianSigs}. * Required whenever guardianSigs is non-empty (v0.20.2 mixed-sig encoding): the * contract dispatches each sig to the guardian at this slot and bitmaps the slot * to prevent double-voting. Omit (with no sigs) for the sigsRequired==0 path. */ signerIdxs?: number[]; /** * Guardian signature blobs, parallel to {@link signerIdxs}: * - ECDSA guardian: 65-byte (r‖s‖v) eth-signed signature over {@link buildInstallModuleHash} * - P-256 guardian: WebAuthn assertion blob * abi.encode(authenticatorData, clientDataJSONPrefix, clientDataJSONSuffix, r, s) * * When empty, the 0-sig path is used and {@link moduleInitData} is passed raw * (backward compatible with accounts whose install threshold yields sigsRequired==0). */ guardianSigs?: string[]; /** Raw bytes passed to module.onInstall() */ moduleInitData?: string; } interface UninstallModuleParams { account: string; moduleTypeId: ModuleTypeId; module: string; /** * Guardian slot indices, parallel to {@link guardianSigs}. Uninstall requires * min(guardianCount, 2) sigs; a 0-guardian account degrades to owner-only and * may pass empty arrays. NOTE: v0.20.2 dropped module deInit data — uninstall no * longer forwards bytes to module.onUninstall(). */ signerIdxs?: number[]; /** Guardian signature blobs (see {@link InstallModuleParams.guardianSigs}). */ guardianSigs?: string[]; } /** * Build the install digest an **ECDSA** guardian must sign (AirAccount v0.20.2, * `AirAccountExtension._verifyGuardianSigByIdx`). Same digest is used for * `proposeModuleInstall` (the timelocked two-step path — identical opLabel/opData): * * innerHash = keccak256(abi.encode( * GUARDIAN_SIG_VERSION, chainId, account, "INSTALL_MODULE", * abi.encode(moduleTypeId, module, keccak256(moduleInitData), nonce) * )) * * Returns `innerHash` (NO EIP-191 prefix). The contract recovers against * `toEthSignedMessageHash(innerHash)`, so the guardian signs the returned hash as a * raw personal_sign message and viem adds the prefix. For P-256 (passkey) guardians * use {@link buildInstallModuleP256Challenge} instead. * * `nonce` is the account's current `moduleManagementNonce()` (issue #75 replay * guard — increments on every install AND uninstall). Read it on-chain via * {@link ModuleManager.readModuleNonce} immediately before collecting signatures. * * @example * const nonce = await mm.readModuleNonce(account); * const hash = buildInstallModuleHash(chainId, account, 1, module, nonce, moduleInitData); * const sig = await guardian.signMessage({ message: { raw: hash } }); */ declare function buildInstallModuleHash(chainId: number, account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint, moduleInitData?: string): string; /** * Build the uninstall digest an ECDSA guardian must sign (v0.20.2): * opData = abi.encode(moduleTypeId, module, nonce) */ declare function buildUninstallModuleHash(chainId: number, account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint): string; /** * Build the `setModuleInstallTimelock` digest an ECDSA guardian must sign when * **weakening** the timelock (lowering it, or disabling → 0). Strengthening is a * direct owner action and needs no guardian sigs (pass empty guardianSigs). * opData = abi.encode(newTimelock, nonce); opLabel = "SET_MODULE_TIMELOCK". * Weakening requires `min(guardianCount, 2)` sigs. */ declare function buildSetModuleTimelockHash(chainId: number, account: string, newTimelock: bigint, nonce: bigint): string; /** * P-256 (WebAuthn passkey) guardian challenge for `installModule`/`proposeModuleInstall` * (AirAccount v0.20.2 `_p256GuardianChallenge`). Distinct from the ECDSA digest — it folds * an extra `"P256_GUARDIAN"` domain string and is NOT EIP-191-prefixed: * keccak256(abi.encode(GUARDIAN_SIG_VERSION, chainId, account, "P256_GUARDIAN", "INSTALL_MODULE", opData)) * Use the returned bytes32 as the WebAuthn ceremony challenge; pack the resulting assertion * as `abi.encode(authenticatorData, clientDataJSONPrefix, clientDataJSONSuffix, r, s)` and pass * it in `guardianSigs` to {@link ModuleManager.encodeInstall} (mixed ECDSA/P-256 supported). */ declare function buildInstallModuleP256Challenge(chainId: number, account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint, moduleInitData?: string): string; /** P-256 guardian challenge for `uninstallModule` (see {@link buildInstallModuleP256Challenge}). */ declare function buildUninstallModuleP256Challenge(chainId: number, account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint): string; /** P-256 guardian challenge for weakening `setModuleInstallTimelock` (see {@link buildInstallModuleP256Challenge}). */ declare function buildSetModuleTimelockP256Challenge(chainId: number, account: string, newTimelock: bigint, nonce: bigint): string; /** * ModuleManager — ERC-7579 module install/uninstall helpers (AirAccount v0.20.2). * * Guardian-gated module governance moved to AirAccountExtension (fallback-routed) * and switched to a mixed ECDSA/P-256 encoding: * installModule(moduleTypeId, module, initData) * initData (sigsRequired>0): abi.encode(uint8[] signerIdxs, bytes[] sigs, bytes moduleInitData) * initData (sigsRequired==0): raw moduleInitData * uninstallModule(moduleTypeId, module, deInitData) * deInitData (always): abi.encode(uint8[] signerIdxs, bytes[] sigs) */ declare class ModuleManager { private readonly provider; private readonly chainId; constructor(provider: PublicClient, chainId: number); /** Shared install initData packing (installModule + proposeModuleInstall use the same shape). */ private packInstallInitData; /** * Encode calldata for installModule(). * Caller is responsible for submitting via UserOp (EntryPoint) or direct tx. */ encodeInstall(params: InstallModuleParams): string; /** * Encode calldata for proposeModuleInstall() — the timelocked two-step install * (issue #58 / KI-6). Same initData encoding + guardian digest as installModule * ({@link buildInstallModuleHash}); after the timelock elapses, finalize with the * core action `executeModuleInstall(moduleInitData)`. */ encodeProposeModuleInstall(params: InstallModuleParams): string; /** * Encode calldata for uninstallModule(). * deInitData is ALWAYS abi.encode(uint8[], bytes[]) — the contract decodes it * unconditionally (unlike install, there is no raw 0-sig passthrough). */ encodeUninstall(params: UninstallModuleParams): string; /** * Build the `guardianSigs` bytes for `setModuleInstallTimelock` when **weakening** * the timelock — `abi.encode(uint8[] signerIdxs, bytes[] sigs)` over * {@link buildSetModuleTimelockHash}. Pass the result as the core action's * `guardianSigs` arg. When **strengthening** (raising the timelock, or first-time set) * no guardian consensus is needed — pass `"0x"` instead (the contract never decodes it). * Weakening needs `min(guardianCount, 2)` sigs. */ encodeSetModuleTimelockGuardianSigs(signerIdxs: number[], guardianSigs: string[]): Hex; /** Read the account's current module-management nonce (issue #75 replay guard). */ readModuleNonce(account: string): Promise; /** Check if a module is currently installed on the account. */ isInstalled(account: string, moduleTypeId: ModuleTypeId, module: string): Promise; /** Return the install digest for an ECDSA guardian to sign. */ installHash(account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint, moduleInitData?: string): string; /** Return the uninstall digest for ECDSA guardians to sign. */ uninstallHash(account: string, moduleTypeId: ModuleTypeId, module: string, nonce: bigint): string; /** * Convenience: build install calldata for the standard M7 module set on the * 0-sig path (no guardian signatures). Valid only for accounts whose install * threshold yields sigsRequired==0; accounts requiring guardian consensus must * use {@link encodeInstall} with signerIdxs + guardianSigs. */ encodeInstallDefaultModules(account: string): { compositeValidator: string; tierGuardHook: string; }; } interface GrantSessionParams { /** Account that owns the session */ account: string; /** The session key address (ephemeral EOA) */ sessionKey: string; /** Expiry unix timestamp (max 7 days from now) */ expiry: number; /** address(0) = any destination allowed */ contractScope?: string; /** bytes4(0) = any selector allowed */ selectorScope?: string; /** Max calls per velocityWindow (0 = unlimited). Session struct field. */ velocityLimit?: number; /** Velocity window in seconds (0 = no window). Session struct field. */ velocityWindow?: number; /** Allowed destination addresses ([] = any). Session struct field. */ callTargets?: string[]; /** Allowed selectors ([] = any). Session struct field. */ selectorAllowlist?: string[]; /** Owner signature over buildGrantHash() — omit if calling directly from account */ ownerSig?: string; } interface SessionInfo { expiry: number; contractScope: string; selectorScope: string; revoked: boolean; velocityLimit: number; velocityWindow: number; callTargets: string[]; selectorAllowlist: string[]; active: boolean; } interface GrantP256SessionParams { /** Account that owns the session */ account: string; /** P256 public key X coordinate (0x-prefixed 32-byte hex) */ keyX: string; /** P256 public key Y coordinate (0x-prefixed 32-byte hex) */ keyY: string; /** Expiry unix timestamp (max 7 days from now) */ expiry: number; /** address(0) = any destination allowed */ contractScope?: string; /** bytes4(0) = any selector allowed */ selectorScope?: string; /** Max calls per velocityWindow (0 = unlimited). Session struct field. */ velocityLimit?: number; /** Velocity window in seconds (0 = no window). Session struct field. */ velocityWindow?: number; /** Allowed destination addresses ([] = any). Session struct field. */ callTargets?: string[]; /** Allowed selectors ([] = any). Session struct field. */ selectorAllowlist?: string[]; /** Owner signature over buildP256GrantHash() — omit if calling directly from the owner EOA */ ownerSig?: string; } /** @deprecated No AgentSessionKeyValidator is deployed (airaccount-contract v0.27.0, #282). Use a scoped M6 {@link GrantSessionParams} instead. */ interface AgentSessionConfig { expiry: number; velocityLimit: number; velocityWindow: number; callTargets: string[]; selectorAllowlist: string[]; } /** @deprecated No AgentSessionKeyValidator is deployed (#282). See {@link AgentSessionConfig}. */ interface AgentSessionInfo extends AgentSessionConfig { revoked: boolean; callCount: bigint; windowStart: bigint; } /** * SessionKeyService — manage M6 session keys on `SessionKeyValidator` (algId `0x08`): * time-limited ECDSA/P256 keys with optional contract+selector scope, for standard delegated * actions (hot wallet, automated tasks, and — via a scoped Session — agent delegation). * * The M7 "agent session" methods (grantAgentSession/delegateSession/…) are **deprecated and throw**: * airaccount-contract v0.27.0 confirmed there is no deployed `AgentSessionKeyValidator` and no distinct * agent algId (Seeder CC-16 / #282). Use the M6 methods with a scoped Session instead. */ declare class SessionKeyService { private readonly skValidator; /** * @param provider viem PublicClient for reads. * @param sessionKeyValidatorAddress the M6 `SessionKeyValidator` (algId 0x08). * @param _agentSessionKeyValidatorAddress @deprecated ignored — no AgentSessionKeyValidator is * deployed (#282). Accepted for one minor to keep the 3-arg call sites compiling. */ constructor(provider: PublicClient, sessionKeyValidatorAddress: string, _agentSessionKeyValidatorAddress?: string); /** * Build the hash that the account owner must sign to grant a session key. * Use grantSession() with this sig, or grantSessionDirect() from the account itself. */ buildGrantHash(params: Omit): Promise; /** Query an ECDSA session key state (decodes the 8-field Session tuple). */ getSession(account: string, sessionKey: string): Promise; /** Check if an ECDSA session is currently active. */ isSessionActive(account: string, sessionKey: string): Promise; /** * Encode calldata for session grant. * * - **With ownerSig** → `grantSession()` — for gasless/UserOp flows. * Owner signs the GRANT_SESSION_V2 typed hash via KMS `sign-grant-session`, * then the relayer calls `grantSession(account, key, cfg, ownerSig)` on-chain. * This is the ONLY path for ERC-4337 sponsored / gasless grant flows. * * - **Without ownerSig** → `grantSessionDirect()` — **owner EOA direct-send only**. * Since v0.17.2 round 3, `grantSessionDirect` requires `msg.sender == ownerOf(account)`. * It does NOT accept `msg.sender == account` (removed in round 3 — confused-deputy fix). * Do NOT encode this for a UserOp callData; the EntryPoint is not the owner EOA. */ encodeGrantSession(params: GrantSessionParams): string; /** Encode calldata for revokeSession(). */ encodeRevokeSession(account: string, sessionKey: string): string; /** * Build the hash that the account owner must sign to grant a P256/passkey session key. * Use grantP256Session() with this sig, or grantP256SessionDirect() from the owner EOA itself. * The owner/KMS signs this hash to authorize a gasless grantP256Session(). */ buildP256GrantHash(params: Omit): Promise; /** * Query a P256 session key state (decodes the 8-field Session tuple). * @param keyHash The keccak256 hash of (keyX, keyY) used as the on-chain session id. */ getP256Session(account: string, keyHash: string): Promise; /** Check if a P256 session is currently active. */ isP256SessionActive(account: string, keyX: string, keyY: string): Promise; /** * Encode calldata for a P256/passkey session grant. * * - **With ownerSig** → `grantP256Session()` — for gasless/UserOp flows. * Owner signs the buildP256GrantHash() digest via KMS `sign-p256-grant-session`, * then the relayer calls `grantP256Session(account, keyX, keyY, cfg, ownerSig)` on-chain. * This is the ONLY path for ERC-4337 sponsored / gasless P256 grant flows. * * - **Without ownerSig** → `grantP256SessionDirect()` — **owner EOA direct-send only**. * Since v0.17.2 round 3, `grantP256SessionDirect` requires `msg.sender == ownerOf(account)`. * It does NOT accept `msg.sender == account` (removed in round 3 — confused-deputy fix). * Do NOT encode this for a UserOp callData; the EntryPoint is not the owner EOA. */ encodeGrantP256Session(params: GrantP256SessionParams): string; /** Encode calldata for revokeP256Session(). */ encodeRevokeP256Session(account: string, keyX: string, keyY: string): string; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. Use {@link encodeGrantSession} with a scoped Session. */ encodeGrantAgentSession(_sessionKey: string, _cfg: AgentSessionConfig): string; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. */ encodeDelegateSession(_account: string, _subKey: string, _subCfg: AgentSessionConfig): string; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. Use {@link encodeRevokeSession}. */ encodeRevokeAgentSession(_sessionKey: string): string; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. Use {@link getSession}. */ getAgentSession(_account: string, _sessionKey: string): Promise; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. Use {@link isSessionActive}. */ isAgentSessionActive(_account: string, _sessionKey: string): Promise; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. */ getSessionKeyOwner(_sessionKey: string): Promise; /** @deprecated No AgentSessionKeyValidator is deployed (#282) — throws. */ getDelegatedBy(_account: string, _subKey: string): Promise; } /** * Pack a secp256k1 session key signature into the 106-byte UserOp.signature format. * * Layout: [0x08][account(20)][sessionKey(20)][r(32)][s(32)][v(1)] * * @param account - The AirAccount address (20 bytes, with or without 0x) * @param sessionKey - The ephemeral EOA session key address (20 bytes) * @param signature - 65-byte hex signature from KMS sign-grant-session (R||S||V) * @returns 106-byte hex string (0x-prefixed) suitable as UserOp.signature */ declare function packSecp256k1SessionSignature(account: string, sessionKey: string, signature: string): string; /** * Pack a P256 session key signature into the 149-byte UserOp.signature format. * * Layout: [0x08][account(20)][keyX(32)][keyY(32)][r(32)][s(32)] * * @param account - The AirAccount address (20 bytes) * @param keyX - P256 public key X coordinate (32 bytes hex, without 0x) * @param keyY - P256 public key Y coordinate (32 bytes hex, without 0x) * @param signature - 64-byte hex signature from KMS sign-p256-grant-session (R||S, no V) * @returns 149-byte hex string (0x-prefixed) suitable as UserOp.signature */ declare function packP256SessionSignature(account: string, keyX: string, keyY: string, signature: string): string; type TierLevel = 1 | 2 | 3; interface GuardState { /** ETH daily limit in wei */ dailyLimit: bigint; /** ETH already spent today in wei */ todaySpent: bigint; /** ETH remaining for today in wei */ remaining: bigint; /** Current tier based on spent amount */ currentTier: TierLevel; /** Tier 1 max spend threshold in wei (single sig) */ tier1Limit: bigint; /** Tier 2 max spend threshold in wei (dual sig) */ tier2Limit: bigint; /** Minimum daily limit floor (cannot decrease below this) */ minDailyLimit: bigint; /** Guard contract address */ guardAddress: string; } interface TokenGuardState { token: string; todaySpent: bigint; dailyLimit: bigint; remaining: bigint; currentTier: TierLevel; tier1Limit: bigint; tier2Limit: bigint; } /** * GuardStateReader — F6: read AAStarGlobalGuard spending state. * * Enables UI components to show: * - Daily spend progress bar * - Current required tier (T1/T2/T3) for next transaction * - Per-token limits and remaining allowances */ declare class GuardStateReader { private readonly provider; constructor(provider: PublicClient); private accountContract; private guardContract; /** * Read the full ETH guard state for an account. * Returns null if the account has no guard (dailyLimit=0). */ getGuardState(accountAddress: string): Promise; /** * Read per-token guard state. * Returns null if the token is not configured on the guard. */ getTokenGuardState(accountAddress: string, token: string): Promise; /** * Determine the minimum tier required to send a given ETH amount. * Useful for showing "this transfer needs 2 signatures" before submission. */ requiredTierForAmount(accountAddress: string, amountWei: bigint): Promise; /** * Check if a given algorithm ID is approved on the guard. */ isAlgorithmApproved(accountAddress: string, algId: number): Promise; } interface OapdConfig { /** Account owner address */ owner: string; /** DApp identifier — use the DApp's domain or contract address */ dappId: string; /** Factory address (defaults to M7 Sepolia) */ factoryAddress?: string; /** * InitConfig for the OAPD account. * Typically lower daily limits than the main account. */ initConfig: { guardians: [string, string, string]; guardianP256X: [string, string, string]; guardianP256Y: [string, string, string]; dailyLimit: bigint; approvedAlgIds: number[]; minDailyLimit: bigint; initialTokens: string[]; initialTokenConfigs: Array<{ tier1Limit: bigint; tier2Limit: bigint; dailyLimit: bigint; }>; }; } /** * Compute the numeric salt for an OAPD address. * salt = uint256(keccak256(abi.encodePacked(owner, dappId))) */ declare function computeOapdSalt(owner: string, dappId: string): bigint; /** * Predict the counterfactual OAPD address without deploying. * Uses the factory's getAddress() view function. */ declare function getOapdAddress(provider: PublicClient, config: OapdConfig): Promise; /** * Get the OAPD address and its ERC-7828 chain-qualified identifier. */ declare function getOapdAddressWithChainId(provider: PublicClient, config: OapdConfig): Promise<{ address: string; chainQualified: string; }>; /** * Check if an OAPD account has been deployed yet. */ declare function isOapdDeployed(provider: PublicClient, config: OapdConfig): Promise; /** 4-byte selector of `executeUserOp((PackedUserOperation),bytes32)`. */ declare const EXECUTE_USER_OP_SELECTOR: `0x${string}`; /** 4-byte selector of `execute(address,uint256,bytes)`. */ declare const EXECUTE_SELECTOR: `0x${string}`; /** 4-byte selector of `executeBatch(address[],uint256[],bytes[])`. */ declare const EXECUTE_BATCH_SELECTOR: `0x${string}`; /** * Wrap inner `execute()` / `executeBatch()` callData with the `executeUserOp` selector so a * guard-enabled (v0.17.2-beta.4) account routes the bundler UserOp through `executeUserOp`. * * Only `execute` / `executeBatch` may be wrapped — the account reverts * `UnsupportedInnerSelector` for anything else (including a nested `executeUserOp`). * * Owner-direct (non-bundler) `execute()` does NOT need this; no-guard accounts can submit * bare callData. Use this only when building a bundler UserOp for a guard-enabled account. * * @param innerCallData ABI-encoded `execute`/`executeBatch` calldata (0x-prefixed) * @returns `executeUserOp.selector ++ innerCallData` * @throws if `innerCallData` is not an `execute`/`executeBatch` call */ declare function wrapExecuteUserOp(innerCallData: string): string; /** True if callData is already wrapped with the executeUserOp selector. */ declare function isExecuteUserOpWrapped(callData: string): boolean; declare const L2_TYPE: { readonly OPTIMISM: 1; readonly ARBITRUM: 2; }; type L2Type = (typeof L2_TYPE)[keyof typeof L2_TYPE]; interface PendingExit { target: string; value: bigint; data: string; proposedAt: bigint; approvalBitmap: bigint; guardians: [string, string, string]; } /** * A viem client capable of backing the ForceExit contract. A `PublicClient` * alone enables the on-chain reads; a `WalletClient` (or the `{ public, wallet }` * pair) is required for the state-changing `proposeForceExit`/`approveForceExit`/ * etc. transactions. This replaces the old `ethers.Provider | ethers.Signer` * argument (provider = reads, signer = reads + writes). */ type ForceExitClient = PublicClient | WalletClient | { public: PublicClient; wallet: WalletClient; }; /** * ForceExitService — typed wrappers for ForceExitModule ERC-7579 emergency L2→L1 exit. * * Flow: * 1. Owner installs module via account.installModule(2, forceExitModuleAddr, encodeOnInstall(L2_TYPE.OPTIMISM)) * 2. Any party calls proposeForceExit(target, value, data) to submit a bridge-out proposal * 3. 2-of-3 guardians each call approveForceExit(account, guardianSig) within their window * 4. Anyone calls executeForceExit(account) once threshold is met — triggers L2→L1 bridge call * * The module is an ERC-7579 Executor (moduleTypeId=2) — call installModule on the account, not here. */ declare class ForceExitService { private readonly moduleAddress; private readonly contract; constructor(moduleAddress: string, client: ForceExitClient); isInitialized(smartAccount: string): Promise; getPendingExit(account: string): Promise; getAccountL2Type(account: string): Promise; getApprovalThreshold(): Promise; getModuleVersion(): Promise; /** * Encode onInstall calldata for installModule() call on the smart account. * Must be submitted by the account owner, with moduleTypeId=2 (EXECUTOR). * * @param l2Type - L2_TYPE.OPTIMISM (1) or L2_TYPE.ARBITRUM (2) * @example * const calldata = forceExit.encodeOnInstall(L2_TYPE.OPTIMISM); * // account.installModule(2, forceExitModuleAddress, calldata) */ encodeOnInstall(l2Type: L2Type): string; encodeOnUninstall(): string; /** * Encode calldata for proposeForceExit — the exit payload to bridge out of L2. * `target` is the L2→L1 bridge contract; `data` is the bridge call payload. */ encodeProposeForceExit(target: string, value: bigint, data: string): string; /** * Encode calldata for approveForceExit — guardian signs off on the pending proposal. * `guardianSig` must be an EIP-191 personal_sign over the proposal hash. */ encodeApproveForceExit(account: string, guardianSig: string): string; encodeExecuteForceExit(account: string): string; encodeCancelForceExit(account: string): string; proposeForceExit(target: string, value: bigint, data: string): Promise; approveForceExit(account: string, guardianSig: string): Promise; executeForceExit(account: string): Promise; cancelForceExit(account: string): Promise; } /** * RECOVERY_THRESHOLD — number of distinct guardian approvals required to recover * (or to cancel a recovery). The contract hard-codes `RECOVERY_THRESHOLD = 2` * against a maximum of 3 guardians, i.e. a 2-of-3 social-recovery scheme. * * Source of truth: `AAStarAirAccountBase.RECOVERY_THRESHOLD` (internal constant). */ declare const RECOVERY_THRESHOLD = 2; /** * MAX_GUARDIANS — the account stores at most 3 guardians (packed slots 12-14). * Source: `AAStarAirAccountBase.addGuardian` (`_guardianCount >= 3` reverts). */ declare const MAX_GUARDIANS = 3; /** * RECOVERY_TIMELOCK_SECONDS — delay between `proposeRecovery` and the earliest * `executeRecovery`. The contract hard-codes `RECOVERY_TIMELOCK = 2 days` * (172800 seconds). * * NOTE: the prose in `docs/abi/capabilities.md` says "72h"; the deployed * contract uses 2 days (48h). The on-chain constant is authoritative. * * Source of truth: `AAStarAirAccountBase.RECOVERY_TIMELOCK` (internal constant). */ declare const RECOVERY_TIMELOCK_SECONDS: bigint; /** * Decoded view of the account's `activeRecovery()` struct (RecoveryProposal). * * On-chain layout (`AAStarAgentStorageLayout.RecoveryProposal`): * - newOwner : proposed new owner (address(0) ⇒ no active recovery) * - proposedAt : block.timestamp when the recovery was proposed * - approvalBitmap : bit i set ⇒ guardian[i] approved (2-of-3 to execute) * - cancellationBitmap : bit i set ⇒ guardian[i] voted to cancel (2-of-3 to cancel) * * The remaining fields are SDK-side conveniences derived from those values. */ interface ActiveRecovery { /** Proposed new owner. `0x0000…0000` means there is no active recovery. */ newOwner: string; /** `block.timestamp` at which the recovery was proposed (seconds). */ proposedAt: bigint; /** Bitmap of guardian approvals (bit i ⇒ guardian[i] approved). */ approvalBitmap: bigint; /** Bitmap of guardian cancel votes (bit i ⇒ guardian[i] voted to cancel). */ cancellationBitmap: bigint; /** Number of distinct guardian approvals (popcount of `approvalBitmap`). */ approvalCount: number; /** Number of distinct guardian cancel votes (popcount of `cancellationBitmap`). */ cancellationCount: number; /** Earliest timestamp at which `executeRecovery` may succeed (`proposedAt + timelock`). */ executeAfter: bigint; /** True when a recovery is currently active (`newOwner != address(0)`). */ isActive: boolean; } /** * RecoveryService — typed wrappers for AirAccount's on-chain social / guardian * recovery (capability F28). Unlike `ForceExitService` (an ERC-7579 module), * these functions live directly on the account contract, so every encoded * calldata below is meant to be submitted **to the account address itself** * (via a direct tx or a UserOp). * * ## Threshold & timelock * 2-of-3 guardians ({@link RECOVERY_THRESHOLD} of {@link MAX_GUARDIANS}), with a * {@link RECOVERY_TIMELOCK_SECONDS} (2-day) delay before execution. * * ## Lifecycle & who-can-call * 1. `addGuardian(guardian)` — **owner only**. Registers a guardian (max 3). * 2. `proposeRecovery(newOwner)` — **any guardian**. Starts the timelock and * records the proposer's approval bit (counts as the first of 2 approvals). * 3. `approveRecovery()` — **another guardian**. Sets its approval bit; once * 2-of-3 approvals are reached the threshold is satisfied. * 4. `executeRecovery()` — **anyone**, but only after the timelock has elapsed * AND the approval threshold is met. Rotates `owner` to `newOwner`. * 5. `cancelRecovery()` — **guardians only** (2-of-3 votes). The owner cannot * cancel: a thief who stole the owner key must not be able to block a * legitimate recovery. Each guardian votes independently. * 6. `removeGuardian(index, guardianSigs)` — **owner**, but additionally * requires >= {@link RECOVERY_THRESHOLD} guardian signatures over the * removal hash (and cannot drop below 2 guardians). * * Guardian signatures are domain-separated per operation (the contract hashes a * per-op label such as "REMOVE_GUARDIAN") to prevent cross-operation replay. * * ## Re-proposing * A new proposal reverts with `RecoveryAlreadyActive` while one is pending. The * docs reference a `clearStaleRecovery()` helper, but that function is NOT * present in the deployed V7 ABI/contract — a stale proposal must instead be * cleared via guardian `cancelRecovery()` votes before re-proposing. */ declare class RecoveryService { private readonly client; /** * @param client viem read client (was `ethers.Provider | ethers.Signer`). Only * on-chain reads are performed here; calldata encoders are pure and never * touch the client. */ constructor(client: PublicClient); /** * Encode `addGuardian(guardian)` calldata. **Owner only.** * Registers a recovery guardian; reverts once 3 guardians are set, or if the * guardian is `address(0)`, the owner, or already registered. */ encodeAddGuardian(guardian: string): string; /** * Encode `removeGuardian(index, guardianSigs)` calldata. **Owner only**, and * requires >= {@link RECOVERY_THRESHOLD} guardian signatures over the removal * hash. Cannot remove while a recovery is active, nor drop below 2 guardians. * * @param index Guardian slot to remove (0-indexed). * @param guardianSigs EIP-191 guardian signatures over the removal hash. */ encodeRemoveGuardian(index: number, guardianSigs: string[]): string; /** * Build the RAW (un-prefixed) challenge hash that each guardian must sign to * authorize `removeGuardian(index, ...)` / `removeGuardianWithMixedSigs(...)`. * * ## v0.20.0 breaking change (#120 final-review [HIGH], spec §6.4) * The signed `opData` changed from `abi.encode(nonce, guardianToRemove)` to * `abi.encode(nonce, index, guardianToRemove, p256X, p256Y)` — it now binds the * SLOT INDEX and the slot's P-256 key. Because P-256 guardians all share the * sentinel address, the old 2-field payload was identical for every P-256 slot, * so a signature collected to remove slot A could be replayed to remove slot B * (or survive a key rotation). The new 5-field payload affects EVERY removal, * including the plain ECDSA path: for an ECDSA slot `p256X`/`p256Y` are both * `bytes32(0)`, but the payload STRUCTURE (extra `index` + two key words) still * changed, so the ECDSA `removeGuardian` signing payload MUST use this encoding. * * Hash construction (matches `AAStarAirAccountBase._guardianOpHash`): * ``` * opData = abi.encode(uint256 nonce, uint8 index, address guardianToRemove, * bytes32 p256X, bytes32 p256Y) * challenge = keccak256(abi.encode(uint8 GUARDIAN_SIG_VERSION, uint256 chainId, * address account, "REMOVE_GUARDIAN", bytes opData)) * ``` * The contract additionally applies `toEthSignedMessageHash()` before * `ecrecover`, so this returns the RAW inner hash and each guardian signs it via * `personal_sign` / `signMessage({ raw: hash })` (which adds the EIP-191 prefix). * Do NOT pre-apply the prefix here (mirrors `buildGuardianAcceptanceHash`). * * @param account The AirAccount address whose guardian is being removed. * @param chainId EVM chain id (bound into the challenge). * @param removalNonce Current `_guardianRemovalNonce` — there is no on-chain * getter (internal storage slot 15), so the caller tracks * it (starts at 0, increments once per successful removal). * @param index Guardian slot being removed (0-indexed, < guardianCount). * @param guardianToRemove Address stored in that slot (a P-256 slot stores * {@link P256_GUARDIAN_SENTINEL}; an ECDSA slot stores the EOA). * @param p256X Slot's P-256 x coordinate; `bytes32(0)` for an ECDSA slot (default). * @param p256Y Slot's P-256 y coordinate; `bytes32(0)` for an ECDSA slot (default). * @returns raw hex keccak256 challenge — guardians sign it with `personal_sign`. */ buildRemoveGuardianHash(args: { account: string; chainId: number | bigint; removalNonce: bigint; index: number; guardianToRemove: string; p256X?: Hex; p256Y?: Hex; }): Hex; /** * Encode `proposeRecovery(newOwner)` calldata. **Any guardian** may call. * Starts the {@link RECOVERY_TIMELOCK_SECONDS} timelock and records the * proposer's approval (1 of {@link RECOVERY_THRESHOLD}). */ encodeProposeRecovery(newOwner: string): string; /** * Encode `approveRecovery()` calldata. **Another guardian** approves the * active proposal, setting its bit in `approvalBitmap`. */ encodeApproveRecovery(): string; /** * Encode `cancelRecovery()` calldata. **Guardians only** — each call is one * vote; recovery is dropped once {@link RECOVERY_THRESHOLD} cancel votes are * reached. The owner cannot cancel. */ encodeCancelRecovery(): string; /** * Encode `executeRecovery()` calldata. **Anyone** may call, but it only * succeeds once the timelock has elapsed and the approval threshold is met. * Rotates the account owner to the proposed `newOwner`. */ encodeExecuteRecovery(): string; /** * Read and decode the account's `activeRecovery()` struct. * Returns derived `approvalCount`, `cancellationCount`, `executeAfter`, and * `isActive` alongside the raw fields. * * @param account The AirAccount address to query. */ getActiveRecovery(account: string): Promise; /** * Read the number of registered guardians via `guardianCount()`. * * @param account The AirAccount address to query. */ getGuardianCount(account: string): Promise; /** * Read the full guardian address list. * * The V7 account exposes positional `guardians(uint256 i)` (3 packed slots) plus * `guardianCount()` — there is no single `getGuardians()` getter on the account * (that exists only on `AirAccountDelegate`, the EIP-7702 path). This reads slots * `0..guardianCount-1` and returns the non-zero guardian addresses. * * @param account The AirAccount address to query. */ getGuardians(account: string): Promise; } declare const AIR_ACCOUNT_DELEGATE_ADDRESS = "0x8603AAF6C3f07fdae810B323c95a198D796EC52E"; interface DelegateInitParams { /** Guardian 1 address */ guardian1: string; /** EIP-712 acceptance signature from guardian 1 */ guardian1Sig: string; /** Guardian 2 address */ guardian2: string; /** EIP-712 acceptance signature from guardian 2 */ guardian2Sig: string; /** Daily ETH spend limit in wei */ dailyLimit: bigint; } interface EIP7702Authorization { /** Chain ID (e.g. 11155111 for Sepolia) */ chainId: number; /** The delegation target — AirAccountDelegate singleton address */ address: string; /** EOA nonce at time of signing */ nonce: bigint; /** Signature over the EIP-7702 authorization hash (65 bytes, R||S||V) */ signature: string; } /** * EIP7702DelegateService — Path A: SDK payload construction for AirAccountDelegate. * * Path A applies when the integrator controls the private key (KMS, server-side signer, * embedded wallet). The EOA signs a SET_CODE authorization offline; the integrator's relay * submits a Type-4 transaction to activate the delegation. * * This service does NOT submit transactions — it produces signed payloads for relay. * * Deployed address: AirAccountDelegate singleton at AIR_ACCOUNT_DELEGATE_ADDRESS (Sepolia). * The user's EOA address does NOT change — only its bytecode pointer changes to 0xef0100||addr. * * Usage: * 1. Build SET_CODE authorization payload (call signer.signAuthorization externally — viem) * 2. Build initialize() calldata for the first UserOp / direct tx * 3. Submit both via integrator's relay */ declare class EIP7702DelegateService { private readonly delegateAddress; /** Parsed ABI (loose viem `Abi` shape) used for encoding calldata and on-chain reads. */ private readonly abi; /** Optional viem read client (was `ethers.Provider | ethers.Signer`). Required only for on-chain reads. */ private readonly client?; constructor(delegateAddress?: string, client?: PublicClient); /** * Encode initialize() calldata for the first post-delegation UserOp. * Must be the callData of a UserOp sent immediately after the SET_CODE delegation activates. * Guardian acceptance sigs follow the same EIP-712 scheme as AirAccountV7 createAccount. */ encodeInitialize(params: DelegateInitParams): string; encodeExecute(dest: string, value: bigint, data: string): string; encodeExecuteBatch(dests: string[], values: bigint[], datas: string[]): string; /** * Compute the EIP-7702 SET_CODE authorization hash that the EOA must sign. * * Hash = keccak256(0x05 || RLP([chainId, address, nonce])) * * This is the hash the private key signs to delegate code execution to * AirAccountDelegate. Use this hash with your KMS sign-hash endpoint or * local viem account. * * @param chainId - Target chain ID (11155111 for Sepolia) * @param nonce - EOA's current transaction nonce * @returns 32-byte hash (0x-prefixed) ready for signing */ buildAuthorizationHash(chainId: number, nonce: bigint): string; /** * Build the full EIP-7702 authorization object for relay submission. * The caller must sign `buildAuthorizationHash()` externally and pass the result here. * * @param chainId - Target chain ID * @param nonce - EOA's current nonce * @param signature - 65-byte ECDSA signature (R||S||V) over the authorization hash */ buildAuthorization(chainId: number, nonce: bigint, signature: string): EIP7702Authorization; /** * Verify that a signature is a valid EIP-7702 authorization for the given EOA address. * Recovers the signer from the authorization hash and checks it matches `eoa`. * * NOTE: now async — viem's `recoverAddress` is asynchronous (ethers' was sync). */ verifyAuthorization(eoa: string, chainId: number, nonce: bigint, signature: string): Promise; isInitialized(eoa: string): Promise; getOwner(eoa: string): Promise; getGuardians(eoa: string): Promise<[string, string, string]>; } /** Timelock that must elapse after a proposal before executeWeightChange() succeeds (WEIGHT_CHANGE_TIMELOCK = 2 days). */ declare const WEIGHT_CHANGE_TIMELOCK_SECONDS: number; /** Guardian approvals required to execute a pending weight change (WEIGHT_CHANGE_THRESHOLD = 2-of-3). */ declare const WEIGHT_CHANGE_THRESHOLD = 2; /** A proposal expires this long after it is proposed; approvals/execution after expiry revert (WEIGHT_CHANGE_EXPIRY = 30 days). */ declare const WEIGHT_CHANGE_EXPIRY_SECONDS: number; /** * WeightConfig — the weighted multi-signature policy for an AAStarAirAccount (algId 0x07). * * Each signer type / guardian contributes its weight; a transaction is authorized when the * summed weight of present signatures meets the relevant tier threshold. tier1 is the base * (required, non-zero) threshold; tier2/tier3 gate higher-value operations and, when set, * must be monotonically non-decreasing (tier1 <= tier2 <= tier3). * * Field order MUST match the on-chain struct exactly (see AAStarAirAccountV7.json): * passkeyWeight, ecdsaWeight, blsWeight, guardian0Weight, guardian1Weight, guardian2Weight, * _padding, tier1Threshold, tier2Threshold, tier3Threshold. */ interface WeightConfig { /** Weight granted by a valid passkey (P256/WebAuthn) signature. */ passkeyWeight: number; /** Weight granted by a valid ECDSA (secp256k1 owner) signature. */ ecdsaWeight: number; /** Weight granted by a valid BLS signature. */ blsWeight: number; /** Weight granted by guardian slot 0. */ guardian0Weight: number; /** Weight granted by guardian slot 1. */ guardian1Weight: number; /** Weight granted by guardian slot 2. */ guardian2Weight: number; /** Reserved padding byte (storage packing); keep 0. */ _padding: number; /** Base threshold; must be non-zero and strictly greater than every individual weight. */ tier1Threshold: number; /** Tier-2 threshold (0 = disabled); when set must be >= tier1Threshold. */ tier2Threshold: number; /** Tier-3 threshold (0 = disabled); when set requires tier2 set and must be >= tier2Threshold. */ tier3Threshold: number; } /** A pending weight-change proposal awaiting guardian approval + timelock. */ interface PendingWeightChange { /** The proposed new WeightConfig. */ proposed: WeightConfig; /** Unix timestamp when the proposal was created; 0 means no active proposal. */ proposedAt: bigint; /** Bitmap of guardian indices that have approved (bit i set => guardian i approved). */ approvalBitmap: bigint; } /** * WeightedSignatureService — typed wrappers for AAStarAirAccount weighted-signature * governance (algId 0x07). * * Governance model: * - setWeightConfig(config): OWNER only. Used for the first-time config and for * *strengthening* changes (no individual weight or tier threshold decreased). * Reverts WeakeningRequiresProposal-equivalent path is enforced by the contract: * a weakening passed here reverts; route weakenings through proposeWeightChange. * Also reverts if a proposal is already pending (WeightChangePending). * - proposeWeightChange(config): OWNER only. Required when the new config *weakens* * security (lowers any weight or threshold). Opens a guardian-approved proposal. * - approveWeightChange(): GUARDIAN only (any of the 3 guardian slots). Each guardian * may approve once; approvals are tracked in approvalBitmap. * - executeWeightChange(): ANYONE, but only succeeds once BOTH conditions hold: * (1) approvals >= WEIGHT_CHANGE_THRESHOLD (2-of-3 guardians), and * (2) WEIGHT_CHANGE_TIMELOCK (2 days) has elapsed since proposedAt. * A proposal also expires after WEIGHT_CHANGE_EXPIRY (30 days). * - cancelWeightChange(): OWNER or any GUARDIAN may cancel a pending proposal. * * Unlike ForceExitService (an ERC-7579 module), these calls target the ACCOUNT itself. * Construct with the account address; encode* methods return calldata for a UserOp or * direct tx, and reads use the contract directly. */ declare class WeightedSignatureService { private readonly accountAddress; private readonly client; private readonly address; constructor(accountAddress: string, client: PublicClient); /** Read the account's current active WeightConfig. */ getWeightConfig(): Promise; /** * Read the pending weight-change proposal. When `proposedAt === 0n` there is no * active proposal (the returned `proposed` config will be all zeros). */ getPendingWeightChange(): Promise; /** * Encode setWeightConfig calldata. OWNER only; for first-time setup or strengthening. * Weakening an existing config must go through encodeProposeWeightChange instead. */ encodeSetWeightConfig(config: WeightConfig): Hex; /** * Encode proposeWeightChange calldata. OWNER only; opens a guardian-governed proposal * (required for any weakening). Subject to 2-of-3 approval + 2-day timelock before execute. */ encodeProposeWeightChange(config: WeightConfig): Hex; /** Encode approveWeightChange calldata. GUARDIAN only; each guardian may approve once. */ encodeApproveWeightChange(): Hex; /** Encode cancelWeightChange calldata. OWNER or any GUARDIAN may cancel a pending proposal. */ encodeCancelWeightChange(): Hex; /** * Encode executeWeightChange calldata. Callable by anyone, but only succeeds once the * threshold (2-of-3) and timelock (2 days) are both satisfied and the proposal has not expired. */ encodeExecuteWeightChange(): Hex; } interface CreateAgentAccountParams { /** The agent's own signing key (EOA controlled by the agent runtime / KMS). */ agentKey: string; /** ERC-8004-style agent identifier (bytes32) binding this account to an off-chain identity. */ agentId: string; /** The human guardian (guardian2) co-owning the agent account for recovery. */ guardian2: string; /** Guardian2's acceptance signature over the creation hash (EIP-191). */ guardian2Sig: string; /** The agent key's acceptance signature over the creation hash (EIP-191). */ agentKeySig: string; /** Unix timestamp (uint48) after which the signatures are rejected. */ deadline: bigint | number; /** Daily transfer limit in wei (on-chain guard enforcement; V7 requires > 0). */ dailyLimit: bigint; } /** * AgentRegistryService — typed wrappers for the AAStar AgentRegistry contract plus the * AAStarAirAccountFactoryV7 agent-account creation helpers. * * **Two contracts, two responsibilities:** * * 1. AgentRegistry (constructor `registryAddress`) — the canonical map of agent wallet → * human owner used by SuperPaymaster to authorise gasless sponsorship. The factory calls * `markValid`/registers the agent at deployment; humans manage the binding afterwards via * `registerAgent` / `revokeAgent` / `deregisterAgent`. * * 2. AAStarAirAccountFactoryV7 — deploys an agent-owned AirAccount. `encodeCreateAgentAccount` * targets the factory (NOT the registry); `getAgentAccountAddress` predicts the CREATE2 * address before deployment. * * All `encode*` methods return ABI-encoded calldata ready for a UserOp (gasless) or a direct * owner transaction. Read methods require a viem `PublicClient`. */ declare class AgentRegistryService { private readonly registryAddress; private readonly client; /** * @param client viem PublicClient for on-chain reads (e.g. `ethereum.getProvider()`). * @param registryAddress deployed AgentRegistry contract address. */ constructor(client: PublicClient, registryAddress: string); /** Encode `account.execute(registry, 0, registerAgent(agentWallet, agentWalletSig))`. */ encodeRegisterAgentViaAccount(agentWallet: string, agentWalletSig: string): string; /** Encode `account.execute(registry, 0, revokeAgent(agentWallet))`. */ encodeRevokeAgentViaAccount(agentWallet: string): string; /** * Encode calldata for `registerAgent(agentWallet, agentWalletSig)`. * * Binds `agentWallet` to the caller (the human owner). `agentWalletSig` must be the agent * wallet's EIP-191 signature proving control of the key. Reverts with * `SelfRegistrationForbidden` if the caller registers itself, or `AgentAlreadyRegistered` * if the wallet is already bound. */ encodeRegisterAgent(agentWallet: string, agentWalletSig: string): string; /** * Encode calldata for `revokeAgent(agentWallet)`. * * Owner-initiated revocation of a previously registered agent wallet. Caller must be the * agent's human owner (else `NotAgentOwner`). */ encodeRevokeAgent(agentWallet: string): string; /** * Encode calldata for `deregisterAgent(agentWallet)`. * * Removes the agent wallet from the registry (full deregistration, distinct from the * lighter-weight `revokeAgent`). Caller must be the agent's human owner. */ encodeDeregisterAgent(agentWallet: string): string; /** Whether `agentWallet` is currently registered in the registry. */ isRegisteredAgent(agentWallet: string): Promise; /** Whether `account` has been marked valid (e.g. an AirAccount minted by the bound factory). */ isValidAccount(account: string): Promise; /** The human owner bound to `agentWallet` (ZeroAddress if unregistered). */ getHumanOwner(agentWallet: string): Promise; /** Number of agents registered under `owner`. */ getAgentCount(owner: string): Promise; /** The agent wallet at `index` in `owner`'s agent list. */ getAgentByIndex(owner: string, index: bigint | number): Promise; /** Full list of agent wallets registered under `humanOwner`. */ getAgents(humanOwner: string): Promise; /** * Paginated slice of `owner`'s agent wallets: `count` entries starting at `start`. * The contract clamps `count` to the remaining length, so the returned array may be shorter. */ getAgentsPage(owner: string, start: bigint | number, count: bigint | number): Promise; /** Raw `agentWalletOwner` mapping read (agentWallet → owner). */ agentWalletOwner(agentWallet: string): Promise; /** Raw `ownerAgents` array read (owner, index → agentWallet). */ ownerAgents(owner: string, index: bigint | number): Promise; /** * Encode calldata for the factory's `createAgentAccount(...)`. * * Targets the AAStarAirAccountFactoryV7, NOT the AgentRegistry. The factory deploys the agent * AirAccount and registers it in the bound AgentRegistry in one transaction. Submit this * calldata to the factory address (direct tx or via a relayer). */ encodeCreateAgentAccount(params: CreateAgentAccountParams): string; /** * Encode calldata for the factory's `setAgentRegistry(_agentRegistry)` (factory-admin only). */ encodeSetAgentRegistry(agentRegistry: string): string; /** * Predict the CREATE2 address of an agent account via the factory's `getAgentAddress(...)`. * * @param factoryAddress AAStarAirAccountFactoryV7 address. * @param humanOwner the human guardian/owner co-owning the agent account. * @param agentKey the agent's signing key. * @param agentId the bytes32 agent identifier. */ getAgentAccountAddress(factoryAddress: string, humanOwner: string, agentKey: string, agentId: string): Promise; /** Read the AgentRegistry address currently bound to the factory. */ getFactoryAgentRegistry(factoryAddress: string): Promise; } declare const ERC8004_ADDRESSES: { readonly mainnet: { readonly identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"; readonly reputationRegistry: "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"; readonly validationRegistry: "0x8004Cc8439f36fd5F9F049D9fF86523Df6dAAB58"; }; readonly testnet: { readonly identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e"; readonly reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"; readonly validationRegistry: "0x8004Cb1BF31DAf7788923b405b754f57acEB4272"; }; }; declare function erc8004AddressesForChain(chainId: number): (typeof ERC8004_ADDRESSES)["mainnet"] | (typeof ERC8004_ADDRESSES)["testnet"]; interface SetAgentWalletParams { agentId: bigint; agentWallet: string; /** AAStar AgentRegistry contract address (SuperPaymaster-facing, NOT the official ERC-8004 registry) */ agentRegistry: string; /** Signature from the agent wallet proving ownership */ agentWalletSig: string; } interface MintAgentIdentityParams { /** Must be the official ERC-8004 identity registry for this chain */ identityRegistry: string; /** Agent metadata URI (ERC-721 tokenURI) */ agentURI: string; } interface BindERC8004AgentWalletParams { /** Must be the official ERC-8004 identity registry for this chain */ identityRegistry: string; agentId: bigint; agentWallet: string; /** Unix timestamp — signature becomes invalid after this deadline */ deadline: bigint; /** Signature authorising the wallet binding, signed by the identity registry's expected signer */ signature: string; } interface SubmitAgentReputationParams { /** Must be the official ERC-8004 reputation registry for this chain */ reputationRegistry: string; agentId: bigint; value: bigint; valueDecimals: number; tag1: string; tag2: string; endpoint: string; feedbackURI: string; feedbackHash: string; } interface QueryAgentReputationParams { /** Must be the official ERC-8004 reputation registry for this chain */ reputationRegistry: string; agentId: bigint; clientAddresses: string[]; tag1: string; tag2: string; } interface AgentReputationSummary { count: bigint; summaryValue: bigint; summaryDecimals: number; } /** * ERC8004Service — TypeScript wrappers for AirAccount's ERC-8004 "Trustless Agents" functions. * * **Two distinct registration paths:** * * 1. `encodeSetAgentWallet` — AAStar/SuperPaymaster path. * Registers the agent wallet in the AAStar AgentRegistry contract. Use this when you want * SuperPaymaster to recognise the agent for gasless sponsorship. The `agentRegistry` argument * is the deployed AgentRegistry address, NOT the official ERC-8004 IdentityRegistry. * * 2. `encodeMintAgentIdentity` + `encodeBindERC8004AgentWallet` — official ERC-8004 path. * Mints an ERC-721 identity NFT in the official ERC-8004 IdentityRegistry and binds an * execution wallet to it. The registry address MUST be from `erc8004AddressesForChain()`. * These calls revert on-chain if the wrong registry address is supplied. * * All `encode*` methods return ABI-encoded calldata ready to be submitted via UserOp (gasless) * or a direct owner transaction. The calldata targets the AirAccount address — the account's * fallback delegates to AirAccountExtension for these selectors. */ declare class ERC8004Service { private readonly abi; private readonly provider?; constructor(provider?: PublicClient); /** * Build a read-only viem contract bound to the account address. The ABI is loaded from * human-readable signatures via `parseAbi` (loose `Abi`), so `read` methods are indexed by * name and return `unknown` — cast at the call site. Mirrors the dynamic surface that * `ethers.Contract` previously exposed. Caller must ensure `this.provider` is set. */ private contractAt; /** * Encode calldata for `setAgentWallet`. * * Registers `agentWallet` in the AAStar AgentRegistry (SuperPaymaster-facing). This is the * correct path when the goal is SuperPaymaster gasless sponsorship for the agent. * * **Not** a call to the official ERC-8004 IdentityRegistry. Use `encodeMintAgentIdentity` * + `encodeBindERC8004AgentWallet` for the ERC-8004 standard path. * * Callable: owner EOA direct tx OR via UserOp (gasless). */ encodeSetAgentWallet(params: SetAgentWalletParams): string; /** * Encode calldata for `mintAgentIdentity`. * * Mints an ERC-721 agent identity NFT in the official ERC-8004 IdentityRegistry and returns * the new `agentId` (decoded from the tx receipt). The `identityRegistry` must be * `erc8004AddressesForChain(chainId).identityRegistry` — the contract reverts otherwise. * * Callable: owner EOA direct tx OR via UserOp (gasless). */ encodeMintAgentIdentity(params: MintAgentIdentityParams): string; /** * Encode calldata for `bindERC8004AgentWallet`. * * Binds an execution wallet to an existing ERC-8004 agent identity NFT. Requires a * deadline-bounded signature from the expected signer (see the IdentityRegistry contract). * The `identityRegistry` must be the official chain-specific address. * * Callable: owner EOA direct tx OR via UserOp (gasless). */ encodeBindERC8004AgentWallet(params: BindERC8004AgentWalletParams): string; /** * Encode calldata for `submitAgentReputation`. * * Submits a reputation feedback entry to the official ERC-8004 ReputationRegistry. * `reputationRegistry` must be `erc8004AddressesForChain(chainId).reputationRegistry`. * * Callable: owner EOA direct tx OR via UserOp (gasless). */ encodeSubmitAgentReputation(params: SubmitAgentReputationParams): string; /** * Query aggregated reputation for an agent from the official ERC-8004 ReputationRegistry. * Returns `null` when no provider was supplied at construction. */ queryAgentReputation(accountAddress: string, params: QueryAgentReputationParams): Promise; /** * Encode calldata for `queryAgentReputation` (for static-call or eth_call without a signer). */ encodeQueryAgentReputation(params: QueryAgentReputationParams): string; /** * Read the agentExtension implementation address from a deployed AirAccount. */ getAgentExtensionAddress(accountAddress: string): Promise; } /** * Request to mint a new agent key under an existing human key. * * WebAuthn-gated: the human approves the mint with a one-time WebAuthn ceremony * (preferred) or a Legacy passkey assertion. The challenge is obtained via * {@link KmsManager.beginAuthentication} (generic, purpose="authentication") — * the caller supplies the resulting assertion here. */ interface KmsCreateAgentKeyRequest { humanKeyId: string; label?: string; webAuthnAssertion?: WebAuthnAssertion; passkeyAssertion?: LegacyPasskeyAssertion; } interface KmsCreateAgentKeyResponse { keyId: string; agentAddress: string; derivationPath: string; agentCredential: string; expiresAt: number; } /** * Request to sign a userOpHash with an agent key, authenticated by the agent's * TEE-JWT credential (Bearer). Used for gasless ERC-4337 sponsorship. */ interface KmsSignAgentRequest { keyId: string; payload: string; algorithm?: string; accountAddress: string; } interface KmsSignAgentResponse { keyId: string; agentAddress: string; /** Hex 106-byte signature: [0x08][account(20)][key(20)][r(32)][s(32)][v(1)]. */ signature: string; } /** * Request to refresh (re-mint) an agent's TEE-JWT credential before it expires. * * Authenticated with the existing (still-valid) credential via Bearer JWT, plus * a WebAuthn / Legacy passkey assertion from the human key owner. */ interface KmsRefreshAgentCredentialRequest { keyId: string; webAuthnAssertion?: WebAuthnAssertion; passkeyAssertion?: LegacyPasskeyAssertion; } /** * The server response shape is not strictly documented; this models the fields * the SDK relies on. `keyId` is echoed back optionally. */ interface KmsRefreshAgentCredentialResponse { keyId?: string; agentCredential: string; expiresAt: number; } /** * Request to revoke an agent's credential (WebAuthn-gated). * * The challenge is obtained via {@link KmsManager.beginAuthentication} (generic, * purpose="authentication"); the caller supplies the resulting assertion here. */ interface KmsRevokeAgentCredentialRequest { keyId: string; webAuthnAssertion?: WebAuthnAssertion; passkeyAssertion?: LegacyPasskeyAssertion; } interface KmsRevokeAgentCredentialResponse { success: boolean; revokedAt: number; } /** * Agent-key lifecycle service for the AAStar TEE KMS (v0.20.0). * * An "agent key" is a TEE-JWT credential minted under a human key, used for * gasless ERC-4337 sponsorship without re-prompting the human for each signature. * Lifecycle: * 1. {@link createAgentKey} — human mints the agent key (WebAuthn-gated) * 2. {@link signAgent} — agent signs userOpHashes (Bearer JWT auth) * 3. {@link refreshAgentCredential}— re-mint before expiry (Bearer JWT + WebAuthn) * 4. {@link revokeAgentCredential} — human revokes the agent key (WebAuthn-gated) * * Wraps a shared {@link KmsHttpClient} — obtain it via {@link KmsManager.httpClient} * so this service reuses the same connection config and auth headers. */ declare class KmsAgentService { private readonly http; constructor(http: KmsHttpClient); /** * Mint a new agent key under an existing human key (WebAuthn-gated). * * The WebAuthn challenge is obtained from a generic * {@link KmsManager.beginAuthentication} ceremony (purpose="authentication"); * the caller supplies the resulting assertion in the request. */ createAgentKey(params: KmsCreateAgentKeyRequest): Promise; /** * Sign a userOpHash with an agent key, authenticated by the agent's TEE-JWT * credential (`jwt`, the `agentCredential` from {@link createAgentKey}). * Returns the 106-byte packed signature for ERC-4337 sponsorship. */ signAgent(params: KmsSignAgentRequest, jwt: string): Promise; /** * Refresh (re-mint) an agent credential before it expires. Authenticated with * the existing credential (`jwt`, Bearer) plus a human WebAuthn / passkey * assertion in the request. */ refreshAgentCredential(params: KmsRefreshAgentCredentialRequest, jwt: string): Promise; /** * Revoke an agent's credential (WebAuthn-gated). * * The WebAuthn challenge is obtained from a generic * {@link KmsManager.beginAuthentication} ceremony (purpose="authentication"); * the caller supplies the resulting assertion in the request. */ revokeAgentCredential(params: KmsRevokeAgentCredentialRequest): Promise; /** * Mint an agent key, running the challenge-binding ceremony internally. * * Auto-binds the v2 mint commitment (AirAccount #115, KMS v0.26.0): * `challenge = SHA-256(nonce ‖ mintDigest({ kind: "create-agent", walletId: humanKeyId, label }))` * — strict mode requires it; transition mode also accepts it. Pass `options.payload` only to * override. `label` defaults to "" to match the KMS server default. */ createAgentKeyWithCeremony(params: Omit, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Refresh an agent credential, running the challenge-binding ceremony * internally. `humanKeyId` is the owning human key challenged by the ceremony * (distinct from the agent `keyId` in `params`); `jwt` is the existing credential. * * Auto-binds the v2 REFRESH commitment (KMS v0.26.0): * `challenge = SHA-256(nonce ‖ mintDigest({ kind: "refresh-agent", walletId: humanKeyId, agentIndex }))` * where `agentIndex` is parsed from `params.keyId` ("wallet_uuid:agent_index"). REFRESH uses a * distinct tag from CREATE so the gesture cannot be replayed as a mint. Pass `options.payload` * to override. */ refreshAgentCredentialWithCeremony(params: Omit, humanKeyId: string, jwt: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Revoke an agent credential, running the challenge-binding ceremony internally. * `humanKeyId` is the owning human key challenged by the ceremony (distinct from * the agent `keyId` in `params`). */ revokeAgentCredentialWithCeremony(params: Omit, humanKeyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; } interface CreateP256SessionKeyRequest { /** Human (root) key under which the session key is minted. */ humanKeyId: string; /** Optional human-readable label for the session key. */ label?: string; /** * One-time WebAuthn assertion gating creation. The challenge comes from a * generic {@link KmsManager.beginAuthentication} ceremony — the caller runs * the ceremony and supplies the resulting assertion here. */ webAuthnAssertion?: WebAuthnAssertion; } interface CreateP256SessionKeyResponse { keyId: string; pubKeyX: string; pubKeyY: string; algorithm: string; agentCredential: string; expiresAt: number; } interface SignP256UserOpRequest { keyId: string; payload: string; accountAddress: string; } interface SignP256UserOpResponse { keyId: string; pubKeyX: string; pubKeyY: string; /** * 149-byte P256 session-key wire format (hex): * [0x08][account(20)][keyX(32)][keyY(32)][r(32)][s(32)]. */ signature: string; } interface RevokeP256SessionKeyRequest { keyId: string; /** * One-time WebAuthn assertion gating revocation. The challenge comes from a * generic {@link KmsManager.beginAuthentication} ceremony — the caller runs * the ceremony and supplies the resulting assertion here. */ webAuthnAssertion?: WebAuthnAssertion; } interface RevokeP256SessionKeyResponse { success: boolean; revokedAt: number; } /** * Manages the lifecycle of a P-256 session key minted under a human key for * ERC-4337 UserOp signing (AAStar TEE KMS v0.20.0). * * A session key is created under a root (human) key, used to sign UserOps via a * TEE-issued bearer JWT (the `agentCredential`), and eventually revoked. The * per-UserOp signature is the 149-byte P256 session-key wire format. * * Relationship to {@link KmsManager.signP256GrantSession}: that method signs the * GRANT_P256_SESSION_V2 authorization needed to *install* this key on-chain * (granting the session key its on-chain scope/policies). This service instead * manages the session key's own lifecycle (create / sign / revoke) once granted. * * Create and revoke are WebAuthn-gated: the challenge originates from a generic * {@link KmsManager.beginAuthentication} ceremony and the caller supplies the * resulting assertion. Per-UserOp signing authenticates with the bearer JWT. * * Wraps a shared {@link KmsHttpClient} — pass `KmsManager.httpClient`. */ declare class KmsSessionService { private readonly http; constructor(http: KmsHttpClient); /** * Create a P-256 session key under a human key (WebAuthn-gated). * * `POST /kms/create-p256-session-key`. The `webAuthnAssertion` challenge comes * from a generic {@link KmsManager.beginAuthentication} ceremony supplied by * the caller. Returns the session key's public key plus an `agentCredential` * JWT used to authenticate subsequent {@link signP256UserOp} calls. */ createP256SessionKey(params: CreateP256SessionKeyRequest): Promise; /** * Sign an ERC-4337 UserOp hash with a P-256 session key (Bearer JWT auth). * * `POST /kms/sign-p256-user-op`, authenticated with the `agentCredential` JWT * returned by {@link createP256SessionKey}. Returns the 149-byte P256 * session-key wire-format signature. */ signP256UserOp(params: SignP256UserOpRequest, jwt: string): Promise; /** * Revoke a P-256 session key (WebAuthn-gated, idempotent). * * `POST /kms/revoke-p256-session-key`. The `webAuthnAssertion` challenge comes * from a generic {@link KmsManager.beginAuthentication} ceremony supplied by * the caller. Idempotent: revoking an already-revoked key still resolves. */ revokeP256SessionKey(params: RevokeP256SessionKeyRequest): Promise; /** * Create a P-256 session key, running the challenge-binding ceremony internally. * * Auto-binds the v2 mint commitment (AirAccount #115, KMS v0.26.0): * `challenge = SHA-256(nonce ‖ mintDigest({ kind: "create-p256", walletId: humanKeyId, label }))` * — strict mode requires it; transition mode also accepts it. Pass `options.payload` only to * override. `label` defaults to "" to match the KMS server default. */ createP256SessionKeyWithCeremony(params: Omit, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** * Revoke a P-256 session key, running the challenge-binding ceremony internally. * `humanKeyId` is the owning human key challenged by the ceremony (distinct from * the session `keyId` in `params`). */ revokeP256SessionKeyWithCeremony(params: Omit, humanKeyId: string, signer: PasskeyCeremonySigner, options?: Omit): Promise; } type KmsPaymentAuth = { jwt: string; } | { webAuthnAssertion: WebAuthnAssertion; }; /** Shared signature response for all payment signing endpoints. */ interface KmsPaymentSignatureResponse { keyId: string; signature: string; } interface KmsSignMicropaymentVoucherRequest { keyId: string; hdPath?: string; chainId: number; verifyingContract: string; channelId: string; cumulativeAmount: string; } interface KmsSignGTokenAuthorizationRequest { keyId: string; hdPath?: string; chainId: number; gTokenAddress: string; from: string; to: string; value: string; validAfter: string; validBefore: string; nonce: string; } interface KmsSignX402PaymentRequest { keyId: string; hdPath?: string; chainId: number; verifyingContract: string; paymentId: string; amount: string; recipient: string; deadline: string; } /** * Convenience signers for SuperPaymaster payment flows (v0.20.0 P2). * * Each method maps to a fixed EIP-712 domain + type that the KMS builds host-side * and signs inside the TEE; the SDK only forwards the structured parameters. Every * endpoint accepts EITHER a one-time `webAuthnAssertion` in the body OR an agent * Bearer JWT — see {@link KmsPaymentAuth}. * * Wraps a shared {@link KmsHttpClient}; reuse the same instance across the agent / * session / payment / monitor services. */ declare class KmsPaymentSigner { private readonly http; constructor(http: KmsHttpClient); /** * Dispatch a payment-signing request with the chosen auth mode. * JWT auth uses `postWithBearer`; WebAuthn auth merges the assertion into the body. */ private signWithAuth; /** * Sign a MicroPaymentChannel voucher (cumulative-amount EIP-712 message) * via `POST /kms/SignMicropaymentVoucher`. */ signMicropaymentVoucher(params: KmsSignMicropaymentVoucherRequest, auth: KmsPaymentAuth): Promise; /** * Sign an EIP-3009 TransferWithAuthorization for a GToken transfer * via `POST /kms/SignGTokenAuthorization`. `from` MUST equal the derived address. */ signGTokenAuthorization(params: KmsSignGTokenAuthorizationRequest, auth: KmsPaymentAuth): Promise; /** * Sign an x402 payment authorization via `POST /kms/SignX402Payment`. */ signX402Payment(params: KmsSignX402PaymentRequest, auth: KmsPaymentAuth): Promise; /** Sign a MicroPaymentChannel voucher, running the committed ceremony internally. */ signMicropaymentVoucherWithCeremony(params: KmsSignMicropaymentVoucherRequest, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** Sign a GToken EIP-3009 authorization, running the committed ceremony internally. */ signGTokenAuthorizationWithCeremony(params: KmsSignGTokenAuthorizationRequest, signer: PasskeyCeremonySigner, options?: Omit): Promise; /** Sign an x402 payment, running the committed ceremony internally. */ signX402PaymentWithCeremony(params: KmsSignX402PaymentRequest, signer: PasskeyCeremonySigner, options?: Omit): Promise; } /** EIP-712 digest for a MicroPaymentChannel `Voucher` (domain MicroPaymentChannel/1.0.0). */ declare function micropaymentVoucherDigest(p: KmsSignMicropaymentVoucherRequest): `0x${string}`; /** EIP-712 digest for a GToken EIP-3009 `TransferWithAuthorization` (domain GToken/1). */ declare function gTokenAuthorizationDigest(p: KmsSignGTokenAuthorizationRequest): `0x${string}`; /** EIP-712 digest for an x402 `PaymentPayload` (domain SuperPaymaster/1). */ declare function x402PaymentDigest(p: KmsSignX402PaymentRequest): `0x${string}`; /** * Liveness probe response. Returned by `GET /health` without auth — works even * when the SDK's KMS feature flag is off. */ interface KmsHealthResponse { status: string; service?: string; ta_mode?: string; version?: string; } /** * Version / capability descriptor. Returned by `GET /version` without auth. * Extra fields are passed through. */ interface KmsVersionResponse { version?: string; ta_mode?: string; endpoints?: string[]; [k: string]: unknown; } /** * KMS request-queue health, including circuit-breaker state. Useful for * back-pressure decisions before submitting signing operations. */ interface KmsQueueStatusResponse { queue_depth: number; estimated_wait_seconds: number; circuit_breaker_open: boolean; consecutive_failures: number; } /** * RPMB anti-rollback monotonic counter (diagnostic, v0.20.0). The exact shape * is undocumented; the known `counter` field is surfaced and all other fields * are passed through. */ interface KmsRollbackCounterResponse { counter?: number; [k: string]: unknown; } /** * Machine-readable runtime statistics (v0.20.0). Pass-through; the response is * not strongly typed. Known top-level fields: * - `wallets` — wallet / key counts * - `tx` — transaction / signing counts * - `queue` — queue depth and timing metrics * - `warnings` — active operational warnings */ interface KmsStatsResponse { [k: string]: unknown; } /** `GET /attestation` evidence bound to a caller nonce (#37). */ interface KmsAttestationResponse { schema?: string; nonce?: string; ta_uuid?: string; ta_measurement?: string; signature?: string; attest_pubkey_exp?: string; attest_pubkey_mod?: string; sig_alg?: number; ree_time_secs?: number; trust_root?: string; [k: string]: unknown; } /** `GET /.well-known/attestation-measurements.json` — Ed25519-signed manifest (#12). */ interface KmsAttestationManifestResponse { body?: Record; publisher_key?: string; signature?: string; [k: string]: unknown; } /** `GET /.well-known/attestation-measurements-proof.json` — Sigsum proof sidecar (#87). */ interface KmsAttestationProofResponse { proof?: Record; [k: string]: unknown; } /** * Response of the destructive operator-only purge action (v0.20.0). * Pass-through; the response shape is not strongly typed. */ interface KmsPurgeKeyResponse { [k: string]: unknown; } /** * Infrastructure monitoring + operator admin surface for the AAStar TEE KMS * (v0.20.0, kms.aastar.io). * * Wraps a shared {@link KmsHttpClient}. Liveness probes (`health`, `version`) * intentionally bypass the `enabled` gate so they work even when the SDK's KMS * feature flag is off; every other method calls `ensureEnabled()` first. */ declare class KmsMonitorService { private readonly http; constructor(http: KmsHttpClient); /** * Liveness probe (`GET /health`, no auth). Does NOT require the KMS feature * flag to be enabled. */ health(): Promise; /** * Version / capability descriptor (`GET /version`, no auth). Does NOT require * the KMS feature flag to be enabled. */ version(): Promise; /** * Request-queue health and circuit-breaker state (`GET /QueueStatus`). */ queueStatus(): Promise; /** * RPMB anti-rollback monotonic counter (`GET /RollbackCounter`, diagnostic, * v0.20.0). */ rollbackCounter(): Promise; /** * Machine-readable runtime statistics (`GET /stats`, v0.20.0) — wallets, tx, * queue, warnings. */ stats(): Promise; /** * TEE remote-attestation evidence bound to a caller nonce (`GET /attestation`, * #37). Public (no auth) — pass a fresh random `nonce` (hex, ≤64 bytes) to bind * the evidence + defeat replay, then verify the returned signed measurement. */ getAttestation(nonce: string): Promise; /** * Ed25519-signed measurement manifest, version → ta_measurement * (`GET /.well-known/attestation-measurements.json`, #12). Public. */ getAttestationMeasurements(): Promise; /** * Sigsum transparency proof sidecar for the measurement manifest * (`GET /.well-known/attestation-measurements-proof.json`, #87). Public. */ getAttestationMeasurementsProof(): Promise; /** * WARNING — DESTRUCTIVE, IRREVERSIBLE. Force-purges a key from both the TEE * and the SQLite store with NO passkey/WebAuthn check (`POST /admin/purge-key`, * v0.20.0). Operator-only: authorised solely by the `KMS_ADMIN_TOKEN` operator * secret sent as `Authorization: Bearer `. There is no recovery * once a key is purged. * * @internal Operator/break-glass tooling only — not part of the general SDK surface. * The endpoint is gated server-side and intentionally omitted from the public KMS * docs; do not expose it in application-facing flows. */ adminPurgeKey(params: { key_id: string; reason: string; }, adminToken: string): Promise; } /** * In-memory storage adapter — useful for testing and demos. * All data is lost when the process exits. */ declare class MemoryStorage implements IStorageAdapter { private accounts; private transfers; private paymasters; private blsConfig; getAccounts(): Promise; saveAccount(account: AccountRecord): Promise; findAccountByUserId(userId: string): Promise; updateAccount(userId: string, updates: Partial): Promise; saveTransfer(transfer: TransferRecord): Promise; findTransfersByUserId(userId: string): Promise; findTransferById(id: string): Promise; updateTransfer(id: string, updates: Partial): Promise; getPaymasters(userId: string): Promise; savePaymaster(userId: string, paymaster: PaymasterRecord): Promise; removePaymaster(userId: string, name: string): Promise; getBlsConfig(): Promise; updateSignerNodesCache(nodes: unknown[]): Promise; } /** * Local wallet signer — backs all users with a single private key. * Suitable for testing, demos, and single-tenant server setups. * * For multi-tenant production use, implement ISignerAdapter with * per-user key management (e.g., KMS, HSM, or encrypted database). */ declare class LocalWalletSigner implements ISignerAdapter { private readonly account; constructor(privateKey: string); getAddress(_userId: string): Promise<`0x${string}`>; signMessage(_userId: string, message: `0x${string}` | Uint8Array, _ctx?: SignerAuthContext): Promise<`0x${string}`>; ensureSigner(_userId: string): Promise<{ address: `0x${string}`; }>; } /** Resolves an app user id to its KMS key + EOA address. App-specific mapping. */ type KmsKeyResolver = (userId: string) => Promise<{ keyId: string; address: `0x${string}`; }>; /** * KMS-backed {@link ISignerAdapter} — the bridge between the SDK signing chain * (BLS / transfer pass a {@link SignerAuthContext}) and the KMS `/SignHash` API. * * This is the concrete adapter the BLS/transfer services expect: it unpacks the * per-call auth context and forwards it to the right KMS endpoint. * - {@link WebAuthnCeremonyContext} (preferred) → `signHashWithWebAuthn` * (one-time, challenge-bound; replay-safe — what KMS v0.20.0+ requires). * - {@link PasskeyAssertionContext} (legacy, @deprecated) → `signHash` * (rejected by KMS unless `KMS_ALLOW_LEGACY_PASSKEY=1`, test only). * * The frontend runs the BeginAuthentication ceremony with the user's device * passkey and passes the resulting `{ ChallengeId, Credential }` down as * `ctx.webAuthnAssertion`. Since each challenge is consumed once, a flow needing * N signatures must pass N assertions — use the tiered transfer path, which needs * only one owner signature. * * The `userId → { keyId, address }` mapping is app-specific; inject it via * {@link KmsKeyResolver}. */ declare class KmsSignerAdapter implements ISignerAdapter { private readonly kms; private readonly resolveKey; constructor(kms: KmsManager, resolveKey: KmsKeyResolver); getAddress(userId: string): Promise<`0x${string}`>; ensureSigner(userId: string): Promise<{ address: `0x${string}`; }>; signMessage(userId: string, message: `0x${string}` | Uint8Array, ctx?: SignerAuthContext): Promise<`0x${string}`>; /** * Strict device-passkey path (two-phase transfer): start a KMS BeginAuthentication ceremony * and return options whose `challenge` is the WYSIWYS commitment over the EXACT digest * {@link signMessage} will sign — `SHA-256(nonce ‖ hashMessage(message))`. The SDK owns the * payload + commitChallenge; the frontend just runs `navigator.credentials.get`. */ beginCeremony(userId: string, message: `0x${string}` | Uint8Array): Promise<{ challengeId: string; publicKeyOptions: PublicKeyCredentialRequestOptions; }>; } export { ACCOUNT_ABI, AGENT_SESSION_KEY_VALIDATOR_ABI, AIRACCOUNT_ABI, AIRACCOUNT_ADDRESSES, AIRACCOUNT_FACTORY_ABI, AIR_ACCOUNT_COMPOSITE_VALIDATOR_ABI, AIR_ACCOUNT_DELEGATE_ABI, AIR_ACCOUNT_DELEGATE_ADDRESS, ALG_ID, AccountManager, type AccountRecord, type ActiveRecovery, AgentRegistryService, type AgentReputationSummary, type AgentSessionConfig, type AgentSessionInfo, AirAccountServerClient, type AirAccountVersion, BLSSignatureData, BLSSignatureService, type BeginCeremonyResponse, type BindERC8004AgentWalletParams, type BlsConfigRecord, type BuildCredentialOptions, CALLDATA_PARSER_REGISTRY_ABI, ConsoleLogger, type CreateAgentAccountParams, type CreateP256SessionKeyRequest, type CreateP256SessionKeyResponse, DEFAULT_CREDENTIAL_ID, DEFAULT_KMS_ENDPOINT, DEFAULT_ORIGIN, DEFAULT_RP_ID, type DelegateInitParams, DvtPendingConfirmationError, type EIP7702Authorization, EIP7702DelegateService, ENTRYPOINT_ABI_V6, ENTRYPOINT_ABI_V7_V8, ENTRYPOINT_ADDRESSES, ERC20_ABI, ERC8004Service, ERC8004_ADDRESSES, EXECUTE_BATCH_SELECTOR, EXECUTE_SELECTOR, EXECUTE_USER_OP_SELECTOR, type EntryPointConfig, EntryPointVersion, type EntryPointVersionConfig, type EstimateGasParams, EthereumProvider, type ExecuteTransferParams, FACTORY_ABI_V6, FACTORY_ABI_V7_V8, FORCE_EXIT_MODULE_ABI, ForceExitService, type FullConfigGuardianParams, GLOBAL_GUARD_ABI, type GrantP256SessionParams, type GrantSessionParams, GuardChecker, type GuardState, GuardStateReader, GuardStatus, type ILogger, type ISignerAdapter, type IStorageAdapter, type InstallModuleParams, KmsAgentService, type KmsAttestationManifestResponse, type KmsAttestationProofResponse, type KmsAttestationResponse, type KmsBeginAuthenticationRequest, type KmsBeginAuthenticationResponse, type KmsBeginGrantSessionAuthRequest, type KmsBeginGrantSessionAuthResponse, type KmsBeginRegistrationRequest, type KmsBeginRegistrationResponse, type KmsChangePasskeyResponse, type KmsCompleteRegistrationRequest, type KmsCompleteRegistrationResponse, type KmsCreateAgentKeyRequest, type KmsCreateAgentKeyResponse, type KmsCreateKeyRequest, type KmsCreateKeyResponse, type KmsDeleteKeyResponse, type KmsDeriveAddressResponse, type KmsDescribeKeyResponse, type KmsEip712Domain, type KmsEip712FieldValue, type KmsEip712TypeDef, type KmsEthereumTransaction, type KmsGetPublicKeyResponse, type KmsHealthResponse, KmsHttpClient, type KmsHttpClientOptions, type KmsKeyResolver, type KmsKeyStatusResponse, type KmsListKeysResponse, KmsManager, KmsMonitorService, type KmsPaymentAuth, type KmsPaymentSignatureResponse, KmsPaymentSigner, type KmsPurgeKeyResponse, type KmsQueueStatusResponse, type KmsRefreshAgentCredentialRequest, type KmsRefreshAgentCredentialResponse, type KmsRevokeAgentCredentialRequest, type KmsRevokeAgentCredentialResponse, type KmsRollbackCounterResponse, KmsSessionService, type KmsSignAgentRequest, type KmsSignAgentResponse, type KmsSignGTokenAuthorizationRequest, type KmsSignGrantSessionRequest, type KmsSignGrantSessionResponse, type KmsSignHashResponse, type KmsSignMicropaymentVoucherRequest, type KmsSignP256GrantSessionRequest, type KmsSignRequest, type KmsSignResponse, type KmsSignTypedDataRequest, type KmsSignTypedDataResponse, type KmsSignX402PaymentRequest, KmsSigner, KmsSignerAdapter, type KmsSignerAuth, type KmsStatsResponse, type KmsVersionResponse, type L2Type, L2_TYPE, type LegacyPasskeyAssertion, LocalWalletSigner, MAX_GUARDIANS, MODULE_TYPE, MemoryStorage, type MintAgentIdentityParams, ModuleManager, type ModuleTypeId, type OapdConfig, type P256GuardianKey, P256PasskeySigner, PackedUserOperation, type PasskeyAssertionContext, type PasskeyCeremonySigner, type PasskeyCreateParams, PaymasterManager, PaymasterPriceStalenessError, type PaymasterRecord, type PendingExit, type PendingWeightChange, PreCheckResult, type PreparedPasskeyCreate, type PreparedTransfer, type QueryAgentReputationParams, RECOVERY_THRESHOLD, RECOVERY_TIMELOCK_SECONDS, REFERENCE_ETH_PROFILES, RecoveryService, type ResolvedTierProfile, type RevokeP256SessionKeyRequest, type RevokeP256SessionKeyResponse, type RunCeremonyOptions, SESSION_KEY_VALIDATOR_ABI, type SerializedGuardianSpec, type ServerConfig, type SessionInfo, SessionKeyService, type SetAgentWalletParams, type SignP256UserOpRequest, type SignP256UserOpResponse, type SignerAuthContext, SilentLogger, type SubmitAgentReputationParams, TIER_GUARD_HOOK_ABI, TierConfig, TierLevel$1 as TierLevel, type TierProfile, type TierProfileToken, type TokenBalance, type TokenGuardState, type TokenInfo, TokenService, TransferManager, type TransferRecord, type TransferResult, type UninstallModuleParams, UserOperation, VALIDATOR_ABI, WEIGHT_CHANGE_EXPIRY_SECONDS, WEIGHT_CHANGE_THRESHOLD, WEIGHT_CHANGE_TIMELOCK_SECONDS, WalletManager, type WebAuthnAssertion, type WebAuthnAuthenticationCredential, type WebAuthnCeremonyContext, type WeightConfig, WeightedSignatureService, YAAAServerClient, base64UrlDecode, base64UrlEncode, beginAuthenticationChallenge, beginGrantSessionChallenge, buildAuthenticationCredential, buildAuthenticatorData, buildClientDataJSON, buildFullInitConfig, buildInstallModuleHash, buildInstallModuleP256Challenge, buildSetModuleTimelockHash, buildSetModuleTimelockP256Challenge, buildUninstallModuleHash, buildUninstallModuleP256Challenge, commitChallenge, computeOapdSalt, eip712Digest, erc8004AddressesForChain, gTokenAuthorizationDigest, getOapdAddress, getOapdAddressWithChainId, grantSessionFinalHash, initConfigFromRecord, initConfigToTuple, isExecuteUserOpWrapped, isOapdDeployed, isPendingConfirmation, micropaymentVoucherDigest, mintDigest, packP256SessionSignature, packSecp256k1SessionSignature, resolveTierProfile, runAuthenticationCeremony, runGrantSessionCeremony, runWebAuthnCeremony, sepoliaV07Config, serializeGuardianSpecs, toGuardianSpecs, validateConfig, wrapExecuteUserOp, x402PaymentDigest };