Skip to content

Jupiter integration

Zenith ships a Rust adapter crate, zenith-jupiter, that implements Jupiter's Amm interface (jupiter-amm-interface 0.6.1) for the AMM and DLMM engines. It lets a Jupiter-compatible router load a Zenith pool, quote against it, and build a swap leg — the standard way a new venue plugs into the aggregator.

What the adapter does

Each engine has an adapter type — ZenithAmm and ZenithDlmm — implementing the Amm trait:

  • from_keyed_account — decode a pool/pair from its on-chain account.
  • get_accounts_to_update / update — declare and cache the accounts a quote needs: the pool + config + a window of tick arrays (AMM), or the pair + a window of bin arrays (DLMM). The window follows the current tick / active bin, so the set is dynamic (has_dynamic_accounts is true).
  • quote — the price. It reproduces the on-chain swap handler exactly: the same base + dynamic fee, then a tick/bin walk that calls the program's own compute_swap_step (AMM) / fill_exact_in (DLMM) between boundaries. Amounts are bit-identical to a realized swap at the same state and slot. Both ExactIn and ExactOut are supported.
  • get_swap_and_account_metas — the account list the program's swap instruction consumes (owner, pool, config, authority, vaults, user ATAs, token program, and the tick/bin arrays in travel order).

Quote parity is enforced

The adapters reuse the programs' exact math, so the quote and the fill can't drift. This is pinned by tests that run a real swap on solana-program-test and assert the adapter's quote() equals the realized token delta — both engines, both directions (tests/amm-integration, tests/dlmm-integration). If you change swap math, that parity test fails until the adapter agrees again.

Using it

rust
use zenith_jupiter::ZenithAmm;
use jupiter_amm_interface::{Amm, AmmContext, KeyedAccount, QuoteParams, SwapMode, FeeMode};

// 1. Load the pool.
let mut amm = ZenithAmm::from_keyed_account(&keyed_pool, &AmmContext::default())?;

// 2. Fetch the accounts it asks for, then feed them back.
let addrs = amm.get_accounts_to_update();       // pool, config, tick arrays
let account_map = fetch(&addrs);                 // your RPC / account cache
amm.update(&account_map)?;

// 3. Quote.
let quote = amm.quote(&QuoteParams {
    amount: 1_000_000,
    input_mint,
    output_mint,
    swap_mode: SwapMode::ExactIn,
    fee_mode: FeeMode::Normal,
})?;
// quote.out_amount == the realized on-chain output at this state.

// 4. Build the leg.
let leg = amm.get_swap_and_account_metas(&swap_params)?;

ZenithDlmm is identical, over bin arrays instead of tick arrays.

Status: ready, not yet listed

The adapter is integration-ready and provably correct — but a live route on jup.ag requires two things Zenith does not yet have:

  • A Swap enum variant. jupiter-amm-interface's Swap enum has no Zenith variant, so get_swap_and_account_metas returns a placeholder tag today. A real router can't select the leg until a variant is added — which happens when the adapter is submitted to and merged by Jupiter.
  • Mainnet. Jupiter's hosted router and quote API index mainnet pools; they do not route arbitrary devnet markets.

So on devnet you can build the leg from the adapter and execute it directly against the program (which is what the parity tests do), but discovery/routing through the real aggregator is a post-mainnet, Jupiter-side step. Nothing here blocks that — the adapter is the prerequisite, and it's done.

Where it lives

  • Crate: crates/zenith-jupiter (ZenithAmm, ZenithDlmm).
  • Build/test: cargo test -p zenith-jupiter (host math + decode), plus the on-chain parity tests in tests/*-integration.
  • The crate is a workspace member but not a default member, so a normal cargo build stays light; target it with -p zenith-jupiter.

Built on Solana devnet. Integer-exact math, no floats.