Documentation Index Fetch the complete documentation index at: https://mintlify.com/stellar/rs-soroban-sdk/llms.txt
Use this file to discover all available pages before exploring further.
Your First Contract
Let’s build a simple smart contract that demonstrates core Soroban SDK concepts. We’ll create an addition contract based on real examples from the SDK test suite.
Create the Project
Create a new Rust library: cargo new --lib my-first-contract
cd my-first-contract
Configure Dependencies
Update your Cargo.toml: [ package ]
name = "my-first-contract"
version = "0.1.0"
edition = "2021"
[ lib ]
crate-type = [ "cdylib" ]
[ dependencies ]
soroban-sdk = "25.1.1"
[ dev-dependencies ]
soroban-sdk = { version = "25.1.1" , features = [ "testutils" ] }
[ profile . release ]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true
Write the Contract
Replace src/lib.rs with this simple addition contract: #![no_std]
use soroban_sdk :: {contract, contractimpl};
#[contract]
pub struct Contract ;
#[contractimpl]
impl Contract {
pub fn add ( a : u64 , b : u64 ) -> u64 {
a + b
}
}
#[cfg(test)]
mod test {
use soroban_sdk :: Env ;
use crate :: { Contract , ContractClient };
#[test]
fn test_add () {
let e = Env :: default ();
let contract_id = e . register ( Contract , ());
let client = ContractClient :: new ( & e , & contract_id );
let x = 10 u64 ;
let y = 12 u64 ;
let z = client . add ( & x , & y );
assert! ( z == 22 );
}
}
This example is taken from tests/add_u64/src/lib.rs:1 in the Soroban SDK repository.
Run the Tests
Test your contract: You should see output indicating the test passed: running 1 test
test test::test_add ... ok
Build the Contract
Build the optimized WASM binary: cargo build --target wasm32v1-none --release
Your compiled contract will be at: target/wasm32v1-none/release/my_first_contract.wasm
Understanding the Code
Let’s break down the key components:
Contract Declaration
#[contract]
pub struct Contract ;
The #[contract] attribute marks this struct as a Soroban smart contract. The struct itself can be a simple unit struct or contain state.
Contract Implementation
#[contractimpl]
impl Contract {
pub fn add ( a : u64 , b : u64 ) -> u64 {
a + b
}
}
The #[contractimpl] attribute designates this implementation block as containing contract methods. Public functions become callable contract entry points.
Testing with Env
let e = Env :: default ();
let contract_id = e . register ( Contract , ());
let client = ContractClient :: new ( & e , & contract_id );
The SDK automatically generates a ContractClient for testing. This client provides type-safe methods to invoke your contract functions.
More Complex Examples
Contract with Storage
Here’s an example using persistent storage from tests/contract_data/src/lib.rs:1:
#![no_std]
use soroban_sdk :: {contract, contractimpl, Env , Symbol };
#[contract]
pub struct Contract ;
#[contractimpl]
impl Contract {
pub fn put ( e : Env , key : Symbol , val : Symbol ) {
e . storage () . persistent () . set ( & key , & val )
}
pub fn get ( e : Env , key : Symbol ) -> Option < Symbol > {
e . storage () . persistent () . get ( & key )
}
pub fn del ( e : Env , key : Symbol ) {
e . storage () . persistent () . remove ( & key )
}
}
This contract demonstrates:
Storage API : Using e.storage().persistent() for permanent data
Key-value operations : set, get, and remove
Option types : get returns Option<Symbol> for missing keys
Contract with Constructor
Contracts can have initialization logic using __constructor from tests/constructor/src/lib.rs:1:
#![no_std]
use soroban_sdk :: {contract, contractimpl, contracttype, Env };
#[contract]
pub struct Contract ;
#[contracttype]
pub enum DataKey {
Persistent ( u32 ),
Temp ( u32 ),
Instance ( u32 ),
}
#[contractimpl]
impl Contract {
pub fn __constructor ( env : Env , init_key : u32 , init_value : i64 ) {
env . storage ()
. persistent ()
. set ( & DataKey :: Persistent ( init_key ), & init_value );
env . storage ()
. temporary ()
. set ( & DataKey :: Temp ( init_key * 2 ), & ( init_value * 2 ));
env . storage ()
. instance ()
. set ( & DataKey :: Instance ( init_key * 3 ), & ( init_value * 3 ));
}
pub fn get_data ( env : Env , key : DataKey ) -> Option < i64 > {
match key {
DataKey :: Persistent ( _ ) => env . storage () . persistent () . get ( & key ),
DataKey :: Temp ( _ ) => env . storage () . temporary () . get ( & key ),
DataKey :: Instance ( _ ) => env . storage () . instance () . get ( & key ),
}
}
}
This demonstrates:
Constructor : The __constructor function runs when the contract is deployed
Custom types : Using #[contracttype] for enums as storage keys
Storage types : Persistent, temporary, and instance storage
Test the constructor by passing initialization arguments to register: let contract_id = env . register (
Contract ,
ContractArgs :: __constructor ( & 100_ u32 , & 1000_ i64 )
);
Contract with Events
Emit events to track contract activity from tests/events/src/lib.rs:1:
#![no_std]
use soroban_sdk :: {contract, contractevent, contractimpl, Address , Env , MuxedAddress };
#[contract]
pub struct Contract ;
#[contractevent]
pub struct Transfer {
#[topic]
from : Address ,
#[topic]
to : Address ,
amount : i128 ,
to_muxed_id : Option < u64 >,
}
#[contractimpl]
impl Contract {
pub fn transfer ( env : Env , from : Address , to : MuxedAddress , amount : i128 ) {
Transfer {
from : from . clone (),
to : to . address (),
amount ,
to_muxed_id : to . id (),
}
. publish ( & env );
}
}
Key features:
Event definition : #[contractevent] defines event structure
Topics : #[topic] marks indexed fields for efficient filtering
Publishing : .publish(&env) emits the event
Testing Best Practices
Use Env::default() The default environment simulates a clean contract execution context
Register Contracts Use env.register() to deploy contracts in the test environment
Generate Test Data Use Address::generate(&env) for random addresses in tests
Assert Events Verify events with env.events().all() after contract calls
Common Patterns
Error Handling
use soroban_sdk :: contracterror;
#[contracterror]
#[derive( Copy , Clone , Debug , Eq , PartialEq , PartialOrd , Ord )]
#[repr( u32 )]
pub enum Error {
InvalidAmount = 1 ,
InsufficientBalance = 2 ,
}
Authorization
pub fn transfer ( env : Env , from : Address , to : Address , amount : i128 ) {
from . require_auth ();
// ... transfer logic
}
Logging (Development)
pub fn debug_info ( env : Env , value : u32 ) {
env . logs () . add () . push_u32 ( value );
}
Build Commands Reference
Development
Release
Test
Format
Lint
cargo build --target wasm32v1-none
Next Steps
Now that you’ve built your first contract, explore more advanced topics:
Example Contracts Explore production-ready contract examples
Developer Guides Learn advanced patterns and best practices
SDK Documentation Comprehensive API reference
Soroban CLI Deploy and interact with contracts