npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hitesh23k/lighter-sdk

v0.4.1

Published

Lighter TypeScript SDK for perpetual futures trading on zkLighter and Robinhood-Chain Lighter: place orders, bracket orders (TP/SL), stream real-time market data over WebSocket, onboard wallets, and sign transactions. Zero-dependency, runs in Node and the

Downloads

656

Readme

Lighter Perpetual Futures SDK for TS/JS: zkLighter & Robinhood-Chain

npm version npm downloads types included license zero dependencies

The unofficial TypeScript / JavaScript SDK for Lighter — trade perpetual futures, place orders with take-profit and stop-loss, stream real-time market data over WebSocket, onboard wallets, and sign transactions, from Node.js or the browser. Supports both venues: zkLighter (zk) and Robinhood-Chain Lighter (robinhood), on mainnet and testnet.

npm install @hitesh23k/lighter-sdk

What is Lighter?

Lighter is a high-performance, order-book perpetual futures exchange (a decentralized derivatives DEX) built as a zk-rollup on Ethereum. Trades are matched on a real order book and settled with zero-knowledge proofs, giving centralized-exchange speed with on-chain verifiability. It runs across a few deployments that share the same core: zkLighter and the Robinhood-Chain instance, on mainnet and testnet.

What is this SDK?

@hitesh23k/lighter-sdk is a batteries-included client for the Lighter API. It handles the hard part — Lighter's zk transaction signing has no native JavaScript implementation, so this SDK ships the official signer compiled to WebAssembly and loads it in-process. You get a clean, typed API for everything:

  • 📈 Trade perpetuals — market, limit, and bracket orders (entry + take-profit + stop-loss in one atomic transaction)
  • 📊 Read market data — order books, prices, funding rates, candles, positions, balances
  • Real-time streaming — WebSocket order book, trades, and authenticated account updates
  • 🔑 Onboarding — link a wallet to a trading key in one flow (nobody else has this)
  • 🔀 Both venues — zkLighter (zk) and Robinhood-Chain Lighter (robinhood), mainnet and testnet, one API
  • 🛡️ Built-in safety — automatic nonce sequencing, market-order price bounds, minimum-size checks
  • 🌐 Runs everywhere — Node.js and the browser, ESM and CommonJS, fully typed
  • 🪶 Zero runtime dependencies — ~50 KB browser bundle, no ethers/axios bloat

Why this SDK

  • Human units, not hand-scaled integers. Say size: 0.5 and price: 3050.5; the SDK scales to the exchange's fixed-point encoding for you. Use side: "long" | "short", not raw isAsk booleans.
  • Zero dependencies. No ethers, no axios. A tiny footprint, a clean supply chain, and a browser bundle around 50 KB.
  • Safe by default. Concurrent orders never race to a duplicate nonce, market orders always carry a worst-case price bound, and sub-minimum orders are caught before they cost you a transaction.
  • Verified on testnet end to end, across both venues.

Contents

Installation

npm install @hitesh23k/lighter-sdk
# or: pnpm add @hitesh23k/lighter-sdk / yarn add @hitesh23k/lighter-sdk

Requires Node.js 18+ (uses global fetch and WebAssembly). Types are bundled — no @types package needed.

TypeScript on nodenext? Install @types/node and add "types": ["node"] to your tsconfig.json so Node globals resolve. The SDK's own types work out of the box.

Quick start

LighterClient is the high-level, "just trade" entry point. It resolves markets by symbol and converts human sizes and prices to Lighter's integer encoding automatically.

import { LighterClient } from "@hitesh23k/lighter-sdk";

const client = new LighterClient({
  venue: "zk",           // "zk" (zkLighter) or "robinhood"
  isMainnet: true,
  signer: { apiPrivateKey: "0x…", accountIndex: 7, apiKeyIndex: 4 },
});

await client.loadMarkets();

// Trade in human units
await client.setLeverage({ symbol: "BTC", leverage: 20 });
await client.placeMarketOrder({ symbol: "BTC", side: "long", size: 0.5 });
await client.placeLimitOrder({ symbol: "ETH", side: "short", size: 2, price: 3050.5 });

// Read your account
const positions = await client.getPositions();

// Stream live data
await client.connect();
client.streamOrderBook("BTC", (msg) => console.log(msg));

Don't have a trading key yet? See Onboarding. Just want market data? Skip the signer and use the read and stream methods.

Reading market data

No authentication needed for public data.

const markets = await client.markets();                 // all tradeable markets
const btc = client.market("BTC");                       // symbol -> market metadata

const funding = await client.rest.getFundingRates();    // Lighter funding rates
const trades = await client.rest.getRecentTrades(btc.marketId, 50);
const candles = await client.rest.getCandles({
  market_id: btc.marketId, resolution: "1h",
  start_timestamp: Date.now() - 24 * 3600_000, end_timestamp: Date.now(), count_back: 24,
});
const account = await client.rest.getAccount(7);

Placing orders

// Market order (a worst-case price bound is added automatically for slippage protection)
await client.placeMarketOrder({ symbol: "BTC", side: "long", size: 0.01, slippage: 0.01 });

// Limit order
await client.placeLimitOrder({ symbol: "BTC", side: "short", size: 0.01, price: 70000, timeInForce: "gtc" });

// Bracket order: entry + take-profit + stop-loss, ONE atomic transaction
await client.placeBracketOrder({
  symbol: "BTC", side: "long", size: 0.01,
  takeProfit: 72000,   // trigger prices in human quote units
  stopLoss: 60000,
});

// Cancel
await client.cancelOrder("BTC", orderIndex);
await client.cancelAllOrders();               // atomic, all markets

Positions, leverage, and margin

await client.setLeverage({ symbol: "BTC", leverage: 10 });
await client.adjustMargin({ symbol: "BTC", amount: 25, action: "add" });  // isolated margin, human USDC

await client.closePosition("BTC");            // market-close, reduce-only
await client.closeAllPositions();             // flatten everything

// Wait for a transaction to settle
const tx = await client.placeMarketOrder({ symbol: "BTC", side: "long", size: 0.001 });
await client.waitForTransaction(tx.tx_hash!);

Real-time WebSocket data

Reconnect with backoff, re-subscription, and keepalive are all automatic.

await client.connect();

client.streamOrderBook("BTC", (msg) => { /* live order book */ });
client.streamTrades("BTC", (msg) => { /* live trades */ });
client.streamAccount((msg) => { /* your balances, positions, orders */ });

// later
client.close();

Account channels are authenticated; LighterClient mints the auth token from your signer automatically. For lower-level control use client.ws (a LighterWs instance) or construct one directly.

Onboarding: get a trading key

To trade you need a signer: { apiPrivateKey, accountIndex, apiKeyIndex }. Getting one is a one-time setup per wallet:

  1. Create an account by depositing to Lighter via its L1 bridge (on-chain; done with your wallet, not this SDK).
  2. Associate an API key. The SDK generates a key, signs a ChangePubKey, your wallet approves it with one personal_sign, and it is submitted. This is a small frontend to backend handshake:
sequenceDiagram
  actor User
  participant FE as Frontend (wallet)
  participant BE as Backend (SDK)
  participant LX as Lighter

  BE->>LX: resolveAccountIndex(l1Address)
  LX-->>BE: accountIndex
  BE->>BE: prepareApiKey() generate key + sign ChangePubKey
  BE-->>FE: messageToSign
  FE->>User: "Approve trading key?"
  User->>FE: personal_sign
  FE-->>BE: L1 signature
  BE->>LX: submitApiKey() merge L1 sig + send
  LX-->>BE: registered

LighterOnboarding runs the whole flow. If your wallet lives on the same side as the SDK, one call does it all:

import { LighterOnboarding, LighterClient } from "@hitesh23k/lighter-sdk";
// bring your own EVM wallet (ethers/viem/etc.) — only used to personal_sign one message

const onboarding = new LighterOnboarding({ venue: "zk", isMainnet: true });

const { signer, apiPrivateKey } = await onboarding.registerApiKey({
  l1Address: wallet.address,
  l1Sign: (message) => wallet.signMessage(message),
});
// Store apiPrivateKey securely — it is your trading credential and cannot be recovered.

const client = new LighterClient({ venue: "zk", isMainnet: true, signer });

Splitting signing across a frontend and backend? Use the two-step form: prepareApiKey() returns a messageToSign (have the wallet sign it wherever it lives), then submitApiKey(pending, l1Signature).

Withdrawals

await client.withdraw({ amount: 100 });   // withdraw 100 USDC to your L1 wallet

Error handling

Errors are thrown (not returned as tuples) and typed, so you can branch on the failure kind:

import { LighterApiError, LighterSignerError, LighterValidationError } from "@hitesh23k/lighter-sdk";

try {
  await client.placeLimitOrder({ symbol: "BTC", side: "long", size: 0.00001, price: 60000 });
} catch (err) {
  if (err instanceof LighterValidationError) { /* e.g. below minimum size */ }
  else if (err instanceof LighterApiError) { console.log(err.code, err.status); }
  else if (err instanceof LighterSignerError) { /* signing failed */ }
}

All SDK errors extend LighterError.

Using in the browser

Import from the /browser entry. REST and WebSocket work unchanged (global fetch / WebSocket). The only difference is the signer: browsers have no filesystem, so you point it at the WASM assets once at startup. Both lighterSigner.wasm and wasm_exec.js ship in the package.

import { initLighterSigner, LighterClient } from "@hitesh23k/lighter-sdk/browser";
import wasmUrl from "@hitesh23k/lighter-sdk/lighterSigner.wasm?url";        // Vite asset URL
import wasmExecUrl from "@hitesh23k/lighter-sdk/wasm_exec.js?url";

initLighterSigner({ wasmUrl, wasmExecUrl });   // lazy: the WASM loads on the first signing call

const client = new LighterClient({ venue: "zk", isMainnet: true });

The browser bundle imports no Node built-ins, so it bundles cleanly with Vite, webpack, and friends.

Networks and venues

The SDK supports both Lighter deployments. Pick one with the venue option; everything else (the same LighterClient API, orders, streams, onboarding) works identically.

| venue | Collateral | Networks | Description | |---|---|---|---| | "zk" (default) | USDC | mainnet, testnet | zkLighter, the main deployment | | "robinhood" | USDG | mainnet, testnet | Robinhood-Chain Lighter |

const zk = new LighterClient({ venue: "zk", isMainnet: true });          // zkLighter mainnet
const rh = new LighterClient({ venue: "robinhood", isMainnet: true });   // Robinhood-Chain mainnet
const test = new LighterClient({ venue: "zk", isMainnet: false });       // zkLighter testnet

isMainnet: false selects testnet. The SDK resolves the correct host and signing chain id per venue and network for you. Accounts and API keys are per-venue (an account on zk is unrelated to one on robinhood).

How it works

Five composable building blocks. Your app uses the high-level LighterClient, or the low-level pieces directly.

flowchart TB
  subgraph app["Your app"]
    fe["Frontend (browser)<br/>market UI, charts, wallet"]
    be["Backend (Node)<br/>bots, order routing, key storage"]
  end
  subgraph sdk["@hitesh23k/lighter-sdk"]
    client["LighterClient<br/><i>trade by symbol</i>"]
    onboard["LighterOnboarding<br/><i>wallet -> trading key</i>"]
    rest["REST client<br/><i>reads + order submit</i>"]
    ws["WebSocket client<br/><i>live data</i>"]
    signer["WASM signer<br/><i>signs transactions</i>"]
  end
  lighter[("Lighter exchange")]
  fe --> rest
  fe --> ws
  be --> client
  client --> rest
  client --> ws
  client --> signer
  onboard --> signer
  onboard --> rest
  rest <--> lighter
  ws <--> lighter

API overview

| Export | What it does | |---|---| | LighterClient | High-level, venue-aware client. Trade by symbol in human units; the recommended entry point. | | LighterRestClient | Typed REST client: market data, account reads, and the signed write path. | | LighterWs | WebSocket client: order book, trades, and authenticated account streams. | | LighterOnboarding | Link a wallet to a programmatic trading key (ChangePubKey flow). | | LighterConstant, LighterHelper | Protocol constants and fixed-point scaling helpers. | | sign* functions | Low-level signers (signCreateOrder, signCreateGroupedOrders, signWithdraw, …). | | LighterError and subclasses | Typed error hierarchy. |

Everything is fully typed. Low-level access is always available via client.rest and client.ws.

Limitations

  • Signing ships a WebAssembly binary and runs in Node (filesystem) or the browser (initLighterSigner). It does not run on edge runtimes that forbid fs and cap bundle size (Cloudflare Workers, Vercel Edge); reads and WebSocket work anywhere.
  • Account creation is an on-chain L1 bridge deposit done with your wallet, not by this SDK. Onboarding assumes the account already exists.
  • Account-to-account transfers additionally require the account owner's L1 signature. The low-level client.rest.transfer signs the L2 part; the L1 signature is your responsibility. Withdrawals do not need this.

License

Apache-2.0. Bundled third-party components (the Lighter Go signer and Go's WASM runtime glue) are attributed in NOTICE.


Not affiliated with or endorsed by Lighter or Elliot. "Lighter" and "zkLighter" are used to describe API compatibility. Trading perpetual futures involves risk; use at your own risk.