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 asked to sign only two things on Lighter:
  • DEPOSIT — the on-chain EVM transaction(s) that fund the account (and create it on first deposit).
  • The REGISTER_API_KEY countersignature — an EIP-191 message authorizing the new API key. This is the L1 half of the dual-signed registration: the new API key signs the on-chain ChangePubKey blob, and the L1 wallet countersigns an EIP-191 message proving the account owner approves that key. (See Setup for the full dual-signing flow.)
Everything else is signed by the API key.

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

The keypair is persisted through the SDK’s LighterKeyStore over a pluggable StorageAdapter (default: browser localStorage; pass a custom adapter for non-browser environments). Each record holds { accountIndex, apiKeyIndex, apiKeyPrivateKey, apiKeyPublicKey }.
  • Namespace: lifi-perps-lighter-key:<l1-address> — keyed per L1 address.
  • Scope: client-side only. The API key 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

ActionSigner
placeOrder, placeTriggerOrder, cancelOrders, modifyOrders, updateLeverage, updatePositionMargin, WITHDRAWALSDK-managed API key (WASM signer, no popup)
DEPOSITUser’s L1 wallet (EVM transaction)
REGISTER_API_KEYBoth — the new API key signs the ChangePubKey blob, the L1 wallet countersigns (EIP-191)

WASM signer dispatch

Trading blobs are produced by a Go WASM module loaded once per process. Each action maps to a specific WASM signing call; the SDK’s high-level methods handle this dispatch automatically.
ActionWASM call
PLACE_ORDERSignCreateOrder
CANCEL_ORDERSignCancelOrder
CANCEL_ALL_ORDERSSignCancelAllOrders
MODIFY_ORDERSignModifyOrder
UPDATE_LEVERAGESignUpdateLeverage
UPDATE_POSITION_MARGINSignUpdateMargin
WITHDRAWALSignWithdraw
TRANSFER (internal, fastwithdraw)SignTransfer
REGISTER_API_KEYSignChangePubKey (dual-signed — see Setup)