Skip to main content
Rise is the developer-facing SDK surface for Phoenix perpetuals. It currently ships as:
  • rise/ts — the TypeScript SDK, with HTTP route clients, a unified exchange-aware client, instruction builders, Flight helpers, and WebSocket adapters
  • rise/rust — the Rust workspace, centered on the phoenix-rise crate, with typed HTTP/WS clients, transaction builders, math helpers, and low-level instruction builders

Where to start

  • Reach for PhoenixHttpClient / client.api when you need public HTTP data or invite activation.
  • Reach for createPhoenixClient(...) when you want exchange metadata, PDA derivation, order-packet helpers, and client.ixs.
  • Reach for client.streams or createPhoenixWsClient(...) when you want typed live adapters.
  • Reach for PhoenixTxBuilder in Rust when you want to construct and sign your own Solana instructions.

Runnable entry points

Onboarding: access code vs referral code

These invite routes are not interchangeable:
  • Use POST /v1/invite/activate when you have an access code / allowlist code. Send that value as code.
  • Use POST /v1/invite/activate-with-referral when you have a referral code. Send that value as referral_code.
import { PhoenixHttpClient } from "@ellipsis-labs/rise";

const client = new PhoenixHttpClient({
  apiUrl: "https://perp-api.phoenix.trade",
});

const authority = "AUTHORITY_PUBKEY";

const activatedWithAccessCode = await client.invite().activateInvite({
  authority,
  code: "ACCESS_CODE",
});

const activatedWithReferral = await client.invite().activateInviteWithReferral({
  authority,
  referral_code: "REF_CODE",
});
For a ready-to-run Rust version of this flow, see rust/sdk/examples/register_trader.rs.

Fetching exchange, market, and trader state

The HTTP surface is intentionally split by what kind of state you want:
  • exchange().getSnapshot() — exchange-wide state plus every market’s current config snapshot
  • exchange().getMarket(symbol) — one market’s fees, risk, funding cadence, and configuration
  • orderbook().getOrderbook(symbol) — an HTTP L2 snapshot for one market
  • traders().getTraderStateSnapshot(...) (TypeScript) or traders().get_trader(...) (Rust) — a trader-centric view of collateral, positions, orders, and triggers
  • markets().getMarketStatsHistory(...) and funding().getFundingRateHistory(...) — time-series data for frontends, vault products, and analytics
import { PhoenixHttpClient } from "@ellipsis-labs/rise";

const client = new PhoenixHttpClient({
  apiUrl: "https://perp-api.phoenix.trade",
});

const symbol = "SOL";
const authority = "AUTHORITY_PUBKEY";

const [snapshot, market, orderbook, trader] = await Promise.all([
  client.exchange().getSnapshot(),
  client.exchange().getMarket(symbol),
  client.orderbook().getOrderbook(symbol),
  client.traders().getTraderStateSnapshot(authority, { traderPdaIndex: 0 }),
]);
For broader TypeScript walkthroughs, see ts/examples/01-http-client.ts and ts/examples/phoenix-client-example.ts.

Order placement and cancellation

The SDK separates packet construction from instruction construction:
  • Build packet sizes and prices with client.orderPackets
  • Build or wrap the actual Solana instructions with client.ixs
  • Use the lower-level builders when you need conditional-account setup or other specialized flows
import {
  Direction,
  Side,
  StopLossOrderKind,
  createPhoenixClient,
} from "@ellipsis-labs/rise";

const client = createPhoenixClient({
  apiUrl: "https://perp-api.phoenix.trade",
  rpcUrl: "https://api.mainnet-beta.solana.com",
  ws: false,
  exchangeMetadata: { stream: false },
});

const authority = "AUTHORITY_PUBKEY";
const symbol = "SOL-PERP";

const limitPacket = await client.orderPackets.buildLimitOrderPacket({
  symbol,
  side: Side.Bid,
  priceUsd: "150.50",
  baseUnits: "0.25",
});

const placeLimitIx = await client.ixs.placeLimitOrder({
  authority,
  symbol,
  orderPacket: limitPacket,
});

const marketPacket = await client.orderPackets.buildMarketOrderPacket({
  symbol,
  side: Side.Bid,
  baseUnits: "0.25",
});

const placeMarketIx = await client.ixs.placeMarketOrder({
  authority,
  symbol,
  orderPacket: marketPacket,
});

const stopLossIx = await client.ixs.buildPlaceStopLoss({
  authority,
  symbol,
  tradeSide: Side.Ask,
  executionDirection: Direction.LessThan,
  orderKind: StopLossOrderKind.IOC,
  triggerPrice: 1420n,
});

const cancelByIdIx = await client.ixs.buildCancelOrdersById({
  authority,
  symbol,
  orders: [{ price: 1500n, orderSequenceNumber: "123" }],
});

const cancelAllIx = await client.ixs.buildCancelAll({ authority, symbol });

const cancelStopLossIx = await client.ixs.buildCancelStopLoss({
  authority,
  symbol,
  executionDirection: Direction.LessThan,
});
buildPlaceStopLoss(...) takes tick-based trigger prices. When starting from USD prices, convert them from market metadata first, or reuse the conditional-order patterns in ts/examples/05-cancel-all-conditional-orders.ts.
Runnable examples:

Flight builder activation and routed orders

Flight support in Rise is currently beta and should not yet be treated as a stable production surface.
Flight is the builder-routing layer. Key pieces:
  • The builder still needs a Phoenix trader account
  • Builder registration is its own on-chain instruction
  • The builder’s associated trader account is the fee collector for Flight-routed orders
  • Once a client is configured with flight: { builderAuthority, ... }, supported order instructions are wrapped automatically
When you register Flight against a builder authority and its associated trader account, all builder fees from Flight-routed orders accrue to that builder trader account. Those fees are withdrawable from the Phoenix frontend.
import {
  MarginType,
  Side,
  createPhoenixClient,
  flight,
} from "@ellipsis-labs/rise";

const builderAuthority = "BUILDER_AUTHORITY";

const client = createPhoenixClient({
  apiUrl: "https://perp-api.phoenix.trade",
  rpcUrl: "https://api.mainnet-beta.solana.com",
  ws: false,
  flight: {
    builderAuthority,
    builderPdaIndex: 0,
    builderSubaccountIndex: 0,
  },
});

const registerTraderIx = await client.ixs.buildRegisterTrader({
  authority: builderAuthority,
  marginType: MarginType.Cross,
});

const registerBuilderIx = await flight.buildRegisterBuilderIx({
  traderAuthority: builderAuthority,
  traderPdaIndex: 0,
  traderSubaccountIndex: 0,
  feeBps: 25n,
});

const routedMarketIx = await client.ixs.placeMarketOrder({
  authority: "TRADER_AUTHORITY",
  symbol: "SOL-PERP",
  orderPacket: await client.orderPackets.buildMarketOrderPacket({
    symbol: "SOL-PERP",
    side: Side.Bid,
    baseUnits: "0.25",
  }),
});

console.log(routedMarketIx.programAddress === flight.FLIGHT_PROGRAM_ADDRESS);
For more Flight-specific TypeScript examples, see ts/src/flight/README.md.

Live market data

Use the typed WebSocket adapters when you want continuous updates instead of a single HTTP snapshot.
TypeScript
import { createPhoenixClient } from "@ellipsis-labs/rise";

const client = createPhoenixClient({
  apiUrl: "https://perp-api.phoenix.trade",
  ws: { connectMode: "eager" },
});

for await (const update of client.streams!.l2Book("SOL-PERP")) {
  console.log(update.bids[0], update.asks[0]);
}
Ready-to-run TypeScript stream examples: