Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5db870d
feat: first draft of tutorial
zachfedor Sep 23, 2025
c225eab
add steps 3 and 4
zachfedor Oct 14, 2025
c853625
fix: rewrite getting started
zachfedor Oct 22, 2025
deb88e7
fix(tutorial): add missing "for"
chadoh Oct 22, 2025
a17d50f
fix(tutorial): better formatting to explain 'environments.toml'
chadoh Oct 22, 2025
048c92d
fix(tutorial): smooth & enrich point #3
chadoh Oct 22, 2025
33fc8e3
fix(tutorial): more your, less our
chadoh Oct 22, 2025
6c810fb
fix(tutorial): conctract \& explorer typos
chadoh Oct 22, 2025
aed4a40
fix(tutorial): add parenthentical about inner attributes
chadoh Oct 22, 2025
fe5be68
fix(tutorial): we're no longer using pre-defined traits
chadoh Oct 22, 2025
86c63fa
fix(tutorial): "don't want _just_ anyone"
chadoh Oct 22, 2025
6d366f3
fix(tutorial): use consistent 'init' name
chadoh Oct 22, 2025
3b730b5
Update docs/tutorial/01-getting-started.md
zachfedor Oct 23, 2025
9d58623
Update docs/tutorial/01-getting-started.md
zachfedor Oct 23, 2025
9bdddc1
Update docs/tutorial/01-getting-started.md
zachfedor Oct 23, 2025
20f9809
feat: move tutorial to docusaurus
chadoh Oct 28, 2025
a493fc4
fix: remove stopgap tutorial page
zachfedor Oct 28, 2025
0b542c8
WIP: mv env.toml walkthrough to Step 2; break contract
chadoh Oct 28, 2025
7830e5c
step 2: trigger the bug, learn env.toml
chadoh Oct 30, 2025
b13395e
rename step 3 to "adding payments"
chadoh Oct 30, 2025
98c9188
Willem updates to 'pub' info in website/docs/tutorial/02-making-impro…
chadoh Oct 30, 2025
d35192d
Willem explains cfg(test) in website/docs/tutorial/01-getting-started.md
chadoh Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions website/docs/tutorial/00-overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
sidebar_label: Overview
---
# Tutorial Overview

This tutorial will help you learn how to use [Scaffold Stellar](https://github.com/theahaco/scaffold-stellar) to build and manage smart contracts on the Stellar blockchain and a decentralized application (dApp) to interact with them. Scaffold Stellar is a developer toolkit that provides CLI tools, contract templates, and a starter React UI to get your idea out of your head and on to the network as fast as possible.

:::tip
If you just want to get up and running quickly, check out the [Quick Start](/docs/quick-start.mdx) guide.
:::

Before jumping in, you should have a basic understanding of the command line and of general programming concepts, but we'll walk through all the code together so don't worry if you're new to Stellar, Rust, or dApp development. We'll link out to [The Rust Programming Language book](https://doc.rust-lang.org/stable/book/) to explain concepts if you want to dive deeper.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also link to the official docs here and recommend people to take some time there https://developers.stellar.org


It's split into four sections:
1. [Getting Started](./01-getting-started.md): will help you setup your development environment, initialize a new project, and explain the architecture and contract code
2. [Making Improvements](./02-making-improvements.md): will explain the front-end architecture and more CLI tooling to get you used to the development workflow
3. [Adding Transactions](./03-adding-transactions.md): will show examples of working with real transactions of XLM in smart contracts and interacting with wallets in the dApp
4. [Best Practices](./04-best-practices.md): will add some final polish to our contract to make sure everything is ready to be deployed to production

Well, what are you waiting for? [Get started!](./01-getting-started.md)
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Tutorial: A Guess the Number Game
---
sidebar_label: Getting Started
---

This section will guide you through the development workflow for using Scaffold Stellar to build and deploy a Guess the Number game with a simple smart contract and an integrated frontend application. You should have a basic understanding of the command line and of general programming concepts, but we'll walk through all the code together so don't worry if you're new to Stellar, Rust, or dApp development.
# Getting Started with Scaffold Stellar

:::tip
If you just want to get up and running quickly, check out the [Quick Start](/docs/quick-start.mdx) guide.
:::
This section will guide you through the development workflow for using Scaffold Stellar to build and deploy a Guess the Number game with a simple smart contract and an integrated frontend application.

We'll cover:

Expand Down Expand Up @@ -43,22 +43,25 @@ We'll run a local Stellar network inside a Docker container, so head to the [Get

### Scaffold Stellar

Lastly, we'll install the Scaffold Stellar plugin for the Stellar CLI. We suggest using cargo-binstall to install it, which is a tool for installing Rust binaries.
Lastly, we'll install the Scaffold Stellar plugin for the Stellar CLI. We suggest using cargo-binstall to install it, which is a tool for installing Rust binaries.

If you don't have it installed, you can do so with:

#### Macos or Linux:

```bash
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
```
#### Windows:
<details>
<summary>Macos or Linux</summary>
```bash
curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
```
</details>

<details>
<summary>Windows</summary>
```powershell
Set-ExecutionPolicy Unrestricted -Scope Process; iex (iwr "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.ps1").Content
```
</details>

### Then install Scaffold Stellar with:
Then install Scaffold Stellar with:

```bash
cargo binstall -y stellar-scaffold-cli
Expand All @@ -70,9 +73,6 @@ Or if you prefer, you can install it directly with Cargo which will compile it f
cargo install --locked stellar-scaffold-cli
```

Preferred installation is binstall


<a id="init"></a>
## 🏗️ Initialize Your Project

Expand Down Expand Up @@ -115,11 +115,11 @@ Open the project in your editor. You will see a generated project structure incl
├── Cargo.lock
├── Cargo.toml
├── contracts/
└──guess-the-number
├── Cargo.toml
└── src
├── lib.rs
└── test.rs
   └──guess-the-number
   ├── Cargo.toml
   └── src
   ├── lib.rs
   └── test.rs
├── environments.toml
├── packages/
├── README.md
Expand Down Expand Up @@ -193,7 +193,7 @@ Once your wallet balance has some XLM, you should see the "GuessTheNumber" compo
<a id="code"></a>
## 🔎 Understand the Contract Code

Now navigate to our contract explorer. Click the "Debugger" button in the header. These are our contract developer tools. They'll let you explore the contracts available to your application, view documentation, and even run methods to help debug them right from your app!
Now navigate to our contract explorer. Click the "&lt;/&gt; Debugger" button in the header. These are our contract developer tools. They'll let you explore the contracts available to your application, view documentation, and even run methods to help debug them right from your app!

Select the `guess_the_number` contract and you should see its Contract ID from the local network deployment. You'll also see the contract's documentation for methods like:

Expand Down Expand Up @@ -223,7 +223,7 @@ In this case `#[contract]` is an [attribute macro](https://doc.rust-lang.org/ref
Here we're defining a [struct](https://doc.rust-lang.org/book/ch05-01-defining-structs.html) (a "structure" to hold values) and applying attributes of a Stellar smart contract. A `struct` also allows defining methods. In this case the structs holds no values but we will still define methods on it.

```rust
const THE_NUMBER: &Symbol = &symbol_short!("n");
const THE_NUMBER: Symbol = symbol_short!("n");
pub const ADMIN_KEY: &Symbol = &symbol_short!("ADMIN");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I always prefer & here since you always refer to them as references, I say we stick with one or the other. Perhaps the value as to not introduce references at this point.

```

Expand All @@ -239,41 +239,45 @@ impl GuessTheNumber {
Let's `impl`ement our contract's functionality.

```rust
pub fn __constructor(env: &Env, admin: &Address) {
Self::set_admin(env, admin);
}
pub fn __constructor(env: &Env, admin: &Address) {
Self::set_admin(env, admin);
}
```

A contract's `constructor` runs when it is deployed. In this case, we're saying who has access to the admin functions. We don't want just anyone to be able to reset our number, do we?!

```rust
/// Update the number. Only callable by admin.
pub fn reset(env: &Env) {
Self::require_admin(env);
let new_number: u64 = env.prng().gen_range(1..=10);
env.storage().instance().set(&THE_NUMBER, &new_number);
}
/// Update the number. Only callable by admin.
pub fn reset(env: &Env) {
Self::require_admin(env);
let new_number: u64 = env.prng().gen_range(1..=10);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are inconsistent with the type here

env.storage().instance().set(&THE_NUMBER, &new_number);
}
```

And here is the reset function. Note that we use `require_admin()` here so only you can run this function. It generates a random number between 1 and 10 and uses our key to store it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a "custom" function. I am not sure if we should discuss this here or later. I think when we introduce no_std, it's would be a good place to say that we "augmented" the SDK with these.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disregard, I see the full contract now in the part 2.


```rust
/// Guess a number between 1 and 10
pub fn guess(env: &Env, a_number: u64) -> bool {
a_number == env.storage().instance().get::<_, u64>(&THE_NUMBER).unwrap()
}
/// Guess a number from 1 to 10
pub fn guess(env: &Env, a_number: u64) -> bool {
a_number
== env
.storage()
.instance()
.get::<_, u64>(&THE_NUMBER)
.expect("no number set")
}
```

Finally, we add the `guess` function which accepts a number as the guess and compares it to the stored number, returning the result. Notice we're using our defined key (that small Symbol) to find stored data that may or may not be there. That's why we need [`unwrap()`](https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html), but we'll talk more about `Option` values later in the tutorial.
Finally, we add the `guess` function which accepts a number as the guess and compares it to the stored number, returning the result. Notice we're using our defined key (that small Symbol) to find stored data that may or may not be there. The thing returned from `get` is a Rust [Option](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values), which is Rust's improvement over the `null` type. An `Option` can be `Some` or `None`. We use [`expect()`](https://doc.rust-lang.org/std/option/enum.Option.html#method.expect) to return the value contained in the `Some` or to panic with the "no number set" message if `None`. We'll talk more about `Option` values later in the tutorial.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should go into details

Suggested change
Finally, we add the `guess` function which accepts a number as the guess and compares it to the stored number, returning the result. Notice we're using our defined key (that small Symbol) to find stored data that may or may not be there. The thing returned from `get` is a Rust [Option](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values), which is Rust's improvement over the `null` type. An `Option` can be `Some` or `None`. We use [`expect()`](https://doc.rust-lang.org/std/option/enum.Option.html#method.expect) to return the value contained in the `Some` or to panic with the "no number set" message if `None`. We'll talk more about `Option` values later in the tutorial.
Finally, we add the `guess` function which accepts a number as the guess and compares it to the stored number, returning the result. Notice we're using our defined key (that small Symbol) to find stored data that may or may not be there. The thing returned from `get` is a Rust [Option](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#the-option-enum-and-its-advantages-over-null-values). An `Option` can be `Some` or `None`. We use [`expect()`](https://doc.rust-lang.org/std/option/enum.Option.html#method.expect) to return the value contained in the `Some` or to panic with the "no number set" message if `None`. We'll talk more about `Option` values later in the tutorial.


```rust
mod test;
```

Post Script: this last line includes the test module into this file. It's handy to write unit tests for our code in a separate file (`contracts/guess-the-number/src/test.rs`), but you could also write them inline if you want.

### 👷 Let's Make a Change
### 👷‍♀️ Let's Make a Change

We should still have our original `npm start` command running. I told you it did a lot of heavy lifting for you, but it also updates all of that automatically whenever you make changes to your code. Let's test it out by making a small change and watch the dev server update immediately.

Expand All @@ -288,55 +292,6 @@ Save the file and watch your terminal output. The contracts get rebuilt, redeplo

Tada!

### 👀 Looking Deeper at the Output

Sharp eyed readers might have noticed interesting lines in the build output. Let's take a look:

```
[0] ℹ️ Starting local network
```

This is the Docker container running Stellar locally. You can manage it with `stellar container` commands, but Scaffold Stellar should handle everything for you. We recommend using the local network for development because it's simpler than working with [the other networks](https://developers.stellar.org/docs/learn/fundamentals/networks), but we'll make use of them in later parts of the tutorial.

```
ℹ️ Build Summary:
[0] Wasm File: target/stellar/local/guess_the_number.wasm
[0] Wasm Hash: 2cda774ed515acb3af208478e64841d6253d52366f22156c5e7a550ee7b06cbe
[0] Exported Functions: 5 found
[0] • _
[0] • __constructor
[0] • guess
[0] • reset
[0] • upgrade
[0] ✅ Build Complete
```

You'll see a build summary for each of our contracts as they compile to Wasm, the code that will actually run on the blockchain. There's a few other functions in our contract that weren't exported here. That's because they're private. We'll implement our own in the next part of the tutorial and explain why they're useful.

```
[0] ℹ️ Creating keys for "me"
[0] ✅ Key saved with alias me in "/Users/zach/code/guessing-game-tutorial/.config/stellar/identity/me.toml"
[0] ✅ Account me funded on "Standalone Network ; February 2017"
```

Scaffold Stellar creates an account for you to use on the local network and funds it so you can actually deploy your contracts. You can manage these accounts with the `stellar keys` command.

```
[0] ✅ Deployed!
[0] ℹ️ ↳ contract_id: CBPAPSB7SXM3MNJVLXPSD6BRQ2ZN33OQVYWO45332TOP4PQLMCHJV4QN
[0] ℹ️ Running after_deploy script for "guess_the_number"
```

After deployment, our `__constructor` method is run which initializes our contract. This is a perfect time to setup anything your contract needs to run. We also ran an `after_deploy` script to call our contract's `reset` method and generate a random number for us to guess. These are all configured in the `environments.toml` file and we'll dive into that in the next part of the tutorial.

```
[0] ℹ️ Binding "guess_the_number" contract
...
[0] ✅ Client "guess_the_number" created successfully
```

This is final piece of the puzzle. Now that the contract is deployed on chain, we can use it to generate an RPC client and TypeScript bindings used in our frontend code.

<a id="app-code"></a>
## 🔎 Understand the Application Code

Expand Down Expand Up @@ -399,7 +354,7 @@ That's it! Scaffold Stellar does all the heavy lifting, letting you jump right i

## What's Next?

This is a great place to start, but there's a few things we can improve on. For example, our number is currently stored in plain text on the contract's storage. We
That's a good start, but there's a lot we can improve on. In the next step, we'll:

- Improve the contract code to make it more robust
- Learn about private contract methods
Expand Down
Loading