#soroban #smart-contracts #testing #blockchain

soroban-rs

A collection of helpers for writing tests for Soroban contracts

3 unstable releases

Uses new Rust 2024

new 0.2.1 Apr 11, 2025
0.2.0 Apr 4, 2025
0.1.0 Mar 24, 2025

#202 in Magic Beans

Download history 105/week @ 2025-03-22 91/week @ 2025-03-29 119/week @ 2025-04-05

318 downloads per month

MIT license

200KB
3.5K SLoC

soroban-rs

soroban-rs is a Rust library designed to interact with the Soroban smart contract platform on the Stellar network. It provides tools for managing accounts, signing transactions, deploying and invoking smart contracts, and handling cryptographic operations.

Features

  • Env: Manages connections to Soroban RPC endpoints and handles network configuration.
  • Signer: Manages signers low level operations compatible with ed25519 private keys.
  • Account: Manages stellar accounts, including signers and management of transactions.
  • Guard: Manages restrictions in account for smart conract interactions, like number of allowed calls.
  • TransactionBuilder: Helps create and manage Soroban transactions.
  • Contract: Simplifies contract deployment and interaction, allows developers to call contracts directly by casting contracts calls using soroban! macro.

Installation

Add the following to your Cargo.toml:

[dependencies]
soroban-rs = "0.1.0"

Usage

Example: Deploying and Invoking a Contract

Here's a basic example of how to deploy and invoke a contract using soroban-rs:

use soroban_rs::{
    Contract, Env, EnvConfigs, Signer,
    xdr::{ScAddress, ScVal},
};
use std::{env, path::Path};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let private_key_str =
        env::var("SOROBAN_PRIVATE_KEY").expect("SOROBAN_PRIVATE_KEY must be set");
    let private_key = PrivateKey::from_string(&private_key_str).expect("Invalid private key");
    let signing_key = SigningKey::from_bytes(&private_key.0);

    let configs = EnvConfigs {
        rpc_url: "https://soroban-testnet.stellar.org".to_string(),
        network_passphrase: "Test SDF Network ; September 2015".to_string(),
    };
    let provider = Env::new(configs)?;

    let mut account = Account::single(Signer::new(private_key));
    let contract = Contract::new("path/to/contract.wasm")?;

    // Deploy contract with constructor argument (u32 value of 42)
    let constructor_args = Some(vec![ScVal::U32(42)]);
    let deployed = contract
        .deploy(&env, &mut account, constructor_args)
        .await?;

    println!("Contract deployed successfully with ID: {:?}", deployed.contract_id());

    let alice = account.account_id().try_into_val()?;
    let bob = account.account_id().try_into_val()?;

    let invoke_res = deployed.invoke("send", vec![alice, bob]).await?;

    println!("Contract invoked successfully with result {:?}", invoke_res);
    Ok(())
}

Example: using soroban!() macro

use /* ... */;

// takes as argument the code of the contract to be called
// generates its bindings under TokenClient struct.
soroban!(
    r#"
    pub struct Token;

    impl Token {
        pub fn __constructor(env: Env, value: u32) {
            env.storage().instance().set(&KEY, &value);
        }

        pub fn send(env: &Env, from: Address, to: Address) -> Vec<String> {
            let from_str = from.to_string();
            let to_str = to.to_string();
            vec![&env, from_str, to_str]
        }
    }
"#
);
// or use
// soroban!("path/to/contract/lib.rs");

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    /* ... */

    let client_configs = ClientContractConfigs {
        contract_id: /* ... */,
        env: /* ... */,
        account: /* ... */,
    };
    // TokenClient is autogenerated from contract
    // it could be generated wether from direct code reference
    // or from the path to the code where is declared: "path/to/contract/lib.rs"
    let mut token = TokenClient::new(&client_configs);

    let alice = /* ... */;
    let bob = /* ... */;

    // Calls send function in contract from Alice and Bob
    // note that "TokenClient" implements all the functions declared
    // in the soroban contract "Token" and implements the client code.
    let invoke_res = token.send(alice, bob).await?;

    // returned value from the function call, in this case
    // the return value is the result of concatenating
    // vec![&env, from_str, to_str]
    println!("Result value: {:?}", invoke_res.get_return_value());
    Ok(())
}

Error Handling

The library uses a custom error type SorobanHelperError to handle various errors such as transaction failures, network request failures, and XDR encoding issues.

Contributing

We welcome contributions from the community! Here's how you can get involved:

  1. Fork the repository
  2. Create your feature branch
  3. Commit your changes
  4. Push to the branch
  5. Create a Pull Request

If you are looking for a good place to start, find a good first issue here.

You can open an issue for a bug report, feature request, or documentation request.

You can find more details in our Contributing guide.

Please read our Code of Conduct and check the Security Policy for reporting vulnerabilities.

License

This project is licensed under the GNU Affero General Public License v3.0 - see the LICENSE file for details.

Security

If you discover a security vulnerability within this project, please see SECURITY.md for instructions on responsible disclosure.

Maintainers

See CODEOWNERS file for the list of project maintainers.

Dependencies

~22–34MB
~532K SLoC