Skip to main content
Lighter uses a two-part signing scheme — the user approves a signer once, then trades execute without a per-trade wallet popup. Two keys sign two classes of action:
  • The user’s L1 wallet — any EVM-compatible signer that owns the funds.
  • An SDK-managed API key — a Lighter-native (Schnorr-style) keypair the SDK creates and registers on-chain once.
Registering the API key is part of the one-time Setup flow (the dual-signed REGISTER_API_KEY step). This page explains what the two signatures are and what the API key can and cannot do — Setup covers the step-by-step API.

The L1 (user) wallet signature

The user’s EVM-compatible signer — provided to the SDK via userWallet (createPerpsClient) or setUserWallet() — is used only when a provider descriptor includes the public USER signer:
  • DEPOSIT — the on-chain EVM transaction(s) that fund the account (and create it on first deposit).
  • REGISTER_API_KEY — the native key signs ChangePubKey and the L1 wallet countersigns its EIP-191 authorization.
  • APPROVE_INTEGRATOR — the native key signs the approval blob and the L1 owner supplies the protocol-required EIP-191 signature.
Other trading and operational actions are handled by the plugin’s public SDK signer.

The API key

The API key is a Lighter-native keypair (Schnorr-style — not an EVM key). The SDK generates it, and its public key is registered on-chain in one of the account’s key slots via REGISTER_API_KEY. Lighter verifies every trade against that registered public key. Signing happens inside a Go WASM signer that produces opaque { txType, txInfo, txHash } blobs. What the API key does:
  • Signs every trading and operational action — placeOrder, placeTriggerOrder, cancelOrders, modifyOrders, updateLeverage, updatePositionMargin, and WITHDRAWAL.
What the API key does not do:
  • It is not the L1 wallet — it cannot sign the DEPOSIT transaction or the EIP-191 countersignature for its own registration. Those require the account owner’s EVM wallet.
  • It cannot redirect funds — withdrawals are constrained to the account owner’s address by the backend, so a leaked API key cannot drain funds elsewhere.
  • It is not an EVM key — it’s a Lighter-native (Schnorr-style) keypair that means nothing to an EVM chain; don’t reuse it as a wallet key.

How the API key is created

The SDK generates a fresh Lighter-native keypair client-side (LighterSigner.generateAPIKey()), then registers its public key on-chain at a key slot — slot 42 by default (DEFAULT_API_KEY_INDEX) — through the dual-signed REGISTER_API_KEY step. Re-registering the same slot overwrites the previous key.

How the API key is stored

Each lighterProvider() / lighterRhProvider() instance creates its own LighterKeyStore over the provider’s storage option. The default is encrypted browser localStorage; pass a custom adapter only for SSR, tests, or another persistence backend. Consumers do not construct or inject a key store separately. Each record holds { accountIndex, apiKeyIndex, apiKeyPrivateKey, apiKeyPublicKey }.
  • Namespace: mainnet preserves lifi-perps-lighter-key:<l1-address>; additional deployments include the provider key, for example lifi-perps-lighter-key:lighter-rh:<l1-address>.
  • Scope: client-side only. The private key is never transmitted to the LI.FI backend.
If the local store is cleared (or the user is on a new device), the SDK has no signing key — checkSetup() re-surfaces REGISTER_API_KEY and the user registers a fresh keypair at the same slot.

Who signs what

WASM signer dispatch

The provider instance owns the Go WASM signer and loads it once per process. Each action maps to a specific WASM call; applications do not load the module or dispatch methods themselves.