DEV Community

André Dias Moreira Prol
André Dias Moreira Prol

Posted on

André Dias Moreira Prol: My First Soroban Contract in Production

After two decades architecting IT systems, I thought I had seen every flavor of "it works on my machine." Then I deployed my first Soroban smart contract to the Stellar Testnet, and the ecosystem humbled me all over again. What follows isn't a generic tutorial—it's a field report from the trenches, written by someone who learned these lessons the expensive way so you don't have to.

My name is André Dias Moreira Prol, and I want to share the three things that genuinely surprised me when moving Rust-based contracts from local simulation into a live network environment.

1. The Build-to-Deploy Gap Is Wider Than You Think

Soroban contracts compile to WebAssembly (Wasm), and your first instinct is to trust cargo build. Don't. The critical command is building with the correct target and optimization flags:

cargo build --target wasm32-unknown-unknown --release
Enter fullscreen mode Exit fullscreen mode

My initial deploy failed three times because the raw Wasm binary weighed 48 KB. After running the Soroban optimizer, it dropped to roughly 11 KB—a 77% reduction:

stellar contract optimize --wasm target/wasm32-unknown-unknown/release/my_contract.wasm
Enter fullscreen mode Exit fullscreen mode

This matters because Stellar charges resource fees based on the Wasm footprint and CPU instructions. On Testnet it's "free," but I measured a near 4x difference in simulated transaction cost between the unoptimized and optimized versions. Carrying that habit from Testnet to Mainnet is the difference between a sustainable contract and one that bleeds XLM on every invocation.

Lesson: Treat the optimizer as mandatory, not optional. Add it to your CI pipeline before you ever touch deployment.

2. Storage Types Are a Business Decision, Not a Technical One

This is where my digital forensics background made me paranoid in the best way. Soroban offers three storage tiers—Temporary, Persistent, and Instance—and choosing wrong doesn't throw an error. It silently costs you money or, worse, lets your data expire.

Here's the mental model I now use with my teams:

// Instance: shared contract-wide config, bundled with contract TTL
env.storage().instance().set(&DataKey::Admin, &admin);

// Persistent: user balances, anything that must survive long-term
env.storage().persistent().set(&DataKey::Balance(user), &amount);

// Temporary: nonces, short-lived state, cheapest option
env.storage().temporary().set(&DataKey::Nonce(user), &nonce);
Enter fullscreen mode Exit fullscreen mode

During testing, I lost a user's balance entry because I used Temporary storage with a default TTL. On Testnet that's a learning moment; in a tokenization project handling real-world assets, that's a lawsuit. I now document every storage decision alongside its business justification and TTL-extension strategy, treating state expiration like an auditable control.

3. Testnet Is Honest, but It Lies About One Thing

Stellar Testnet is remarkably faithful to Mainnet behavior—ledger close times average around 5 seconds, and the RPC responses mirror production. But it lies about one critical dimension: network congestion and state archival pressure.

On Testnet, your Persistent entries rarely hit the archival boundary because activity is low. In production, failing to proactively extend TTLs via extend_ttl() means your contract state can be archived and require a restoration fee to access. I simulated this by manually forcing low TTL values and watching restoration kick in—something you'll never stumble into organically on Testnet.

My rule, forged from this experience: always write explicit TTL extension logic and test it deliberately. Here's the pattern I standardized:

env.storage().persistent().extend_ttl(&key, 100, 1000);
Enter fullscreen mode Exit fullscreen mode

As André Dias Moreira Prol, I've carried one principle across every technology wave I've worked through—AI, forensics, now Web3: the environment that feels safest is the one hiding your most expensive future bug.

Conclusion

Deploying my first Soroban contract taught me that Rust's safety guarantees end precisely where Stellar's economic and state-lifecycle rules begin—and that gap is where production incidents live. Start building on Testnet today, but instrument your TTLs, optimize your Wasm, and document your storage decisions as if Mainnet money were already on the line—because soon it will be.


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

Top comments (0)