import { type StorageAdapter } from '@lifi/perps-sdk'; import type { ApproveReadOnlyTokenParams, LighterAccountConfig, LighterProviderKey } from '@lifi/perps-types'; import type { Address } from 'viem'; /** * Default token name persisted alongside Lighter's `tokens/create` row. * Lighter requires a non-empty `name` form field; the literal here is what * surfaces in Lighter's UI listing under `app.lighter.xyz/read-only-tokens`. * @public */ export declare const DEFAULT_READ_ONLY_TOKEN_NAME = "LI.FI Perps"; /** * Persisted shape of a Lighter read-only token. The `token` string is * Lighter's opaque `ro:{accountIndex}:{scope}:{expiry}:{rand}` bearer; the * SDK never parses it — `expiry`/`scope`/`accountIndex` are create-time inputs * we keep alongside so consumers can render expiry UX without re-fetching. * @public */ export interface LighterReadOnlyToken { /** Opaque bearer string — never parse client-side. */ token: string; /** Unix seconds. The SDK's source of truth for expiry. */ expiry: number; /** Lighter's `single | all` scope literal recorded at create time. */ scope: 'single' | 'all'; /** Lighter L2 account index the token authorises. */ accountIndex: number; } /** * Lighter `POST /api/v1/tokens/create` response shape (subset). Lighter's * OpenAPI documents additional fields (`token_id`, `name`, `revoked`, etc.) * we don't need client-side. * @public */ export interface LighterCreateTokenResponse { api_token: string; account_index: number; expiry: number; scopes: string; } /** * Function injected for the HTTP boundary against Lighter's `tokens/create` * endpoint. Returning a parsed {@link LighterCreateTokenResponse} keeps the * create code free of fetch/multipart plumbing and lets tests drop a fixture * in without spinning a mock server. * @public */ export type LighterTokenFetcher = (params: { url: string; authorization: string; name: string; accountIndex: number; expiry: number; subAccountAccess: boolean; scopes: string; }) => Promise; /** * Dependencies and overrides for {@link LighterReadOnlyTokenManager}. * Storage and token-fetching defaults target the browser's local storage and * Lighter's mainnet REST API. * * @public */ export interface LighterReadOnlyTokenManagerOptions { storage?: StorageAdapter; /** * Provider instance key. Namespaces persisted tokens so two Lighter * instances sharing a storage backend never serve each other's token for a * coincident `(address, accountIndex)` pair, and stamps the `provider` of * the {@link ApproveReadOnlyTokenResult} config. Defaults to `'lighter'`. */ providerKey?: LighterProviderKey; /** Lighter API host. Defaults to {@link DEFAULT_LIGHTER_REST_URL}. */ lighterApiUrl?: string; /** Override the multipart POST. Defaults to a `fetch`-based implementation. */ fetcher?: LighterTokenFetcher; /** Clock injection for testing expiry logic. Defaults to `Date.now`. */ now?: () => number; } /** * Input to the read-only-token approval flow. Extends the shared approval * parameters with the L1 address whose wallet authorizes token creation. * * @public */ export interface ApproveReadOnlyTokenInputs extends ApproveReadOnlyTokenParams { /** L1 wallet address that signs the create message. */ address: Address; } /** * Result of approving or creating a Lighter read-only token. `config` is the * account-state projection callers can persist alongside the token. * * @public */ export interface ApproveReadOnlyTokenResult { token: LighterReadOnlyToken; /** Projection of the post-create Lighter account state. */ config: Pick; } /** * Manage the per-account Lighter read-only token alongside the existing * `(L1 address, account index)`-scoped storage adapter pattern. Takes an * injectable `StorageAdapter` so callers can share one storage backend across * the SDK's session stores. The token is opaque — never parsed client-side; * `expiry`/`scope` recorded at create time are the source of truth. * * The L1 wallet signer and the HTTP fetcher are both injectable so unit * tests don't need a real wallet or network. * * @security The default adapter encrypts records at rest (AES-GCM-256 via * WebCrypto, keyed by a non-extractable key held in IndexedDB) before writing * ciphertext to `localStorage`, defeating generic storage/disk scanning and raw * token exfiltration. It does not defend against malware targeting this SDK or * a fully compromised page — a same-origin script can still drive this manager * to decrypt. Blast radius is limited to reads: the token cannot sign orders * or move funds. * @public */ export declare class LighterReadOnlyTokenManager { private readonly storage; private readonly providerKey; private readonly lighterApiUrl; private readonly fetcher; private readonly now; private readonly cache; constructor(options?: LighterReadOnlyTokenManagerOptions); private storageKey; private isExpired; /** * Return the stored token for the `(address, accountIndex)` pair, or * `undefined` if absent or past its `expiry`. Never surfaces an expired * token — consumers can rely on the return value being usable as-is. */ get(address: Address, accountIndex: number): Promise; /** * `true` when a stored token exists AND its `expiry` falls within * `thresholdDays` of now. `false` when no token exists, when the token is * already expired (caller should treat that as "no token"), or when it * has more than `thresholdDays` of life left. */ isReadOnlyTokenExpiringSoon(address: Address, accountIndex: number, thresholdDays?: number): Promise; /** * Persist `token` for the `(address, accountIndex)` pair. Overwrites any * prior stored token under the same key — used both by the approve flow * and by the renewal flow. */ set(address: Address, accountIndex: number, token: LighterReadOnlyToken): Promise; /** * Remove the stored token for an `(address, accountIndex)` pair and clear * the corresponding cache entry. */ remove(address: Address, accountIndex: number): Promise; /** * Create and persist a new Lighter read-only token. * * POSTs to Lighter's `/api/v1/tokens/create` (via the injected `fetcher`) * and persists the returned `ro:` bearer alongside its * `expiry`/`scope`/`accountIndex`. * * `authorization` MUST be a **standard** Lighter auth token — one created by * the account's API key (`createAuthToken` / the WASM signer), NOT an L1 * wallet signature. Lighter authenticates the create request with that token * and rejects anything else as `invalid auth string` (code 20013). * * `expirySeconds` is the absolute unix-seconds expiry Lighter records on * the row. Lighter enforces 1 day ≤ lifetime ≤ 10 years server-side; the * SDK does NOT pre-validate and surfaces Lighter's 400 verbatim. * * `scope` selects sub-account coverage: `'all'` sets `sub_account_access` * (read access to every sub-account of the owner); `'single'` scopes to the * one account. The permission set (`scopes` form field) is always read-only * (`read.*`). * * @param authorization Standard (API-key-signed) Lighter auth token. * @param inputs Token-create parameters plus the owning `address`. */ approve(authorization: string, inputs: ApproveReadOnlyTokenInputs): Promise; } /** * Default fetcher: posts a multipart/form-data request to Lighter's * `tokens/create` endpoint and returns the parsed response. Throws a * {@link PerpsError} with the Lighter-side body when the response is * non-2xx. * @public */ export declare const defaultLighterTokenFetcher: LighterTokenFetcher; //# sourceMappingURL=LighterReadOnlyTokenManager.d.ts.map