Skip to content

Conversation

@d4mr
Copy link
Member

@d4mr d4mr commented Oct 23, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added /solana/program API endpoint for executing Solana program instructions with type-safe instruction building via IDL support
    • Automatic account resolution including PDA derivation for program interactions
    • IDL caching service for improved performance on repeated program queries
  • Tests

    • Added integration tests for Solana program interactions and PDA derivation scenarios
  • Documentation

    • Added Solana program API specification document
  • Chores

    • Added dependencies for Solana ecosystem integration

@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

Walkthrough

This pull request introduces a comprehensive /solana/program HTTP endpoint for Solana program interactions. It adds new Solana/SPL dependencies, creates a modular core library with account resolution, IDL caching, and instruction encoding, integrates the endpoint into the server with error handling, and includes extensive tests and documentation.

Changes

Cohort / File(s) Summary
Configuration & Dependencies
.gitignore, Cargo.toml, solana-core/Cargo.toml, server/Cargo.toml, eip7702-core/src/constants.rs
Updated .gitignore with .env patterns; added Solana/SPL interfaces, serialization, IDL, caching, and HTTP dependencies to workspace and solana-core; added engine-solana-core local dependency to server; minor whitespace cleanup.
Specification
SOLANA_PROGRAM_ENDPOINT_SPEC.md
Comprehensive specification for /solana/program endpoint, covering API design, request/response schemas, IDL-driven instruction building, account resolution, PDA derivation, error handling, implementation phases, and usage examples.
Error Types
core/src/error.rs, solana-core/src/error.rs, server/src/http/error.rs
Added SolanaProgramError enum with 13 variants (ProgramNotFound, InstructionNotFound, IdlParseError, etc.) in solana-core; integrated into EngineError; mapped to HTTP 400 BAD_REQUEST.
Solana Core Libraries
solana-core/src/account_resolver.rs, solana-core/src/builtin_programs.rs, solana-core/src/idl_cache.rs, solana-core/src/idl_types.rs, solana-core/src/instruction_encoder.rs, solana-core/src/program.rs
Introduced modular libraries: AccountResolver for PDA/ATA derivation and seed resolution; WellKnownProgram registry for System/SPL programs; IdlCache for on-chain IDL fetching with Anchor and PMP fallback; IDL type wrappers with serialization format detection; InstructionEncoder for Borsh/Bincode encoding; high-level ProgramCall preparation workflow.
Library Structure
solana-core/src/lib.rs, solana-core/src/transaction.rs
Added module declarations and re-exports for new core libraries; enhanced InstructionDataEncoding with PartialEq/Eq derives.
HTTP API Layer
server/src/http/routes/mod.rs, server/src/http/routes/solana_program.rs, server/src/http/server.rs, server/src/main.rs
Added solana_program route module; implemented send_solana_program handler with parallel preparation, account resolution, instruction encoding, and delegation to Solana executor; integrated IdlCache into EngineServerState and initialized at startup.
Tests
solana-core/tests/integration_tests.rs, solana-core/tests/pda_derivation_tests.rs, solana-core/tests/program_interaction_tests.rs, solana-core/tests/jupiter_idl.json
Comprehensive integration test suite covering Jupiter/Raydium swaps, SPL token transfers, account resolution, PDA derivation (ATAs, multi-seed PDAs), IDL caching, instruction encoding, and error paths; included Jupiter IDL fixture for testing.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Server as /solana/program
    participant Preparer as Instruction Preparer
    participant IDLCache as IDL Cache
    participant RPC as Solana RPC
    participant Executor as Solana Executor

    Client->>Server: POST /solana/program (ProgramCall[])
    Server->>Preparer: prepare_calls_parallel()
    
    par Parallel Preparation
        Preparer->>IDLCache: get_idl(program_id)
        
        alt IDL in Cache
            IDLCache-->>Preparer: return Arc<ProgramIdl>
        else Cache Miss
            IDLCache->>RPC: fetch_anchor_idl_account(program_id)
            alt Anchor IDL Found
                RPC-->>IDLCache: return compressed IDL
                IDLCache->>IDLCache: decompress & parse
            else Anchor Fails
                IDLCache->>RPC: fetch_pmp_idl_account(program_id)
                RPC-->>IDLCache: return IDL (zlib or JSON)
                IDLCache->>IDLCache: decompress & parse
            end
            IDLCache->>IDLCache: cache_insert(program_id, idl)
            IDLCache-->>Preparer: return Arc<ProgramIdl>
        end
        
        Preparer->>Preparer: resolve_accounts(idl, provided_accounts)
        note over Preparer: Derive PDAs, resolve seeds, handle signer/system accounts
        
        Preparer->>Preparer: encode_instruction(idl, args)
        note over Preparer: Apply Anchor discriminator (SHA256), encode via Borsh/Bincode
        
        Preparer-->>Server: PreparedProgramCall (instruction_data, accounts)
    end
    
    Server->>Server: aggregate_results()
    alt All Prepared
        Server->>Executor: execute_solana(instructions)
        Executor-->>Server: QueuedSolanaTransactionResponse
        Server-->>Client: 202 Accepted + TxId
    else Any Failed
        Server-->>Client: 400 ValidationError + error details
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

This diff introduces substantial new functionality spanning multiple layers: six new core libraries with dense logic (account resolution, PDA derivation, IDL caching and parsing, instruction encoding), integration into the HTTP server with a new endpoint, comprehensive error handling across three modules, new dependencies, and extensive test coverage. While changes are cohesive and well-organized, the breadth of heterogeneous components (Solana cryptography, IDL parsing, instruction encoding, async caching) and the depth of logic in each module require careful review for correctness, security implications, and proper error handling.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The pull request title "poc" is a generic abbreviation that fails to convey meaningful information about the changeset to reviewers scanning history. While the changeset does implement a proof of concept for Solana program interactions with IDL support, instruction encoding, account resolution, and a new /solana/program endpoint, the title uses a non-descriptive term that doesn't explain what the actual change accomplishes. This violates the principle of being concise and clear enough that teammates understand the primary change without reading the full diff. The title should be revised to clearly describe the main change, such as "Add Solana program interaction endpoint with IDL support" or "Implement /solana/program API for type-safe Solana instruction building". This would help reviewers quickly understand that the PR introduces infrastructure for Solana program calls with account resolution, PDA derivation, and instruction encoding capabilities.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pb/solana-program

Comment @coderabbitai help to get the list of available commands and usage tips.

@socket-security
Copy link

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​borsh@​0.10.41009993100100
Addedcargo/​dotenvy@​0.15.79710093100100
Addedcargo/​solana-system-interface@​1.0.010010093100100
Addedcargo/​anchor-lang@​0.32.110010093100100

View full report

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
solana-core/Cargo.toml (1)

1-4: Add Rust toolchain version pinning to support edition 2024.

The repository uses edition 2024 (which requires Rust ≥ 1.85.0, stabilized Feb 20, 2025) but does not pin a toolchain version. CI workflows do not specify rust-version, so builds will use the default on ubuntu-latest, which may be older than required.

Create a rust-toolchain.toml file in the repository root with:

[toolchain]
channel = "1.85"

Alternatively, add explicit Rust version setup in CI workflows before build steps.

🧹 Nitpick comments (41)
.gitignore (1)

5-6: Ignore env files; keep a committed template.

Good addition. Consider a broader pattern while preserving .env.example.

+.env*
+!.env.example
-.env.test
-.env
core/src/error.rs (1)

357-363: Ensure JSON shape + schema consistency for the new variant.

  • Confirm engine_solana_core::error::SolanaProgramError derives Serialize/ToSchema; otherwise HTTP serialization/docs break.
  • Align field case with other variants for consistency.
     #[schema(title = "Solana Program Interaction Error")]
     #[error("Solana program interaction error: {message}")]
-    SolanaProgramError {
+    #[serde(rename_all = "camelCase")]
+    SolanaProgramError {
         message: String,
         kind: engine_solana_core::error::SolanaProgramError,
     },

If already deriving Serialize/ToSchema in engine-solana-core, ignore the first point. Otherwise, add those derives there.

solana-core/src/transaction.rs (1)

31-31: Equality derives are appropriate; consider Hash too.

Deriving Hash can help use InstructionDataEncoding as a map key in caches or metrics.

-#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, PartialEq, Eq)]
+#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, PartialEq, Eq, Hash)]
server/Cargo.toml (1)

17-17: Ensure workspace membership and feature scope for new local crate.

Add engine-solana-core to the root Cargo.toml [workspace] members and confirm feature flags are minimal to avoid pulling heavy deps into the server binary.

solana-core/tests/program_interaction_tests.rs (4)

31-37: Avoid hard failure on missing SOLANA_MAINNET_RPC_URL; provide fallback.

Use a sensible default or skip networked tests when the env var isn’t set to keep CI green.

Apply this diff:

-fn create_test_idl_cache() -> IdlCache {
+fn create_test_idl_cache() -> IdlCache {
     init_test_env();
-    let rpc_url = std::env::var("SOLANA_MAINNET_RPC_URL")
-        .expect("SOLANA_MAINNET_RPC_URL must be set in .env.test file");
+    let rpc_url = std::env::var("SOLANA_MAINNET_RPC_URL")
+        .unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".to_string());
     IdlCache::new(rpc_url)
 }

349-397: Gate real network test behind an env flag to prevent flakiness.

This test depends on mainnet and RPC stability. Skip unless explicitly enabled.

Apply this diff:

 #[tokio::test]
 async fn test_fetch_jupiter_idl_from_chain() {
+    if std::env::var("RUN_SOLANA_NETWORK_TESTS").unwrap_or_default() != "1" {
+        eprintln!("Skipping networked test_fetch_jupiter_idl_from_chain (set RUN_SOLANA_NETWORK_TESTS=1 to run)");
+        return;
+    }
     let cache = create_test_idl_cache();

499-502: Relax timing thresholds to reduce CI flakes.

Cache lookups are fast but 10ms can be exceeded on shared runners.

Apply this diff:

-        // Both should be very fast (< 1ms)
-        assert!(first_duration.as_millis() < 10, "Cache should be fast");
-        assert!(second_duration.as_millis() < 10, "Cache should be fast");
+        // Both should be very fast; allow generous headroom on CI
+        assert!(first_duration.as_millis() < 50, "Cache should be fast");
+        assert!(second_duration.as_millis() < 50, "Cache should be fast");

235-241: Use numeric JSON for amounts to match typical IDL arg types.

Amounts are usually u64; avoid quoting to reduce accidental type mismatches later.

Apply this diff:

-            map.insert("amountIn".to_string(), json!("1000000000"));
-            map.insert("minimumAmountOut".to_string(), json!("150000000"));
+            map.insert("amountIn".to_string(), json!(1_000_000_000u64));
+            map.insert("minimumAmountOut".to_string(), json!(150_000_000u64));
solana-core/tests/jupiter_idl.json (1)

11-27: Prefer IDL-provided discriminators when available.

Jupiter IDL includes explicit 8‑byte discriminators per instruction. The encoder should use these when present, falling back to Anchor’s sha256("global:") only if absent. This avoids edge‑case mismatches.

SOLANA_PROGRAM_ENDPOINT_SPEC.md (4)

21-25: Add languages to fenced code blocks (markdownlint MD040).

Use explicit languages for all code fences, e.g., json, rust, ```shell. This improves rendering and linting.

Also applies to: 41-45, 56-69, 89-97, 110-137, 321-372, 731-749, 754-778, 783-806


414-420: Punctuation: use “etc.” with a period.

Replace “etc” with “etc.” in those bullet points.

Also applies to: 429-436


60-86: Request shape inconsistency: “instructions” vs “params”.

Spec shows request.instructions, while the type SendSolanaProgramRequest and handler use params. Align to one field name to avoid API confusion and docs drift.

Also applies to: 272-314


550-558: Anchor dependency version mismatch.

Spec lists anchor‑lang/anchor‑spl 0.30, while code references anchor_lang 0.32.x types. Update the document to reflect the actual versions used or declare version ranges.

solana-core/src/instruction_encoder.rs (4)

33-49: Bincode path unimplemented; doc claims support.

Either implement bincode serialization or adjust docs to state Borsh‑only for now. Consider feature‑gating native/bincode until ready.


35-42: Use IDL discriminator when present; fallback to computed Anchor discriminator.

Rely on instruction.discriminator if provided by the IDL to avoid mismatches with non‑standard encoders; compute only when absent.

-                let discriminator = Self::calculate_anchor_discriminator(instruction_name);
-                data.extend_from_slice(&discriminator);
+                if let Some(d) = &instruction.discriminator {
+                    data.extend_from_slice(d);
+                } else {
+                    let discriminator = Self::calculate_anchor_discriminator(instruction_name);
+                    data.extend_from_slice(&discriminator);
+                }

76-86: Optional args: treat missing Option as None instead of error.

Current logic errors when an optional arg key is omitted. If arg_def.ty is Option(_), default to JSON null when key is missing.

-        for arg_def in arg_definitions {
-            let value = arg_values.get(&arg_def.name).ok_or_else(|| {
+        for arg_def in arg_definitions {
+            let value = match (&arg_def.ty, arg_values.get(&arg_def.name)) {
+                (IdlType::Option(_), None) => &serde_json::Value::Null,
+                (_, Some(v)) => v,
+                _ => {
                 SolanaProgramError::InvalidArgument {
                     arg: arg_def.name.clone(),
                     error: "Missing required argument".to_string(),
-                }
-            })?;
+                }.into()
+            };

319-336: Strengthen tests; assert known values and cover edge cases.

  • Assert a known discriminator value (e.g., for “claim”) rather than printing.
  • Add tests for numeric bounds (u8/u16 overflows, negative i8 underflows).
  • Add Option(None) and Vec(Pubkey) cases.

Also applies to: 330-336

solana-core/tests/pda_derivation_tests.rs (1)

276-279: Bump assertion is brittle; don’t assume a minimum.

Bump is 0..=255. Replace “bump >= 253” with a sanity check (e.g., just validate determinism, or 0<=bump<=255 which is always true).

-    assert!(bump >= 253, "Bump should be a valid bump seed (253-255)");
+    assert!(bump <= 255, "Bump must be within 0..=255");
solana-core/src/lib.rs (2)

19-20: Avoid blanket re-export of Anchor IDL types

pub use anchor_lang::idl::types::* leaks a large, unstable surface and risks name collisions. Prefer a namespaced re-export (e.g., pub mod anchor_idl { pub use anchor_lang::idl::types::*; }) or selectively re-export only what you need.


10-16: Consolidate/align re-exports to prevent duplication

You also re-export IdlField/IdlType via idl_types.rs. Double re-exports can confuse downstream users and break semver when any source changes. Consider centralizing re-exports in one place (this file) and have idl_types.rs import internally.

solana-core/src/error.rs (2)

5-50: Solid, structured error surface; align JSON field casing

Enum and display messages look good. For API consistency with other request/response types using camelCase, consider renaming payload fields to camelCase (e.g., programId, instructionName) or wrapping details under a details object. This avoids mixed casing next to #[serde(tag = "type", rename_all = "SCREAMING_SNAKE_CASE")].


52-52: Result alias scope

pub type Result<T> = ... is fine, but exporting it at crate root can shadow std::result::Result in consumers. Consider encouraging engine_solana_core::error::Result in docs to avoid ambiguity.

solana-core/tests/integration_tests.rs (5)

76-103: Add meaningful assertions or explicitly expect failure

Currently, this branch logs and always passes. Either assert expected success/failure or keep it ignored. Example minimal assertion:

-    match result {
+    match result {
         Ok(prepared) => {
             // ...
         }
         Err(e) => {
             eprintln!("\n⚠ Failed to prepare Jupiter swap");
             eprintln!("Error: {:?}", e);
-            println!("\nThis is expected - account resolution is not yet fully implemented");
+            // Until account resolution lands, we expect an error.
+            // Remove this assert and the #[ignore] once implemented.
+            assert!(true, "Known limitation: account resolution pending");
         }
     }

Would you prefer I convert this to a table-driven test that enumerates known failing/missing accounts to assert specific error variants?


105-116: Network-dependent test: add skip-on-missing-env guard

Mainnet IDL fetch can flake. Add an early-return if SOLANA_MAINNET_RPC_URL is unset, similar to the helper guard.


191-213: Avoid hard-coding Anchor discriminator bytes

Hard-coded discriminator [62, 198, 214, 193, 213, 159, 108, 210] is brittle. Compute it at runtime via SHA-256 of b"global:claim" and compare first 8 bytes.

+use sha2::{Digest, Sha256};
@@
-            assert_eq!(&decoded_data[..8], &[62, 198, 214, 193, 213, 159, 108, 210], "Discriminator should match IDL");
+            let mut hasher = Sha256::new();
+            hasher.update(b"global:claim");
+            let disc = &hasher.finalize()[..8];
+            assert_eq!(&decoded_data[..8], disc, "Discriminator should match Anchor rule");

Please ensure sha2 is present in [dev-dependencies]. If not, I can open a PR to add sha2 = "0.10" there.


308-310: Mark Raydium IDL test as ignored or add assertions

This test is informative-only and network-dependent. Recommend ignoring by default:

-#[tokio::test]
+#[tokio::test]
+#[ignore] // enable with RUN_MAINNET_TESTS and add assertions when stable

397-417: Convert checklist test to doc or ignore it

This prints a checklist and always passes. Either move to docs or ignore to keep CI signal high:

-#[test]
+#[test]
+#[ignore] // documentation-style checklist; not a real test
solana-core/src/idl_types.rs (2)

33-40: Doc comment mismatch

The doc says “detecting Anchor if discriminators are present,” but no detection is implemented. Either implement detection or update the comment to reflect the new default behavior.


90-91: Re-export duplication with lib.rs

You also re-export IdlType/IdlField here while lib.rs re-exports all Anchor IDL types. Consider removing one to avoid ambiguous imports for users.

server/src/http/routes/solana_program.rs (2)

77-90: Bound parallel preparations to protect the server

join_all fires all futures at once. Large instructions arrays can cause load spikes (RPC connections, CPU). Use a bounded stream (e.g., .buffer_unordered(16)).

Example:

-use futures::future::join_all;
+use futures::{future::join_all, stream, StreamExt};
@@
-    let preparation_results = join_all(prepare_futures).await;
+    let preparation_results: Vec<_> =
+        stream::iter(prepare_futures).buffer_unordered(16).collect().await;

117-133: Preserve partial successes or return structured per-instruction errors

You currently fail the whole request if any preparation fails. If single-transaction semantics are required, document it. Otherwise, consider returning both prepared instructions and a structured error list to let clients decide.

Also, return typed error bodies (array of { index, instruction, error: { type, ... } }) instead of a concatenated string to improve client UX.

solana-core/src/account_resolver.rs (2)

79-91: Pre-validate seed lengths (<=32 bytes) before PDA derivation

find_program_address fails when any seed exceeds 32 bytes; surfacing a clear error improves UX.

-        let seed_refs: Vec<&[u8]> = seed_bytes.iter().map(|v| v.as_slice()).collect();
+        for s in &seed_bytes {
+            if s.len() > 32 {
+                return Err(SolanaProgramError::InvalidSeed {
+                    error: format!("Seed length {} exceeds 32 bytes", s.len()),
+                });
+            }
+        }
+        let seed_refs: Vec<&[u8]> = seed_bytes.iter().map(|v| v.as_slice()).collect();
```<!-- review_comment_end -->

---

`223-232`: **ResolvedAccount is defined in two modules; unify to one type**

Duplicate structs here and in program.rs increase drift risk. Reuse a single exported type.
<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>solana-core/src/idl_cache.rs (3)</summary><blockquote>

`46-63`: **Deduplicate concurrent fetches with get_with/try_get_with**

Current get→fetch→insert can trigger duplicate network calls under concurrency. Use moka’s get_with.

```diff
-        if let Some(idl) = self.cache.get(program_id).await {
-            tracing::debug!(program_id = %program_id, "IDL cache hit");
-            return Ok(idl);
-        }
-        // Cache miss - fetch from on-chain
-        tracing::debug!(program_id = %program_id, "IDL cache miss, fetching from on-chain");
-        let idl = self.fetch_idl(program_id).await?;
-        let idl_arc = Arc::new(idl);
-        self.cache.insert(*program_id, idl_arc.clone()).await;
-        Ok(idl_arc)
+        let idl_arc = self
+            .cache
+            .get_with(*program_id, async {
+                tracing::debug!(program_id = %program_id, "IDL cache miss, fetching from on-chain");
+                Arc::new(self.fetch_idl(program_id).await.expect("idl fetch"))
+            })
+            .await;
+        Ok(idl_arc)

Do the same for get_idl_with_client.


299-330: PMP path: guard against oversized decompression and non-JSON payloads

Unbounded zlib decompression can be abused. Add a soft size cap and fall back early when data is clearly not zlib.

-        let mut decoder = ZlibDecoder::new(&account.data[..]);
+        let mut decoder = ZlibDecoder::new(&account.data[..]);
         let mut decompressed = Vec::new();
-        match decoder.read_to_end(&mut decompressed) {
+        match decoder.read_to_end(&mut decompressed) {
             Ok(_) => {
+                if decompressed.len() > 2 * 1024 * 1024 {
+                    return Err(SolanaProgramError::IdlFetchError {
+                        program: program_id.to_string(),
+                        error: "Decompressed IDL exceeds 2MB limit".to_string(),
+                    });
+                }
                 // Successfully decompressed, parse as JSON

Also consider checking account.owner for the PMP program before processing.


365-398: Test noise: remove println! output

Use tracing in tests or drop prints to keep logs clean.

solana-core/src/program.rs (4)

278-293: Remove unnecessary async from resolve_accounts

The method performs no awaits. Make it synchronous to simplify call sites and avoid .await.

-    async fn resolve_accounts(
+    fn resolve_accounts(
 ...
-            self.resolve_accounts(&idl, &signer, &program_id).await?;
+            self.resolve_accounts(&idl, &signer, &program_id)?;
```<!-- review_comment_end -->

---

`287-291`: **Enforce 32‑byte seed limit when building PDA seeds**

Return a clear error if any seed exceeds 32 bytes.

```diff
-            let seed_bytes = self.resolve_seed_value(seed_def, resolved_accounts, args)?;
+            let seed_bytes = self.resolve_seed_value(seed_def, resolved_accounts, args)?;
+            if seed_bytes.len() > 32 {
+                return Err(SolanaProgramError::InvalidSeed {
+                    error: format!("Seed '{}' exceeds 32 bytes", hex::encode(&seed_bytes)),
+                });
+            }
             seeds.push(seed_bytes);
```<!-- review_comment_end -->

---

`235-243`: **Fixed-IDL addresses labeled as System; consider a distinct source**

A literal address in the IDL isn’t necessarily a system/sysvar account. Use a new AccountSource variant (e.g., Fixed) to avoid misleading provenance.
<!-- review_comment_end -->

---

`139-147`: **Built-in IDL TODO: clarify behavior or implement**

You detect well-known programs with has_builtin_idl() but always fall through to fetch. Either implement built‑in IDLs or document the fallback clearly.
<!-- review_comment_end -->

</blockquote></details>
<details>
<summary>solana-core/src/builtin_programs.rs (1)</summary><blockquote>

`237-249`: **From<String>/<&str> always maps to Address; confirm intent**

Constructors treat any string (including “spl-token”) as Address, relying on ProgramInfo::from_identifier to re-map. If you want stronger typing, consider TryFrom<&str> that prefers Named on known aliases.

<!-- review_comment_end -->

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: CodeRabbit UI

**Review profile**: CHILL

**Plan**: Pro

**Disabled knowledge base sources:**

- Linear integration is disabled by default for public repositories

> You can enable these sources in your CodeRabbit configuration.

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3beb5ce17d8cab112c4b5cc3ffd8d7ae7bf09c6e and 9cefe566c5b48e4422840ecc60508e18a2f1f78b.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `Cargo.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (25)</summary>

* `.gitignore` (1 hunks)
* `Cargo.toml` (1 hunks)
* `SOLANA_PROGRAM_ENDPOINT_SPEC.md` (1 hunks)
* `core/src/error.rs` (2 hunks)
* `eip7702-core/src/constants.rs` (1 hunks)
* `server/Cargo.toml` (1 hunks)
* `server/src/http/error.rs` (1 hunks)
* `server/src/http/routes/mod.rs` (1 hunks)
* `server/src/http/routes/solana_program.rs` (1 hunks)
* `server/src/http/server.rs` (2 hunks)
* `server/src/main.rs` (3 hunks)
* `solana-core/Cargo.toml` (1 hunks)
* `solana-core/src/account_resolver.rs` (1 hunks)
* `solana-core/src/builtin_programs.rs` (1 hunks)
* `solana-core/src/error.rs` (1 hunks)
* `solana-core/src/idl_cache.rs` (1 hunks)
* `solana-core/src/idl_types.rs` (1 hunks)
* `solana-core/src/instruction_encoder.rs` (1 hunks)
* `solana-core/src/lib.rs` (1 hunks)
* `solana-core/src/program.rs` (1 hunks)
* `solana-core/src/transaction.rs` (1 hunks)
* `solana-core/tests/integration_tests.rs` (1 hunks)
* `solana-core/tests/jupiter_idl.json` (1 hunks)
* `solana-core/tests/pda_derivation_tests.rs` (1 hunks)
* `solana-core/tests/program_interaction_tests.rs` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code graph analysis (11)</summary>

<details>
<summary>server/src/main.rs (2)</summary><blockquote>

<details>
<summary>server/src/http/server.rs (1)</summary>

* `new` (50-118)

</details>
<details>
<summary>server/src/queue/manager.rs (1)</summary>

* `new` (52-318)

</details>

</blockquote></details>
<details>
<summary>server/src/http/server.rs (1)</summary><blockquote>

<details>
<summary>server/src/http/routes/solana_program.rs (1)</summary>

* `send_solana_program` (60-166)

</details>

</blockquote></details>
<details>
<summary>solana-core/tests/program_interaction_tests.rs (6)</summary><blockquote>

<details>
<summary>solana-core/src/account_resolver.rs (6)</summary>

* `serde_json` (178-178)
* `pubkey` (56-68)
* `get_system_account` (176-181)
* `test_pda_derivation` (269-275)
* `derive_pda` (79-91)
* `to_bytes` (200-215)

</details>
<details>
<summary>solana-core/src/idl_cache.rs (2)</summary>

* `new` (25-34)
* `default` (345-348)

</details>
<details>
<summary>solana-core/src/builtin_programs.rs (4)</summary>

* `from_identifier` (60-78)
* `from_identifier` (122-145)
* `program_id` (27-45)
* `name` (48-57)

</details>
<details>
<summary>solana-core/src/program.rs (1)</summary>

* `seeds` (226-226)

</details>
<details>
<summary>solana-core/src/idl_types.rs (1)</summary>

* `from_json` (35-39)

</details>
<details>
<summary>solana-core/src/instruction_encoder.rs (1)</summary>

* `encode_instruction` (17-52)

</details>

</blockquote></details>
<details>
<summary>solana-core/tests/pda_derivation_tests.rs (1)</summary><blockquote>

<details>
<summary>solana-core/src/account_resolver.rs (3)</summary>

* `pubkey` (56-68)
* `derive_pda` (79-91)
* `derive_ata` (105-116)

</details>

</blockquote></details>
<details>
<summary>solana-core/src/instruction_encoder.rs (2)</summary><blockquote>

<details>
<summary>solana-core/src/account_resolver.rs (2)</summary>

* `serde_json` (178-178)
* `s` (148-148)

</details>
<details>
<summary>solana-core/src/program.rs (1)</summary>

* `s` (347-347)

</details>

</blockquote></details>
<details>
<summary>solana-core/tests/integration_tests.rs (2)</summary><blockquote>

<details>
<summary>solana-core/tests/program_interaction_tests.rs (2)</summary>

* `init_test_env` (24-29)
* `create_test_idl_cache` (32-37)

</details>
<details>
<summary>solana-core/src/idl_cache.rs (1)</summary>

* `new` (25-34)

</details>

</blockquote></details>
<details>
<summary>server/src/http/routes/solana_program.rs (2)</summary><blockquote>

<details>
<summary>server/src/http/server.rs (1)</summary>

* `new` (50-118)

</details>
<details>
<summary>solana-core/src/idl_cache.rs (1)</summary>

* `new` (25-34)

</details>

</blockquote></details>
<details>
<summary>solana-core/src/account_resolver.rs (3)</summary><blockquote>

<details>
<summary>solana-core/src/program.rs (2)</summary>

* `seeds` (226-226)
* `s` (347-347)

</details>
<details>
<summary>solana-core/src/builtin_programs.rs (2)</summary>

* `program_id` (27-45)
* `name` (48-57)

</details>
<details>
<summary>solana-core/tests/program_interaction_tests.rs (1)</summary>

* `test_pda_derivation` (112-138)

</details>

</blockquote></details>
<details>
<summary>solana-core/src/builtin_programs.rs (2)</summary><blockquote>

<details>
<summary>solana-core/src/account_resolver.rs (2)</summary>

* `pubkey` (56-68)
* `s` (148-148)

</details>
<details>
<summary>solana-core/src/program.rs (1)</summary>

* `s` (347-347)

</details>

</blockquote></details>
<details>
<summary>solana-core/src/idl_cache.rs (1)</summary><blockquote>

<details>
<summary>solana-core/src/idl_types.rs (1)</summary>

* `from_json` (35-39)

</details>

</blockquote></details>
<details>
<summary>solana-core/src/program.rs (5)</summary><blockquote>

<details>
<summary>solana-core/src/account_resolver.rs (3)</summary>

* `pubkey` (56-68)
* `serde_json` (178-178)
* `s` (148-148)

</details>
<details>
<summary>solana-core/src/builtin_programs.rs (4)</summary>

* `program_id` (27-45)
* `name` (48-57)
* `from_identifier` (60-78)
* `from_identifier` (122-145)

</details>
<details>
<summary>solana-core/src/instruction_encoder.rs (4)</summary>

* `encode_instruction` (17-52)
* `s` (278-278)
* `s` (296-296)
* `s` (308-308)

</details>
<details>
<summary>solana-core/src/idl_types.rs (1)</summary>

* `from_json` (35-39)

</details>
<details>
<summary>solana-core/src/idl_cache.rs (1)</summary>

* `new` (25-34)

</details>

</blockquote></details>

</details><details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>SOLANA_PROGRAM_ENDPOINT_SPEC.md</summary>

[style] ~418-~418: In American English, abbreviations like “etc.” require a period.
Context: ...4. Handle system accounts (rent, clock, etc) 5. Write integration tests  **Delivera...

(ETC_PERIOD)

---

[style] ~429-~429: In American English, abbreviations like “etc.” require a period.
Context: ...program IDs (System, Token, Token-2022, etc) 3. Implement instruction builders for ...

(ETC_PERIOD)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

<details>
<summary>SOLANA_PROGRAM_ENDPOINT_SPEC.md</summary>

21-21: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

42-42: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

56-56: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)</summary>

* GitHub Check: Socket Security: Pull Request Alerts

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>core/src/error.rs (1)</summary><blockquote>

`842-849`: **From<…> mapping looks fine; verify downstream pattern matches and HTTP mapping.**

- Adding a public enum variant can break exhaustive matches; ensure all call sites updated.
- Confirm `server/src/http/error.rs` maps this variant to the intended status (e.g., 400).

</blockquote></details>
<details>
<summary>solana-core/Cargo.toml (2)</summary><blockquote>

`34-38`: **Reqwest/Tokio feature surfaces.**

- Using `reqwest` with only `json` assumes TLS features come from the workspace; confirm TLS backend (rustls or native-tls) is enabled at workspace level.
- `tokio` only enables `sync`; confirm no runtime/macros needed here.

---

`40-45`: **SPL interface crates: good choice.**

Interfaces over on-chain program crates keeps deps light. LGTM.

</blockquote></details>
<details>
<summary>server/src/http/routes/mod.rs (1)</summary><blockquote>

`8-8`: **Route module exposure looks good; wire into router + docs.**

Confirm router registration and OpenAPI exposure for `/solana/program`.

</blockquote></details>
<details>
<summary>server/src/main.rs (2)</summary><blockquote>

`43-46`: **LGTM: cloning URLs avoids unintended moves.**

---

`145-145`: **Current idl_cache implementation is architecturally correct; review comment's suggestion misrepresents the design.**

The codebase intentionally uses a single shared `idl_cache` by design. As documented in `main.rs` (lines 125–126): "Use mainnet for IDL fetching since IDLs are typically the same across networks. The cache is shared across all Solana networks."

The `idl_cache` addition to `EngineServerState` aligns with existing patterns (e.g., `kms_client_cache`). Routes that use it (e.g., `solana_program.rs:79`) access it directly without chain-specific selection, which is correct for shared Solana IDLs. No `idl_cache_for_chain` method or multi-cache selection logic exists in the codebase because it is not needed under the current architecture.



> Likely an incorrect or invalid review comment.

</blockquote></details>
<details>
<summary>server/src/http/error.rs (1)</summary><blockquote>

`100-101`: **LGTM: map SolanaProgramError to 400.**

Appropriate for client/preparation errors; keep SolanaRpcError mapping for transport issues as 5xx/4xx as already implemented.

</blockquote></details>
<details>
<summary>server/src/http/server.rs (1)</summary><blockquote>

`72-74`: **LGTM: route registered for /solana/program.**

Confirm the handler has utoipa annotations so it appears in OpenAPI.

</blockquote></details>
<details>
<summary>solana-core/tests/pda_derivation_tests.rs (1)</summary><blockquote>

`8-11`: **No action needed—the import path is correct.**

The crate name in solana-core/Cargo.toml is `engine-solana-core`. In Rust, hyphens in package names automatically convert to underscores when used in imports, so `use engine_solana_core::{...}` is the correct import syntax for this crate name.



> Likely an incorrect or invalid review comment.

</blockquote></details>
<details>
<summary>Cargo.toml (1)</summary><blockquote>

`31-35`: **Version alignment confirmed correct; no issues found.**

All Solana crates are consistently aligned: solana-sdk/client/transaction-status/connection-cache/commitment-config/compute-budget-interface at v3.0, with solana-system-interface v2.0 (the correct pairing for SDK 3.x as system instructions were separated into the standalone v2.0 crate). Workspace audit confirms no conflicting versions across member crates—all Solana dependencies are centralized in the root and homogeneous at their respective versions.

</blockquote></details>
<details>
<summary>solana-core/src/account_resolver.rs (1)</summary><blockquote>

`45-47`: **SysvarRecentBlockhashes exists in solana-sdk 3.0.0 but is deprecated; document status and future removal plan**

The sysvar is deprecated in solana-sdk 3.x but the constant still exists and compiles without breaking. The enum variant is publicly exposed via serde aliases for deserialization, so removing it would break external API consumers. No internal usage found in the codebase. To mitigate future SDK incompatibility:

- Add a `#[deprecated]` attribute to the variant with a migration note pointing to modern alternatives (e.g., `getLatestBlockhash` RPC or newer sysvar crates)
- Document the removal plan if you intend to drop it in a future version

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +37 to +38
bincode = "1.3"
borsh = "1.5" # Borsh serialization for Anchor programs
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Serialization stack added; confirm single source of truth for Borsh/Bincode.

Since borsh 1.5 and bincode 1.3 are added at workspace level, ensure crates don’t pin conflicting minor versions locally. Also verify the encoder’s “bincode path” is either implemented or feature‑gated to avoid runtime Unsupported errors.


🏁 Script executed:

#!/bin/bash
# Check for duplicate version specs in crate manifests
fd Cargo.toml | xargs -I{} rg -nP '^(borsh|bincode)\s*=\s*".*"' {}

Length of output: 78



🏁 Script executed:

#!/bin/bash
# Find all Cargo.toml files in the workspace
echo "=== All Cargo.toml files ==="
fd Cargo.toml

echo -e "\n=== Check for bincode/borsh in non-root crates ==="
# Exclude root Cargo.toml and check if any crate-level manifests define these
fd Cargo.toml -not -path './Cargo.toml' | xargs -I{} sh -c 'echo "File: {}"; rg -P "(borsh|bincode)\s*=" {} || echo "  (none)"'

echo -e "\n=== Search for bincode path usage ==="
rg -nP -i 'bincode.*path|path.*bincode' -A2 -B2

Length of output: 543


🏁 Script executed:

#!/bin/bash
# Search for feature-gating or encoding implementation
echo "=== Search for feature flags or conditional compilation ==="
rg -nP '^\s*#\[cfg.*bincode' 

echo -e "\n=== Search for Unsupported error or encoding context ==="
rg -nP 'Unsupported|bincode.*encode|encode.*bincode' -C2 --type rust

Length of output: 7781


Bincode serialization is unimplemented and will cause runtime errors.

Verification confirms no conflicting dependency versions exist (only root Cargo.toml defines borsh and bincode). However, the bincode encoder path is explicitly unimplemented at solana-core/src/instruction_encoder.rs:43-47 and returns SolanaProgramError::UnsupportedType when encountered. Either implement bincode serialization or feature-gate it to prevent runtime failures.

🤖 Prompt for AI Agents
In Cargo.toml around lines 37-38 the bincode dependency is enabled but the
bincode encoder is unimplemented in solana-core/src/instruction_encoder.rs
(lines ~43-47), causing runtime UnsupportedType errors; either implement the
bincode path or remove/feature-gate the dependency: A) Implement bincode
serialization by wiring the unimplemented branch to call bincode::serialize on
the instruction payload and map serialization errors to SolanaProgramError,
updating solana-core error conversions accordingly; or B) Make bincode an
optional dependency in Cargo.toml and add a feature (e.g., "bincode") that gates
both the dependency and the encoder branch, and update Cargo.toml features and
code cfg(attributes) so builds without that feature don’t hit the unimplemented
path.

Comment on lines +25 to +30
pub struct SendSolanaProgramRequest {
/// Idempotency key for this transaction (defaults to random UUID)
#[serde(default)]
pub idempotency_key: String,

/// Solana execution options
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Idempotency key: doc says random UUID, code defaults to empty string

Empty keys break idempotency (collisions across requests). Generate a UUID when missing.

 pub struct SendSolanaProgramRequest {
     /// Idempotency key for this transaction (defaults to random UUID)
-    #[serde(default)]
-    pub idempotency_key: String,
+    #[serde(default)]
+    pub idempotency_key: String,
 }

And in the handler:

@@
-    let transaction_id = request.idempotency_key.clone();
+    let transaction_id = if request.idempotency_key.is_empty() {
+        uuid::Uuid::now_v7().to_string()
+    } else {
+        request.idempotency_key.clone()
+    };
@@
-    let transaction_request = SendSolanaTransactionRequest {
-        idempotency_key: request.idempotency_key,
+    let transaction_request = SendSolanaTransactionRequest {
+        idempotency_key: transaction_id.clone(),
         instructions: prepared_instructions,
         execution_options: request.execution_options,
         webhook_options: request.webhook_options,
     };

Add import:

+use uuid::Uuid;

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +36 to 39

// Solana IDL cache for program interaction
pub idl_cache: Arc<engine_solana_core::IdlCache>,
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Carry per‑cluster IDL caches in state and expose a selector.

Single cache won’t respect chain_id. Replace with three caches and a helper to choose by chain, to keep route code clean.

Apply this diff:

-    // Solana IDL cache for program interaction
-    pub idl_cache: Arc<engine_solana_core::IdlCache>,
+    // Solana IDL caches per cluster for correct IDL resolution
+    pub idl_cache_mainnet: Arc<engine_solana_core::IdlCache>,
+    pub idl_cache_devnet: Arc<engine_solana_core::IdlCache>,
+    pub idl_cache_local: Arc<engine_solana_core::IdlCache>,

Then add (outside this hunk):

impl EngineServerState {
    pub fn idl_cache_for_chain(&self, chain_id: &str) -> &Arc<engine_solana_core::IdlCache> {
        match chain_id {
            // adapt to your ChainId representation if not plain string
            s if s.eq_ignore_ascii_case("solana:mainnet") || s.eq_ignore_ascii_case("mainnet") => &self.idl_cache_mainnet,
            s if s.eq_ignore_ascii_case("solana:devnet") || s.eq_ignore_ascii_case("devnet") => &self.idl_cache_devnet,
            _ => &self.idl_cache_local,
        }
    }
}
🤖 Prompt for AI Agents
In server/src/http/server.rs around lines 36 to 39, the EngineServerState
currently holds a single idl_cache that doesn't respect chain/cluster, so
replace that single field with three Arc<engine_solana_core::IdlCache> fields
named idl_cache_mainnet, idl_cache_devnet, and idl_cache_local, update any
constructor/initialization sites to populate these three caches, and add the
provided idl_cache_for_chain(&self, chain_id: &str) ->
&Arc<engine_solana_core::IdlCache> helper implementation (adjusting chain_id
matching to your ChainId type if it's not a plain str); also ensure necessary
use/imports for Arc and engine_solana_core are present and update any code that
referenced the old idl_cache to call idl_cache_for_chain or use the new fields
directly.

Comment on lines +124 to +132
// Initialize Solana IDL cache
// Use mainnet for IDL fetching since IDLs are typically the same across networks
// The cache is shared across all Solana networks
let idl_cache = Arc::new(engine_solana_core::IdlCache::new(
config.solana.mainnet.http_url.clone(),
));

tracing::info!("Solana IDL cache initialized with mainnet RPC");

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Cluster-agnostic IDL cache will mis-fetch on devnet/local; make IDL cache cluster-aware.

Using a single mainnet RPC for all requests can fetch the wrong IDL or fail for programs that only exist on devnet/local. Provide per‑cluster caches and select by chain_id.

Apply this diff here:

-    // Initialize Solana IDL cache
-    // Use mainnet for IDL fetching since IDLs are typically the same across networks
-    // The cache is shared across all Solana networks
-    let idl_cache = Arc::new(engine_solana_core::IdlCache::new(
-        config.solana.mainnet.http_url.clone(),
-    ));
-    
-    tracing::info!("Solana IDL cache initialized with mainnet RPC");
+    // Initialize per‑cluster Solana IDL caches
+    let idl_cache_mainnet = Arc::new(engine_solana_core::IdlCache::new(
+        config.solana.mainnet.http_url.clone(),
+    ));
+    let idl_cache_devnet = Arc::new(engine_solana_core::IdlCache::new(
+        config.solana.devnet.http_url.clone(),
+    ));
+    let idl_cache_local = Arc::new(engine_solana_core::IdlCache::new(
+        config.solana.local.http_url.clone(),
+    ));
+
+    tracing::info!("Solana IDL caches initialized (mainnet/devnet/local)");

And pass all three into server state:

-        idl_cache,
+        idl_cache_mainnet,
+        idl_cache_devnet,
+        idl_cache_local,

Add a selector helper (outside this hunk) to EngineServerState and use it in the solana_program route to pick the correct cache based on request.execution_options.chain_id.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Initialize Solana IDL cache
// Use mainnet for IDL fetching since IDLs are typically the same across networks
// The cache is shared across all Solana networks
let idl_cache = Arc::new(engine_solana_core::IdlCache::new(
config.solana.mainnet.http_url.clone(),
));
tracing::info!("Solana IDL cache initialized with mainnet RPC");
// Initialize per‑cluster Solana IDL caches
let idl_cache_mainnet = Arc::new(engine_solana_core::IdlCache::new(
config.solana.mainnet.http_url.clone(),
));
let idl_cache_devnet = Arc::new(engine_solana_core::IdlCache::new(
config.solana.devnet.http_url.clone(),
));
let idl_cache_local = Arc::new(engine_solana_core::IdlCache::new(
config.solana.local.http_url.clone(),
));
tracing::info!("Solana IDL caches initialized (mainnet/devnet/local)");

Comment on lines +28 to +32
anchor-lang = { version = "0.32.1", features = ["idl-build"], default-features = false }
bincode = { workspace = true } # Solana's native serialization format
borsh = { workspace = true } # Borsh serialization for Anchor programs
sha2 = "0.10" # For calculating Anchor discriminators

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Anchor/Solana compatibility check.

Verify anchor-lang = 0.32.1 is compatible with the Solana SDK version set in the workspace (comments mention SDK 3.0).


🌐 Web query:

Is anchor-lang 0.32.1 compatible with Solana SDK v2.x/v3.x (system-interface v2), and if not, which anchor-lang version matches Solana 2.x/3.x?

💡 Result:

Short answer: No — anchor-lang 0.32.1 is intended for Solana v2 (recommended v2.3.0) and its crate dependencies still pin solana-system-interface = "1", so it does not target a Solana SDK with system-interface v2. For Solana 2.x use Anchor 0.31.x–0.32.x (0.32.x recommends Solana 2.3.0). There is no official anchor-lang release documented that switches solana-system-interface to v2 in the 0.32.1 sources or release notes. [1][2][3]

Sources:

  • anchor-lang 0.32.1 release notes (recommended Solana version 2.3.0). [1]
  • anchor-lang 0.32.1 Cargo.toml showing solana-system-interface = "1". [2]
  • anchor 0.31.0/0.32.0 release notes (Solana 2.x recommendations). [3]

If you want, I can look for any newer anchor releases (post‑0.32.1) that update solana-system-interface to v2.


I need to check if there are newer anchor-lang versions supporting Solana 3.x with system-interface v2, since the workspace comments mention SDK 3.0.


🌐 Web query:

anchor-lang Solana 3.x system-interface v2 compatible version

💡 Result:

I don't have enough detail to give a precise compatible version. Can you paste the error message or your Cargo.toml (anchor-lang and solana-* versions) — or tell me which Solana 3.x crate/version (e.g. solana-program 3.x / system-interface v2) you're trying to match?


I need to check the actual workspace Solana SDK version to assess whether anchor-lang 0.32.1 is truly incompatible. Let me inspect the codebase.


---

🏁 Script executed:

```shell
#!/bin/bash
# Search for Solana SDK version pins in workspace Cargo.toml
fd Cargo.toml --max-depth 2 -x grep -l "solana" {} \;

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Look for Solana version dependencies in root workspace
cat Cargo.toml 2>/dev/null | grep -A2 -B2 "solana\|anchor"

Length of output: 430


🏁 Script executed:

#!/bin/bash
# Search for "SDK 3.0" or version references in comments
rg -n "SDK|solana.*3\.0|system-interface.*2" --max-count=10

Length of output: 1312


The incompatibility is confirmed. The workspace requires Solana 3.0 with system-interface v2, but anchor-lang 0.32.1 pins system-interface v1. I need to find which anchor-lang version targets Solana 3.0.


🌐 Web query:

anchor-lang latest version Solana 3.0 migration release notes

💡 Result:

Latest Anchor (anchor-lang) release: 0.32.0 (released 2025‑10‑08). Key Solana‑3.0‑related notes: Anchor 0.32.0 replaces the solana-program crate with smaller crates and updates CLI/CLI tooling to work with newer Solana tooling (part of the Solana 3 / Agave transition); see the Anchor changelog and release notes for full migration steps. [1][2]

Sources

  • Anchor changelog (0.32.0) — 2025‑10‑08. [1]
  • Anchor release notes (0.31.0) — upgrade instructions and recommended Solana/Agave tool versions. [2]

I need to confirm the system-interface version that anchor-lang 0.32.0 and 0.32.1 actually depend on.


🌐 Web query:

anchor-lang 0.32.0 0.32.1 solana-system-interface version dependency

💡 Result:

Short answer: anchor-lang 0.32.0 and 0.32.1 depend on solana-system-interface = "1" (crate version 1.0.0). [1][2]

Sources:

  • anchor-lang Cargo.toml (0.32.1) showing solana-system-interface = "1". [1]
  • anchor/client Cargo.lock showing solana-system-interface 1.0.0 in the dependency graph. [2]

Anchor-lang 0.32.1 is incompatible with Solana SDK 3.0 — update to a compatible version.

The workspace pins solana-system-interface = "2.0" (for Solana 3.0), but anchor-lang 0.32.1 depends on solana-system-interface = "1". This creates a dependency conflict. Either:

  • Downgrade the workspace to Solana 2.x (where anchor-lang 0.32.x is designed to work), or
  • Hold on anchor-lang until a version supporting Solana 3.0 / system-interface v2 is released.

Comment on lines +367 to +379
Value::Array(arr) => {
// Flatten array of bytes
arr.iter()
.map(|v| {
v.as_u64()
.ok_or_else(|| SolanaProgramError::InvalidArgument {
arg: "seed".to_string(),
error: "Array elements must be numbers".to_string(),
})
.map(|n| n as u8)
})
.collect()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate array elements are byte-sized in serialize_arg_for_seed

Casting u64→u8 silently wraps values >255.

-            Value::Array(arr) => {
-                // Flatten array of bytes
-                arr.iter()
-                    .map(|v| {
-                        v.as_u64()
-                            .ok_or_else(|| SolanaProgramError::InvalidArgument {
-                                arg: "seed".to_string(),
-                                error: "Array elements must be numbers".to_string(),
-                            })
-                            .map(|n| n as u8)
-                    })
-                    .collect()
-            }
+            Value::Array(arr) => {
+                arr.iter()
+                    .map(|v| {
+                        let n = v.as_u64().ok_or_else(|| SolanaProgramError::InvalidArgument {
+                            arg: "seed".to_string(),
+                            error: "Array elements must be numbers".to_string(),
+                        })?;
+                        if n > 255 {
+                            return Err(SolanaProgramError::InvalidArgument {
+                                arg: "seed".to_string(),
+                                error: format!("Byte value {} exceeds 255", n),
+                            });
+                        }
+                        Ok(n as u8)
+                    })
+                    .collect()
+            }
```<!-- review_comment_end -->

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
            Value::Array(arr) => {
                arr.iter()
                    .map(|v| {
                        let n = v.as_u64().ok_or_else(|| SolanaProgramError::InvalidArgument {
                            arg: "seed".to_string(),
                            error: "Array elements must be numbers".to_string(),
                        })?;
                        if n > 255 {
                            return Err(SolanaProgramError::InvalidArgument {
                                arg: "seed".to_string(),
                                error: format!("Byte value {} exceeds 255", n),
                            });
                        }
                        Ok(n as u8)
                    })
                    .collect()
            }
🤖 Prompt for AI Agents
In solana-core/src/program.rs around lines 367 to 379, the code casts u64 values
to u8 without checking bounds which allows silent wrapping for values >255;
modify the mapping so each array element is validated for byte range before
casting — for each v, call v.as_u64(), then if n > 255 return a
SolanaProgramError::InvalidArgument with arg "seed" and an error message like
"Array elements must be bytes (0..=255)"; only after that safe-guard cast n as
u8 and collect the bytes, preserving the existing error for non-numeric
elements.

Comment on lines +21 to +29
/// Helper to create test IDL cache
fn create_test_idl_cache() -> IdlCache {
init_test_env();

let rpc_url = std::env::var("SOLANA_MAINNET_RPC_URL")
.expect("SOLANA_MAINNET_RPC_URL must be set in .env.test");

IdlCache::new(rpc_url)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard external-RPC tests to avoid CI flakes/panics

expect("SOLANA_MAINNET_RPC_URL must be set...") will panic in CI. Skip gracefully when env is missing.

Apply:

 fn create_test_idl_cache() -> IdlCache {
-    init_test_env();
-    
-    let rpc_url = std::env::var("SOLANA_MAINNET_RPC_URL")
-        .expect("SOLANA_MAINNET_RPC_URL must be set in .env.test");
-    
-    IdlCache::new(rpc_url)
+    init_test_env();
+    let rpc_url = match std::env::var("SOLANA_MAINNET_RPC_URL") {
+        Ok(v) => v,
+        Err(_) => {
+            eprintln!("SKIP: SOLANA_MAINNET_RPC_URL not set; skipping integration test.");
+            // Provide a dummy URL; callers should early-return before using it.
+            return IdlCache::new("http://localhost:0".into());
+        }
+    };
+    IdlCache::new(rpc_url)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Helper to create test IDL cache
fn create_test_idl_cache() -> IdlCache {
init_test_env();
let rpc_url = std::env::var("SOLANA_MAINNET_RPC_URL")
.expect("SOLANA_MAINNET_RPC_URL must be set in .env.test");
IdlCache::new(rpc_url)
}
/// Helper to create test IDL cache
fn create_test_idl_cache() -> IdlCache {
init_test_env();
let rpc_url = match std::env::var("SOLANA_MAINNET_RPC_URL") {
Ok(v) => v,
Err(_) => {
eprintln!("SKIP: SOLANA_MAINNET_RPC_URL not set; skipping integration test.");
// Provide a dummy URL; callers should early-return before using it.
return IdlCache::new("http://localhost:0".into());
}
};
IdlCache::new(rpc_url)
}
🤖 Prompt for AI Agents
In solana-core/tests/integration_tests.rs around lines 21 to 29, the helper
currently calls expect on SOLANA_MAINNET_RPC_URL which will panic in CI; change
the helper to avoid panicking by returning an Option<IdlCache> (or Result)
instead of IdlCache, use std::env::var("SOLANA_MAINNET_RPC_URL").ok() to check
for the env var, and if missing return None so callers/tests can detect the
absence and skip the test gracefully (or propagate a clear error) rather than
panic.

Comment on lines +35 to +37
#[tokio::test]
async fn test_jupiter_swap_instruction() {
let cache = create_test_idl_cache();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mark exploratory test as ignored (passes even on failure)

This test doesn’t assert invariants; it prints on both Ok/Err. Mark as ignored until account resolution is ready.

-#[tokio::test]
+#[tokio::test]
+#[ignore] // enable when account resolution is implemented and assertions are added
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[tokio::test]
async fn test_jupiter_swap_instruction() {
let cache = create_test_idl_cache();
#[tokio::test]
#[ignore] // enable when account resolution is implemented and assertions are added
async fn test_jupiter_swap_instruction() {
let cache = create_test_idl_cache();
🤖 Prompt for AI Agents
In solana-core/tests/integration_tests.rs around lines 35 to 37, the exploratory
test prints results for both Ok and Err and does not assert invariants; mark it
as ignored until account resolution is implemented by adding the #[ignore] test
attribute to the test declaration (e.g. change the attributes on the test
function to include #[ignore]) so the test runner skips it during normal runs.

Comment on lines +201 to +203
} else {
hex::decode(&prepared.instruction.data).expect("Valid hex")
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Ensure hex crate is available in dev-deps

hex::decode/encode requires the hex crate. Verify it exists in workspace dev-dependencies.


🏁 Script executed:

#!/bin/bash
# Verify hex and base64 versions in Cargo.toml files
rg -nP '^\\s*hex\\s*=\\s*' -g '*Cargo.toml' -S
rg -nP '^\\s*base64\\s*=\\s*' -g '*Cargo.toml' -S

Length of output: 0


I need to use a broader search approach to verify the hex crate availability. The empty output suggests the regex pattern may not match the actual formatting.


🏁 Script executed:

#!/bin/bash
# Search for hex usage in the test file
echo "=== Checking for hex usage in integration_tests.rs ==="
fd 'integration_tests.rs' -x cat {} | head -50

echo -e "\n=== Search for hex in Cargo.toml files (all occurrences) ==="
fd 'Cargo.toml' -x grep -n 'hex' {}

echo -e "\n=== Check workspace root Cargo.toml structure ==="
cat Cargo.toml 2>/dev/null | head -100

Length of output: 4021


🏁 Script executed:

#!/bin/bash
# Search for hex usage in the test file
echo "=== Checking for hex usage in integration_tests.rs ==="
fd 'integration_tests.rs' -x cat {} | head -50

echo -e "\n=== Search for hex in Cargo.toml files (all occurrences) ==="
fd 'Cargo.toml' -x grep -n 'hex' {}

echo -e "\n=== Check workspace root Cargo.toml structure ==="
cat Cargo.toml 2>/dev/null | head -100

Length of output: 4021



🏁 Script executed:

#!/bin/bash
# Find solana-core Cargo.toml and check dev-dependencies
echo "=== solana-core Cargo.toml content ==="
cat solana-core/Cargo.toml

echo -e "\n=== Check for hex in solana-core dev-dependencies ==="
grep -A 50 '\[dev-dependencies\]' solana-core/Cargo.toml | head -60

Length of output: 1730


Add hex to solana-core dev-dependencies

The hex crate is declared in [dependencies] but missing from [dev-dependencies]. Integration tests are compiled with dev-dependencies only, so hex::decode at lines 201-203 will fail to compile. Add hex = { workspace = true } to the [dev-dependencies] section of solana-core/Cargo.toml.

🤖 Prompt for AI Agents
In solana-core/tests/integration_tests.rs around lines 201 to 203, the test uses
hex::decode but the hex crate is only listed under [dependencies]; add hex = {
workspace = true } to the [dev-dependencies] section of solana-core/Cargo.toml
so integration tests (which compile with dev-dependencies) can use hex::decode.

Comment on lines +149 to +156
"name": "route_plan",
"type": { "vec": { "defined": { "name": "RoutePlanStep" } } }
},
{ "name": "out_amount", "type": "u64" },
{ "name": "quoted_in_amount", "type": "u64" },
{ "name": "slippage_bps", "type": "u16" },
{ "name": "platform_fee_bps", "type": "u8" }
],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

IDL uses complex defined types your encoder doesn’t support yet.

Many args reference defined structs/enums (e.g., vec, Swap variants). Current InstructionEncoder returns UnsupportedType for IdlType::Defined, so these instructions can’t be encoded yet. Prioritize Defined types support or gate tests to simpler instructions until ready.

Also applies to: 670-993

🤖 Prompt for AI Agents
In solana-core/tests/jupiter_idl.json around lines 149-156 (and similarly for
ranges ~670-993), the IDL declares fields using defined complex types (e.g.,
vec<RoutePlanStep>, Swap variants) which our InstructionEncoder currently treats
as IdlType::Defined and returns UnsupportedType; update the encoder to handle
IdlType::Defined by resolving the referenced type definitions (structs/enums)
from the IDL and recursively encoding their fields/variants (including vectors
and nested defined types), or alternatively adjust the tests/IDL to use only
primitive/inline types until Defined support is implemented; ensure
decoding/encoding symmetry and add unit tests covering a struct, an enum
(variant), and a vec of a defined struct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants