|
| 1 | +--- |
| 2 | +title: "Lighter Perp DEX on Robinhood Chain API" |
| 3 | +description: "Track Lighter perpetual futures on Robinhood Chain with Bitquery — USDG margin deposits and withdrawals, the ZkLighter rollup contract, batch commits, and full flow history over GraphQL APIs and WebSocket streams." |
| 4 | +sidebar_position: 10 |
| 5 | +keywords: |
| 6 | + - Lighter Robinhood API |
| 7 | + - Lighter perp DEX data |
| 8 | + - Robinhood perpetual futures onchain |
| 9 | + - ZkLighter contract Robinhood Chain |
| 10 | + - Lighter USDG deposits |
| 11 | + - Lighter margin withdrawals |
| 12 | + - Lighter deposit tracking |
| 13 | + - zkLighter rollup batches |
| 14 | + - Robinhood Chain perps |
| 15 | + - Bitquery Lighter API |
| 16 | +--- |
| 17 | + |
| 18 | +# Lighter Perp DEX on Robinhood Chain API |
| 19 | + |
| 20 | +[Lighter](https://lighter.xyz) is the perpetual-futures DEX integrated into Robinhood Wallet. When Robinhood Chain mainnet went live on July 1, 2026, Lighter launched alongside it as the venue behind in-app perps: eligible users post **USDG** margin from their wallet, and the funds are locked in Lighter's smart contract on Robinhood Chain while positions are managed by Lighter's zk engine. |
| 21 | + |
| 22 | +What actually lives on Robinhood Chain is a full **ZkLighter rollup contract** — not just a token vault. It receives every margin deposit, queues and pays withdrawals, and records the rollup's batch lifecycle (`commit → verify → execute`) at roughly **one batch per minute**. This page shows how to query and stream all of it with Bitquery's `EVM` cubes on `network: robinhood`. |
| 23 | + |
| 24 | +Scale as of late August 2026, measured with the queries on this page: about **$33.4M USDG deposited and $7.7M withdrawn** since launch (net ≈ the contract's current $25.7M balance), with August deposits running at ~3.7× July and 1,000–3,000 deposits per day. |
| 25 | + |
| 26 | +:::note API Key Required |
| 27 | +To query or stream data outside the Bitquery IDE, you need an API access token. |
| 28 | + |
| 29 | +Follow the steps here: [How to generate Bitquery API token ➤](/docs/authorization/how-to-generate/) |
| 30 | +::: |
| 31 | + |
| 32 | +:::tip Related docs |
| 33 | +- [Robinhood Events API](/docs/blockchain/robinhood/robinhood-events-api/) |
| 34 | +- [Robinhood Transfers](/docs/blockchain/robinhood/robinhood-transfers/) |
| 35 | +- [Robinhood Calls API](/docs/blockchain/robinhood/robinhood-calls-api/) |
| 36 | +- [Robinhood Balances API](/docs/blockchain/robinhood/robinhood-balances-api/) |
| 37 | +- [EVM Events schema](/docs/schema/evm/events/) |
| 38 | +::: |
| 39 | + |
| 40 | +**On this page:** [What is on-chain](#what-is-on-chain-and-what-is-not) · [Contracts](#contract-map) · [Event reference](#event-reference-topic0-map) · [Deposits](#track-margin-deposits) · [Withdrawals](#track-withdrawals) · [Margin flow & history](#usdg-margin-flow-full-history) · [Rollup heartbeat](#monitor-the-rollup-heartbeat) · [Deposit calls](#deposit-calls-by-selector) · [Decoding notes](#decoding-notes) |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## What is on-chain (and what is not) |
| 45 | + |
| 46 | +Lighter's matching engine, order book, positions, funding, and liquidations run inside its zk rollup — they are **not** individual Robinhood Chain transactions. What settles on Robinhood Chain, and what Bitquery therefore indexes, is: |
| 47 | + |
| 48 | +| On Robinhood Chain (queryable here) | Inside the Lighter engine (not on-chain) | |
| 49 | +| --- | --- | |
| 50 | +| USDG margin **deposits** into the ZkLighter contract | Individual trades and fills | |
| 51 | +| **Withdrawal** queue events and USDG payouts back to users | Open positions and PnL | |
| 52 | +| Rollup **batch lifecycle**: commit, verification, execution, state roots | Funding payments (hourly, peer-to-peer) | |
| 53 | +| Priority requests (forced operations, key changes, escape hatches) | Order placement and cancellation (except the on-chain `cancelAllOrders` escape hatch) | |
| 54 | +| Market/asset registry events | Liquidation events themselves (only the margin effects appear) | |
| 55 | + |
| 56 | +This makes the on-chain data ideal for **money-flow questions** — who is depositing margin, how much, net flows, contract TVL, whether the rollup operator is alive — rather than trade-level analytics. |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## Contract map |
| 61 | + |
| 62 | +| Role | Address | |
| 63 | +| --- | --- | |
| 64 | +| **ZkLighter proxy** — receives margin, pays withdrawals, emits every Lighter event | `0x94bab9693ba2f6358507effcbd372b0660afff9d` | |
| 65 | +| Current verified implementation (`ZkLighter`) | `0x82DE5B1161C93afDFE21bA0D5343f01Cd7401d90` | |
| 66 | +| USDG (Global Dollar, 6 decimals) — canonical currency address in Bitquery cubes | `0x5fc5360d0400a0fd4f2af552add042d716f1d168` | |
| 67 | +| Robinhood deposit router (sweeps in-app deposits into `deposit()`) | `0x8062df5b3220ad1f528365650a3eb3e8c7b0dad1` | |
| 68 | + |
| 69 | +:::info Filter by LogHeader.Address, not Log.SmartContract |
| 70 | +The ZkLighter proxy delegates to implementation modules, so `Log.SmartContract` shows the **implementation** address — and implementations rotate on upgrades (the last rotation was August 21, 2026; older module addresses you may see in results include `0x1be72833…`, `0xe470e41c…`, and `0xda2b59ff…`). The stable anchor is `LogHeader.Address` = the proxy `0x94bab969…`, combined with the event's `SignatureHash` (topic0). Every query below follows that pattern. |
| 71 | +::: |
| 72 | + |
| 73 | +--- |
| 74 | + |
| 75 | +## Event reference (topic0 map) |
| 76 | + |
| 77 | +All events are emitted with the proxy as `LogHeader.Address`. Hash fields in Bitquery are hex strings **without** a `0x` prefix. |
| 78 | + |
| 79 | +| Event | topic0 (`Log.Signature.SignatureHash`) | |
| 80 | +| --- | --- | |
| 81 | +| `Deposit(uint48 toAccountIndex, address toAddress, uint16 assetIndex, uint8 routeType, uint128 baseAmount)` | `493c3b8240368e8343bcd42cac5f4b8b161c06d061710e542a72f06a40ddd9d1` | |
| 82 | +| `WithdrawPending(address indexed owner, uint16 assetIndex, uint128 baseAmount)` | `ef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f` | |
| 83 | +| `NewPriorityRequest(address sender, uint64 serialId, uint8 pubdataType, bytes pubData, uint64 expirationTimestamp)` | `efdd379e3e15772fcc7d2a67fa5bbb0790b932724153aded4648307094733b2f` | |
| 84 | +| `BatchCommit(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber)` | `181b25ea9d4d730f30d779f3d2099c03b26b653c889d33eef253d54baaacbd0d` | |
| 85 | +| `BatchVerification(uint64 batchNumber, uint32 batchSize, uint64 endBlockNumber)` | `5c836e1ff20ea85c52b6e3d2ef0124d3304bf3b37cc8fb0e2c84ae7d44c0593e` | |
| 86 | +| `BatchesExecuted(uint64 batchNumber, uint64 endBlockNumber)` | `5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb325511` | |
| 87 | +| `StateRootUpdate(uint64 batchNumber, bytes32 oldStateRoot, bytes32 newStateRoot)` | `645e0b8f839353842bdac87abd27fc8bdda536e0731cdb7cc75e4f0740b575ac` | |
| 88 | +| `CreateMarket((uint16,uint8,bytes), uint8 sizeDecimals, uint8 priceDecimals, bytes32 symbol)` | `134f63a6bbe3b3ef885ce4067eb2753fe1c912c51c4b8e0cc7966f21773c047e` | |
| 89 | +| `RegisterAssetConfig(uint16 assetIndex, address tokenAddress, …)` | `f1b24e81016b9f39e2290cf2a9303264a07534a569df7e6200a39573d7f26b0c` | |
| 90 | + |
| 91 | +The remaining admin events (`UpdateMarket`, `UpdateAssetConfig`, `BatchesRevert`, `DesertMode`, `TreasuryUpdate`, `InsuranceFundOperatorUpdate`, `Initialized`) exist in the ABI but fire rarely; the full verified ABI is on the [Robinhood Chain explorer](https://robinhoodchain.blockscout.com/address/0x82DE5B1161C93afDFE21bA0D5343f01Cd7401d90?tab=contract). |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## Track margin deposits |
| 96 | + |
| 97 | +Every deposit into Lighter emits one `Deposit` event. This query returns the latest ones; turn it into a live stream by replacing `query` with `subscription` and dropping `dataset`/`limit`/`orderBy`. |
| 98 | + |
| 99 | +```graphql |
| 100 | +{ |
| 101 | + EVM(network: robinhood, dataset: realtime) { |
| 102 | + Events( |
| 103 | + limit: { count: 10 } |
| 104 | + orderBy: { descending: Block_Number } |
| 105 | + where: { |
| 106 | + LogHeader: { Address: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } } |
| 107 | + Log: { Signature: { SignatureHash: { is: "493c3b8240368e8343bcd42cac5f4b8b161c06d061710e542a72f06a40ddd9d1" } } } |
| 108 | + } |
| 109 | + ) { |
| 110 | + Block { Number Time } |
| 111 | + Transaction { Hash From } |
| 112 | + Log { SmartContract Signature { SignatureHash } } |
| 113 | + LogHeader { Address Data } |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +`Deposit` has no indexed parameters, so all five fields sit in `LogHeader.Data` as 32-byte words, in ABI order: |
| 120 | + |
| 121 | +| Word | Field | Notes | |
| 122 | +| --- | --- | --- | |
| 123 | +| 0 | `toAccountIndex` | The user's Lighter account index | |
| 124 | +| 1 | `toAddress` | The wallet credited — for Robinhood-app deposits this is the user's deposit address, while the ERC-20 transfer arrives via the router `0x8062df5b…` | |
| 125 | +| 2 | `assetIndex` | `3` = USDG on this deployment | |
| 126 | +| 3 | `routeType` | `0` for ~98% of deposits | |
| 127 | +| 4 | `baseAmount` | Raw token units — divide by 10⁶ for USDG (verified to match the ERC-20 transfer in the same transaction) | |
| 128 | + |
| 129 | +--- |
| 130 | + |
| 131 | +## Track withdrawals |
| 132 | + |
| 133 | +Withdrawals are two-step: the rollup queues the amount (`WithdrawPending`, with the receiving wallet **indexed** as `Topics[1]`), then operator transactions push the USDG payout to the user via `withdrawPendingBalance`. To watch the queue: |
| 134 | + |
| 135 | +```graphql |
| 136 | +{ |
| 137 | + EVM(network: robinhood, dataset: realtime) { |
| 138 | + Events( |
| 139 | + limit: { count: 10 } |
| 140 | + orderBy: { descending: Block_Number } |
| 141 | + where: { |
| 142 | + LogHeader: { Address: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } } |
| 143 | + Log: { Signature: { SignatureHash: { is: "ef80235b5f4cf1822ad6a8621af41ac64372ff672c402874f507fc63dbe5e06f" } } } |
| 144 | + } |
| 145 | + ) { |
| 146 | + Block { Number Time } |
| 147 | + Transaction { Hash } |
| 148 | + Topics { Hash } # Topics[1] = padded owner address |
| 149 | + LogHeader { Data } # [assetIndex, baseAmount(6dp)] |
| 150 | + } |
| 151 | + } |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +The actual payout is a plain USDG transfer **from** the proxy, so the Transfers query below covers the money leg of withdrawals too. |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## USDG margin flow (full history) |
| 160 | + |
| 161 | +The simplest lens on Lighter needs no event decoding at all: USDG transfers to the proxy are margin in, transfers from it are margin out. The `combined` dataset holds Robinhood Chain history back to the chain's start, so this works from Lighter's first deposit (June 26, 2026 — a soft start a few days before the public July 1 launch). |
| 162 | + |
| 163 | +Monthly deposit and withdrawal totals since launch: |
| 164 | + |
| 165 | +```graphql |
| 166 | +{ |
| 167 | + EVM(network: robinhood, dataset: combined) { |
| 168 | + deposits: Transfers( |
| 169 | + where: { |
| 170 | + Transfer: { |
| 171 | + Currency: { SmartContract: { is: "0x5fc5360d0400a0fd4f2af552add042d716f1d168" } } |
| 172 | + Receiver: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } |
| 173 | + } |
| 174 | + } |
| 175 | + ) { |
| 176 | + Block { Date(interval: { count: 1, in: months }) } |
| 177 | + count |
| 178 | + sum(of: Transfer_Amount) |
| 179 | + } |
| 180 | + withdrawals: Transfers( |
| 181 | + where: { |
| 182 | + Transfer: { |
| 183 | + Currency: { SmartContract: { is: "0x5fc5360d0400a0fd4f2af552add042d716f1d168" } } |
| 184 | + Sender: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } |
| 185 | + } |
| 186 | + } |
| 187 | + ) { |
| 188 | + Block { Date(interval: { count: 1, in: months }) } |
| 189 | + count |
| 190 | + sum(of: Transfer_Amount) |
| 191 | + } |
| 192 | + } |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +Swap the monthly interval for a `days` interval for daily flow charts, or drop the aggregation and add `limit`/`orderBy` to list individual transfers with sender and receiver. The difference between lifetime deposits and withdrawals is the contract's standing USDG balance — Lighter-on-Robinhood's margin TVL. |
| 197 | + |
| 198 | +To size total activity, an aggregate probe over full history: |
| 199 | + |
| 200 | +```graphql |
| 201 | +{ |
| 202 | + EVM(network: robinhood, dataset: combined) { |
| 203 | + Events( |
| 204 | + where: { |
| 205 | + LogHeader: { Address: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } } |
| 206 | + Log: { Signature: { SignatureHash: { is: "493c3b8240368e8343bcd42cac5f4b8b161c06d061710e542a72f06a40ddd9d1" } } } |
| 207 | + } |
| 208 | + ) { |
| 209 | + count |
| 210 | + earliest: Block { Time(minimum: Block_Time) } |
| 211 | + latest: Block { Time(maximum: Block_Time) } |
| 212 | + } |
| 213 | + } |
| 214 | +} |
| 215 | +``` |
| 216 | + |
| 217 | +--- |
| 218 | + |
| 219 | +## Monitor the rollup heartbeat |
| 220 | + |
| 221 | +Lighter posts its zk batch lifecycle to Robinhood Chain about once a minute. Streaming the three lifecycle events is a ready-made **liveness monitor** for the venue — if commits stop, the engine or its operator has a problem: |
| 222 | + |
| 223 | +```graphql |
| 224 | +subscription { |
| 225 | + EVM(network: robinhood) { |
| 226 | + Events( |
| 227 | + where: { |
| 228 | + LogHeader: { Address: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } } |
| 229 | + Log: { |
| 230 | + Signature: { |
| 231 | + SignatureHash: { |
| 232 | + in: [ |
| 233 | + "181b25ea9d4d730f30d779f3d2099c03b26b653c889d33eef253d54baaacbd0d" |
| 234 | + "5c836e1ff20ea85c52b6e3d2ef0124d3304bf3b37cc8fb0e2c84ae7d44c0593e" |
| 235 | + "5d490d991d08230b7690c7511bb854b7b8a05fb7c87e2348e1909384cb325511" |
| 236 | + ] |
| 237 | + } |
| 238 | + } |
| 239 | + } |
| 240 | + } |
| 241 | + ) { |
| 242 | + Block { Number Time } |
| 243 | + Transaction { Hash From } |
| 244 | + Log { Signature { SignatureHash } } |
| 245 | + LogHeader { Data } # batchNumber, batchSize, endBlockNumber |
| 246 | + } |
| 247 | + } |
| 248 | +} |
| 249 | +``` |
| 250 | + |
| 251 | +Each of `commitBatch`, `verifyBatch`, and `executeBatches` is sent by a single operator EOA, so `Transaction.From` also identifies the Lighter operator. |
| 252 | + |
| 253 | +--- |
| 254 | + |
| 255 | +## deposit() calls by selector |
| 256 | + |
| 257 | +The Calls cube gives the function-call view of the same activity — useful for catching deposits, forced withdrawals (`withdraw`, selector `d20191bd`), key registrations (`changePubKey`, `17010c68`), and escape-hatch cancels (`cancelAllOrders`, `a4b6f756`): |
| 258 | + |
| 259 | +```graphql |
| 260 | +{ |
| 261 | + EVM(network: robinhood, dataset: realtime) { |
| 262 | + Calls( |
| 263 | + limit: { count: 10 } |
| 264 | + orderBy: { descending: Block_Number } |
| 265 | + where: { |
| 266 | + Call: { |
| 267 | + To: { is: "0x94bab9693ba2f6358507effcbd372b0660afff9d" } |
| 268 | + Signature: { SignatureHash: { is: "8a857083" } } # deposit(address,uint16,uint8,uint256) |
| 269 | + } |
| 270 | + } |
| 271 | + ) { |
| 272 | + Block { Number Time } |
| 273 | + Call { From Value Signature { SignatureHash } } |
| 274 | + Transaction { Hash From } |
| 275 | + } |
| 276 | + } |
| 277 | +} |
| 278 | +``` |
| 279 | + |
| 280 | +--- |
| 281 | + |
| 282 | +## Decoding notes |
| 283 | + |
| 284 | +- **Signatures currently arrive unparsed** — `Log.Signature.Name` is empty for ZkLighter events until the ABI is registered in the decoding pipeline, so filter by `SignatureHash` (as every query on this page does) and decode `LogHeader.Data` client-side with the word layouts above. Once the ABI lands, the same queries also return decoded `Arguments`. |
| 285 | +- `LogHeader.Address` is the address a node's `eth_getLogs` would report; `Log.SmartContract` is the implementation behind the proxy and changes on upgrades. Pin queries to `LogHeader.Address`. |
| 286 | +- Amounts (`baseAmount` and USDG `Transfer.Amount`) are 6-decimal USDG units; `Transfer.Amount` in the API is already decimal-adjusted. |
| 287 | +- Robinhood in-app deposits are swept from per-user deposit addresses through the router `0x8062df5b…`, so the ERC-20 `Transfer.Sender` into the proxy is often the router while the credited wallet is `Deposit.toAddress`. Count depositors from the `Deposit` event, not from transfer senders. |
| 288 | +- For trade-level perps data (fills, positions, funding), use Lighter's own venue APIs — that activity never touches Robinhood Chain. On-chain data here answers flow, TVL, user-count, and liveness questions. |
| 289 | + |
| 290 | +Every query on this page was executed against the production `streaming.bitquery.io/graphql` endpoint on August 22, 2026 before publishing. |
0 commit comments