LighterPerpStrategy lets a fund’s agent trade perpetual futures on Lighter, a zk-rollup order-book perp exchange on Robinhood Chain, collateralized in USDG.
Each strategy clone opens and owns its own Lighter account. USDG is pulled from the vault and deposited as margin. The agent trades that account through Lighter’s API with a trade-only key the contract registers on-chain. The contract keeps an on-chain kill switch: it can cancel orders, force-close positions and drain the account. The venue authenticates each of those to the account owner, which is the contract.
What the custody boundary covers. The agent holds only an L2 API key. That key can place and cancel orders and nothing else. It cannot withdraw, transfer or change keys: the venue authenticates all of those to the account owner (the strategy contract), and the contract only ever pushes USDG to its own vault.
Architecture
Requirements
- The vault asset must be USDG. Init reverts
AssetMismatchotherwise. - The vault’s
TierRegistrymust allow the zkLighter proxy as a counterparty. Init revertsCounterpartyNotAllowedotherwise. markets: 1 to 16 perp market ids, no duplicates. These are the markets the automatic unwind closes.depositAmount: at least 1 USDG. It is a ceiling. Execute deploys it scaled by the proposal’s approve coverage, so readdeployedAmount()after execute and size the drain against that.
Guardrails (the on-chain kill switch)
While the proposal isExecuted, the proposer or the vault owner can fire these on the clone. None of them depend on the agent.
Code
4, a former WITHDRAW, is retired and reverts. Draining is the separate queueWithdraw(ticks) call, because it must keep working after settlement.
The vault owner holds the same levers as the proposer. The proposer’s authority is re-checked against the vault’s live agent set on every call, so removing a misbehaving agent would otherwise remove the only hand on the kill switch.
Lifecycle and the three-step exit
Lighter withdrawals are asynchronous. They mature minutes to days after they are queued, never in the same block. The exit is therefore three steps, with a key rotation before the last one.1
Propose and execute
Execute pulls the coverage-scaled USDG amount into a fresh clone’s Lighter account. The first deposit registers the account.
2
Register the agent key
sherwood lighter register-key registers the trade-only L2 key on-chain. From here the agent trades off-chain with that key.3
Exit step 1: initiate return
sherwood lighter initiate-return cancels all orders and fires a both-side market close on every configured market. It queues nothing: the amount to withdraw is only known once the closes fill. The proposer or vault owner can call it any time. Anyone can call it once the strategy duration has elapsed.4
Exit step 2: queue the withdrawal
After the closes fill,
sherwood lighter queue-withdraw --all reads the account’s true L2 balance from the Lighter API and queues it. It refuses while any position is open. The call is repeatable and still works after settlement.5
Wait, then rotate the key
Wait for the withdrawal to mature. Then
sherwood lighter prepare-settle checks that nothing is left at the venue or in flight, and rotates the agent key to a burn key. After settlement nobody can rotate the key, cancel orders or close a market, so a live key could keep trading residual margin.6
Exit step 3: settle
Settlement claims the matured USDG and pushes all of it to the vault. Settling before the queued amount arrived reverts
WithdrawalInFlight. Settling with nothing queued reverts NothingQueued. A settle cannot book a phantom loss.What the settle gate proves
The settle guard checks that everything the proposer asked for came back. It does not check that the account is empty: queueing 1 tick satisfies it with the rest of the margin still at Lighter. It cannot be made complete on-chain, because balances and positions live with Lighter’s off-chain sequencer. Completeness is an off-chain guarantee.queue-withdraw --all reads the true L2 balance and refuses with an open position. A proposer who skips it and settles short does not steal anything, but the settlement price is stamped before the rest lands. That moves value from holders who exited at that price to those who stayed.
Shortfalls
If the venue returns less than was queued, for example after a partial fill or a liquidation, the settle guard would hold settlement shut.sherwood lighter acknowledge-shortfall --yes waives that check. It works only after the return was initiated, after a withdrawal was queued, and while a shortfall is observable now. It cannot redirect funds. A slow withdrawal will still mature, so wait instead of waiving.
The waiver does not waive the governor. settleProposal still refuses a price per share below the proposal’s maxDrawdownBps floor (capped at 90%). Declare maxDrawdownBps for a leveraged perp venue with that in mind.
Never rescue before settle
rescueTo(USDG) must never come before settle() in the same batch. Inside that batch the rescued USDG reads as missing and settle reverts WithdrawalInFlight. Outside it nothing is missing, so the waiver reverts NoShortfall. Only the vault owner’s emergency settle gets out. A pre-settle rescue is never needed, because settlement claims matured USDG itself. A proposal’s settle calls are [settle()].
After settlement: late arrivals
Anything still at Lighter after settlement, such as a late tranche or an under-queued remainder, is recovered in three steps:sherwood lighter queue-withdrawqueues the remainder. It works in theSettledstate.sherwood lighter recovercallsrecoverResiduals(), which claims matured USDG onto the clone. It is permissionless and does not push to the vault.- A vault batch calls
clone.rescueTo(USDG)to bring it home, typically in a later proposal’s settle calls or an owner emergency batch.recoverprints this batch, for use aftersettle()only.
Vault liquidity
While a Lighter proposal is open, instant deposits and redemptions are closed. Queued redemptions are paid at the per-proposal price after settlement, which cannot happen until Lighter matures the withdrawal. The exit is slow by construction: close, queue, wait, settle. Tell depositors of a fund that runs Lighter positions about that lock window.CLI
Propose, once the template is deployed and enabled:--pubkey and --key-index default to the key keygen saved. Take --markets ids from sherwood lighter markets. Operator commands:
Full flags: Strategy commands.
Venue addresses
USDG on Robinhood Chain is listed on Deployments. The zkLighter proxy must be an allowed counterparty onTierRegistry before the template can initialize; its address and the template’s are published there once the template is approved.