@zama-fhe/sdk 1.2.0-alpha.5 → 2.0.0-alpha.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"activity.cjs","names":["erc20Abi","#map","#cachedDerivedKey","#cachedDerivedKeyIdentity","#deriveKey","#storage","#assertSessionEntry","#onEvent","#extendContracts","SigningRejectedError","SigningFailedError","#deleteCredentials","#createPromise","#createPromiseKey","#extendPromise","#extendCredentials","#relayer","#storeKey","#cachedStoreKey","#cachedStoreKeyIdentity","#relayer","#storeKey","#create","#signDelegated","#cachedStoreKey","#cachedStoreKeyIdentity","#onEvent","supportsInterfaceContract","ERC7984_INTERFACE_ID","ERC7984_WRAPPER_INTERFACE_ID","#batchDecryptCore","#assertDelegationActive","DecryptionFailedError","wrapperExistsContract","getWrapperContract","underlyingContract","DelegationNotFoundError","DelegationExpiredError","confidentialBalanceOfContract","ConfigurationError","NoCiphertextError","RelayerRequestFailedError","SigningRejectedError","SigningFailedError"],"sources":["../../src/utils.ts","../../src/contracts/erc20.ts","../../src/contracts/fee-manager.ts","../../src/contracts/acl.ts","../../src/contracts/constants.ts","../../src/events/sdk-events.ts","../../src/token/memory-storage.ts","../../src/token/credential-crypto.ts","../../src/token/credential-validation.ts","../../src/token/session-signatures.ts","../../src/token/credential-manager-base.ts","../../src/token/credentials-manager.ts","../../src/token/delegated-credentials-manager.ts","../../src/token/balance-cache.ts","../../src/token/readonly-token.ts","../../src/events/onchain-events.ts","../../src/activity.ts"],"sourcesContent":["import type { Hex } from \"viem\";\n\n/** Coerce an unknown caught value to an Error instance. */\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/** Normalize a un-prefixed hex payload to a 0x-prefixed `Hex` value. */\nexport function prefixHex(value: string): Hex {\n return (value.startsWith(\"0x\") ? value : `0x${value}`) as Hex;\n}\n\n/** Convert a public `Hex` value back an unprefixed format. */\nexport function unprefixHex(value: Hex): string {\n assertCondition(value.startsWith(\"0x\"), `Expected 0x-prefixed hex, got: ${value}`);\n return value.slice(2);\n}\n\nexport function assertObject(\n value: unknown,\n context: string,\n): asserts value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new TypeError(`${context} must be an object, got ${typeof value}`);\n }\n}\n\nexport function assertString(value: unknown, context: string): asserts value is string {\n if (typeof value !== \"string\") {\n throw new TypeError(`${context} must be a string, got ${typeof value}`);\n }\n}\n\nexport function assertArray(value: unknown, context: string): asserts value is unknown[] {\n if (!Array.isArray(value)) {\n throw new TypeError(`${context} must be an array, got ${typeof value}`);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function assertFunction(value: unknown, context: string): asserts value is Function {\n if (typeof value !== \"function\") {\n throw new TypeError(`${context} must be a function, got ${typeof value}`);\n }\n}\n\n/** Assert that `obj[key]` is a string. Narrows `obj` to include `{ [key]: string }`. */\nexport function assertStringProp<\n K extends string,\n O extends Record<string, unknown> = Record<string, unknown>,\n>(obj: O, key: K, context: string): asserts obj is O & Record<K, string> {\n assertString(obj[key], context);\n}\n\n/** Assert that `obj[key]` is a function. Narrows `obj` to include `{ [key]: F }`. */\nexport function assertFunctionProp<\n K extends string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n F extends Function,\n O extends Record<string, unknown> = Record<string, unknown>,\n>(obj: O, key: K, context: string): asserts obj is O & Record<K, F> {\n assertFunction(obj[key], context);\n}\n\nexport function assertCondition(condition: boolean, message: string): asserts condition {\n if (!condition) {\n throw new TypeError(message);\n }\n}\n\n// ── Environment detection ────────────────────────────────────\n\n/**\n * Subset of the WebExtensions `runtime` API used by the SDK.\n * @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime\n */\nexport interface BrowserExtensionRuntime {\n /** The ID of the extension. */\n id: string;\n /** Convert a relative path within the extension to a fully-qualified URL. */\n getURL: (path: string) => string;\n}\n\n/**\n * Return the browser extension runtime object, or `undefined` outside extensions.\n * Works across Chrome/Edge (`chrome.runtime`) and Firefox/Safari (`browser.runtime`).\n * Extensions have restricted CSP that blocks `blob:` URLs, so callers use\n * this to detect the environment and resolve file URLs via `runtime.getURL`.\n */\nexport function getBrowserExtensionRuntime(): BrowserExtensionRuntime | undefined {\n const g = globalThis as unknown as Record<string, unknown>;\n for (const ns of [g.chrome, g.browser]) {\n try {\n assertObject(ns, \"ns\");\n const { runtime } = ns;\n assertObject(runtime, \"runtime\");\n assertStringProp(runtime, \"id\", \"runtime.id\");\n assertFunctionProp<\"getURL\", (path: string) => string>(runtime, \"getURL\", \"runtime.getURL\");\n return runtime;\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\n// ── Concurrency helper ──────────────────────────────────────\n\n/**\n * Execute an array of async thunks with bounded concurrency.\n * Defaults to `Infinity` (equivalent to `Promise.all`).\n */\nexport async function pLimit<T>(\n fns: (() => Promise<T>)[],\n maxConcurrency = Infinity,\n): Promise<T[]> {\n if (!Number.isFinite(maxConcurrency) || maxConcurrency >= fns.length) {\n return Promise.all(fns.map((f) => f()));\n }\n\n const results: T[] = Array.from({ length: fns.length });\n let index = 0;\n\n async function worker() {\n while (index < fns.length) {\n const i = index++;\n results[i] = await fns[i]!();\n }\n }\n\n await Promise.all(Array.from({ length: maxConcurrency }, worker));\n return results;\n}\n","import { erc20Abi, type Address } from \"viem\";\n\n/**\n * Returns the contract config to read a token's name.\n *\n * @example\n * ```ts\n * const name = await signer.readContract(nameContract(tokenAddress));\n * ```\n */\nexport function nameContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"name\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read a token's symbol.\n *\n * @example\n * ```ts\n * const symbol = await signer.readContract(symbolContract(tokenAddress));\n * ```\n */\nexport function symbolContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"symbol\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read a token's decimals.\n *\n * @example\n * ```ts\n * const decimals = await signer.readContract(decimalsContract(tokenAddress));\n * ```\n */\nexport function decimalsContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"decimals\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read an ERC-20 balance.\n *\n * @example\n * ```ts\n * const balance = await signer.readContract(\n * balanceOfContract(tokenAddress, account),\n * );\n * ```\n */\nexport function balanceOfContract(tokenAddress: Address, account: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"balanceOf\",\n args: [account],\n } as const;\n}\n\n/**\n * Returns the contract config to read an ERC-20 allowance.\n *\n * @example\n * ```ts\n * const allowance = await signer.readContract(\n * allowanceContract(tokenAddress, owner, spender),\n * );\n * ```\n */\nexport function allowanceContract(tokenAddress: Address, owner: Address, spender: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"allowance\",\n args: [owner, spender],\n } as const;\n}\n\n/**\n * Returns the contract config for an ERC-20 approve.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * approveContract(tokenAddress, spender, amount),\n * );\n * ```\n */\nexport function approveContract(tokenAddress: Address, spender: Address, value: bigint) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"approve\",\n args: [spender, value],\n } as const;\n}\n","import type { Address } from \"viem\";\n\nexport const feeManagerAbi = [\n {\n inputs: [\n {\n internalType: \"uint256\",\n name: \"amount\",\n type: \"uint256\",\n },\n {\n internalType: \"address\",\n name: \"wrapFrom\",\n type: \"address\",\n },\n {\n internalType: \"address\",\n name: \"wrapTo\",\n type: \"address\",\n },\n ],\n name: \"getWrapFee\",\n outputs: [\n {\n internalType: \"uint256\",\n name: \"\",\n type: \"uint256\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [\n {\n internalType: \"uint64\",\n name: \"amount\",\n type: \"uint64\",\n },\n {\n internalType: \"address\",\n name: \"unwrapFrom\",\n type: \"address\",\n },\n {\n internalType: \"address\",\n name: \"unwrapTo\",\n type: \"address\",\n },\n ],\n name: \"getUnwrapFee\",\n outputs: [\n {\n internalType: \"uint64\",\n name: \"\",\n type: \"uint64\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [],\n name: \"getBatchTransferFee\",\n outputs: [\n {\n internalType: \"uint64\",\n name: \"\",\n type: \"uint64\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [],\n name: \"getFeeRecipient\",\n outputs: [\n {\n internalType: \"address\",\n name: \"\",\n type: \"address\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n] as const;\n\n/**\n * Returns the contract config to compute the wrap fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getWrapFeeContract(feeManager, amount, from, to),\n * );\n * ```\n */\nexport function getWrapFeeContract(\n feeManagerAddress: Address,\n amount: bigint,\n wrapFrom: Address,\n wrapTo: Address,\n) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getWrapFee\",\n args: [amount, wrapFrom, wrapTo],\n } as const;\n}\n\n/**\n * Returns the contract config to compute the unwrap fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getUnwrapFeeContract(feeManager, amount, from, to),\n * );\n * ```\n */\nexport function getUnwrapFeeContract(\n feeManagerAddress: Address,\n amount: bigint,\n unwrapFrom: Address,\n unwrapTo: Address,\n) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getUnwrapFee\",\n args: [amount, unwrapFrom, unwrapTo],\n } as const;\n}\n\n/**\n * Returns the contract config to read the batch transfer fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getBatchTransferFeeContract(feeManagerAddress),\n * );\n * ```\n */\nexport function getBatchTransferFeeContract(feeManagerAddress: Address) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getBatchTransferFee\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read the fee recipient address.\n *\n * @example\n * ```ts\n * const recipient = await signer.readContract(\n * getFeeRecipientContract(feeManagerAddress),\n * );\n * ```\n */\nexport function getFeeRecipientContract(feeManagerAddress: Address) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getFeeRecipient\",\n args: [],\n } as const;\n}\n","import type { Address } from \"viem\";\n\nexport const aclAbi = [\n {\n inputs: [\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n { internalType: \"uint64\", name: \"expirationDate\", type: \"uint64\" },\n ],\n name: \"delegateForUserDecryption\",\n outputs: [],\n stateMutability: \"nonpayable\",\n type: \"function\",\n },\n {\n inputs: [\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n ],\n name: \"revokeDelegationForUserDecryption\",\n outputs: [],\n stateMutability: \"nonpayable\",\n type: \"function\",\n },\n {\n inputs: [\n { internalType: \"address\", name: \"delegator\", type: \"address\" },\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n ],\n name: \"getUserDecryptionDelegationExpirationDate\",\n outputs: [{ internalType: \"uint64\", name: \"\", type: \"uint64\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n] as const;\n\n/**\n * Returns the contract config to delegate user decryption rights.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * delegateForUserDecryptionContract(aclAddress, delegateAddress, contractAddress, expirationDate),\n * );\n * ```\n */\nexport function delegateForUserDecryptionContract(\n aclAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n expirationDate: bigint,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"delegateForUserDecryption\",\n args: [delegateAddress, contractAddress, expirationDate],\n } as const;\n}\n\n/**\n * Returns the contract config to revoke a user decryption delegation.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * revokeDelegationContract(aclAddress, delegateAddress, contractAddress),\n * );\n * ```\n */\nexport function revokeDelegationContract(\n aclAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"revokeDelegationForUserDecryption\",\n args: [delegateAddress, contractAddress],\n } as const;\n}\n\n/**\n * Returns the contract config to read the delegation expiry date.\n *\n * @example\n * ```ts\n * const expiry = await signer.readContract(\n * getDelegationExpiryContract(aclAddress, delegatorAddress, delegateAddress, contractAddress),\n * );\n * ```\n */\nexport function getDelegationExpiryContract(\n aclAddress: Address,\n delegatorAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"getUserDecryptionDelegationExpirationDate\",\n args: [delegatorAddress, delegateAddress, contractAddress],\n } as const;\n}\n","/** uint64 max — represents a permanent (no-expiry) delegation. */\nexport const MAX_UINT64 = 2n ** 64n - 1n;\n","import type { Address, Hex } from \"viem\";\n\n/**\n * All SDK event keys, accessible as `ZamaSDKEvents.EncryptStart` etc.\n */\nexport const ZamaSDKEvents = {\n // Credentials lifecycle\n CredentialsLoading: \"credentials:loading\",\n CredentialsCached: \"credentials:cached\",\n CredentialsExpired: \"credentials:expired\",\n CredentialsCreating: \"credentials:creating\",\n CredentialsCreated: \"credentials:created\",\n CredentialsRevoked: \"credentials:revoked\",\n CredentialsAllowed: \"credentials:allowed\",\n SessionExpired: \"session:expired\",\n // FHE operations\n EncryptStart: \"encrypt:start\",\n EncryptEnd: \"encrypt:end\",\n EncryptError: \"encrypt:error\",\n DecryptStart: \"decrypt:start\",\n DecryptEnd: \"decrypt:end\",\n DecryptError: \"decrypt:error\",\n // Write operations\n TransactionError: \"transaction:error\",\n ShieldSubmitted: \"shield:submitted\",\n TransferSubmitted: \"transfer:submitted\",\n TransferFromSubmitted: \"transferFrom:submitted\",\n ApproveSubmitted: \"approve:submitted\",\n ApproveUnderlyingSubmitted: \"approveUnderlying:submitted\",\n UnwrapSubmitted: \"unwrap:submitted\",\n FinalizeUnwrapSubmitted: \"finalizeUnwrap:submitted\",\n // Delegation operations\n DelegationSubmitted: \"delegation:submitted\",\n RevokeDelegationSubmitted: \"revokeDelegation:submitted\",\n // Unshield orchestration\n UnshieldPhase1Submitted: \"unshield:phase1_submitted\",\n UnshieldPhase2Started: \"unshield:phase2_started\",\n UnshieldPhase2Submitted: \"unshield:phase2_submitted\",\n} as const;\n\n/** Union of all SDK event type strings. */\nexport type ZamaSDKEventType = (typeof ZamaSDKEvents)[keyof typeof ZamaSDKEvents];\n\n// -- Base fields present on every event --\n\nexport interface BaseEvent {\n tokenAddress?: Address;\n timestamp: number;\n /** Shared identifier linking related events in multi-phase operations (e.g. unshield). */\n operationId?: string;\n}\n\n// -- Per-event typed payloads --\n\nexport interface CredentialsLoadingEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsLoading;\n /** Contract addresses being requested. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCachedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCached;\n /** Contract addresses covered by the cached credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsExpiredEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsExpired;\n /** Contract addresses that need re-authorization. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCreatingEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCreating;\n /** Contract addresses being authorized. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCreatedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCreated;\n /** Contract addresses covered by the new credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsRevokedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsRevoked;\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsAllowedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsAllowed;\n /** Contract addresses covered by the authorized credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface SessionExpiredEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.SessionExpired;\n /** Why the session expired. Currently always `\"ttl\"`, extensible for future inactivity timeout. */\n reason: \"ttl\";\n}\n\nexport interface EncryptStartEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptStart;\n}\n\nexport interface EncryptEndEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptEnd;\n durationMs: number;\n}\n\nexport interface EncryptErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptError;\n /** The error that caused the encryption to fail. */\n error: Error;\n durationMs: number;\n}\n\nexport interface DecryptStartEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptStart;\n}\n\nexport interface DecryptEndEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptEnd;\n durationMs: number;\n}\n\nexport interface DecryptErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptError;\n /** The error that caused the decryption to fail. */\n error: Error;\n durationMs: number;\n}\n\nexport interface TransactionErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransactionError;\n /** Which write operation failed. */\n operation: string;\n /** The error that caused the transaction to fail. */\n error: Error;\n}\n\nexport interface ShieldSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ShieldSubmitted;\n txHash: Hex;\n}\n\nexport interface TransferSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransferSubmitted;\n txHash: Hex;\n}\n\nexport interface TransferFromSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransferFromSubmitted;\n txHash: Hex;\n}\n\nexport interface ApproveSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ApproveSubmitted;\n txHash: Hex;\n}\n\nexport interface ApproveUnderlyingSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ApproveUnderlyingSubmitted;\n txHash: Hex;\n}\n\nexport interface UnwrapSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnwrapSubmitted;\n txHash: Hex;\n}\n\nexport interface FinalizeUnwrapSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.FinalizeUnwrapSubmitted;\n txHash: Hex;\n}\n\nexport interface DelegationSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DelegationSubmitted;\n txHash: Hex;\n}\n\nexport interface RevokeDelegationSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.RevokeDelegationSubmitted;\n txHash: Hex;\n}\n\nexport interface UnshieldPhase1SubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase1Submitted;\n txHash: Hex;\n}\n\nexport interface UnshieldPhase2StartedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase2Started;\n}\n\nexport interface UnshieldPhase2SubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase2Submitted;\n txHash: Hex;\n}\n\n/** Discriminated union of all SDK events. Never contains amounts, private keys, handles, or proofs. */\nexport type ZamaSDKEvent =\n | CredentialsLoadingEvent\n | CredentialsCachedEvent\n | CredentialsExpiredEvent\n | CredentialsCreatingEvent\n | CredentialsCreatedEvent\n | CredentialsRevokedEvent\n | CredentialsAllowedEvent\n | SessionExpiredEvent\n | EncryptStartEvent\n | EncryptEndEvent\n | EncryptErrorEvent\n | DecryptStartEvent\n | DecryptEndEvent\n | DecryptErrorEvent\n | TransactionErrorEvent\n | ShieldSubmittedEvent\n | TransferSubmittedEvent\n | TransferFromSubmittedEvent\n | ApproveSubmittedEvent\n | ApproveUnderlyingSubmittedEvent\n | UnwrapSubmittedEvent\n | FinalizeUnwrapSubmittedEvent\n | DelegationSubmittedEvent\n | RevokeDelegationSubmittedEvent\n | UnshieldPhase1SubmittedEvent\n | UnshieldPhase2StartedEvent\n | UnshieldPhase2SubmittedEvent;\n\nexport type ZamaSDKEventListener = (event: ZamaSDKEvent) => void;\n\n/** Distributive Omit that preserves the discriminated union. */\nexport type ZamaSDKEventInput = ZamaSDKEvent extends infer E\n ? E extends ZamaSDKEvent\n ? Omit<E, \"timestamp\" | \"tokenAddress\">\n : never\n : never;\n","import type { GenericStorage } from \"./token.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 async get<T = unknown>(key: string): Promise<T | null> {\n return (this.#map.get(key) as T) ?? null;\n }\n\n async set<T = unknown>(key: string, value: T): Promise<void> {\n this.#map.set(key, value);\n }\n\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 { Address, Hex } from \"viem\";\nimport { prefixHex } from \"../utils\";\n\n/** Encrypted data format with IV for AES-GCM decryption. */\nexport interface EncryptedData {\n /** Base64-encoded initialization vector. */\n iv: string;\n /** Base64-encoded ciphertext. */\n ciphertext: string;\n}\n\n/**\n * Manages AES-GCM encryption and decryption of FHE private keys.\n *\n * The encryption key is derived from a wallet signature via PBKDF2\n * (600 000 iterations, SHA-256). The signature is a secret known only\n * to the wallet holder, providing meaningful encryption protection\n * for the stored private key.\n *\n * The derived key is cached internally so that repeated encrypt/decrypt\n * calls with the same (signature, address) pair skip the expensive\n * PBKDF2 derivation.\n */\nexport class CredentialCrypto {\n #cachedDerivedKey: CryptoKey | null = null;\n #cachedDerivedKeyIdentity: string | null = null;\n\n /** Clear the cached derived key. Call when the session is revoked or the signer changes. */\n clearCache(): void {\n this.#cachedDerivedKey = null;\n this.#cachedDerivedKeyIdentity = null;\n }\n\n /** Encrypt an FHE private key using AES-GCM with a key derived from the wallet signature. */\n async encrypt(plaintext: Hex, signature: Hex, address: Address): Promise<EncryptedData> {\n const key = await this.#deriveKey(signature, address);\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const encoder = new TextEncoder();\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n encoder.encode(plaintext),\n );\n\n return {\n iv: btoa(String.fromCharCode(...iv)),\n ciphertext: btoa(String.fromCharCode(...new Uint8Array(ciphertext))),\n };\n }\n\n /** Decrypt an AES-GCM encrypted FHE private key using a key derived from the wallet signature. */\n async decrypt(encrypted: EncryptedData, signature: Hex, address: Address): Promise<Hex> {\n const key = await this.#deriveKey(signature, address);\n const iv = Uint8Array.from(atob(encrypted.iv), (c) => c.charCodeAt(0));\n const ciphertext = Uint8Array.from(atob(encrypted.ciphertext), (c) => c.charCodeAt(0));\n\n const plaintext = await crypto.subtle.decrypt({ name: \"AES-GCM\", iv }, key, ciphertext);\n\n return prefixHex(new TextDecoder().decode(plaintext));\n }\n\n async #deriveKey(signature: Hex, address: Address): Promise<CryptoKey> {\n const identity = `${signature}:${address}`;\n if (this.#cachedDerivedKey && this.#cachedDerivedKeyIdentity === identity) {\n return this.#cachedDerivedKey;\n }\n\n const encoder = new TextEncoder();\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(signature),\n \"PBKDF2\",\n false,\n [\"deriveKey\"],\n );\n\n const key = await crypto.subtle.deriveKey(\n {\n name: \"PBKDF2\",\n salt: encoder.encode(address),\n iterations: 600_000,\n hash: \"SHA-256\",\n },\n keyMaterial,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n\n this.#cachedDerivedKeyIdentity = identity;\n this.#cachedDerivedKey = key;\n return key;\n }\n}\n","import { getAddress, isAddress, type Address, type Hex } from \"viem\";\nimport { assertArray, assertCondition, assertObject, assertString } from \"../utils\";\nimport type { EncryptedData } from \"./credential-crypto\";\n\n/** Encrypted credential shape stored in persistent storage (shared base fields). */\nexport interface BaseEncryptedCredentials {\n publicKey: Hex;\n contractAddresses: Address[];\n encryptedPrivateKey: EncryptedData;\n startTimestamp: number;\n durationDays: number;\n}\n\n/**\n * Assert that `data` contains the base encrypted credential fields:\n * publicKey, contractAddresses (valid hex addresses), and encryptedPrivateKey (iv + ciphertext).\n */\nexport function assertBaseEncryptedCredentials(\n data: unknown,\n): asserts data is BaseEncryptedCredentials {\n assertObject(data, \"Stored credentials\");\n assertString(data.publicKey, \"credentials.publicKey\");\n assertArray(data.contractAddresses, \"credentials.contractAddresses\");\n for (const addr of data.contractAddresses) {\n assertCondition(\n typeof addr === \"string\" && isAddress(addr, { strict: false }),\n `Expected each contractAddress to be a valid hex address`,\n );\n }\n assertObject(data.encryptedPrivateKey, \"credentials.encryptedPrivateKey\");\n assertString(data.encryptedPrivateKey.iv, \"encryptedPrivateKey.iv\");\n assertString(data.encryptedPrivateKey.ciphertext, \"encryptedPrivateKey.ciphertext\");\n}\n\n/**\n * Assert that `data` contains the delegated-specific fields:\n * delegatorAddress, delegateAddress (valid addresses), startTimestamp and durationDays (numbers).\n */\nexport function assertDelegatedFields(data: unknown): asserts data is BaseEncryptedCredentials & {\n delegatorAddress: Address;\n delegateAddress: Address;\n} {\n assertBaseEncryptedCredentials(data);\n const obj = data as unknown as Record<string, unknown>;\n assertCondition(\n typeof obj.delegatorAddress === \"string\" && isAddress(obj.delegatorAddress, { strict: false }),\n \"Expected credentials.delegatorAddress to be a valid address\",\n );\n assertCondition(\n typeof obj.delegateAddress === \"string\" && isAddress(obj.delegateAddress, { strict: false }),\n \"Expected credentials.delegateAddress to be a valid address\",\n );\n assertCondition(typeof obj.startTimestamp === \"number\", \"Expected startTimestamp to be a number\");\n assertCondition(typeof obj.durationDays === \"number\", \"Expected durationDays to be a number\");\n}\n\n/** Check if credentials are still within their keypair TTL. */\nexport function isTimeValid(creds: { startTimestamp: number; durationDays: number }): boolean {\n const nowSeconds = Math.floor(Date.now() / 1000);\n const expiresAt = creds.startTimestamp + creds.durationDays * 86400;\n return nowSeconds < expiresAt;\n}\n\n/** Check if the signed address set covers all required addresses. */\nexport function coversContracts(signedAddresses: Address[], requiredContracts: Address[]): boolean {\n const required = new Set(requiredContracts.map((address) => getAddress(address)));\n const signed = new Set(signedAddresses.map((address) => getAddress(address)));\n return required.isSubsetOf(signed);\n}\n\n/**\n * Check if credentials are valid: not time-expired and covering all required contracts.\n */\nexport function isCredentialValid(\n creds: { startTimestamp: number; durationDays: number; contractAddresses: Address[] },\n requiredContracts: Address[],\n): boolean {\n if (!isTimeValid(creds)) {\n return false;\n }\n return coversContracts(creds.contractAddresses, requiredContracts);\n}\n\n/** Deduplicate and sort a list of addresses by their checksummed form. */\nexport function normalizeAddresses(addresses: Address[]): Address[] {\n return [...new Set(addresses.map((address) => getAddress(address)))].toSorted();\n}\n\n/** Compute a truncated SHA-256 store key from arbitrary identity segments. */\nexport async function computeStoreKey(...segments: (string | number)[]): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(segments.map(String).join(\":\")),\n );\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .slice(0, 32);\n}\n","import type { Hex } from \"viem\";\nimport { assertCondition, assertObject, assertString } from \"../utils\";\nimport type { GenericStorage } from \"./token.types\";\n\n/** Structured session entry stored in session storage. */\nexport interface SessionEntry {\n /** EIP-712 wallet signature authorizing decryption. */\n signature: Hex;\n /** Epoch seconds when the session was created. */\n createdAt: number;\n /** TTL at creation time (not current config). `0` = always expired, `\"infinite\"` = never expires. */\n ttl: number | \"infinite\";\n}\n\n/**\n * Manages session signature entries in a {@link GenericStorage} backend.\n *\n * A session entry caches a wallet signature so that subsequent decrypt\n * operations can reuse it without prompting the user again. The TTL\n * is recorded at creation time so that changing the config later does\n * not retroactively extend or shorten existing sessions.\n */\nexport class SessionSignatures {\n #storage: GenericStorage;\n\n constructor(storage: GenericStorage) {\n this.#storage = storage;\n }\n\n #assertSessionEntry(data: unknown): asserts data is SessionEntry {\n assertObject(data, \"Session entry\");\n assertString(data.signature, \"session.signature\");\n assertCondition(\n typeof data.createdAt === \"number\",\n `Expected session.createdAt to be a number`,\n );\n assertCondition(\n typeof data.ttl === \"number\" || data.ttl === \"infinite\",\n `Expected session.ttl to be a number or \"infinite\"`,\n );\n }\n\n /** Retrieve and validate a session entry, or `null` if none exists. */\n async get(key: string): Promise<SessionEntry | null> {\n const raw = await this.#storage.get<SessionEntry>(key);\n if (raw === null) {\n return null;\n }\n this.#assertSessionEntry(raw);\n return raw;\n }\n\n /** Create and store a session entry with the given TTL. */\n async set(params: { key: string; signature: Hex; ttl: number | \"infinite\" }): Promise<void> {\n const entry: SessionEntry = {\n signature: params.signature,\n createdAt: Math.floor(Date.now() / 1000),\n ttl: params.ttl,\n };\n await this.#storage.set(params.key, entry);\n }\n\n /** Delete a session entry. */\n async delete(key: string): Promise<void> {\n await this.#storage.delete(key);\n }\n\n /**\n * Check if a session entry has expired based on its recorded TTL.\n * - `0` = always expired (every operation triggers a signing prompt).\n * - `\"infinite\"` = never expires.\n * - Positive number = seconds until expiration.\n */\n isExpired(entry: SessionEntry): boolean {\n if (entry.ttl === \"infinite\") {\n return false;\n }\n if (entry.ttl === 0) {\n return true;\n }\n return Math.floor(Date.now() / 1000) - entry.createdAt >= entry.ttl;\n }\n}\n","import type { Address, Hex } from \"viem\";\nimport type { ZamaSDKEventInput, ZamaSDKEventListener } from \"../events/sdk-events\";\nimport { ZamaSDKEvents } from \"../events/sdk-events\";\nimport { CredentialCrypto } from \"./credential-crypto\";\nimport type { BaseEncryptedCredentials } from \"./credential-validation\";\nimport {\n coversContracts,\n isCredentialValid,\n isTimeValid,\n normalizeAddresses,\n} from \"./credential-validation\";\nimport { SigningFailedError, SigningRejectedError, wrapSigningError } from \"./errors\";\nimport { SessionSignatures } from \"./session-signatures\";\nimport type { GenericSigner, GenericStorage, StoredCredentials } from \"./token.types\";\n\n/** Shared configuration accepted by both credential manager variants. */\nexport interface CredentialsConfig {\n /** Backend that generates FHE keypairs (public/private). */\n relayer: { generateKeypair(): Promise<{ publicKey: Hex; privateKey: Hex }> };\n /** Wallet signer used for EIP-712 authorization signatures. */\n signer: GenericSigner;\n /** Persistent storage for encrypted credentials. */\n storage: GenericStorage;\n /** Storage for session signatures (shorter-lived than credentials). */\n sessionStorage: GenericStorage;\n /** FHE keypair lifetime in seconds. Defaults to `86400` (1 day). */\n keypairTTL?: number;\n /** Session signature lifetime. `0` = always re-sign, `\"infinite\"` = never expire. Defaults to `2592000` (30 days). */\n sessionTTL?: number | \"infinite\";\n /** Optional listener for credential lifecycle events. */\n onEvent?: ZamaSDKEventListener;\n}\n\n/** Minimal fields needed to produce an EIP-712 signing request. */\nexport interface SigningMeta {\n /** FHE public key being authorized. */\n publicKey: Hex;\n /** Epoch seconds when the keypair authorization begins. */\n startTimestamp: number;\n /** Number of days the keypair authorization is valid for. */\n durationDays: number;\n /** Delegator address, present only for delegated credentials. */\n delegatorAddress?: string;\n}\n\n/** Options for {@link BaseCredentialsManager.resolveCredentials}. */\ninterface ResolveCredentialsOptions<TCreds> {\n /** Storage key identifying the credential entry. */\n key: string;\n /** Contract addresses the caller needs access to. */\n contracts: Address[];\n /** Deduplication key — concurrent calls with the same key share a single creation promise. */\n createKey: string;\n /** Factory that creates fresh credentials when nothing usable is cached. */\n createFn: () => Promise<TCreds>;\n}\n\n/** Options for {@link BaseCredentialsManager.createCredentials}. */\ninterface CreateCredentialsOptions<TCreds> {\n /** Storage key identifying the credential entry. */\n key: string;\n /** Contract addresses being authorized. */\n contractAddresses: Address[];\n /** Factory that builds the credential payload (keypair + signature). */\n createFn: () => Promise<TCreds>;\n /** Human-readable context included in signing error messages. */\n errorContext: string;\n}\n\n/**\n * Abstract base for credential managers. Contains the entire allow/extend/expire\n * state machine. Subclasses provide the assertion, signing, and encrypt/decrypt\n * hooks that differ between regular and delegated flows.\n *\n * @typeParam TCreds - The in-memory credential shape (includes plaintext privateKey).\n * @typeParam TEncrypted - The on-disk shape (privateKey replaced by encryptedPrivateKey).\n */\nexport abstract class BaseCredentialsManager<\n TCreds extends StoredCredentials,\n TEncrypted extends BaseEncryptedCredentials,\n> {\n protected readonly signer: GenericSigner;\n protected readonly storage: GenericStorage;\n protected readonly sessionSignatures: SessionSignatures;\n protected readonly crypto: CredentialCrypto;\n protected readonly keypairTTL: number;\n protected readonly sessionTTL: number | \"infinite\";\n\n #onEvent: ZamaSDKEventListener;\n #createPromise: Promise<TCreds> | null = null;\n #createPromiseKey: string | null = null;\n #extendPromise: Promise<TCreds> | null = null;\n\n constructor(config: CredentialsConfig) {\n this.signer = config.signer;\n this.storage = config.storage;\n this.sessionSignatures = new SessionSignatures(config.sessionStorage);\n this.crypto = new CredentialCrypto();\n this.keypairTTL = config.keypairTTL ?? 86400;\n this.sessionTTL = config.sessionTTL ?? 2592000;\n this.#onEvent = config.onEvent ?? (() => {});\n\n if (typeof this.keypairTTL === \"number\" && this.keypairTTL < 0) {\n throw new Error(\"keypairTTL must be >= 0\");\n }\n if (typeof this.sessionTTL === \"number\" && this.sessionTTL < 0) {\n throw new Error(\"sessionTTL must be >= 0\");\n }\n }\n\n /** Emit a credential lifecycle event, stamped with the current time. */\n protected emit(partial: ZamaSDKEventInput): void {\n this.#onEvent({ ...partial, timestamp: Date.now() } as never);\n }\n\n // ── Abstract hooks ────────────────────────────────────────────\n\n /** Validate that raw storage data matches the expected encrypted shape. */\n protected abstract assertEncrypted(data: unknown): asserts data is TEncrypted;\n\n /** Sign an EIP-712 authorization for the given contract addresses. */\n protected abstract signForContracts(\n meta: SigningMeta,\n contractAddresses: Address[],\n ): Promise<Hex>;\n\n /** Encrypt credentials for persistent storage. */\n protected abstract encryptCredentials(creds: TCreds): Promise<TEncrypted>;\n\n /** Decrypt credentials from persistent storage. */\n protected abstract decryptCredentials(encrypted: TEncrypted, signature: Hex): Promise<TCreds>;\n\n // ── Core credential resolution ────────────────────────────────\n\n /**\n * The allow() state machine: load → validate → extend/re-sign → or create fresh.\n * Subclasses call this from their public `allow()` after computing the key.\n */\n protected async resolveCredentials({\n key,\n contracts,\n createKey,\n createFn,\n }: ResolveCredentialsOptions<TCreds>): Promise<TCreds> {\n this.emit({ type: ZamaSDKEvents.CredentialsLoading, contractAddresses: contracts });\n\n try {\n const encrypted = await this.storage.get<TEncrypted>(key);\n if (encrypted) {\n this.assertEncrypted(encrypted);\n\n const sessionEntry = await this.sessionSignatures.get(key);\n if (sessionEntry) {\n if (this.sessionSignatures.isExpired(sessionEntry)) {\n await this.sessionSignatures.delete(key);\n this.emit({ type: ZamaSDKEvents.SessionExpired, reason: \"ttl\" });\n } else {\n const creds = await this.decryptCredentials(encrypted, sessionEntry.signature);\n if (isCredentialValid(creds, contracts)) {\n this.emit({ type: ZamaSDKEvents.CredentialsCached, contractAddresses: contracts });\n this.emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses: contracts });\n return creds;\n }\n if (isTimeValid(creds)) {\n return this.#extendContracts({\n key,\n credentials: creds,\n requiredContracts: contracts,\n });\n }\n this.emit({ type: ZamaSDKEvents.CredentialsExpired, contractAddresses: contracts });\n }\n }\n\n // No session or expired — need to re-sign\n if (isTimeValid(encrypted)) {\n if (coversContracts(encrypted.contractAddresses, contracts)) {\n const signature = await this.signForContracts(encrypted, encrypted.contractAddresses);\n await this.sessionSignatures.set({ key, signature, ttl: this.sessionTTL });\n const creds = await this.decryptCredentials(encrypted, signature);\n this.emit({ type: ZamaSDKEvents.CredentialsCached, contractAddresses: contracts });\n this.emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses: contracts });\n return creds;\n }\n // Time-valid but missing contracts — sign with old set to decrypt, then extend\n const oldSignature = await this.signForContracts(encrypted, encrypted.contractAddresses);\n const creds = await this.decryptCredentials(encrypted, oldSignature);\n return this.#extendContracts({\n key,\n credentials: creds,\n requiredContracts: contracts,\n });\n }\n\n this.emit({ type: ZamaSDKEvents.CredentialsExpired, contractAddresses: contracts });\n }\n } catch (error) {\n if (error instanceof SigningRejectedError || error instanceof SigningFailedError) {\n throw error;\n }\n console.warn(\"[zama-sdk] Credential resolution failed, recreating:\", error);\n await this.#deleteCredentials(key);\n }\n\n // Nothing cached — create fresh (deduplicated)\n if (!this.#createPromise || this.#createPromiseKey !== createKey) {\n this.#createPromiseKey = createKey;\n this.#createPromise = createFn()\n .then((creds) => {\n this.emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses: contracts });\n return creds;\n })\n .finally(() => {\n this.#createPromise = null;\n this.#createPromiseKey = null;\n });\n }\n return this.#createPromise;\n }\n\n // ── Shared public method implementations ──────────────────────\n\n /**\n * Check whether stored credentials are expired or don't cover the given contract.\n * Returns `true` if credentials are missing, expired, or corrupted.\n */\n protected async checkExpired(key: string, contractAddress?: Address): Promise<boolean> {\n try {\n const stored = await this.storage.get<TEncrypted>(key);\n if (!stored) {\n return false;\n }\n this.assertEncrypted(stored);\n const requiredContracts = contractAddress ? [contractAddress] : [];\n return !isCredentialValid(stored, requiredContracts);\n } catch (error) {\n console.warn(\"[zama-sdk] isExpired check failed, treating as expired:\", error);\n return true;\n }\n }\n\n /** Delete the session signature and clear caches, forcing a fresh wallet signature on next use. */\n protected async revokeSession(key: string, contractAddresses?: Address[]): Promise<void> {\n await this.sessionSignatures.delete(key);\n this.clearCaches();\n this.emit({\n type: ZamaSDKEvents.CredentialsRevoked,\n ...(contractAddresses ? { contractAddresses } : {}),\n } as ZamaSDKEventInput);\n }\n\n protected async checkAllowed(key: string): Promise<boolean> {\n const entry = await this.sessionSignatures.get(key);\n if (entry === null) {\n return false;\n }\n return !this.sessionSignatures.isExpired(entry);\n }\n\n protected async clearAll(key: string): Promise<void> {\n await this.sessionSignatures.delete(key);\n this.clearCaches();\n await this.#deleteCredentials(key);\n }\n\n /** Override to also clear subclass-specific caches (e.g. key cache). */\n protected clearCaches(): void {\n this.crypto.clearCache();\n }\n\n // ── Credential creation helper ────────────────────────────────\n\n /**\n * Shared wrapper for fresh credential creation:\n * emits events, persists, saves session, and wraps signing errors.\n */\n protected async createCredentials({\n key,\n contractAddresses,\n createFn,\n errorContext,\n }: CreateCredentialsOptions<TCreds>): Promise<TCreds> {\n this.emit({ type: ZamaSDKEvents.CredentialsCreating, contractAddresses });\n try {\n const creds = await createFn();\n await this.persistCredentials(key, creds);\n await this.sessionSignatures.set({\n key,\n signature: creds.signature,\n ttl: this.sessionTTL,\n });\n this.emit({ type: ZamaSDKEvents.CredentialsCreated, contractAddresses });\n return creds;\n } catch (error) {\n wrapSigningError(error, errorContext);\n }\n }\n\n // ── Contract extension ────────────────────────────────────────\n\n async #extendContracts({\n key,\n credentials,\n requiredContracts,\n }: {\n key: string;\n credentials: TCreds;\n requiredContracts: Address[];\n }): Promise<TCreds> {\n if (this.#extendPromise) {\n const previous = await this.#extendPromise;\n if (coversContracts(previous.contractAddresses, requiredContracts)) {\n this.emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses: requiredContracts });\n return previous;\n }\n credentials = previous;\n }\n\n const promise = this.#extendCredentials({ key, credentials, requiredContracts });\n this.#extendPromise = promise;\n try {\n return await promise;\n } finally {\n if (this.#extendPromise === promise) {\n this.#extendPromise = null;\n }\n }\n }\n\n async #extendCredentials({\n key,\n credentials,\n requiredContracts,\n }: {\n key: string;\n credentials: TCreds;\n requiredContracts: Address[];\n }): Promise<TCreds> {\n const merged = normalizeAddresses([...credentials.contractAddresses, ...requiredContracts]);\n const signature = await this.signForContracts(credentials, merged);\n\n const extended: TCreds = { ...credentials, contractAddresses: merged, signature };\n // Persist ciphertext before session signature to prevent a window where a\n // concurrent reader sees the new signature but finds old ciphertext.\n await this.persistCredentials(key, extended);\n await this.sessionSignatures.set({\n key,\n signature,\n ttl: this.sessionTTL,\n });\n this.emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses: requiredContracts });\n return extended;\n }\n\n protected async persistCredentials(key: string, credentials: TCreds): Promise<void> {\n try {\n const encrypted = await this.encryptCredentials(credentials);\n await this.storage.set(key, encrypted);\n } catch (error) {\n console.warn(\"[zama-sdk] Failed to encrypt credentials for persistence:\", error);\n return;\n }\n }\n\n async #deleteCredentials(key: string): Promise<void> {\n try {\n await this.storage.delete(key);\n } catch (error) {\n console.warn(\"[zama-sdk] Failed to delete credentials:\", error);\n }\n }\n}\n","import { getAddress, type Address, type Hex } from \"viem\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { StoredCredentials } from \"./token.types\";\nimport { MemoryStorage } from \"./memory-storage\";\nimport {\n BaseCredentialsManager,\n type CredentialsConfig,\n type SigningMeta,\n} from \"./credential-manager-base\";\nimport {\n type BaseEncryptedCredentials,\n assertBaseEncryptedCredentials,\n normalizeAddresses,\n computeStoreKey,\n} from \"./credential-validation\";\n\n/** Internal storage shape — same as BaseEncryptedCredentials. */\ntype EncryptedCredentials = BaseEncryptedCredentials;\n\n/** Configuration for constructing a {@link CredentialsManager}. */\nexport interface CredentialsManagerConfig extends CredentialsConfig {\n /** FHE relayer backend for keypair generation and EIP-712 creation. */\n relayer: RelayerSDK;\n}\n\nfunction hasExtensionRuntimeId(value: unknown): value is { runtime: { id: string } } {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const runtime = Reflect.get(value, \"runtime\");\n if (typeof runtime !== \"object\" || runtime === null) {\n return false;\n }\n return typeof Reflect.get(runtime, \"id\") === \"string\";\n}\n\n/**\n * Manages FHE decrypt credentials for a single wallet.\n * Handles keypair generation, EIP-712 authorization signing, and\n * encrypted persistence scoped to a (wallet address, chain ID) pair.\n */\nexport class CredentialsManager extends BaseCredentialsManager<\n StoredCredentials,\n EncryptedCredentials\n> {\n #relayer: RelayerSDK;\n #cachedStoreKey: string | null = null;\n #cachedStoreKeyIdentity: string | null = null;\n\n /** Derive the deterministic storage key for a given wallet address and chain. */\n static async computeStoreKey(address: Address, chainId: number): Promise<string> {\n return computeStoreKey(getAddress(address), chainId);\n }\n\n constructor(config: CredentialsManagerConfig) {\n super(config);\n this.#relayer = config.relayer;\n\n // Warn when using in-memory session storage inside a Chrome extension context\n const chromeNamespace =\n typeof globalThis !== \"undefined\" ? Reflect.get(globalThis, \"chrome\") : undefined;\n if (hasExtensionRuntimeId(chromeNamespace) && config.sessionStorage instanceof MemoryStorage) {\n console.warn(\n \"[zama-sdk] Detected Chrome extension context with in-memory session storage. \" +\n \"Session signatures will be lost on service worker restart and won't be shared across contexts. \" +\n \"Consider using chromeSessionStorage instead. \",\n );\n }\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n /**\n * Authorize FHE credentials for one or more contract addresses.\n *\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails for any other reason.\n */\n async allow(...contractAddresses: Address[]): Promise<StoredCredentials> {\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey();\n return this.resolveCredentials({\n key,\n contracts: normalized,\n createKey: normalized.join(\",\"),\n createFn: () => this.create(normalized),\n });\n }\n\n /** Check if stored credentials are expired or unusable. */\n async isExpired(contractAddress?: Address): Promise<boolean> {\n return this.checkExpired(await this.#storeKey(), contractAddress);\n }\n\n /** Revoke the session signature. Next decrypt requires a fresh wallet signature. */\n async revoke(...contractAddresses: Address[]): Promise<void> {\n await this.revokeSession(\n await this.#storeKey(),\n contractAddresses.length > 0 ? contractAddresses : undefined,\n );\n }\n\n /** Whether a session signature is currently cached. */\n async isAllowed(): Promise<boolean> {\n return this.checkAllowed(await this.#storeKey());\n }\n\n /** Delete stored credentials (best-effort). */\n async clear(): Promise<void> {\n await this.clearAll(await this.#storeKey());\n }\n\n /**\n * Generate a fresh FHE keypair, create an EIP-712 authorization, and\n * prompt the user to sign it.\n */\n async create(contractAddresses: Address[]): Promise<StoredCredentials> {\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey();\n return this.createCredentials({\n key,\n contractAddresses: normalized,\n createFn: async () => {\n const keypair = await this.#relayer.generateKeypair();\n const startTimestamp = Math.floor(Date.now() / 1000);\n const durationDays = Math.ceil(this.keypairTTL / 86400);\n\n const eip712 = await this.#relayer.createEIP712(\n keypair.publicKey,\n normalized,\n startTimestamp,\n durationDays,\n );\n const signature = await this.signer.signTypedData(eip712);\n\n return {\n publicKey: keypair.publicKey,\n privateKey: keypair.privateKey,\n signature,\n contractAddresses: normalized,\n startTimestamp,\n durationDays,\n };\n },\n errorContext: \"Failed to create decrypt credentials\",\n });\n }\n\n // ── Abstract implementations ──────────────────────────────────\n\n protected assertEncrypted(data: unknown): asserts data is EncryptedCredentials {\n assertBaseEncryptedCredentials(data);\n }\n\n protected async signForContracts(meta: SigningMeta, contractAddresses: Address[]): Promise<Hex> {\n const eip712 = await this.#relayer.createEIP712(\n meta.publicKey,\n contractAddresses,\n meta.startTimestamp,\n meta.durationDays,\n );\n return this.signer.signTypedData(eip712);\n }\n\n protected async encryptCredentials(creds: StoredCredentials): Promise<EncryptedCredentials> {\n const address = await this.signer.getAddress();\n const encryptedPrivateKey = await this.crypto.encrypt(\n creds.privateKey,\n creds.signature,\n address,\n );\n const { privateKey: _, signature: _sig, ...rest } = creds;\n return { ...rest, encryptedPrivateKey };\n }\n\n protected async decryptCredentials(\n encrypted: EncryptedCredentials,\n signature: Hex,\n ): Promise<StoredCredentials> {\n const address = await this.signer.getAddress();\n const privateKey = await this.crypto.decrypt(encrypted.encryptedPrivateKey, signature, address);\n const { encryptedPrivateKey: _, ...rest } = encrypted;\n return { ...rest, privateKey, signature };\n }\n\n protected override clearCaches(): void {\n this.#cachedStoreKey = null;\n this.#cachedStoreKeyIdentity = null;\n super.clearCaches();\n }\n\n // ── Store key ─────────────────────────────────────────────────\n\n async #storeKey(): Promise<string> {\n const address = await this.signer.getAddress();\n const chainId = await this.signer.getChainId();\n const identity = `${getAddress(address)}:${chainId}`;\n if (this.#cachedStoreKey && this.#cachedStoreKeyIdentity === identity) {\n return this.#cachedStoreKey;\n }\n const key = await CredentialsManager.computeStoreKey(address, chainId);\n this.#cachedStoreKeyIdentity = identity;\n this.#cachedStoreKey = key;\n return key;\n }\n}\n","import { getAddress, type Address, type Hex } from \"viem\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { DelegatedStoredCredentials } from \"./token.types\";\nimport {\n BaseCredentialsManager,\n type CredentialsConfig,\n type SigningMeta,\n} from \"./credential-manager-base\";\nimport {\n type BaseEncryptedCredentials,\n assertDelegatedFields,\n normalizeAddresses,\n computeStoreKey,\n} from \"./credential-validation\";\n\n/** Internal storage shape — base fields plus delegator/delegate addresses. */\ninterface EncryptedCredentials extends BaseEncryptedCredentials {\n delegatorAddress: Address;\n delegateAddress: Address;\n}\n\n/** Configuration for constructing a {@link DelegatedCredentialsManager}. */\nexport interface DelegatedCredentialsManagerConfig extends CredentialsConfig {\n /** FHE relayer backend for keypair generation and delegated EIP-712 creation. */\n relayer: RelayerSDK;\n}\n\n/** Signing metadata for delegated credentials, adding the delegator's address. */\nexport interface DelegatedSigningMeta extends SigningMeta {\n /** On-chain address of the account that delegated decryption rights. */\n delegatorAddress: Address;\n}\n\n/**\n * Manages FHE decrypt credentials for delegated decryption.\n * Scoped to a (delegate, delegator) pair.\n */\nexport class DelegatedCredentialsManager extends BaseCredentialsManager<\n DelegatedStoredCredentials,\n EncryptedCredentials\n> {\n #relayer: RelayerSDK;\n #cachedStoreKey: string | null = null;\n #cachedStoreKeyIdentity: string | null = null;\n\n /** Derive the deterministic storage key for a (delegate, delegator, chain) triple. */\n static async computeStoreKey(\n delegateAddress: Address,\n delegatorAddress: Address,\n chainId: number,\n ): Promise<string> {\n return computeStoreKey(getAddress(delegateAddress), getAddress(delegatorAddress), chainId);\n }\n\n constructor(config: DelegatedCredentialsManagerConfig) {\n super(config);\n this.#relayer = config.relayer;\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n /**\n * Authorize FHE delegated credentials for one or more contract addresses.\n *\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails for any other reason.\n */\n async allow(\n delegatorAddress: Address,\n ...contractAddresses: Address[]\n ): Promise<DelegatedStoredCredentials> {\n const normalizedDelegator = getAddress(delegatorAddress);\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey(normalizedDelegator);\n return this.resolveCredentials({\n key,\n contracts: normalized,\n createKey: `${normalizedDelegator}:${normalized.join(\",\")}`,\n createFn: () => this.#create(normalizedDelegator, normalized),\n });\n }\n\n /** Check if stored delegated credentials are expired or unusable. */\n async isExpired(delegatorAddress: Address, contractAddress?: Address): Promise<boolean> {\n return this.checkExpired(await this.#storeKey(getAddress(delegatorAddress)), contractAddress);\n }\n\n /** Revoke the session signature for a delegator. */\n async revoke(delegatorAddress: Address): Promise<void> {\n await this.revokeSession(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n /** Whether a session signature is currently cached for a delegator. */\n async isAllowed(delegatorAddress: Address): Promise<boolean> {\n return this.checkAllowed(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n /** Delete stored credentials for a delegator (best-effort). */\n async clear(delegatorAddress: Address): Promise<void> {\n await this.clearAll(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n // ── Credential creation ───────────────────────────────────────\n\n async #create(\n delegatorAddress: Address,\n contractAddresses: Address[],\n ): Promise<DelegatedStoredCredentials> {\n const key = await this.#storeKey(delegatorAddress);\n return this.createCredentials({\n key,\n contractAddresses,\n createFn: async () => {\n const keypair = await this.#relayer.generateKeypair();\n const delegateAddress = await this.signer.getAddress();\n const startTimestamp = Math.floor(Date.now() / 1000);\n const durationDays = Math.ceil(this.keypairTTL / 86400);\n\n const meta = {\n publicKey: keypair.publicKey,\n startTimestamp,\n durationDays,\n delegatorAddress,\n };\n const signature = await this.#signDelegated(meta, contractAddresses);\n\n return {\n publicKey: keypair.publicKey,\n privateKey: keypair.privateKey,\n signature,\n contractAddresses,\n startTimestamp,\n durationDays,\n delegatorAddress,\n delegateAddress,\n };\n },\n errorContext: \"Failed to create delegated decrypt credentials\",\n });\n }\n\n // ── Abstract implementations ──────────────────────────────────\n\n protected assertEncrypted(data: unknown): asserts data is EncryptedCredentials {\n assertDelegatedFields(data);\n }\n\n protected async signForContracts(\n meta: DelegatedSigningMeta,\n contractAddresses: Address[],\n ): Promise<Hex> {\n return this.#signDelegated(meta, contractAddresses);\n }\n\n protected async encryptCredentials(\n creds: DelegatedStoredCredentials,\n ): Promise<EncryptedCredentials> {\n const address = await this.signer.getAddress();\n const encryptedPrivateKey = await this.crypto.encrypt(\n creds.privateKey,\n creds.signature,\n address,\n );\n const { privateKey: _, signature: _sig, ...rest } = creds;\n return { ...rest, encryptedPrivateKey };\n }\n\n protected async decryptCredentials(\n encrypted: EncryptedCredentials,\n signature: Hex,\n ): Promise<DelegatedStoredCredentials> {\n const address = await this.signer.getAddress();\n const privateKey = await this.crypto.decrypt(encrypted.encryptedPrivateKey, signature, address);\n const { encryptedPrivateKey: _, ...rest } = encrypted;\n return { ...rest, privateKey, signature };\n }\n\n protected override clearCaches(): void {\n this.#cachedStoreKey = null;\n this.#cachedStoreKeyIdentity = null;\n super.clearCaches();\n }\n\n // ── Private helpers ───────────────────────────────────────────\n\n async #storeKey(delegatorAddress: Address): Promise<string> {\n const delegateAddress = await this.signer.getAddress();\n const chainId = await this.signer.getChainId();\n const identity = `${getAddress(delegateAddress)}:${getAddress(delegatorAddress)}:${chainId}`;\n if (this.#cachedStoreKey && this.#cachedStoreKeyIdentity === identity) {\n return this.#cachedStoreKey;\n }\n const key = await DelegatedCredentialsManager.computeStoreKey(\n delegateAddress,\n delegatorAddress,\n chainId,\n );\n this.#cachedStoreKeyIdentity = identity;\n this.#cachedStoreKey = key;\n return key;\n }\n\n async #signDelegated(meta: DelegatedSigningMeta, contractAddresses: Address[]): Promise<Hex> {\n const delegatedEIP712 = await this.#relayer.createDelegatedUserDecryptEIP712(\n meta.publicKey,\n contractAddresses,\n meta.delegatorAddress,\n meta.startTimestamp,\n meta.durationDays,\n );\n return this.signer.signTypedData({\n domain: {\n ...delegatedEIP712.domain,\n chainId: Number(delegatedEIP712.domain.chainId),\n },\n types: delegatedEIP712.types,\n message: {\n ...delegatedEIP712.message,\n startTimestamp: BigInt(delegatedEIP712.message.startTimestamp),\n durationDays: BigInt(delegatedEIP712.message.durationDays),\n },\n });\n }\n}\n","import { getAddress, type Address } from \"viem\";\nimport type { GenericStorage } from \"./token.types\";\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\n\nconst BALANCES_KEY = \"zama:balances\";\n\nexport interface BalanceCachePayload {\n storage: GenericStorage;\n tokenAddress: Address;\n owner: Address;\n handle: Handle;\n}\n\n/**\n * Build a storage key for a cached decrypted balance.\n * The handle is embedded in the key so a new on-chain handle automatically\n * invalidates the cache entry — no TTL needed.\n */\nfunction storageKey(tokenAddress: Address, owner: Address, handle: Handle): string {\n return `zama:balance:${getAddress(tokenAddress)}:${getAddress(owner)}:${handle.toLowerCase()}`;\n}\n\n/**\n * Load a cached decrypted balance, or `null` on cache miss.\n */\nexport async function loadCachedBalance({\n storage,\n tokenAddress,\n owner,\n handle,\n}: BalanceCachePayload): Promise<bigint | null> {\n try {\n const raw = await storage.get<string>(storageKey(tokenAddress, owner, handle));\n return raw !== null ? BigInt(raw) : null;\n } catch (error) {\n console.warn(\"[zama-sdk] Balance cache read failed:\", error);\n return null;\n }\n}\n\n/**\n * Persist a decrypted balance to storage.\n */\nexport async function saveCachedBalance(\n payload: BalanceCachePayload & { value: bigint },\n): Promise<void> {\n const { storage, tokenAddress, owner, handle, value } = payload;\n const key = storageKey(tokenAddress, owner, handle);\n try {\n await storage.set(key, value.toString());\n await trackKey(storage, key);\n } catch {\n // Best-effort — never block the caller.\n }\n}\n\nconst trackKeyChains = new WeakMap<GenericStorage, Promise<void>>();\n\nasync function trackKey(storage: GenericStorage, key: string): Promise<void> {\n // Serialize read-modify-write per storage instance to prevent concurrent\n // saveCachedBalance calls from overwriting each other's key additions.\n const prev = trackKeyChains.get(storage) ?? Promise.resolve();\n const next = prev.then(async () => {\n const raw = await storage.get<string>(BALANCES_KEY);\n const keys: string[] = raw ? JSON.parse(raw) : [];\n if (!keys.includes(key)) {\n keys.push(key);\n await storage.set(BALANCES_KEY, JSON.stringify(keys));\n }\n });\n trackKeyChains.set(\n storage,\n next.catch(() => {}),\n ); // prevent chain poisoning\n return next;\n}\n\n/**\n * Remove all cached decrypted balances from storage.\n * Best-effort — never throws.\n */\nexport async function clearAllCachedBalances(storage: GenericStorage): Promise<void> {\n try {\n const raw = await storage.get<string>(BALANCES_KEY);\n if (!raw) {\n return;\n }\n const keys: string[] = JSON.parse(raw);\n await Promise.all(keys.map((key) => storage.delete(key)));\n await storage.delete(BALANCES_KEY);\n } catch {\n // Best-effort — never block the caller.\n }\n}\n","import { type Address, getAddress } from \"viem\";\nimport {\n ERC7984_INTERFACE_ID,\n ERC7984_WRAPPER_INTERFACE_ID,\n MAX_UINT64,\n allowanceContract,\n confidentialBalanceOfContract,\n decimalsContract,\n getDelegationExpiryContract,\n getWrapperContract,\n nameContract,\n supportsInterfaceContract,\n symbolContract,\n underlyingContract,\n wrapperExistsContract,\n} from \"../contracts\";\nimport type { ZamaSDKEventInput, ZamaSDKEventListener } from \"../events/sdk-events\";\nimport { ZamaSDKEvents } from \"../events/sdk-events\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\nimport { pLimit, toError } from \"../utils\";\nimport { loadCachedBalance, saveCachedBalance } from \"./balance-cache\";\nimport { CredentialsManager } from \"./credentials-manager\";\nimport { DelegatedCredentialsManager } from \"./delegated-credentials-manager\";\nimport {\n ConfigurationError,\n DecryptionFailedError,\n DelegationExpiredError,\n DelegationNotFoundError,\n NoCiphertextError,\n RelayerRequestFailedError,\n SigningFailedError,\n SigningRejectedError,\n} from \"./errors\";\nimport type { GenericSigner, GenericStorage } from \"./token.types\";\n\n/** 32-byte zero handle, used to detect uninitialized encrypted balances. */\nexport const ZERO_HANDLE =\n \"0x0000000000000000000000000000000000000000000000000000000000000000\" as const;\n\n/** Options for {@link ReadonlyToken.batchDecryptBalances}. */\nexport interface BatchDecryptOptions {\n /** Pre-fetched encrypted handles. When omitted, handles are fetched from the chain. */\n handles?: Handle[];\n /** Balance owner address. Defaults to the connected signer. */\n owner?: Address;\n /**\n * Called when decryption fails for a single token. Return a fallback bigint value.\n * When omitted, errors are collected and thrown as an aggregated DecryptionFailedError.\n *\n * @example\n * ```ts\n * // Silent zero (old behavior):\n * onError: () => 0n\n * // Log and use zero:\n * onError: (err, addr) => { console.error(addr, err); return 0n; }\n * ```\n */\n onError?: (error: Error, address: Address) => bigint;\n /** Maximum number of concurrent decrypt calls. Default: `Infinity` (no limit). */\n maxConcurrency?: number;\n}\n\n/** Options for {@link ReadonlyToken.batchDecryptBalancesAs}. */\nexport interface BatchDecryptAsOptions {\n /** The address of the account that delegated decryption rights. */\n delegatorAddress: Address;\n /** Pre-fetched encrypted handles. When omitted, handles are fetched from the chain. */\n handles?: Handle[];\n /** Balance owner address. Defaults to the delegator address. */\n owner?: Address;\n /** Maximum number of concurrent decrypt calls. Default: Infinity. */\n maxConcurrency?: number;\n /** Called when decryption fails for a single token. Return a fallback bigint. */\n onError?: (error: Error, address: Address) => bigint;\n}\n\n/** Configuration for constructing a {@link ReadonlyToken}. */\nexport interface ReadonlyTokenConfig {\n /** FHE relayer backend. */\n relayer: RelayerSDK;\n /** Wallet signer for read calls and credential signing. */\n signer: GenericSigner;\n /** Credential storage backend. */\n storage: GenericStorage;\n /** Session storage for wallet signatures. Shared across all tokens in the same SDK instance. */\n sessionStorage: GenericStorage;\n /** Shared CredentialsManager instance. When provided, storage/sessionStorage/keypairTTL/onEvent are ignored for credential creation. */\n credentials?: CredentialsManager;\n /** Shared DelegatedCredentialsManager instance. When provided, storage/sessionStorage/keypairTTL/onEvent are ignored for delegated credential creation. */\n delegatedCredentials?: DelegatedCredentialsManager;\n /** Address of the confidential token contract. */\n address: Address;\n /** How long the re-encryption keypair remains valid, in seconds. Default: `86400` (1 day). */\n keypairTTL?: number;\n /** Controls session signature lifetime in seconds. Default: `2592000` (30 days). `0` means every operation triggers a signing prompt. `\"infinite\"` means the session never expires. */\n sessionTTL?: number | \"infinite\";\n /** Optional structured event listener for debugging and telemetry. */\n onEvent?: ZamaSDKEventListener;\n}\n\n/**\n * Read-only interface for a confidential token.\n * Supports balance queries, authorization, and ERC-165 checks.\n * Does not require a wrapper address.\n */\nexport class ReadonlyToken {\n protected readonly credentials: CredentialsManager;\n protected readonly delegatedCredentials: DelegatedCredentialsManager;\n protected readonly relayer: RelayerSDK;\n readonly signer: GenericSigner;\n readonly address: Address;\n readonly storage: GenericStorage;\n readonly #onEvent: ZamaSDKEventListener | undefined;\n\n constructor(config: ReadonlyTokenConfig) {\n const credentialsConfig = {\n relayer: config.relayer,\n signer: config.signer,\n storage: config.storage,\n sessionStorage: config.sessionStorage,\n keypairTTL: config.keypairTTL ?? 86400,\n sessionTTL: config.sessionTTL ?? 2592000,\n onEvent: config.onEvent,\n };\n this.credentials = config.credentials ?? new CredentialsManager(credentialsConfig);\n this.delegatedCredentials =\n config.delegatedCredentials ?? new DelegatedCredentialsManager(credentialsConfig);\n this.relayer = config.relayer;\n this.signer = config.signer;\n this.address = getAddress(config.address);\n this.storage = config.storage;\n this.#onEvent = config.onEvent;\n }\n\n /** Emit a structured event (no-op when no listener is registered). */\n protected emit(partial: ZamaSDKEventInput): void {\n this.#onEvent?.({\n ...partial,\n tokenAddress: this.address,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Decrypt and return the plaintext balance for the given owner.\n * Generates FHE credentials automatically if they don't exist.\n *\n * @param owner - Optional balance owner address. Defaults to the connected signer.\n * @returns The decrypted plaintext balance as a bigint.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n *\n * @example\n * ```ts\n * const balance = await token.balanceOf();\n * // or for another address:\n * const balance = await token.balanceOf(\"0xOwner\");\n * ```\n */\n async balanceOf(owner?: Address): Promise<bigint> {\n const ownerAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n const handle = await this.readConfidentialBalanceOf(ownerAddress);\n return this.decryptBalance(handle, ownerAddress);\n }\n\n /**\n * Return the raw encrypted balance handle without decrypting.\n *\n * @param owner - Optional balance owner address. Defaults to the connected signer.\n * @returns The encrypted balance handle as a hex string.\n *\n * @example\n * ```ts\n * const handle = await token.confidentialBalanceOf();\n * ```\n */\n async confidentialBalanceOf(owner?: Address): Promise<Handle> {\n const ownerAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n return this.readConfidentialBalanceOf(ownerAddress);\n }\n\n /**\n * ERC-165 check for {@link ERC7984_INTERFACE_ID} support.\n *\n * @returns `true` if the contract implements the ERC-7984 confidential token interface.\n *\n * @example\n * ```ts\n * if (await token.isConfidential()) {\n * // Token supports encrypted operations\n * }\n * ```\n */\n async isConfidential(): Promise<boolean> {\n const result = await this.signer.readContract(\n supportsInterfaceContract(this.address, ERC7984_INTERFACE_ID),\n );\n return result;\n }\n\n /**\n * ERC-165 check for {@link ERC7984_WRAPPER_INTERFACE_ID} support.\n *\n * @returns `true` if the contract implements the ERC-7984 wrapper interface.\n *\n * @example\n * ```ts\n * if (await token.isWrapper()) {\n * // Token is a confidential wrapper\n * }\n * ```\n */\n async isWrapper(): Promise<boolean> {\n const result = await this.signer.readContract(\n supportsInterfaceContract(this.address, ERC7984_WRAPPER_INTERFACE_ID),\n );\n return result;\n }\n\n /**\n * Decrypt multiple token balances in parallel.\n * When `handles` are provided, decrypts them directly (useful for two-phase\n * polling where handles are already known). When omitted, fetches handles\n * from the chain first.\n *\n * **Error handling:** If a per-token decryption fails and no `onError` callback\n * is provided, errors are collected and thrown as an aggregated\n * `DecryptionFailedError`. When the relayer returns no value for a handle,\n * a `DecryptionFailedError` is thrown for that token (never silently returns `0n`).\n * Pass `onError: () => 0n` to opt into the silent zero behavior.\n *\n * @param tokens - Array of ReadonlyToken instances to decrypt balances for.\n * @param options - Optional configuration for handles, owner, error handling, and concurrency.\n * @returns A Map from token address to decrypted balance.\n * @throws {@link DecryptionFailedError} if any decryption fails and no `onError` callback is provided.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n *\n * @example\n * ```ts\n * // Simple one-shot:\n * const balances = await ReadonlyToken.batchDecryptBalances(tokens);\n *\n * // With pre-fetched handles and error callback:\n * const handles = await Promise.all(tokens.map(t => t.confidentialBalanceOf()));\n * const balances = await ReadonlyToken.batchDecryptBalances(tokens, {\n * handles,\n * onError: (err, addr) => { console.error(addr, err); return 0n; },\n * });\n * ```\n */\n static async batchDecryptBalances(\n tokens: ReadonlyToken[],\n options?: BatchDecryptOptions,\n ): Promise<Map<Address, bigint>> {\n if (tokens.length === 0) {\n return new Map();\n }\n\n const { handles, owner, onError, maxConcurrency } = options ?? {};\n const firstToken = tokens[0]!;\n const sdk = ReadonlyToken.assertSameRelayer(tokens);\n const signerAddress = owner ?? (await firstToken.signer.getAddress());\n\n return ReadonlyToken.#batchDecryptCore({\n tokens,\n handles,\n ownerAddress: signerAddress,\n onError,\n maxConcurrency,\n obtainCreds: (uncachedAddresses) => firstToken.credentials.allow(...uncachedAddresses),\n decrypt: (creds, handle, contractAddress) =>\n sdk.userDecrypt({\n handles: [handle],\n contractAddress,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n }),\n errorPrefix: \"Batch decryption\",\n });\n }\n\n /**\n * Batch decrypt confidential balances as a delegate across multiple tokens.\n * Mirrors {@link batchDecryptBalances} but uses delegated credentials.\n *\n * **Error handling:** If a per-token decryption fails and no `onError` callback\n * is provided, errors are collected and thrown as an aggregated\n * `DecryptionFailedError`. When the relayer returns no value for a handle,\n * a `DecryptionFailedError` is thrown for that token (never silently returns `0n`).\n * Pass `onError: () => 0n` to opt into the silent zero behavior.\n *\n * @param tokens - Array of ReadonlyToken instances to decrypt balances for.\n * @param options - Delegated decryption configuration.\n * @returns A Map from token address to decrypted balance.\n * @throws {@link DelegationNotFoundError} if no active delegation exists from the delegator to the connected signer.\n * @throws {@link DelegationExpiredError} if the delegation has expired.\n * @throws {@link DecryptionFailedError} if any decryption fails and no `onError` callback is provided.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n *\n * @example\n * ```ts\n * const balances = await ReadonlyToken.batchDecryptBalancesAs(tokens, {\n * delegatorAddress: \"0xDelegator\",\n * onError: (err, addr) => { console.error(addr, err); return 0n; },\n * });\n * ```\n */\n static async batchDecryptBalancesAs(\n tokens: ReadonlyToken[],\n options: BatchDecryptAsOptions,\n ): Promise<Map<Address, bigint>> {\n if (tokens.length === 0) {\n return new Map();\n }\n\n const { delegatorAddress, handles, owner, onError, maxConcurrency } = options;\n const ownerAddress = owner ?? delegatorAddress;\n const firstToken = tokens[0]!;\n ReadonlyToken.assertSameRelayer(tokens);\n\n return ReadonlyToken.#batchDecryptCore({\n tokens,\n handles,\n ownerAddress,\n onError,\n maxConcurrency,\n preFlightCheck: () => firstToken.#assertDelegationActive(delegatorAddress),\n obtainCreds: (uncachedAddresses) =>\n firstToken.delegatedCredentials.allow(delegatorAddress, ...uncachedAddresses),\n decrypt: (creds, handle, contractAddress) =>\n firstToken.relayer.delegatedUserDecrypt({\n handles: [handle],\n contractAddress,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n delegatorAddress: creds.delegatorAddress,\n delegateAddress: creds.delegateAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n }),\n errorPrefix: \"Batch delegated decryption\",\n });\n }\n\n static async #batchDecryptCore<TCreds>(config: {\n tokens: ReadonlyToken[];\n handles: Handle[] | undefined;\n ownerAddress: Address;\n onError?: (error: Error, address: Address) => bigint;\n maxConcurrency?: number;\n preFlightCheck?: () => Promise<void>;\n obtainCreds: (uncachedAddresses: Address[]) => Promise<TCreds>;\n decrypt: (\n creds: TCreds,\n handle: Address,\n contractAddress: Address,\n ) => Promise<Record<string, unknown>>;\n errorPrefix: string;\n }): Promise<Map<Address, bigint>> {\n const {\n tokens,\n handles,\n ownerAddress,\n onError,\n maxConcurrency,\n obtainCreds,\n decrypt,\n errorPrefix,\n } = config;\n\n const firstToken = tokens[0]!;\n const resolvedHandles =\n handles ?? (await Promise.all(tokens.map((t) => t.readConfidentialBalanceOf(ownerAddress))));\n\n if (tokens.length !== resolvedHandles.length) {\n throw new DecryptionFailedError(\n `tokens.length (${tokens.length}) must equal handles.length (${resolvedHandles.length})`,\n );\n }\n\n const tokenStorage = firstToken.storage;\n const results = new Map<Address, bigint>();\n\n // Parallel cache lookups — avoids sequential IDB round-trips.\n const uncached: { token: ReadonlyToken; handle: Address }[] = [];\n const cachedValues = await Promise.all(\n tokens.map((token, i) => {\n const handle = resolvedHandles[i]!;\n if (token.isZeroHandle(handle)) {\n return 0n;\n }\n return loadCachedBalance({\n storage: tokenStorage,\n tokenAddress: token.address,\n owner: ownerAddress,\n handle,\n });\n }),\n );\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i]!;\n const handle = resolvedHandles[i]!;\n const cached = cachedValues[i];\n\n if (cached !== null && cached !== undefined) {\n results.set(token.address, cached);\n continue;\n }\n\n uncached.push({ token, handle });\n }\n\n // All balances resolved from cache — no credentials needed.\n if (uncached.length === 0) {\n return results;\n }\n\n // Pre-flight check runs after cache lookups — skips RPC overhead when\n // all balances are cached. Best-effort: checks the first token's contract\n // only (delegations are typically granted per-delegator, not per-token).\n if (config.preFlightCheck) {\n await config.preFlightCheck();\n }\n\n const uncachedAddresses = uncached.map((entry) => entry.token.address);\n const creds = await obtainCreds(uncachedAddresses);\n\n const errors: { address: Address; error: Error }[] = [];\n const decryptFns: (() => Promise<void>)[] = [];\n\n for (const { token, handle } of uncached) {\n decryptFns.push(() =>\n decrypt(creds, handle, token.address)\n .then(async (result) => {\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(\n `${errorPrefix} returned no value for handle ${handle} on token ${token.address}`,\n );\n }\n results.set(token.address, value);\n try {\n await saveCachedBalance({\n storage: tokenStorage,\n tokenAddress: token.address,\n owner: ownerAddress,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n })\n .catch((error) => {\n const err = toError(error);\n if (onError) {\n try {\n results.set(token.address, onError(err, token.address));\n } catch (callbackError) {\n errors.push({\n address: token.address,\n error: toError(callbackError),\n });\n }\n } else {\n errors.push({ address: token.address, error: err });\n }\n }),\n );\n }\n\n await pLimit(decryptFns, maxConcurrency);\n\n if (errors.length > 0) {\n const message = errors.map((e) => `${e.address}: ${e.error.message}`).join(\"; \");\n throw new DecryptionFailedError(\n `${errorPrefix} failed for ${errors.length} token(s): ${message}`,\n );\n }\n\n return results;\n }\n\n /**\n * Look up the wrapper contract for this token via the deployment coordinator.\n * Returns `null` if no wrapper is deployed.\n *\n * @param coordinatorAddress - The deployment coordinator contract address.\n * @returns The wrapper address, or `null` if no wrapper exists.\n *\n * @example\n * ```ts\n * const wrapper = await token.discoverWrapper(\"0xCoordinator\");\n * if (wrapper) {\n * const fullToken = sdk.createToken(token.address, wrapper);\n * }\n * ```\n */\n async discoverWrapper(coordinatorAddress: Address): Promise<Address | null> {\n const coordinator = getAddress(coordinatorAddress);\n const exists = await this.signer.readContract(wrapperExistsContract(coordinator, this.address));\n if (!exists) {\n return null;\n }\n return this.signer.readContract(getWrapperContract(coordinator, this.address));\n }\n\n /**\n * Read the underlying ERC-20 address from this token's wrapper contract.\n *\n * @returns The underlying ERC-20 token address.\n *\n * @example\n * ```ts\n * const underlying = await token.underlyingToken();\n * ```\n */\n async underlyingToken(): Promise<Address> {\n return this.signer.readContract(underlyingContract(this.address));\n }\n\n /**\n * Read the ERC-20 allowance of the underlying token for a given wrapper.\n *\n * @param wrapper - The wrapper contract address to check allowance for.\n * @param owner - Optional owner address. Defaults to the connected signer.\n * @returns The current allowance as a bigint.\n *\n * @example\n * ```ts\n * const allowance = await token.allowance(\"0xWrapper\");\n * ```\n */\n async allowance(wrapper: Address, owner?: Address): Promise<bigint> {\n const normalizedWrapper = getAddress(wrapper);\n const underlying = await this.signer.readContract(underlyingContract(normalizedWrapper));\n const userAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n return this.signer.readContract(allowanceContract(underlying, userAddress, normalizedWrapper));\n }\n\n /**\n * Read the token name from the contract.\n *\n * @returns The token name string.\n *\n * @example\n * ```ts\n * const name = await token.name(); // \"Wrapped USDC\"\n * ```\n */\n async name(): Promise<string> {\n return this.signer.readContract(nameContract(this.address));\n }\n\n /**\n * Read the token symbol from the contract.\n *\n * @returns The token symbol string.\n *\n * @example\n * ```ts\n * const symbol = await token.symbol(); // \"cUSDC\"\n * ```\n */\n async symbol(): Promise<string> {\n return this.signer.readContract(symbolContract(this.address));\n }\n\n /**\n * Read the token decimals from the contract.\n *\n * @returns The number of decimals.\n *\n * @example\n * ```ts\n * const decimals = await token.decimals(); // 6\n * ```\n */\n async decimals(): Promise<number> {\n return this.signer.readContract(decimalsContract(this.address));\n }\n\n /**\n * Ensure FHE decrypt credentials exist for this token.\n * Generates a keypair and requests an EIP-712 signature if needed.\n * Call this before any decrypt operation to avoid mid-flow wallet prompts.\n *\n * @returns Resolves when credentials are cached.\n *\n * @example\n * ```ts\n * await token.allow();\n * // Credentials are now cached — subsequent decrypts won't prompt\n * const balance = await token.balanceOf();\n * ```\n */\n async allow(): Promise<void> {\n await this.credentials.allow(this.address);\n }\n\n /**\n * Whether a session signature is currently cached for the connected wallet.\n * Use this to check if decrypt operations can proceed without a wallet prompt.\n */\n async isAllowed(): Promise<boolean> {\n return this.credentials.isAllowed();\n }\n\n /**\n * Revoke the session signature for the connected wallet.\n * Stored credentials remain intact, but the next decrypt operation\n * will require a fresh wallet signature.\n */\n async revoke(...contractAddresses: Address[]): Promise<void> {\n await this.credentials.revoke(...contractAddresses);\n }\n\n /**\n * Ensure FHE decrypt credentials exist for all given tokens in a single\n * wallet signature. Call this early (e.g. after loading the token list) so\n * that subsequent individual decrypt operations reuse cached credentials.\n *\n * @param tokens - Array of ReadonlyToken instances to allow.\n * @returns Resolves when all credentials are cached.\n *\n * @example\n * ```ts\n * const tokens = addresses.map(a => sdk.createReadonlyToken(a));\n * await ReadonlyToken.allow(...tokens);\n * // All tokens now share the same credentials\n * ```\n */\n static async allow(...tokens: ReadonlyToken[]): Promise<void> {\n if (tokens.length === 0) {\n return;\n }\n const allAddresses = tokens.map((t) => t.address);\n await tokens[0]!.credentials.allow(...allAddresses);\n }\n\n protected async getAclAddress(): Promise<Address> {\n return this.relayer.getAclAddress();\n }\n\n /**\n * Check whether a delegation is active for this token's contract address.\n *\n * @param delegatorAddress - The address that granted the delegation.\n * @param delegateAddress - The address that received delegation rights.\n * @returns `true` if the delegation exists and has not expired.\n */\n async isDelegated(params: {\n delegatorAddress: Address;\n delegateAddress: Address;\n }): Promise<boolean> {\n const expiry = await this.getDelegationExpiry(params);\n if (expiry === 0n) {\n return false;\n }\n // Permanent delegation (uint64 max) — skip the RPC round-trip for block timestamp.\n if (expiry === MAX_UINT64) {\n return true;\n }\n const now = await this.signer.getBlockTimestamp();\n return expiry > now;\n }\n\n /**\n * Get the expiration timestamp of a delegation for this token.\n *\n * @param delegatorAddress - The address that granted the delegation.\n * @param delegateAddress - The address that received delegation rights.\n * @returns Unix timestamp as bigint. `0n` = no delegation. `2^64 - 1` = permanent.\n */\n async getDelegationExpiry({\n delegatorAddress,\n delegateAddress,\n }: {\n delegatorAddress: Address;\n delegateAddress: Address;\n }): Promise<bigint> {\n const acl = await this.getAclAddress();\n return this.signer.readContract(\n getDelegationExpiryContract(\n acl,\n getAddress(delegatorAddress),\n getAddress(delegateAddress),\n this.address,\n ),\n );\n }\n\n /**\n * Throws if there is no active delegation from `delegatorAddress` to the\n * connected signer for this token contract.\n */\n async #assertDelegationActive(delegatorAddress: Address): Promise<void> {\n const delegateAddress = await this.signer.getAddress();\n const expiry = await this.getDelegationExpiry({\n delegatorAddress,\n delegateAddress,\n });\n if (expiry === 0n) {\n throw new DelegationNotFoundError(\n `No active delegation from ${delegatorAddress} to ${delegateAddress} for ${this.address}`,\n );\n }\n if (expiry !== MAX_UINT64) {\n const now = await this.signer.getBlockTimestamp();\n if (expiry <= now) {\n throw new DelegationExpiredError(\n `Delegation from ${delegatorAddress} to ${delegateAddress} for ${this.address} has expired`,\n );\n }\n }\n }\n\n protected async readConfidentialBalanceOf(owner: Address): Promise<Handle> {\n return (await this.signer.readContract(\n confidentialBalanceOfContract(this.address, owner),\n )) as Handle;\n }\n\n isZeroHandle(handle: string): handle is typeof ZERO_HANDLE | `0x` {\n return handle === ZERO_HANDLE || handle === \"0x\";\n }\n\n /**\n * Decrypt the balance of a delegator using delegated decryption credentials.\n * The connected signer acts as the delegate who has been granted permission\n * by the delegator to decrypt their balance.\n *\n * Decrypted values are cached in storage keyed by `(token, owner, handle)`.\n * Cache write failures are silently ignored — they do not affect the returned value.\n *\n * @param delegatorAddress - The address of the account that delegated decryption rights.\n * @param owner - Optional balance owner address. Defaults to the delegator address.\n * @returns The decrypted plaintext balance as a bigint.\n * @throws {@link DelegationNotFoundError} if no active delegation exists from the delegator to the connected signer.\n * @throws {@link DelegationExpiredError} if the delegation has expired.\n * @throws {@link DecryptionFailedError} if delegated decryption fails or the relayer returns no value.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails.\n * @throws {@link NoCiphertextError} if the relayer returns HTTP 400 (no ciphertext for this account).\n * @throws {@link RelayerRequestFailedError} if the relayer returns a non-400 HTTP error.\n *\n * @example\n * ```ts\n * const balance = await token.decryptBalanceAs({\n * delegatorAddress: \"0xDelegator\",\n * });\n * ```\n */\n async decryptBalanceAs({\n delegatorAddress,\n owner,\n }: {\n delegatorAddress: Address;\n owner?: Address;\n }): Promise<bigint> {\n const normalizedDelegator = getAddress(delegatorAddress);\n const normalizedOwner = owner ? getAddress(owner) : normalizedDelegator;\n\n const handle = await this.readConfidentialBalanceOf(normalizedOwner);\n if (this.isZeroHandle(handle)) {\n return 0n;\n }\n\n // Check persistent cache keyed by (token, owner, handle).\n // When the on-chain balance changes the encrypted handle changes too,\n // so stale entries are never served — no TTL needed.\n const cached = await loadCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: normalizedOwner,\n handle,\n });\n if (cached !== null) {\n return cached;\n }\n\n // Pre-flight delegation check — avoids wasting a wallet signature on an\n // expired or non-existent delegation.\n await this.#assertDelegationActive(normalizedDelegator);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n\n const creds = await this.delegatedCredentials.allow(normalizedDelegator, this.address);\n\n const result = await this.relayer.delegatedUserDecrypt({\n handles: [handle],\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n delegatorAddress: creds.delegatorAddress,\n delegateAddress: creds.delegateAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(\n `Delegated decryption returned no value for handle ${handle}`,\n );\n }\n\n try {\n await saveCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: normalizedOwner,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n\n return value;\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt delegated balance\");\n }\n }\n\n /**\n * Decrypt a single encrypted handle into a plaintext bigint.\n * Returns `0n` for zero handles without calling the relayer.\n *\n * @param handle - The encrypted balance handle to decrypt.\n * @param owner - Optional owner address for the decrypt request.\n * @returns The decrypted plaintext value as a bigint.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n *\n * @example\n * ```ts\n * const handle = await token.confidentialBalanceOf();\n * const value = await token.decryptBalance(handle);\n * ```\n */\n async decryptBalance(handle: Handle, owner?: Address): Promise<bigint> {\n if (this.isZeroHandle(handle)) {\n return 0n;\n }\n\n const signerAddress = owner ?? (await this.signer.getAddress());\n\n // Check persistent cache — avoids the 2–5 s decrypt spinner on reload.\n const cached = await loadCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: signerAddress,\n handle,\n });\n if (cached !== null) {\n return cached;\n }\n\n const creds = await this.credentials.allow(this.address);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n const result = await this.relayer.userDecrypt({\n handles: [handle],\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(`Decryption returned no value for handle ${handle}`);\n }\n try {\n await saveCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: signerAddress,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n return value;\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt balance\");\n }\n }\n\n /**\n * Batch-decrypt arbitrary encrypted handles in a single relayer call.\n * Zero handles are returned as 0n without hitting the relayer.\n *\n * @param handles - Array of encrypted handles to decrypt.\n * @param owner - Optional owner address for the decrypt request.\n * @returns A Map from handle to decrypted bigint value.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n */\n async decryptHandles(handles: Handle[], owner?: Address): Promise<Map<Handle, bigint>> {\n const results = new Map<Handle, bigint>();\n const nonZeroHandles: Handle[] = [];\n\n for (const handle of handles) {\n if (this.isZeroHandle(handle)) {\n results.set(handle, 0n);\n } else {\n nonZeroHandles.push(handle);\n }\n }\n\n if (nonZeroHandles.length === 0) {\n return results;\n }\n\n const creds = await this.credentials.allow(this.address);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n const decrypted = await this.relayer.userDecrypt({\n handles: nonZeroHandles,\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress: owner ?? (await this.signer.getAddress()),\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n for (const handle of nonZeroHandles) {\n const value = decrypted[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(`Decryption returned no value for handle ${handle}`);\n }\n results.set(handle, value);\n }\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt handles\");\n }\n\n return results;\n }\n\n /** Verify all tokens share the same relayer and return it. */\n private static assertSameRelayer(tokens: ReadonlyToken[]): RelayerSDK {\n const relayer = tokens[0]!.relayer;\n for (let i = 1; i < tokens.length; i++) {\n if (tokens[i]!.relayer !== relayer) {\n throw new ConfigurationError(\n \"All tokens in a batch operation must share the same relayer instance\",\n );\n }\n }\n return relayer;\n }\n}\n\n/**\n * Inspect a caught error for an HTTP status code and return the appropriate\n * typed SDK error (NoCiphertextError for 400, RelayerRequestFailedError for\n * other HTTP errors, or the generic DecryptionFailedError as fallback).\n */\nfunction wrapDecryptError(error: unknown, fallbackMessage: string): Error {\n if (\n error instanceof DecryptionFailedError ||\n error instanceof NoCiphertextError ||\n error instanceof RelayerRequestFailedError ||\n error instanceof SigningRejectedError ||\n error instanceof SigningFailedError\n ) {\n return error;\n }\n\n const statusCode =\n error !== null &&\n error !== undefined &&\n typeof error === \"object\" &&\n \"statusCode\" in error &&\n typeof (error as Record<string, unknown>).statusCode === \"number\"\n ? ((error as Record<string, unknown>).statusCode as number)\n : undefined;\n\n if (statusCode === 400) {\n return new NoCiphertextError(\n error instanceof Error ? error.message : \"No ciphertext for this account\",\n { cause: error },\n );\n }\n\n if (statusCode !== undefined) {\n return new RelayerRequestFailedError(\n error instanceof Error ? error.message : fallbackMessage,\n statusCode,\n { cause: error },\n );\n }\n\n return new DecryptionFailedError(fallbackMessage, {\n cause: error,\n });\n}\n","/**\n * Framework-agnostic event decoders for confidential token contracts.\n * Works with raw log data from any provider.\n */\n\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\nimport { getAddress, type Address, type Hex } from \"viem\";\nimport { prefixHex } from \"../utils\";\n\n// ---------------------------------------------------------------------------\n// Generic log shape\n// ---------------------------------------------------------------------------\n\n/** Framework-agnostic log shape compatible with any Ethereum provider. */\nexport interface RawLog {\n /** Indexed event topics (topic[0] is the event signature hash). */\n readonly topics: readonly Hex[];\n /** ABI-encoded non-indexed event data. */\n readonly data: Hex;\n}\n\n// ---------------------------------------------------------------------------\n// Event topic0 constants (keccak256 of canonical signature)\n// ---------------------------------------------------------------------------\n\n/**\n * Event topic0 constants (keccak256 of the canonical Solidity signature).\n * Pass to `getLogs({ topics: [Object.values(Topics)] })` to fetch all events.\n */\nexport const Topics = {\n /** `ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)` */\n ConfidentialTransfer: \"0x67500e8d0ed826d2194f514dd0d8124f35648ab6e3fb5e6ed867134cffe661e9\",\n /** `Wrapped(uint64 mintAmount, uint256 amountIn, uint256 feeAmount, address indexed to_, uint256 indexed mintTxId)` */\n Wrapped: \"0x1f7907f4d84043abe0fb7c74e8865ee5fe93fe4f691c54a7b8fa9d6fb17c7cba\",\n /** `UnwrapRequested(address indexed receiver, bytes32 amount)` */\n UnwrapRequested: \"0x77d02d353c5629272875d11f1b34ec4c65d7430b075575b78cd2502034c469ee\",\n /** `UnwrappedFinalized(bytes32 indexed burntAmountHandle, ...)` */\n UnwrappedFinalized: \"0xc64e7c81b18b674fc5b037d8a0041bfe3332d86c780a4688f404ee01fbabb152\",\n /** `UnwrappedStarted(bool returnVal, uint256 indexed requestId, ...)` */\n UnwrappedStarted: \"0x3838891d4843c6d7f9f494570b6fd8843f4e3c3ddb817c1411760bd31b819806\",\n} as const;\n\n// ---------------------------------------------------------------------------\n// Typed event interfaces\n// ---------------------------------------------------------------------------\n\n/** Decoded `ConfidentialTransfer` event — an encrypted token transfer. */\nexport interface ConfidentialTransferEvent {\n readonly eventName: \"ConfidentialTransfer\";\n /** Sender address. */\n readonly from: Address;\n /** Receiver address. */\n readonly to: Address;\n /** FHE ciphertext handle for the transferred amount. */\n readonly encryptedAmountHandle: Handle;\n}\n\n/** Decoded `Wrapped` event — an ERC-20 shield (wrap) operation. */\nexport interface WrappedEvent {\n readonly eventName: \"Wrapped\";\n /** Confidential tokens minted. */\n readonly mintAmount: bigint;\n /** Underlying ERC-20 tokens deposited. */\n readonly amountIn: bigint;\n /** Fee deducted during wrapping. */\n readonly feeAmount: bigint;\n /** Receiver of the minted confidential tokens. */\n readonly to: Address;\n /** On-chain mint transaction ID. */\n readonly mintTxId: bigint;\n}\n\n/** Decoded `UnwrapRequested` event — an unshield request submitted. */\nexport interface UnwrapRequestedEvent {\n readonly eventName: \"UnwrapRequested\";\n /** Address that will receive the unwrapped ERC-20 tokens. */\n readonly receiver: Address;\n /** FHE ciphertext handle for the requested unshield amount. */\n readonly encryptedAmount: Handle;\n}\n\n/** Decoded `UnwrappedFinalized` event — an unshield completed on-chain. */\nexport interface UnwrappedFinalizedEvent {\n readonly eventName: \"UnwrappedFinalized\";\n /** FHE handle of the burnt confidential balance. */\n readonly burntAmountHandle: Handle;\n /** Whether the finalization succeeded. */\n readonly finalizeSuccess: boolean;\n /** Whether the fee transfer succeeded. */\n readonly feeTransferSuccess: boolean;\n /** Amount of confidential tokens burnt. */\n readonly burnAmount: bigint;\n /** Amount of underlying ERC-20 tokens returned. */\n readonly unwrapAmount: bigint;\n /** Fee deducted during unwrapping. */\n readonly feeAmount: bigint;\n /** Next on-chain transaction ID. */\n readonly nextTxId: bigint;\n}\n\n/** Decoded `UnwrappedStarted` event — the relayer began processing an unshield. */\nexport interface UnwrappedStartedEvent {\n readonly eventName: \"UnwrappedStarted\";\n /** Whether the unwrap start succeeded. */\n readonly returnVal: boolean;\n /** On-chain request ID. */\n readonly requestId: bigint;\n /** On-chain transaction ID. */\n readonly txId: bigint;\n /** Receiver address. */\n readonly to: Address;\n /** Refund address (if applicable). */\n readonly refund: Address;\n /** FHE handle of the requested amount. */\n readonly requestedAmount: Handle;\n /** FHE handle of the burn amount. */\n readonly burnAmount: Handle;\n}\n\n/** Union of all decoded confidential token event types. */\nexport type OnChainEvent =\n | ConfidentialTransferEvent\n | WrappedEvent\n | UnwrapRequestedEvent\n | UnwrappedFinalizedEvent\n | UnwrappedStartedEvent;\n\n// ---------------------------------------------------------------------------\n// ABI decoding helpers (no external deps)\n// ---------------------------------------------------------------------------\n\nfunction topicToAddress(topic: Hex): Address {\n return getAddress(prefixHex(topic.slice(-40)));\n}\n\nfunction topicToBigInt(topic: Hex): bigint {\n return BigInt(topic);\n}\n\nfunction topicToBytes32(topic: Hex): Handle {\n // EVM topics are already 32-byte 0x-prefixed hex — cast directly\n return topic as Handle;\n}\n\nfunction wordAt(data: Hex, index: number): string {\n // data starts with \"0x\", each word is 64 hex chars (32 bytes)\n const start = 2 + index * 64;\n const word = data.slice(start, start + 64);\n return word.length === 64 ? word : word.padEnd(64, \"0\");\n}\n\nfunction wordToAddress(data: Hex, index: number): Address {\n return getAddress(prefixHex(wordAt(data, index).slice(-40)));\n}\n\nfunction wordToBigInt(data: Hex, index: number): bigint {\n return BigInt(\"0x\" + wordAt(data, index));\n}\n\nfunction wordToBool(data: Hex, index: number): boolean {\n return BigInt(\"0x\" + wordAt(data, index)) !== 0n;\n}\n\nfunction wordToBytes32(data: Hex, index: number): Handle {\n // wordAt returns exactly 64 hex chars — prefix and cast directly\n return prefixHex(wordAt(data, index)) as Handle;\n}\n\n// ---------------------------------------------------------------------------\n// Individual decoders\n// ---------------------------------------------------------------------------\n\n/**\n * ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)\n * All 3 params indexed → topics[1..3], no data.\n */\nexport function decodeConfidentialTransfer(log: RawLog): ConfidentialTransferEvent | null {\n if (log.topics[0] !== Topics.ConfidentialTransfer) {\n return null;\n }\n if (log.topics.length < 4) {\n return null;\n }\n\n return {\n eventName: \"ConfidentialTransfer\",\n from: topicToAddress(log.topics[1]!),\n to: topicToAddress(log.topics[2]!),\n encryptedAmountHandle: topicToBytes32(log.topics[3]!),\n };\n}\n\n/**\n * Wrapped(uint64 mintAmount, uint256 amountIn, uint256 feeAmount, address indexed to_, uint256 indexed mintTxId)\n * Indexed: to_ (topics[1]), mintTxId (topics[2])\n * Data: mintAmount (uint64, abi-encoded as uint256), amountIn, feeAmount\n */\nexport function decodeWrapped(log: RawLog): WrappedEvent | null {\n if (log.topics[0] !== Topics.Wrapped) {\n return null;\n }\n if (log.topics.length < 3) {\n return null;\n }\n\n return {\n eventName: \"Wrapped\",\n to: topicToAddress(log.topics[1]!),\n mintTxId: topicToBigInt(log.topics[2]!),\n mintAmount: wordToBigInt(log.data, 0),\n amountIn: wordToBigInt(log.data, 1),\n feeAmount: wordToBigInt(log.data, 2),\n };\n}\n\n/**\n * UnwrapRequested(address indexed receiver, bytes32 amount)\n * Indexed: receiver (topics[1])\n * Data: amount (bytes32)\n */\nexport function decodeUnwrapRequested(log: RawLog): UnwrapRequestedEvent | null {\n if (log.topics[0] !== Topics.UnwrapRequested) {\n return null;\n }\n if (log.topics.length < 2) {\n return null;\n }\n\n return {\n eventName: \"UnwrapRequested\",\n receiver: topicToAddress(log.topics[1]!),\n encryptedAmount: wordToBytes32(log.data, 0),\n };\n}\n\n/**\n * UnwrappedFinalized(bytes32 indexed burntAmountHandle, bool finalizeSuccess, bool feeTransferSuccess,\n * uint64 burnAmount, uint256 unwrapAmount, uint256 feeAmount, uint256 indexed nextTxId)\n * Indexed: burntAmountHandle (topics[1]), nextTxId (topics[2])\n * Data: finalizeSuccess, feeTransferSuccess, burnAmount, unwrapAmount, feeAmount\n */\nexport function decodeUnwrappedFinalized(log: RawLog): UnwrappedFinalizedEvent | null {\n if (log.topics[0] !== Topics.UnwrappedFinalized) {\n return null;\n }\n if (log.topics.length < 3) {\n return null;\n }\n\n return {\n eventName: \"UnwrappedFinalized\",\n burntAmountHandle: topicToBytes32(log.topics[1]!),\n nextTxId: topicToBigInt(log.topics[2]!),\n finalizeSuccess: wordToBool(log.data, 0),\n feeTransferSuccess: wordToBool(log.data, 1),\n burnAmount: wordToBigInt(log.data, 2),\n unwrapAmount: wordToBigInt(log.data, 3),\n feeAmount: wordToBigInt(log.data, 4),\n };\n}\n\n/**\n * UnwrappedStarted(bool returnVal, uint256 indexed requestId, uint256 indexed txId, address indexed to,\n * address refund, bytes32 requestedAmount, bytes32 burnAmount)\n * Indexed: requestId (topics[1]), txId (topics[2]), to (topics[3])\n * Data: returnVal, refund, requestedAmount, burnAmount\n */\nexport function decodeUnwrappedStarted(log: RawLog): UnwrappedStartedEvent | null {\n if (log.topics[0] !== Topics.UnwrappedStarted) {\n return null;\n }\n if (log.topics.length < 4) {\n return null;\n }\n\n return {\n eventName: \"UnwrappedStarted\",\n requestId: topicToBigInt(log.topics[1]!),\n txId: topicToBigInt(log.topics[2]!),\n to: topicToAddress(log.topics[3]!),\n returnVal: wordToBool(log.data, 0),\n refund: wordToAddress(log.data, 1),\n requestedAmount: wordToBytes32(log.data, 2),\n burnAmount: wordToBytes32(log.data, 3),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Convenience helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Try all decoders on a single log and return the first match, or `null`.\n *\n * @example\n * ```ts\n * const event = decodeOnChainEvent(log);\n * if (event?.eventName === \"ConfidentialTransfer\") {\n * console.log(event.from, event.to);\n * }\n * ```\n */\nexport function decodeOnChainEvent(log: RawLog): OnChainEvent | null {\n return (\n decodeConfidentialTransfer(log) ??\n decodeWrapped(log) ??\n decodeUnwrapRequested(log) ??\n decodeUnwrappedFinalized(log) ??\n decodeUnwrappedStarted(log)\n );\n}\n\n/**\n * Batch-decode an array of logs, skipping unrecognized entries.\n *\n * @example\n * ```ts\n * const events = decodeOnChainEvents(receipt.logs);\n * ```\n */\nexport function decodeOnChainEvents(logs: readonly RawLog[]): OnChainEvent[] {\n const events: OnChainEvent[] = [];\n for (const log of logs) {\n const event = decodeOnChainEvent(log);\n if (event) {\n events.push(event);\n }\n }\n return events;\n}\n\n/**\n * Find the first {@link UnwrapRequestedEvent} in a logs array.\n *\n * @example\n * ```ts\n * const event = findUnwrapRequested(receipt.logs);\n * if (event) console.log(event.receiver, event.encryptedAmount);\n * ```\n */\nexport function findUnwrapRequested(logs: readonly RawLog[]): UnwrapRequestedEvent | null {\n for (const log of logs) {\n const event = decodeUnwrapRequested(log);\n if (event) {\n return event;\n }\n }\n return null;\n}\n\n/**\n * Find the first {@link WrappedEvent} in a logs array.\n *\n * @example\n * ```ts\n * const event = findWrapped(receipt.logs);\n * if (event) console.log(event.to, event.amountIn);\n * ```\n */\nexport function findWrapped(logs: readonly RawLog[]): WrappedEvent | null {\n for (const log of logs) {\n const event = decodeWrapped(log);\n if (event) {\n return event;\n }\n }\n return null;\n}\n\n/**\n * All 5 confidential token event topic0 hashes.\n * Pass to `getLogs({ topics: [TOKEN_TOPICS] })` to fetch\n * all confidential token events in a single RPC call.\n */\nexport const TOKEN_TOPICS = [\n Topics.ConfidentialTransfer,\n Topics.Wrapped,\n Topics.UnwrapRequested,\n Topics.UnwrappedFinalized,\n Topics.UnwrappedStarted,\n] as const;\n","/**\n * Higher-level activity feed helpers for confidential token events.\n * Normalizes all 5 event types into a single renderable \"activity item\",\n * classifies direction (incoming/outgoing/self) relative to the user,\n * and supports batch-decryption of encrypted transfer amounts.\n *\n * Pure functions, no framework dependency — works with any provider.\n */\n\nimport {\n decodeOnChainEvent,\n type RawLog,\n type OnChainEvent,\n type ConfidentialTransferEvent,\n type WrappedEvent,\n type UnwrapRequestedEvent,\n type UnwrappedFinalizedEvent,\n type UnwrappedStartedEvent,\n} from \"./events/onchain-events\";\nimport type { Address, Hex } from \"viem\";\nimport type { Handle } from \"./relayer/relayer-sdk.types\";\nimport { ZERO_HANDLE } from \"./token/readonly-token\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Direction of an activity item relative to the connected wallet. */\nexport type ActivityDirection = \"incoming\" | \"outgoing\" | \"self\";\n\n/** Classified type of a token activity event. */\nexport type ActivityType =\n | \"transfer\"\n | \"shield\"\n | \"unshield_requested\"\n | \"unshield_started\"\n | \"unshield_finalized\";\n\n/** Amount attached to an activity item — either cleartext or encrypted (with optional decrypted value). */\nexport type ActivityAmount =\n | { readonly type: \"clear\"; readonly value: bigint }\n | {\n readonly type: \"encrypted\";\n readonly handle: Handle;\n /** Populated after batch decryption via {@link applyDecryptedValues}. */\n readonly decryptedValue?: bigint;\n };\n\n/** On-chain metadata attached to each activity item. */\nexport interface ActivityLogMetadata {\n /** Transaction hash containing this event. */\n readonly transactionHash?: Hex;\n /** Block number where this event was emitted. */\n readonly blockNumber?: bigint | number;\n /** Log index within the transaction. */\n readonly logIndex?: number;\n}\n\n/**\n * A single renderable activity feed entry.\n * Produced by {@link parseActivityFeed} from raw event logs.\n */\nexport interface ActivityItem {\n /** Classified event type. */\n readonly type: ActivityType;\n /** Direction relative to the connected wallet. */\n readonly direction: ActivityDirection;\n /** Transfer amount (clear or encrypted). */\n readonly amount: ActivityAmount;\n /** Sender address (if applicable). */\n readonly from?: Address;\n /** Receiver address (if applicable). */\n readonly to?: Address;\n /** Fee deducted (for shield/unshield events). */\n readonly fee?: bigint;\n /** Whether the on-chain operation succeeded (for unshield events). */\n readonly success?: boolean;\n /** On-chain metadata (tx hash, block number, log index). */\n readonly metadata: ActivityLogMetadata;\n /** The original decoded event. */\n readonly rawEvent: OnChainEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction addressesEqual(a: Address, b: Address): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\nfunction classifyDirection(\n userAddress: Address,\n from: Address | undefined,\n to: Address | undefined,\n): ActivityDirection {\n const isFrom = from !== undefined && addressesEqual(userAddress, from);\n const isTo = to !== undefined && addressesEqual(userAddress, to);\n\n if (isFrom && isTo) {\n return \"self\";\n }\n if (isFrom) {\n return \"outgoing\";\n }\n return \"incoming\";\n}\n\nfunction eventToActivityItem(\n event: OnChainEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n switch (event.eventName) {\n case \"ConfidentialTransfer\":\n return buildTransfer(event, userAddress, metadata);\n case \"Wrapped\":\n return buildShield(event, userAddress, metadata);\n case \"UnwrapRequested\":\n return buildUnshieldRequested(event, userAddress, metadata);\n case \"UnwrappedStarted\":\n return buildUnshieldStarted(event, userAddress, metadata);\n case \"UnwrappedFinalized\":\n return buildUnshieldFinalized(event, metadata);\n }\n}\n\nfunction buildTransfer(\n event: ConfidentialTransferEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"transfer\",\n direction: classifyDirection(userAddress, event.from, event.to),\n amount: { type: \"encrypted\", handle: event.encryptedAmountHandle },\n from: event.from,\n to: event.to,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildShield(\n event: WrappedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"shield\",\n direction: classifyDirection(userAddress, undefined, event.to),\n amount: { type: \"clear\", value: event.amountIn },\n to: event.to,\n fee: event.feeAmount,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldRequested(\n event: UnwrapRequestedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_requested\",\n direction: classifyDirection(userAddress, undefined, event.receiver),\n amount: { type: \"encrypted\", handle: event.encryptedAmount },\n to: event.receiver,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldStarted(\n event: UnwrappedStartedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_started\",\n direction: classifyDirection(userAddress, undefined, event.to),\n amount: { type: \"encrypted\", handle: event.requestedAmount },\n to: event.to,\n success: event.returnVal,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldFinalized(\n event: UnwrappedFinalizedEvent,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_finalized\",\n // Finalized events don't carry from/to addresses, always treat as incoming\n direction: \"incoming\",\n amount: { type: \"clear\", value: event.unwrapAmount },\n fee: event.feeAmount,\n success: event.finalizeSuccess,\n metadata,\n rawEvent: event,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Decode raw logs into classified activity items.\n * Skips logs that don't match any confidential token event.\n */\nexport function parseActivityFeed(\n logs: readonly (RawLog & Partial<ActivityLogMetadata>)[],\n userAddress: Address,\n): ActivityItem[] {\n const items: ActivityItem[] = [];\n for (const log of logs) {\n const event = decodeOnChainEvent(log);\n if (!event) {\n continue;\n }\n\n const metadata: ActivityLogMetadata = {\n transactionHash: log.transactionHash,\n blockNumber: log.blockNumber,\n logIndex: log.logIndex,\n };\n items.push(eventToActivityItem(event, userAddress, metadata));\n }\n return items;\n}\n\n/**\n * Extract unique non-zero encrypted handles that need decryption.\n */\nexport function extractEncryptedHandles(items: readonly ActivityItem[]): Handle[] {\n const handles = new Set<Handle>();\n for (const item of items) {\n if (item.amount.type === \"encrypted\" && item.amount.decryptedValue === undefined) {\n const h = item.amount.handle;\n // Skip zero handles\n if (h !== \"0x\" && h !== ZERO_HANDLE) {\n handles.add(h);\n }\n }\n }\n return [...handles];\n}\n\n/**\n * Return new activity items with decrypted values populated.\n * Items whose handles aren't in the map are returned unchanged.\n */\nexport function applyDecryptedValues(\n items: readonly ActivityItem[],\n decryptedMap: ReadonlyMap<Handle, bigint>,\n): ActivityItem[] {\n return items.map((item) => {\n if (item.amount.type !== \"encrypted\") {\n return item;\n }\n\n const value = decryptedMap.get(item.amount.handle);\n if (value === undefined) {\n return item;\n }\n\n return {\n ...item,\n amount: {\n type: \"encrypted\" as const,\n handle: item.amount.handle,\n decryptedValue: value,\n },\n };\n });\n}\n\n/**\n * Sort activity items by block number, most recent first.\n * Items without a block number are placed at the beginning (most recent).\n */\nexport function sortByBlockNumber(items: readonly ActivityItem[]): ActivityItem[] {\n return [...items].toSorted((a, b) => {\n const aBlock = a.metadata.blockNumber;\n const bBlock = b.metadata.blockNumber;\n\n if (aBlock === undefined && bBlock === undefined) {\n return 0;\n }\n if (aBlock === undefined) {\n return -1;\n }\n if (bBlock === undefined) {\n return 1;\n }\n\n // Convert to bigint for comparison\n const aBig = typeof aBlock === \"bigint\" ? aBlock : BigInt(aBlock);\n const bBig = typeof bBlock === \"bigint\" ? bBlock : BigInt(bBlock);\n\n if (bBig > aBig) {\n return 1;\n }\n if (bBig < aBig) {\n return -1;\n }\n\n // Same block: sort by logIndex descending\n const aIdx = a.metadata.logIndex ?? 0;\n const bIdx = b.metadata.logIndex ?? 0;\n return bIdx - aIdx;\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"activity.cjs","names":["erc20Abi","#map","#cachedDerivedKey","#cachedDerivedKeyIdentity","#deriveKey","#storage","#assertSessionEntry","#onEvent","#extendContracts","SigningRejectedError","SigningFailedError","#deleteCredentials","#createPromise","#createPromiseKey","#extendPromise","#extendCredentials","#relayer","#storeKey","#cachedStoreKey","#cachedStoreKeyIdentity","#relayer","#storeKey","#create","#signDelegated","#cachedStoreKey","#cachedStoreKeyIdentity","#onEvent","supportsInterfaceContract","ERC7984_INTERFACE_ID","ERC7984_WRAPPER_INTERFACE_ID","#batchDecryptCore","#assertDelegationActive","DecryptionFailedError","wrapperExistsContract","getWrapperContract","underlyingContract","DelegationNotFoundError","DelegationExpiredError","confidentialBalanceOfContract","ConfigurationError","NoCiphertextError","RelayerRequestFailedError","SigningRejectedError","SigningFailedError"],"sources":["../../src/utils.ts","../../src/contracts/erc20.ts","../../src/contracts/fee-manager.ts","../../src/contracts/acl.ts","../../src/contracts/constants.ts","../../src/events/sdk-events.ts","../../src/token/memory-storage.ts","../../src/token/credential-crypto.ts","../../src/token/credential-validation.ts","../../src/token/session-signatures.ts","../../src/token/credential-manager-base.ts","../../src/token/credentials-manager.ts","../../src/token/delegated-credentials-manager.ts","../../src/token/balance-cache.ts","../../src/token/readonly-token.ts","../../src/events/onchain-events.ts","../../src/activity.ts"],"sourcesContent":["import type { Hex } from \"viem\";\n\n/** Coerce an unknown caught value to an Error instance. */\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/** Normalize a un-prefixed hex payload to a 0x-prefixed `Hex` value. */\nexport function prefixHex(value: string): Hex {\n return (value.startsWith(\"0x\") ? value : `0x${value}`) as Hex;\n}\n\n/** Convert a public `Hex` value back an unprefixed format. */\nexport function unprefixHex(value: Hex): string {\n assertCondition(value.startsWith(\"0x\"), `Expected 0x-prefixed hex, got: ${value}`);\n return value.slice(2);\n}\n\nexport function assertObject(\n value: unknown,\n context: string,\n): asserts value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new TypeError(`${context} must be an object, got ${typeof value}`);\n }\n}\n\nexport function assertString(value: unknown, context: string): asserts value is string {\n if (typeof value !== \"string\") {\n throw new TypeError(`${context} must be a string, got ${typeof value}`);\n }\n}\n\nexport function assertArray(value: unknown, context: string): asserts value is unknown[] {\n if (!Array.isArray(value)) {\n throw new TypeError(`${context} must be an array, got ${typeof value}`);\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function assertFunction(value: unknown, context: string): asserts value is Function {\n if (typeof value !== \"function\") {\n throw new TypeError(`${context} must be a function, got ${typeof value}`);\n }\n}\n\n/** Assert that `obj[key]` is a string. Narrows `obj` to include `{ [key]: string }`. */\nexport function assertStringProp<\n K extends string,\n O extends Record<string, unknown> = Record<string, unknown>,\n>(obj: O, key: K, context: string): asserts obj is O & Record<K, string> {\n assertString(obj[key], context);\n}\n\n/** Assert that `obj[key]` is a function. Narrows `obj` to include `{ [key]: F }`. */\nexport function assertFunctionProp<\n K extends string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n F extends Function,\n O extends Record<string, unknown> = Record<string, unknown>,\n>(obj: O, key: K, context: string): asserts obj is O & Record<K, F> {\n assertFunction(obj[key], context);\n}\n\nexport function assertCondition(condition: boolean, message: string): asserts condition {\n if (!condition) {\n throw new TypeError(message);\n }\n}\n\n// ── Environment detection ────────────────────────────────────\n\n/**\n * Subset of the WebExtensions `runtime` API used by the SDK.\n * @see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime\n */\nexport interface BrowserExtensionRuntime {\n /** The ID of the extension. */\n id: string;\n /** Convert a relative path within the extension to a fully-qualified URL. */\n getURL: (path: string) => string;\n}\n\n/**\n * Return the browser extension runtime object, or `undefined` outside extensions.\n * Works across Chrome/Edge (`chrome.runtime`) and Firefox/Safari (`browser.runtime`).\n * Extensions have restricted CSP that blocks `blob:` URLs, so callers use\n * this to detect the environment and resolve file URLs via `runtime.getURL`.\n */\nexport function getBrowserExtensionRuntime(): BrowserExtensionRuntime | undefined {\n const g = globalThis as unknown as Record<string, unknown>;\n for (const ns of [g.chrome, g.browser]) {\n try {\n assertObject(ns, \"ns\");\n const { runtime } = ns;\n assertObject(runtime, \"runtime\");\n assertStringProp(runtime, \"id\", \"runtime.id\");\n assertFunctionProp<\"getURL\", (path: string) => string>(runtime, \"getURL\", \"runtime.getURL\");\n return runtime;\n } catch {\n continue;\n }\n }\n return undefined;\n}\n\n// ── Concurrency helper ──────────────────────────────────────\n\n/**\n * Execute an array of async thunks with bounded concurrency.\n * Defaults to `Infinity` (equivalent to `Promise.all`).\n */\nexport async function pLimit<T>(\n fns: (() => Promise<T>)[],\n maxConcurrency = Infinity,\n): Promise<T[]> {\n if (!Number.isFinite(maxConcurrency) || maxConcurrency >= fns.length) {\n return Promise.all(fns.map((f) => f()));\n }\n\n const results: T[] = Array.from({ length: fns.length });\n let index = 0;\n\n async function worker() {\n while (index < fns.length) {\n const i = index++;\n results[i] = await fns[i]!();\n }\n }\n\n await Promise.all(Array.from({ length: maxConcurrency }, worker));\n return results;\n}\n","import { erc20Abi, type Address } from \"viem\";\n\n/**\n * Returns the contract config to read a token's name.\n *\n * @example\n * ```ts\n * const name = await signer.readContract(nameContract(tokenAddress));\n * ```\n */\nexport function nameContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"name\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read a token's symbol.\n *\n * @example\n * ```ts\n * const symbol = await signer.readContract(symbolContract(tokenAddress));\n * ```\n */\nexport function symbolContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"symbol\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read a token's decimals.\n *\n * @example\n * ```ts\n * const decimals = await signer.readContract(decimalsContract(tokenAddress));\n * ```\n */\nexport function decimalsContract(tokenAddress: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"decimals\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read an ERC-20 balance.\n *\n * @example\n * ```ts\n * const balance = await signer.readContract(\n * balanceOfContract(tokenAddress, account),\n * );\n * ```\n */\nexport function balanceOfContract(tokenAddress: Address, account: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"balanceOf\",\n args: [account],\n } as const;\n}\n\n/**\n * Returns the contract config to read an ERC-20 allowance.\n *\n * @example\n * ```ts\n * const allowance = await signer.readContract(\n * allowanceContract(tokenAddress, owner, spender),\n * );\n * ```\n */\nexport function allowanceContract(tokenAddress: Address, owner: Address, spender: Address) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"allowance\",\n args: [owner, spender],\n } as const;\n}\n\n/**\n * Returns the contract config for an ERC-20 approve.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * approveContract(tokenAddress, spender, amount),\n * );\n * ```\n */\nexport function approveContract(tokenAddress: Address, spender: Address, value: bigint) {\n return {\n address: tokenAddress,\n abi: erc20Abi,\n functionName: \"approve\",\n args: [spender, value],\n } as const;\n}\n","import type { Address } from \"viem\";\n\nexport const feeManagerAbi = [\n {\n inputs: [\n {\n internalType: \"uint256\",\n name: \"amount\",\n type: \"uint256\",\n },\n {\n internalType: \"address\",\n name: \"wrapFrom\",\n type: \"address\",\n },\n {\n internalType: \"address\",\n name: \"wrapTo\",\n type: \"address\",\n },\n ],\n name: \"getWrapFee\",\n outputs: [\n {\n internalType: \"uint256\",\n name: \"\",\n type: \"uint256\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [\n {\n internalType: \"uint64\",\n name: \"amount\",\n type: \"uint64\",\n },\n {\n internalType: \"address\",\n name: \"unwrapFrom\",\n type: \"address\",\n },\n {\n internalType: \"address\",\n name: \"unwrapTo\",\n type: \"address\",\n },\n ],\n name: \"getUnwrapFee\",\n outputs: [\n {\n internalType: \"uint64\",\n name: \"\",\n type: \"uint64\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [],\n name: \"getBatchTransferFee\",\n outputs: [\n {\n internalType: \"uint64\",\n name: \"\",\n type: \"uint64\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n {\n inputs: [],\n name: \"getFeeRecipient\",\n outputs: [\n {\n internalType: \"address\",\n name: \"\",\n type: \"address\",\n },\n ],\n stateMutability: \"view\",\n type: \"function\",\n },\n] as const;\n\n/**\n * Returns the contract config to compute the wrap fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getWrapFeeContract(feeManager, amount, from, to),\n * );\n * ```\n */\nexport function getWrapFeeContract(\n feeManagerAddress: Address,\n amount: bigint,\n wrapFrom: Address,\n wrapTo: Address,\n) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getWrapFee\",\n args: [amount, wrapFrom, wrapTo],\n } as const;\n}\n\n/**\n * Returns the contract config to compute the unwrap fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getUnwrapFeeContract(feeManager, amount, from, to),\n * );\n * ```\n */\nexport function getUnwrapFeeContract(\n feeManagerAddress: Address,\n amount: bigint,\n unwrapFrom: Address,\n unwrapTo: Address,\n) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getUnwrapFee\",\n args: [amount, unwrapFrom, unwrapTo],\n } as const;\n}\n\n/**\n * Returns the contract config to read the batch transfer fee.\n *\n * @example\n * ```ts\n * const fee = await signer.readContract(\n * getBatchTransferFeeContract(feeManagerAddress),\n * );\n * ```\n */\nexport function getBatchTransferFeeContract(feeManagerAddress: Address) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getBatchTransferFee\",\n args: [],\n } as const;\n}\n\n/**\n * Returns the contract config to read the fee recipient address.\n *\n * @example\n * ```ts\n * const recipient = await signer.readContract(\n * getFeeRecipientContract(feeManagerAddress),\n * );\n * ```\n */\nexport function getFeeRecipientContract(feeManagerAddress: Address) {\n return {\n address: feeManagerAddress,\n abi: feeManagerAbi,\n functionName: \"getFeeRecipient\",\n args: [],\n } as const;\n}\n","import type { Address } from \"viem\";\n\nexport const aclAbi = [\n {\n inputs: [\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n { internalType: \"uint64\", name: \"expirationDate\", type: \"uint64\" },\n ],\n name: \"delegateForUserDecryption\",\n outputs: [],\n stateMutability: \"nonpayable\",\n type: \"function\",\n },\n {\n inputs: [\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n ],\n name: \"revokeDelegationForUserDecryption\",\n outputs: [],\n stateMutability: \"nonpayable\",\n type: \"function\",\n },\n {\n inputs: [\n { internalType: \"address\", name: \"delegator\", type: \"address\" },\n { internalType: \"address\", name: \"delegate\", type: \"address\" },\n { internalType: \"address\", name: \"contractAddress\", type: \"address\" },\n ],\n name: \"getUserDecryptionDelegationExpirationDate\",\n outputs: [{ internalType: \"uint64\", name: \"\", type: \"uint64\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n] as const;\n\n/**\n * Returns the contract config to delegate user decryption rights.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * delegateForUserDecryptionContract(aclAddress, delegateAddress, contractAddress, expirationDate),\n * );\n * ```\n */\nexport function delegateForUserDecryptionContract(\n aclAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n expirationDate: bigint,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"delegateForUserDecryption\",\n args: [delegateAddress, contractAddress, expirationDate],\n } as const;\n}\n\n/**\n * Returns the contract config to revoke a user decryption delegation.\n *\n * @example\n * ```ts\n * const txHash = await signer.writeContract(\n * revokeDelegationContract(aclAddress, delegateAddress, contractAddress),\n * );\n * ```\n */\nexport function revokeDelegationContract(\n aclAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"revokeDelegationForUserDecryption\",\n args: [delegateAddress, contractAddress],\n } as const;\n}\n\n/**\n * Returns the contract config to read the delegation expiry date.\n *\n * @example\n * ```ts\n * const expiry = await signer.readContract(\n * getDelegationExpiryContract(aclAddress, delegatorAddress, delegateAddress, contractAddress),\n * );\n * ```\n */\nexport function getDelegationExpiryContract(\n aclAddress: Address,\n delegatorAddress: Address,\n delegateAddress: Address,\n contractAddress: Address,\n) {\n return {\n address: aclAddress,\n abi: aclAbi,\n functionName: \"getUserDecryptionDelegationExpirationDate\",\n args: [delegatorAddress, delegateAddress, contractAddress],\n } as const;\n}\n","/** uint64 max — represents a permanent (no-expiry) delegation. */\nexport const MAX_UINT64 = 2n ** 64n - 1n;\n","import type { Address, Hex } from \"viem\";\n\n/**\n * All SDK event keys, accessible as `ZamaSDKEvents.EncryptStart` etc.\n */\nexport const ZamaSDKEvents = {\n // Credentials lifecycle\n CredentialsLoading: \"credentials:loading\",\n CredentialsCached: \"credentials:cached\",\n CredentialsExpired: \"credentials:expired\",\n CredentialsCreating: \"credentials:creating\",\n CredentialsCreated: \"credentials:created\",\n CredentialsRevoked: \"credentials:revoked\",\n CredentialsAllowed: \"credentials:allowed\",\n SessionExpired: \"session:expired\",\n // FHE operations\n EncryptStart: \"encrypt:start\",\n EncryptEnd: \"encrypt:end\",\n EncryptError: \"encrypt:error\",\n DecryptStart: \"decrypt:start\",\n DecryptEnd: \"decrypt:end\",\n DecryptError: \"decrypt:error\",\n // Write operations\n TransactionError: \"transaction:error\",\n ShieldSubmitted: \"shield:submitted\",\n TransferSubmitted: \"transfer:submitted\",\n TransferFromSubmitted: \"transferFrom:submitted\",\n ApproveSubmitted: \"approve:submitted\",\n ApproveUnderlyingSubmitted: \"approveUnderlying:submitted\",\n UnwrapSubmitted: \"unwrap:submitted\",\n FinalizeUnwrapSubmitted: \"finalizeUnwrap:submitted\",\n // Delegation operations\n DelegationSubmitted: \"delegation:submitted\",\n RevokeDelegationSubmitted: \"revokeDelegation:submitted\",\n // Unshield orchestration\n UnshieldPhase1Submitted: \"unshield:phase1_submitted\",\n UnshieldPhase2Started: \"unshield:phase2_started\",\n UnshieldPhase2Submitted: \"unshield:phase2_submitted\",\n} as const;\n\n/** Union of all SDK event type strings. */\nexport type ZamaSDKEventType = (typeof ZamaSDKEvents)[keyof typeof ZamaSDKEvents];\n\n// -- Base fields present on every event --\n\nexport interface BaseEvent {\n tokenAddress?: Address;\n timestamp: number;\n /** Shared identifier linking related events in multi-phase operations (e.g. unshield). */\n operationId?: string;\n}\n\n// -- Per-event typed payloads --\n\nexport interface CredentialsLoadingEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsLoading;\n /** Contract addresses being requested. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCachedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCached;\n /** Contract addresses covered by the cached credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsExpiredEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsExpired;\n /** Contract addresses that need re-authorization. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCreatingEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCreating;\n /** Contract addresses being authorized. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsCreatedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsCreated;\n /** Contract addresses covered by the new credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsRevokedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsRevoked;\n contractAddresses?: Address[];\n}\n\nexport interface CredentialsAllowedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.CredentialsAllowed;\n /** Contract addresses covered by the authorized credentials. */\n contractAddresses?: Address[];\n}\n\nexport interface SessionExpiredEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.SessionExpired;\n /** Why the session expired. Currently always `\"ttl\"`, extensible for future inactivity timeout. */\n reason: \"ttl\";\n}\n\nexport interface EncryptStartEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptStart;\n}\n\nexport interface EncryptEndEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptEnd;\n durationMs: number;\n}\n\nexport interface EncryptErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.EncryptError;\n /** The error that caused the encryption to fail. */\n error: Error;\n durationMs: number;\n}\n\nexport interface DecryptStartEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptStart;\n}\n\nexport interface DecryptEndEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptEnd;\n durationMs: number;\n}\n\nexport interface DecryptErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DecryptError;\n /** The error that caused the decryption to fail. */\n error: Error;\n durationMs: number;\n}\n\nexport interface TransactionErrorEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransactionError;\n /** Which write operation failed. */\n operation: string;\n /** The error that caused the transaction to fail. */\n error: Error;\n}\n\nexport interface ShieldSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ShieldSubmitted;\n txHash: Hex;\n}\n\nexport interface TransferSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransferSubmitted;\n txHash: Hex;\n}\n\nexport interface TransferFromSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.TransferFromSubmitted;\n txHash: Hex;\n}\n\nexport interface ApproveSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ApproveSubmitted;\n txHash: Hex;\n}\n\nexport interface ApproveUnderlyingSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.ApproveUnderlyingSubmitted;\n txHash: Hex;\n}\n\nexport interface UnwrapSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnwrapSubmitted;\n txHash: Hex;\n}\n\nexport interface FinalizeUnwrapSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.FinalizeUnwrapSubmitted;\n txHash: Hex;\n}\n\nexport interface DelegationSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.DelegationSubmitted;\n txHash: Hex;\n}\n\nexport interface RevokeDelegationSubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.RevokeDelegationSubmitted;\n txHash: Hex;\n}\n\nexport interface UnshieldPhase1SubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase1Submitted;\n txHash: Hex;\n}\n\nexport interface UnshieldPhase2StartedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase2Started;\n}\n\nexport interface UnshieldPhase2SubmittedEvent extends BaseEvent {\n type: typeof ZamaSDKEvents.UnshieldPhase2Submitted;\n txHash: Hex;\n}\n\n/** Discriminated union of all SDK events. Never contains amounts, private keys, handles, or proofs. */\nexport type ZamaSDKEvent =\n | CredentialsLoadingEvent\n | CredentialsCachedEvent\n | CredentialsExpiredEvent\n | CredentialsCreatingEvent\n | CredentialsCreatedEvent\n | CredentialsRevokedEvent\n | CredentialsAllowedEvent\n | SessionExpiredEvent\n | EncryptStartEvent\n | EncryptEndEvent\n | EncryptErrorEvent\n | DecryptStartEvent\n | DecryptEndEvent\n | DecryptErrorEvent\n | TransactionErrorEvent\n | ShieldSubmittedEvent\n | TransferSubmittedEvent\n | TransferFromSubmittedEvent\n | ApproveSubmittedEvent\n | ApproveUnderlyingSubmittedEvent\n | UnwrapSubmittedEvent\n | FinalizeUnwrapSubmittedEvent\n | DelegationSubmittedEvent\n | RevokeDelegationSubmittedEvent\n | UnshieldPhase1SubmittedEvent\n | UnshieldPhase2StartedEvent\n | UnshieldPhase2SubmittedEvent;\n\nexport type ZamaSDKEventListener = (event: ZamaSDKEvent) => void;\n\n/** Distributive Omit that preserves the discriminated union. */\nexport type ZamaSDKEventInput = ZamaSDKEvent extends infer E\n ? E extends ZamaSDKEvent\n ? Omit<E, \"timestamp\" | \"tokenAddress\">\n : never\n : never;\n","import type { GenericStorage } from \"./token.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 async get<T = unknown>(key: string): Promise<T | null> {\n return (this.#map.get(key) as T) ?? null;\n }\n\n async set<T = unknown>(key: string, value: T): Promise<void> {\n this.#map.set(key, value);\n }\n\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 { Address, Hex } from \"viem\";\nimport { prefixHex } from \"../utils\";\n\n/** Encrypted data format with IV for AES-GCM decryption. */\nexport interface EncryptedData {\n /** Base64-encoded initialization vector. */\n iv: string;\n /** Base64-encoded ciphertext. */\n ciphertext: string;\n}\n\n/**\n * Manages AES-GCM encryption and decryption of FHE private keys.\n *\n * The encryption key is derived from a wallet signature via PBKDF2\n * (600 000 iterations, SHA-256). The signature is a secret known only\n * to the wallet holder, providing meaningful encryption protection\n * for the stored private key.\n *\n * The derived key is cached internally so that repeated encrypt/decrypt\n * calls with the same (signature, address) pair skip the expensive\n * PBKDF2 derivation.\n */\nexport class CredentialCrypto {\n #cachedDerivedKey: CryptoKey | null = null;\n #cachedDerivedKeyIdentity: string | null = null;\n\n /** Clear the cached derived key. Call when the session is revoked or the signer changes. */\n clearCache(): void {\n this.#cachedDerivedKey = null;\n this.#cachedDerivedKeyIdentity = null;\n }\n\n /** Encrypt an FHE private key using AES-GCM with a key derived from the wallet signature. */\n async encrypt(plaintext: Hex, signature: Hex, address: Address): Promise<EncryptedData> {\n const key = await this.#deriveKey(signature, address);\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const encoder = new TextEncoder();\n\n const ciphertext = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n encoder.encode(plaintext),\n );\n\n return {\n iv: btoa(String.fromCharCode(...iv)),\n ciphertext: btoa(String.fromCharCode(...new Uint8Array(ciphertext))),\n };\n }\n\n /** Decrypt an AES-GCM encrypted FHE private key using a key derived from the wallet signature. */\n async decrypt(encrypted: EncryptedData, signature: Hex, address: Address): Promise<Hex> {\n const key = await this.#deriveKey(signature, address);\n const iv = Uint8Array.from(atob(encrypted.iv), (c) => c.charCodeAt(0));\n const ciphertext = Uint8Array.from(atob(encrypted.ciphertext), (c) => c.charCodeAt(0));\n\n const plaintext = await crypto.subtle.decrypt({ name: \"AES-GCM\", iv }, key, ciphertext);\n\n return prefixHex(new TextDecoder().decode(plaintext));\n }\n\n async #deriveKey(signature: Hex, address: Address): Promise<CryptoKey> {\n const identity = `${signature}:${address}`;\n if (this.#cachedDerivedKey && this.#cachedDerivedKeyIdentity === identity) {\n return this.#cachedDerivedKey;\n }\n\n const encoder = new TextEncoder();\n const keyMaterial = await crypto.subtle.importKey(\n \"raw\",\n encoder.encode(signature),\n \"PBKDF2\",\n false,\n [\"deriveKey\"],\n );\n\n const key = await crypto.subtle.deriveKey(\n {\n name: \"PBKDF2\",\n salt: encoder.encode(address),\n iterations: 600_000,\n hash: \"SHA-256\",\n },\n keyMaterial,\n { name: \"AES-GCM\", length: 256 },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n\n this.#cachedDerivedKeyIdentity = identity;\n this.#cachedDerivedKey = key;\n return key;\n }\n}\n","import { getAddress, isAddress, type Address, type Hex } from \"viem\";\nimport { assertArray, assertCondition, assertObject, assertString } from \"../utils\";\nimport type { EncryptedData } from \"./credential-crypto\";\n\n/** Encrypted credential shape stored in persistent storage (shared base fields). */\nexport interface BaseEncryptedCredentials {\n publicKey: Hex;\n contractAddresses: Address[];\n encryptedPrivateKey: EncryptedData;\n startTimestamp: number;\n durationDays: number;\n}\n\n/**\n * Assert that `data` contains the base encrypted credential fields:\n * publicKey, contractAddresses (valid hex addresses), and encryptedPrivateKey (iv + ciphertext).\n */\nexport function assertBaseEncryptedCredentials(\n data: unknown,\n): asserts data is BaseEncryptedCredentials {\n assertObject(data, \"Stored credentials\");\n assertString(data.publicKey, \"credentials.publicKey\");\n assertArray(data.contractAddresses, \"credentials.contractAddresses\");\n for (const addr of data.contractAddresses) {\n assertCondition(\n typeof addr === \"string\" && isAddress(addr, { strict: false }),\n `Expected each contractAddress to be a valid hex address`,\n );\n }\n assertObject(data.encryptedPrivateKey, \"credentials.encryptedPrivateKey\");\n assertString(data.encryptedPrivateKey.iv, \"encryptedPrivateKey.iv\");\n assertString(data.encryptedPrivateKey.ciphertext, \"encryptedPrivateKey.ciphertext\");\n}\n\n/**\n * Assert that `data` contains the delegated-specific fields:\n * delegatorAddress, delegateAddress (valid addresses), startTimestamp and durationDays (numbers).\n */\nexport function assertDelegatedFields(data: unknown): asserts data is BaseEncryptedCredentials & {\n delegatorAddress: Address;\n delegateAddress: Address;\n} {\n assertBaseEncryptedCredentials(data);\n const obj = data as unknown as Record<string, unknown>;\n assertCondition(\n typeof obj.delegatorAddress === \"string\" && isAddress(obj.delegatorAddress, { strict: false }),\n \"Expected credentials.delegatorAddress to be a valid address\",\n );\n assertCondition(\n typeof obj.delegateAddress === \"string\" && isAddress(obj.delegateAddress, { strict: false }),\n \"Expected credentials.delegateAddress to be a valid address\",\n );\n assertCondition(typeof obj.startTimestamp === \"number\", \"Expected startTimestamp to be a number\");\n assertCondition(typeof obj.durationDays === \"number\", \"Expected durationDays to be a number\");\n}\n\n/** Check if credentials are still within their keypair TTL. */\nexport function isTimeValid(creds: { startTimestamp: number; durationDays: number }): boolean {\n const nowSeconds = Math.floor(Date.now() / 1000);\n const expiresAt = creds.startTimestamp + creds.durationDays * 86400;\n return nowSeconds < expiresAt;\n}\n\n/** Check if the signed address set covers all required addresses. */\nexport function coversContracts(signedAddresses: Address[], requiredContracts: Address[]): boolean {\n const required = new Set(requiredContracts.map((address) => getAddress(address)));\n const signed = new Set(signedAddresses.map((address) => getAddress(address)));\n return required.isSubsetOf(signed);\n}\n\n/**\n * Check if credentials are valid: not time-expired and covering all required contracts.\n */\nexport function isCredentialValid(\n creds: { startTimestamp: number; durationDays: number; contractAddresses: Address[] },\n requiredContracts: Address[],\n): boolean {\n if (!isTimeValid(creds)) {\n return false;\n }\n return coversContracts(creds.contractAddresses, requiredContracts);\n}\n\n/** Deduplicate and sort a list of addresses by their checksummed form. */\nexport function normalizeAddresses(addresses: Address[]): Address[] {\n return [...new Set(addresses.map((address) => getAddress(address)))].toSorted();\n}\n\n/** Compute a truncated SHA-256 store key from arbitrary identity segments. */\nexport async function computeStoreKey(...segments: (string | number)[]): Promise<string> {\n const hash = await crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(segments.map(String).join(\":\")),\n );\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .slice(0, 32);\n}\n","import type { Hex } from \"viem\";\nimport { assertCondition, assertObject, assertString } from \"../utils\";\nimport type { GenericStorage } from \"./token.types\";\n\n/** Structured session entry stored in session storage. */\nexport interface SessionEntry {\n /** EIP-712 wallet signature authorizing decryption. */\n signature: Hex;\n /** Epoch seconds when the session was created. */\n createdAt: number;\n /** TTL at creation time (not current config). `0` = always expired, `\"infinite\"` = never expires. */\n ttl: number | \"infinite\";\n}\n\n/**\n * Manages session signature entries in a {@link GenericStorage} backend.\n *\n * A session entry caches a wallet signature so that subsequent decrypt\n * operations can reuse it without prompting the user again. The TTL\n * is recorded at creation time so that changing the config later does\n * not retroactively extend or shorten existing sessions.\n */\nexport class SessionSignatures {\n #storage: GenericStorage;\n\n constructor(storage: GenericStorage) {\n this.#storage = storage;\n }\n\n #assertSessionEntry(data: unknown): asserts data is SessionEntry {\n assertObject(data, \"Session entry\");\n assertString(data.signature, \"session.signature\");\n assertCondition(\n typeof data.createdAt === \"number\",\n `Expected session.createdAt to be a number`,\n );\n assertCondition(\n typeof data.ttl === \"number\" || data.ttl === \"infinite\",\n `Expected session.ttl to be a number or \"infinite\"`,\n );\n }\n\n /** Retrieve and validate a session entry, or `null` if none exists. */\n async get(key: string): Promise<SessionEntry | null> {\n const raw = await this.#storage.get<SessionEntry>(key);\n if (raw === null) {\n return null;\n }\n this.#assertSessionEntry(raw);\n return raw;\n }\n\n /** Create and store a session entry with the given TTL. */\n async set(params: { key: string; signature: Hex; ttl: number | \"infinite\" }): Promise<void> {\n const entry: SessionEntry = {\n signature: params.signature,\n createdAt: Math.floor(Date.now() / 1000),\n ttl: params.ttl,\n };\n await this.#storage.set(params.key, entry);\n }\n\n /** Delete a session entry. */\n async delete(key: string): Promise<void> {\n await this.#storage.delete(key);\n }\n\n /**\n * Check if a session entry has expired based on its recorded TTL.\n * - `0` = always expired (every operation triggers a signing prompt).\n * - `\"infinite\"` = never expires.\n * - Positive number = seconds until expiration.\n */\n isExpired(entry: SessionEntry): boolean {\n if (entry.ttl === \"infinite\") {\n return false;\n }\n if (entry.ttl === 0) {\n return true;\n }\n return Math.floor(Date.now() / 1000) - entry.createdAt >= entry.ttl;\n }\n}\n","import type { Address, Hex } from \"viem\";\nimport type { ZamaSDKEventInput, ZamaSDKEventListener } from \"../events/sdk-events\";\nimport { ZamaSDKEvents } from \"../events/sdk-events\";\nimport { CredentialCrypto } from \"./credential-crypto\";\nimport type { BaseEncryptedCredentials } from \"./credential-validation\";\nimport {\n coversContracts,\n isCredentialValid,\n isTimeValid,\n normalizeAddresses,\n} from \"./credential-validation\";\nimport { SigningFailedError, SigningRejectedError, wrapSigningError } from \"./errors\";\nimport { SessionSignatures } from \"./session-signatures\";\nimport type { GenericSigner, GenericStorage, StoredCredentials } from \"./token.types\";\n\n/** Shared configuration accepted by both credential manager variants. */\nexport interface CredentialsConfig {\n /** Backend that generates FHE keypairs (public/private). */\n relayer: { generateKeypair(): Promise<{ publicKey: Hex; privateKey: Hex }> };\n /** Wallet signer used for EIP-712 authorization signatures. */\n signer: GenericSigner;\n /** Persistent storage for encrypted credentials. */\n storage: GenericStorage;\n /** Storage for session signatures (shorter-lived than credentials). */\n sessionStorage: GenericStorage;\n /** FHE keypair lifetime in seconds. Defaults to `86400` (1 day). */\n keypairTTL?: number;\n /** Session signature lifetime. `0` = always re-sign, `\"infinite\"` = never expire. Defaults to `2592000` (30 days). */\n sessionTTL?: number | \"infinite\";\n /** Optional listener for credential lifecycle events. */\n onEvent?: ZamaSDKEventListener;\n}\n\n/** Minimal fields needed to produce an EIP-712 signing request. */\nexport interface SigningMeta {\n /** FHE public key being authorized. */\n publicKey: Hex;\n /** Epoch seconds when the keypair authorization begins. */\n startTimestamp: number;\n /** Number of days the keypair authorization is valid for. */\n durationDays: number;\n /** Delegator address, present only for delegated credentials. */\n delegatorAddress?: string;\n}\n\n/** Options for {@link BaseCredentialsManager.resolveCredentials}. */\ninterface ResolveCredentialsOptions<TCreds> {\n /** Storage key identifying the credential entry. */\n key: string;\n /** Contract addresses the caller needs access to. */\n contracts: Address[];\n /** Deduplication key — concurrent calls with the same key share a single creation promise. */\n createKey: string;\n /** Factory that creates fresh credentials when nothing usable is cached. */\n createFn: () => Promise<TCreds>;\n}\n\n/** Options for {@link BaseCredentialsManager.createCredentials}. */\ninterface CreateCredentialsOptions<TCreds> {\n /** Storage key identifying the credential entry. */\n key: string;\n /** Contract addresses being authorized. */\n contractAddresses: Address[];\n /** Factory that builds the credential payload (keypair + signature). */\n createFn: () => Promise<TCreds>;\n /** Human-readable context included in signing error messages. */\n errorContext: string;\n}\n\n/**\n * Abstract base for credential managers. Contains the entire allow/extend/expire\n * state machine. Subclasses provide the assertion, signing, and encrypt/decrypt\n * hooks that differ between regular and delegated flows.\n *\n * @typeParam TCreds - The in-memory credential shape (includes plaintext privateKey).\n * @typeParam TEncrypted - The on-disk shape (privateKey replaced by encryptedPrivateKey).\n */\nexport abstract class BaseCredentialsManager<\n TCreds extends StoredCredentials,\n TEncrypted extends BaseEncryptedCredentials,\n> {\n protected readonly signer: GenericSigner;\n protected readonly storage: GenericStorage;\n protected readonly sessionSignatures: SessionSignatures;\n protected readonly crypto: CredentialCrypto;\n readonly keypairTTL: number;\n readonly sessionTTL: number | \"infinite\";\n\n #onEvent: ZamaSDKEventListener;\n #createPromise: Promise<TCreds> | null = null;\n #createPromiseKey: string | null = null;\n #extendPromise: Promise<TCreds> | null = null;\n\n constructor(config: CredentialsConfig) {\n this.signer = config.signer;\n this.storage = config.storage;\n this.sessionSignatures = new SessionSignatures(config.sessionStorage);\n this.crypto = new CredentialCrypto();\n this.keypairTTL = config.keypairTTL ?? 86400;\n this.sessionTTL = config.sessionTTL ?? 2592000;\n this.#onEvent = config.onEvent ?? (() => {});\n\n if (typeof this.keypairTTL === \"number\" && this.keypairTTL < 0) {\n throw new Error(\"keypairTTL must be >= 0\");\n }\n if (typeof this.sessionTTL === \"number\" && this.sessionTTL < 0) {\n throw new Error(\"sessionTTL must be >= 0\");\n }\n }\n\n /** Emit a credential lifecycle event, stamped with the current time. */\n protected emit(partial: ZamaSDKEventInput): void {\n this.#onEvent({ ...partial, timestamp: Date.now() } as never);\n }\n\n // ── Abstract hooks ────────────────────────────────────────────\n\n /** Validate that raw storage data matches the expected encrypted shape. */\n protected abstract assertEncrypted(data: unknown): asserts data is TEncrypted;\n\n /** Sign an EIP-712 authorization for the given contract addresses. */\n protected abstract signForContracts(\n meta: SigningMeta,\n contractAddresses: Address[],\n ): Promise<Hex>;\n\n /** Encrypt credentials for persistent storage. */\n protected abstract encryptCredentials(creds: TCreds): Promise<TEncrypted>;\n\n /** Decrypt credentials from persistent storage. */\n protected abstract decryptCredentials(encrypted: TEncrypted, signature: Hex): Promise<TCreds>;\n\n // ── Core credential resolution ────────────────────────────────\n\n /**\n * The allow() state machine: load → validate → extend/re-sign → or create fresh.\n * Subclasses call this from their public `allow()` after computing the key.\n */\n protected async resolveCredentials({\n key,\n contracts,\n createKey,\n createFn,\n }: ResolveCredentialsOptions<TCreds>): Promise<TCreds> {\n this.emit({\n type: ZamaSDKEvents.CredentialsLoading,\n contractAddresses: contracts,\n });\n\n try {\n const encrypted = await this.storage.get<TEncrypted>(key);\n if (encrypted) {\n this.assertEncrypted(encrypted);\n\n const sessionEntry = await this.sessionSignatures.get(key);\n if (sessionEntry) {\n if (this.sessionSignatures.isExpired(sessionEntry)) {\n await this.sessionSignatures.delete(key);\n this.emit({ type: ZamaSDKEvents.SessionExpired, reason: \"ttl\" });\n } else {\n const creds = await this.decryptCredentials(encrypted, sessionEntry.signature);\n if (isCredentialValid(creds, contracts)) {\n this.emit({\n type: ZamaSDKEvents.CredentialsCached,\n contractAddresses: contracts,\n });\n this.emit({\n type: ZamaSDKEvents.CredentialsAllowed,\n contractAddresses: contracts,\n });\n return creds;\n }\n if (isTimeValid(creds)) {\n return this.#extendContracts({\n key,\n credentials: creds,\n requiredContracts: contracts,\n });\n }\n this.emit({\n type: ZamaSDKEvents.CredentialsExpired,\n contractAddresses: contracts,\n });\n }\n }\n\n // No session or expired — need to re-sign\n if (isTimeValid(encrypted)) {\n if (coversContracts(encrypted.contractAddresses, contracts)) {\n const signature = await this.signForContracts(encrypted, encrypted.contractAddresses);\n await this.sessionSignatures.set({\n key,\n signature,\n ttl: this.sessionTTL,\n });\n const creds = await this.decryptCredentials(encrypted, signature);\n this.emit({\n type: ZamaSDKEvents.CredentialsCached,\n contractAddresses: contracts,\n });\n this.emit({\n type: ZamaSDKEvents.CredentialsAllowed,\n contractAddresses: contracts,\n });\n return creds;\n }\n // Time-valid but missing contracts — sign with old set to decrypt, then extend\n const oldSignature = await this.signForContracts(encrypted, encrypted.contractAddresses);\n const creds = await this.decryptCredentials(encrypted, oldSignature);\n return this.#extendContracts({\n key,\n credentials: creds,\n requiredContracts: contracts,\n });\n }\n\n this.emit({\n type: ZamaSDKEvents.CredentialsExpired,\n contractAddresses: contracts,\n });\n }\n } catch (error) {\n if (error instanceof SigningRejectedError || error instanceof SigningFailedError) {\n throw error;\n }\n console.warn(\"[zama-sdk] Credential resolution failed, recreating:\", error);\n await this.#deleteCredentials(key);\n }\n\n // Nothing cached — create fresh (deduplicated)\n if (!this.#createPromise || this.#createPromiseKey !== createKey) {\n this.#createPromiseKey = createKey;\n this.#createPromise = createFn()\n .then((creds) => {\n this.emit({\n type: ZamaSDKEvents.CredentialsAllowed,\n contractAddresses: contracts,\n });\n return creds;\n })\n .finally(() => {\n this.#createPromise = null;\n this.#createPromiseKey = null;\n });\n }\n return this.#createPromise;\n }\n\n // ── Shared public method implementations ──────────────────────\n\n /**\n * Check whether stored credentials are expired or don't cover the given contract.\n * Returns `true` if credentials are missing, expired, or corrupted.\n */\n protected async checkExpired(key: string, contractAddress?: Address): Promise<boolean> {\n try {\n const stored = await this.storage.get<TEncrypted>(key);\n if (!stored) {\n return false;\n }\n this.assertEncrypted(stored);\n const requiredContracts = contractAddress ? [contractAddress] : [];\n return !isCredentialValid(stored, requiredContracts);\n } catch (error) {\n console.warn(\"[zama-sdk] isExpired check failed, treating as expired:\", error);\n return true;\n }\n }\n\n /** Delete the session signature and clear caches, forcing a fresh wallet signature on next use. */\n protected async revokeSession(key: string, contractAddresses?: Address[]): Promise<void> {\n await this.sessionSignatures.delete(key);\n this.clearCaches();\n this.emit({\n type: ZamaSDKEvents.CredentialsRevoked,\n ...(contractAddresses ? { contractAddresses } : {}),\n } as ZamaSDKEventInput);\n }\n\n protected async checkAllowed(key: string): Promise<boolean> {\n const entry = await this.sessionSignatures.get(key);\n if (entry === null) {\n return false;\n }\n return !this.sessionSignatures.isExpired(entry);\n }\n\n protected async clearAll(key: string): Promise<void> {\n await this.sessionSignatures.delete(key);\n this.clearCaches();\n await this.#deleteCredentials(key);\n }\n\n /** Override to also clear subclass-specific caches (e.g. key cache). */\n protected clearCaches(): void {\n this.crypto.clearCache();\n }\n\n // ── Credential creation helper ────────────────────────────────\n\n /**\n * Shared wrapper for fresh credential creation:\n * emits events, persists, saves session, and wraps signing errors.\n */\n protected async createCredentials({\n key,\n contractAddresses,\n createFn,\n errorContext,\n }: CreateCredentialsOptions<TCreds>): Promise<TCreds> {\n this.emit({ type: ZamaSDKEvents.CredentialsCreating, contractAddresses });\n try {\n const creds = await createFn();\n await this.persistCredentials(key, creds);\n await this.sessionSignatures.set({\n key,\n signature: creds.signature,\n ttl: this.sessionTTL,\n });\n this.emit({ type: ZamaSDKEvents.CredentialsCreated, contractAddresses });\n return creds;\n } catch (error) {\n wrapSigningError(error, errorContext);\n }\n }\n\n // ── Contract extension ────────────────────────────────────────\n\n async #extendContracts({\n key,\n credentials,\n requiredContracts,\n }: {\n key: string;\n credentials: TCreds;\n requiredContracts: Address[];\n }): Promise<TCreds> {\n if (this.#extendPromise) {\n const previous = await this.#extendPromise;\n if (coversContracts(previous.contractAddresses, requiredContracts)) {\n this.emit({\n type: ZamaSDKEvents.CredentialsAllowed,\n contractAddresses: requiredContracts,\n });\n return previous;\n }\n credentials = previous;\n }\n\n const promise = this.#extendCredentials({\n key,\n credentials,\n requiredContracts,\n });\n this.#extendPromise = promise;\n try {\n return await promise;\n } finally {\n if (this.#extendPromise === promise) {\n this.#extendPromise = null;\n }\n }\n }\n\n async #extendCredentials({\n key,\n credentials,\n requiredContracts,\n }: {\n key: string;\n credentials: TCreds;\n requiredContracts: Address[];\n }): Promise<TCreds> {\n const merged = normalizeAddresses([...credentials.contractAddresses, ...requiredContracts]);\n const signature = await this.signForContracts(credentials, merged);\n\n const extended: TCreds = {\n ...credentials,\n contractAddresses: merged,\n signature,\n };\n // Persist ciphertext before session signature to prevent a window where a\n // concurrent reader sees the new signature but finds old ciphertext.\n await this.persistCredentials(key, extended);\n await this.sessionSignatures.set({\n key,\n signature,\n ttl: this.sessionTTL,\n });\n this.emit({\n type: ZamaSDKEvents.CredentialsAllowed,\n contractAddresses: requiredContracts,\n });\n return extended;\n }\n\n protected async persistCredentials(key: string, credentials: TCreds): Promise<void> {\n try {\n const encrypted = await this.encryptCredentials(credentials);\n await this.storage.set(key, encrypted);\n } catch (error) {\n console.warn(\"[zama-sdk] Failed to encrypt credentials for persistence:\", error);\n return;\n }\n }\n\n async #deleteCredentials(key: string): Promise<void> {\n try {\n await this.storage.delete(key);\n } catch (error) {\n console.warn(\"[zama-sdk] Failed to delete credentials:\", error);\n }\n }\n}\n","import { getAddress, type Address, type Hex } from \"viem\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { StoredCredentials } from \"./token.types\";\nimport { MemoryStorage } from \"./memory-storage\";\nimport {\n BaseCredentialsManager,\n type CredentialsConfig,\n type SigningMeta,\n} from \"./credential-manager-base\";\nimport {\n type BaseEncryptedCredentials,\n assertBaseEncryptedCredentials,\n normalizeAddresses,\n computeStoreKey,\n} from \"./credential-validation\";\n\n/** Internal storage shape — same as BaseEncryptedCredentials. */\ntype EncryptedCredentials = BaseEncryptedCredentials;\n\n/** Configuration for constructing a {@link CredentialsManager}. */\nexport interface CredentialsManagerConfig extends CredentialsConfig {\n /** FHE relayer backend for keypair generation and EIP-712 creation. */\n relayer: RelayerSDK;\n}\n\nfunction hasExtensionRuntimeId(value: unknown): value is { runtime: { id: string } } {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const runtime = Reflect.get(value, \"runtime\");\n if (typeof runtime !== \"object\" || runtime === null) {\n return false;\n }\n return typeof Reflect.get(runtime, \"id\") === \"string\";\n}\n\n/**\n * Manages FHE decrypt credentials for a single wallet.\n * Handles keypair generation, EIP-712 authorization signing, and\n * encrypted persistence scoped to a (wallet address, chain ID) pair.\n */\nexport class CredentialsManager extends BaseCredentialsManager<\n StoredCredentials,\n EncryptedCredentials\n> {\n #relayer: RelayerSDK;\n #cachedStoreKey: string | null = null;\n #cachedStoreKeyIdentity: string | null = null;\n\n /** Derive the deterministic storage key for a given wallet address and chain. */\n static async computeStoreKey(address: Address, chainId: number): Promise<string> {\n return computeStoreKey(getAddress(address), chainId);\n }\n\n constructor(config: CredentialsManagerConfig) {\n super(config);\n this.#relayer = config.relayer;\n\n // Warn when using in-memory session storage inside a Chrome extension context\n const chromeNamespace =\n typeof globalThis !== \"undefined\" ? Reflect.get(globalThis, \"chrome\") : undefined;\n if (hasExtensionRuntimeId(chromeNamespace) && config.sessionStorage instanceof MemoryStorage) {\n console.warn(\n \"[zama-sdk] Detected Chrome extension context with in-memory session storage. \" +\n \"Session signatures will be lost on service worker restart and won't be shared across contexts. \" +\n \"Consider using chromeSessionStorage instead. \",\n );\n }\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n /**\n * Authorize FHE credentials for one or more contract addresses.\n *\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails for any other reason.\n */\n async allow(...contractAddresses: Address[]): Promise<StoredCredentials> {\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey();\n return this.resolveCredentials({\n key,\n contracts: normalized,\n createKey: normalized.join(\",\"),\n createFn: () => this.create(normalized),\n });\n }\n\n /** Check if stored credentials are expired or unusable. */\n async isExpired(contractAddress?: Address): Promise<boolean> {\n return this.checkExpired(await this.#storeKey(), contractAddress);\n }\n\n /** Revoke the session signature. Next decrypt requires a fresh wallet signature. */\n async revoke(...contractAddresses: Address[]): Promise<void> {\n await this.revokeSession(\n await this.#storeKey(),\n contractAddresses.length > 0 ? contractAddresses : undefined,\n );\n }\n\n /** Whether a session signature is currently cached. */\n async isAllowed(): Promise<boolean> {\n return this.checkAllowed(await this.#storeKey());\n }\n\n /** Delete stored credentials (best-effort). */\n async clear(): Promise<void> {\n await this.clearAll(await this.#storeKey());\n }\n\n /**\n * Generate a fresh FHE keypair, create an EIP-712 authorization, and\n * prompt the user to sign it.\n */\n async create(contractAddresses: Address[]): Promise<StoredCredentials> {\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey();\n return this.createCredentials({\n key,\n contractAddresses: normalized,\n createFn: async () => {\n const keypair = await this.#relayer.generateKeypair();\n const startTimestamp = Math.floor(Date.now() / 1000);\n const durationDays = Math.ceil(this.keypairTTL / 86400);\n\n const eip712 = await this.#relayer.createEIP712(\n keypair.publicKey,\n normalized,\n startTimestamp,\n durationDays,\n );\n const signature = await this.signer.signTypedData(eip712);\n\n return {\n publicKey: keypair.publicKey,\n privateKey: keypair.privateKey,\n signature,\n contractAddresses: normalized,\n startTimestamp,\n durationDays,\n };\n },\n errorContext: \"Failed to create decrypt credentials\",\n });\n }\n\n // ── Abstract implementations ──────────────────────────────────\n\n protected assertEncrypted(data: unknown): asserts data is EncryptedCredentials {\n assertBaseEncryptedCredentials(data);\n }\n\n protected async signForContracts(meta: SigningMeta, contractAddresses: Address[]): Promise<Hex> {\n const eip712 = await this.#relayer.createEIP712(\n meta.publicKey,\n contractAddresses,\n meta.startTimestamp,\n meta.durationDays,\n );\n return this.signer.signTypedData(eip712);\n }\n\n protected async encryptCredentials(creds: StoredCredentials): Promise<EncryptedCredentials> {\n const address = await this.signer.getAddress();\n const encryptedPrivateKey = await this.crypto.encrypt(\n creds.privateKey,\n creds.signature,\n address,\n );\n const { privateKey: _, signature: _sig, ...rest } = creds;\n return { ...rest, encryptedPrivateKey };\n }\n\n protected async decryptCredentials(\n encrypted: EncryptedCredentials,\n signature: Hex,\n ): Promise<StoredCredentials> {\n const address = await this.signer.getAddress();\n const privateKey = await this.crypto.decrypt(encrypted.encryptedPrivateKey, signature, address);\n const { encryptedPrivateKey: _, ...rest } = encrypted;\n return { ...rest, privateKey, signature };\n }\n\n protected override clearCaches(): void {\n this.#cachedStoreKey = null;\n this.#cachedStoreKeyIdentity = null;\n super.clearCaches();\n }\n\n // ── Store key ─────────────────────────────────────────────────\n\n async #storeKey(): Promise<string> {\n const address = await this.signer.getAddress();\n const chainId = await this.signer.getChainId();\n const identity = `${getAddress(address)}:${chainId}`;\n if (this.#cachedStoreKey && this.#cachedStoreKeyIdentity === identity) {\n return this.#cachedStoreKey;\n }\n const key = await CredentialsManager.computeStoreKey(address, chainId);\n this.#cachedStoreKeyIdentity = identity;\n this.#cachedStoreKey = key;\n return key;\n }\n}\n","import { getAddress, type Address, type Hex } from \"viem\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { DelegatedStoredCredentials } from \"./token.types\";\nimport {\n BaseCredentialsManager,\n type CredentialsConfig,\n type SigningMeta,\n} from \"./credential-manager-base\";\nimport {\n type BaseEncryptedCredentials,\n assertDelegatedFields,\n normalizeAddresses,\n computeStoreKey,\n} from \"./credential-validation\";\n\n/** Internal storage shape — base fields plus delegator/delegate addresses. */\ninterface EncryptedCredentials extends BaseEncryptedCredentials {\n delegatorAddress: Address;\n delegateAddress: Address;\n}\n\n/** Configuration for constructing a {@link DelegatedCredentialsManager}. */\nexport interface DelegatedCredentialsManagerConfig extends CredentialsConfig {\n /** FHE relayer backend for keypair generation and delegated EIP-712 creation. */\n relayer: RelayerSDK;\n}\n\n/** Signing metadata for delegated credentials, adding the delegator's address. */\nexport interface DelegatedSigningMeta extends SigningMeta {\n /** On-chain address of the account that delegated decryption rights. */\n delegatorAddress: Address;\n}\n\n/**\n * Manages FHE decrypt credentials for delegated decryption.\n * Scoped to a (delegate, delegator) pair.\n */\nexport class DelegatedCredentialsManager extends BaseCredentialsManager<\n DelegatedStoredCredentials,\n EncryptedCredentials\n> {\n #relayer: RelayerSDK;\n #cachedStoreKey: string | null = null;\n #cachedStoreKeyIdentity: string | null = null;\n\n /** Derive the deterministic storage key for a (delegate, delegator, chain) triple. */\n static async computeStoreKey(\n delegateAddress: Address,\n delegatorAddress: Address,\n chainId: number,\n ): Promise<string> {\n return computeStoreKey(getAddress(delegateAddress), getAddress(delegatorAddress), chainId);\n }\n\n constructor(config: DelegatedCredentialsManagerConfig) {\n super(config);\n this.#relayer = config.relayer;\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n /**\n * Authorize FHE delegated credentials for one or more contract addresses.\n *\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails for any other reason.\n */\n async allow(\n delegatorAddress: Address,\n ...contractAddresses: Address[]\n ): Promise<DelegatedStoredCredentials> {\n const normalizedDelegator = getAddress(delegatorAddress);\n const normalized = normalizeAddresses(contractAddresses);\n const key = await this.#storeKey(normalizedDelegator);\n return this.resolveCredentials({\n key,\n contracts: normalized,\n createKey: `${normalizedDelegator}:${normalized.join(\",\")}`,\n createFn: () => this.#create(normalizedDelegator, normalized),\n });\n }\n\n /** Check if stored delegated credentials are expired or unusable. */\n async isExpired(delegatorAddress: Address, contractAddress?: Address): Promise<boolean> {\n return this.checkExpired(await this.#storeKey(getAddress(delegatorAddress)), contractAddress);\n }\n\n /** Revoke the session signature for a delegator. */\n async revoke(delegatorAddress: Address): Promise<void> {\n await this.revokeSession(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n /** Whether a session signature is currently cached for a delegator. */\n async isAllowed(delegatorAddress: Address): Promise<boolean> {\n return this.checkAllowed(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n /** Delete stored credentials for a delegator (best-effort). */\n async clear(delegatorAddress: Address): Promise<void> {\n await this.clearAll(await this.#storeKey(getAddress(delegatorAddress)));\n }\n\n // ── Credential creation ───────────────────────────────────────\n\n async #create(\n delegatorAddress: Address,\n contractAddresses: Address[],\n ): Promise<DelegatedStoredCredentials> {\n const key = await this.#storeKey(delegatorAddress);\n return this.createCredentials({\n key,\n contractAddresses,\n createFn: async () => {\n const keypair = await this.#relayer.generateKeypair();\n const delegateAddress = await this.signer.getAddress();\n const startTimestamp = Math.floor(Date.now() / 1000);\n const durationDays = Math.ceil(this.keypairTTL / 86400);\n\n const meta = {\n publicKey: keypair.publicKey,\n startTimestamp,\n durationDays,\n delegatorAddress,\n };\n const signature = await this.#signDelegated(meta, contractAddresses);\n\n return {\n publicKey: keypair.publicKey,\n privateKey: keypair.privateKey,\n signature,\n contractAddresses,\n startTimestamp,\n durationDays,\n delegatorAddress,\n delegateAddress,\n };\n },\n errorContext: \"Failed to create delegated decrypt credentials\",\n });\n }\n\n // ── Abstract implementations ──────────────────────────────────\n\n protected assertEncrypted(data: unknown): asserts data is EncryptedCredentials {\n assertDelegatedFields(data);\n }\n\n protected async signForContracts(\n meta: DelegatedSigningMeta,\n contractAddresses: Address[],\n ): Promise<Hex> {\n return this.#signDelegated(meta, contractAddresses);\n }\n\n protected async encryptCredentials(\n creds: DelegatedStoredCredentials,\n ): Promise<EncryptedCredentials> {\n const address = await this.signer.getAddress();\n const encryptedPrivateKey = await this.crypto.encrypt(\n creds.privateKey,\n creds.signature,\n address,\n );\n const { privateKey: _, signature: _sig, ...rest } = creds;\n return { ...rest, encryptedPrivateKey };\n }\n\n protected async decryptCredentials(\n encrypted: EncryptedCredentials,\n signature: Hex,\n ): Promise<DelegatedStoredCredentials> {\n const address = await this.signer.getAddress();\n const privateKey = await this.crypto.decrypt(encrypted.encryptedPrivateKey, signature, address);\n const { encryptedPrivateKey: _, ...rest } = encrypted;\n return { ...rest, privateKey, signature };\n }\n\n protected override clearCaches(): void {\n this.#cachedStoreKey = null;\n this.#cachedStoreKeyIdentity = null;\n super.clearCaches();\n }\n\n // ── Private helpers ───────────────────────────────────────────\n\n async #storeKey(delegatorAddress: Address): Promise<string> {\n const delegateAddress = await this.signer.getAddress();\n const chainId = await this.signer.getChainId();\n const identity = `${getAddress(delegateAddress)}:${getAddress(delegatorAddress)}:${chainId}`;\n if (this.#cachedStoreKey && this.#cachedStoreKeyIdentity === identity) {\n return this.#cachedStoreKey;\n }\n const key = await DelegatedCredentialsManager.computeStoreKey(\n delegateAddress,\n delegatorAddress,\n chainId,\n );\n this.#cachedStoreKeyIdentity = identity;\n this.#cachedStoreKey = key;\n return key;\n }\n\n async #signDelegated(meta: DelegatedSigningMeta, contractAddresses: Address[]): Promise<Hex> {\n const delegatedEIP712 = await this.#relayer.createDelegatedUserDecryptEIP712(\n meta.publicKey,\n contractAddresses,\n meta.delegatorAddress,\n meta.startTimestamp,\n meta.durationDays,\n );\n return this.signer.signTypedData({\n domain: {\n ...delegatedEIP712.domain,\n chainId: Number(delegatedEIP712.domain.chainId),\n },\n types: delegatedEIP712.types,\n message: {\n ...delegatedEIP712.message,\n startTimestamp: BigInt(delegatedEIP712.message.startTimestamp),\n durationDays: BigInt(delegatedEIP712.message.durationDays),\n },\n });\n }\n}\n","import { getAddress, type Address } from \"viem\";\nimport type { GenericStorage } from \"./token.types\";\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\n\nconst BALANCES_KEY = \"zama:balances\";\n\nexport interface BalanceCachePayload {\n storage: GenericStorage;\n tokenAddress: Address;\n owner: Address;\n handle: Handle;\n}\n\n/**\n * Build a storage key for a cached decrypted balance.\n * The handle is embedded in the key so a new on-chain handle automatically\n * invalidates the cache entry — no TTL needed.\n */\nfunction storageKey(tokenAddress: Address, owner: Address, handle: Handle): string {\n return `zama:balance:${getAddress(tokenAddress)}:${getAddress(owner)}:${handle.toLowerCase()}`;\n}\n\n/**\n * Load a cached decrypted balance, or `null` on cache miss.\n */\nexport async function loadCachedBalance({\n storage,\n tokenAddress,\n owner,\n handle,\n}: BalanceCachePayload): Promise<bigint | null> {\n try {\n const raw = await storage.get<string>(storageKey(tokenAddress, owner, handle));\n return raw !== null ? BigInt(raw) : null;\n } catch (error) {\n console.warn(\"[zama-sdk] Balance cache read failed:\", error);\n return null;\n }\n}\n\n/**\n * Persist a decrypted balance to storage.\n */\nexport async function saveCachedBalance(\n payload: BalanceCachePayload & { value: bigint },\n): Promise<void> {\n const { storage, tokenAddress, owner, handle, value } = payload;\n const key = storageKey(tokenAddress, owner, handle);\n try {\n await storage.set(key, value.toString());\n await trackKey(storage, key);\n } catch {\n // Best-effort — never block the caller.\n }\n}\n\nconst trackKeyChains = new WeakMap<GenericStorage, Promise<void>>();\n\nasync function trackKey(storage: GenericStorage, key: string): Promise<void> {\n // Serialize read-modify-write per storage instance to prevent concurrent\n // saveCachedBalance calls from overwriting each other's key additions.\n const prev = trackKeyChains.get(storage) ?? Promise.resolve();\n const next = prev.then(async () => {\n const raw = await storage.get<string>(BALANCES_KEY);\n const keys: string[] = raw ? JSON.parse(raw) : [];\n if (!keys.includes(key)) {\n keys.push(key);\n await storage.set(BALANCES_KEY, JSON.stringify(keys));\n }\n });\n trackKeyChains.set(\n storage,\n next.catch(() => {}),\n ); // prevent chain poisoning\n return next;\n}\n\n/**\n * Remove all cached decrypted balances from storage.\n * Best-effort — never throws.\n */\nexport async function clearAllCachedBalances(storage: GenericStorage): Promise<void> {\n try {\n const raw = await storage.get<string>(BALANCES_KEY);\n if (!raw) {\n return;\n }\n const keys: string[] = JSON.parse(raw);\n await Promise.all(keys.map((key) => storage.delete(key)));\n await storage.delete(BALANCES_KEY);\n } catch {\n // Best-effort — never block the caller.\n }\n}\n","import { type Address, getAddress } from \"viem\";\nimport {\n ERC7984_INTERFACE_ID,\n ERC7984_WRAPPER_INTERFACE_ID,\n MAX_UINT64,\n allowanceContract,\n confidentialBalanceOfContract,\n decimalsContract,\n getDelegationExpiryContract,\n getWrapperContract,\n nameContract,\n supportsInterfaceContract,\n symbolContract,\n underlyingContract,\n wrapperExistsContract,\n} from \"../contracts\";\nimport type { ZamaSDKEventInput, ZamaSDKEventListener } from \"../events/sdk-events\";\nimport { ZamaSDKEvents } from \"../events/sdk-events\";\nimport type { RelayerSDK } from \"../relayer/relayer-sdk\";\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\nimport { pLimit, toError } from \"../utils\";\nimport { loadCachedBalance, saveCachedBalance } from \"./balance-cache\";\nimport { CredentialsManager } from \"./credentials-manager\";\nimport { DelegatedCredentialsManager } from \"./delegated-credentials-manager\";\nimport {\n ConfigurationError,\n DecryptionFailedError,\n DelegationExpiredError,\n DelegationNotFoundError,\n NoCiphertextError,\n RelayerRequestFailedError,\n SigningFailedError,\n SigningRejectedError,\n} from \"./errors\";\nimport type { GenericSigner, GenericStorage } from \"./token.types\";\n\n/** 32-byte zero handle, used to detect uninitialized encrypted balances. */\nexport const ZERO_HANDLE =\n \"0x0000000000000000000000000000000000000000000000000000000000000000\" as const;\n\n/** Options for {@link ReadonlyToken.batchDecryptBalances}. */\nexport interface BatchDecryptOptions {\n /** Pre-fetched encrypted handles. When omitted, handles are fetched from the chain. */\n handles?: Handle[];\n /** Balance owner address. Defaults to the connected signer. */\n owner?: Address;\n /**\n * Called when decryption fails for a single token. Return a fallback bigint value.\n * When omitted, errors are collected and thrown as an aggregated DecryptionFailedError.\n *\n * @example\n * ```ts\n * // Silent zero (old behavior):\n * onError: () => 0n\n * // Log and use zero:\n * onError: (err, addr) => { console.error(addr, err); return 0n; }\n * ```\n */\n onError?: (error: Error, address: Address) => bigint;\n /** Maximum number of concurrent decrypt calls. Default: `Infinity` (no limit). */\n maxConcurrency?: number;\n}\n\n/** Options for {@link ReadonlyToken.batchDecryptBalancesAs}. */\nexport interface BatchDecryptAsOptions {\n /** The address of the account that delegated decryption rights. */\n delegatorAddress: Address;\n /** Pre-fetched encrypted handles. When omitted, handles are fetched from the chain. */\n handles?: Handle[];\n /** Balance owner address. Defaults to the delegator address. */\n owner?: Address;\n /** Maximum number of concurrent decrypt calls. Default: Infinity. */\n maxConcurrency?: number;\n /** Called when decryption fails for a single token. Return a fallback bigint. */\n onError?: (error: Error, address: Address) => bigint;\n}\n\n/** Configuration for constructing a {@link ReadonlyToken}. */\nexport interface ReadonlyTokenConfig {\n /** FHE relayer backend. */\n relayer: RelayerSDK;\n /** Wallet signer for read calls and credential signing. */\n signer: GenericSigner;\n /** Credential storage backend. */\n storage: GenericStorage;\n /** Session storage for wallet signatures. Shared across all tokens in the same SDK instance. */\n sessionStorage: GenericStorage;\n /** Shared CredentialsManager instance. When provided, storage/sessionStorage/keypairTTL/onEvent are ignored for credential creation. */\n credentials?: CredentialsManager;\n /** Shared DelegatedCredentialsManager instance. When provided, storage/sessionStorage/keypairTTL/onEvent are ignored for delegated credential creation. */\n delegatedCredentials?: DelegatedCredentialsManager;\n /** Address of the confidential token contract. */\n address: Address;\n /** How long the re-encryption keypair remains valid, in seconds. Default: `86400` (1 day). */\n keypairTTL?: number;\n /** Controls session signature lifetime in seconds. Default: `2592000` (30 days). `0` means every operation triggers a signing prompt. `\"infinite\"` means the session never expires. */\n sessionTTL?: number | \"infinite\";\n /** Optional structured event listener for debugging and telemetry. */\n onEvent?: ZamaSDKEventListener;\n}\n\n/**\n * Read-only interface for a confidential token.\n * Supports balance queries, authorization, and ERC-165 checks.\n * Does not require a wrapper address.\n */\nexport class ReadonlyToken {\n protected readonly credentials: CredentialsManager;\n protected readonly delegatedCredentials: DelegatedCredentialsManager;\n protected readonly relayer: RelayerSDK;\n readonly signer: GenericSigner;\n readonly address: Address;\n readonly storage: GenericStorage;\n readonly #onEvent: ZamaSDKEventListener | undefined;\n\n constructor(config: ReadonlyTokenConfig) {\n const credentialsConfig = {\n relayer: config.relayer,\n signer: config.signer,\n storage: config.storage,\n sessionStorage: config.sessionStorage,\n keypairTTL: config.keypairTTL ?? 86400,\n sessionTTL: config.sessionTTL ?? 2592000,\n onEvent: config.onEvent,\n };\n this.credentials = config.credentials ?? new CredentialsManager(credentialsConfig);\n this.delegatedCredentials =\n config.delegatedCredentials ?? new DelegatedCredentialsManager(credentialsConfig);\n this.relayer = config.relayer;\n this.signer = config.signer;\n this.address = getAddress(config.address);\n this.storage = config.storage;\n this.#onEvent = config.onEvent;\n }\n\n /** Emit a structured event (no-op when no listener is registered). */\n protected emit(partial: ZamaSDKEventInput): void {\n this.#onEvent?.({\n ...partial,\n tokenAddress: this.address,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Decrypt and return the plaintext balance for the given owner.\n * Generates FHE credentials automatically if they don't exist.\n *\n * @param owner - Optional balance owner address. Defaults to the connected signer.\n * @returns The decrypted plaintext balance as a bigint.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n *\n * @example\n * ```ts\n * const balance = await token.balanceOf();\n * // or for another address:\n * const balance = await token.balanceOf(\"0xOwner\");\n * ```\n */\n async balanceOf(owner?: Address): Promise<bigint> {\n const ownerAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n const handle = await this.readConfidentialBalanceOf(ownerAddress);\n return this.decryptBalance(handle, ownerAddress);\n }\n\n /**\n * Return the raw encrypted balance handle without decrypting.\n *\n * @param owner - Optional balance owner address. Defaults to the connected signer.\n * @returns The encrypted balance handle as a hex string.\n *\n * @example\n * ```ts\n * const handle = await token.confidentialBalanceOf();\n * ```\n */\n async confidentialBalanceOf(owner?: Address): Promise<Handle> {\n const ownerAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n return this.readConfidentialBalanceOf(ownerAddress);\n }\n\n /**\n * ERC-165 check for {@link ERC7984_INTERFACE_ID} support.\n *\n * @returns `true` if the contract implements the ERC-7984 confidential token interface.\n *\n * @example\n * ```ts\n * if (await token.isConfidential()) {\n * // Token supports encrypted operations\n * }\n * ```\n */\n async isConfidential(): Promise<boolean> {\n const result = await this.signer.readContract(\n supportsInterfaceContract(this.address, ERC7984_INTERFACE_ID),\n );\n return result;\n }\n\n /**\n * ERC-165 check for {@link ERC7984_WRAPPER_INTERFACE_ID} support.\n *\n * @returns `true` if the contract implements the ERC-7984 wrapper interface.\n *\n * @example\n * ```ts\n * if (await token.isWrapper()) {\n * // Token is a confidential wrapper\n * }\n * ```\n */\n async isWrapper(): Promise<boolean> {\n const result = await this.signer.readContract(\n supportsInterfaceContract(this.address, ERC7984_WRAPPER_INTERFACE_ID),\n );\n return result;\n }\n\n /**\n * Decrypt multiple token balances in parallel.\n * When `handles` are provided, decrypts them directly (useful for two-phase\n * polling where handles are already known). When omitted, fetches handles\n * from the chain first.\n *\n * **Error handling:** If a per-token decryption fails and no `onError` callback\n * is provided, errors are collected and thrown as an aggregated\n * `DecryptionFailedError`. When the relayer returns no value for a handle,\n * a `DecryptionFailedError` is thrown for that token (never silently returns `0n`).\n * Pass `onError: () => 0n` to opt into the silent zero behavior.\n *\n * @param tokens - Array of ReadonlyToken instances to decrypt balances for.\n * @param options - Optional configuration for handles, owner, error handling, and concurrency.\n * @returns A Map from token address to decrypted balance.\n * @throws {@link DecryptionFailedError} if any decryption fails and no `onError` callback is provided.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n *\n * @example\n * ```ts\n * // Simple one-shot:\n * const balances = await ReadonlyToken.batchDecryptBalances(tokens);\n *\n * // With pre-fetched handles and error callback:\n * const handles = await Promise.all(tokens.map(t => t.confidentialBalanceOf()));\n * const balances = await ReadonlyToken.batchDecryptBalances(tokens, {\n * handles,\n * onError: (err, addr) => { console.error(addr, err); return 0n; },\n * });\n * ```\n */\n static async batchDecryptBalances(\n tokens: ReadonlyToken[],\n options?: BatchDecryptOptions,\n ): Promise<Map<Address, bigint>> {\n if (tokens.length === 0) {\n return new Map();\n }\n\n const { handles, owner, onError, maxConcurrency } = options ?? {};\n const firstToken = tokens[0]!;\n const sdk = ReadonlyToken.assertSameRelayer(tokens);\n const signerAddress = owner ?? (await firstToken.signer.getAddress());\n\n return ReadonlyToken.#batchDecryptCore({\n tokens,\n handles,\n ownerAddress: signerAddress,\n onError,\n maxConcurrency,\n obtainCreds: (uncachedAddresses) => firstToken.credentials.allow(...uncachedAddresses),\n decrypt: (creds, handle, contractAddress) =>\n sdk.userDecrypt({\n handles: [handle],\n contractAddress,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n }),\n errorPrefix: \"Batch decryption\",\n });\n }\n\n /**\n * Batch decrypt confidential balances as a delegate across multiple tokens.\n * Mirrors {@link batchDecryptBalances} but uses delegated credentials.\n *\n * **Error handling:** If a per-token decryption fails and no `onError` callback\n * is provided, errors are collected and thrown as an aggregated\n * `DecryptionFailedError`. When the relayer returns no value for a handle,\n * a `DecryptionFailedError` is thrown for that token (never silently returns `0n`).\n * Pass `onError: () => 0n` to opt into the silent zero behavior.\n *\n * @param tokens - Array of ReadonlyToken instances to decrypt balances for.\n * @param options - Delegated decryption configuration.\n * @returns A Map from token address to decrypted balance.\n * @throws {@link DelegationNotFoundError} if no active delegation exists from the delegator to the connected signer.\n * @throws {@link DelegationExpiredError} if the delegation has expired.\n * @throws {@link DecryptionFailedError} if any decryption fails and no `onError` callback is provided.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n *\n * @example\n * ```ts\n * const balances = await ReadonlyToken.batchDecryptBalancesAs(tokens, {\n * delegatorAddress: \"0xDelegator\",\n * onError: (err, addr) => { console.error(addr, err); return 0n; },\n * });\n * ```\n */\n static async batchDecryptBalancesAs(\n tokens: ReadonlyToken[],\n options: BatchDecryptAsOptions,\n ): Promise<Map<Address, bigint>> {\n if (tokens.length === 0) {\n return new Map();\n }\n\n const { delegatorAddress, handles, owner, onError, maxConcurrency } = options;\n const ownerAddress = owner ?? delegatorAddress;\n const firstToken = tokens[0]!;\n ReadonlyToken.assertSameRelayer(tokens);\n\n return ReadonlyToken.#batchDecryptCore({\n tokens,\n handles,\n ownerAddress,\n onError,\n maxConcurrency,\n preFlightCheck: () => firstToken.#assertDelegationActive(delegatorAddress),\n obtainCreds: (uncachedAddresses) =>\n firstToken.delegatedCredentials.allow(delegatorAddress, ...uncachedAddresses),\n decrypt: (creds, handle, contractAddress) =>\n firstToken.relayer.delegatedUserDecrypt({\n handles: [handle],\n contractAddress,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n delegatorAddress: creds.delegatorAddress,\n delegateAddress: creds.delegateAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n }),\n errorPrefix: \"Batch delegated decryption\",\n });\n }\n\n static async #batchDecryptCore<TCreds>(config: {\n tokens: ReadonlyToken[];\n handles: Handle[] | undefined;\n ownerAddress: Address;\n onError?: (error: Error, address: Address) => bigint;\n maxConcurrency?: number;\n preFlightCheck?: () => Promise<void>;\n obtainCreds: (uncachedAddresses: Address[]) => Promise<TCreds>;\n decrypt: (\n creds: TCreds,\n handle: Address,\n contractAddress: Address,\n ) => Promise<Record<string, unknown>>;\n errorPrefix: string;\n }): Promise<Map<Address, bigint>> {\n const {\n tokens,\n handles,\n ownerAddress,\n onError,\n maxConcurrency,\n obtainCreds,\n decrypt,\n errorPrefix,\n } = config;\n\n const firstToken = tokens[0]!;\n const resolvedHandles =\n handles ?? (await Promise.all(tokens.map((t) => t.readConfidentialBalanceOf(ownerAddress))));\n\n if (tokens.length !== resolvedHandles.length) {\n throw new DecryptionFailedError(\n `tokens.length (${tokens.length}) must equal handles.length (${resolvedHandles.length})`,\n );\n }\n\n const tokenStorage = firstToken.storage;\n const results = new Map<Address, bigint>();\n\n // Parallel cache lookups — avoids sequential IDB round-trips.\n const uncached: { token: ReadonlyToken; handle: Address }[] = [];\n const cachedValues = await Promise.all(\n tokens.map((token, i) => {\n const handle = resolvedHandles[i]!;\n if (token.isZeroHandle(handle)) {\n return 0n;\n }\n return loadCachedBalance({\n storage: tokenStorage,\n tokenAddress: token.address,\n owner: ownerAddress,\n handle,\n });\n }),\n );\n\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i]!;\n const handle = resolvedHandles[i]!;\n const cached = cachedValues[i];\n\n if (cached !== null && cached !== undefined) {\n results.set(token.address, cached);\n continue;\n }\n\n uncached.push({ token, handle });\n }\n\n // All balances resolved from cache — no credentials needed.\n if (uncached.length === 0) {\n return results;\n }\n\n // Pre-flight check runs after cache lookups — skips RPC overhead when\n // all balances are cached. Best-effort: checks the first token's contract\n // only (delegations are typically granted per-delegator, not per-token).\n if (config.preFlightCheck) {\n await config.preFlightCheck();\n }\n\n const uncachedAddresses = uncached.map((entry) => entry.token.address);\n const creds = await obtainCreds(uncachedAddresses);\n\n const errors: { address: Address; error: Error }[] = [];\n const decryptFns: (() => Promise<void>)[] = [];\n\n for (const { token, handle } of uncached) {\n decryptFns.push(() =>\n decrypt(creds, handle, token.address)\n .then(async (result) => {\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(\n `${errorPrefix} returned no value for handle ${handle} on token ${token.address}`,\n );\n }\n results.set(token.address, value);\n try {\n await saveCachedBalance({\n storage: tokenStorage,\n tokenAddress: token.address,\n owner: ownerAddress,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n })\n .catch((error) => {\n const err = toError(error);\n if (onError) {\n try {\n results.set(token.address, onError(err, token.address));\n } catch (callbackError) {\n errors.push({\n address: token.address,\n error: toError(callbackError),\n });\n }\n } else {\n errors.push({ address: token.address, error: err });\n }\n }),\n );\n }\n\n await pLimit(decryptFns, maxConcurrency);\n\n if (errors.length > 0) {\n const message = errors.map((e) => `${e.address}: ${e.error.message}`).join(\"; \");\n throw new DecryptionFailedError(\n `${errorPrefix} failed for ${errors.length} token(s): ${message}`,\n );\n }\n\n return results;\n }\n\n /**\n * Look up the wrapper contract for this token via the deployment coordinator.\n * Returns `null` if no wrapper is deployed.\n *\n * @param coordinatorAddress - The deployment coordinator contract address.\n * @returns The wrapper address, or `null` if no wrapper exists.\n *\n * @example\n * ```ts\n * const wrapper = await token.discoverWrapper(\"0xCoordinator\");\n * if (wrapper) {\n * const fullToken = sdk.createToken(token.address, wrapper);\n * }\n * ```\n */\n async discoverWrapper(coordinatorAddress: Address): Promise<Address | null> {\n const coordinator = getAddress(coordinatorAddress);\n const exists = await this.signer.readContract(wrapperExistsContract(coordinator, this.address));\n if (!exists) {\n return null;\n }\n return this.signer.readContract(getWrapperContract(coordinator, this.address));\n }\n\n /**\n * Read the underlying ERC-20 address from this token's wrapper contract.\n *\n * @returns The underlying ERC-20 token address.\n *\n * @example\n * ```ts\n * const underlying = await token.underlyingToken();\n * ```\n */\n async underlyingToken(): Promise<Address> {\n return this.signer.readContract(underlyingContract(this.address));\n }\n\n /**\n * Read the ERC-20 allowance of the underlying token for a given wrapper.\n *\n * @param wrapper - The wrapper contract address to check allowance for.\n * @param owner - Optional owner address. Defaults to the connected signer.\n * @returns The current allowance as a bigint.\n *\n * @example\n * ```ts\n * const allowance = await token.allowance(\"0xWrapper\");\n * ```\n */\n async allowance(wrapper: Address, owner?: Address): Promise<bigint> {\n const normalizedWrapper = getAddress(wrapper);\n const underlying = await this.signer.readContract(underlyingContract(normalizedWrapper));\n const userAddress = owner ? getAddress(owner) : await this.signer.getAddress();\n return this.signer.readContract(allowanceContract(underlying, userAddress, normalizedWrapper));\n }\n\n /**\n * Read the token name from the contract.\n *\n * @returns The token name string.\n *\n * @example\n * ```ts\n * const name = await token.name(); // \"Wrapped USDC\"\n * ```\n */\n async name(): Promise<string> {\n return this.signer.readContract(nameContract(this.address));\n }\n\n /**\n * Read the token symbol from the contract.\n *\n * @returns The token symbol string.\n *\n * @example\n * ```ts\n * const symbol = await token.symbol(); // \"cUSDC\"\n * ```\n */\n async symbol(): Promise<string> {\n return this.signer.readContract(symbolContract(this.address));\n }\n\n /**\n * Read the token decimals from the contract.\n *\n * @returns The number of decimals.\n *\n * @example\n * ```ts\n * const decimals = await token.decimals(); // 6\n * ```\n */\n async decimals(): Promise<number> {\n return this.signer.readContract(decimalsContract(this.address));\n }\n\n /**\n * Ensure FHE decrypt credentials exist for this token.\n * Generates a keypair and requests an EIP-712 signature if needed.\n * Call this before any decrypt operation to avoid mid-flow wallet prompts.\n *\n * @returns Resolves when credentials are cached.\n *\n * @example\n * ```ts\n * await token.allow();\n * // Credentials are now cached — subsequent decrypts won't prompt\n * const balance = await token.balanceOf();\n * ```\n */\n async allow(): Promise<void> {\n await this.credentials.allow(this.address);\n }\n\n /**\n * Whether a session signature is currently cached for the connected wallet.\n * Use this to check if decrypt operations can proceed without a wallet prompt.\n */\n async isAllowed(): Promise<boolean> {\n return this.credentials.isAllowed();\n }\n\n /**\n * Revoke the session signature for the connected wallet.\n * Stored credentials remain intact, but the next decrypt operation\n * will require a fresh wallet signature.\n */\n async revoke(...contractAddresses: Address[]): Promise<void> {\n await this.credentials.revoke(...contractAddresses);\n }\n\n /**\n * Ensure FHE decrypt credentials exist for all given tokens in a single\n * wallet signature. Call this early (e.g. after loading the token list) so\n * that subsequent individual decrypt operations reuse cached credentials.\n *\n * @param tokens - Array of ReadonlyToken instances to allow.\n * @returns Resolves when all credentials are cached.\n *\n * @example\n * ```ts\n * const tokens = addresses.map(a => sdk.createReadonlyToken(a));\n * await ReadonlyToken.allow(...tokens);\n * // All tokens now share the same credentials\n * ```\n */\n static async allow(...tokens: ReadonlyToken[]): Promise<void> {\n if (tokens.length === 0) {\n return;\n }\n const allAddresses = tokens.map((t) => t.address);\n await tokens[0]!.credentials.allow(...allAddresses);\n }\n\n protected async getAclAddress(): Promise<Address> {\n return this.relayer.getAclAddress();\n }\n\n /**\n * Check whether a delegation is active for this token's contract address.\n *\n * @param delegatorAddress - The address that granted the delegation.\n * @param delegateAddress - The address that received delegation rights.\n * @returns `true` if the delegation exists and has not expired.\n */\n async isDelegated(params: {\n delegatorAddress: Address;\n delegateAddress: Address;\n }): Promise<boolean> {\n const expiry = await this.getDelegationExpiry(params);\n if (expiry === 0n) {\n return false;\n }\n // Permanent delegation (uint64 max) — skip the RPC round-trip for block timestamp.\n if (expiry === MAX_UINT64) {\n return true;\n }\n const now = await this.signer.getBlockTimestamp();\n return expiry > now;\n }\n\n /**\n * Get the expiration timestamp of a delegation for this token.\n *\n * @param delegatorAddress - The address that granted the delegation.\n * @param delegateAddress - The address that received delegation rights.\n * @returns Unix timestamp as bigint. `0n` = no delegation. `2^64 - 1` = permanent.\n */\n async getDelegationExpiry({\n delegatorAddress,\n delegateAddress,\n }: {\n delegatorAddress: Address;\n delegateAddress: Address;\n }): Promise<bigint> {\n const acl = await this.getAclAddress();\n return this.signer.readContract(\n getDelegationExpiryContract(\n acl,\n getAddress(delegatorAddress),\n getAddress(delegateAddress),\n this.address,\n ),\n );\n }\n\n /**\n * Throws if there is no active delegation from `delegatorAddress` to the\n * connected signer for this token contract.\n */\n async #assertDelegationActive(delegatorAddress: Address): Promise<void> {\n const delegateAddress = await this.signer.getAddress();\n const expiry = await this.getDelegationExpiry({\n delegatorAddress,\n delegateAddress,\n });\n if (expiry === 0n) {\n throw new DelegationNotFoundError(\n `No active delegation from ${delegatorAddress} to ${delegateAddress} for ${this.address}`,\n );\n }\n if (expiry !== MAX_UINT64) {\n const now = await this.signer.getBlockTimestamp();\n if (expiry <= now) {\n throw new DelegationExpiredError(\n `Delegation from ${delegatorAddress} to ${delegateAddress} for ${this.address} has expired`,\n );\n }\n }\n }\n\n protected async readConfidentialBalanceOf(owner: Address): Promise<Handle> {\n return (await this.signer.readContract(\n confidentialBalanceOfContract(this.address, owner),\n )) as Handle;\n }\n\n isZeroHandle(handle: string): handle is typeof ZERO_HANDLE | `0x` {\n return handle === ZERO_HANDLE || handle === \"0x\";\n }\n\n /**\n * Decrypt the balance of a delegator using delegated decryption credentials.\n * The connected signer acts as the delegate who has been granted permission\n * by the delegator to decrypt their balance.\n *\n * Decrypted values are cached in storage keyed by `(token, owner, handle)`.\n * Cache write failures are silently ignored — they do not affect the returned value.\n *\n * @param delegatorAddress - The address of the account that delegated decryption rights.\n * @param owner - Optional balance owner address. Defaults to the delegator address.\n * @returns The decrypted plaintext balance as a bigint.\n * @throws {@link DelegationNotFoundError} if no active delegation exists from the delegator to the connected signer.\n * @throws {@link DelegationExpiredError} if the delegation has expired.\n * @throws {@link DecryptionFailedError} if delegated decryption fails or the relayer returns no value.\n * @throws {@link SigningRejectedError} if the user rejects the wallet signature prompt.\n * @throws {@link SigningFailedError} if the signing operation fails.\n * @throws {@link NoCiphertextError} if the relayer returns HTTP 400 (no ciphertext for this account).\n * @throws {@link RelayerRequestFailedError} if the relayer returns a non-400 HTTP error.\n *\n * @example\n * ```ts\n * const balance = await token.decryptBalanceAs({\n * delegatorAddress: \"0xDelegator\",\n * });\n * ```\n */\n async decryptBalanceAs({\n delegatorAddress,\n owner,\n }: {\n delegatorAddress: Address;\n owner?: Address;\n }): Promise<bigint> {\n const normalizedDelegator = getAddress(delegatorAddress);\n const normalizedOwner = owner ? getAddress(owner) : normalizedDelegator;\n\n const handle = await this.readConfidentialBalanceOf(normalizedOwner);\n if (this.isZeroHandle(handle)) {\n return 0n;\n }\n\n // Check persistent cache keyed by (token, owner, handle).\n // When the on-chain balance changes the encrypted handle changes too,\n // so stale entries are never served — no TTL needed.\n const cached = await loadCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: normalizedOwner,\n handle,\n });\n if (cached !== null) {\n return cached;\n }\n\n // Pre-flight delegation check — avoids wasting a wallet signature on an\n // expired or non-existent delegation.\n await this.#assertDelegationActive(normalizedDelegator);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n\n const creds = await this.delegatedCredentials.allow(normalizedDelegator, this.address);\n\n const result = await this.relayer.delegatedUserDecrypt({\n handles: [handle],\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n delegatorAddress: creds.delegatorAddress,\n delegateAddress: creds.delegateAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(\n `Delegated decryption returned no value for handle ${handle}`,\n );\n }\n\n try {\n await saveCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: normalizedOwner,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n\n return value;\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt delegated balance\");\n }\n }\n\n /**\n * Decrypt a single encrypted handle into a plaintext bigint.\n * Returns `0n` for zero handles without calling the relayer.\n *\n * @param handle - The encrypted balance handle to decrypt.\n * @param owner - Optional owner address for the decrypt request.\n * @returns The decrypted plaintext value as a bigint.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n *\n * @example\n * ```ts\n * const handle = await token.confidentialBalanceOf();\n * const value = await token.decryptBalance(handle);\n * ```\n */\n async decryptBalance(handle: Handle, owner?: Address): Promise<bigint> {\n if (this.isZeroHandle(handle)) {\n return 0n;\n }\n\n const signerAddress = owner ?? (await this.signer.getAddress());\n\n // Check persistent cache — avoids the 2–5 s decrypt spinner on reload.\n const cached = await loadCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: signerAddress,\n handle,\n });\n if (cached !== null) {\n return cached;\n }\n\n const creds = await this.credentials.allow(this.address);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n const result = await this.relayer.userDecrypt({\n handles: [handle],\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress,\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n const value = result[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(`Decryption returned no value for handle ${handle}`);\n }\n try {\n await saveCachedBalance({\n storage: this.storage,\n tokenAddress: this.address,\n owner: signerAddress,\n handle,\n value,\n });\n } catch {\n // Cache write failure should not invalidate a successful decryption\n }\n return value;\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt balance\");\n }\n }\n\n /**\n * Batch-decrypt arbitrary encrypted handles in a single relayer call.\n * Zero handles are returned as 0n without hitting the relayer.\n *\n * @param handles - Array of encrypted handles to decrypt.\n * @param owner - Optional owner address for the decrypt request.\n * @returns A Map from handle to decrypted bigint value.\n * @throws {@link DecryptionFailedError} if FHE decryption fails.\n */\n async decryptHandles(handles: Handle[], owner?: Address): Promise<Map<Handle, bigint>> {\n const results = new Map<Handle, bigint>();\n const nonZeroHandles: Handle[] = [];\n\n for (const handle of handles) {\n if (this.isZeroHandle(handle)) {\n results.set(handle, 0n);\n } else {\n nonZeroHandles.push(handle);\n }\n }\n\n if (nonZeroHandles.length === 0) {\n return results;\n }\n\n const creds = await this.credentials.allow(this.address);\n\n const t0 = Date.now();\n try {\n this.emit({ type: ZamaSDKEvents.DecryptStart });\n const decrypted = await this.relayer.userDecrypt({\n handles: nonZeroHandles,\n contractAddress: this.address,\n signedContractAddresses: creds.contractAddresses,\n privateKey: creds.privateKey,\n publicKey: creds.publicKey,\n signature: creds.signature,\n signerAddress: owner ?? (await this.signer.getAddress()),\n startTimestamp: creds.startTimestamp,\n durationDays: creds.durationDays,\n });\n this.emit({\n type: ZamaSDKEvents.DecryptEnd,\n durationMs: Date.now() - t0,\n });\n\n for (const handle of nonZeroHandles) {\n const value = decrypted[handle] as bigint | undefined;\n if (value === undefined) {\n throw new DecryptionFailedError(`Decryption returned no value for handle ${handle}`);\n }\n results.set(handle, value);\n }\n } catch (error) {\n this.emit({\n type: ZamaSDKEvents.DecryptError,\n error: toError(error),\n durationMs: Date.now() - t0,\n });\n throw wrapDecryptError(error, \"Failed to decrypt handles\");\n }\n\n return results;\n }\n\n /** Verify all tokens share the same relayer and return it. */\n private static assertSameRelayer(tokens: ReadonlyToken[]): RelayerSDK {\n const relayer = tokens[0]!.relayer;\n for (let i = 1; i < tokens.length; i++) {\n if (tokens[i]!.relayer !== relayer) {\n throw new ConfigurationError(\n \"All tokens in a batch operation must share the same relayer instance\",\n );\n }\n }\n return relayer;\n }\n}\n\n/**\n * Inspect a caught error for an HTTP status code and return the appropriate\n * typed SDK error (NoCiphertextError for 400, RelayerRequestFailedError for\n * other HTTP errors, or the generic DecryptionFailedError as fallback).\n */\nfunction wrapDecryptError(error: unknown, fallbackMessage: string): Error {\n if (\n error instanceof DecryptionFailedError ||\n error instanceof NoCiphertextError ||\n error instanceof RelayerRequestFailedError ||\n error instanceof SigningRejectedError ||\n error instanceof SigningFailedError\n ) {\n return error;\n }\n\n const statusCode =\n error !== null &&\n error !== undefined &&\n typeof error === \"object\" &&\n \"statusCode\" in error &&\n typeof (error as Record<string, unknown>).statusCode === \"number\"\n ? ((error as Record<string, unknown>).statusCode as number)\n : undefined;\n\n if (statusCode === 400) {\n return new NoCiphertextError(\n error instanceof Error ? error.message : \"No ciphertext for this account\",\n { cause: error },\n );\n }\n\n if (statusCode !== undefined) {\n return new RelayerRequestFailedError(\n error instanceof Error ? error.message : fallbackMessage,\n statusCode,\n { cause: error },\n );\n }\n\n return new DecryptionFailedError(fallbackMessage, {\n cause: error,\n });\n}\n","/**\n * Framework-agnostic event decoders for confidential token contracts.\n * Works with raw log data from any provider.\n */\n\nimport type { Handle } from \"../relayer/relayer-sdk.types\";\nimport { getAddress, type Address, type Hex } from \"viem\";\nimport { prefixHex } from \"../utils\";\n\n// ---------------------------------------------------------------------------\n// Generic log shape\n// ---------------------------------------------------------------------------\n\n/** Framework-agnostic log shape compatible with any Ethereum provider. */\nexport interface RawLog {\n /** Indexed event topics (topic[0] is the event signature hash). */\n readonly topics: readonly Hex[];\n /** ABI-encoded non-indexed event data. */\n readonly data: Hex;\n}\n\n// ---------------------------------------------------------------------------\n// Event topic0 constants (keccak256 of canonical signature)\n// ---------------------------------------------------------------------------\n\n/**\n * Event topic0 constants (keccak256 of the canonical Solidity signature).\n * Pass to `getLogs({ topics: [Object.values(Topics)] })` to fetch all events.\n */\nexport const Topics = {\n /** `ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)` */\n ConfidentialTransfer: \"0x67500e8d0ed826d2194f514dd0d8124f35648ab6e3fb5e6ed867134cffe661e9\",\n /** `Wrapped(uint64 mintAmount, uint256 amountIn, uint256 feeAmount, address indexed to_, uint256 indexed mintTxId)` */\n Wrapped: \"0x1f7907f4d84043abe0fb7c74e8865ee5fe93fe4f691c54a7b8fa9d6fb17c7cba\",\n /** `UnwrapRequested(address indexed receiver, bytes32 amount)` */\n UnwrapRequested: \"0x77d02d353c5629272875d11f1b34ec4c65d7430b075575b78cd2502034c469ee\",\n /** `UnwrappedFinalized(bytes32 indexed burntAmountHandle, ...)` */\n UnwrappedFinalized: \"0xc64e7c81b18b674fc5b037d8a0041bfe3332d86c780a4688f404ee01fbabb152\",\n /** `UnwrappedStarted(bool returnVal, uint256 indexed requestId, ...)` */\n UnwrappedStarted: \"0x3838891d4843c6d7f9f494570b6fd8843f4e3c3ddb817c1411760bd31b819806\",\n} as const;\n\n// ---------------------------------------------------------------------------\n// Typed event interfaces\n// ---------------------------------------------------------------------------\n\n/** Decoded `ConfidentialTransfer` event — an encrypted token transfer. */\nexport interface ConfidentialTransferEvent {\n readonly eventName: \"ConfidentialTransfer\";\n /** Sender address. */\n readonly from: Address;\n /** Receiver address. */\n readonly to: Address;\n /** FHE ciphertext handle for the transferred amount. */\n readonly encryptedAmountHandle: Handle;\n}\n\n/** Decoded `Wrapped` event — an ERC-20 shield (wrap) operation. */\nexport interface WrappedEvent {\n readonly eventName: \"Wrapped\";\n /** Confidential tokens minted. */\n readonly mintAmount: bigint;\n /** Underlying ERC-20 tokens deposited. */\n readonly amountIn: bigint;\n /** Fee deducted during wrapping. */\n readonly feeAmount: bigint;\n /** Receiver of the minted confidential tokens. */\n readonly to: Address;\n /** On-chain mint transaction ID. */\n readonly mintTxId: bigint;\n}\n\n/** Decoded `UnwrapRequested` event — an unshield request submitted. */\nexport interface UnwrapRequestedEvent {\n readonly eventName: \"UnwrapRequested\";\n /** Address that will receive the unwrapped ERC-20 tokens. */\n readonly receiver: Address;\n /** FHE ciphertext handle for the requested unshield amount. */\n readonly encryptedAmount: Handle;\n}\n\n/** Decoded `UnwrappedFinalized` event — an unshield completed on-chain. */\nexport interface UnwrappedFinalizedEvent {\n readonly eventName: \"UnwrappedFinalized\";\n /** FHE handle of the burnt confidential balance. */\n readonly burntAmountHandle: Handle;\n /** Whether the finalization succeeded. */\n readonly finalizeSuccess: boolean;\n /** Whether the fee transfer succeeded. */\n readonly feeTransferSuccess: boolean;\n /** Amount of confidential tokens burnt. */\n readonly burnAmount: bigint;\n /** Amount of underlying ERC-20 tokens returned. */\n readonly unwrapAmount: bigint;\n /** Fee deducted during unwrapping. */\n readonly feeAmount: bigint;\n /** Next on-chain transaction ID. */\n readonly nextTxId: bigint;\n}\n\n/** Decoded `UnwrappedStarted` event — the relayer began processing an unshield. */\nexport interface UnwrappedStartedEvent {\n readonly eventName: \"UnwrappedStarted\";\n /** Whether the unwrap start succeeded. */\n readonly returnVal: boolean;\n /** On-chain request ID. */\n readonly requestId: bigint;\n /** On-chain transaction ID. */\n readonly txId: bigint;\n /** Receiver address. */\n readonly to: Address;\n /** Refund address (if applicable). */\n readonly refund: Address;\n /** FHE handle of the requested amount. */\n readonly requestedAmount: Handle;\n /** FHE handle of the burn amount. */\n readonly burnAmount: Handle;\n}\n\n/** Union of all decoded confidential token event types. */\nexport type OnChainEvent =\n | ConfidentialTransferEvent\n | WrappedEvent\n | UnwrapRequestedEvent\n | UnwrappedFinalizedEvent\n | UnwrappedStartedEvent;\n\n// ---------------------------------------------------------------------------\n// ABI decoding helpers (no external deps)\n// ---------------------------------------------------------------------------\n\nfunction topicToAddress(topic: Hex): Address {\n return getAddress(prefixHex(topic.slice(-40)));\n}\n\nfunction topicToBigInt(topic: Hex): bigint {\n return BigInt(topic);\n}\n\nfunction topicToBytes32(topic: Hex): Handle {\n // EVM topics are already 32-byte 0x-prefixed hex — cast directly\n return topic as Handle;\n}\n\nfunction wordAt(data: Hex, index: number): string {\n // data starts with \"0x\", each word is 64 hex chars (32 bytes)\n const start = 2 + index * 64;\n const word = data.slice(start, start + 64);\n return word.length === 64 ? word : word.padEnd(64, \"0\");\n}\n\nfunction wordToAddress(data: Hex, index: number): Address {\n return getAddress(prefixHex(wordAt(data, index).slice(-40)));\n}\n\nfunction wordToBigInt(data: Hex, index: number): bigint {\n return BigInt(\"0x\" + wordAt(data, index));\n}\n\nfunction wordToBool(data: Hex, index: number): boolean {\n return BigInt(\"0x\" + wordAt(data, index)) !== 0n;\n}\n\nfunction wordToBytes32(data: Hex, index: number): Handle {\n // wordAt returns exactly 64 hex chars — prefix and cast directly\n return prefixHex(wordAt(data, index)) as Handle;\n}\n\n// ---------------------------------------------------------------------------\n// Individual decoders\n// ---------------------------------------------------------------------------\n\n/**\n * ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)\n * All 3 params indexed → topics[1..3], no data.\n */\nexport function decodeConfidentialTransfer(log: RawLog): ConfidentialTransferEvent | null {\n if (log.topics[0] !== Topics.ConfidentialTransfer) {\n return null;\n }\n if (log.topics.length < 4) {\n return null;\n }\n\n return {\n eventName: \"ConfidentialTransfer\",\n from: topicToAddress(log.topics[1]!),\n to: topicToAddress(log.topics[2]!),\n encryptedAmountHandle: topicToBytes32(log.topics[3]!),\n };\n}\n\n/**\n * Wrapped(uint64 mintAmount, uint256 amountIn, uint256 feeAmount, address indexed to_, uint256 indexed mintTxId)\n * Indexed: to_ (topics[1]), mintTxId (topics[2])\n * Data: mintAmount (uint64, abi-encoded as uint256), amountIn, feeAmount\n */\nexport function decodeWrapped(log: RawLog): WrappedEvent | null {\n if (log.topics[0] !== Topics.Wrapped) {\n return null;\n }\n if (log.topics.length < 3) {\n return null;\n }\n\n return {\n eventName: \"Wrapped\",\n to: topicToAddress(log.topics[1]!),\n mintTxId: topicToBigInt(log.topics[2]!),\n mintAmount: wordToBigInt(log.data, 0),\n amountIn: wordToBigInt(log.data, 1),\n feeAmount: wordToBigInt(log.data, 2),\n };\n}\n\n/**\n * UnwrapRequested(address indexed receiver, bytes32 amount)\n * Indexed: receiver (topics[1])\n * Data: amount (bytes32)\n */\nexport function decodeUnwrapRequested(log: RawLog): UnwrapRequestedEvent | null {\n if (log.topics[0] !== Topics.UnwrapRequested) {\n return null;\n }\n if (log.topics.length < 2) {\n return null;\n }\n\n return {\n eventName: \"UnwrapRequested\",\n receiver: topicToAddress(log.topics[1]!),\n encryptedAmount: wordToBytes32(log.data, 0),\n };\n}\n\n/**\n * UnwrappedFinalized(bytes32 indexed burntAmountHandle, bool finalizeSuccess, bool feeTransferSuccess,\n * uint64 burnAmount, uint256 unwrapAmount, uint256 feeAmount, uint256 indexed nextTxId)\n * Indexed: burntAmountHandle (topics[1]), nextTxId (topics[2])\n * Data: finalizeSuccess, feeTransferSuccess, burnAmount, unwrapAmount, feeAmount\n */\nexport function decodeUnwrappedFinalized(log: RawLog): UnwrappedFinalizedEvent | null {\n if (log.topics[0] !== Topics.UnwrappedFinalized) {\n return null;\n }\n if (log.topics.length < 3) {\n return null;\n }\n\n return {\n eventName: \"UnwrappedFinalized\",\n burntAmountHandle: topicToBytes32(log.topics[1]!),\n nextTxId: topicToBigInt(log.topics[2]!),\n finalizeSuccess: wordToBool(log.data, 0),\n feeTransferSuccess: wordToBool(log.data, 1),\n burnAmount: wordToBigInt(log.data, 2),\n unwrapAmount: wordToBigInt(log.data, 3),\n feeAmount: wordToBigInt(log.data, 4),\n };\n}\n\n/**\n * UnwrappedStarted(bool returnVal, uint256 indexed requestId, uint256 indexed txId, address indexed to,\n * address refund, bytes32 requestedAmount, bytes32 burnAmount)\n * Indexed: requestId (topics[1]), txId (topics[2]), to (topics[3])\n * Data: returnVal, refund, requestedAmount, burnAmount\n */\nexport function decodeUnwrappedStarted(log: RawLog): UnwrappedStartedEvent | null {\n if (log.topics[0] !== Topics.UnwrappedStarted) {\n return null;\n }\n if (log.topics.length < 4) {\n return null;\n }\n\n return {\n eventName: \"UnwrappedStarted\",\n requestId: topicToBigInt(log.topics[1]!),\n txId: topicToBigInt(log.topics[2]!),\n to: topicToAddress(log.topics[3]!),\n returnVal: wordToBool(log.data, 0),\n refund: wordToAddress(log.data, 1),\n requestedAmount: wordToBytes32(log.data, 2),\n burnAmount: wordToBytes32(log.data, 3),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Convenience helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Try all decoders on a single log and return the first match, or `null`.\n *\n * @example\n * ```ts\n * const event = decodeOnChainEvent(log);\n * if (event?.eventName === \"ConfidentialTransfer\") {\n * console.log(event.from, event.to);\n * }\n * ```\n */\nexport function decodeOnChainEvent(log: RawLog): OnChainEvent | null {\n return (\n decodeConfidentialTransfer(log) ??\n decodeWrapped(log) ??\n decodeUnwrapRequested(log) ??\n decodeUnwrappedFinalized(log) ??\n decodeUnwrappedStarted(log)\n );\n}\n\n/**\n * Batch-decode an array of logs, skipping unrecognized entries.\n *\n * @example\n * ```ts\n * const events = decodeOnChainEvents(receipt.logs);\n * ```\n */\nexport function decodeOnChainEvents(logs: readonly RawLog[]): OnChainEvent[] {\n const events: OnChainEvent[] = [];\n for (const log of logs) {\n const event = decodeOnChainEvent(log);\n if (event) {\n events.push(event);\n }\n }\n return events;\n}\n\n/**\n * Find the first {@link UnwrapRequestedEvent} in a logs array.\n *\n * @example\n * ```ts\n * const event = findUnwrapRequested(receipt.logs);\n * if (event) console.log(event.receiver, event.encryptedAmount);\n * ```\n */\nexport function findUnwrapRequested(logs: readonly RawLog[]): UnwrapRequestedEvent | null {\n for (const log of logs) {\n const event = decodeUnwrapRequested(log);\n if (event) {\n return event;\n }\n }\n return null;\n}\n\n/**\n * Find the first {@link WrappedEvent} in a logs array.\n *\n * @example\n * ```ts\n * const event = findWrapped(receipt.logs);\n * if (event) console.log(event.to, event.amountIn);\n * ```\n */\nexport function findWrapped(logs: readonly RawLog[]): WrappedEvent | null {\n for (const log of logs) {\n const event = decodeWrapped(log);\n if (event) {\n return event;\n }\n }\n return null;\n}\n\n/**\n * All 5 confidential token event topic0 hashes.\n * Pass to `getLogs({ topics: [TOKEN_TOPICS] })` to fetch\n * all confidential token events in a single RPC call.\n */\nexport const TOKEN_TOPICS = [\n Topics.ConfidentialTransfer,\n Topics.Wrapped,\n Topics.UnwrapRequested,\n Topics.UnwrappedFinalized,\n Topics.UnwrappedStarted,\n] as const;\n","/**\n * Higher-level activity feed helpers for confidential token events.\n * Normalizes all 5 event types into a single renderable \"activity item\",\n * classifies direction (incoming/outgoing/self) relative to the user,\n * and supports batch-decryption of encrypted transfer amounts.\n *\n * Pure functions, no framework dependency — works with any provider.\n */\n\nimport {\n decodeOnChainEvent,\n type RawLog,\n type OnChainEvent,\n type ConfidentialTransferEvent,\n type WrappedEvent,\n type UnwrapRequestedEvent,\n type UnwrappedFinalizedEvent,\n type UnwrappedStartedEvent,\n} from \"./events/onchain-events\";\nimport type { Address, Hex } from \"viem\";\nimport type { Handle } from \"./relayer/relayer-sdk.types\";\nimport { ZERO_HANDLE } from \"./token/readonly-token\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Direction of an activity item relative to the connected wallet. */\nexport type ActivityDirection = \"incoming\" | \"outgoing\" | \"self\";\n\n/** Classified type of a token activity event. */\nexport type ActivityType =\n | \"transfer\"\n | \"shield\"\n | \"unshield_requested\"\n | \"unshield_started\"\n | \"unshield_finalized\";\n\n/** Amount attached to an activity item — either cleartext or encrypted (with optional decrypted value). */\nexport type ActivityAmount =\n | { readonly type: \"clear\"; readonly value: bigint }\n | {\n readonly type: \"encrypted\";\n readonly handle: Handle;\n /** Populated after batch decryption via {@link applyDecryptedValues}. */\n readonly decryptedValue?: bigint;\n };\n\n/** On-chain metadata attached to each activity item. */\nexport interface ActivityLogMetadata {\n /** Transaction hash containing this event. */\n readonly transactionHash?: Hex;\n /** Block number where this event was emitted. */\n readonly blockNumber?: bigint | number;\n /** Log index within the transaction. */\n readonly logIndex?: number;\n}\n\n/**\n * A single renderable activity feed entry.\n * Produced by {@link parseActivityFeed} from raw event logs.\n */\nexport interface ActivityItem {\n /** Classified event type. */\n readonly type: ActivityType;\n /** Direction relative to the connected wallet. */\n readonly direction: ActivityDirection;\n /** Transfer amount (clear or encrypted). */\n readonly amount: ActivityAmount;\n /** Sender address (if applicable). */\n readonly from?: Address;\n /** Receiver address (if applicable). */\n readonly to?: Address;\n /** Fee deducted (for shield/unshield events). */\n readonly fee?: bigint;\n /** Whether the on-chain operation succeeded (for unshield events). */\n readonly success?: boolean;\n /** On-chain metadata (tx hash, block number, log index). */\n readonly metadata: ActivityLogMetadata;\n /** The original decoded event. */\n readonly rawEvent: OnChainEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction addressesEqual(a: Address, b: Address): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n\nfunction classifyDirection(\n userAddress: Address,\n from: Address | undefined,\n to: Address | undefined,\n): ActivityDirection {\n const isFrom = from !== undefined && addressesEqual(userAddress, from);\n const isTo = to !== undefined && addressesEqual(userAddress, to);\n\n if (isFrom && isTo) {\n return \"self\";\n }\n if (isFrom) {\n return \"outgoing\";\n }\n return \"incoming\";\n}\n\nfunction eventToActivityItem(\n event: OnChainEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n switch (event.eventName) {\n case \"ConfidentialTransfer\":\n return buildTransfer(event, userAddress, metadata);\n case \"Wrapped\":\n return buildShield(event, userAddress, metadata);\n case \"UnwrapRequested\":\n return buildUnshieldRequested(event, userAddress, metadata);\n case \"UnwrappedStarted\":\n return buildUnshieldStarted(event, userAddress, metadata);\n case \"UnwrappedFinalized\":\n return buildUnshieldFinalized(event, metadata);\n }\n}\n\nfunction buildTransfer(\n event: ConfidentialTransferEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"transfer\",\n direction: classifyDirection(userAddress, event.from, event.to),\n amount: { type: \"encrypted\", handle: event.encryptedAmountHandle },\n from: event.from,\n to: event.to,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildShield(\n event: WrappedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"shield\",\n direction: classifyDirection(userAddress, undefined, event.to),\n amount: { type: \"clear\", value: event.amountIn },\n to: event.to,\n fee: event.feeAmount,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldRequested(\n event: UnwrapRequestedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_requested\",\n direction: classifyDirection(userAddress, undefined, event.receiver),\n amount: { type: \"encrypted\", handle: event.encryptedAmount },\n to: event.receiver,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldStarted(\n event: UnwrappedStartedEvent,\n userAddress: Address,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_started\",\n direction: classifyDirection(userAddress, undefined, event.to),\n amount: { type: \"encrypted\", handle: event.requestedAmount },\n to: event.to,\n success: event.returnVal,\n metadata,\n rawEvent: event,\n };\n}\n\nfunction buildUnshieldFinalized(\n event: UnwrappedFinalizedEvent,\n metadata: ActivityLogMetadata,\n): ActivityItem {\n return {\n type: \"unshield_finalized\",\n // Finalized events don't carry from/to addresses, always treat as incoming\n direction: \"incoming\",\n amount: { type: \"clear\", value: event.unwrapAmount },\n fee: event.feeAmount,\n success: event.finalizeSuccess,\n metadata,\n rawEvent: event,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Decode raw logs into classified activity items.\n * Skips logs that don't match any confidential token event.\n */\nexport function parseActivityFeed(\n logs: readonly (RawLog & Partial<ActivityLogMetadata>)[],\n userAddress: Address,\n): ActivityItem[] {\n const items: ActivityItem[] = [];\n for (const log of logs) {\n const event = decodeOnChainEvent(log);\n if (!event) {\n continue;\n }\n\n const metadata: ActivityLogMetadata = {\n transactionHash: log.transactionHash,\n blockNumber: log.blockNumber,\n logIndex: log.logIndex,\n };\n items.push(eventToActivityItem(event, userAddress, metadata));\n }\n return items;\n}\n\n/**\n * Extract unique non-zero encrypted handles that need decryption.\n */\nexport function extractEncryptedHandles(items: readonly ActivityItem[]): Handle[] {\n const handles = new Set<Handle>();\n for (const item of items) {\n if (item.amount.type === \"encrypted\" && item.amount.decryptedValue === undefined) {\n const h = item.amount.handle;\n // Skip zero handles\n if (h !== \"0x\" && h !== ZERO_HANDLE) {\n handles.add(h);\n }\n }\n }\n return [...handles];\n}\n\n/**\n * Return new activity items with decrypted values populated.\n * Items whose handles aren't in the map are returned unchanged.\n */\nexport function applyDecryptedValues(\n items: readonly ActivityItem[],\n decryptedMap: ReadonlyMap<Handle, bigint>,\n): ActivityItem[] {\n return items.map((item) => {\n if (item.amount.type !== \"encrypted\") {\n return item;\n }\n\n const value = decryptedMap.get(item.amount.handle);\n if (value === undefined) {\n return item;\n }\n\n return {\n ...item,\n amount: {\n type: \"encrypted\" as const,\n handle: item.amount.handle,\n decryptedValue: value,\n },\n };\n });\n}\n\n/**\n * Sort activity items by block number, most recent first.\n * Items without a block number are placed at the beginning (most recent).\n */\nexport function sortByBlockNumber(items: readonly ActivityItem[]): ActivityItem[] {\n return [...items].toSorted((a, b) => {\n const aBlock = a.metadata.blockNumber;\n const bBlock = b.metadata.blockNumber;\n\n if (aBlock === undefined && bBlock === undefined) {\n return 0;\n }\n if (aBlock === undefined) {\n return -1;\n }\n if (bBlock === undefined) {\n return 1;\n }\n\n // Convert to bigint for comparison\n const aBig = typeof aBlock === \"bigint\" ? aBlock : BigInt(aBlock);\n const bBig = typeof bBlock === \"bigint\" ? bBlock : BigInt(bBlock);\n\n if (bBig > aBig) {\n return 1;\n }\n if (bBig < aBig) {\n return -1;\n }\n\n // Same block: sort by logIndex descending\n const aIdx = a.metadata.logIndex ?? 0;\n const bIdx = b.metadata.logIndex ?? 0;\n return bIdx - aIdx;\n });\n}\n"],"mappings":"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"}