本文へスキップ

Soroban SDK & Rust - Writing Your First Smart Contract on the Stellar Blockchain

Soroban SDK & Rust - Writing Your First Smart Contract on the Stellar Blockchain

Hello! My name is Luiz, and in this article, I'll guide you through the basics of creating a smart contract on the Stellar blockchain using the Soroban SDK and Rust.

We'll build a simple Greeting Contract that interacts with users by storing and returning customized greeting messages. So, without further ado, let's get started!


Installing Stellar CLI and Setting Up the Development Environment

The best way to install stellar-cli on your computer is via cargo and Rust. Follow these steps to set up your environment:

  1. Install Rust by following the official documentation:
    • Rust Installation Guide
    • After installing Rust, add the wasm32-unknown-unknown target:
      rustup target add wasm32-unknown-unknown
      
  2. Install Stellar CLI by running:
    cargo install --locked stellar-cli --features opt
    
  3. Verify the installation:
    stellar --help
    
  4. Initialize a new smart contract project:
    stellar contract init greeting.soroban
    
  5. Bonus Tip: Create a rustfmt.toml file in the project root to define custom formatting:
    hard_tabs = true
    tab_spaces = 2
    
  6. Bonus Tip: Set up a Makefile to manage builds, tests, and formatting across multiple contracts:
    CONTRACT_DIRS := $(wildcard contracts/*)
    
    default: build
    
    all: test
    
    build:
      @set -e; \
      for dir in $(CONTRACT_DIRS); do \
        echo "Building contract in $$dir..."; \
        $(MAKE) -C $$dir build || exit 1; \
      done
      @ls -l target/wasm32-unknown-unknown/release/*.wasm
    
    test:
      @set -e; \
      for dir in $(CONTRACT_DIRS); do \
        echo "Building contract in $$dir..."; \
        $(MAKE) -C $$dir test || exit 1; \
      done
    

Coding the Greeting Contract

Our contract will have two functions:

  • say_hello: Returns a greeting message based on stored user data.
    • If user data exists, it returns the stored custom message.
    • Otherwise, it returns the default message: "Hello, Developer!"
  • customize_greeting: Allows users to set their custom greeting message.

1. Creating storage.rs

This file will manage the contract's storage, ensuring consistency and ease of access.

#![allow(unused)]
use soroban_sdk::{contracttype, symbol_short, Address, Env, IntoVal, String};

#[contracttype]
pub enum UserInfoRegistry {
    UserInfo(Address),
}

#[contracttype]
#[derive(Clone)]
pub struct UserInfo {
    pub address: Address,
    pub message: String,
}

pub struct StorageClient;

impl StorageClient {
    pub fn get_default_user_info(env: Env, user: Address) -> UserInfo {
        let message: String = "Hello, Developer!".into_val(&env);
        UserInfo { address: user.clone(), message }
    }
}

2. Exposing the Storage Module

In the main contract file, include mod storage; to access the StorageClient module.

#![no_std]
use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec};

#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn hello(env: Env, to: String) -> Vec<String> {
        vec![&env, String::from_str(&env, "Hello"), to]
    }
}

mod storage;
mod test;

3. Implementing say_hello

#[contractimpl]
  impl Contract {
    // ...
    pub fn say_hello(env: Env, to: Address) -> storage::UserInfo {
      let key = storage::UserInfoRegistry::UserInfo(to.clone()); // gets the key on the storage

      let user_record = env
        .storage()
        .instance()
        .get(&key)
        .unwrap_or(storage::StorageClient::get_default_user_info(env, to)); // gets the user record from the storage

      return user_record; // returns the user record
    }
    // ...
  }

4. Implementing customize_greeting

#[contractimpl]
impl Contract {
  // ...
  pub fn customize_greeting(env: Env, to: Address, message: String) -> storage::UserInfo {
    let key = storage::UserInfoRegistry::UserInfo(to.clone()); // gets the key on the storage

    let mut user_record =
      env
        .storage()
        .instance()
        .get(&key)
        .unwrap_or(storage::StorageClient::get_default_user_info(
          env.clone(),
          to,
        )); // gets the user record from the storage

    user_record.message = message; // modify the message

    env.storage().instance().set(&key, &user_record); // save message on storage

    return user_record; // returns the user record
  }
  // ...
}

5. Final Implementation

#![no_std]
use soroban_sdk::{contract, contractimpl, Address, Env, String};

#[contract]
pub struct Contract;

#[contractimpl]
impl Contract {
    pub fn say_hello(env: Env, to: Address) -> storage::UserInfo {
        let key = storage::UserInfoRegistry::UserInfo(to.clone()); // gets the key on the storage

        let user_record = env
            .storage()
            .instance()
            .get(&key)
            .unwrap_or(storage::StorageClient::get_default_user_info(env, to)); // gets the user record from the storage

        return user_record; // returns the user record
    }

    pub fn customize_greeting(env: Env, to: Address, message: String) -> storage::UserInfo {
        let key = storage::UserInfoRegistry::UserInfo(to.clone()); // gets the key on the storage

        let mut user_record =
            env
                .storage()
                .instance()
                .get(&key)
                .unwrap_or(storage::StorageClient::get_default_user_info(
                    env.clone(),
                    to,
                )); // gets the user record from the storage

        user_record.message = message;

        env.storage().instance().set(&key, &user_record);

        return user_record; // returns the user record
    }
}

mod storage;
mod test;

Writing Tests

In this section, we’ll explore how to write and run unit tests for Soroban smart contracts using Rust. Unit testing plays a crucial role in smart contract development by allowing developers to verify the correctness of individual contract functions in an isolated environment

1. Testing say_hello

#[test]
fn test_say_hello() {
    let env = Env::default();

    let contract_id = env.register(Contract, ()); // registers the contract
    let client = ContractClient::new(&env, &contract_id); // deploys/instantiates the contract

    let alice = Address::generate(&env); // generates a random address

    let greeting = client.say_hello(&alice); // calls the say_hello function

    let message = String::from_str(&env, "Hello Developer!"); // creates the default expected message

    // asserts that the values match
    assert_eq!(greeting.address, alice);
    assert_eq!(greeting.message, message);
}

2. Testing customize_greeting

#[test]
fn test_customize_greeting() {
    let env: Env = Env::default();

  let contract_id = env.register(Contract, ()); // registers the contract
  let client = ContractClient::new(&env, &contract_id); // deploys/instantiates the contract

  let alice = Address::generate(&env); // generates a random address

  let message = String::from_str(&env, "Hello Alice!"); // creates the customized expected message

  let greeting = client.customize_greeting(&alice, &message); // calls the customize_greeting function to change the message on storage

  let greeting_say_hello = client.say_hello(&alice); // calls the say_hello function to ensure that the message was changed

    // asserts that the values match
  assert_eq!(greeting.address, alice);
  assert_eq!(greeting.message, message);
  assert_eq!(greeting_say_hello.address, alice);
  assert_eq!(greeting_say_hello.message, message);
}

Run tests with:

make test

Deploying and Interacting with the Contract

Now that we have our smart contract ready and tested by unit tests, let's deploy it to the Stellar Testnet network using stellar-cli. This will allow us to interact with our contract in a real blockchain environment, simulating how it would work in production. We'll go through the process of setting up a testnet network connection, creating a test account, deploying our contract, and interacting with it step by step.

1. Adding Network to Stellar CLI

stellar network add --global testnet \
  --rpc-url https://soroban-testnet.stellar.org:443 \
  --network-passphrase "Test SDF Network ; September 2015"

2. Creating a Test Account

stellar keys generate --global alice --network testnet

3. Deploying the Contract

stellar contract deploy \
  --wasm target/wasm32-unknown-unknown/release/greeting_contract.wasm \
  --source alice \
  --network testnet

ℹ️ Simulating install transaction…
ℹ️ Signing transaction: feecf902b1eba34529ed340c9bf28cacd179c3c0e628ccdbb50414d1b5265a51
🌎 Submitting install transaction…
ℹ️ Using wasm hash 9be6a4a2a6c1c4794ce50d38ce69929d0f8d5eb58a9a4451b861c07d8f1dda25
ℹ️ Simulating deploy transaction…
ℹ️ Transaction hash is 38a2ee724c0e1c3ab7c90bdb732d777c21e3e8bd5332bc2aae422deabadbf756
🔗 https://stellar.expert/explorer/testnet/tx/38a2ee724c0e1c3ab7c90bdb732d777c21e3e8bd5332bc2aae422deabadbf756
ℹ️ Signing transaction: 38a2ee724c0e1c3ab7c90bdb732d777c21e3e8bd5332bc2aae422deabadbf756
🌎 Submitting deploy transaction…
🔗 https://stellar.expert/explorer/testnet/contract/CCSYEHWP6B2UZ7AIJKMV2EP5CFAPAEUE2EDJQN4AN5IUNKP5GHT5RW2X
 Deployed!
CCSYEHWP6B2UZ7AIJKMV2EP5CFAPAEUE2EDJQN4AN5IUNKP5GHT5RW2X

4. Invoking the Contract

stellar contract invoke \
  --id CCSYEHWP6B2UZ7AIJKMV2EP5CFAPAEUE2EDJQN4AN5IUNKP5GHT5RW2X \
  --source alice \
  --network testnet \
  -- say_hello \
  --to GBGOKKUZ4OMRT26PDCSBCVON2FZCGBNDHGXSMQT2AHAKR22GRR74U2B4

ℹ️ Simulation identified as read-only. Send by rerunning with `--send=yes`.
{"address":"GBGOKKUZ4OMRT26PDCSBCVON2FZCGBNDHGXSMQT2AHAKR22GRR74U2B4","message":"Hello Developer!"}

Conclusion

Congratulations! You've successfully written, tested, deployed, and invoked your first Soroban smart contract on the Stellar Blockchain using Rust. Now, you can explore more advanced contract functionalities and integrate them into real-world applications.

Happy coding! 🚀

Profile picture
Luiz Fernando - シニアソフトウェアエンジニア

読んでくれてありがとう!

この記事を楽しんで読んでいただければ幸いです。ご質問やご意見がございましたら、下記のソーシャル メディアからお気軽にご連絡ください。良い一日をお過ごしください。

Carousel imageCarousel imageCarousel imageCarousel imageCarousel image
次へ / 良い会話から始めましょう

大きなアイデア。
Little Luiz.

実現したいプロダクト、強くしたいチーム。一緒に何ができるか、話してみませんか。

道を選ぶ
01Freelance / Products

プロジェクトの相談

フリーランスでの協業、プロダクト開発、技術的な課題。

  • プロダクトの発見からリリースまで
  • ウェブ、モバイル、バックエンド開発
  • 明確なスコープと直接の協業
一緒につくりましょう
02Hiring / Teams

チームへの採用

エンジニアの採用と長期的な機会。

  • シニアフルスタックエンジニアリング
  • プロダクトの視点と技術的な責任
  • 分散チームでの経験
チームへの採用相談
03Résumé / Versions

履歴書を見る

汎用エンジニアリング、ヘルステック、フィンテックなど、用途別のバージョン。

  • 6つの特化バージョン
  • 印刷用PDFエクスポート
  • 役割ごとに更新
履歴書を見る
luizepauloxd@gmail.com

メールをご希望ですか?直接送るか、こちらで下書きを作成できます。

考えていることを教えてください。

© 2026 Luiz Fernando 意図と、少しの好奇心を込めて。ページの先頭へ