@zama-fhe/sdk 3.6.0-beta.2 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/base-signer.cjs +1 -1
- package/dist/cjs/base-signer.cjs.map +1 -1
- package/dist/cjs/fhevm-relayer.cjs.map +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/wrappers-registry.cjs.map +1 -1
- package/dist/esm/base-signer-DGIEzavu.js +2 -0
- package/dist/esm/base-signer-DGIEzavu.js.map +1 -0
- package/dist/esm/ethers/index.js +1 -1
- package/dist/esm/fhevm-relayer-Cm-ZR1vP.js.map +1 -1
- package/dist/esm/{index-Doh_XVSI.d.ts → index-BCh3VaZo.d.ts} +4 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -1
- package/dist/esm/viem/index.js +1 -1
- package/dist/esm/wrappers-registry-BLe7PkZi.js.map +1 -1
- package/package.json +1 -1
- package/dist/esm/base-signer-CzEZ2cCF.js +0 -2
- package/dist/esm/base-signer-CzEZ2cCF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-signer-DGIEzavu.js","names":["#dbName","#dbVersion","#storeName","#db","#dbPromise","#withTransaction","#getDB","#map","#chains","#relayers","#chainId","#secret","#baseKey","#importBaseKey","#generator","#storage","#ttl","#logger","#scope","#derivationSecret","#pending","#epoch","#readByKey","#identityKey","#identity","#failLoudly","#discard","#discardIfExpired","#recover","#getOrCreateByKey","#storage","#logger","#trackScope","#deleteScope","#untrackScope","#readIndex","#vault","#store","#router","#signer","#permitTTL","#logger","#emitEvent","#scope","#permitRecoveries","#requireSigner","#permissionScope","#failPermit","#signPermit","#readScopePermits","#clearAndRegrantScope","#logger","#prefix","#addresses","#ttlMs","#cache","#getCached","#setCached","#pairWithMetadata","#listeners","#snapshot","#resolved","#emit","#disposed"],"sources":["../../src/storage/indexeddb-storage.ts","../../src/storage/memory-storage.ts","../../src/config/resolve.ts","../../src/chains/router.ts","../../src/schemas/primitives.ts","../../src/credentials/keypair-wrapping.ts","../../src/credentials/utils.ts","../../src/credentials/schemas.ts","../../src/credentials/keypair-entry-codec.ts","../../src/credentials/storage-keys.ts","../../src/credentials/keypair-vault.ts","../../src/credentials/permissions.ts","../../src/credentials/permission-store.ts","../../src/credentials/credential-service.ts","../../src/services/logger-service.ts","../../src/wrappers-registry.ts","../../src/config/build.ts","../../src/signer/wallet-account-store.ts","../../src/signer/base-signer.ts"],"sourcesContent":["\"use client\";\n\nimport type { GenericStorage } from \"../types\";\n\n/**\n * IndexedDB-backed {@link GenericStorage}.\n *\n * Stores keypair and permit objects keyed by a hashed wallet address. The\n * SDK delegates security to the storage backend — values are persisted\n * verbatim with no SDK-layer encryption.\n */\nexport class IndexedDBStorage implements GenericStorage {\n #db: IDBDatabase | null = null;\n #dbPromise: Promise<IDBDatabase> | null = null;\n #dbName: string;\n #dbVersion: number;\n #storeName: string;\n\n constructor(dbName = \"CredentialStore\", dbVersion = 1, storeName = \"credentials\") {\n this.#dbName = dbName;\n this.#dbVersion = dbVersion;\n this.#storeName = storeName;\n }\n\n #getDB(): Promise<IDBDatabase> {\n if (this.#db) {\n return Promise.resolve(this.#db);\n }\n if (this.#dbPromise) {\n return this.#dbPromise;\n }\n\n this.#dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(this.#dbName, this.#dbVersion);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(this.#storeName)) {\n db.createObjectStore(this.#storeName, { keyPath: \"key\" });\n }\n };\n\n request.onsuccess = () => {\n this.#db = request.result;\n this.#dbPromise = null;\n this.#db.onversionchange = () => {\n // Another tab upgraded the DB; close our handle so it can proceed.\n // Informational only and self-healing, so emit nothing to the console.\n this.#db?.close();\n this.#db = null;\n this.#dbPromise = null;\n };\n this.#db.onclose = () => {\n this.#db = null;\n this.#dbPromise = null;\n };\n resolve(this.#db);\n };\n\n request.onerror = () => {\n this.#db = null;\n this.#dbPromise = null;\n reject(request.error);\n };\n });\n\n return this.#dbPromise;\n }\n\n async #withTransaction<T>(\n mode: IDBTransactionMode,\n fn: (store: IDBObjectStore) => IDBRequest,\n ): Promise<T> {\n const db = await this.#getDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.#storeName, mode);\n tx.onabort = () => reject(tx.error ?? new Error(\"Transaction aborted\"));\n const request = fn(tx.objectStore(this.#storeName));\n if (mode === \"readonly\") {\n request.onsuccess = () => resolve(request.result);\n } else {\n tx.oncomplete = () => resolve(request.result);\n }\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieve a value by key.\n * @returns The stored value, or `null` if the key does not exist.\n */\n async get<T = unknown>(key: string): Promise<T | null> {\n const result = await this.#withTransaction<{ value: T } | undefined>(\"readonly\", (store) =>\n store.get(key),\n );\n return result?.value ?? null;\n }\n\n /** Store a value under the given key, overwriting any existing entry. */\n async set<T = unknown>(key: string, value: T): Promise<void> {\n await this.#withTransaction<void>(\"readwrite\", (store) => store.put({ key, value }));\n }\n\n /** Remove the entry for the given key (no-op if absent). */\n async delete(key: string): Promise<void> {\n await this.#withTransaction<void>(\"readwrite\", (store) => store.delete(key));\n }\n\n /** Remove all stored entries. */\n async clear(): Promise<void> {\n await this.#withTransaction<void>(\"readwrite\", (store) => store.clear());\n }\n}\n\n/** Default singleton for application-wide use. */\nexport const indexedDBStorage = new IndexedDBStorage();\n","import type { GenericStorage } from \"../types\";\n\n/** In-memory credential store. Credentials are lost on page reload. */\nexport class MemoryStorage implements GenericStorage {\n #map = new Map<string, unknown>();\n\n /**\n * Retrieve a value by key.\n * @returns The stored value, or `null` if the key does not exist.\n */\n async get<T = unknown>(key: string): Promise<T | null> {\n return (this.#map.get(key) as T) ?? null;\n }\n\n /** Store a value under the given key, overwriting any existing entry. */\n async set<T = unknown>(key: string, value: T): Promise<void> {\n this.#map.set(key, value);\n }\n\n /** Remove the entry for the given key (no-op if absent). */\n async delete(key: string): Promise<void> {\n this.#map.delete(key);\n }\n}\n\n/** Default singleton for application-wide use. */\nexport const memoryStorage = new MemoryStorage();\n","import type { FheChain } from \"../chains\";\nimport { ConfigurationError } from \"../errors\";\nimport { IndexedDBStorage } from \"../storage/indexeddb-storage\";\nimport { MemoryStorage } from \"../storage/memory-storage\";\nimport type { LoggerService } from \"../services/logger-service\";\nimport type { GenericStorage } from \"../types\";\nimport type { RelayerConfig } from \"./types\";\n\n// ── Storage defaults ─────────────────────────────────────────────────────────\n\nfunction getDefaultStorage(): GenericStorage {\n return typeof window !== \"undefined\"\n ? new IndexedDBStorage(\"CredentialStore\")\n : new MemoryStorage();\n}\n\n/** @internal */\nexport function resolveStorage(\n storage: GenericStorage | undefined = getDefaultStorage(),\n permitStorage: GenericStorage | undefined = storage,\n): { storage: GenericStorage; permitStorage: GenericStorage } {\n return { storage, permitStorage };\n}\n\n// ── Chain relayer resolution ────────────────────────────────────────────────\n\n/** @internal */\nexport interface ResolvedChainRelayer {\n chain: FheChain;\n relayerConfig: RelayerConfig;\n}\n\n/** @internal */\nexport function resolveChainRelayers(\n chains: readonly FheChain[],\n relayers: Readonly<Record<number, RelayerConfig>>,\n logger: LoggerService,\n): Map<number, ResolvedChainRelayer> {\n const ids = chains.map((c) => c.id);\n const dupes = [...new Set(ids.filter((id, i) => ids.indexOf(id) !== i))];\n if (dupes.length > 0) {\n throw new ConfigurationError(\n `Duplicate chain id(s) [${dupes.join(\", \")}] in the chains array. ` +\n `Each chain id must appear only once. Note: hardhat and anvil are aliases (both use 31337).`,\n );\n }\n\n const orphaned = Object.keys(relayers)\n .map(Number)\n .filter((id) => !ids.includes(id));\n if (orphaned.length > 0) {\n logger.warn(\n `Relayer entries for chain(s) [${orphaned.join(\", \")}] are ignored: those chains are not in ` +\n `the chains array. Add the chain config to use them, or drop the relayer entries.`,\n );\n }\n\n const result = new Map<number, ResolvedChainRelayer>();\n for (const chain of chains) {\n const relayerConfig = relayers[chain.id];\n if (!relayerConfig) {\n throw new ConfigurationError(\n `Chain ${chain.id} has no relayer configured. ` +\n `Add a relayer entry: relayers: { [${chain.id}]: web() }`,\n );\n }\n result.set(chain.id, { chain, relayerConfig });\n }\n\n return result;\n}\n","import type { FheChain } from \"./types\";\nimport { resolveChainRelayers } from \"../config/resolve\";\nimport type { RelayerConfig } from \"../config/types\";\nimport { ConfigurationError } from \"../errors\";\nimport type { RelayerSDK } from \"../relayer/types\";\nimport type { LoggerService } from \"../services/logger-service\";\n\n/**\n * Multichain router. Owns chain management (chains / chain / switchChain) and\n * hands out the single-chain {@link RelayerSDK} backend for the currently active\n * chain via {@link ChainRouter.relayer}. Builds one backend per chain from its\n * {@link RelayerConfig}.\n *\n * @internal\n */\nexport class ChainRouter {\n readonly #chains: Map<number, FheChain>;\n readonly #relayers: Map<number, RelayerSDK>;\n #chainId: number;\n\n constructor(\n chains: readonly [FheChain, ...FheChain[]],\n configs: Readonly<Record<number, RelayerConfig>>,\n logger: LoggerService,\n ) {\n if (chains.length === 0) {\n throw new ConfigurationError(\"At least one chain is required.\");\n }\n this.#chains = new Map(chains.map((c) => [c.id, c]));\n this.#chainId = chains[0].id;\n\n // One backend per chain. A caller-supplied createRelayer may return one\n // backend for several chains, so dispose dedupes.\n const relayers = new Map<number, RelayerSDK>();\n for (const [chainId, { relayerConfig, chain }] of resolveChainRelayers(\n chains,\n configs,\n logger,\n )) {\n relayers.set(chainId, relayerConfig.createRelayer(chain, logger));\n }\n this.#relayers = relayers;\n }\n\n get chains(): readonly FheChain[] {\n return [...this.#chains.values()];\n }\n\n get chain(): FheChain {\n const chain = this.#chains.get(this.#chainId);\n if (chain === undefined) {\n throw new ConfigurationError(\n `Chain ${this.#chainId} is not configured. Add it to the chains array.`,\n );\n }\n return chain;\n }\n\n /**\n * Point the router at `chainId`. The active chain follows the wallet even when\n * that chain has no configured backend: the {@link chain} / {@link relayer}\n * getters throw a {@link ConfigurationError} for an unconfigured chain, so an\n * unsupported chain fails loudly at the next operation rather than silently\n * routing to the previously active chain (which would encrypt against, and\n * sign an EIP-712 decryption permit for, the wrong chain's domain and ACL).\n * Switching back to a configured chain restores service.\n */\n switchChain(chainId: number): void {\n this.#chainId = chainId;\n }\n\n /**\n * Releases every backend's resources, deduping shared instances. Backends\n * stay usable: in-flight work finishes and later calls re-acquire what they need.\n */\n dispose(): void {\n for (const relayer of new Set(this.#relayers.values())) {\n relayer.dispose?.();\n }\n }\n\n /** The single-chain backend for the currently active chain. */\n get relayer(): RelayerSDK {\n const relayer = this.#relayers.get(this.chain.id);\n if (relayer === undefined) {\n throw new ConfigurationError(\n `No relayer configured for chain ${this.#chainId}. Add it to the relayers object.`,\n );\n }\n return relayer;\n }\n}\n","import { type Address, getAddress, isAddress, isHex, type Hex } from \"viem\";\nimport { z } from \"zod/mini\";\n\ndeclare const checksummedTag: unique symbol;\n\n/** An EVM address that has been put through `getAddress` (EIP-55 checksum form). */\nexport type ChecksummedAddress = Address & { readonly [checksummedTag]: true };\n\n/** Stamp the brand on an arbitrary `Address` after normalizing it. */\nexport function checksum(value: Address): ChecksummedAddress {\n return getAddress(value) as ChecksummedAddress;\n}\n\n/** `0x`-prefixed hex string. */\nexport const hex = z.custom<Hex>(\n (v) => typeof v === \"string\" && isHex(v, { strict: true }),\n \"expected 0x-prefixed hex string\",\n);\n\n/** Validates an EVM address string. Output type is `Address` (`` `0x${string}` ``). */\nexport const evmAddress = z.custom<Address>(\n (v) => typeof v === \"string\" && isAddress(v, { strict: false }),\n \"expected EVM address\",\n);\n\n/** Validates and EIP-55 checksums an EVM address. Output is {@link ChecksummedAddress}. */\nexport const checksummedAddress = z.pipe(evmAddress, z.transform(checksum));\n\n/** Non-negative integer Unix timestamp in seconds. */\nexport const unixSeconds = z.int().check(z.nonnegative());\n\n/** Positive integer count of seconds (e.g. a TTL). */\nexport const positiveSeconds = z.int().check(z.positive());\n\n/** Non-negative integer count of seconds (e.g. a cache TTL where 0 disables caching). */\nexport const nonNegativeSeconds = z.int().check(z.nonnegative());\n\n/** Positive integer count of days (e.g. a permit duration). */\nexport const positiveDays = z.int().check(z.positive());\n\n/** Positive integer EVM chain ID. */\nexport const chainId = z.int().check(z.positive());\n","import { toBytes, toHex, type Hex } from \"viem\";\n\n/**\n * Parameters of wrapping scheme v1, in one place so the crypto path and the on-disk\n * format can never disagree about them. The `info` tag is versioned so a future scheme\n * can run alongside this one without silently reusing (and confusing) its derived keys,\n * and `version` is persisted with every entry so a future scheme is detectable on disk\n * instead of reading as corruption.\n */\nexport const WRAPPING_SCHEME_V1 = {\n version: 1,\n info: \"zama-sdk-keypair-wrapping-v1\",\n /** AES-GCM recommended nonce size. Generated fresh per wrap, never reused. */\n ivLengthBytes: 12,\n /** AES-GCM authentication tag size; any valid ciphertext is at least this long. */\n tagLengthBytes: 16,\n} as const;\n\nconst AES_KEY_LENGTH_BITS = 256;\n\n/** On-disk shape of a wrapped private key. Public key and timestamps are stored alongside, unwrapped. */\nexport interface WrappedPrivateKey {\n wrappedPrivateKey: Hex;\n iv: Hex;\n}\n\n/**\n * The sibling plaintext fields stored alongside the ciphertext. Bound as AES-GCM\n * additional authenticated data so a storage-level attacker who can't decrypt the\n * private key also can't silently tamper with these — e.g. mismatching `publicKey`\n * against the wrapped private key, or extending `expiresAt` past its intended TTL.\n */\nexport interface WrappedPrivateKeyMetadata {\n publicKey: Hex;\n createdAt: number;\n expiresAt: number;\n tkmsVersion?: string;\n}\n\n// `new Uint8Array(bytes)` copies into a fresh, plain ArrayBuffer-backed view — needed\n// because a consumer-supplied Uint8Array (or viem's `toBytes()` output) is typed as\n// `Uint8Array<ArrayBufferLike>`, which TS's DOM lib no longer accepts as `BufferSource`\n// (it admits `SharedArrayBuffer`, which `crypto.subtle` doesn't).\nfunction toBufferSource(bytes: Uint8Array): Uint8Array<ArrayBuffer> {\n return new Uint8Array(bytes);\n}\n\n/** Deterministic byte encoding of {@link WrappedPrivateKeyMetadata} for use as AES-GCM AAD.\n * An absent `tkmsVersion` encodes as `null`, distinct from any string value. */\nfunction metadataAad(metadata: WrappedPrivateKeyMetadata): Uint8Array<ArrayBuffer> {\n return toBufferSource(\n new TextEncoder().encode(\n JSON.stringify([\n metadata.publicKey,\n metadata.createdAt,\n metadata.expiresAt,\n metadata.tkmsVersion ?? null,\n ]),\n ),\n );\n}\n\nfunction ikmBytes(secret: string | Uint8Array): Uint8Array<ArrayBuffer> {\n return typeof secret === \"string\" ? new TextEncoder().encode(secret) : toBufferSource(secret);\n}\n\n/**\n * Owns the SDK's copy of a derivation secret and hands out the memoized, non-extractable\n * HKDF base key derived from it. Takes ownership of a `Uint8Array` secret: it is zeroized\n * once the import succeeds, which shortens the raw secret's lifetime and keeps it out of\n * accidental readback paths. It does not prevent exfiltration, since the returned key\n * stays usable for deriving.\n */\nexport class DerivationSecretHolder {\n #secret: string | Uint8Array | undefined;\n #baseKey: Promise<CryptoKey> | undefined;\n\n constructor(secret: string | Uint8Array) {\n this.#secret = secret;\n }\n\n async baseKey(): Promise<CryptoKey> {\n // Memoized so concurrent callers share one import, which consumes #secret exactly once.\n this.#baseKey ??= this.#importBaseKey(this.#secret as string | Uint8Array);\n return this.#baseKey;\n }\n\n async #importBaseKey(secret: string | Uint8Array): Promise<CryptoKey> {\n const ikm = ikmBytes(secret);\n const key = await crypto.subtle.importKey(\"raw\", ikm, \"HKDF\", false, [\"deriveKey\"]);\n ikm.fill(0);\n if (secret instanceof Uint8Array) {\n secret.fill(0);\n }\n // Strings can't be zeroized, so dropping the reference is all that's available.\n this.#secret = undefined;\n return key;\n }\n}\n\n/**\n * Derive a non-extractable AES-256-GCM key from the derivation secret's base key, bound\n * to `identity` (the same signer-address-or-scope identity used for storage keying — see\n * {@link TransportKeyPairVault} — not the storage key string itself, so a future rename\n * of storage-key formatting can never silently change already-wrapped keys).\n *\n * Non-extractable by construction: the raw derived key bytes can never be read back out\n * by any caller, this module included.\n */\nasync function deriveWrappingKey(\n derivationSecret: DerivationSecretHolder,\n identity: string,\n): Promise<CryptoKey> {\n const ikmKey = await derivationSecret.baseKey();\n return crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new TextEncoder().encode(identity),\n info: new TextEncoder().encode(WRAPPING_SCHEME_V1.info),\n },\n ikmKey,\n { name: \"AES-GCM\", length: AES_KEY_LENGTH_BITS },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\n/** Wrap a private key for at-rest storage. Generates a fresh random IV every call. */\nexport async function wrapPrivateKey(\n privateKey: Hex,\n derivationSecret: DerivationSecretHolder,\n identity: string,\n metadata: WrappedPrivateKeyMetadata,\n): Promise<WrappedPrivateKey> {\n const key = await deriveWrappingKey(derivationSecret, identity);\n const iv = crypto.getRandomValues(new Uint8Array(WRAPPING_SCHEME_V1.ivLengthBytes));\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv, additionalData: metadataAad(metadata) },\n key,\n toBufferSource(toBytes(privateKey)),\n );\n return { wrappedPrivateKey: toHex(new Uint8Array(ciphertext)), iv: toHex(iv) };\n}\n\n/**\n * Unwrap a private key. `metadata` must be the exact sibling fields stored alongside\n * the ciphertext — mismatching any of them (or a mismatched `derivationSecret`, or a\n * tampered ciphertext) fails the same way, closed.\n *\n * @throws a `DOMException` named `\"OperationError\"` on any AES-GCM authentication\n * failure: wrong `derivationSecret`, tampered ciphertext, or tampered `metadata`.\n * Any other thrown error (e.g. `crypto.subtle` unavailable) is a distinct failure\n * mode and must not be conflated with an authentication mismatch.\n */\nexport async function unwrapPrivateKey(\n wrapped: WrappedPrivateKey,\n derivationSecret: DerivationSecretHolder,\n identity: string,\n metadata: WrappedPrivateKeyMetadata,\n): Promise<Hex> {\n const key = await deriveWrappingKey(derivationSecret, identity);\n const plaintext = await crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: toBufferSource(toBytes(wrapped.iv)),\n additionalData: metadataAad(metadata),\n },\n key,\n toBufferSource(toBytes(wrapped.wrappedPrivateKey)),\n );\n return toHex(new Uint8Array(plaintext));\n}\n\n/** True for the specific AES-GCM authentication failure `unwrapPrivateKey` throws on\n * a wrong `derivationSecret`, tampered ciphertext, or tampered metadata — false for\n * anything else (e.g. `crypto.subtle` unavailable), which is not a routine mismatch\n * and should not be handled the same way. Checks `.name` rather than `instanceof\n * DOMException`/`instanceof Error`: those can disagree across realms (e.g. a VM-based\n * test sandbox, or a worker's `crypto.subtle` vs. the main thread's global classes). */\nexport function isUnwrapAuthFailure(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"OperationError\"\n );\n}\n","import type { Address } from \"viem\";\nimport { type ChecksummedAddress, checksum } from \"../schemas/primitives\";\nexport { checksum, type ChecksummedAddress };\n\n/** Maximum number of contract addresses a single permit may bind, enforced by the FHE protocol. */\nexport const MAX_CONTRACTS_PER_PERMIT = 10;\n\n/**\n * Maximum `durationDays` a V1 permit accepts (`@fhevm/sdk`'s `MAX_USER_DECRYPT_DURATION_DAYS`).\n * The unsigned typed-data builder `preparePermit` uses does not enforce this itself — only the\n * atomic sign path does — so `preparePermit` checks it explicitly to fail before an\n * out-of-process signing ceremony instead of after.\n */\nexport const MAX_V1_PERMIT_DURATION_DAYS = 365;\n\nexport const SECONDS_PER_DAY = 86400;\n\n/** Current Unix time in whole seconds. */\nexport function nowSeconds(): number {\n return Math.floor(Date.now() / 1000);\n}\n\n/** Deduplicate and sort a list of addresses by their checksummed form. */\nexport function normalizeAddresses(addresses: readonly Address[]): ChecksummedAddress[] {\n return [...new Set(addresses.map(checksum))].sort();\n}\n\n/**\n * Converts an EIP-712 domain's `chainId` from `bigint` to a decimal string, mirroring\n * `@fhevm/sdk`'s internal (unexported) `_toJsonSafeEip712`: its unsigned permit builders\n * return a `bigint` `domain.chainId`, which `JSON.stringify` cannot serialize, so a\n * `PreparedPermit` built from one is not JSON-safe as documented until normalized here.\n * `parseSignedDecryptionPermit` accepts the chainId back as a string or number\n * (`_normalizeSerializedPermitDomainChainId`), so the round-trip is lossless.\n */\nexport function toJsonSafeEip712<T extends { domain: Record<string, unknown> }>(eip712: T): T {\n const { chainId } = eip712.domain;\n if (typeof chainId !== \"bigint\") {\n return eip712;\n }\n return { ...eip712, domain: { ...eip712.domain, chainId: chainId.toString() } };\n}\n","import { z } from \"zod/mini\";\nimport {\n checksummedAddress,\n hex,\n positiveDays,\n positiveSeconds,\n unixSeconds,\n} from \"../schemas/primitives\";\nimport type { PreparedPermit } from \"./types\";\nimport { MAX_CONTRACTS_PER_PERMIT, MAX_V1_PERMIT_DURATION_DAYS, SECONDS_PER_DAY } from \"./utils\";\n\nconst transportKeyPairTTLError = \"transportKeyPairTTL must be a positive integer number of seconds\";\nconst permitTTLError = \"permitTTL must be a positive integer number of days\";\nconst transportKeyPairScopeError = \"transportKeyPairScope must be a non-empty string\";\n/** 256 bits, matching the AES-256 wrapping key HKDF derives from this secret, so the derived key\n * is never weaker than the cipher. A length check can't verify entropy: this is a floor, not a\n * guarantee. */\nexport const MIN_DERIVATION_SECRET_LENGTH_BYTES = 32;\n/** Strings are encoded, so characters carry fewer bits than bytes: 64 covers hex's worst case\n * (4 bits per character), keeping a validated string at or above the same 256-bit floor. */\nexport const MIN_DERIVATION_SECRET_LENGTH_CHARS = 64;\nconst derivationSecretError = `transportKeyPairDerivationSecret must be a Uint8Array of at least ${MIN_DERIVATION_SECRET_LENGTH_BYTES} bytes or a string of at least ${MIN_DERIVATION_SECRET_LENGTH_CHARS} characters (256 bits of entropy): source it from a CSPRNG or secrets manager, not a human-memorable passphrase`;\n\n/** Maximum transportKeyPairTTL accepted by the fhevm ACL contract (365 days, in seconds). */\nexport const MAX_TRANSPORT_KEY_PAIR_TTL_SECONDS = 365 * SECONDS_PER_DAY;\n\n// z.int already rejects NaN, Infinity, and non-integers, so it covers\n// the previous .finite() + .int() combo with a single schema.\nexport const TransportKeyPairTTLSchema = z\n .int({ error: transportKeyPairTTLError })\n .check(\n z.positive({ error: transportKeyPairTTLError }),\n z.maximum(\n MAX_TRANSPORT_KEY_PAIR_TTL_SECONDS,\n `transportKeyPairTTL must not exceed the fhevm ACL maximum of ${MAX_TRANSPORT_KEY_PAIR_TTL_SECONDS}s (365 days)`,\n ),\n );\n\nexport const PermitTTLSchema = z\n .int({ error: permitTTLError })\n .check(\n z.positive({ error: permitTTLError }),\n z.maximum(\n MAX_V1_PERMIT_DURATION_DAYS,\n `permitTTL must not exceed the V1 permit maximum of ${MAX_V1_PERMIT_DURATION_DAYS} days`,\n ),\n );\n\nexport const TransportKeyPairScopeSchema = z\n .string({ error: transportKeyPairScopeError })\n .check(z.minLength(1, transportKeyPairScopeError));\n\nexport const DerivationSecretSchema = z.union(\n [\n z\n .string({ error: derivationSecretError })\n .check(z.minLength(MIN_DERIVATION_SECRET_LENGTH_CHARS, derivationSecretError)),\n z\n .instanceof(Uint8Array, { error: derivationSecretError })\n .check(\n z.refine((v) => v.byteLength >= MIN_DERIVATION_SECRET_LENGTH_BYTES, {\n error: derivationSecretError,\n }),\n ),\n ],\n { error: derivationSecretError },\n);\n\n/** @internal */\nexport const StoredTransportKeyPairSchema = z.object({\n publicKey: hex,\n privateKey: hex,\n createdAt: unixSeconds,\n expiresAt: positiveSeconds,\n tkmsVersion: z.optional(z.string()),\n});\n\nexport const Eip712Schema = z.object({\n domain: z.record(z.string(), z.unknown()),\n primaryType: z.optional(z.string()),\n types: z.record(z.string(), z.array(z.object({ name: z.string(), type: z.string() }))),\n message: z.record(z.string(), z.unknown()),\n});\n\nexport const SerializedPermitSchema = z.object({\n version: z.int().check(z.positive()),\n eip712: Eip712Schema,\n signature: hex,\n signerAddress: checksummedAddress,\n});\n\n/** @internal */\nexport const PermissionSchema = z.object({\n keypairPublicKey: hex,\n contractAddresses: z.array(checksummedAddress).check(z.maxLength(MAX_CONTRACTS_PER_PERMIT)),\n serializedPermit: SerializedPermitSchema,\n startTimestamp: unixSeconds,\n durationDays: positiveDays,\n});\n\nexport const PermissionListSchema = z.array(PermissionSchema);\n\nexport const ScopeIndexSchema = z.array(z.string());\n\n/**\n * Defensive shape validation for the `prepared` argument `registerPermit`\n * receives — a payload that may have crossed a process boundary (custody\n * API, HSM ceremony) and been `JSON.parse`'d back. Mirrors {@link PreparedPermit}.\n */\nexport const PreparedPermitSchema = z.object({\n version: z.literal(1),\n eip712: Eip712Schema,\n signerAddress: checksummedAddress,\n}) satisfies z.ZodMiniType<PreparedPermit>;\n","/**\n * Single owner of the persisted transport key pair format: what an entry looks like on\n * disk, how a value read back from storage is classified, and how the wrapped shape is\n * encoded and decoded. Everything version-specific lives in a codec descriptor, so a\n * future scheme is added here rather than branched on by every reader.\n */\nimport { z } from \"zod/mini\";\nimport { hex, positiveSeconds, unixSeconds } from \"../schemas/primitives\";\nimport {\n unwrapPrivateKey,\n wrapPrivateKey,\n WRAPPING_SCHEME_V1,\n type DerivationSecretHolder,\n type WrappedPrivateKeyMetadata,\n} from \"./keypair-wrapping\";\nimport { StoredTransportKeyPairSchema } from \"./schemas\";\nimport type { StoredTransportKeyPair } from \"./types\";\n\nconst wrappingVersionError = \"wrappingVersion must be a wrapping scheme this SDK can read\";\nconst ivLengthError = `iv must be a ${WRAPPING_SCHEME_V1.ivLengthBytes}-byte hex string`;\nconst wrappedPrivateKeyLengthError = `wrappedPrivateKey must be a whole number of bytes and at least ${WRAPPING_SCHEME_V1.tagLengthBytes} bytes (the AES-GCM authentication tag)`;\n\nfunction hexByteLength(v: string): number {\n return (v.length - 2) / 2;\n}\n\n/**\n * On-disk shape when `transportKeyPairDerivationSecret` is configured, distinct from the\n * plaintext shape every caller outside the vault still sees. `publicKey` is never\n * sensitive and stays unwrapped; only the private key half is encrypted.\n */\nconst WrappedEntryV1Schema = z.object({\n wrappingVersion: z.literal(WRAPPING_SCHEME_V1.version, { error: wrappingVersionError }),\n publicKey: hex,\n // Length-checked, not just shape-checked: a truncated ciphertext/IV (e.g. from a buggy\n // custom GenericStorage adapter) would otherwise reach crypto.subtle.decrypt and fail\n // with the same generic OperationError a genuine wrong-secret case does.\n // Even hex length checked first: an odd digit count is a fractional byte count that would\n // clear the minimum (33 digits reads as 16.5 bytes), and viem's toBytes then left-pads it\n // into a plausible-looking ciphertext that only fails at decrypt time.\n wrappedPrivateKey: hex.check(\n z.refine((v) => v.length % 2 === 0 && hexByteLength(v) >= WRAPPING_SCHEME_V1.tagLengthBytes, {\n error: wrappedPrivateKeyLengthError,\n }),\n ),\n iv: hex.check(\n z.refine((v) => hexByteLength(v) === WRAPPING_SCHEME_V1.ivLengthBytes, {\n error: ivLengthError,\n }),\n ),\n createdAt: unixSeconds,\n expiresAt: positiveSeconds,\n tkmsVersion: z.optional(z.string()),\n});\n\n/** @internal */\nexport type WrappedPrivateKeyEntry = z.infer<typeof WrappedEntryV1Schema>;\n\n/** Everything this vault ever hands to `storage.set`. @internal */\nexport type PersistedTransportKeyPair = StoredTransportKeyPair | WrappedPrivateKeyEntry;\n\n/** One wrapping scheme: its identity on disk, its entry schema, and its codec. */\ninterface WrappedEntryCodec {\n readonly version: number;\n readonly schema: z.ZodMiniType<WrappedPrivateKeyEntry>;\n encode(\n keyPair: StoredTransportKeyPair,\n derivationSecret: DerivationSecretHolder,\n identity: string,\n ): Promise<WrappedPrivateKeyEntry>;\n decode(\n entry: WrappedPrivateKeyEntry,\n derivationSecret: DerivationSecretHolder,\n identity: string,\n ): Promise<StoredTransportKeyPair>;\n}\n\n/** The sibling plaintext fields bound as AES-GCM additional authenticated data. */\nfunction metadataOf(entry: {\n publicKey: WrappedPrivateKeyMetadata[\"publicKey\"];\n createdAt: number;\n expiresAt: number;\n tkmsVersion?: string;\n}): WrappedPrivateKeyMetadata {\n const metadata: WrappedPrivateKeyMetadata = {\n publicKey: entry.publicKey,\n createdAt: entry.createdAt,\n expiresAt: entry.expiresAt,\n };\n if (entry.tkmsVersion) {\n metadata.tkmsVersion = entry.tkmsVersion;\n }\n return metadata;\n}\n\nconst CODEC_V1: WrappedEntryCodec = {\n version: WRAPPING_SCHEME_V1.version,\n schema: WrappedEntryV1Schema,\n async encode(keyPair, derivationSecret, identity) {\n const { wrappedPrivateKey, iv } = await wrapPrivateKey(\n keyPair.privateKey,\n derivationSecret,\n identity,\n metadataOf(keyPair),\n );\n const entry: WrappedPrivateKeyEntry = {\n wrappingVersion: WRAPPING_SCHEME_V1.version,\n publicKey: keyPair.publicKey,\n wrappedPrivateKey,\n iv,\n createdAt: keyPair.createdAt,\n expiresAt: keyPair.expiresAt,\n };\n if (keyPair.tkmsVersion) {\n entry.tkmsVersion = keyPair.tkmsVersion;\n }\n return entry;\n },\n async decode(entry, derivationSecret, identity) {\n const privateKey = await unwrapPrivateKey(\n { wrappedPrivateKey: entry.wrappedPrivateKey, iv: entry.iv },\n derivationSecret,\n identity,\n metadataOf(entry),\n );\n const keyPair: StoredTransportKeyPair = {\n publicKey: entry.publicKey,\n privateKey,\n createdAt: entry.createdAt,\n expiresAt: entry.expiresAt,\n };\n if (entry.tkmsVersion) {\n keyPair.tkmsVersion = entry.tkmsVersion;\n }\n return keyPair;\n },\n};\n\n/** Every scheme this build can read, keyed by the version persisted with the entry. */\nconst CODECS = new Map<number, WrappedEntryCodec>([[CODEC_V1.version, CODEC_V1]]);\n\n/** The scheme new entries are written under. */\nconst WRITER_CODEC = CODEC_V1;\n\n/**\n * How a raw stored value reads under this build's knowledge of the format. The three\n * failure kinds are distinct because they call for different operator action: an\n * unsupported version needs a build that knows it, a corrupt wrapped entry still holds\n * someone's ciphertext, and an unrecognized value holds nothing worth protecting.\n */\nexport type ClassifiedEntry =\n | { readonly kind: \"plaintext\"; readonly keyPair: StoredTransportKeyPair }\n | {\n readonly kind: \"wrapped\";\n readonly expiresAt: number;\n readonly decode: (\n derivationSecret: DerivationSecretHolder,\n identity: string,\n ) => Promise<StoredTransportKeyPair>;\n }\n | { readonly kind: \"unsupported-version\"; readonly version: unknown }\n | { readonly kind: \"corrupt-wrapped\" }\n | { readonly kind: \"unrecognized\" };\n\n/**\n * Version-agnostic probe for a value that carries a wrapped private key. Deliberately\n * shallow and independent of any codec: an entry written by a scheme this build cannot\n * read must still be recognized as someone's ciphertext, or it gets regenerated over.\n */\nfunction isWrappedEnvelope(raw: object): boolean {\n const entry = raw as Record<string, unknown>;\n return (\n entry.wrappingVersion !== undefined ||\n (entry.wrappedPrivateKey !== undefined && entry.iv !== undefined)\n );\n}\n\n/** Classify a value read back from storage. Never throws and never decrypts. */\nexport function classifyPersistedEntry(raw: unknown): ClassifiedEntry {\n if (typeof raw !== \"object\" || raw === null || !isWrappedEnvelope(raw)) {\n const plaintext = StoredTransportKeyPairSchema.safeParse(raw);\n return plaintext.success\n ? { kind: \"plaintext\", keyPair: plaintext.data }\n : { kind: \"unrecognized\" };\n }\n\n const version = (raw as Record<string, unknown>).wrappingVersion;\n const codec = typeof version === \"number\" ? CODECS.get(version) : undefined;\n if (codec === undefined) {\n return { kind: \"unsupported-version\", version };\n }\n\n const parsed = codec.schema.safeParse(raw);\n if (!parsed.success) {\n return { kind: \"corrupt-wrapped\" };\n }\n const entry = parsed.data;\n return {\n kind: \"wrapped\",\n expiresAt: entry.expiresAt,\n decode: (derivationSecret, identity) => codec.decode(entry, derivationSecret, identity),\n };\n}\n\n/** Encode a key pair under the scheme this build writes. */\nexport function encodeWrappedEntry(\n keyPair: StoredTransportKeyPair,\n derivationSecret: DerivationSecretHolder,\n identity: string,\n): Promise<WrappedPrivateKeyEntry> {\n return WRITER_CODEC.encode(keyPair, derivationSecret, identity);\n}\n","import type { ChecksummedAddress } from \"../schemas/primitives\";\n\nexport interface PermissionScope {\n signerAddress: ChecksummedAddress;\n chainId: number;\n delegatorAddress: ChecksummedAddress;\n}\n\nexport function transportKeyPairStorageKey(signerAddress: ChecksummedAddress): string {\n // The \"keypair:\" prefix is intentionally preserved for back-compat with already-persisted entries.\n return `keypair:${signerAddress}`;\n}\n\n/**\n * Storage key for a scope-shared transport key pair (opt-in, B2B2C/WaaS operators).\n * Namespaced under \"keypair:scope:\" so it can never collide with a per-signer key,\n * whose suffix is always a checksummed `0x`-address.\n */\nexport function transportKeyPairScopeStorageKey(scope: string): string {\n return `keypair:scope:${scope}`;\n}\n\nexport function permissionScopeKey(scope: PermissionScope): string {\n return `permits:${scope.signerAddress}:${scope.chainId}:${scope.delegatorAddress}`;\n}\n\nexport function permissionIndexKey(signerAddress: ChecksummedAddress): string {\n return `permits-index:${signerAddress}`;\n}\n","import type { SerializeTransportKeyPairReturnType } from \"@fhevm/sdk/actions/chain\";\nimport { KeyWrappingError, TransportKeyPairChangedError } from \"../errors/credential\";\nimport type { ChecksummedAddress } from \"../schemas/primitives\";\nimport type { GenericLogger, GenericStorage } from \"../types\";\nimport { swallow } from \"../utils/swallow\";\nimport {\n classifyPersistedEntry,\n encodeWrappedEntry,\n type PersistedTransportKeyPair,\n} from \"./keypair-entry-codec\";\nimport { isUnwrapAuthFailure, type DerivationSecretHolder } from \"./keypair-wrapping\";\nimport { transportKeyPairScopeStorageKey, transportKeyPairStorageKey } from \"./storage-keys\";\nimport type { StoredTransportKeyPair } from \"./types\";\nimport { nowSeconds } from \"./utils\";\n\ninterface TransportKeyPairVaultConfig {\n generator: () => Promise<SerializeTransportKeyPairReturnType>;\n storage: GenericStorage;\n /** Transport key pair lifetime in seconds. Pre-validated by the caller. */\n ttl: number;\n /** SDK-wide logger for best-effort storage diagnostics. */\n logger: GenericLogger;\n /**\n * Opt-in shared-tenant scope (B2B2C/WaaS). When set, every signer configured with\n * this scope reads/creates the *same* key pair instead of one per signer address.\n * Undefined preserves the default per-signer behavior.\n */\n scope?: string;\n /**\n * Opt-in at-rest wrapping for headless contexts with no secure storage backend to\n * delegate to (CLI tools, bare-metal agents, local dev). When set, the private key\n * half is encrypted before every `storage.set()` and decrypted after every\n * `storage.get()` — transparently, at this vault's storage boundary only. Undefined\n * preserves the default: plaintext, security delegated to the storage backend.\n */\n derivationSecret?: DerivationSecretHolder;\n}\n\nfunction scopeIdentity(scope: string): string {\n return `scope:${scope}`;\n}\n\n/** Why a stored entry can't be served to this instance. */\ntype RecoveryReason =\n | \"wrapped-without-secret\"\n | \"unwrapped-under-secret\"\n | \"unwrap-failed\"\n | \"corrupt-wrapped\";\n\n/** Operator-facing diagnostic for a scope whose shared entry is left in place. */\nfunction scopedFailureMessage(reason: RecoveryReason, scope: string): string {\n const messages: Record<RecoveryReason, string> = {\n \"wrapped-without-secret\":\n `Transport key pair for scope \"${scope}\" is wrapped, but this instance has no ` +\n \"transportKeyPairDerivationSecret configured. Every instance sharing this scope must use \" +\n \"the same transportKeyPairDerivationSecret; once they do, call \" +\n \"permits.revokeTransportKeyPair() to rotate the entry.\",\n \"unwrapped-under-secret\":\n `Transport key pair for scope \"${scope}\" is stored unwrapped, but this instance is ` +\n \"configured with a transportKeyPairDerivationSecret. A peer instance sharing this scope \" +\n \"is running without the secret: configure the same transportKeyPairDerivationSecret \" +\n \"everywhere, then call permits.revokeTransportKeyPair() to migrate the scope to a \" +\n \"wrapped entry.\",\n \"unwrap-failed\":\n `Transport key pair for scope \"${scope}\" failed to unwrap. This usually means another ` +\n \"instance created it with a different transportKeyPairDerivationSecret: every instance \" +\n \"sharing this scope must use the same secret. It can also mean the stored entry is \" +\n \"corrupted.\",\n \"corrupt-wrapped\":\n `Transport key pair for scope \"${scope}\" is wrapped but structurally invalid: the stored ` +\n \"iv or ciphertext is not a shape this wrapping scheme can produce. This usually means \" +\n \"the stored entry is corrupted, or instances sharing this scope are running mismatched \" +\n \"wrapping-scheme versions.\",\n };\n return messages[reason];\n}\n\n/**\n * Operator-facing diagnostic for a per-signer entry this instance can't read at all: a\n * missing secret is a deployment mistake far more often than an intentional downgrade,\n * and self-healing would drop at-rest wrapping without anyone noticing.\n */\nconst UNSCOPED_WRAPPED_WITHOUT_SECRET_MESSAGE =\n \"Transport key pair for this signer is wrapped, but this instance has no \" +\n \"transportKeyPairDerivationSecret configured. Configure the same \" +\n \"transportKeyPairDerivationSecret the entry was written with. To downgrade to plaintext \" +\n \"at rest on purpose, discard the stored entry first by calling \" +\n \"permits.clear(), then run without the secret.\";\n\n/** Discard reason for a per-signer entry, which self-heals instead of failing loudly. */\nconst UNSCOPED_FAILURE_REASONS: Record<\n Exclude<RecoveryReason, \"wrapped-without-secret\">,\n string\n> = {\n \"unwrapped-under-secret\":\n \"unwrapped transport key pair entry found while a transportKeyPairDerivationSecret is configured\",\n \"unwrap-failed\":\n \"transport key pair entry failed to unwrap (wrong transportKeyPairDerivationSecret?)\",\n \"corrupt-wrapped\": \"structurally invalid wrapped transport key pair entry\",\n};\n\nfunction unsupportedVersionMessage(version: unknown, scope: string | undefined): string {\n const label =\n typeof version === \"number\"\n ? `wrappingVersion ${version}`\n : \"no wrappingVersion this build recognizes\";\n const target = scope !== undefined ? `for scope \"${scope}\"` : \"for this signer\";\n const remedy =\n scope !== undefined\n ? \"run a build that supports it everywhere, or call permits.revokeTransportKeyPair() to rotate the scope\"\n : \"run a build that supports it, or call permits.clear() to discard the entry deliberately\";\n return (\n `Transport key pair ${target} was written under a wrapping scheme this SDK build cannot ` +\n `read (${label}). The entry is preserved instead of regenerated, since an instance that ` +\n `understands the scheme may still be using it: ${remedy}.`\n );\n}\n\n/**\n * Identity-scoped, chain-independent vault for ML-KEM transport key pairs.\n *\n * One transport key pair per signer address by default; the key pair survives chain\n * switches and permit revocations. When `scope` is configured, reads/creates key\n * off the shared scope identity instead of the signer address. {@link clear} always\n * targets the per-signer key regardless of scope — an individual signer's teardown must\n * never delete a scope's shared key pair. Only {@link clearScope} can do that.\n *\n * When `derivationSecret` is configured, the private key half is wrapped at rest. That is\n * invisible to every method here and to every caller outside this class: `getOrCreate`/\n * `readStored` always return the plaintext {@link StoredTransportKeyPair} shape regardless\n * of how the entry is actually stored on disk.\n */\nexport class TransportKeyPairVault {\n readonly #generator: () => Promise<SerializeTransportKeyPairReturnType>;\n readonly #storage: GenericStorage;\n readonly #ttl: number;\n readonly #logger: GenericLogger;\n readonly #scope: string | undefined;\n readonly #derivationSecret: DerivationSecretHolder | undefined;\n readonly #pending = new Map<string, Promise<StoredTransportKeyPair>>();\n /**\n * Per-key generation counter, bumped by {@link clearScope}. Lets a generation that\n * was already in flight when a rotation happened detect it's stale once its\n * `#generator()` round trip completes, so it skips persisting a key that would\n * silently resurrect the one the rotation just deleted.\n */\n readonly #epoch = new Map<string, number>();\n\n constructor(config: TransportKeyPairVaultConfig) {\n this.#generator = config.generator;\n this.#storage = config.storage;\n this.#ttl = config.ttl;\n this.#logger = config.logger;\n this.#scope = config.scope;\n this.#derivationSecret = config.derivationSecret;\n }\n\n /**\n * Identity a wrapped key is bound to (the HKDF salt). Prefixed by kind so a scope\n * named after a checksummed address can never derive the same wrapping key as that\n * signer. Deliberately not the storage key string: that prefix is a storage-layer\n * detail, and renaming it must never stop already-wrapped keys from decrypting.\n */\n #identity(signerAddress: ChecksummedAddress): string {\n return this.#scope !== undefined ? scopeIdentity(this.#scope) : `signer:${signerAddress}`;\n }\n\n /** Storage key for reads/creates: the shared scope key when configured, else the per-signer key. */\n #identityKey(signerAddress: ChecksummedAddress): string {\n return this.#scope !== undefined\n ? transportKeyPairScopeStorageKey(this.#scope)\n : transportKeyPairStorageKey(signerAddress);\n }\n\n async readStored(signerAddress: ChecksummedAddress): Promise<StoredTransportKeyPair | null> {\n return this.#readByKey(this.#identityKey(signerAddress), this.#identity(signerAddress));\n }\n\n async #readByKey(key: string, identity: string): Promise<StoredTransportKeyPair | null> {\n const raw = await this.#storage.get(key);\n if (raw === null || raw === undefined) {\n return null;\n }\n\n const entry = classifyPersistedEntry(raw);\n if (entry.kind === \"unsupported-version\") {\n // Preserved for every configuration, scoped or not: only a build that knows the\n // scheme can read this ciphertext, and regenerating would destroy it for good.\n throw this.#failLoudly(key, unsupportedVersionMessage(entry.version, this.#scope));\n }\n\n if (entry.kind === \"unrecognized\") {\n // Neither shape holds a key pair any instance could read, so there is nothing to\n // protect by keeping it, scope or no scope.\n await this.#discard(key, \"malformed transport key pair entry\");\n return null;\n }\n\n const derivationSecret = this.#derivationSecret;\n if (derivationSecret === undefined) {\n if (entry.kind === \"plaintext\") {\n return (await this.#discardIfExpired(key, entry.keyPair.expiresAt)) ? null : entry.keyPair;\n }\n // Corrupt or intact, a wrapped entry is unreadable here for the same single reason.\n return this.#recover(key, \"wrapped-without-secret\");\n }\n\n if (entry.kind === \"plaintext\") {\n return this.#recover(key, \"unwrapped-under-secret\");\n }\n if (entry.kind === \"corrupt-wrapped\") {\n return this.#recover(key, \"corrupt-wrapped\");\n }\n if (await this.#discardIfExpired(key, entry.expiresAt)) {\n return null;\n }\n\n try {\n return await entry.decode(derivationSecret, identity);\n } catch (error) {\n if (!isUnwrapAuthFailure(error)) {\n // Not a wrong secret or a tampered entry but an environment problem (e.g.\n // crypto.subtle unavailable), which must not read as a routine cache miss.\n this.#logger.error(\"transport key pair unwrap failed unexpectedly\", { key, error });\n throw new KeyWrappingError(\"Transport key pair unwrap failed unexpectedly.\", {\n cause: error,\n });\n }\n return this.#recover(key, \"unwrap-failed\", error);\n }\n }\n\n /**\n * The one place that decides what happens to an entry this instance can't serve. A\n * scope's entry is shared, and AES-GCM can't distinguish a peer's valid entry wrapped\n * under a different secret from genuine corruption, so discarding it risks clobbering a\n * key pair the whole cohort is using: scoped vaults fail loudly and leave it in place,\n * unscoped ones self-heal since only this signer is affected. The one unscoped case that\n * also fails loudly is a wrapped entry with no secret configured, where self-healing\n * would turn a missing secret into a silent plaintext downgrade.\n */\n async #recover(key: string, reason: RecoveryReason, cause?: unknown): Promise<null> {\n if (this.#scope !== undefined) {\n throw this.#failLoudly(key, scopedFailureMessage(reason, this.#scope), cause);\n }\n if (reason === \"wrapped-without-secret\") {\n throw this.#failLoudly(key, UNSCOPED_WRAPPED_WITHOUT_SECRET_MESSAGE, cause);\n }\n const discardReason = UNSCOPED_FAILURE_REASONS[reason];\n // Warned, not just discarded: a regenerated entry can silently lose the at-rest\n // wrapping the discarded one had.\n this.#logger.warn(discardReason, { key });\n await this.#discard(key, discardReason);\n return null;\n }\n\n /** Logged where the storage key is still in hand, then thrown for the caller to act on. */\n #failLoudly(key: string, message: string, cause?: unknown): KeyWrappingError {\n this.#logger.error(message, { key });\n return new KeyWrappingError(message, { cause });\n }\n\n /** An entry past its TTL is a cache miss: dropped so the next call regenerates it. */\n async #discardIfExpired(key: string, expiresAt: number): Promise<boolean> {\n if (nowSeconds() < expiresAt) {\n return false;\n }\n await this.#discard(key, \"expired transport key pair entry\");\n return true;\n }\n\n async #discard(key: string, reason: string): Promise<void> {\n this.#logger.debug(\"discarding transport key pair entry\", { key, reason });\n await swallow(\"delete transport key pair entry\", () => this.#storage.delete(key), this.#logger);\n }\n\n /**\n * Return the cached transport key pair, generating and persisting a fresh one if absent or expired.\n *\n * @remarks Deduplicates concurrent calls within this process. Across separate\n * processes/instances sharing a scope, two first-time callers can still race the\n * underlying storage — unlike the per-signer version of this race, the loser here can\n * be a *different* end-user, whose existing permit then gets silently pruned as stale.\n * Pre-warm a scope once via {@link warmScope} before opening concurrent traffic to\n * avoid hitting this with a whole cohort at once.\n *\n * @throws if the stored entry was written under a wrapping scheme this build cannot\n * read, in any configuration: it is preserved rather than regenerated over.\n * {@link KeyWrappingError}\n * @throws if the stored entry is wrapped and no `derivationSecret` is configured, in\n * any configuration: regenerating would downgrade this signer to plaintext at rest\n * without a signal. {@link KeyWrappingError}\n * @throws if `derivationSecret` is configured together with `scope` and the stored\n * entry can't be served (fails to unwrap, or is stored in an unexpected shape),\n * since silently regenerating would overwrite a scope's shared entry that may simply\n * be wrapped under a different (but valid) `derivationSecret` by another instance.\n * The unscoped equivalent self-heals. {@link KeyWrappingError}\n * @throws if wrapping the freshly generated private key fails (e.g. `crypto.subtle`\n * unavailable in this environment). {@link KeyWrappingError}\n * @throws with `options.strict`, if a concurrent {@link clearScope} rotated this key\n * while a fresh one was being generated: persistence is skipped either way (see the\n * epoch check in the implementation), but a strict caller cannot silently return the\n * unpersisted result. {@link TransportKeyPairChangedError}\n * @param options.strict - When `true`, a `storage.set` failure during persistence\n * rejects instead of being logged and swallowed. Use for callers that hand a\n * freshly generated key pair to an out-of-process ceremony (offline permits):\n * an unpersisted key pair would silently strand that ceremony's result once the\n * external signer returns, so preparation must fail immediately instead.\n */\n async getOrCreate(\n signerAddress: ChecksummedAddress,\n options?: { strict?: boolean },\n ): Promise<StoredTransportKeyPair> {\n return this.#getOrCreateByKey(this.#identityKey(signerAddress), this.#identity(signerAddress), {\n strict: options?.strict,\n });\n }\n\n /**\n * @param strict - When `true`, a `storage.set` failure during persistence rejects\n * instead of being logged and swallowed. Used by {@link warmScope}, which — like\n * {@link clearScope} — must not resolve successfully while its write silently failed.\n */\n async #getOrCreateByKey(\n key: string,\n identity: string,\n options?: { strict?: boolean },\n ): Promise<StoredTransportKeyPair> {\n const strict = options?.strict ?? false;\n // Keyed on (key, strict), not key alone: a strict caller (warmScope) and a\n // non-strict caller (getOrCreate) can target the same shared scope identity, and\n // must never adopt each other's persistence guarantee — whichever registers first\n // would otherwise silently impose its strictness on the other. See the strict\n // param doc above.\n const pendingKey = `${key}::${strict}`;\n const existing = this.#pending.get(pendingKey);\n if (existing) {\n return existing;\n }\n\n const epochAtStart = this.#epoch.get(key) ?? 0;\n\n const promise = (async () => {\n const cached = await this.#readByKey(key, identity);\n if (cached !== null) {\n return cached;\n }\n const fresh = await this.#generator();\n const createdAt = nowSeconds();\n const expiresAt = createdAt + this.#ttl;\n const stored: StoredTransportKeyPair = {\n publicKey: fresh.publicKey,\n privateKey: fresh.privateKey,\n createdAt,\n expiresAt,\n };\n // Persist the TKMS version so a later parse deserializes the private key\n // under the version it was generated with, surviving a KMS/TKMS rotation.\n if (fresh.tkmsVersion) {\n stored.tkmsVersion = fresh.tkmsVersion;\n }\n\n let entry: PersistedTransportKeyPair = stored;\n if (this.#derivationSecret !== undefined) {\n try {\n entry = await encodeWrappedEntry(stored, this.#derivationSecret, identity);\n } catch (error) {\n const message = \"Failed to wrap the transport private key for at-rest storage.\";\n this.#logger.error(message, { key, error });\n throw new KeyWrappingError(message, { cause: error });\n }\n }\n\n // If a rotation bumped this key's epoch while `#generator()` (and, when\n // derivationSecret is configured, the wrap above) were in flight, this\n // generation started before the rotation and must not persist — doing so would\n // silently resurrect the key the rotation just deleted (see `clearScope`).\n if ((this.#epoch.get(key) ?? 0) === epochAtStart) {\n if (strict) {\n await this.#storage.set(key, entry);\n } else {\n await swallow(\n \"persist transport key pair\",\n () => this.#storage.set(key, entry),\n this.#logger,\n );\n }\n } else if (strict) {\n // Non-strict callers still return the unpersisted key pair here (the\n // next access regenerates and persists against the new epoch), which is\n // fine for a best-effort prefetch. A strict caller cannot: it promises\n // its result is durably stored, and a caller like `preparePermit` hands\n // this key's public key to an out-of-process signing ceremony that can\n // take hours — returning an unpersisted key would silently strand that\n // ceremony's result once the signer comes back and `registerPermit`\n // can't find a matching key pair.\n throw new TransportKeyPairChangedError(\n \"The transport key pair was rotated while it was being generated, so the freshly \" +\n \"generated key pair could not be persisted. Retry.\",\n );\n }\n return stored;\n })().finally(() => {\n this.#pending.delete(pendingKey);\n });\n\n this.#pending.set(pendingKey, promise);\n return promise;\n }\n\n /**\n * Delete the stored transport key pair for the given signer address.\n *\n * Always targets the per-signer key, never the shared scope key — even when this\n * vault is configured with a scope. Signer-level teardown (disconnect, revoke) must\n * never take down a shared-tenant key pair; use {@link clearScope} for that.\n *\n * Best-effort: a storage failure is logged and swallowed. Worst case, one signer's\n * stale key lingers until its TTL — a low-stakes, self-correcting outcome.\n */\n async clear(signerAddress: ChecksummedAddress): Promise<void> {\n const key = transportKeyPairStorageKey(signerAddress);\n await swallow(\"delete transport key pair entry\", () => this.#storage.delete(key), this.#logger);\n }\n\n /**\n * Delete the stored key pair for this identity so the next {@link getOrCreate}\n * regenerates it. Unlike {@link clear}, this is **scope-aware** — it targets the\n * shared scope key when a scope is configured, else the per-signer key.\n *\n * The self-heal primitive for a key pair the relayer rejects as invalid (e.g. a\n * TKMS-version mismatch after a KMS rotation, which surfaces as\n * `invalid TransportKeyPairKeyPair`): the stored bytes can no longer be parsed,\n * so dropping and regenerating them is the only recovery. Best-effort — a\n * storage failure is logged and swallowed, so a wedged entry is retried on the\n * next call rather than hard-failing the current one.\n */\n async evict(signerAddress: ChecksummedAddress): Promise<void> {\n const key = this.#identityKey(signerAddress);\n await swallow(\"evict transport key pair entry\", () => this.#storage.delete(key), this.#logger);\n }\n\n /**\n * Delete the shared scope's transport key pair (operator-level rotation).\n *\n * Every permit in the scope embeds the old key pair's public key, so once it's gone,\n * `pruneUnusable` drops all of them as stale on next access — no separate permit\n * cleanup needed. No-op if no scope is configured on this vault.\n *\n * Unlike {@link clear}, this is **not** best-effort: a storage failure propagates\n * instead of being swallowed. This is the primitive operators reach for on suspected\n * key compromise — a caller that gets a resolved promise must be able to trust that\n * the key pair is actually gone, not silently still live because a transient storage\n * error (exactly the kind of failure likely during an incident) was logged and\n * dropped.\n *\n * Also bumps the key's generation epoch *before* deleting, so a `getOrCreate`/\n * `warmScope` generation already in flight for this same scope discards its result\n * instead of persisting it once the round trip completes — otherwise that write would\n * land after this delete and silently resurrect the key this call just removed. This\n * relies on the backing {@link GenericStorage} applying same-key writes in dispatch\n * order — true for `MemoryStorage` and conventional stores, but a custom async\n * adapter without that guarantee could still let an in-flight `set` land after this\n * `delete`.\n */\n async clearScope(): Promise<void> {\n if (this.#scope === undefined) {\n return;\n }\n const key = transportKeyPairScopeStorageKey(this.#scope);\n this.#epoch.set(key, (this.#epoch.get(key) ?? 0) + 1);\n await this.#storage.delete(key);\n }\n\n /**\n * Generate and persist the shared scope's transport key pair if absent — the\n * pre-warm counterpart to {@link clearScope}. No-op if no scope is configured.\n *\n * Unlike {@link getOrCreate}, this needs no signer address at all: a scoped identity\n * never depends on one. Operators use this to warm a scope once, deliberately,\n * before opening concurrent traffic to it — see the class docs on {@link getOrCreate}\n * for why that avoids a thundering-herd race across the whole cohort.\n *\n * Not best-effort: like {@link clearScope}, a `storage.set` failure during persistence\n * propagates instead of being swallowed. A resolved promise here is meant to mean the\n * key pair is actually warmed in shared storage — an operator pre-warming ahead of\n * traffic to avoid the thundering-herd race must be able to trust that, not silently\n * still be exposed to it because a transient write failure was logged and dropped.\n *\n * @throws if a concurrent {@link clearScope} rotates the key while this call is\n * generating one: for the same reason, this must reject rather than resolve with an\n * unpersisted key pair. {@link TransportKeyPairChangedError}\n */\n async warmScope(): Promise<void> {\n if (this.#scope === undefined) {\n return;\n }\n await this.#getOrCreateByKey(\n transportKeyPairScopeStorageKey(this.#scope),\n scopeIdentity(this.#scope),\n { strict: true },\n );\n }\n}\n","import type { Hex } from \"viem\";\nimport type { Permission } from \"./types\";\nimport type { ChecksummedAddress } from \"../schemas/primitives\";\nimport { MAX_CONTRACTS_PER_PERMIT, SECONDS_PER_DAY } from \"./utils\";\n\n/** Contracts in `requested` not covered by the signed payload of any permission. */\nexport function uncoveredContracts(\n permissions: readonly Permission[],\n requested: readonly ChecksummedAddress[],\n): ChecksummedAddress[] {\n const covered = new Set(permissions.flatMap((p) => p.contractAddresses));\n return requested.filter((addr) => !covered.has(addr));\n}\n\n/** Split a list of addresses into permit-sized chunks (≤ {@link MAX_CONTRACTS_PER_PERMIT}). */\nexport function chunkContracts(addresses: readonly ChecksummedAddress[]): ChecksummedAddress[][] {\n const chunks: ChecksummedAddress[][] = [];\n for (let i = 0; i < addresses.length; i += MAX_CONTRACTS_PER_PERMIT) {\n chunks.push(addresses.slice(i, i + MAX_CONTRACTS_PER_PERMIT));\n }\n return chunks;\n}\n\n/** Drop permissions that are time-expired or bound to a stale keypair. */\nexport function pruneUnusable(\n permissions: readonly Permission[],\n keypairPublicKey: Hex,\n nowSeconds: number,\n): Permission[] {\n return permissions.filter(\n (p) =>\n p.keypairPublicKey === keypairPublicKey &&\n nowSeconds < p.startTimestamp + p.durationDays * SECONDS_PER_DAY,\n );\n}\n\n/** Drop every permission whose signed payload touches any address in `contracts`. */\nexport function withoutPermitsTouching(\n permissions: readonly Permission[],\n contracts: readonly ChecksummedAddress[],\n): Permission[] {\n const removeSet = new Set(contracts);\n return permissions.filter((p) => !p.contractAddresses.some((a) => removeSet.has(a)));\n}\n\n/** Deduplicate and sort the union of two pre-checksummed address lists. */\nexport function sortedUnion<T extends string>(a: readonly T[], b: readonly T[]): T[] {\n return [...new Set([...a, ...b])].sort();\n}\n\n/**\n * Find the in-scope permit best suited to be widened with `uncovered`.\n *\n * Returns the permit whose `contractAddresses ∪ uncovered` still fits\n * the 10-contract cap and that has the largest overlap with `requested`. Ties\n * broken by most-recent `startTimestamp`. Returns `null` when none fits.\n */\nexport function findPermitToWiden(\n permissions: readonly Permission[],\n uncovered: readonly ChecksummedAddress[],\n requested: readonly ChecksummedAddress[],\n): Permission | null {\n const requestedSet = new Set(requested);\n const feasible = permissions.filter(\n (p) => new Set([...p.contractAddresses, ...uncovered]).size <= MAX_CONTRACTS_PER_PERMIT,\n );\n if (feasible.length === 0) {\n return null;\n }\n\n function overlap(p: Permission) {\n return p.contractAddresses.reduce((n, a) => n + (requestedSet.has(a) ? 1 : 0), 0);\n }\n\n const maxOverlap = Math.max(...feasible.map(overlap));\n const topTier = feasible.filter((p) => overlap(p) === maxOverlap);\n return topTier.reduce((a, b) => (b.startTimestamp > a.startTimestamp ? b : a));\n}\n","import type { Hex } from \"viem\";\nimport type { GenericLogger, GenericStorage } from \"../types\";\nimport { swallow } from \"../utils/swallow\";\nimport { pruneUnusable, withoutPermitsTouching } from \"./permissions\";\nimport { PermissionListSchema, PermissionSchema, ScopeIndexSchema } from \"./schemas\";\nimport { permissionIndexKey, permissionScopeKey, type PermissionScope } from \"./storage-keys\";\nimport type { Permission } from \"./types\";\nimport type { ChecksummedAddress } from \"../schemas/primitives\";\n\nexport type { PermissionScope };\n\ninterface PermissionStoreConfig {\n storage: GenericStorage;\n /** SDK-wide logger for best-effort storage diagnostics. */\n logger: GenericLogger;\n}\n\n/**\n * Chain-scoped, 1-to-many store of EIP-712 permits.\n *\n * Permits are grouped by `(signerAddress, chainId, delegatorAddress)` so that\n * direct (`delegator === signer`) and delegated permits never collide. An\n * auxiliary per-signer index lists every scope so `clearAllForSigner` can\n * cascade across chains and delegators without enumerating storage keys.\n */\nexport class PermissionStore {\n readonly #storage: GenericStorage;\n readonly #logger: GenericLogger;\n\n constructor(config: PermissionStoreConfig) {\n this.#storage = config.storage;\n this.#logger = config.logger;\n }\n\n /** Read all stored permissions for the given scope. Returns `[]` if none. */\n async list(scope: PermissionScope): Promise<Permission[]> {\n const key = permissionScopeKey(scope);\n const raw = await this.#storage.get(key);\n if (raw === null || raw === undefined) {\n return [];\n }\n const parsed = PermissionListSchema.safeParse(raw);\n if (!parsed.success) {\n await this.clearScope(scope);\n return [];\n }\n return parsed.data;\n }\n\n /**\n * Return permissions still live and bound to the current keypair.\n * Expired or stale-keypair permissions are pruned in one storage pass.\n */\n async listUsableAndPrune(scope: PermissionScope, keypairPublicKey: Hex): Promise<Permission[]> {\n const all = await this.list(scope);\n const usable = pruneUnusable(all, keypairPublicKey, Math.floor(Date.now() / 1000));\n\n if (usable.length !== all.length) {\n if (usable.length === 0) {\n await this.clearScope(scope);\n } else {\n await swallow(\n \"update permit entry\",\n () => this.#storage.set(permissionScopeKey(scope), usable),\n this.#logger,\n );\n }\n }\n return usable;\n }\n\n /**\n * Append signed permits to the given scope. Every permission is run through\n * `PermissionSchema` so the schema is the single normalization point for both\n * read and write paths. Signed payloads themselves are immutable and never\n * edited — only addresses and types are normalized.\n */\n async append(scope: PermissionScope, permissions: readonly Permission[]): Promise<void> {\n if (permissions.length === 0) {\n return;\n }\n const validated = permissions.map((p) => PermissionSchema.parse(p));\n const existing = await this.list(scope);\n await this.#storage.set(permissionScopeKey(scope), [...existing, ...validated]);\n await this.#trackScope(scope);\n }\n\n /**\n * Replace the entry identified by `oldSignature` with `newPermission`.\n *\n * Atomic relative to the storage backend's `set`: the old entry survives in\n * storage until the new list is written. If `oldSignature` is not found\n * (raced eviction or concurrent prune), `newPermission` is still persisted —\n * equivalent to {@link append}.\n */\n async replace(\n scope: PermissionScope,\n oldSignature: Hex,\n newPermission: Permission,\n ): Promise<void> {\n const validated = PermissionSchema.parse(newPermission);\n const existing = await this.list(scope);\n const filtered = existing.filter((p) => p.serializedPermit.signature !== oldSignature);\n await this.#storage.set(permissionScopeKey(scope), [...filtered, validated]);\n await this.#trackScope(scope);\n }\n\n /**\n * Delete every permit whose signed payload touches any listed contract.\n *\n * The store never edits `contractAddresses`, because that field is part\n * of the EIP-712 payload covered by `signature`.\n */\n async deletePermitsTouching(\n scope: PermissionScope,\n contractsToRemove: ChecksummedAddress[],\n ): Promise<void> {\n const existing = await this.list(scope);\n if (existing.length === 0) {\n return;\n }\n const next = withoutPermitsTouching(existing, contractsToRemove);\n if (next.length === 0) {\n await this.clearScope(scope);\n } else {\n await this.#storage.set(permissionScopeKey(scope), next);\n }\n }\n\n /** Delete every permit in the scope and untrack it from the per-signer index. */\n async clearScope(scope: PermissionScope): Promise<void> {\n await this.#deleteScope(permissionScopeKey(scope));\n await this.#untrackScope(scope);\n }\n\n /**\n * Delete every permission for the given signer across all chains and delegators.\n * Uses the per-signer scope index to cascade without enumerating all storage keys.\n */\n async clearAllForSigner(signerAddress: ChecksummedAddress): Promise<void> {\n const indexKey = permissionIndexKey(signerAddress);\n const scopeKeys = await this.#readIndex(indexKey);\n await Promise.all(scopeKeys.map((k) => this.#deleteScope(k)));\n await swallow(\"delete permit index\", () => this.#storage.delete(indexKey), this.#logger);\n }\n\n async #readIndex(indexKey: string): Promise<string[]> {\n const raw = await this.#storage.get(indexKey);\n if (raw === null || raw === undefined) {\n return [];\n }\n const parsed = ScopeIndexSchema.safeParse(raw);\n if (!parsed.success) {\n await swallow(\"delete permit index\", () => this.#storage.delete(indexKey), this.#logger);\n return [];\n }\n return parsed.data;\n }\n\n async #trackScope(scope: PermissionScope): Promise<void> {\n const indexKey = permissionIndexKey(scope.signerAddress);\n const scopeKey = permissionScopeKey(scope);\n const list = await this.#readIndex(indexKey);\n if (list.includes(scopeKey)) {\n return;\n }\n await this.#storage.set(indexKey, [...list, scopeKey]);\n }\n\n async #untrackScope(scope: PermissionScope): Promise<void> {\n const indexKey = permissionIndexKey(scope.signerAddress);\n const scopeKey = permissionScopeKey(scope);\n const list = await this.#readIndex(indexKey);\n const next = list.filter((entry) => entry !== scopeKey);\n if (next.length === list.length) {\n return;\n }\n if (next.length === 0) {\n await swallow(\"delete permit index\", () => this.#storage.delete(indexKey), this.#logger);\n } else {\n await swallow(\"update permit index\", () => this.#storage.set(indexKey, next), this.#logger);\n }\n }\n\n async #deleteScope(scopeKey: string): Promise<void> {\n await swallow(\"delete permit entry\", () => this.#storage.delete(scopeKey), this.#logger);\n }\n}\n","import type { Address, Hex } from \"viem\";\nimport type { ChainRouter } from \"../chains/router\";\nimport { ZamaError } from \"../errors/base\";\nimport {\n KeyWrappingError,\n PreparedPermitChainMismatchError,\n PreparedPermitExpiredError,\n TransportKeyPairChangedError,\n} from \"../errors/credential\";\nimport { ConfigurationError } from \"../errors/relayer\";\nimport { SignerNotConfiguredError } from \"../errors/signer\";\nimport { wrapSigningError } from \"../errors/signing\";\nimport type { PermitOperation, ZamaSDKEventInput } from \"../events/sdk-events\";\nimport { ZamaSDKEvents } from \"../events/sdk-events\";\nimport type { ChecksummedAddress } from \"../schemas/primitives\";\nimport { checksum } from \"../schemas/primitives\";\nimport type { GenericLogger, GenericSigner, GenericStorage } from \"../types\";\nimport { isInvalidTransportKeyPairMessage } from \"../utils/error\";\nimport { swallow } from \"../utils/swallow\";\nimport { parseSchema } from \"../validation\";\nimport { TransportKeyPairVault } from \"./keypair-vault\";\nimport type { DerivationSecretHolder } from \"./keypair-wrapping\";\nimport { PermissionStore } from \"./permission-store\";\nimport { chunkContracts, findPermitToWiden, sortedUnion, uncoveredContracts } from \"./permissions\";\nimport {\n Eip712Schema,\n PermitTTLSchema,\n PreparedPermitSchema,\n SerializedPermitSchema,\n} from \"./schemas\";\nimport { permissionScopeKey, type PermissionScope } from \"./storage-keys\";\nimport type {\n Permission,\n PreparedPermit,\n PreparePermitRequest,\n SerializedTransportKeyPairWithPermissions,\n StoredTransportKeyPair,\n} from \"./types\";\nimport {\n MAX_CONTRACTS_PER_PERMIT,\n normalizeAddresses,\n nowSeconds,\n SECONDS_PER_DAY,\n toJsonSafeEip712,\n} from \"./utils\";\n\nexport const DEFAULT_TRANSPORT_KEY_PAIR_TTL_SECONDS = 30 * SECONDS_PER_DAY;\nexport const DEFAULT_PERMIT_DURATION_DAYS = 30;\n\n/** Configuration for {@link CredentialService}. TTLs are pre-validated by the caller. */\nexport interface CredentialServiceConfig {\n router: ChainRouter;\n /**\n * Optional signer. Required for {@link CredentialService.grantPermit},\n * {@link CredentialService.revokePermits}, and\n * {@link CredentialService.clearCredentials} — each throws\n * {@link SignerNotConfiguredError} without one. Omitted for signer-less\n * usage (public decryption / encryption, or construction before a wallet\n * connects). Deferred/custody signing is served by passing an\n * out-of-process signer here, not by a separate signer-less code path.\n */\n signer?: GenericSigner;\n /** Transport key pair lifetime in seconds. Pre-validated. */\n transportKeyPairTTL: number;\n /** Permit lifetime in days. Pre-validated. */\n permitTTL: number;\n /** Backing storage for transport key pairs (and permits if `permitStorage` is omitted). */\n storage: GenericStorage;\n /** Optional dedicated storage for permits; defaults to `storage`. */\n permitStorage?: GenericStorage;\n /** SDK-wide logger for credential-path diagnostics. */\n logger: GenericLogger;\n /**\n * Publishes structured SDK events (e.g. {@link ZamaSDKEvents.PermitError}) into\n * the unified `onEvent` stream — the same callback every other `*Service` receives.\n */\n emitEvent: (input: ZamaSDKEventInput) => void;\n /**\n * Opt-in shared-tenant scope (B2B2C/WaaS operators). When set, every signer\n * configured with the same scope shares one transport key pair instead of one\n * per signer address. Permits stay per-signer regardless — see\n * {@link revokeTransportKeyPair} for the operator-level counterpart to\n * signer-level revocation.\n */\n scope?: string;\n /**\n * Opt-in at-rest wrapping of the transport key pair's private half, for headless\n * contexts with no secure storage backend to delegate to. See\n * {@link TransportKeyPairVault}.\n */\n derivationSecret?: DerivationSecretHolder;\n}\n\n/**\n * Single facade coordinating the keypair vault and the permission store.\n *\n * `CredentialService` is the only credentials object held by {@link ZamaSDK}. It accepts identity\n * transitions via `handleWalletAccountChange`.\n *\n * Two distinct revocation tiers exist and must not be conflated: {@link clearCredentials}\n * (signer-level, e.g. an end-user \"disconnect\") only ever removes that signer's own\n * key-pair slot and permits — when a scope is configured, the shared key pair was never\n * stored there, so it's untouched. {@link revokeTransportKeyPair} (operator-level) is\n * the only way to invalidate a scope's shared key pair, and it does so for every signer\n * in the scope at once.\n *\n * @internal\n */\nexport class CredentialService {\n readonly #vault: TransportKeyPairVault;\n readonly #store: PermissionStore;\n readonly #router: ChainRouter;\n readonly #signer: GenericSigner | undefined;\n readonly #permitTTL: number;\n readonly #logger: GenericLogger;\n readonly #emitEvent: (input: ZamaSDKEventInput) => void;\n readonly #scope: string | undefined;\n /** In-flight revoked-context recoveries, one per permission scope. */\n readonly #permitRecoveries = new Map<string, Promise<void>>();\n\n constructor(config: CredentialServiceConfig) {\n this.#vault = new TransportKeyPairVault({\n generator: async () => {\n const relayer = config.router.relayer;\n const transportKeyPair = await relayer.generateTransportKeyPair();\n return relayer.serializeTransportKeyPair({ transportKeyPair });\n },\n storage: config.storage,\n ttl: config.transportKeyPairTTL,\n logger: config.logger,\n scope: config.scope,\n derivationSecret: config.derivationSecret,\n });\n this.#store = new PermissionStore({\n storage: config.permitStorage ?? config.storage,\n logger: config.logger,\n });\n this.#router = config.router;\n this.#signer = config.signer;\n this.#permitTTL = config.permitTTL;\n this.#logger = config.logger;\n this.#emitEvent = config.emitEvent;\n this.#scope = config.scope;\n }\n\n #requireSigner(operation: string): GenericSigner {\n if (!this.#signer) {\n throw new SignerNotConfiguredError(operation);\n }\n return this.#signer;\n }\n\n /**\n * Emit the corresponding {@link ZamaSDKEvents.PermitError} event for a raw\n * permit-flow error and return it for the caller to throw. Single choke\n * point for `grantPermit`/`registerPermit` (and `#signPermit`) so every\n * failure emits exactly once — safe to call with an error that has nothing\n * to do with wallet signing (schema validation, a stale/expired prepared\n * permit, a permit-store read): {@link wrapSigningError} passes an\n * already-typed `ZamaError` through unchanged, so only a genuinely\n * unclassified error gets folded into `SigningFailedError`, the same\n * fallback {@link wrapEncryptError}/{@link wrapDecryptError} use elsewhere.\n *\n * Never call this with an error from credential/vault resolution\n * (`#vault.getOrCreate` / `#vault.readStored`, or anything derived from their\n * result, e.g. `TransportKeyPairChangedError`) — those failures can be\n * adjacent to `transportKeyPairDerivationSecret` internals and must never\n * reach the public `onEvent` stream.\n */\n #failPermit(operation: PermitOperation, error: unknown): ZamaError {\n const failure = wrapSigningError(error, { operation });\n this.#emitEvent({ type: ZamaSDKEvents.PermitError, operation, error: failure });\n return failure;\n }\n\n /**\n * Resolve a keypair and permissions covering `contracts`, minimizing wallet\n * prompts by reusing or widening existing permits where possible. Empty\n * `contracts` warms the keypair without prompting.\n *\n * @returns The resolved keypair and permits covering the requested contracts.\n * @throws if the user rejects a wallet signature prompt. {@link SigningRejectedError}\n * @throws if signing fails for any other reason. {@link SigningFailedError}\n * @throws if `derivationSecret` is configured and the keypair fails to wrap or\n * unwrap — see {@link TransportKeyPairVault.getOrCreate}. {@link KeyWrappingError}\n */\n async grantPermit(\n contracts: readonly Address[],\n delegator?: Address,\n ): Promise<SerializedTransportKeyPairWithPermissions> {\n const operation: PermitOperation =\n delegator !== undefined ? \"grantDelegationPermit\" : \"grantPermit\";\n let signerAddress: ChecksummedAddress;\n let requested: ChecksummedAddress[];\n let scope: PermissionScope;\n try {\n const signer = this.#requireSigner(\"grantPermit\");\n const account = signer.requireWalletAccount(\"grantPermit\");\n signerAddress = checksum(account.address);\n // normalizeAddresses/#permissionScope both call checksum(), which can\n // throw — kept in this try so a malformed address still emits PermitError.\n requested = normalizeAddresses(contracts);\n scope = this.#permissionScope(signerAddress, delegator);\n } catch (error) {\n throw this.#failPermit(operation, error);\n }\n\n // Credential/vault resolution — deliberately not wrapped; those failures\n // must never reach onEvent.\n const keypair = await this.#vault.getOrCreate(signerAddress);\n if (requested.length === 0) {\n return { keypair, permissions: [] };\n }\n\n let permissions: Permission[];\n try {\n permissions = await this.#store.listUsableAndPrune(scope, keypair.publicKey);\n } catch (error) {\n throw this.#failPermit(operation, error);\n }\n\n const uncovered = uncoveredContracts(permissions, requested);\n if (uncovered.length > 0) {\n const candidate = findPermitToWiden(permissions, uncovered, requested);\n if (candidate !== null) {\n const widenedSet = sortedUnion(candidate.contractAddresses, uncovered);\n const widened = await this.#signPermit({ chunk: widenedSet, keypair, scope });\n await swallow(\n \"replace permit\",\n () => this.#store.replace(scope, candidate.serializedPermit.signature, widened),\n this.#logger,\n );\n permissions[permissions.indexOf(candidate)] = widened;\n } else {\n for (const chunk of chunkContracts(uncovered)) {\n const permission = await this.#signPermit({ chunk, keypair, scope });\n permissions.push(permission);\n await swallow(\n \"persist permit\",\n () => this.#store.append(scope, [permission]),\n this.#logger,\n );\n }\n }\n }\n\n const requestedSet = new Set(requested);\n return {\n keypair,\n permissions: permissions.filter((p) => p.contractAddresses.some((a) => requestedSet.has(a))),\n };\n }\n\n /**\n * Offline permit flow, phase 1: build the unsigned EIP-712 typed data for a\n * decryption permit without signing it. The caller (an HSM, custody API, or\n * any out-of-process signer) signs the returned `eip712` with\n * `eth_signTypedData_v4` and hands the signature to {@link registerPermit}.\n *\n * Signer-less: the transport key pair is resolved from `request.signer`\n * directly — no wallet account or configured signer needed. Signer-offline,\n * not network-offline: building the typed data still reads the chain's KMS\n * signers context on-chain.\n *\n * One permit per call — no widening or chunking against existing permits,\n * unlike {@link grantPermit}. Prefer `grantPermit` unless signing must\n * happen out-of-process.\n *\n * @throws if `request.contracts` is empty or exceeds {@link MAX_CONTRACTS_PER_PERMIT},\n * `request.delegator` equals `request.signer`, or `request.durationDays` exceeds\n * the V1 permit maximum of 365 days (enforced by `PermitTTLSchema`). {@link ConfigurationError}\n */\n async preparePermit(request: PreparePermitRequest): Promise<PreparedPermit> {\n const signerAddress = checksum(request.signer);\n const contracts = normalizeAddresses(request.contracts);\n if (contracts.length === 0) {\n throw new ConfigurationError(\"preparePermit: request.contracts must not be empty.\");\n }\n if (contracts.length > MAX_CONTRACTS_PER_PERMIT) {\n throw new ConfigurationError(\n `preparePermit: request.contracts must not exceed ${MAX_CONTRACTS_PER_PERMIT} addresses per call ` +\n `(got ${contracts.length}) — grantPermit chunks automatically, preparePermit does not.`,\n );\n }\n const delegatorAddress = request.delegator ? checksum(request.delegator) : undefined;\n if (delegatorAddress !== undefined && delegatorAddress === signerAddress) {\n throw new ConfigurationError(\n \"preparePermit: request.delegator must differ from request.signer — self-delegation is not allowed.\",\n );\n }\n // PermitTTLSchema caps at MAX_V1_PERMIT_DURATION_DAYS, so an explicit\n // request.durationDays is bounded here, and the this.#permitTTL fallback\n // is bounded the same way at config-build time — no separate check needed.\n const durationDays =\n request.durationDays !== undefined\n ? parseSchema(PermitTTLSchema, request.durationDays)\n : this.#permitTTL;\n\n // Snapshot the relayer before the first await: a chain switch mid-flight\n // must not let the EIP-712 domain get built against a relayer bound to a\n // different chain than the one active when this call started.\n const relayer = this.#router.relayer;\n\n // Strict persistence: an out-of-process signing ceremony can take hours or\n // days, so a key pair that fails to persist here must fail immediately\n // instead of silently binding `eip712` to a key `registerPermit` will later\n // fail to find (surfacing only as a confusing `TransportKeyPairChangedError`\n // after the ceremony completes).\n const keypair = await this.#vault.getOrCreate(signerAddress, { strict: true });\n const transportKeyPair = await relayer.parseTransportKeyPair(keypair);\n const startTimestamp = nowSeconds();\n const eip712 = toJsonSafeEip712(\n Eip712Schema.parse(\n await relayer.createUnsignedLegacyDecryptionPermitEip712({\n transportKeyPair,\n contractAddresses: contracts,\n startTimestamp,\n durationSeconds: durationDays * SECONDS_PER_DAY,\n ...(delegatorAddress && { delegatorAddress }),\n }),\n ),\n );\n\n return { version: 1, eip712, signerAddress };\n }\n\n /**\n * Offline permit flow, phase 2: verify the signature an out-of-process\n * signer produced for a {@link preparePermit} payload, then persist it as a\n * usable permit.\n *\n * Idempotent: safe to call more than once for the same `(prepared, signature)`\n * pair (e.g. a webhook re-delivery, or a retried registration call) — the\n * second call replaces the first call's stored entry instead of duplicating it.\n *\n * Every field used below (chain, timing, transport key, contracts, delegation)\n * is read from `prepared.eip712` — the unsigned typed data — or, once verified,\n * from the signature-checked `signedPermit` the relayer returns. `preparePermit`\n * never hands back a separate \"claimed\" copy of any of these for this method to\n * cross-check against: there is exactly one source of truth for each field, so\n * there is nothing to tamper with independently of the signature itself.\n *\n * @throws if `prepared` doesn't match the {@link PreparedPermit} shape (e.g. it\n * crossed a process boundary and was corrupted). {@link ConfigurationError}\n * @throws if the chain embedded in `prepared.eip712` doesn't match the currently\n * active chain. {@link PreparedPermitChainMismatchError}\n * @throws if the permit's validity window has already elapsed. {@link PreparedPermitExpiredError}\n * @throws if no transport key pair is stored for `prepared.signerAddress`, or it no\n * longer matches the public key `prepared.eip712` was built against (e.g. a TTL\n * expiry or eviction in between). {@link TransportKeyPairChangedError}\n * @throws if the signature is invalid or malformed. {@link SigningFailedError}\n */\n async registerPermit(prepared: PreparedPermit, signature: Hex): Promise<void> {\n let parsed: PreparedPermit;\n try {\n parsed = parseSchema(PreparedPermitSchema, prepared);\n } catch (error) {\n throw this.#failPermit(\"registerPermit\", error);\n }\n\n // Snapshot the relayer and chain together, before the first await — see the\n // identical guard in preparePermit.\n const relayer = this.#router.relayer;\n const activeChainId = this.#router.chain.id;\n\n const { domain, message } = parsed.eip712;\n const preparedChainId = Number(domain.chainId);\n if (preparedChainId !== activeChainId) {\n throw this.#failPermit(\n \"registerPermit\",\n new PreparedPermitChainMismatchError({ preparedChainId, activeChainId }),\n );\n }\n const startTimestamp = Number(message.startTimestamp);\n const durationDays = Number(message.durationDays);\n if (nowSeconds() >= startTimestamp + durationDays * SECONDS_PER_DAY) {\n throw this.#failPermit(\n \"registerPermit\",\n new PreparedPermitExpiredError(\n `registerPermit: the prepared permit's validity window (starting ${startTimestamp}, ` +\n `${durationDays}d) has already elapsed — call preparePermit again.`,\n ),\n );\n }\n\n // No key pair to fall back on generating here: `prepared.eip712` was built\n // against a specific transport public key, so a missing (or mismatched)\n // stored key pair can only mean it changed since preparePermit ran —\n // generating a fresh one via getOrCreate would just be discarded by the\n // comparison below, having wastefully persisted a key nothing will use.\n //\n // Credential/vault resolution is deliberately not wrapped; those\n // failures must never reach onEvent.\n const signerAddress = parsed.signerAddress;\n const keypair = await this.#vault.readStored(signerAddress);\n if (keypair === null || keypair.publicKey !== message.publicKey) {\n throw new TransportKeyPairChangedError(\n \"registerPermit: the transport key pair changed since preparePermit ran — call \" +\n \"preparePermit again to rebind the signature request to the current key pair.\",\n );\n }\n\n try {\n const transportKeyPair = await relayer.parseTransportKeyPair(keypair);\n const signedPermit = await relayer.parseSignedDecryptionPermit({\n serializedPermit: {\n version: parsed.version,\n eip712: parsed.eip712,\n signature,\n signerAddress,\n },\n transportKeyPair,\n });\n if (signedPermit.version !== 1) {\n throw new ConfigurationError(\n `registerPermit: expected a V1 signed permit, got version ${signedPermit.version}.`,\n );\n }\n\n const serializedPermit = parseSchema(\n SerializedPermitSchema,\n await relayer.serializeSignedDecryptionPermit({ signedPermit }),\n );\n\n const permission: Permission = {\n keypairPublicKey: keypair.publicKey,\n contractAddresses: normalizeAddresses(signedPermit.eip712.message.contractAddresses),\n serializedPermit,\n startTimestamp: Number(signedPermit.eip712.message.startTimestamp),\n durationDays: Number(signedPermit.eip712.message.durationDays),\n };\n const scope: PermissionScope = {\n signerAddress,\n chainId: activeChainId,\n delegatorAddress: checksum(signedPermit.encryptedDataOwnerAddress),\n };\n await swallow(\n \"replace permit\",\n () => this.#store.replace(scope, serializedPermit.signature, permission),\n this.#logger,\n );\n } catch (error) {\n throw this.#failPermit(\"registerPermit\", error);\n }\n }\n\n /**\n * Pure store lookup: are stored permits sufficient to cover `contracts`? No wallet prompt.\n *\n * Never throws on a `KeyWrappingError`: a stored keypair that fails to unwrap reports\n * no permit instead, since this is a read-only status check. `grantPermit()` still\n * surfaces that same failure normally.\n *\n * @returns `true` if cached permits cover all requested contracts (vacuously\n * true for an empty list); `false` if no keypair exists, it fails to unwrap, or\n * coverage is incomplete.\n * @throws if the backing storage read fails for a reason unrelated to `derivationSecret`\n * wrapping: this is not swallowed, unlike a `KeyWrappingError`.\n */\n async hasPermit(contracts: readonly Address[], delegator?: Address): Promise<boolean> {\n if (contracts.length === 0) {\n return true;\n }\n const account = this.#signer?.walletAccount.getSnapshot();\n if (!account) {\n return false;\n }\n const signerAddress = checksum(account.address);\n let keypair: StoredTransportKeyPair | null;\n try {\n keypair = await this.#vault.readStored(signerAddress);\n } catch (error) {\n if (!(error instanceof KeyWrappingError)) {\n throw error;\n }\n // The vault already logged this failure; this read-only status check must not\n // surface it a second time as a rejection.\n return false;\n }\n if (keypair === null) {\n return false;\n }\n const scope = this.#permissionScope(signerAddress, delegator);\n const permits = await this.#store.listUsableAndPrune(scope, keypair.publicKey);\n return uncoveredContracts(permits, normalizeAddresses(contracts)).length === 0;\n }\n\n /**\n * Wipe FHE permits for the current signer.\n *\n * - With no argument: every permit referencing this signer is removed across\n * all chains and delegators. The keypair survives — use\n * {@link clearCredentials} to also wipe the keypair.\n * - With a contract list: every signed permit in the direct-decrypt scope\n * (current chain) whose immutable payload touches any listed address is\n * removed. Delegated permits are not touched in this mode.\n *\n * @throws if reading the signer address fails. {@link SigningFailedError}\n */\n async revokePermits(contracts?: readonly Address[]): Promise<void> {\n const signer = this.#requireSigner(\"revokePermits\");\n const account = signer.requireWalletAccount(\"revokePermits\");\n const signerAddress = checksum(account.address);\n if (contracts === undefined) {\n await this.#store.clearAllForSigner(signerAddress);\n return;\n }\n const normalized = normalizeAddresses(contracts);\n if (normalized.length === 0) {\n return;\n }\n await this.#store.deletePermitsTouching(this.#permissionScope(signerAddress), normalized);\n }\n\n /**\n * Permits are keyed by the router's active chain, the same chain their\n * EIP-712 domain is signed against, so the storage key and the signature\n * can never disagree.\n */\n #permissionScope(signerAddress: ChecksummedAddress, delegator?: Address): PermissionScope {\n return {\n signerAddress,\n chainId: this.#router.chain.id,\n delegatorAddress: delegator ? checksum(delegator) : signerAddress,\n };\n }\n\n /**\n * Wipe the keypair for the current signer and cascade-delete every\n * permission referencing it across all chains and delegators.\n *\n * Signer-level only: when a scope is configured, the shared key pair was never\n * stored under this signer's own slot, so it survives untouched — only this\n * signer's permits are removed. Use {@link revokeTransportKeyPair} to invalidate\n * the shared key pair itself.\n *\n * @throws if reading the signer address fails. {@link SigningFailedError}\n */\n async clearCredentials(): Promise<void> {\n const signer = this.#requireSigner(\"clearCredentials\");\n const account = signer.requireWalletAccount(\"clearCredentials\");\n const signerAddress = checksum(account.address);\n await this.#vault.clear(signerAddress);\n await this.#store.clearAllForSigner(signerAddress);\n }\n\n /**\n * Revoke this scope's shared transport key pair (operator-level action).\n *\n * Deletes the shared key pair. Every permit signed under it embeds the old\n * public key, so `listUsableAndPrune` treats them all as stale on next access —\n * no permit is touched directly, and no connected wallet is required. This is\n * the only operation that can invalidate a shared key pair; signer-level\n * {@link clearCredentials} never does.\n *\n * Not best-effort: unlike {@link clearCredentials}, a storage failure here rejects\n * instead of being logged and swallowed — see {@link TransportKeyPairVault.clearScope}.\n *\n * Only stops the SDK from reissuing or reusing the deleted key going forward — does\n * not revoke any permit already issued under it. A permit is a self-contained,\n * bearer-style EIP-712 signature the relayer accepts independently of this SDK's\n * storage; one already exfiltrated alongside the key remains usable until its own\n * `permitTTL` expiry regardless of this call.\n *\n * @param scopeId - Must match the scope this service was configured with. Requiring\n * the caller to name it guards against revoking the wrong scope by mistake.\n * @throws if no scope is configured, or `scopeId` doesn't match it. {@link ConfigurationError}\n * @throws if the underlying storage delete fails.\n */\n async revokeTransportKeyPair(scopeId: string): Promise<void> {\n if (this.#scope === undefined) {\n throw new ConfigurationError(\n \"revokeTransportKeyPair() requires a transportKeyPairScope to be configured on this SDK instance — there is no shared key pair to revoke.\",\n );\n }\n if (scopeId !== this.#scope) {\n throw new ConfigurationError(\n `revokeTransportKeyPair(\"${scopeId}\") does not match the configured scope (\"${this.#scope}\").`,\n );\n }\n await this.#vault.clearScope();\n }\n\n /**\n * Warm the signer transport key pair cache for a known address.\n *\n * Best-effort prefetch primitive: correctness still comes from `grantPermit`,\n * which lazily creates the transport key pair when needed. Errors (storage failure,\n * relayer 4xx, missing Worker in SSR) are **not** swallowed here — the caller\n * decides whether to log, ignore, or surface them.\n *\n * Not for pre-warming a shared scope: `address` is ignored for storage keying once\n * a scope is configured (see {@link TransportKeyPairVault}), so this call's actual\n * effect wouldn't match its apparent per-signer intent. Use\n * {@link warmTransportKeyPairScope} instead.\n *\n * @throws if `derivationSecret` is configured and the keypair fails to wrap or\n * unwrap — see {@link TransportKeyPairVault.getOrCreate}. {@link KeyWrappingError}\n */\n async warmTransportKeyPair(address: Address): Promise<void> {\n await this.#vault.getOrCreate(checksum(address));\n }\n\n /**\n * Warm this scope's shared transport key pair (operator-level, no wallet needed) —\n * the pre-warm counterpart to {@link revokeTransportKeyPair}.\n *\n * @param scopeId - Must match the scope this service was configured with. Requiring\n * the caller to name it guards against warming the wrong scope by mistake.\n * @throws if no scope is configured, or `scopeId` doesn't match it. {@link ConfigurationError}\n * @throws if `derivationSecret` is configured and the keypair fails to wrap or\n * unwrap — see {@link TransportKeyPairVault.getOrCreate}. {@link KeyWrappingError}\n */\n async warmTransportKeyPairScope(scopeId: string): Promise<void> {\n if (this.#scope === undefined) {\n throw new ConfigurationError(\n \"warmTransportKeyPairScope() requires a transportKeyPairScope to be configured on this SDK instance — there is no shared key pair to warm.\",\n );\n }\n if (scopeId !== this.#scope) {\n throw new ConfigurationError(\n `warmTransportKeyPairScope(\"${scopeId}\") does not match the configured scope (\"${this.#scope}\").`,\n );\n }\n await this.#vault.warmScope();\n }\n\n /**\n * Apply a wallet account transition.\n *\n * Address change clears persisted credentials for the previous account.\n * Chain-only changes keep credentials intact because permits are chain-scoped\n * already and stale decrypt plaintext is cleared by {@link ZamaSDK}.\n */\n async handleWalletAccountChange(\n prev?: { address: Address },\n next?: { address: Address },\n ): Promise<void> {\n const prevAddr = prev ? checksum(prev.address) : undefined;\n const nextAddr = next ? checksum(next.address) : undefined;\n if (prevAddr === nextAddr) {\n return;\n }\n if (prevAddr) {\n await this.#vault.clear(prevAddr);\n await this.#store.clearAllForSigner(prevAddr);\n }\n }\n\n async #signPermit(input: {\n chunk: ChecksummedAddress[];\n keypair: StoredTransportKeyPair;\n scope: PermissionScope;\n }): Promise<Permission> {\n const { chunk, keypair, scope } = input;\n const startTimestamp = nowSeconds();\n const isDelegated = scope.delegatorAddress !== scope.signerAddress;\n const relayer = this.#router.relayer;\n try {\n const transportKeyPair = await relayer.parseTransportKeyPair(keypair);\n const permitInput = {\n transportKeyPair,\n contractAddresses: chunk,\n startTimestamp,\n durationSeconds: this.#permitTTL * SECONDS_PER_DAY,\n signerAddress: scope.signerAddress,\n signer: this.#requireSigner(\"signPermit\"),\n };\n const signedPermit = isDelegated\n ? await relayer.signDecryptionPermit({\n ...permitInput,\n delegatorAddress: scope.delegatorAddress,\n })\n : await relayer.signDecryptionPermit(permitInput);\n\n const serializedPermit = parseSchema(\n SerializedPermitSchema,\n await relayer.serializeSignedDecryptionPermit({ signedPermit }),\n );\n\n return {\n keypairPublicKey: keypair.publicKey,\n contractAddresses: chunk,\n serializedPermit,\n startTimestamp,\n durationDays: this.#permitTTL,\n };\n } catch (error) {\n const operation: PermitOperation = isDelegated ? \"grantDelegationPermit\" : \"grantPermit\";\n // A key pair the relayer can't re-derive (post KMS/TKMS rotation) is\n // unusable: evict it so the next grantPermit regenerates a valid one, then\n // surface the typed InvalidTransportKeyPairError via wrapSigningError.\n if (\n !(error instanceof ZamaError) &&\n error instanceof Error &&\n isInvalidTransportKeyPairMessage(error.message)\n ) {\n await this.#vault.evict(scope.signerAddress);\n }\n throw this.#failPermit(operation, error);\n }\n }\n\n /**\n * Evict the current signer's transport key pair so the next credential\n * resolution regenerates it — the self-heal hook the decrypt path calls when\n * the relayer rejects the stored key pair as invalid (see\n * {@link InvalidTransportKeyPairError}). No-op when no wallet is connected;\n * best-effort, never throws.\n */\n async evictTransportKeyPair(): Promise<void> {\n const account = this.#signer?.walletAccount.getSnapshot();\n if (!account) {\n return;\n }\n await this.#vault.evict(checksum(account.address));\n }\n\n /**\n * Recover the scope from a revoked KMS context: every permit in it was\n * signed under the dead context, so drop them all and re-sign the scope's\n * prior coverage plus `contracts` in one prompt, then resolve credentials as\n * {@link grantPermit} would. The self-heal hook the decrypt path calls on\n * {@link RevokedKmsContextError}; the key pair is untouched, only the\n * permits embed the dead context.\n *\n * Concurrent and repeated recoveries for the same scope cost at most one\n * wallet prompt: a running recovery is shared, and a call whose\n * `staleSignatures` are no longer stored re-signs nothing because the scope\n * was already recovered. If the stored permits cannot be read, nothing is\n * evicted and the stored (dead) permits are returned unchanged. If the\n * re-grant fails, the stored permits minus `staleSignatures` are put back\n * before the error propagates.\n *\n * @param staleSignatures Signatures of the permits the failed decrypt used.\n * Omitted or empty makes recovery unconditional.\n * @throws if the user rejects the re-grant prompt. {@link SigningRejectedError}\n * @throws if re-signing fails for any other reason. {@link SigningFailedError}\n */\n async recoverPermits(\n contracts: readonly Address[],\n delegator?: Address,\n staleSignatures?: readonly Hex[],\n ): Promise<SerializedTransportKeyPairWithPermissions> {\n const signer = this.#requireSigner(\"recoverPermits\");\n const account = signer.requireWalletAccount(\"recoverPermits\");\n const scope = this.#permissionScope(checksum(account.address), delegator);\n const key = permissionScopeKey(scope);\n\n // A recovery for this scope is already running: wait for it instead of\n // starting another, no matter which permits this call failed with.\n const inFlight = this.#permitRecoveries.get(key);\n if (inFlight) {\n await inFlight;\n return this.grantPermit(contracts, delegator);\n }\n\n const stored = await this.#readScopePermits(scope);\n\n // A sibling recovery may have started while the read above was awaiting.\n // Check again: a sibling registers itself in the map before it clears the\n // store, so if the read saw a cleared store, the sibling is found here.\n const registered = this.#permitRecoveries.get(key);\n if (registered) {\n await registered;\n return this.grantPermit(contracts, delegator);\n }\n\n // The read failed: never clear a scope we could not read, or coverage we\n // cannot re-sign would be lost.\n if (stored === null) {\n return this.grantPermit(contracts, delegator);\n }\n\n const stale = new Set(staleSignatures ?? []);\n\n // None of the failing permits is stored anymore: another recovery already\n // replaced them. Retry with the fresh permit instead of re-signing.\n if (stale.size > 0 && !stored.some((p) => stale.has(p.serializedPermit.signature))) {\n return this.grantPermit(contracts, delegator);\n }\n\n const survivors = stored.filter((p) => !stale.has(p.serializedPermit.signature));\n const prior = stored.flatMap((p) => p.contractAddresses);\n const recovery = this.#clearAndRegrantScope(\n scope,\n prior,\n contracts,\n survivors,\n delegator,\n ).finally(() => {\n this.#permitRecoveries.delete(key);\n });\n // No `await` between the map re-check above and this `set`, so two callers\n // cannot both start a recovery for the same scope.\n this.#permitRecoveries.set(key, recovery);\n await recovery;\n // The union re-grant already covered `contracts`, so no extra prompt here.\n return this.grantPermit(contracts, delegator);\n }\n\n /** `null` marks a storage failure, distinct from an empty scope. */\n async #readScopePermits(scope: PermissionScope): Promise<Permission[] | null> {\n try {\n return await this.#store.list(scope);\n } catch (error) {\n this.#logger.warn(\"read permits for recovery failed\", { error });\n return null;\n }\n }\n\n async #clearAndRegrantScope(\n scope: PermissionScope,\n prior: readonly ChecksummedAddress[],\n contracts: readonly Address[],\n survivors: readonly Permission[],\n delegator?: Address,\n ): Promise<void> {\n await swallow(\"evict revoked permits\", () => this.#store.clearScope(scope), this.#logger);\n try {\n // Re-sign the union of what the scope covered, not just this call's\n // contracts: one signature restores every widened permit, so sibling\n // decrypt calls joining the recovery find their contracts already covered.\n await this.grantPermit(sortedUnion(prior, normalizeAddresses(contracts)), delegator);\n } catch (error) {\n // Coverage the session cannot re-sign must survive a failed re-grant.\n if (survivors.length > 0) {\n await swallow(\n \"restore surviving permits\",\n async () => {\n // A failed eviction leaves survivors stored: append only the missing ones.\n const current = await this.#store.list(scope);\n const present = new Set(current.map((p) => p.serializedPermit.signature));\n const missing = survivors.filter((p) => !present.has(p.serializedPermit.signature));\n if (missing.length > 0) {\n await this.#store.append(scope, missing);\n }\n },\n this.#logger,\n );\n }\n throw error;\n }\n }\n}\n","import type { GenericLogger } from \"../types\";\n\n/**\n * Internal logger with a consistent, always-present shape.\n *\n * The consumer supplies an optional {@link GenericLogger} via configuration;\n * it is wrapped once into a `LoggerService` that is threaded through the SDK.\n * Internal code therefore calls `logger.warn(...)` directly — no optional\n * chaining, no per-call `undefined` checks — and a single place (this class)\n * owns the \"silent by default\" behavior: when no logger was supplied, every\n * level is a no-op.\n *\n * Every message is prefixed with `[zama-sdk]` here, so call sites pass only\n * their own message (and optional component tag) without repeating the prefix.\n *\n * `LoggerService` itself satisfies `GenericLogger`, so it can be passed\n * anywhere a logger is expected.\n */\nexport class LoggerService implements GenericLogger {\n readonly #logger: GenericLogger | undefined;\n readonly #prefix = \"[zama-sdk]\";\n\n constructor(logger?: GenericLogger) {\n this.#logger = logger;\n }\n\n error(message: string, data?: Record<string, unknown>): void {\n this.#logger?.error(`${this.#prefix} ${message}`, data);\n }\n\n warn(message: string, data?: Record<string, unknown>): void {\n this.#logger?.warn(`${this.#prefix} ${message}`, data);\n }\n\n info(message: string, data?: Record<string, unknown>): void {\n this.#logger?.info(`${this.#prefix} ${message}`, data);\n }\n\n debug(message: string, data?: Record<string, unknown>): void {\n this.#logger?.debug(`${this.#prefix} ${message}`, data);\n }\n\n /**\n * Warns on the console whatever the logger is, and once more through the\n * consumer's own logger when one was supplied. The configured logger may be\n * silenced, while these failures must still reach the console.\n */\n warnAlways(message: string): void {\n console.warn(`${this.#prefix} ${message}`);\n this.warn(message);\n }\n}\n","import { type Address, getAddress, zeroAddress } from \"viem\";\nimport { z } from \"zod/mini\";\nimport type { TokenWrapperPairWithMetadata, PaginatedResult, TokenWrapperPair } from \"./contracts\";\nimport {\n decimalsContract,\n erc20TotalSupplyContract,\n getConfidentialTokenAddressContract,\n getTokenAddressContract,\n getTokenPairContract,\n getTokenPairsContract,\n getTokenPairsLengthContract,\n getTokenPairsSliceContract,\n isConfidentialTokenValidContract,\n nameContract,\n symbolContract,\n} from \"./contracts\";\nimport { ConfigurationError } from \"./errors/relayer\";\nimport { mainnet, polygon, sepolia, polygonAmoy, hoodi, ingenTestnet, bscTestnet } from \"./chains\";\nimport { checksummedAddress, nonNegativeSeconds } from \"./schemas/primitives\";\nimport type { GenericProvider } from \"./types/provider\";\nimport { parseSchema } from \"./validation\";\n\n/**\n * Default wrappers registry addresses for known chains.\n * Only includes chains where a registry is deployed (excludes Hardhat).\n *\n * @deprecated Read `registryAddress` from the chain presets instead (e.g.\n * `sepolia.registryAddress`). Will be removed in the next major version.\n */\nexport const DefaultRegistryAddresses: Record<number, Address> = {\n [mainnet.id]: mainnet.registryAddress,\n [polygon.id]: polygon.registryAddress,\n [sepolia.id]: sepolia.registryAddress,\n [polygonAmoy.id]: polygonAmoy.registryAddress,\n [hoodi.id]: hoodi.registryAddress,\n [ingenTestnet.id]: ingenTestnet.registryAddress,\n [bscTestnet.id]: bscTestnet.registryAddress,\n};\n\n/** Default registry TTL in seconds (24 hours). */\nexport const DEFAULT_REGISTRY_TTL_SECONDS = 86_400;\n\n/** Per-chain wrappers-registry address overrides. */\nexport const RegistryAddressesSchema = z.record(\n z.string().check(z.regex(/^\\d+$/, \"expected numeric chain id key\")),\n checksummedAddress,\n);\n\n/** TTL (seconds) for cached registry results. `0` means entries expire immediately. */\nexport const RegistryTTLSchema = nonNegativeSeconds;\n\n/** Default page size for {@link WrappersRegistry.listPairs}. */\nconst DEFAULT_PAGE_SIZE = 100;\n\n/** Configuration for {@link WrappersRegistry}. */\nexport interface WrappersRegistryConfig {\n /** Read-only chain provider used for every registry lookup. */\n provider: GenericProvider;\n /**\n * Per-chain registry address overrides, merged on top of\n * {@link DefaultRegistryAddresses}. Use this to supply a registry\n * address for custom or local chains (e.g. Hardhat).\n *\n * @example\n * ```ts\n * new WrappersRegistry({\n * provider,\n * registryAddresses: { [31337]: \"0xYourHardhatRegistry\" },\n * });\n * ```\n */\n registryAddresses?: Record<number, Address>;\n /**\n * How long cached registry results remain valid, in seconds.\n * Default: `86400` (24 hours).\n */\n registryTTL?: number;\n}\n\n/** Options for {@link WrappersRegistry.listPairs}. */\nexport interface ListPairsOptions {\n /** Page number (1-indexed). Default: `1`. */\n page?: number;\n /** Number of items per page. Default: `100`. */\n pageSize?: number;\n /**\n * When `true`, fetches on-chain metadata (name, symbol, decimals) for both\n * the ERC-20 and confidential token, plus totalSupply for the ERC-20.\n * Default: `false`.\n */\n metadata?: boolean;\n}\n\n/** Shorter TTL for negative lookups (5 minutes) so newly registered tokens are discoverable quickly. */\nconst NEGATIVE_CACHE_TTL_MS = 5 * 60 * 1000;\n\n/** Cache entry with expiry timestamp. */\ninterface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\n/**\n * High-level interface for the on-chain token wrappers registry.\n *\n * Uses the configured {@link GenericProvider} to resolve the correct registry\n * contract address for the current chain and exposes typed read helpers for\n * every registry query. Results are cached in memory with a configurable TTL\n * (default 24 hours).\n *\n * @example\n * ```ts\n * const registry = new WrappersRegistry({ provider });\n *\n * // Paginated listing\n * const page1 = await registry.listPairs({ page: 1, pageSize: 20 });\n *\n * // Structured lookups\n * const result = await registry.getConfidentialToken(erc20Address);\n * if (result) console.log(result.confidentialTokenAddress);\n *\n * // Force refresh\n * registry.refresh();\n * ```\n */\nexport class WrappersRegistry {\n /** Read-only chain access used for registry contract reads. */\n readonly provider: GenericProvider;\n readonly #addresses: Record<number, Address>;\n readonly #ttlMs: number;\n readonly #cache = new Map<string, CacheEntry<unknown>>();\n\n constructor(config: WrappersRegistryConfig) {\n this.provider = config.provider;\n this.#addresses = Object.assign(\n {},\n DefaultRegistryAddresses,\n parseSchema(z.optional(RegistryAddressesSchema), config.registryAddresses),\n );\n this.#ttlMs =\n parseSchema(RegistryTTLSchema, config.registryTTL ?? DEFAULT_REGISTRY_TTL_SECONDS) * 1000;\n }\n\n /**\n * Synchronous lookup of the registry address for a given chain ID.\n * Returns `undefined` if no address is configured for that chain.\n */\n getAddress(chainId: number): Address | undefined {\n return this.#addresses[chainId];\n }\n\n // ---------------------------------------------------------------------------\n // Cache helpers\n // ---------------------------------------------------------------------------\n\n #getCached<T>(key: string): T | undefined {\n const entry = this.#cache.get(key);\n if (!entry) {\n return undefined;\n }\n if (Date.now() >= entry.expiresAt) {\n this.#cache.delete(key);\n return undefined;\n }\n return entry.data as T;\n }\n\n #setCached<T>(key: string, data: T, ttlMs = this.#ttlMs): T {\n this.#cache.set(key, { data, expiresAt: Date.now() + ttlMs });\n return data;\n }\n\n /**\n * Force-invalidate the in-memory cache. The next call to any read method\n * will fetch fresh data from the chain.\n */\n refresh(): void {\n this.#cache.clear();\n }\n\n /**\n * The cache TTL in milliseconds.\n * Exposed so query option factories can set a matching `staleTime`.\n */\n get ttlMs(): number {\n return this.#ttlMs;\n }\n\n // ---------------------------------------------------------------------------\n // Registry address resolution\n // ---------------------------------------------------------------------------\n\n /**\n * Resolve the registry contract address for the current chain.\n *\n * Priority: `registryAddresses[chainId]` \\> built-in default.\n *\n * @returns The registry contract address for the connected chain.\n * @throws if no address is configured for the chain. {@link ConfigurationError}\n */\n async getRegistryAddress(): Promise<Address> {\n const chainId = await this.provider.getChainId();\n const address = this.#addresses[chainId];\n\n if (!address) {\n throw new ConfigurationError(\n `No wrappers registry address configured for chain ${chainId}.\\n` +\n `Pass a registryAddresses entry for this chain.`,\n );\n }\n\n return getAddress(address);\n }\n\n // ---------------------------------------------------------------------------\n // Paginated listing\n // ---------------------------------------------------------------------------\n\n /**\n * List token wrapper pairs with page-based pagination.\n *\n * Internally maps to `getTokenConfidentialTokenPairsSlice` on-chain.\n *\n * @param options - Pagination and enrichment options.\n * @returns A {@link PaginatedResult} of pairs.\n *\n * @example\n * ```ts\n * const result = await registry.listPairs({ page: 1, pageSize: 20 });\n * console.log(`${result.total} pairs, showing page ${result.page}`);\n *\n * // With on-chain metadata\n * const withMeta = await registry.listPairs({ metadata: true, pageSize: 10 });\n * for (const pair of withMeta.items) {\n * console.log(pair.underlying.symbol, \"→\", pair.confidential.symbol);\n * }\n * ```\n */\n async listPairs(\n options: ListPairsOptions & { metadata: true },\n ): Promise<PaginatedResult<TokenWrapperPairWithMetadata>>;\n /** Lists registered underlying/confidential token pairs; pass `{ metadata: true }` to include on-chain token metadata. */\n async listPairs(options?: ListPairsOptions): Promise<PaginatedResult<TokenWrapperPair>>;\n async listPairs(\n options: ListPairsOptions = {},\n ): Promise<PaginatedResult<TokenWrapperPair | TokenWrapperPairWithMetadata>> {\n const page = options.page ?? 1;\n const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n const metadata = options.metadata ?? false;\n\n if (page < 1) {\n throw new ConfigurationError(`page must be >= 1, got ${page}`);\n }\n if (pageSize < 1) {\n throw new ConfigurationError(`pageSize must be >= 1, got ${pageSize}`);\n }\n\n const registry = await this.getRegistryAddress();\n\n // Fetch total (cached)\n const totalCacheKey = `total:${registry}`;\n let total = this.#getCached<number>(totalCacheKey);\n if (total === undefined) {\n const raw = await this.provider.readContract(getTokenPairsLengthContract(registry));\n total = this.#setCached(totalCacheKey, Number(raw));\n }\n\n // Compute slice indices, clamping toIndex to total\n const fromIndex = BigInt((page - 1) * pageSize);\n const clampedToIndex =\n fromIndex + BigInt(pageSize) > BigInt(total) ? BigInt(total) : fromIndex + BigInt(pageSize);\n\n // Page beyond total — return empty\n if (fromIndex >= BigInt(total)) {\n return { items: [], total, page, pageSize };\n }\n\n // Fetch slice (cached)\n const sliceCacheKey = `slice:${registry}:${fromIndex}:${clampedToIndex}`;\n let items = this.#getCached<TokenWrapperPair[]>(sliceCacheKey);\n if (items === undefined) {\n const raw = await this.provider.readContract(\n getTokenPairsSliceContract(registry, fromIndex, clampedToIndex),\n );\n items = this.#setCached(sliceCacheKey, [...raw]);\n }\n\n if (!metadata) {\n return { items, total, page, pageSize };\n }\n\n // Enrich with on-chain metadata (resilient — individual failures don't break the batch)\n const metadataCacheKey = `metadata:${registry}:${fromIndex}:${clampedToIndex}`;\n let metadataItems = this.#getCached<TokenWrapperPairWithMetadata[]>(metadataCacheKey);\n if (metadataItems === undefined) {\n const settled = await Promise.allSettled(items.map((pair) => this.#pairWithMetadata(pair)));\n const hasFailures = settled.some((r) => r.status === \"rejected\");\n const enriched = settled.map((result, i) =>\n result.status === \"fulfilled\"\n ? result.value\n : Object.assign({}, items[i], {\n metadataFailed: true as const,\n underlying: { name: \"Unknown\", symbol: \"???\", decimals: 0, totalSupply: 0n },\n confidential: { name: \"Unknown\", symbol: \"???\", decimals: 0 },\n }),\n );\n // Use negative cache TTL when any metadata fetch failed so retries happen sooner\n metadataItems = this.#setCached(\n metadataCacheKey,\n enriched,\n hasFailures ? NEGATIVE_CACHE_TTL_MS : undefined,\n );\n }\n\n return { items: metadataItems, total, page, pageSize };\n }\n\n async #pairWithMetadata(pair: TokenWrapperPair): Promise<TokenWrapperPairWithMetadata> {\n const [uName, uSymbol, uDecimals, uTotalSupply, cName, cSymbol, cDecimals] = await Promise.all([\n this.provider.readContract(nameContract(pair.tokenAddress)),\n this.provider.readContract(symbolContract(pair.tokenAddress)),\n this.provider.readContract(decimalsContract(pair.tokenAddress)),\n this.provider.readContract(erc20TotalSupplyContract(pair.tokenAddress)),\n this.provider.readContract(nameContract(pair.confidentialTokenAddress)),\n this.provider.readContract(symbolContract(pair.confidentialTokenAddress)),\n this.provider.readContract(decimalsContract(pair.confidentialTokenAddress)),\n ]);\n\n return {\n ...pair,\n underlying: { name: uName, symbol: uSymbol, decimals: uDecimals, totalSupply: uTotalSupply },\n confidential: { name: cName, symbol: cSymbol, decimals: cDecimals },\n };\n }\n\n // ---------------------------------------------------------------------------\n // Structured single-pair lookups\n // ---------------------------------------------------------------------------\n\n /**\n * Look up the confidential token for a given plain ERC-20 address.\n *\n * @param tokenAddress - The plain ERC-20 token address.\n * @returns The lookup result, or `null` if the token has never been registered.\n * **Note:** revoked tokens (registered then invalidated) return a non-null result\n * with `isValid: false`. Check `result.isValid` explicitly rather than using\n * `if (result)` to guard against processing revoked tokens.\n *\n * @example\n * ```ts\n * const result = await registry.getConfidentialToken(usdcAddress);\n * if (result?.isValid) {\n * console.log(result.confidentialTokenAddress);\n * }\n * ```\n */\n async getConfidentialToken(\n tokenAddress: Address,\n ): Promise<{ confidentialTokenAddress: Address; isValid: boolean } | null> {\n const registry = await this.getRegistryAddress();\n const normalized = getAddress(tokenAddress);\n\n const cacheKey = `ct:${registry}:${normalized}`;\n const cached = this.#getCached<{ confidentialTokenAddress: Address; isValid: boolean } | null>(\n cacheKey,\n );\n if (cached !== undefined) {\n return cached;\n }\n\n const [isValid, confidentialTokenAddress] = await this.provider.readContract(\n getConfidentialTokenAddressContract(registry, normalized),\n );\n\n // Zero address means the token is not registered at all (never seen by the registry).\n // A non-zero address with isValid=false means it was registered but later revoked.\n if (confidentialTokenAddress === zeroAddress) {\n return this.#setCached(cacheKey, null, NEGATIVE_CACHE_TTL_MS);\n }\n\n return this.#setCached(cacheKey, { confidentialTokenAddress, isValid });\n }\n\n /**\n * Reverse lookup — find the plain ERC-20 for a given confidential token.\n *\n * @param confidentialTokenAddress - The confidential token address.\n * @returns The lookup result, or `null` if no pair is registered.\n *\n * @example\n * ```ts\n * const result = await registry.getUnderlyingToken(cUsdcAddress);\n * if (result) {\n * console.log(result.tokenAddress, result.isValid);\n * }\n * ```\n */\n async getUnderlyingToken(\n confidentialTokenAddress: Address,\n ): Promise<{ tokenAddress: Address; isValid: boolean } | null> {\n const registry = await this.getRegistryAddress();\n const normalized = getAddress(confidentialTokenAddress);\n\n const cacheKey = `ut:${registry}:${normalized}`;\n const cached = this.#getCached<{ tokenAddress: Address; isValid: boolean } | null>(cacheKey);\n if (cached !== undefined) {\n return cached;\n }\n\n const [isValid, tokenAddress] = await this.provider.readContract(\n getTokenAddressContract(registry, normalized),\n );\n\n // Zero address means the confidential token is not registered at all.\n // A non-zero address with isValid=false means it was registered but later revoked.\n if (tokenAddress === zeroAddress) {\n return this.#setCached(cacheKey, null, NEGATIVE_CACHE_TTL_MS);\n }\n\n return this.#setCached(cacheKey, { tokenAddress, isValid });\n }\n\n // ---------------------------------------------------------------------------\n // Low-level pass-through methods (backward compatible)\n // ---------------------------------------------------------------------------\n\n /**\n * Fetch all token wrapper pairs from the registry.\n *\n * @returns All registered `TokenWrapperPair` entries.\n */\n async getTokenPairs(): Promise<readonly TokenWrapperPair[]> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(getTokenPairsContract(registry));\n }\n\n /**\n * Get the total number of token wrapper pairs.\n *\n * @returns The count as a bigint.\n */\n async getTokenPairsLength(): Promise<bigint> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(getTokenPairsLengthContract(registry));\n }\n\n /**\n * Fetch a range of token wrapper pairs (paginated by index).\n *\n * @param fromIndex - Start index (inclusive).\n * @param toIndex - End index (exclusive).\n * @returns The slice of `TokenWrapperPair` entries.\n */\n async getTokenPairsSlice(\n fromIndex: bigint,\n toIndex: bigint,\n ): Promise<readonly TokenWrapperPair[]> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(getTokenPairsSliceContract(registry, fromIndex, toIndex));\n }\n\n /**\n * Fetch a single token wrapper pair by index.\n *\n * @param index - Zero-based pair index.\n * @returns The `TokenWrapperPair` at that index.\n */\n async getTokenPair(index: bigint): Promise<TokenWrapperPair> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(getTokenPairContract(registry, index));\n }\n\n /**\n * Look up the confidential token address for a given plain ERC-20 token.\n *\n * @param tokenAddress - The plain ERC-20 token address.\n * @returns A tuple `[isValid, confidentialTokenAddress]`. `isValid` is `true` only for a\n * registered, non-revoked wrapper. The address is the zero address when no pair is registered.\n * A non-zero address with `isValid=false` means the wrapper was registered but later revoked.\n */\n async getConfidentialTokenAddress(tokenAddress: Address): Promise<readonly [boolean, Address]> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(\n getConfidentialTokenAddressContract(registry, getAddress(tokenAddress)),\n );\n }\n\n /**\n * Reverse lookup — find the plain ERC-20 for a given confidential token.\n *\n * @param confidentialTokenAddress - The confidential token address.\n * @returns A tuple `[isValid, tokenAddress]`. `isValid` is `true` only for a registered,\n * non-revoked wrapper. The address is the zero address when no pair is registered.\n * A non-zero address with `isValid=false` means the wrapper was registered but later revoked.\n */\n async getTokenAddress(confidentialTokenAddress: Address): Promise<readonly [boolean, Address]> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(\n getTokenAddressContract(registry, getAddress(confidentialTokenAddress)),\n );\n }\n\n /**\n * Check whether a confidential token is registered and valid.\n *\n * @param confidentialTokenAddress - The confidential token address to check.\n * @returns `true` if the token is a known valid wrapper in the registry.\n */\n async isConfidentialTokenValid(confidentialTokenAddress: Address): Promise<boolean> {\n const registry = await this.getRegistryAddress();\n return this.provider.readContract(\n isConfidentialTokenValidContract(registry, getAddress(confidentialTokenAddress)),\n );\n }\n}\n","import { hasFhevmRuntimeConfig, setFhevmRuntimeConfig } from \"@fhevm/sdk/viem\";\nimport { ChainRouter } from \"../chains/router\";\nimport {\n DEFAULT_PERMIT_DURATION_DAYS,\n DEFAULT_TRANSPORT_KEY_PAIR_TTL_SECONDS,\n} from \"../credentials/credential-service\";\nimport {\n PermitTTLSchema,\n TransportKeyPairScopeSchema,\n TransportKeyPairTTLSchema,\n} from \"../credentials/schemas\";\nimport { LoggerService } from \"../services/logger-service\";\nimport type { GenericProvider, GenericSigner } from \"../types\";\nimport { parseSchema } from \"../validation\";\nimport { DEFAULT_REGISTRY_TTL_SECONDS, RegistryTTLSchema } from \"../wrappers-registry\";\nimport { recordAppliedRuntimeConfig } from \"../relayer/applied-runtime\";\nimport { resolveStorage } from \"./resolve\";\nimport type { ZamaConfig, ZamaConfigBase } from \"./types\";\n\n/**\n * @internal Shared config builder — not part of the public API.\n *\n * Applies defaults, validates TTLs, and resolves storage so the\n * returned config is fully populated and ready for `ZamaSDK`.\n */\nexport function buildZamaConfig(\n signer: GenericSigner | undefined,\n provider: GenericProvider,\n params: ZamaConfigBase,\n): ZamaConfig {\n const logger = new LoggerService(params.logger);\n const consumerLogger = params.logger;\n\n const runtime = {\n wasmAssetLoadMode: \"auto\" as const,\n moduleVersions: \"auto\" as const,\n logger: {\n error: (message: string, cause?: unknown) => consumerLogger?.error(message, { cause }),\n warn: (message: string) => consumerLogger?.warn(message),\n debug: (message: string) => consumerLogger?.debug(message),\n },\n ...params.runtime,\n };\n\n if (hasFhevmRuntimeConfig()) {\n // One config per signer is a supported pattern, so a second call is only worth warning about\n // when it carries runtime options that the already-locked runtime config will ignore.\n const message = \"runtime configuration is already set and cannot be changed.\";\n if (params.runtime === undefined) {\n logger.debug(message);\n } else {\n logger.warn(message);\n }\n } else {\n setFhevmRuntimeConfig(runtime);\n recordAppliedRuntimeConfig(runtime);\n }\n\n const { storage, permitStorage } = resolveStorage(params.storage, params.permitStorage);\n\n const router = new ChainRouter(params.chains, params.relayers, logger);\n\n return {\n chains: params.chains,\n router,\n provider,\n signer,\n storage,\n permitStorage,\n transportKeyPairTTL: parseSchema(\n TransportKeyPairTTLSchema,\n params.transportKeyPairTTL ?? DEFAULT_TRANSPORT_KEY_PAIR_TTL_SECONDS,\n ),\n permitTTL: parseSchema(PermitTTLSchema, params.permitTTL ?? DEFAULT_PERMIT_DURATION_DAYS),\n transportKeyPairScope:\n params.transportKeyPairScope === undefined\n ? undefined\n : parseSchema(TransportKeyPairScopeSchema, params.transportKeyPairScope),\n registryTTL: parseSchema(RegistryTTLSchema, params.registryTTL ?? DEFAULT_REGISTRY_TTL_SECONDS),\n logger,\n onEvent: params.onEvent,\n } as unknown as ZamaConfig;\n}\n","import type {\n WalletAccount,\n WalletAccountChange,\n WalletAccountListener,\n WalletAccountStore,\n} from \"../types\";\n\nexport function walletAccountsEqual(\n a: WalletAccount | undefined,\n b: WalletAccount | undefined,\n): boolean {\n return a?.address === b?.address && a?.chainId === b?.chainId;\n}\n\n/**\n * Writable {@link WalletAccountStore} for custom signer adapters. Adapters call\n * {@link MutableWalletAccountStore.setSnapshot} when their underlying wallet\n * provider notifies of a connect, disconnect, account change, or chain change.\n *\n * Most custom adapters should use the {@link createWalletAccountStore} factory\n * rather than instantiating this class directly.\n */\nexport class MutableWalletAccountStore implements WalletAccountStore {\n readonly #listeners = new Set<WalletAccountListener>();\n #snapshot: WalletAccount | undefined;\n #resolved: boolean;\n\n constructor(initial?: WalletAccount) {\n this.#snapshot = initial;\n this.#resolved = initial !== undefined;\n }\n\n /** Return the current wallet account snapshot, or `undefined` when none is set. */\n getSnapshot(): WalletAccount | undefined {\n return this.#snapshot;\n }\n\n /**\n * Whether the store has received at least one snapshot (via the constructor\n * or {@link setSnapshot}). Adapters whose initial account is only available\n * asynchronously start unresolved; callers can distinguish \"still loading\"\n * from \"wallet not connected\".\n */\n isReady(): boolean {\n return this.#resolved;\n }\n\n /**\n * Push a new wallet account snapshot. No-op when the next value is\n * value-equal to the current one. Emits `{ previous, next }` to every\n * subscriber otherwise.\n */\n setSnapshot(next: WalletAccount | undefined): void {\n this.#resolved = true;\n const previous = this.#snapshot;\n if (walletAccountsEqual(previous, next)) {\n return;\n }\n this.#snapshot = next;\n this.#emit({ previous, next });\n }\n\n /**\n * Register a listener for wallet account changes, emitting the current\n * snapshot immediately if one exists.\n * @returns An unsubscribe function.\n */\n subscribe(listener: WalletAccountListener): () => void {\n this.#listeners.add(listener);\n const snapshot = this.#snapshot;\n if (snapshot) {\n listener({ previous: undefined, next: snapshot });\n }\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #emit(change: WalletAccountChange): void {\n for (const listener of this.#listeners) {\n listener(change);\n }\n }\n}\n\n/**\n * Create a {@link MutableWalletAccountStore} for a custom {@link GenericSigner}\n * adapter.\n *\n * @param initial - Optional initial wallet account snapshot.\n */\nexport function createWalletAccountStore(initial?: WalletAccount): MutableWalletAccountStore {\n return new MutableWalletAccountStore(initial);\n}\n","import type { Hex } from \"viem\";\nimport { WalletNotConnectedError } from \"../errors\";\nimport type { EIP712TypedData } from \"../relayer/types\";\nimport type {\n ContractAbi,\n GenericSigner,\n WalletAccount,\n WriteContractArgs,\n WriteContractConfig,\n WriteFunctionName,\n} from \"../types\";\nimport { MutableWalletAccountStore } from \"./wallet-account-store\";\n\n/**\n * Abstract base class that supplies the wallet-account / dispose\n * boilerplate every signer adapter needs: an observable wallet-account\n * store, `requireWalletAccount`, and an idempotent `dispose` /\n * `Disposable`. Subclasses provide `signTypedData` and `writeContract` — the\n * resulting concrete class automatically satisfies {@link GenericSigner}.\n *\n * Using this class is optional — implementing {@link GenericSigner} directly\n * with {@link createWalletAccountStore} remains fully supported.\n */\nexport abstract class BaseSigner implements GenericSigner, Disposable {\n /** Observable wallet account readiness state. */\n readonly walletAccount: MutableWalletAccountStore;\n #disposed = false;\n\n constructor(initial?: WalletAccount) {\n this.walletAccount = new MutableWalletAccountStore(initial);\n }\n\n /** Return the connected wallet account or throw {@link WalletNotConnectedError}. */\n requireWalletAccount(operation: string): WalletAccount {\n const account = this.walletAccount.getSnapshot();\n if (!account) {\n throw new WalletNotConnectedError(operation);\n }\n return account;\n }\n\n /** Sign EIP-712 typed data (used for decrypt authorization). */\n abstract signTypedData(typedData: EIP712TypedData): Promise<Hex>;\n\n /** Send a write transaction and return the tx hash. */\n abstract writeContract<\n const TAbi extends ContractAbi,\n TFunctionName extends WriteFunctionName<TAbi>,\n const TArgs extends WriteContractArgs<TAbi, TFunctionName>,\n >(config: WriteContractConfig<TAbi, TFunctionName, TArgs>): Promise<Hex>;\n\n /** Release adapter-owned wallet watchers or provider event listeners; idempotent. */\n dispose(): void {\n if (this.#disposed) {\n return;\n }\n this.#disposed = true;\n this.onDispose();\n }\n\n /** `Disposable` support; delegates to {@link BaseSigner.dispose}. */\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n /** Subclass cleanup hook, invoked once on the first {@link BaseSigner.dispose} call. */\n protected onDispose(): void {}\n}\n"],"mappings":"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"}
|
package/dist/esm/ethers/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"../relayer-CVKO9GmQ.js";import{F as t,K as n,L as r,M as i,N as a,O as o,P as s,R as c,S as l,T as u,U as d,V as f,W as p,a as m,i as h,m as g,n as _,o as v,r as y,s as b,t as ee,z as te}from"../wrappers-registry-BLe7PkZi.js";import{i as x,t as S}from"../base-signer-
|
|
1
|
+
import{t as e}from"../relayer-CVKO9GmQ.js";import{F as t,K as n,L as r,M as i,N as a,O as o,P as s,R as c,S as l,T as u,U as d,V as f,W as p,a as m,i as h,m as g,n as _,o as v,r as y,s as b,t as ee,z as te}from"../wrappers-registry-BLe7PkZi.js";import{i as x,t as S}from"../base-signer-DGIEzavu.js";import{n as C}from"../assertions-Ca1ePdRJ.js";import{t as w}from"../eip1193-subscribe-CNxVHW6y.js";import{ProviderRpcError as T,decodeFunctionResult as E,encodeFunctionData as D,getAddress as O,isHex as k}from"viem";import{BrowserProvider as A,Contract as j,Transaction as M,ethers as N}from"ethers";function P(e){return{to:e.address,data:D({abi:e.abi,functionName:e.functionName,args:e.args}),...e.gas===void 0?{}:{gasLimit:e.gas},...e.value===void 0?{}:{value:e.value}}}async function F(e,t){let n=await e.call(P(t));if(!k(n))throw TypeError(`Expected hex string, got: ${n}`);return E({abi:t.abi,functionName:t.functionName,data:n})}async function I(e,t){let n=await e.sendTransaction(P(t));if(!k(n.hash))throw TypeError(`Expected hex string, got: ${n.hash}`);return n.hash}function L(e,t,n){return F(e,l(t,n))}function R(e,t){return F(e,a(t))}function z(e,t,n){return F(e,g(t,n))}function B(e,t,n,r,i){return I(e,u(t,n,r,i))}function V(e,t,n,r,i,a){return I(e,s(t,n,r,i,a))}function H(e,n,r,i,a){return I(e,t(n,r,i,a))}function U(e,t,n,r,i){return I(e,o(t,n,r,i))}function W(e,t,n,r){return I(e,i(t,n,r))}function G(e,t,n,i){return I(e,r(t,n,i))}function K(e,t){return F(e,h(t))}function q(e,t){return F(e,m(t))}function J(e,t,n,r){return F(e,v(t,n,r))}function Y(e,t,n){return F(e,y(t,n))}function X(e,t,n){return F(e,ee(t,n))}function Z(e,t,n){return F(e,_(t,n))}function ne(e,t,n){return F(e,b(t,n))}var Q=class{#e;constructor(e){`ethereum`in e?this.#e=new A(e.ethereum):this.#e=e.provider}async getChainId(){let e=await this.#e.getNetwork();return Number(e.chainId)}async readContract(e){return F(this.#e,{address:e.address,abi:e.abi,functionName:e.functionName,args:e.args})}async getBlockTimestamp(){let t=await this.#e.getBlock(`latest`);if(!t)throw new e(`EthersProvider.getBlockTimestamp: failed to fetch latest block`);if(t.timestamp===null)throw new e(`EthersProvider.getBlockTimestamp: latest block has no timestamp`);return BigInt(t.timestamp)}async waitForTransactionReceipt(e){let t=await this.#e.waitForTransaction(e);if(!t)throw new n(`EthersProvider.waitForTransactionReceipt: no receipt found for tx ${e}. The transaction may have been dropped from the mempool, or the RPC's wait timeout elapsed.`);return{logs:t.logs.map(e=>({address:e.address,topics:e.topics.filter(e=>e!==null),data:e.data}))}}async prepareTransaction(t){let{from:r,calldata:i}=t,a=new N.Interface(i.abi),o=[];a.forEachFunction(e=>{e.name===i.functionName&&o.push(e)});let s=i.args.length,c=o.length===1?o[0]:o.find(e=>e.inputs.length===s);if(!c)throw Error(`Function ${i.functionName}(${s} args) not found in ABI`);let l=a.encodeFunctionData(c,i.args);C(l,`data`);let u=i.value??0n,d=this.#e.getNetwork(),f=t.nonce===void 0?this.#e.getTransactionCount(r,`pending`):Promise.resolve(t.nonce),p=t.gasLimit===void 0?i.gas??this.#e.estimateGas({from:r,to:i.address,data:l,value:u}).catch(e=>{throw new n(`EthersProvider.prepareTransaction: gas estimation reverted for ${i.functionName} on ${i.address}`,{cause:e})}):Promise.resolve(t.gasLimit),m=t.fees??this.#e.getFeeData(),[h,g,_,v]=await Promise.all([d,f,p,m]),y=v.maxFeePerGas,b=v.maxPriorityFeePerGas;if(y===null||b===null)throw new e(`EthersProvider.prepareTransaction: EIP-1559 fee data unavailable (provider returned null maxFeePerGas). The connected chain may not support EIP-1559 type-2 transactions.`);return M.from({type:2,chainId:Number(h.chainId),nonce:g,to:i.address,data:l,value:u,gasLimit:_,maxFeePerGas:y,maxPriorityFeePerGas:b}).unsignedSerialized}},$=class extends S{#e;#t;#n;#r;#i;constructor(e){super(),`ethereum`in e?(this.#e=new A(e.ethereum),this.#n=e.ethereum,this.#r=w({provider:e.ethereum,getInitialWalletAccount:()=>this.#l(),onWalletAccountChange:({next:e})=>{this.walletAccount.setSnapshot(e)}})):(this.#t=e.signer,this.#r=()=>{},c(`refresh wallet account`,async()=>{await this.refreshWalletAccount()}))}requireWalletAccount(e){let t=this.walletAccount.getSnapshot();if(!t&&!this.walletAccount.isReady())throw new d(e);if(!t)throw new p(e);return t}refreshWalletAccount(){return this.#n?this.#c():this.#t?this.#s(this.#t):Promise.resolve(void 0)}onDispose(){this.#r()}async#a(){if(this.#t)return this.#t;if(!this.#e)throw new f(`resolveSigner`);return this.#e.getSigner()}async#o(e){let t=e.provider;if(!t)return;let[n,r]=await Promise.all([e.getAddress(),t.getNetwork()]);return{address:O(n),chainId:Number(r.chainId)}}async signTypedData(e){let t=await this.#a(),{domain:n,types:r,message:i}=e,{EIP712Domain:a,...o}=r,s=Object.fromEntries(Object.entries(o).map(([e,t])=>[e,[...t]])),c=await t.signTypedData(n,s,i);if(!k(c))throw TypeError(`Expected hex string, got: ${c}`);return c}async writeContract(e){let t=await this.#a(),n=new j(e.address,e.abi,t),r={};e.value!==void 0&&(r.value=e.value),e.gas!==void 0&&(r.gasLimit=e.gas);let i=await n.getFunction(e.functionName)(...e.args,r);if(!k(i.hash))throw TypeError(`Expected hex string, got: ${i.hash}`);return i.hash}async#s(e){return this.#i??=this.#o(e).then(e=>(this.walletAccount.setSnapshot(e),e)).finally(()=>{this.#i=void 0}),this.#i}async#c(){let e=await this.#l();return this.walletAccount.setSnapshot(e),e}async#l(){let e=this.#n;if(!e)return;let[t,n]=await Promise.all([e.request({method:`eth_accounts`}),e.request({method:`eth_chainId`})]);if(!Array.isArray(t)||typeof t[0]!=`string`)return;let r=Number(n);if(!(!Number.isSafeInteger(r)||r<=0))return{address:O(t[0]),chainId:r}}};function re(e){if(`signer`in e&&e.signer){let t=new $({signer:e.signer});if(!e.signer.provider)throw Error(`createConfig requires a Signer with an attached provider for chain reads`);return x(t,new Q({provider:e.signer.provider}),e)}return x(new $({ethereum:e.ethereum}),`provider`in e&&e.provider?new Q({provider:e.provider}):new Q({ethereum:e.ethereum}),e)}export{S as BaseSigner,Q as EthersProvider,$ as EthersSigner,T as ProviderRpcError,te as ZamaSDKEvents,re as createConfig,L as readConfidentialBalanceOfContract,X as readConfidentialTokenAddressContract,ne as readIsConfidentialTokenValidContract,z as readSupportsInterfaceContract,Z as readTokenAddressContract,Y as readTokenPairContract,K as readTokenPairsContract,q as readTokenPairsLengthContract,J as readTokenPairsSliceContract,R as readUnderlyingTokenContract,B as writeConfidentialTransferContract,U as writeFinalizeUnwrapContract,W as writeSetOperatorContract,V as writeUnwrapContract,H as writeUnwrapFromBalanceContract,G as writeWrapContract};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|