Skip to content

zenith-camm — Constant Product + Idle-Reserve Yield

zenith-camm is a full-range constant-product AMM (x·y=k) with one distinguishing feature: its idle reserves earn yield. A share of the pool's reserves is marked as "deployed", and yield accrues on that principal over time, flowing back into the reserves and raising every LP's share value — without the LP doing anything.

Program ID (devnet): CjjcK3rnskHswBpTgZquLGgS7P2QyzeaNwwe98FUUdy7

Core ideas

Constant product with LP shares

Reserves x and y trade against the invariant x·y=k. Liquidity providers receive fungible LP shares:

  • The first deposit bootstraps shares from the geometric mean of the two amounts, and a small MINIMUM_LIQUIDITY is permanently locked to prevent the share price from being manipulated to zero.
  • Later deposits mint shares proportional to the pool they add.
  • Redeeming shares returns a proportional slice of the reserves.

Swaps use the standard out = f(in) / in = f(out) constant-product formulas (exact, in fixed point), with a base fee split into an LP share (left in the pool, so it accrues to all holders) and a protocol share.

The yield vault

This is what sets zenith-camm apart. Reserves that aren't needed as an immediate swap buffer can be marked deployed, and yield accrues on them:

accrued_yield(deployed, rate, elapsed_slots) = deployed · rate · elapsed / SCALE

The share price is therefore (reserves + pending yield) / lp_supply, and it only ever rises as yield lands.

The devnet implementation is an honest mock — there is no external lending market on devnet to integrate against, so Zenith does not pretend to. Instead:

  • Deployed principal physically stays in the reserve vault (it is an accounting marker only), so the pool is always solvent for swaps and there is no withdraw-on-demand risk.
  • Yield is paid from a pre-funded yield-source vault into the reserves when harvested, which is exactly what raises the share price.

A buffer (in basis points) keeps a slice of reserves undeployed so swaps always have working liquidity.

No just-in-time yield capture

Depositing right before a harvest to skim un-accrued yield is blocked: add_liquidity prices any pending, un-harvested yield into the effective reserve used for share minting (computed on-chain from the deployed amount, rate, and elapsed slots). A late depositor buys in at the already-grown share price.

State

The Pool account holds the reserves, LP mint, fee parameters, and the yield fields: deployed_a / deployed_b, last_accrual_slot, yield_rate, and the buffer_bps.

Instructions

InstructionPurpose
initialize_poolOpen the pool, LP mint, and reserve vaults; set fee parameters.
add_liquidityDeposit both tokens, mint LP shares (pending yield priced in).
remove_liquidityBurn LP shares, withdraw a proportional slice of reserves.
swapConstant-product trade with fee split.
yield_vaultYield operations: initialize_yield (set rate + buffer, fund sources), harvest_yield (pay accrued yield into reserves — permissionless), rebalance_to_vault (harvest, then re-mark the deployable amount).

What proves it works

  • Constant-product and LP-share golden vectors in the math crate.
  • Devnet e2e checks in sdk/scripts: a swap whose realized output equals the SDK quote exactly, an LP add/remove round-trip that conserves value (within a bounded pending-yield charge), and a harvest that grows the reserve by exactly the amount transferred from the yield source.

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