👋 Hello World FIRST CONTRACT

Create, test, build, and deploy your very first Soroban smart contract on Stellar — step by step from zero to deployed
1
Create
2
Structure
3
Config
4
Code
5
Test
6
Build
7
Optimize
🚀

Step 1 — Create a New Project

Scaffold a Soroban project with the Stellar CLI
STEP 1

Once you’ve set up your development environment (Rust + WASM target + Stellar CLI), create your first smart contract project using the init command:

$ stellar contract init soroban-hello-world

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.

💡
Make sure you have Rust (latest stable, v1.84.0+) and the WASM target installed first:
rustup target add wasm32v1-none
📁

Step 2 — Project Structure

Understand the workspace layout
STEP 2

The generated project follows Rust’s workspace pattern, allowing you to include multiple smart contracts in a single repository:

soroban-hello-world/ ├── Cargo.lock ├── Cargo.toml ← Workspace root config ├── README.md └── contracts/ └── hello_world/ ├── Cargo.toml ← Contract-specific config ├── Makefile └── src/ ├── lib.rs ← Contract code └── test.rs ← Unit tests
📦
Workspace Root
The top-level Cargo.toml defines the workspace and shared dependencies. All contracts in contracts/ are workspace members.
📝
Contracts Directory
Each contract lives in its own subdirectory under contracts/ with its own Cargo.toml, source, and tests.
🧪
Test File
test.rs contains unit tests. It’s included via mod test; at the bottom of lib.rs.

🌐 OLIGHFT SMART COIN — Full Project Map

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:

OLIGHFT SMART COIN/ ├── index.html ← Landing page ├── auth.html ← Login / Register (BIP39) ├── wallet.html ← HD Wallet & Assets ├── send.html ← Send Transactions ├── receive.html ← Receive / QR ├── swap.html ← Token Swap ├── testing-guide.html ← Testing Docs ├── Card Staking (6 tiers)/ │ ├── card-visa.html │ ├── card-mastercard.html │ ├── card-gold.html │ ├── card-platinum.html │ ├── card-amex.html │ └── card-black.html ├── OLIGHFT SMART COIN backend/ │ └── backend.html ← Admin Dashboard └── Smart Contracts/ ← You are here ├── hello-world.html ★ This tutorial ├── smart-contract-guide.html ├── staking-compound.html └── terms.html
🔗
The Smart Contracts folder contains all Soroban development guides. The hello-world contract you’re building here powers the on-chain logic behind the platform’s card staking, token swaps, and multisig custody features.
💳
Card Staking
6 staking tiers (Visa → Black) use Soroban contracts to lock tokens and distribute APY rewards on Stellar Testnet.
🔒
Self-Custody Wallet
BIP39 mnemonic & SEP-0005 HD derivation. The wallet signs contract invocations client-side — keys never leave the browser.
🛠️
Admin Backend
Dashboard for monitoring wallets, staking pools, token supply, and transaction activity across the platform.
⚙️

Step 3 — Cargo Configuration

Workspace setup, dependencies & release profiles
STEP 3

📦 Workspace Cargo.toml

The root Cargo.toml sets up the Rust workspace and declares shared dependencies:

TOMLCargo.toml (root)
[workspace]
resolver = "2"
members = [
  "contracts/*",
]

[workspace.dependencies]
soroban-sdk = "22"
ℹ️
The 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.

🚀 Release Profile

Soroban contracts have a maximum size of 64KB. Without these optimizations, even small Rust programs almost always exceed this limit:

TOMLCargo.toml — release profile
[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

📝 Release with Logs Profile

When you need debug logs while using the Stellar CLI (not needed for tests or step-through debugging):

TOMLCargo.toml — release-with-logs
[profile.release-with-logs]
inherits = "release"
debug-assertions = true

📋 Contract-Specific Cargo.toml

Each contract has its own Cargo.toml that relies on the workspace configuration:

TOMLcontracts/hello_world/Cargo.toml
[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"] }
📌
crate-type = ["cdylib"] is required for all Soroban contracts — it tells Rust to produce a dynamic library suitable for compilation to WASM.

🌐 OLIGHFT SMART COIN — Workspace Config

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:

TOMLCargo.toml — OLIGHFT workspace
[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"]

💳 Staking Contract Config

The card-staking contract manages the 6 staking tiers (Visa → Black). It imports the shared workspace SDK plus token helpers:

TOMLcontracts/card_staking/Cargo.toml
[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"] }

🔄 Swap Contract Config

The token-swap contract handles atomic swaps between the 5 supported tokens. It cross-imports the staking contract for balance checks:

TOMLcontracts/token_swap/Cargo.toml
[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"] }
💡
Why a workspace? The OLIGHFT platform deploys 3+ contracts (hello-world, card-staking, token-swap). Workspaces let them share one Cargo.lock, one SDK version, and one release profile — so every contract builds with identical settings.

🗂️ OLIGHFT Contracts Tree

contracts/ ├── hello_world/ ★ This tutorial (Step 4 code) │ ├── Cargo.toml │ └── src/ lib.rs, test.rs ├── card_staking/ ← 6-tier staking (Visa–Black) │ ├── Cargo.toml │ └── src/ lib.rs, tiers.rs, test.rs ├── token_swap/ ← Atomic token swaps │ ├── Cargo.toml │ └── src/ lib.rs, pool.rs, test.rs └── multisig_custody/ ← Native Stellar multisig ├── Cargo.toml └── src/ lib.rs, signers.rs, test.rs
💳
card_staking
Manages lock periods, APY tiers, compound rewards. Reads token balances from soroban-auth for the 6 card levels on card pages.
🔄
token_swap
Atomic swaps between XLM, USDC, wETH, wBTC, EURC. Powers the swap page with on-chain settlement.
🔒
multisig_custody
Stellar-native multisig for the self-custody wallet. Configures signer weights & thresholds per HD account.
✍️

Step 4 — Contract Source Code

Write the hello world contract in Rust
STEP 4

Soroban contracts are written in Rust and compiled to WebAssembly. Let’s walk through each piece of the contract:

1️⃣ #![no_std] — No Standard Library

All 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.

2️⃣ Imports from soroban_sdk

The 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.

Available Types
Vec, Map, Bytes, BytesN, Symbol, String, Address — all from soroban_sdk
Primitives
u128, i128, u64, i64, u32, i32, bool are all supported
Not Supported
Floats and floating point math are not supported. Contract inputs must not be references.

3️⃣ The #[contract] Attribute

The #[contract] attribute designates the struct as the type to which contract functions are associated.

4️⃣ The #[contractimpl] Block

Contract functions are defined in an impl block annotated with #[contractimpl]. Key rules:

  • Function names have a maximum length of 32 characters
  • Functions intended to be called externally must be pub
  • The first argument is typically env: Env for access to the Soroban environment

📜 Complete Contract

Rustcontracts/hello_world/src/lib.rs
#![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
💡
The 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.

🌐 OLIGHFT — Real Contract: Card Staking

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:

Rustcontracts/card_staking/src/lib.rs
#![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;
💳
6 Card Tiers
Each card page calls stake() with the matching tier enum. APY ranges from 5% (Visa) to 15% (Black).
🔒
require_auth()
The wallet’s BIP39 keypair signs each staking transaction. require_auth() verifies the signature on-chain.
💾
Persistent Storage
env.storage().persistent() stores stake records that survive ledger expiry. The admin backend reads these to display staking analytics.
🔗
Pattern: Every OLIGHFT contract follows the same hello-world skeleton — #![no_std]#[contract]#[contractimpl]mod test;. Master this pattern and you can build any of the platform’s contracts.
🧪

Step 5 — Contract Unit Tests

Write & run tests using standard Rust testing
STEP 5

Testing Soroban contracts uses standard Rust testing tools — the same cargo test and #[test] attributes you’d use for any Rust code.

Rustcontracts/hello_world/src/test.rs
#![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"),
        ]
    );
}

🔍 Understanding the Test Pattern

🌍
1. Create Env
Env::default() creates the Soroban environment your contract runs inside. Always the first step.
📝
2. Register Contract
env.register(Contract, ()) registers the contract. Pass None for an auto-generated ID, or specify one.
📞
3. Create Client
ContractClient is auto-generated for every #[contractimpl] block. Named {Type}Client.
4. Assert Results
Use assert_eq! to verify return values match expectations. Works with all Soroban types.

▶️ Run the Tests

$ cargo test
Output
running 1 test
test test::test ... ok

test result: ok. 1 passed; 0 failed; 0 ignored
📝
The first time you run tests, cargo will compile all dependencies before running. Subsequent runs will be much faster. Try changing the values in the test to see how it works!

🌐 OLIGHFT — Testing the Staking Contract

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:

Rustcontracts/card_staking/src/test.rs
#![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);
    }
}

▶️ Run All OLIGHFT Tests

$ cargo test --workspace
Output
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
🔏
mock_all_auths()
Skips real cryptographic signatures in tests. In production, the wallet’s BIP39 key signs each require_auth() call.
💳
Tier Coverage
The second test loops all 6 tiers (Visa → Black) verifying APY values match the card page configurations.
🧪
--workspace Flag
cargo test --workspace runs tests for all OLIGHFT contracts simultaneously — hello_world, card_staking, token_swap, and multisig_custody.
💡
For a deeper dive into OLIGHFT’s testing methodology, see the Testing Guide which covers integration tests, Testnet deployment tests, and the full CI pipeline.
🔨

Step 6 — Build the Contract

Compile Rust to WebAssembly
STEP 6

Build your contract to deploy or run using the Stellar CLI:

$ stellar contract build

This is a wrapper around cargo build that sets the target to wasm32v1-none and the profile to release. It’s equivalent to:

$ cargo build --target wasm32v1-none --release

The compiled .wasm file is output to:

Output Path
target/wasm32v1-none/release/hello_world.wasm
⚠️
If you get “can’t find crate for core”, you need to install the wasm32 target:
rustup target add wasm32v1-none
(Requires Rust v1.84.0 or higher)

📦 What’s in the .wasm File?

⚙️
Contract Logic
The compiled business logic of your contract — all functions, types, and state management.
📋
Interface / Spec
The contract specification and interface types, which can be imported by other contracts that want to call yours.
🎯
Single Artifact
This one file is everything needed to deploy, share the interface, or integration test against the contract.

🌐 OLIGHFT — Building All Contracts

The OLIGHFT platform builds 4 contracts in one command using the workspace. Each produces its own .wasm artifact:

$ stellar contract build

Since all contracts are workspace members, this single command builds every one. The output tree looks like:

target/wasm32v1-none/release/ ├── hello_world.wasm ★ This tutorial ├── olighft_card_staking.wasm ← 6-tier staking logic ├── olighft_token_swap.wasm ← Atomic swap engine └── multisig_custody.wasm ← Multisig manager

🚀 Deploy to Stellar Testnet

After building, deploy each contract to the Testnet that the wallet, swap, and card pages connect to:

$ stellar contract deploy --wasm target/wasm32v1-none/release/hello_world.wasm --network testnet --source alice
Output
CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC  # Contract ID on Testnet
$ stellar contract invoke --id CDLZFC3S... --network testnet --source alice -- hello --to World
Output
["Hello", "World"]
🌐
Testnet Network
OLIGHFT uses horizon-testnet.stellar.org for all contract interactions. The wallet connects to the same RPC endpoint.
💰
Token Contracts
XLM, USDC, wETH, wBTC, EURC — the 5 tokens on the swap and wallet pages are Stellar assets that the staking contract interacts with.
🛡️
Admin Monitoring
Once deployed, the admin backend tracks contract invocations, staking pools, and token supply in real time.

Step 7 — Optimize the Build

Minimize WASM size for deployment
STEP 7

Optimize your .wasm for minimum size using the Stellar CLI:

$ stellar contract optimize --wasm target/wasm32v1-none/release/hello_world.wasm

This outputs an optimized file at the same location:

Output
target/wasm32v1-none/release/hello_world.optimized.wasm
💡
When to optimize: Optimizing is only necessary when deploying to a network with fees or when analyzing contract size. During development, you can skip this step and use the unoptimized .wasm directly.

📊 Size Comparison

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

🌐 OLIGHFT — Optimize All Contracts

In the OLIGHFT platform, all 4 contracts must be optimized before deploying to Stellar Testnet. Use a single script to optimize the entire workspace:

Bashscripts/optimize-all.sh
#!/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
target/wasm32v1-none/release/ ├── hello_world.optimized.wasm ~8 KB ├── olighft_card_staking.optimized.wasm ~18 KB ├── olighft_token_swap.optimized.wasm ~22 KB └── multisig_custody.optimized.wasm ~15 KB

📊 OLIGHFT Contract Sizes

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
⚠️
64 KB hard limit: Every optimized OLIGHFT contract stays well under the Soroban maximum. The release profile from Step 3 (opt-level = "z", lto = true, strip = "symbols") is critical — without it, the staking contract alone exceeds 200 KB.
📦
Deploy Pipeline
Build → Test → Optimize → Deploy to Testnet. The admin backend verifies each deployment and tracks contract IDs.
🔄
Upgrade Path
Soroban contracts can be upgraded by deploying a new WASM. The platform frontend auto-detects the latest contract version via Horizon RPC.
🧾
Fee Estimation
Smaller WASM = lower deploy fees. Optimization saves ~50% on Stellar deploy costs. Check fees on the send page before deploying.

Summary & Next Steps