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_accountsistrue).quote— the price. It reproduces the on-chainswaphandler exactly: the same base + dynamic fee, then a tick/bin walk that calls the program's owncompute_swap_step(AMM) /fill_exact_in(DLMM) between boundaries. Amounts are bit-identical to a realized swap at the same state and slot. BothExactInandExactOutare supported.get_swap_and_account_metas— the account list the program'sswapinstruction 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
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
Swapenum variant.jupiter-amm-interface'sSwapenum has no Zenith variant, soget_swap_and_account_metasreturns 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 intests/*-integration. - The crate is a workspace member but not a default member, so a normal
cargo buildstays light; target it with-p zenith-jupiter.