Once you’ve set up your development environment (Rust + WASM target + Stellar CLI), create your first smart contract project using the init command:
The init command creates a Rust workspace project using the recommended structure for Soroban contracts. It includes a ready-to-use hello_world contract to get you started immediately.
rustup target add wasm32v1-noneThe generated project follows Rust’s workspace pattern, allowing you to include multiple smart contracts in a single repository:
Cargo.toml defines the workspace and shared dependencies. All contracts in contracts/ are workspace members.contracts/ with its own Cargo.toml, source, and tests.test.rs contains unit tests. It’s included via mod test; at the bottom of lib.rs.This hello-world contract lives inside the OLIGHFT SMART COIN DeFi platform. Here’s how the entire project is organized — click any file to open it:
The root Cargo.toml sets up the Rust workspace and declares shared dependencies:
[workspace] resolver = "2" members = [ "contracts/*", ] [workspace.dependencies] soroban-sdk = "22"
testutils feature is automatically enabled inside Rust unit tests in the same crate as your contract. If you write tests from another crate, add features = ["testutils"] and run with cargo test --features testutils.Soroban contracts have a maximum size of 64KB. Without these optimizations, even small Rust programs almost always exceed this limit:
[profile.release] opt-level = "z" # Minimize WASM size overflow-checks = true # Safety first debug = 0 # No debug info strip = "symbols" # Strip symbols debug-assertions = false # Disable in release panic = "abort" # Abort on panic codegen-units = 1 # Better optimization lto = true # Link-time optimization
When you need debug logs while using the Stellar CLI (not needed for tests or step-through debugging):
[profile.release-with-logs] inherits = "release" debug-assertions = true
Each contract has its own Cargo.toml that relies on the workspace configuration:
[package] name = "hello-world" version = "0.0.0" edition = "2021" publish = false [lib] crate-type = ["cdylib"] # Required for building contracts doctest = false [dependencies] soroban-sdk = { workspace = true } [dev-dependencies] soroban-sdk = { workspace = true, features = ["testutils"] }
The OLIGHFT platform extends this workspace to house multiple Soroban contracts — each powering a different DeFi feature. Here’s how the real workspace Cargo.toml evolves from the hello-world starter:
[workspace] resolver = "2" members = [ "contracts/*", # All contracts auto-discovered ] [workspace.dependencies] soroban-sdk = "22" soroban-auth = "22" # Multisig & auth helpers [workspace.metadata.olighft] network = "testnet" horizon = "https://horizon-testnet.stellar.org" tokens = ["XLM", "USDC", "wETH", "wBTC", "EURC"]
The card-staking contract manages the 6 staking tiers (Visa → Black). It imports the shared workspace SDK plus token helpers:
[package] name = "olighft-card-staking" version = "0.1.0" edition = "2021" publish = false [lib] crate-type = ["cdylib"] doctest = false [dependencies] soroban-sdk = { workspace = true } soroban-auth = { workspace = true } [dev-dependencies] soroban-sdk = { workspace = true, features = ["testutils"] }
The token-swap contract handles atomic swaps between the 5 supported tokens. It cross-imports the staking contract for balance checks:
[package] name = "olighft-token-swap" version = "0.1.0" edition = "2021" publish = false [lib] crate-type = ["cdylib"] doctest = false [dependencies] soroban-sdk = { workspace = true } soroban-auth = { workspace = true } [dev-dependencies] soroban-sdk = { workspace = true, features = ["testutils"] }
Cargo.lock, one SDK version, and one release profile — so every contract builds with identical settings.soroban-auth for the 6 card levels on card pages.Soroban contracts are written in Rust and compiled to WebAssembly. Let’s walk through each piece of the contract:
#![no_std] — No Standard LibraryAll contracts must begin with #![no_std] to ensure the Rust standard library is not included. The standard library is too large for blockchain programs. Instead, use types from soroban_sdk.
soroban_sdkThe contract imports the types and macros it needs. Many standard Rust types like std::vec::Vec are not available — there is no allocator and no heap memory in Soroban contracts.
Vec, Map, Bytes, BytesN, Symbol, String, Address — all from soroban_sdku128, i128, u64, i64, u32, i32, bool are all supported#[contract] AttributeThe #[contract] attribute designates the struct as the type to which contract functions are associated.
#[contractimpl] BlockContract functions are defined in an impl block annotated with #[contractimpl]. Key rules:
pubenv: Env for access to the Soroban environment#![no_std] use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec}; // Designate the struct as a Soroban contract #[contract] pub struct Contract; // Implement contract functions #[contractimpl] impl Contract { /// Takes a name and returns ["Hello", name] pub fn hello(env: Env, to: String) -> Vec<String> { vec![&env, String::from_str(&env, "Hello"), to] } } mod test; // Include the test module
mod test; line at the bottom tells Rust to compile and run the test code from test.rs. This is the standard Soroban pattern for organizing contract tests.The hello-world pattern scales directly into production contracts. Here’s the card_staking contract that powers the 6 staking tiers on OLIGHFT — same #[contract] + #[contractimpl] structure:
#![no_std] use soroban_sdk::{ contract, contractimpl, contracttype, Address, Env, Map, Symbol, }; // ── Staking tier enum (Visa → Black) ── #[contracttype] pub enum Tier { Visa = 0, Mastercard = 1, Gold = 2, Platinum = 3, Amex = 4, Black = 5, } // ── Stake record stored per user ── #[contracttype] pub struct Stake { pub owner: Address, pub amount: i128, pub tier: Tier, pub locked_at: u64, pub apy_bps: u32, // basis points (800 = 8%) } #[contract] pub struct CardStaking; #[contractimpl] impl CardStaking { /// Stake tokens into a card tier pub fn stake( env: Env, user: Address, amount: i128, tier: Tier, ) -> Stake { user.require_auth(); // BIP39 wallet signs this let apy = match tier { Tier::Visa => 500, // 5% Tier::Mastercard => 600, // 6% Tier::Gold => 800, // 8% Tier::Platinum => 1000, // 10% Tier::Amex => 1200, // 12% Tier::Black => 1500, // 15% }; let record = Stake { owner: user.clone(), amount, tier, locked_at: env.ledger().timestamp(), apy_bps: apy, }; env.storage().persistent().set(&user, &record); record } /// Read a user's current stake pub fn get_stake(env: Env, user: Address) -> Stake { env.storage().persistent().get(&user).unwrap() } } mod test;
require_auth() verifies the signature on-chain.env.storage().persistent() stores stake records that survive ledger expiry. The admin backend reads these to display staking analytics.#![no_std] → #[contract] → #[contractimpl] → mod test;. Master this pattern and you can build any of the platform’s contracts.Testing Soroban contracts uses standard Rust testing tools — the same cargo test and #[test] attributes you’d use for any Rust code.
#![cfg(test)] use super::*; use soroban_sdk::{vec, Env, String}; #[test] fn test() { // 1. Create the Soroban environment let env = Env::default(); // 2. Register the contract let contract_id = env.register(Contract, ()); // 3. Create a client (auto-generated from #[contractimpl]) let client = ContractClient::new(&env, &contract_id); // 4. Call the function and assert the result let words = client.hello(&String::from_str(&env, "Dev")); assert_eq!( words, vec![ &env, String::from_str(&env, "Hello"), String::from_str(&env, "Dev"), ] ); }
Env::default() creates the Soroban environment your contract runs inside. Always the first step.env.register(Contract, ()) registers the contract. Pass None for an auto-generated ID, or specify one.ContractClient is auto-generated for every #[contractimpl] block. Named {Type}Client.assert_eq! to verify return values match expectations. Works with all Soroban types.running 1 test test test::test ... ok test result: ok. 1 passed; 0 failed; 0 ignored
The staking contract from Step 4 uses the exact same test pattern. Here’s the real test that validates all 6 card tiers on Stellar Testnet:
#![cfg(test)] use super::*; use soroban_sdk::{Env, Address}; #[test] fn test_gold_tier_stake() { // 1. Create environment let env = Env::default(); env.mock_all_auths(); // Skip real BIP39 signatures in tests // 2. Register staking contract let id = env.register(CardStaking, ()); let client = CardStakingClient::new(&env, &id); // 3. Create a test user (simulates a wallet HD account) let user = Address::generate(&env); // 4. Stake 5000 tokens into Gold tier (8% APY) let stake = client.stake(&user, &5000_0000000, &Tier::Gold); assert_eq!(stake.apy_bps, 800); // 8% = 800 bps assert_eq!(stake.amount, 5000_0000000); // 5. Verify stored stake matches let read = client.get_stake(&user); assert_eq!(read.tier as u32, Tier::Gold as u32); } #[test] fn test_all_tiers_apy() { let env = Env::default(); env.mock_all_auths(); let id = env.register(CardStaking, ()); let client = CardStakingClient::new(&env, &id); // Verify APY for each card tier let tiers = [ (Tier::Visa, 500), // 5% (Tier::Mastercard, 600), // 6% (Tier::Gold, 800), // 8% (Tier::Platinum, 1000), // 10% (Tier::Amex, 1200), // 12% (Tier::Black, 1500), // 15% ]; for (tier, expected_apy) in tiers { let user = Address::generate(&env); let s = client.stake(&user, &1000_0000000, &tier); assert_eq!(s.apy_bps, expected_apy); } }
running 1 test (hello_world) test test::test ... ok running 2 tests (card_staking) test test::test_gold_tier_stake ... ok test test::test_all_tiers_apy ... ok test result: ok. 3 passed; 0 failed; 0 ignored
require_auth() call.cargo test --workspace runs tests for all OLIGHFT contracts simultaneously — hello_world, card_staking, token_swap, and multisig_custody.Build your contract to deploy or run using the Stellar CLI:
This is a wrapper around cargo build that sets the target to wasm32v1-none and the profile to release. It’s equivalent to:
The compiled .wasm file is output to:
target/wasm32v1-none/release/hello_world.wasm
rustup target add wasm32v1-noneThe OLIGHFT platform builds 4 contracts in one command using the workspace. Each produces its own .wasm artifact:
Since all contracts are workspace members, this single command builds every one. The output tree looks like:
After building, deploy each contract to the Testnet that the wallet, swap, and card pages connect to:
CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC # Contract ID on Testnet
["Hello", "World"]
horizon-testnet.stellar.org for all contract interactions. The wallet connects to the same RPC endpoint.Optimize your .wasm for minimum size using the Stellar CLI:
This outputs an optimized file at the same location:
target/wasm32v1-none/release/hello_world.optimized.wasm
.wasm directly.| BUILD TYPE | TYPICAL SIZE | USE FOR |
|---|---|---|
| Debug build | 200–500+ KB | Never deploy |
| Release build | 10–50 KB | Development & testing |
| Optimized | 5–30 KB | Production deployment |
In the OLIGHFT platform, all 4 contracts must be optimized before deploying to Stellar Testnet. Use a single script to optimize the entire workspace:
#!/bin/bash # Optimize all OLIGHFT contracts for Testnet deployment set -e WASM_DIR="target/wasm32v1-none/release" echo "Building workspace..." stellar contract build for wasm in "$WASM_DIR"/*.wasm; do [[ "$wasm" == *.optimized.wasm ]] && continue echo "Optimizing $(basename $wasm)..." stellar contract optimize --wasm "$wasm" done echo "Done! Optimized artifacts:" ls -lh "$WASM_DIR"/*.optimized.wasm
| CONTRACT | RELEASE | OPTIMIZED | POWERS |
|---|---|---|---|
| hello_world | 14 KB | 8 KB | Tutorial baseline |
| card_staking | 38 KB | 18 KB | 6 Card Tiers |
| token_swap | 45 KB | 22 KB | Swap Page |
| multisig_custody | 32 KB | 15 KB | Wallet Multisig |
opt-level = "z", lto = true, strip = "symbols") is critical — without it, the staking contract alone exceeds 200 KB.You’ve just built your first Soroban smart contract — and seen how the same patterns power the entire OLIGHFT SMART COIN platform!
stellar contract init, mapped to the full OLIGHFT project structurerequire_auth(), persistent storagemock_all_auths(), per-tier APY verification, cargo test --workspace for all 4 contracts