Skip to content

Commit 572ca91

Browse files
zachfedorchadohwillemnealpselletupui
authored
feat: first draft of tutorial (#193)
* feat: first draft of tutorial * add steps 3 and 4 * fix: rewrite getting started * fix(tutorial): add missing "for" * fix(tutorial): better formatting to explain 'environments.toml' * fix(tutorial): smooth & enrich point #3 * fix(tutorial): more your, less our * fix(tutorial): conctract \& explorer typos * fix(tutorial): add parenthentical about inner attributes * fix(tutorial): we're no longer using pre-defined traits * fix(tutorial): "don't want _just_ anyone" * fix(tutorial): use consistent 'init' name * Update docs/tutorial/01-getting-started.md Co-authored-by: Willem Wyndham <willem@ahalabs.dev> * Update docs/tutorial/01-getting-started.md Co-authored-by: Willem Wyndham <willem@ahalabs.dev> * Update docs/tutorial/01-getting-started.md Co-authored-by: Willem Wyndham <willem@ahalabs.dev> * feat: move tutorial to docusaurus * fix: remove stopgap tutorial page * WIP: mv env.toml walkthrough to Step 2; break contract * step 2: trigger the bug, learn env.toml - remove `environments.toml` walkthrough from step 1 - add it to step 2, as part of finding the line to change to break things - remove `unwrap` vs `expect` considerations, since both result in identical & terrible errors in Soroban context - explain `unsafe` bonus step - clean up outdated references to `alice` and such * rename step 3 to "adding payments" also - update code to more closely match expected final state - add frontend-update section (it's a stub for now) * Willem updates to 'pub' info in website/docs/tutorial/02-making-improvements.md Co-authored-by: Willem Wyndham <willem@ahalabs.dev> * Willem explains cfg(test) in website/docs/tutorial/01-getting-started.md Co-authored-by: Willem Wyndham <willem@ahalabs.dev> * fix: add --tutorial flag * feat: add spanish tutorial content * fix: overview link * fix: fmt * fix: spanish relative links * fix: relative paths * Update website/docs/tutorial/01-getting-started.md Co-authored-by: Pamphile Roy <23188539+tupui@users.noreply.github.com> * Update website/docs/tutorial/02-making-improvements.md Remove slop definition Co-authored-by: Pamphile Roy <23188539+tupui@users.noreply.github.com> * Fix code issues --------- Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com> Co-authored-by: Willem Wyndham <willem@ahalabs.dev> Co-authored-by: Pam S <pam@theaha.co> Co-authored-by: Pamphile Roy <23188539+tupui@users.noreply.github.com> Co-authored-by: Pam Selle <pamela.selle@gmail.com>
1 parent b6948d6 commit 572ca91

11 files changed

Lines changed: 2743 additions & 154 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
sidebar_label: Overview
3+
---
4+
5+
# Tutorial Overview
6+
7+
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.
8+
9+
:::tip If you just want to get up and running quickly, check out the [Quick Start](../quick-start.mdx) guide. :::
10+
11+
## 🎯 What will we build?
12+
13+
Our smart contract will be a Guess The Number game. You (the admin) can deploy the contract, randomly select a number between 1 and 10, and seed the contract with a prize. Users can make guesses and win the prize if they're correct!
14+
15+
We'll use Scaffold Stellar to create the initial project structure containing contract code and a frontend application to interact with it. It will handle all the heavy lifting for us, letting us focus on the game logic in the contract and immediately build up the frontend for users to play the game.
16+
17+
## 📋Prerequisites
18+
19+
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.
20+
21+
## 📑 Contents
22+
23+
This tutorial is split into four sections:
24+
25+
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
26+
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
27+
3. [Adding Transactions](./03-adding-payments.md): will show examples of working with real transactions of XLM in smart contracts and interacting with wallets in the dApp
28+
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
29+
30+
Well, what are you waiting for? [Get started!](./01-getting-started.md)

website/docs/tutorial.mdx renamed to website/docs/tutorial/01-getting-started.md

Lines changed: 100 additions & 147 deletions
Large diffs are not rendered by default.

website/docs/tutorial/02-making-improvements.md

Lines changed: 504 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# Adding in Payments
2+
3+
Now comes the exciting part: adding some economic incentives to our guessing game! We'll implement a system where users pay to play and winners take the entire pot. This is where blockchain development gets really interesting. And honestly, it's probably why you're here in the first place, right?
4+
5+
## What We'll Accomplish
6+
7+
By the end of this step, you'll have:
8+
9+
- A guessing fee that players must pay
10+
- A prize pot that accumulates all the fees
11+
- Automatic winner payouts
12+
- Admin funding of the initial prize pot
13+
- Understanding of token transfers in smart contracts
14+
15+
## Understanding the Economic Model
16+
17+
Here's how our game economics will work:
18+
19+
1. **Admin funds the pot**: When resetting, admin transfers XLM to the contract
20+
2. **Players pay to play**: Each guess costs a small fee (added to the pot)
21+
3. **Winner takes all**: Correct guesses win the entire accumulated pot
22+
4. **New round starts**: Admin can reset with fresh funding
23+
24+
This creates real stakes and makes the game much more engaging!
25+
26+
## Step 1: 🪙 Add Asset Import
27+
28+
First, we need the `import_asset` macro from Stellar Registry. Add the following to your imports at the top of `lib.rs`:
29+
30+
```diff
31+
#![no_std]
32+
use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Symbol};
33+
+use stellar_registry::import_asset;
34+
+import_asset!(xlm);
35+
```
36+
37+
Stellar Registry integrates with Scaffold Stellar, giving names & versions to contracts & contract Wasms. It also provides helpers like `import_asset` to make it easier to work with [Stellar Asset Contracts](https://developers.stellar.org/docs/tokens/stellar-asset-contract).
38+
39+
## Step 2: 💰 Add Funds to the Contract
40+
41+
Whenever the admin resets the number, we need to transfer some funds to the contract to get the pot started. The easiest way to do this is directly in the `set_random_number` method. Remember, this is the private function we call once in the constructor when we first deploy the contract and again any time the reset method is invoked.
42+
43+
```rust
44+
fn set_random_number(env: &Env) {
45+
let new_number: u64 = env.prng().gen_range(1..=10);
46+
env.storage().instance().set(&THE_NUMBER, &new_number);
47+
48+
// Seed the initial pot
49+
let x = xlm::client(env);
50+
let admin = Self::admin(env).expect("admin not set");
51+
x.transfer(10_000_000_0, &admin, env.current_contract_address());
52+
}
53+
```
54+
55+
This creates a client to interact with the XLM contract via cross-contract calls. It gets the admin's address from storage, and then runs a transfer. If the transfer fails, perhaps because the admin does not have sufficient balance, the whole transaction gets rolled back. If this is the call to `__constructor` during the initial deploy, then the deploy will fail.
56+
57+
You may have noticed that the number there looks really big! Seven zeroes after that `10`. When transferring assets in smart contracts, you must use their smallest-divisible unit. For XLM, this means adding seven zeroes. (The smallest unit of XLM is called a [stroop](https://developers.stellar.org/docs/learn/glossary#stroop).)
58+
59+
## Step 3: 🙋 Update the Guess Function
60+
61+
This is the big one! Let's make guessing cost money and pay out winners:
62+
63+
```rust
64+
/// Guess a number between 1 and 10
65+
/// Costs a fee and pays out the entire pot if correct
66+
pub fn guess(env: &Env, guesser: Address, a_number: u64) -> bool {
67+
let xlm_client = xlm::token_client(env);
68+
let contract_address = env.current_contract_address();
69+
let guessed_it = a_number == Self::number(env);
70+
71+
if guessed_it {
72+
let balance = xlm_client.balance(&contract_address);
73+
if balance == 0 {
74+
panic!("Pot already won! New game not yet started.")
75+
}
76+
77+
// pay full pot to `guesser`, whether they sent the transaction or not
78+
let tx = xlm_client.transfer(
79+
env.current_contract_address(),
80+
guesser,
81+
xlm_client.balance(env.current_contract_address()),
82+
);
83+
if tx.is_err() {
84+
panic!("transfer failed!");
85+
}
86+
} else {
87+
// Before transferring their funds, make sure guesser is actually the one calling this function
88+
guesser.require_auth();
89+
let tx = xlm_client.transfer(guesser, env.current_contract_address(), 1_000_000_0);
90+
if tx.is_err() {
91+
panic!("transfer failed!");
92+
}
93+
}
94+
95+
guessed_it
96+
}
97+
```
98+
99+
## Step 4: Update the frontend
100+
101+
TODO: this section is a stub.
102+
103+
In `src/components/GuessTheNumber.tsx`, add this at the top:
104+
105+
```ts
106+
import { wallet } from "../util/wallet";
107+
```
108+
109+
Then change this:
110+
111+
```ts
112+
const submitGuess = async () => {
113+
if (!theGuess) return;
114+
const { result } = await game.guess({ a_number: BigInt(theGuess) });
115+
setGuessedIt(result);
116+
};
117+
```
118+
119+
...to this:
120+
121+
```ts
122+
const submitGuess = async () => {
123+
if (!theGuess) return;
124+
const tx = await game.guess(
125+
{ guesser: address, a_number: BigInt(theGuess) },
126+
// @ts-expect-error js-stellar-sdk has bad typings; publicKey is, in fact, allowed
127+
{ publicKey: address },
128+
);
129+
const { result } = await tx.signAndSend({
130+
signTransaction: wallet.signTransaction.bind(game),
131+
});
132+
setGuessedIt(result);
133+
};
134+
```
135+
136+
## Step 7: Your Complete Updated Contract
137+
138+
Here's your full contract with economic incentives:
139+
140+
_🏗️✨ TODO: add link to github repo_
141+
142+
## Step 8: Test the Economic System
143+
144+
_🏗️✨ TODO: add screenshots of interacting with contract explorer_
145+
146+
Now let's test our new economic features:
147+
148+
### Check the Prize Pot on Deploy
149+
150+
You should see `10000000` (1 XLM)!
151+
152+
### Make Some Paid Guesses
153+
154+
Now let's have different users make guesses:
155+
156+
Use freighter to switch accounts:
157+
158+
_🏗️✨ TODO: add screenshots of interacting with freighter_
159+
160+
Via the CLI:
161+
162+
```bash
163+
# Bob makes a guess (and pays the fee)
164+
stellar contract invoke \
165+
--id [CONTRACT_ID] \
166+
--source bob \
167+
--network local \
168+
-- guess \
169+
--guesser $(stellar keys address bob) \
170+
--a_number 3
171+
172+
# Check the pot after Bob's guess
173+
stellar contract invoke \
174+
--id [CONTRACT_ID] \
175+
--source alice \
176+
--network local \
177+
-- get_prize_pot
178+
```
179+
180+
The pot should now be `10100000` (1.01 XLM). That's the original 1 XLM plus Bob's 0.01 XLM guess fee.
181+
182+
### Test Winning
183+
184+
Keep guessing with different numbers until someone wins. When someone guesses correctly, they'll receive the entire pot, and the pot will reset to 0.
185+
186+
## 🧪 Update the Tests
187+
188+
_🏗️✨ TODO_
189+
190+
## What We've Learned
191+
192+
### 1. Token Economics in Smart Contracts
193+
194+
- **Fee collection**: Charge users for actions
195+
- **Prize pools**: Accumulate fees for distribution
196+
- **Automatic payouts**: Transfer winnings programmatically
197+
198+
### 2. Cross-Contract Calls
199+
200+
- Token transfers are calls to the native token contract
201+
- `token::Client` provides a convenient interface
202+
- All transfers require proper authentication
203+
204+
### 3. State Management
205+
206+
- Track financial state alongside game state
207+
- Update balances consistently
208+
- Handle edge cases (empty pots, etc.)
209+
210+
## What's Next?
211+
212+
In **Step 4**, we'll add professional polish by:
213+
214+
- Implementing proper error handling with custom error types
215+
- Adding security measures
216+
- Adding events for better monitoring
217+
- 🚀 Deploy to mainnet!
218+
219+
This final step will transform our fun game into production-ready code!

0 commit comments

Comments
 (0)