Architecture
JIKA is one Anchor program with four instructions and one account type. Everything else (keepers, apps, consumers) sits outside it.
Components
| Part | Where | Role |
|---|---|---|
jika program | programs/jika | Reads pools, computes spot, band and steady price, serves read |
| Feed account | PDA ["feed", mint] | One per token: venues, buckets, steady price, band |
| DEX adapters | venue.rs | Decode the seven supported pool layouts into price and depth |
| Tick math | math.rs | log and pow on 1.0001 in fixed point, Q64.64 prices |
| Oracle math | oracle.rs | Weighted median, band terms, steady step, the three-bucket window |
| Client | sdk/jika.mjs | PDAs, instruction builders, decoders |
| Keeper | sdk/keeper.mjs | One update per feed per 10-second bucket |
The flow of one update
| Step | Who | What happens |
|---|---|---|
| 1 | Anyone | Sends update with the feed, the USDC feed if needed, and each stored venue's accounts |
| 2 | Program | Checks every venue account is the one the pool names, in stored order |
| 3 | Program | Reads each pool's price (tick) and depth; converts USDC pools through the USDC feed |
| 4 | Program | Updates each venue's confirmed depth; weight = min(depth now, confirmed) |
| 5 | Program | Takes the depth-weighted median (spot) and records it in the current 10-second bucket |
| 6 | Program | Moves the steady price toward the least extreme sample of the last three buckets |
| 7 | Program | Computes the band and stores everything with last_update_slot, emits FeedUpdated |
The flow of one read
| Step | Who | What happens |
|---|---|---|
| 1 | Consumer program | CPI read(max_age_slots, max_conf_ticks) with the feed and, for dollars, the USDC feed |
| 2 | JIKA | Fails with NotLive, Stale or TooUncertain if any check fails |
| 3 | JIKA | Returns PriceView with the steady price as Q64.64 |
Accounts
The feed account is 720 bytes. Its layout, for readers that do not use Anchor:
| Offset | Field | Type |
|---|---|---|
| 0 | Anchor discriminator | [u8; 8] |
| 8 | mint | Pubkey |
| 40 | decimals | u8 |
| 41 | bump | u8 |
| 42 | venue_count | u8 |
| 43 | has_spot | bool |
| 44 | steady_set | bool |
| 45 | created_slot | u64 |
| 53 | last_update_slot | u64 |
| 61 | credited_slots | u64 |
| 69 | steady_x16 (steady tick x 65,536) | i64 |
| 77 | spot_tick | i32 |
| 81 | conf_ticks | u32 |
| 85 | depth (lamports) | u64 |
| 93 | top_share_bps | u16 |
| 95 | live_venues | u8 |
| 96 | buckets[3]: id u64, lo i32, hi i32 | 16 bytes each |
| 144 | venues[8]: pool, kind, base_is_first, quote (0 SOL, 1 USDC), alive, added_slot, tick, depth, depth_ewma, weight | 72 bytes each |
Venue accounts passed to update
Every stored venue, in the feed's order, as the pool followed by:
| Kind | DEX | After the pool |
|---|---|---|
| 0 | Orca Whirlpool | vault A (pool offset 133), vault B (213) |
| 1 | Raydium CLMM | vault 0 (137), vault 1 (169) |
| 2 | Raydium CPMM | vault 0 (72), vault 1 (104) |
| 3 | Raydium AMM v4 | coin vault (336), pc vault (368) |
| 4 | Meteora DLMM | the active bin array: PDA ["bin_array", pool, floor(active_id / 70) as i64 LE] under the DLMM program |
| 5 | pump.fun curve | nothing |
| 6 | PumpSwap | base vault (139), quote vault (171) |
sdk/jika.mjs builds this list from chain state with updateIx(connection, mint).
Units
Every feed is priced in SOL (lamports per base raw unit). wSOL has no feed of its own. USDC pools are converted through the one USDC feed, and a dollar price is a read with the USDC feed as quote_feed.
Compute
| Instruction | Compute units |
|---|---|
update, 3 venues | 52,162 |
update, 8 venues | 74,896 |
read | 6,400 |
Build
Anchor 0.30.1 on cargo-build-sbf 2.1.0. target/deploy/jika.so is 302,952 bytes, sha256 2f6141eafa89c9d6b89bdb8c0654a4df32f24f1e3f461185465fcfe6984ced0a.