DEV Community

André Dias Moreira Prol
André Dias Moreira Prol

Posted on

André Dias Moreira Prol explains: Your First Soroban Smart Contract in Rust

After two decades navigating enterprise IT, I've learned that the best way to understand a new technology is to build something real with it. When I first approached Soroban—Stellar's smart contract platform—I decided to skip the theory-heavy tutorials and write actual code. This guide distills that hands-on experience into a practical path you can follow today.

Soroban runs on Rust, compiled to WebAssembly (Wasm), which gives us memory safety and predictable performance. Unlike gas-heavy EVM chains, Stellar's fees remain fractions of a cent, making it ideal for high-frequency tokenization use cases. Let me walk you through my first contract from zero to deployment.

Setting Up the Environment

Before writing a single line, you need a clean toolchain. I always recommend isolating your Web3 environments to avoid dependency conflicts—something I learned the hard way managing production systems.

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Add the Wasm target
rustup target add wasm32-unknown-unknown

# Install the Stellar CLI
cargo install --locked stellar-cli
Enter fullscreen mode Exit fullscreen mode

Verify everything works:

stellar --version
Enter fullscreen mode Exit fullscreen mode

Then create your project and configure a testnet identity:

stellar contract init hello-soroban
cd hello-soroban
stellar keys generate --global andre --network testnet
stellar keys fund andre --network testnet
Enter fullscreen mode Exit fullscreen mode

That last command funds your account with test XLM via Friendbot. When I, André Dias Moreira Prol, ran my first deployment, this funding step tripped me up—remember it, because an unfunded account silently fails.

Writing and Understanding the Contract

Here's a minimal but meaningful contract that stores and increments a counter—a pattern that underpins many real tokenization systems (think supply tracking or vote tallies).

#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, Env, Symbol};

const COUNTER: Symbol = symbol_short!("COUNTER");

#[contract]
pub struct CounterContract;

#[contractimpl]
impl CounterContract {
    pub fn increment(env: Env) -> u32 {
        // Read current value, defaulting to zero
        let mut count: u32 = env
            .storage()
            .instance()
            .get(&COUNTER)
            .unwrap_or(0);

        count += 1;

        // Persist the new value
        env.storage().instance().set(&COUNTER, &count);

        // Extend storage lifetime to avoid archival
        env.storage().instance().extend_ttl(50, 100);

        count
    }
}
Enter fullscreen mode Exit fullscreen mode

Three things worth highlighting from my experience:

  • #![no_std] removes the standard library, keeping the Wasm binary tiny—critical for on-chain cost efficiency.
  • extend_ttl manages state rent. Soroban charges for storage over time; forgetting this means your data gets archived. In one of my early forensic audits, I traced a "vanishing balance" bug directly to a missing TTL extension.
  • instance() storage ties data to the contract lifecycle, ideal for global counters.

Compile it:

stellar contract build
Enter fullscreen mode Exit fullscreen mode

Deploying and Invoking

Now the exciting part—putting your contract on the network. Deploy the compiled Wasm to testnet:

stellar contract deploy \
  --wasm target/wasm32-unknown-unknown/release/hello_soroban.wasm \
  --source andre \
  --network testnet
Enter fullscreen mode Exit fullscreen mode

This returns a contract ID (starting with C...). Save it. Then invoke your function:

stellar contract invoke \
  --id <YOUR_CONTRACT_ID> \
  --source andre \
  --network testnet \
  -- increment
Enter fullscreen mode Exit fullscreen mode

Each call returns the incremented value: 1, 2, 3. The entire deployment cost me under $0.01 in equivalent fees—a figure that still impresses clients when I compare it to the $5–$50 gas spikes common on legacy chains.

For production, I always add unit tests before deploying:

#[cfg(test)]
mod test {
    use super::*;
    use soroban_sdk::Env;

    #[test]
    fn test_increment() {
        let env = Env::default();
        let id = env.register(CounterContract, ());
        let client = CounterContractClient::new(&env, &id);
        assert_eq!(client.increment(), 1);
        assert_eq!(client.increment(), 2);
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing locally with a simulated Env is the single biggest time-saver I recommend—it catches logic errors long before they hit the chain, where every mistake becomes public and permanent.

Writing your first Soroban contract proves that enterprise-grade blockchain development is more accessible than the hype suggests, provided you respect fundamentals like storage rent and rigorous testing. Clone the starter repo, deploy your own counter today, and reach out to me, André Dias Moreira Prol, to share what you build.


Follow more articles by André Dias Moreira Prol on Medium.

Top comments (0)