Sickle Sol

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.

ProgramPathDeployedPurpose
sickleprograms/sickleyesThe liquidity manager. Every page under docs/dev/ is about it.
mock-transfer-hookprograms/mock-transfer-hooknoA 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.rs declares 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.

ModuleDepends onUsed by
constants.rsstate, intent65 of 65 handler files, every runner
errors.rsnoneevery module
state.rsconstants, errors, venue::family64 of 65 handler files, every runner, fees
events.rsconstants, intent, state36 of 65 handler files, every runner
fees.rsconstants, errors, state13 of 65 handler files, adapters, compound
settings.rsconstants, errors, events, statedecrease_close, owner_rebalance, keeper_exit, meteora_close_position
intent.rsconstants, errors, meteora, state24 of 65 handler files, every keeper runner
allocator.rsnonelib.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

ProgramProgram IDCalled fromChecked against
Orca WhirlpoolswhirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCcorca.rsallowlist slot Protocol::OrcaWhirlpool
Raydium CLMMCAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqKraydium.rsallowlist slot Protocol::RaydiumClmm
Meteora DLMMLBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxometeora.rsallowlist slot Protocol::MeteoraDlmm
Jupiter v6JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4instructions/jupiter/cpi.rsallowlist slot Protocol::Jupiter
MemoMemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHrorca.rs, raydium.rs, meteora.rsfixed constant
SPL TokenTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DAfees.rs, intent.rs, handlersAnchor Program type
Token-2022TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEbfees.rs, intent.rs, handlersAnchor Program type
Associated TokenATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knLhandlersAnchor Program type
System11111111111111111111111111111111intent.rs, settings.rsAnchor Program type
ed25519Ed25519SigVerify111111111111111111111111111intent.rs, read via Instructions sysvarfixed constant
BPF Loader UpgradeableBPFLoaderUpgradeab1e11111111111111111111111initialize_global_config, initialize_intent_domainAnchor 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.

AddressMeaning
3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhrOrca's position NFT metadata update authority.
JCYMX9Nx7DTUdguptRR5LLSc62MEbNmFYsbT5R9yCDGyThe 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.

GroupInstructionsRunnerBinding
Admininitialize_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_automatornonenone
Sickleinitialize_sickle, set_manager, set_custom_automator, create_sickle_ata, sweep_tokennonenone
Settingsset_position_settings, unset_position_settingsvenue::familyorca, raydium, meteora (geometry only)
Orca ownerorca_open_position, orca_increase_liquidity, orca_decrease_liquidity, orca_collect_fees, orca_collect_rewards, orca_close_position, orca_rebalance_positionopen_increase, decrease_close, claim, owner_rebalanceorca
Orca keeperharvest_for, compound_for, rebalance_for, exit_forclaim, compound, keeper_rebalance, keeper_exitorca
Raydium ownerraydium_open_position, raydium_increase_liquidity, raydium_decrease_liquidity, raydium_collect_fees, raydium_close_position, raydium_rebalance_positionopen_increase, decrease_close, claim, owner_rebalanceraydium
Raydium keeperraydium_harvest_for, raydium_compound_for, raydium_rebalance_for, raydium_exit_forclaim, compound, keeper_rebalance, keeper_exitraydium
Meteora ownermeteora_open_position, meteora_increase_position_length, meteora_add_liquidity, meteora_remove_liquidity, meteora_claim_fees, meteora_claim_reward, meteora_close_positionnonemeteora
Meteora keepermeteora_harvest_for, meteora_compound_for, meteora_rebalance_for, meteora_exit_for, meteora_intent_open_position, meteora_intent_increase_position_length, meteora_intent_add_liquidityclaim, compound, keeper_exit, familymeteora
Swapjupiter_swapnonejupiter
Authorization (CLMM)initialize_intent_domain, set_intent_session, preauthorize_intent, cancel_intent_authorization, expire_intent_authorization, finalize_intentnoneintent
Authorization (DLMM)preauthorize_dlmm_intent, cancel_dlmm_intent_authorization, expire_dlmm_intent_authorization, finalize_dlmm_intentnoneintent

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

CrateVersionWhy
anchor-lang1.1.2Program framework. Features: init-if-needed, event-cpi.
anchor-spl1.1.2Token, Token-2022, and Associated Token types. No default features.
solana-instructions-sysvar3.0Reads the Instructions sysvar for the ed25519 and finalizer checks.
solana-sha256-hasher3.1.0Computes 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.

  1. venue::clmm, venue::dlmm, and venue::compound import account struct types from instructions. The runners are generic over the concrete account structs, so this edge is by design.
  2. state.rs imports PositionGeometry from venue::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.

FixturePins
tests/fixtures/intent-vectors.jsonThe AuthorizationV2 digest layout.
tests/fixtures/meteora-dlmm-layout.jsonThe Meteora account parser offsets.

Source: docs/dev/dependency-graph.md in vfat-io/sickle-programs, vendored here by its sync workflow. Edit the markdown there; this page is rebuilt from it on deploy.