soroban-sdk-tools 0.1.2

Enhanced tools for Soroban smart contract development
Documentation

soroban-sdk-tools

Crates.io Documentation CI

Proc macros and utilities for soroban-sdk that replace the repetitive parts of storage, error definitions, and auth test setup.

Getting started

cargo add soroban-sdk-tools
cargo add soroban-sdk-tools --dev --features testutils

Or add to your contract's Cargo.toml directly:

[dependencies]
soroban-sdk-tools = "0.1"

[dev-dependencies]
soroban-sdk-tools = { version = "0.1", features = ["testutils"] }

The testutils feature pulls in auth helpers and crypto deps. Keep it in dev-dependencies so it doesn't end up in your WASM.

Storage

#[contractstorage] turns a struct into typed storage handles:

use soroban_sdk::{contract, contractimpl, Address, Env};
use soroban_sdk_tools::{contractstorage, PersistentMap, InstanceItem};

#[contractstorage]
pub struct MyStorage {
    balances: PersistentMap<Address, u64>,
    total_supply: InstanceItem<u64>,
}

#[contract]
pub struct Token;

#[contractimpl]
impl Token {
    pub fn mint(env: &Env, to: &Address, amount: u64) {
        // One-liners for single operations
        let supply = MyStorage::get_total_supply(env).unwrap_or(0);
        MyStorage::set_total_supply(env, &(supply + amount));
        MyStorage::update_balances(env, to, |bal| bal.unwrap_or(0) + amount);
    }

    pub fn transfer(env: &Env, from: &Address, to: &Address, amount: u64) {
        // Grab the struct when you need multiple fields
        let s = MyStorage::new(env);
        let from_bal = s.balances.get(from).unwrap_or(0);
        s.balances.set(from, &(from_bal - amount));
        s.balances.set(to, &(s.balances.get(to).unwrap_or(0) + amount));
    }
}

There's a Map and Item variant for each durability: PersistentMap<K,V> / PersistentItem<V>, InstanceMap<K,V> / InstanceItem<V>, TemporaryMap<K,V> / TemporaryItem<V>.

#[contractstorage(auto_shorten = true)] compresses keys automatically, which typically saves 30-40% on storage fees. More detail in the storage docs on docs.rs.

Error handling

#[scerr] generates a #[contracterror] enum with sequential codes and doc-comment descriptions:

use soroban_sdk_tools::scerr;

#[scerr]
pub enum TokenError {
    /// insufficient balance for transfer
    InsufficientBalance,
    Unauthorized,
    InvalidAmount,
}

For cross-contract calls, #[transparent] propagates errors in the same WASM via ?, and #[from_contract_client] handles try_ calls via ??:

#[scerr]
pub enum AppError {
    Unauthorized,

    #[transparent]
    Token(#[from] TokenError),       // converts via ? operator

    #[from_contract_client]
    External(ExternalError),         // converts via ?? on try_ calls
}

Codes are assigned sequentially and the WASM spec comes out fully flattened, so your TypeScript bindings see every variant without gaps. See error docs for the composition rules.

Contract imports

Works like the SDK's contractimport!. The difference is it generates ContractError and ContractErrorSpec trait impls too, which #[scerr] needs for composition:

mod math {
    soroban_sdk_tools::contractimport!(
        file = "../target/wasm32v1-none/release/math_contract.wasm"
    );
}

#[scerr]
pub enum AppError {
    #[from_contract_client]
    Math(math::MathError),
}

Auth testing

With testutils enabled you get helpers for mock and real-signature auth:

use soroban_sdk_tools::{setup_mock_auth, setup_real_auth, Secp256r1Keypair};

// Skip the crypto, just test your logic
setup_mock_auth(&env, &[&user], &client.address);
client.deposit(&user, &100);

// Or actually sign the payload with a real passkey
let kp = Secp256r1Keypair::generate();
let signer = setup_real_auth(&env, &kp);
client.deposit(&signer, &100);

Supports ed25519, secp256k1, and secp256r1. Details in the auth docs.

Examples

Working contracts in examples/ you can build and test:

  • increment - storage with struct and one-liner patterns
  • errors/ - composable errors across contracts
  • auth/ - token, vault, and atomic swap with auth testing
  • features/ - feature-gated storage

License

Apache-2.0

Acknowledgments

Inspired by loam-soroban-sdk.