import { SdkError } from '@parity/product-sdk-errors'; import { Result } from '@parity/result'; import { PolkadotSigner } from 'polkadot-api'; import { SS58String } from '@parity/product-sdk-address'; import { AllocatableResource, AllocationOutcome } from '@parity/product-sdk-host'; /** Function that unsubscribes a listener when called. */ type Unsubscribe = () => void; /** * Interface that all signer providers must implement. * * Providers are responsible for discovering accounts and creating signers * from a specific source (Host API or dev accounts). */ interface SignerProvider { /** Unique identifier for this provider type. */ readonly type: ProviderType; /** * Attempt to connect and discover accounts. * * @param signal - Optional AbortSignal to cancel the connection attempt. * @returns Accounts on success, typed error on failure. */ connect(signal?: AbortSignal): Promise>; /** * Disconnect and clean up resources. * Safe to call multiple times. */ disconnect(): void; /** * Subscribe to connection status changes. * * Not all providers emit status changes — for example, dev accounts * are always "connected" and never emit. * * @returns Unsubscribe function. */ onStatusChange(callback: (status: ConnectionStatus) => void): Unsubscribe; /** * Subscribe to account list changes. * * Emitted when the set of available accounts changes (e.g., user * connects/disconnects in the host wallet). * * @returns Unsubscribe function. */ onAccountsChange(callback: (accounts: SignerAccount[]) => void): Unsubscribe; } /** Connection status for a signer provider. */ type ConnectionStatus = "disconnected" | "connecting" | "connected"; /** * Identifies the source of an account. * * - `"host"`: Host API provider (Polkadot Desktop / Mobile) — the primary provider * - `"dev"`: Development accounts (Alice, Bob, etc.) — for testing only */ type ProviderType = "host" | "dev"; /** A signing-capable account from any provider. */ interface SignerAccount { /** SS58 address (generic prefix 42 by default). */ address: SS58String; /** * H160 EVM address derived from the public key. * * For native Substrate accounts: keccak256(publicKey), last 20 bytes. * For EVM-derived accounts: strips the 0xEE padding. * Used for pallet-revive / Asset Hub EVM contract interactions. */ h160Address: `0x${string}`; /** Raw public key (32 bytes). May be sr25519, ed25519, or ecdsa depending on the provider. */ publicKey: Uint8Array; /** Human-readable name if available from the provider. */ name: string | null; /** Which provider supplied this account. */ source: ProviderType; /** Get the PolkadotSigner for this account. */ getSigner(): PolkadotSigner; } /** Full state snapshot emitted to subscribers. */ interface SignerState { /** Current connection status. */ status: ConnectionStatus; /** All available accounts across all connected providers. */ accounts: readonly SignerAccount[]; /** Currently selected account (null if none selected). */ selectedAccount: SignerAccount | null; /** Which provider is active (null if disconnected). */ activeProvider: ProviderType | null; /** Last error (null if no error). */ error: SignerError | null; } /** Factory function that creates a SignerProvider for a given type. */ type ProviderFactory = (type: ProviderType) => SignerProvider; /** * Adapter for persisting the selected account address across sessions. * * `globalThis.localStorage` satisfies this interface. Pass a custom * implementation for container environments (e.g., hostLocalStorage) * or for testing. */ interface AccountPersistence { getItem(key: string): string | null | Promise; setItem(key: string, value: string): void | Promise; removeItem(key: string): void | Promise; } /** Options for SignerManager construction. */ interface SignerManagerOptions { /** SS58 prefix for address encoding. Default: 42 */ ss58Prefix?: number; /** * Maximum time in ms to wait for the Host API. * Applied as an AbortSignal timeout on the host provider connection. * Default: 10_000 */ hostTimeout?: number; /** Maximum retry attempts for provider connection. Default: 3 */ maxRetries?: number; /** Custom provider factory. Override to inject test doubles or custom providers. */ createProvider?: ProviderFactory; /** * App name used for storage key namespacing. Default: "product-sdk" * The selected account is persisted under `product-sdk:signer:{dappName}:selectedAccount`. */ dappName?: string; /** * Storage adapter for persisting selected account. * Uses host localStorage when inside a container. * Set to `null` to disable persistence entirely. */ persistence?: AccountPersistence | null; /** * Callback fired exactly when the manager transitions to `connected` * with a selected account — not on subsequent state mutations while * still connected. Fires again after auto-reconnect, so a fresh host * session re-runs the callback. * * Common use: request product resource allocations once per session. * The `ctx` exposes a pre-bound `requestResourceAllocation` helper * plus an `AbortSignal` that fires if the user disconnects or * destroys the manager mid-flight. * * `requestResourceAllocation` throws on failure (it adapts the * `Result`-returning `@parity/product-sdk-host` export of the same name, * re-throwing the typed error on the `err` channel); errors thrown from * `onConnect` are logged but do not affect the connected state — the next * reconnect retries. * * @example * ```ts * new SignerManager({ * onConnect: async (_account, { requestResourceAllocation, signal }) => { * try { * const outcomes = await requestResourceAllocation([ * { tag: "AutoSigning", value: undefined }, * ]); * if (signal.aborted) return; * if (outcomes.some((o) => o !== "Allocated")) { * logWarning("partial permissions", outcomes); * } * } catch (cause) { * logWarning("resource allocation failed", cause); * } * }, * }); * ``` */ onConnect?: OnConnect; } /** Context passed to the `onConnect` callback. */ interface ConnectContext { /** * Aborted when the manager disconnects or is destroyed while the * callback is still running. Pass through to `fetch` / cancellation * primitives so mid-flight work stops promptly. */ signal: AbortSignal; /** * Request a batch of host resource allocations. Adapts * `requestResourceAllocation` from `@parity/product-sdk-host` (which returns * a `Result`) to this throwing contract — returns the unwrapped outcomes on * success, throws the typed host error on failure. */ requestResourceAllocation: (resources: AllocatableResource[]) => Promise; } /** Callback signature for {@link SignerManagerOptions.onConnect}. */ type OnConnect = (account: SignerAccount, ctx: ConnectContext) => void | Promise; /** * Base class for all signer errors. Use `instanceof SignerError` to catch any * signer-related error. Implements the cross-package {@link SdkError} marker so * `isSdkError(e)` also recognizes it. */ declare class SignerError extends Error implements SdkError { readonly isSdkError: true; readonly source = "signer"; constructor(message: string, options?: ErrorOptions); } /** * The Host API is not available. * * Common causes: * - The app is loaded outside a Polkadot host container (a regular browser tab * under `npm run dev`, no iframe, no WebView). This is the dominant case * during local development. * - The host environment is detected but the TruAPI transport can't be reached * (no injected message port / unresolvable host origin). * * Branch with `instanceof HostUnavailableError` to surface a "open this app * in a Polkadot host, or pick a dev provider" message to the user. */ declare class HostUnavailableError extends SignerError { constructor(message?: string); } /** The host rejected the account or signing request. */ declare class HostRejectedError extends SignerError { constructor(message?: string); } /** The host connection was lost. */ declare class HostDisconnectedError extends SignerError { constructor(message?: string); } /** A signing operation failed. */ declare class SigningFailedError extends SignerError { constructor(cause: unknown, message?: string); } /** No accounts available from the provider. */ declare class NoAccountsError extends SignerError { readonly provider: ProviderType; constructor(provider: ProviderType, message?: string); } /** An operation timed out. */ declare class TimeoutError extends SignerError { readonly operation: string; readonly ms: number; constructor(operation: string, ms: number); } /** An account was not found by address. */ declare class AccountNotFoundError extends SignerError { readonly address: string; constructor(address: string); } /** The SignerManager has been destroyed and is no longer usable. */ declare class DestroyedError extends SignerError { constructor(); } /** Check if a SignerError is a host-related error. */ declare function isHostError(e: SignerError): e is HostUnavailableError | HostRejectedError | HostDisconnectedError; export { AccountNotFoundError as A, type ConnectionStatus as C, DestroyedError as D, HostDisconnectedError as H, NoAccountsError as N, type OnConnect as O, type ProviderType as P, type SignerProvider as S, TimeoutError as T, type Unsubscribe as U, type SignerAccount as a, SignerError as b, type SignerManagerOptions as c, type SignerState as d, type AccountPersistence as e, type ConnectContext as f, HostRejectedError as g, HostUnavailableError as h, type ProviderFactory as i, SigningFailedError as j, isHostError as k };