← Protocols
Perennial
01Description

Hyper-efficient perps liquidity layer on Arbitrum and Base. A two-sided LP-vs-trader model where makers provide liquidity to specific markets, with VAA-pulled Pyth prices and oracle-aware funding.

02Best for
  • 01perps frontends that build their own UX on top of shared liquidity
  • 02vault strategies acting as makers
  • 03developer-built leveraged tokens / structured products
  • 04Arbitrum + Base markets with Pyth pricing
  • 05low-fee derivatives integrators
03Install
  • pnpm add @perennial/sdk viem
04Environment variables
VariableScopeDescription
NEXT_PUBLIC_PERENNIAL_CHAIN_IDClient`42161` (Arbitrum) or `8453` (Base).
NEXT_PUBLIC_RPC_URLClientRPC for the selected chain (must support `eth_call` reliably).
NEXT_PUBLIC_PERENNIAL_GRAPH_URLClientHosted subgraph URL — required by the SDK for trade history and stats.
PYTH_HERMES_URLClientPyth Hermes endpoint (https://hermes.pyth.network) — Perennial pulls VAAs to settle positions.
05Prompt snippet
Use `@perennial/sdk`: `const sdk = new PerennialSDK({ chainId, rpcUrl, graphUrl, pythUrl })`. Open or modify a position with `sdk.markets.update({ market, address, sizeDelta, collateralDelta, signer })` — pass an empty `bytes` for `priceCommitment` to use the latest oracle, or fetch a Pyth VAA from Hermes and pass it for atomic settlement. Read user state via `sdk.markets.snapshots({ address, marketAddresses })` and `sdk.markets.openOrders({ address })`. Each market is a single contract; market addresses are looked up via `MarketFactory`.
06Gotchas
  • Settlement is request-based: `update()` queues a position change at the current oracle version; the actual position only updates after a Pyth price update for the next version is committed. Until then `pendingPosition` ≠ `position`.
  • Funding has two components — `funding` (long/short imbalance) and `interest` (utilization). Both accrue per-block to maker LPs. UIs that show only one will under-report yield.
  • Makers (LPs) can be socialized-loss when traders win net — vault providers must monitor `riskParameter.makerLimit` and the maker side's `efficiency` value.
  • Liquidations require a keeper to call `update()` with a liquidation flag; small positions may sit in 'liquidatable' state briefly. Don't assume a margin breach means an instant close.
  • Pyth VAA payloads are time-bounded — fetching a VAA and submitting it more than ~60 seconds later reverts with `PythErrors.StalePrice`. Always fetch immediately before sending the tx.
  • Arbitrum and Base have different `MarketFactory` addresses and different listed markets — never hard-code a market address; resolve by `(asset, chainId)` from the SDK.
07Alternatives