Entry points and program-wide plumbing
This layer declares the program ID, dispatches every instruction to its handler, installs the heap allocator, and defines the shared constants.
Files
| Path | Lines | Purpose |
|---|---|---|
programs/sickle/src/lib.rs | 630 | Module list, program ID, allocator install, and the 65 instruction entry functions. |
programs/sickle/src/allocator.rs | 104 | An upward bump allocator that can use a requested heap frame above 32 KiB. |
programs/sickle/src/constants.rs | 212 | PDA seeds, fee and settings caps, default program IDs, and authorization sizes. |
programs/sickle/Cargo.toml | 57 | Crate metadata, Cargo features, and dependencies. |
/Cargo.toml | 20 | Workspace members, release profile, and the shared lint table. |
Anchor.toml | 77 | Program IDs per cluster, test scripts, and validator clone list. |
Responsibilities
- Declare the on-chain program ID with
declare_id!atlib.rs:40. - Export the crate modules that every other layer imports at
lib.rs:5-18. - Define one public entry function per instruction inside the
#[program]module atlib.rs:55-630. - Forward each entry function to the handler with the same name in
instructions/. - Replace the default SBF heap allocator when the
custom-heapfeature is on atlib.rs:43-53. - Hold the PDA seed bytes, the fee cap, the settings caps, and the authorization sizes in
constants.rs. - Hold the default venue program IDs that
initialize_global_configwrites into the allowlist atinstructions/admin/initialize_global_config.rs:70-76. - Select the Cargo features that Anchor and the Solana entrypoint read at
programs/sickle/Cargo.toml:13-22.
Public items
lib.rs
| Item | Kind | Meaning |
|---|---|---|
ID, ID_CONST | static | The program ID qCxmYKG8poFtsi2PoWDuNgVoPPAcUVLGGDJWSUQ9VTc. Generated by declare_id! at lib.rs:40. |
EVENT_AUTHORITY_AND_BUMP | const | The event authority PDA [b"__event_authority"] and its bump. Generated by declare_id! under the event-cpi feature. The events layer reads it at events.rs:89. |
constants, errors, events, ... | mod | 13 public modules at lib.rs:7-18. The allocator module is private and compiled only under test or under custom-heap on the solana target at lib.rs:5-6. |
SBF_ALLOCATOR | static | The global allocator. Installed only when custom-heap is on, no-entrypoint is off, and the target is solana at lib.rs:43-53. |
sickle | mod | The #[program] module. Anchor generates the entrypoint and the dispatcher from it. It contains 65 public functions at lib.rs:59-629. |
Anchor derives each instruction discriminator as the first 8 bytes of sha256("global:<function_name>"). The dispatcher compares the first 8 bytes of the instruction data against each discriminator and calls the matching function with the rest of the data. Source: anchor-syn-1.1.2/src/codegen/program/common.rs:5-16 and dispatch.rs:76-95.
Each entry function calls the handler function with the same name and the same arguments. No entry function checks anything itself. The table lists every entry function, grouped as in the dependency graph, with its line and its argument list.
Admin
| Instruction | lib.rs | Arguments |
|---|---|---|
initialize_global_config | 59 | fee_collector: Pubkey |
migrate_global_config | 66 | meteora_dlmm_program: Pubkey |
transfer_admin | 73 | new_admin: Pubkey |
accept_admin | 77 | none |
set_fee_collector | 81 | fee_collector: Pubkey |
set_fee | 85 | action: FeeAction, fee_bps: u16 |
set_guardian | 89 | guardian: Pubkey |
pause | 93 | none |
unpause | 97 | none |
set_protocol_program | 101 | protocol: Protocol, program_id: Pubkey |
set_global_automator | 109 | authority: Pubkey, permissions: u8 |
revoke_global_automator | 117 | none |
Sickle
| Instruction | lib.rs | Arguments |
|---|---|---|
initialize_sickle | 121 | manager: Pubkey, referral_code: [u8; 32] |
set_manager | 129 | new_manager: Pubkey |
set_custom_automator | 133 | automator: Pubkey, permissions: u8 |
create_sickle_ata | 145 | none |
sweep_token | 149 | amount: Option<u64> |
Settings
| Instruction | lib.rs | Arguments |
|---|---|---|
set_position_settings | 552 | position: Pubkey, args: PositionSettingsArgs |
unset_position_settings | 627 | none |
Orca owner
| Instruction | lib.rs | Arguments |
|---|---|---|
orca_open_position | 269 | tick_lower_index: i32, tick_upper_index: i32, with_token_metadata: bool |
orca_increase_liquidity | 283 | liquidity_amount: u128, token_max_a: u64, token_max_b: u64, hook_lens: [u8; 2] |
orca_decrease_liquidity | 345 | liquidity_amount: u128, token_min_a: u64, token_min_b: u64, hook_lens: [u8; 2] |
orca_collect_fees | 299 | hook_lens: [u8; 2] |
orca_collect_rewards | 306 | reward_hook_lens: [u8; 3] |
orca_close_position | 361 | reward_hook_lens: [u8; 3] |
orca_rebalance_position | 369 | new_tick_lower_index: i32, new_tick_upper_index: i32, token_min_a: u64, token_min_b: u64, with_token_metadata: bool, hook_lens: [u8; 2], reward_hook_lens: [u8; 3] |
Orca keeper
| Instruction | lib.rs | Arguments |
|---|---|---|
harvest_for | 313 | hook_lens: [u8; 2], reward_hook_lens: [u8; 3] |
compound_for | 321 | hook_lens: [u8; 2], reward_hook_lens: [u8; 3] |
rebalance_for | 392 | new_tick_lower_index: i32, new_tick_upper_index: i32, token_min_a: u64, token_min_b: u64, with_token_metadata: bool, hook_lens: [u8; 2], reward_hook_lens: [u8; 3] |
exit_for | 329 | token_min_a: u64, token_min_b: u64, hook_lens: [u8; 2], reward_hook_lens: [u8; 3] |
Raydium owner
| Instruction | lib.rs | Arguments |
|---|---|---|
raydium_open_position | 414 | tick_lower_index: i32, tick_upper_index: i32, tick_array_lower_start_index: i32, tick_array_upper_start_index: i32, with_metadata: bool |
raydium_increase_liquidity | 432 | liquidity: u128, amount_0_max: u64, amount_1_max: u64 |
raydium_decrease_liquidity | 446 | liquidity: u128, amount_0_min: u64, amount_1_min: u64 |
raydium_collect_fees | 460 | none |
raydium_close_position | 466 | none |
raydium_rebalance_position | 473 | new_tick_lower_index: i32, new_tick_upper_index: i32, new_tick_array_lower_start_index: i32, new_tick_array_upper_start_index: i32, amount_0_min: u64, amount_1_min: u64, with_metadata: bool |
Raydium keeper
| Instruction | lib.rs | Arguments |
|---|---|---|
raydium_harvest_for | 495 | none |
raydium_compound_for | 499 | none |
raydium_rebalance_for | 506 | new_tick_lower_index: i32, new_tick_upper_index: i32, new_tick_array_lower_start_index: i32, new_tick_array_upper_start_index: i32, amount_0_min: u64, amount_1_min: u64, with_metadata: bool |
raydium_exit_for | 528 | amount_0_min: u64, amount_1_min: u64 |
Meteora owner
| Instruction | lib.rs | Arguments |
|---|---|---|
meteora_open_position | 177 | lower_bin_id: i32, width: i32 |
meteora_increase_position_length | 243 | length_to_add: u16, side: u8 |
meteora_add_liquidity | 153 | parameters: DlmmLiquidityParameterByStrategy |
meteora_remove_liquidity | 255 | from_bin_id: i32, to_bin_id: i32, bps_to_remove: u16 |
meteora_claim_fees | 189 | min_bin_id: i32, max_bin_id: i32 |
meteora_claim_reward | 197 | reward_index: u64, min_bin_id: i32, max_bin_id: i32 |
meteora_close_position | 185 | none |
Meteora keeper
| Instruction | lib.rs | Arguments |
|---|---|---|
meteora_harvest_for | 211 | min_bin_id: i32, max_bin_id: i32 |
meteora_compound_for | 219 | min_bin_id: i32, max_bin_id: i32 |
meteora_rebalance_for | 235 | amount_x_min: u64, amount_y_min: u64 |
meteora_exit_for | 227 | amount_x_min: u64, amount_y_min: u64 |
meteora_intent_open_position | 167 | none |
meteora_intent_increase_position_length | 171 | none |
meteora_intent_add_liquidity | 160 | parameters: DlmmLiquidityParameterByStrategy |
Swap
| Instruction | lib.rs | Arguments |
|---|---|---|
jupiter_swap | 536 | amount_in: u64, min_amount_out: u64, fee_action: Option<FeeAction>, route_data: Vec<u8> |
Authorization, CLMM
| Instruction | lib.rs | Arguments |
|---|---|---|
initialize_intent_domain | 560 | domain_salt: [u8; 32] |
set_intent_session | 567 | signer: Pubkey, args: IntentSessionArgs |
preauthorize_intent | 575 | authorization: AuthorizationV2 |
cancel_intent_authorization | 589 | none |
expire_intent_authorization | 603 | none |
finalize_intent | 617 | none |
Authorization, DLMM
| Instruction | lib.rs | Arguments |
|---|---|---|
preauthorize_dlmm_intent | 582 | authorization: DlmmAuthorizationV1 |
cancel_dlmm_intent_authorization | 593 | none |
expire_dlmm_intent_authorization | 609 | none |
finalize_dlmm_intent | 621 | none |
The argument types come from other layers. FeeAction and Protocol are enums in state.rs:21-28 and state.rs:36-43. PositionSettingsArgs is at state.rs:228-237. IntentSessionArgs is at instructions/intent/set_intent_session.rs:14-21. DlmmLiquidityParameterByStrategy is at meteora.rs:192. AuthorizationV2 and DlmmAuthorizationV1 are in intent.rs. The reward array length 3 is orca::WHIRLPOOL_REWARD_SLOT_COUNT.
allocator.rs
| Item | Kind | Meaning |
|---|---|---|
MAX_HEAP_FRAME_BYTES | const | 256 KiB. The largest address range the allocator hands out. allocator.rs:14. |
UpwardBumpAllocator | struct | Holds a start address and a length. allocator.rs:16-19. |
UpwardBumpAllocator::with_fixed_address_range | fn | const unsafe constructor. The caller reserves the range. allocator.rs:27-29. |
GlobalAlloc for UpwardBumpAllocator | impl | alloc bumps a cursor upward with checked arithmetic. dealloc does nothing. allocator.rs:34-67. |
Both items are pub(crate). Only lib.rs:49-53 uses them.
The module comment at allocator.rs:3-8 gives the reason for the allocator. The default Solana allocator starts at the top of a fixed 32 KiB range and grows downward. A RequestHeapFrame instruction maps more memory, but the default allocator cannot address it. This allocator grows upward from the heap base. A transaction without a heap request keeps the same first 32 KiB. A transaction with a request can continue into the mapped extension.
The allocator stores its cursor in the first word of the range at allocator.rs:37-44. A zero word means the first call. Every addition is checked, and an overflow or an address past the end returns null at allocator.rs:39-60.
The Solana entrypoint installs its own allocator only when the custom-heap feature is off. Source: solana-program-entrypoint-3.1.1/src/lib.rs:219-230. The crate turns custom-heap on by default at programs/sickle/Cargo.toml:14, so lib.rs:43-53 installs this allocator instead.
constants.rs
Seeds. The #[constant] attribute exports each seed into the IDL.
| Item | Kind | Value | Line |
|---|---|---|---|
GLOBAL_CONFIG_SEED | const | b"global_config" | 4 |
SICKLE_SEED | const | b"sickle" | 7 |
POSITION_SETTINGS_SEED | const | b"position_settings" | 10 |
KEEPER_SEED | const | b"keeper" | 13 |
INTENT_DOMAIN_SEED | const | b"intent_domain" | 98 |
INTENT_NONCE_SEED | const | b"intent_nonce" | 101 |
INTENT_SESSION_SEED | const | b"intent_session" | 104 |
INTENT_AUTHORIZATION_SEED | const | b"intent_authorization" | 107 |
DLMM_INTENT_AUTHORIZATION_SEED | const | b"dlmm_intent_authorization" | 116 |
DLMM_INTENT_POSITION_SEED | const | b"dlmm_intent_position" | 124 |
ACTION_INTENT_SEED | const | b"action_intent" | 127 |
Caps and sizes.
| Item | Kind | Value | Line | Meaning |
|---|---|---|---|---|
BPS_DENOMINATOR | const | 10000 | 15 | The bps base for every fee and cap. |
MAX_FEE_BPS | const | 500 bps | 18 | The maximum protocol fee. |
FEE_ACTION_COUNT | const | 6 | 22 | The length of the fee array in the global config. |
MAX_SLIPPAGE_BPS | const | 500 bps | 28 | The maximum settings slippage. |
MAX_PRICE_IMPACT_BPS | const | 5000 bps | 29 | The maximum settings price impact. |
MAX_DUST_BPS | const | 5000 bps | 30 | The maximum settings dust. |
MAX_COMPOUND_DUST_BPS | const | 100 bps | 36 | The maximum dust in a compound authorization. |
METEORA_DLMM_INTENT_MAX_WIDTH | const | 56 bins | 78 | The widest Meteora range a keeper authorization may cover. |
PROTOCOL_COUNT | const | 4 | 81 | The number of venue enum variants. |
GLOBAL_CONFIG_INLINE_PROTOCOL_COUNT | const | 3 | 89 | The number of allowlist slots in the original global config array. |
SICKLE_VERSION | const | 1 | 91 | The version written into a new Sickle. |
INTENT_DOMAIN_VERSION | const | 1 | 121 | The version of the intent domain account. |
AUTHORIZATION_VERSION | const | 2 | 127 | The version byte inside a CLMM authorization digest. |
DLMM_AUTHORIZATION_VERSION | const | 1 | 132 | The version byte inside a DLMM authorization digest. |
MAX_INTENT_ASSETS | const | 3 | 139 | The number of asset entries in an authorization. |
MAX_SWAP_EDGES | const | 2 | 143 | The number of swap edges in an authorization. |
Prefixes and addresses.
| Item | Kind | Value | Line |
|---|---|---|---|
INTENT_DIGEST_PREFIX | const | b"SICKLE_ACTION_INTENT" | 146 |
DLMM_INTENT_DIGEST_PREFIX | const | b"SICKLE_DLMM_ACTION_INTENT" | 149 |
INTENT_DOMAIN_PREFIX | const | b"SICKLE_INTENT_DOMAIN" | 152 |
ORCA_WHIRLPOOL_PROGRAM_ID | const | whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc | 44 |
JUPITER_PROGRAM_ID | const | JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4 | 51 |
RAYDIUM_CLMM_PROGRAM_ID | const | CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK | 57 |
METEORA_DLMM_PROGRAM_ID | const | LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo | 63 |
METEORA_DLMM_AUTOMATION_LB_PAIR | const | JCYMX9Nx7DTUdguptRR5LLSc62MEbNmFYsbT5R9yCDGy | 71 |
Permission bits.
| Item | Kind | Value | Line |
|---|---|---|---|
PERMISSION_HARVEST | const | 0x01 | 155 |
PERMISSION_COMPOUND | const | 0x02 | 156 |
PERMISSION_REBALANCE | const | 0x04 | 157 |
PERMISSION_EXIT | const | 0x08 | 158 |
PERMISSION_ALL | const | 0x0F | 159 |
programs/sickle/Cargo.toml
| Feature | Line | Effect |
|---|---|---|
default | 14 | Turns on custom-heap. |
cpi | 15 | Turns on no-entrypoint. A crate that calls this program through CPI uses it. |
no-entrypoint | 16 | Anchor skips the entrypoint. lib.rs:45 also skips the allocator install. |
no-idl | 17 | Anchor skips the IDL instructions. |
no-log-ix-name | 18 | Anchor skips the instruction-name log line. |
idl-build | 19 | Turns on anchor-lang/idl-build and anchor-spl/idl-build for anchor build. |
anchor-debug | 20 | Anchor debug output. |
custom-heap | 21 | The Solana entrypoint does not install its allocator. lib.rs:43-53 installs the upward allocator. |
custom-panic | 22 | Declared. No code in programs/sickle/src reads it. |
The crate depends on anchor-lang 1.1.2 with init-if-needed and event-cpi, on anchor-spl 1.1.2 with associated_token, token, and token_2022, on solana-instructions-sysvar 3.0, and on solana-sha256-hasher 3.1.0 at programs/sickle/Cargo.toml:28-36. The dev dependencies are base64, serde_json, and trybuild at programs/sickle/Cargo.toml:41-48.
The workspace /Cargo.toml:15-20 allows the unexpected_cfgs lint for target_os = "solana" and for the custom-heap, custom-panic, and anchor-debug features. It also allows the deprecated lint for the whole workspace. The release profile turns on overflow-checks, fat LTO, and one codegen unit at /Cargo.toml:5-8.
Anchor.toml
| Cluster | Program | ID | Line |
|---|---|---|---|
devnet | sickle | Scksx45gGiyMt2x7jcKzG39iKvHSaXarYK8L2jv7TbG | 9 |
localnet | sickle | qCxmYKG8poFtsi2PoWDuNgVoPPAcUVLGGDJWSUQ9VTc | 16 |
localnet | mock_transfer_hook | HookK1nGpuGKQxWWvVvXrpKjHi3rTfRvhmPXHkNW8yUS | 15 |
mainnet | sickle | qCxmYKG8poFtsi2PoWDuNgVoPPAcUVLGGDJWSUQ9VTc | 19 |
The declare_id! value at lib.rs:40 equals the localnet and mainnet IDs. It does not equal the devnet ID. A devnet build needs a different declare_id! value. Anchor's entrypoint rejects a mismatch with DeclaredProgramIdMismatch at anchor-syn-1.1.2/src/codegen/program/entry.rs:61-63.
Anchor.toml:25-32 defines the test scripts. Anchor.toml:47-69 lists 8 mainnet accounts that the legacy test validator clones. Anchor.toml:71-77 configures Surfpool against the mainnet RPC.
How a call flows through it
This follows sweep_token.
- The runtime calls the Anchor entrypoint with the instruction data. Anchor reads the first 8 bytes and matches them against
sha256("global:sweep_token")[..8]. Source:anchor-syn-1.1.2/src/codegen/program/dispatch.rs:81-95. - Anchor deserializes the
SweepTokenaccounts struct and runs its constraints. Thesickleconstraint requiressickle.is_owner(&authority.key())and returnsUnauthorizedotherwise.instructions/sickle/sweep_token.rs:23-28. - Anchor calls
sickle::sweep_token(ctx, amount)atlib.rs:146. - The entry function calls
instructions::sickle::sweep_token::sweep_token(ctx, amount)atlib.rs:147. - The handler resolves
amount, returns early on zero, and transfers the tokens with the Sickle PDA seeds[SICKLE_SEED, owner, bump].instructions/sickle/sweep_token.rs:52-74. - The handler builds a
Sweptevent and emits it twice, once by self-CPI and once as a log line.instructions/sickle/sweep_token.rs:76-83. - The self-CPI re-enters the program. Anchor's dispatcher recognizes the event tag and returns without a second dispatch. Source:
anchor-syn-1.1.2/src/codegen/program/dispatch.rs:90-93.
Every heap allocation in steps 2 to 7 goes through UpwardBumpAllocator::alloc at allocator.rs:36-63.
Invariants the layer owns
- The runtime program ID equals
ID. Anchor's generated entrypoint returnsDeclaredProgramIdMismatchotherwise. Source:anchor-syn-1.1.2/src/codegen/program/entry.rs:61-63. - Every instruction reaches exactly one handler. Each entry function at
lib.rs:59-629has one body line that calls the handler with the same name and arguments. No entry function has a check. - The allocator never hands out an address past the 256 KiB frame.
alloccompares the end address againststart + lenand returns null atallocator.rs:54-60. - The allocator never overflows its arithmetic. Every addition uses
checked_addatallocator.rs:39-57. - A protocol fee never exceeds 500 bps.
set_feerequiresfee_bps <= MAX_FEE_BPSand returnsFeeTooHighatinstructions/admin/set_fee.rs:21. - A permission byte uses only the 4 known bits.
set_global_automatorchecks the byte againstPERMISSION_ALLand returnsInvalidPermissionsatinstructions/admin/set_global_automator.rs:45. - A new Sickle carries version 1.
initialize_sicklewritesSICKLE_VERSIONatinstructions/sickle/initialize_sickle.rs:36. - The allowlist starts with the 4 default venue IDs.
initialize_global_configwrites the 4 constants atinstructions/admin/initialize_global_config.rs:70-76.
Tests
| Test location | Covers |
|---|---|
allocator.rs:76-92 | An allocation sequence crosses the 32 KiB boundary and stays inside 256 KiB. |
allocator.rs:95-103 | A 256 KiB allocation returns null. |
allocator.rs:73 | A compile-time assert that custom-heap is on during cargo test. |
state.rs:1373 | The Protocol variant count equals PROTOCOL_COUNT. |
.github/workflows/ci.yml:106-108 | The build log contains no SBF frame overflow diagnostic. |
tests/sickle.ts and tests/sickle/*.ts | Integration tests call the instructions through the IDL on a local validator. |
Gaps. No test asserts the declare_id! value against Anchor.toml. No test asserts the 65 instruction count or the instruction list against the IDL. No test reads SICKLE_VERSION back. No test covers a RequestHeapFrame transaction against the deployed allocator in Rust. The allocator tests run on the host, not on the SBF target.
Simplification and audit notes
- L01-N001 —
lib.rs:1allowsclippy::diverging_sub_expressionfor the whole crate. The allow has no comment. A reader cannot tell which expression needs it. - L01-N002 —
lib.rs:5compiles the allocator module undertestor undercustom-heapon thesolanatarget.lib.rs:43-47installs the allocator only whenno-entrypointis also off. The twocfglists differ. Undercpithe module compiles on thesolanatarget, and nothing uses it. - L01-N003 —
allocator.rs:73is aconstassert that thecustom-heapfeature is on.cargo test --no-default-featurestherefore fails to compile the test module. The assert documents the coupling between the test and the default feature set. - L01-N004 —
lib.rs:59-629orders the entry functions as admin, Sickle, Meteora, Orca, Raydium, swap,set_position_settings, authorization,unset_position_settings. The order differs from the grouping indocs/dev/dependency-graph.mdsection 5.set_position_settingsatlib.rs:552andunset_position_settingsatlib.rs:627are 75 lines apart with 10 authorization functions between them. The order has no effect on dispatch. - L01-N005 —
lib.rs:590-597are the only 2 entry functions with a doc comment. The other 63 have none. The IDL carries an instruction doc only for those 2. - L01-N006 —
lib.rs:153andlib.rs:160namecrate::meteora::DlmmLiquidityParameterByStrategywith a full path.lib.rs:21-22importsAuthorizationV2,DlmmAuthorizationV1,FeeAction,PositionSettingsArgs, andProtocolat the top.IntentSessionArgsatlib.rs:570arrives through theinstructions::*glob atlib.rs:20. Three import styles serve one purpose. - L01-N007 —
lib.rs:359,lib.rs:382,lib.rs:463, andlib.rs:496allowclippy::too_many_argumentson 4 entry functions with 7 arguments each. The Orca and Raydium rebalance pairs repeat the same 7-argument list twice each. - L01-N008 —
constants.rs:133definesSICKLE_VERSION.instructions/sickle/initialize_sickle.rs:36writes it. No line inprograms/sickle/srcreads the storedversionfield back. The field has no consumer on-chain. - L01-N009 —
constants.rs:125definesPROTOCOL_COUNT. Its only use outsideconstants.rsis the test atstate.rs:1373. Production code does not read it. - L01-N011 —
constants.rs:184-188says the asset ledger holds token A and token B because "reward emissions are deferred".instructions/orca/harvest_for.rs:193andinstructions/orca/compound_for.rs:237emitRewardsCollectedfor keeper flows today, as do the three Raydium claim paths since #184. The comment does not say what is still deferred. - L01-N012 —
constants.rs:44-45,51,57, and63hold 4 default program IDs. Their only production reader isinstructions/admin/initialize_global_config.rs:70-76. Every later check reads the global config. The comments atconstants.rs:38-43say so, but each of the 4 comments repeats the same 3 sentences. - L01-N013 —
constants.rs:16-17,24-27, and43reference the EVM contracts inside doc comments. A reader of this layer cannot verify those references from this repository. - L01-N014 —
programs/sickle/Cargo.toml:22declares thecustom-panicfeature. No file inprograms/sickle/srcreads it. The workspace lint table at/Cargo.toml:18still lists it as a known feature. - L01-N015 —
/Cargo.toml:20allows thedeprecatedlint for the workspace. A deprecated Anchor or Solana API produces no warning in CI. - L01-N016 —
Anchor.toml:29definestest-surfpoolas one script over 6 files.Anchor.toml:30-32define the 3 per-venue scripts over the same 6 files. The file lists are written twice. - L01-N017 — No test asserts the instruction count.
docs/dev/dependency-graph.md:182states 65, andinstructions/mod.rshas 65pub modlines, but only a manual count connects them.
Resolved
L01-R001 —
Anchor.toml:9gives devnet the IDScksx45gGiyMt2x7jcKzG39iKvHSaXarYK8L2jv7TbG.lib.rs:40declaresqCxmYKG8poFtsi2PoWDuNgVoPPAcUVLGGDJWSUQ9VTc. Anchor's entrypoint returnsDeclaredProgramIdMismatchwhen the runtime program ID differs fromID. Source:anchor-syn-1.1.2/src/codegen/program/entry.rs:61-63. A devnet deploy of the checked-in source therefore needs an edit tolib.rsfirst.scripts/deploy/deploy.shrunsanchor keys syncbefore the build, but bare: it is passed neither the run's cluster nor its--program-keypair, both of which do reachanchor deploy. WithAnchor.tomlsetting the provider cluster tolocalnet, that sync need not touch theprograms.devnetentry at all, and the script's clean-tree assertion cannot see a sync that changed nothing. No script or test asserts the pair.The mismatch was real and the deploy path did not handle it.
deploy.shrananchor keys sync, which takes its answer fromtarget/deploy/sickle-keypair.jsonand accepts neither a cluster nor a--program-keypairargument, so a devnet run could leave the devnet entry untouched and build the mainnet id into the binary. A program whose declared id is not its deployed address rejects every instruction, and no gate caught it: a sync that changes nothing leaves a clean tree behind.Both ids are now declared in
lib.rsbehind thedevnetcargo feature, as reviewed constants, andprograms/sickle/tests/program_id.rspins each arm against its ownAnchor.tomlentry. The sync is gone from bothdeploy.shandupgrade.sh, which also removes the deeper problem: it rewrote tracked source in the middle of a deploy whose entire purpose is to prove the binary came from a reviewed commit. That gate is now absolute rather than something worked around.The note asked for a test pinning "the pair". There are two pairs, and asserting the devnet one against
declare_id!would have asserted exactly what the old deploy path existed to break. With both ids constant the question dissolves: each arm is pinned to its own cluster. Resolved in #191.L01-N010 — The program hard-coded one Meteora pair address, and the DLMM canonical-form check compared every DLMM authorization pool against it. A second pair needed a program upgrade. The pin is removed.
validate_canonicalnow requires only a non-default pool, atintent.rs:618. Each keeper transaction-B instruction checks the inclusive width against the live pair reward-slot count, atmeteora.rs:377-394. The limit table isconstants.rs:103. The keeper and the management UI still act only on the ADX-USDC pair. That pin is now a rollout choice off chain.