Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Soroban integration

The adapter crate (crates/adapters/soroban) is the boundary between local deterministic simulation and actual Stellar/Soroban execution:

Crucible Simulator
     ├── LocalAdapter   -> local deterministic environment (fast, default)
     └── TestnetAdapter -> Soroban RPC on Stellar Testnet (slow, optional)

The rule from the spec is absolute: the local simulator must never require Testnet access. It doesn’t — the default adapter executes operations through the simulator itself.

The adapter trait

Higher layers depend only on SorobanAdapter, not on any concrete execution environment:

#![allow(unused)]
fn main() {
pub trait SorobanAdapter {
    fn name(&self) -> &'static str;                     // "local" | "testnet"
    fn simulate(&mut self, request: &AdapterRequest)    // normalized invocation
        -> Result<AdapterResponse, AdapterError>;
}
}
  • AdapterRequest normalizes a lifecycle operation into a kind + numeric arguments + optional amount/reference, so callers (tooling, future contracts) do not depend on the simulator’s internal operation types.
  • AdapterResponse returns the outcome summary, event names, ledger sequence, and resulting state root — everything an outer layer needs.
  • AdapterError carries the structured simulator error code (e.g. insufficient_balance, consumed_commitment) through translation, so callers can branch on machine-readable codes rather than string content.

LocalAdapter — the default

LocalAdapter wraps a Simulator and maps normalized requests onto the high-level API. It is deterministic by construction and fully covered end to end: register → deposit → transfer → withdraw through the adapter produces the expected events, balances, and errors.

TestnetAdapter — the boundary, not the wiring

TestnetAdapter exists today so downstream code can be written against the interface now and swapped later. Every call returns AdapterError::Unimplemented — a structured error, not a silent success — until the Soroban SDK integration lands. Concretely, the roadmap work is:

  1. Build a real Soroban client (contract invocation → simulation → response parsing → event extraction) using the shapes in client.rs (ContractInvocation, ContractResult).
  2. Translate contract responses and RPC errors into AdapterResponse/AdapterError using the same vocabulary the local adapter uses, so scenario expectations do not change.
  3. Run the testnet suites separately from the deterministic suites (see docs/testnet.md).

Because both adapters implement the same trait over the same request shapes, switching execution environments later is a configuration change, not a code change.