Dependency graph
This page shows what the Sickle program depends on, and what depends on it.
The graph comes from the use crate:: imports and the CPI targets in
programs/sickle/src on 2026-09-08. Regenerate it when a module moves.
Scope
This repository builds two programs.
| Program | Path | Deployed | Purpose |
|---|---|---|---|
sickle | programs/sickle | yes | The liquidity manager. Every page under docs/dev/ is about it. |
mock-transfer-hook | programs/mock-transfer-hook | no | A test-only Token-2022 transfer hook. One Surfpool test uses it. |
The EVM contracts in ../sickle-contracts are a separate code base with 520
Solidity files. They are not part of this graph.
Layers
The program has six layers. Each layer depends only on the layers below it, with three exceptions listed in section 7.
flowchart TB
L1["1. Entry points<br/>lib.rs (65 instructions)"]
L2["2. Instruction handlers<br/>instructions/*.rs"]
L3["3. Shared runners<br/>venue/{open_increase, decrease_close, claim, compound,<br/>owner_rebalance, keeper_rebalance, keeper_exit, keeper, policy, clmm, dlmm, family}"]
L4["4. Venue adapters<br/>venue/adapters/{orca, raydium}"]
L5["5. Venue bindings<br/>orca.rs, raydium.rs, meteora.rs, instructions/jupiter/cpi.rs"]
L6["6. External programs<br/>Orca, Raydium, Meteora, Jupiter, Token, Token-2022, System, Memo, ed25519"]
X["Cross-cutting<br/>state, constants, errors, events, fees, settings, intent, allocator"]
L1 --> L2 --> L3 --> L4 --> L5 --> L6
L2 --> L5
L2 -.-> X
L3 -.-> X
L4 -.-> X
L5 -.-> X
- Entry points.
lib.rsdeclares the program ID and one public function per instruction. Each function calls one handler. - Instruction handlers. One file per instruction. A handler declares the account struct, then calls a shared runner or a venue binding.
- Shared runners. Generic flow logic that both Orca and Raydium use. A runner owns the order of checks, the fee classes, and the event fields.
- Venue adapters. Implement the runner traits for the concrete Orca and Raydium account structs. Meteora has no adapter. Its handlers call the Meteora binding directly.
- Venue bindings. Hand-written instruction builders and account parsers for each external program. No external crate is used for any venue.
- Cross-cutting modules. Types and helpers that every layer imports.
1. Cross-cutting modules
The "Used by" counts are handler files under programs/sickle/src/instructions/ that import the module directly. Runners, adapters, and bindings import them as well and are named, not counted.
| Module | Depends on | Used by |
|---|---|---|
constants.rs | state, intent | 65 of 65 handler files, every runner |
errors.rs | none | every module |
state.rs | constants, errors, venue::family | 64 of 65 handler files, every runner, fees |
events.rs | constants, intent, state | 36 of 65 handler files, every runner |
fees.rs | constants, errors, state | 13 of 65 handler files, adapters, compound |
settings.rs | constants, errors, events, state | decrease_close, owner_rebalance, keeper_exit, meteora_close_position |
intent.rs | constants, errors, meteora, state | 24 of 65 handler files, every keeper runner |
allocator.rs | none | lib.rs only |
2. Shared runners
flowchart LR
subgraph runners["venue/"]
clmm["clmm<br/>CLMM venue trait"]
dlmm["dlmm<br/>DLMM venue trait"]
family["family<br/>position geometry"]
policy["policy<br/>active-flow checks"]
keeper["keeper<br/>keeper checks"]
open_increase["open_increase"]
decrease_close["decrease_close"]
claim["claim<br/>harvest + compound"]
compound["compound<br/>compound trait impls"]
owner_rebalance["owner_rebalance"]
keeper_rebalance["keeper_rebalance"]
keeper_exit["keeper_exit"]
end
keeper --> clmm
keeper_exit --> clmm
compound --> claim
open_increase --> intent
keeper_rebalance --> intent
keeper_exit --> intent
compound --> intent
decrease_close --> settings
owner_rebalance --> settings
keeper_exit --> settings
keeper_rebalance --> settings
intent["intent.rs"]
settings["settings.rs"]
Every runner also imports errors, state, and events. Those edges are
omitted from the picture.
3. Venue adapters and bindings
flowchart LR
subgraph adapters["venue/adapters/"]
A_orca["orca"]
A_ray["raydium"]
end
subgraph runners["venue/ runners"]
R["open_increase, decrease_close, claim,<br/>owner_rebalance, keeper_rebalance, keeper_exit,<br/>keeper, clmm"]
end
subgraph bindings["bindings"]
B_orca["orca.rs"]
B_ray["raydium.rs"]
B_met["meteora.rs"]
B_jup["instructions/jupiter/cpi.rs"]
end
A_orca --> R
A_ray --> R
A_orca --> B_orca
A_ray --> B_ray
B_ray -->|shared helpers| B_orca
B_met -->|shared helpers| B_orca
A_orca --> fees
A_ray --> fees
A_orca --> intent
A_ray --> intent
B_orca --> P_orca["Orca Whirlpools"]
B_ray --> P_ray["Raydium CLMM"]
B_met --> P_met["Meteora DLMM"]
B_jup --> P_jup["Jupiter v6"]
B_orca --> P_memo["Memo"]
B_ray --> P_memo
B_met --> P_memo
fees --> P_tok["Token / Token-2022"]
intent --> P_ed["ed25519 (via Instructions sysvar)"]
intent --> P_sys["System"]
settings --> P_sys
fees["fees.rs"]
intent["intent.rs"]
settings["settings.rs"]
4. External programs
| Program | Program ID | Called from | Checked against |
|---|---|---|---|
| Orca Whirlpools | whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc | orca.rs | allowlist slot Protocol::OrcaWhirlpool |
| Raydium CLMM | CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK | raydium.rs | allowlist slot Protocol::RaydiumClmm |
| Meteora DLMM | LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo | meteora.rs | allowlist slot Protocol::MeteoraDlmm |
| Jupiter v6 | JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 | instructions/jupiter/cpi.rs | allowlist slot Protocol::Jupiter |
| Memo | MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr | orca.rs, raydium.rs, meteora.rs | fixed constant |
| SPL Token | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA | fees.rs, intent.rs, handlers | Anchor Program type |
| Token-2022 | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb | fees.rs, intent.rs, handlers | Anchor Program type |
| Associated Token | ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL | handlers | Anchor Program type |
| System | 11111111111111111111111111111111 | intent.rs, settings.rs | Anchor Program type |
| ed25519 | Ed25519SigVerify111111111111111111111111111 | intent.rs, read via Instructions sysvar | fixed constant |
| BPF Loader Upgradeable | BPFLoaderUpgradeab1e11111111111111111111111 | initialize_global_config, initialize_intent_domain | Anchor ProgramData type |
The admin sets the four allowlist slots with set_protocol_program. Every
venue CPI checks the target against its slot before the call.
Two more fixed addresses appear in the bindings.
| Address | Meaning |
|---|---|
3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr | Orca's position NFT metadata update authority. |
JCYMX9Nx7DTUdguptRR5LLSc62MEbNmFYsbT5R9yCDGy | The only Meteora pair with a measured compute budget. |
5. Instructions by runner
The 65 instructions fall into 12 groups. The table names the runner and the binding each group uses.
| Group | Instructions | Runner | Binding |
|---|---|---|---|
| Admin | initialize_global_config, migrate_global_config, transfer_admin, accept_admin, set_fee, set_fee_collector, set_guardian, pause, unpause, set_protocol_program, set_global_automator, revoke_global_automator | none | none |
| Sickle | initialize_sickle, set_manager, set_custom_automator, create_sickle_ata, sweep_token | none | none |
| Settings | set_position_settings, unset_position_settings | venue::family | orca, raydium, meteora (geometry only) |
| Orca owner | orca_open_position, orca_increase_liquidity, orca_decrease_liquidity, orca_collect_fees, orca_collect_rewards, orca_close_position, orca_rebalance_position | open_increase, decrease_close, claim, owner_rebalance | orca |
| Orca keeper | harvest_for, compound_for, rebalance_for, exit_for | claim, compound, keeper_rebalance, keeper_exit | orca |
| Raydium owner | raydium_open_position, raydium_increase_liquidity, raydium_decrease_liquidity, raydium_collect_fees, raydium_close_position, raydium_rebalance_position | open_increase, decrease_close, claim, owner_rebalance | raydium |
| Raydium keeper | raydium_harvest_for, raydium_compound_for, raydium_rebalance_for, raydium_exit_for | claim, compound, keeper_rebalance, keeper_exit | raydium |
| Meteora owner | meteora_open_position, meteora_increase_position_length, meteora_add_liquidity, meteora_remove_liquidity, meteora_claim_fees, meteora_claim_reward, meteora_close_position | none | meteora |
| Meteora keeper | meteora_harvest_for, meteora_compound_for, meteora_rebalance_for, meteora_exit_for, meteora_intent_open_position, meteora_intent_increase_position_length, meteora_intent_add_liquidity | claim, compound, keeper_exit, family | meteora |
| Swap | jupiter_swap | none | jupiter |
| Authorization (CLMM) | initialize_intent_domain, set_intent_session, preauthorize_intent, cancel_intent_authorization, expire_intent_authorization, finalize_intent | none | intent |
| Authorization (DLMM) | preauthorize_dlmm_intent, cancel_dlmm_intent_authorization, expire_dlmm_intent_authorization, finalize_dlmm_intent | none | intent |
The Meteora owner group does not use the CLMM runners. Meteora positions are bin ranges, not tick ranges, and a compile-time test proves the Meteora venue cannot satisfy the CLMM venue trait.
6. Crate dependencies
| Crate | Version | Why |
|---|---|---|
anchor-lang | 1.1.2 | Program framework. Features: init-if-needed, event-cpi. |
anchor-spl | 1.1.2 | Token, Token-2022, and Associated Token types. No default features. |
solana-instructions-sysvar | 3.0 | Reads the Instructions sysvar for the ed25519 and finalizer checks. |
solana-sha256-hasher | 3.1.0 | Computes the authorization digest and the intent domain. |
No venue SDK crate is a dependency. Each binding is written by hand and unit-tested against the venue's documented account layout.
7. Edges that cross layers upward
Two edges go from a lower layer to a higher one. They are correct today, but
a module move must keep them in mind. The helper edge that stood here through
contracts 11 and 12 is gone on both venues: adapters/orca.rs and
adapters/raydium.rs now name no handler item except their own account
struct. A third is gone: the settings-transfer
helper both CLMM runners call was venue-agnostic and living in an Orca handler
file, and the move to settings.rs this section predicted has now happened.
venue::clmm,venue::dlmm, andvenue::compoundimport account struct types frominstructions. The runners are generic over the concrete account structs, so this edge is by design.state.rsimportsPositionGeometryfromvenue::family. The settings validation needs the per-venue tick or bin bounds.
Two more edges stay inside one layer. raydium.rs and meteora.rs import the
byte readers, the reward-slot scanner, the tick-range check, and the Memo
program address from orca.rs, which hosts them because the bindings layer
has no module of its own for shared code. Nothing in orca.rs imports the
other two, so there is no cycle. state.rs references orca.rs only in a
unit test.
7.1 What keeps the list above complete
scripts/dev/check-layer-boundaries.py refuses any fully qualified
crate::instructions:: reference in a .rs file under venue/ that is not
inside a use item. It runs in the same CI job as the citation checker and for
the same reason: the rule is about source, so the commits that break it are
commits that touch no documentation.
It does not check that the boundary is clean, and the section above is the
evidence — those edges are imports and it says nothing about them. What it buys
is that the import list is a complete census of what the layer reaches for.
Without it, counting imports undercounts: historical layer 02 note 5 put the
Orca figure at eleven because assert_token_2022_mint was reached through a
qualified path that no import list showed, and the true figure was fourteen.
#[cfg(test)] modules are included, because a rule that exempts test code is a
rule satisfied by moving code into a test module. Comments are excluded, because
an intra-doc link reaches for nothing at runtime.
8. What depends on the program
Off-chain code reads the program through its Anchor IDL. Nothing off-chain links the Rust crate.
flowchart TB
IDL["target/idl/sickle.json<br/>(from anchor build)"]
builder["scripts/builder/<br/>instruction builders, PDAs, flows, intent codec"]
keeper["scripts/keeper/<br/>decision logic, Meteora runtime"]
sim["scripts/keeper-sim.ts"]
canary["scripts/canary*.ts"]
ui["management-ui/<br/>(legacy compatibility tree)"]
tests["tests/*.ts"]
fixtures["tests/fixtures/<br/>intent-vectors.json, meteora-dlmm-layout.json"]
IDL --> builder
builder --> keeper
builder --> sim
builder --> canary
builder --> ui
builder --> tests
keeper --> sim
fixtures --> tests
fixtures -.->|asserted by cargo test| program["programs/sickle"]
program --> IDL
tests/offchain-boundaries.ts enforces the asymmetric edge in this diagram:
every TypeScript import under scripts/builder/ is resolved, and the test
fails if any points into scripts/keeper/. The keeper may consume builder
decoders and transaction machinery; the reusable builder cannot depend on the
runtime that orchestrates it.
Two fixture files are shared between Rust and TypeScript. Both test suites assert them, so a byte-layout change fails on both sides.
| Fixture | Pins |
|---|---|
tests/fixtures/intent-vectors.json | The AuthorizationV2 digest layout. |
tests/fixtures/meteora-dlmm-layout.json | The Meteora account parser offsets. |
Related
- Style guide
- orca_open_position, the first instruction page.
CLAUDE.md, the design record for every divergence from the EVM reference.