Skip to content

Commit 894f242

Browse files
Update tutorial to match frontend tutorial branch (#344)
* Clean up tutorial to match frontend tutorial branch * Tweaks to adding-payments * Apply formatter updates * Make ADMIN_KEY private because it doesn't need to be public in tutorial * A few more tweaks to be the same as the tutorial code
1 parent 53234ea commit 894f242

3 files changed

Lines changed: 46 additions & 46 deletions

File tree

website/docs/tutorial/01-getting-started.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,15 @@ In this case `#[contract]` is an [attribute macro](https://doc.rust-lang.org/ref
225225
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.
226226

227227
```rust
228-
const THE_NUMBER: Symbol = symbol_short!("n");
229-
pub const ADMIN_KEY: &Symbol = &symbol_short!("ADMIN");
228+
const THE_NUMBER: &Symbol = &symbol_short!("n");
229+
const ADMIN_KEY: &Symbol = &symbol_short!("ADMIN");
230230
```
231231

232232
Now the most important part of our contract: the number! This line creates a key for storing and retrieving contract data. A `Symbol` is a short string type (max 32 characters) that is more optimized for use on the blockchain. And we're using the `symbol_short` macro for an even smaller key (max 9 characters). As a contract author, you want to use tricks like this to lower costs as much as you can.
233233

234-
The second line creates a key for storing the address of this contract's administrator. It's almost the same code as storing our number, but uses the `&` which is called a [reference](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html). Instead of the value, it's a pointer to where the value lives.
234+
The second line creates a key for storing the address of this contract's administrator.
235+
236+
Both of these keys use the `&` which is called a [reference](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html). Instead of the value, it's a pointer to where the value lives.
235237

236238
```rust
237239
#[contractimpl]
@@ -241,7 +243,7 @@ impl GuessTheNumber {
241243
Let's `impl`ement our contract's functionality.
242244

243245
```rust
244-
pub fn __constructor(env: &Env, admin: &Address) {
246+
pub fn __constructor(env: &Env, admin: Address) {
245247
Self::set_admin(env, admin);
246248
}
247249
```
@@ -253,20 +255,20 @@ A contract's `constructor` runs when it is deployed. In this case, we're saying
253255
pub fn reset(env: &Env) {
254256
Self::require_admin(env);
255257
let new_number: u64 = env.prng().gen_range(1..=10);
256-
env.storage().instance().set(&THE_NUMBER, &new_number);
258+
env.storage().instance().set(THE_NUMBER, &new_number);
257259
}
258260
```
259261

260262
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.
261263

262264
```rust
263-
/// Guess a number from 1 to 10
265+
/// Guess a number between 1 and 10
264266
pub fn guess(env: &Env, a_number: u64) -> bool {
265267
a_number
266268
== env
267269
.storage()
268270
.instance()
269-
.get::<_, u64>(&THE_NUMBER)
271+
.get::<_, u64>(THE_NUMBER)
270272
.expect("no number set")
271273
}
272274
```
@@ -277,7 +279,7 @@ Finally, we add the `guess` function which accepts a number as the guess and com
277279
mod test;
278280
```
279281

280-
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 by defining the module. Note you also need to tell the compile that this is a test module, which is at the top of our file `#![cfg(test)]`.
282+
This line makes sure to include the test module in the 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 by defining the module. Note you also need to tell the compile that this is a test module, which is at the top of our file `#![cfg(test)]`.
281283

282284
```rust
283285
#[cfg(test)]
@@ -297,7 +299,7 @@ The docstring for our `guess` function says to guess a number "between 1 and 10"
297299
pub fn guess(env: &Env, a_number: u64) -> bool {
298300
```
299301

300-
Save the file and watch your terminal output. The contracts get rebuilt, redeployed, and clients for them get regenerated for your frontend. Then Vite hot-reloads your app and you should see the change in the contract explorer in your browser.
302+
Save the file and watch your terminal output. The contracts get rebuilt, redeployed, and clients for them get regenerated for your frontend. Then Vite hot-reloads your app and you should see the change in the Debugger in your browser.
301303

302304
Tada!
303305

@@ -324,8 +326,11 @@ We're storing some state for tracking the input's value and whether the guess wa
324326
325327
```ts
326328
const submitGuess = async () => {
327-
if (!theGuess) return;
328-
const { result } = await game.guess({ a_number: BigInt(theGuess) });
329+
if (!theGuess || !address) return;
330+
const { result } = await game.guess({
331+
a_number: BigInt(theGuess),
332+
guesser: address,
333+
});
329334
setGuessedIt(result);
330335
};
331336
```

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

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ Let's walk through this line by line:
154154
1. The Wasm gets uploaded to the blockchain, so that many contracts could use it.
155155
2. A contract gets deployed (aka "instantiated", in the current parlance of this output) so that there is an actual smart contract that refers to, or points to, that Wasm.
156156

157-
- `after_deploy`: calls to the contract to make after it gets deployed. Kind of like the `constructor_args`, these are specified using _only_ the part that comes after the `--`. The setting above tells Scaffold CLI to make the following call, after deploying the contract:
157+
- `after_deploy`: method calls to make to the contract after it gets deployed. Kind of like the `constructor_args`, but, these are specified using _only_ the part that comes after the `--`. The setting above tells Scaffold CLI to make call the `reset` method, after deploying the contract:
158158

159159
```bash
160160
stellar contract deploy \
@@ -192,9 +192,9 @@ If you already tried re-running the `guess` logic in the app, you'll see...
192192

193193
Nothing. Nothing happens. At least not yet.
194194

195-
The contract didn't change, so Scaffold CLI didn't re-deploy the contract. You're still using the one that had the `reset` method called right after deploy.
195+
The contract didn't change, so Scaffold CLI didn't re-deploy the contract. You're still using the instance that had the `reset` method called right after deploy.
196196

197-
Once [theahaco/scaffold-stellar#259](https://github.com/theahaco/scaffold-stellar/issues/259) is complete, you will be able to run `stellar scaffold reset`. Until then, you can remove the alias that Scaffold Stellar uses to keep track of this contract. Stop the `npm run start` process, then run:
197+
Once [theahaco/scaffold-stellar#259](https://github.com/theahaco/scaffold-stellar/issues/259) is complete, you will be able to run `stellar scaffold reset`. Until then, you can remove the alias that Scaffold Stellar uses to keep track of this contract, which allows us to re-deploy a new `guess_the_number` contract. Stop the `npm run start` process, then run:
198198

199199
```bash
200200
stellar contract alias remove guess_the_number --network local
@@ -242,7 +242,7 @@ impl GuessTheNumber {
242242
/// Private helper function to generate and store a new random number
243243
fn set_random_number(env: &Env) {
244244
let new_number: u64 = env.prng().gen_range(1..=10);
245-
env.storage().instance().set(&THE_NUMBER, &new_number);
245+
env.storage().instance().set(THE_NUMBER, &new_number);
246246
}
247247
}
248248
```
@@ -261,7 +261,7 @@ Notice that this function doesn't have `pub` in front of it - this makes it priv
261261
Now let's modify the `__constructor` to set an initial number when the contract is deployed:
262262

263263
```rust
264-
pub fn __constructor(env: &Env, admin: &Address) {
264+
pub fn __constructor(env: &Env, admin: Address) {
265265
Self::set_admin(env, admin);
266266
Self::set_random_number(env); // Add this line
267267
}
@@ -291,7 +291,7 @@ Much cleaner! The logic is now centralized in our helper function. Note that thi
291291
$ npm start
292292
```
293293

294-
Click over to `&lt;/&gt; Debugger` if you're not there already and select the `guess_the_number` contract. You'll see that `reset` is listed here, but `set_random_number` is not.
294+
Click over to the Debugger if you're not there already and select the `guess_the_number` contract. You'll see that `reset` is listed here, but `set_random_number` is not.
295295

296296
Our `reset` method is available to be called by code _outside_ our contract because we opted in to it being a public method with the `pub` keyword. Our `set_random_number` is private by default, it's not visible to the outside world. It's not listed in the Contract Explorer. It's not listed in the CLI help either:
297297

@@ -315,7 +315,7 @@ error: unrecognized subcommand 'set_random_number'
315315

316316
Nope! Just because we made it public, we still require authentication so only admins can call it. Rust's idea of public vs private handles "where" the functions can be called. You still need to handle "who" calls it. That's why we set the contract admin in it's constructor method and check it with `Self::require_admin(env);`.
317317

318-
You can try this out by invoking it from the Contract Explorer in your browser. The admin is `me`, but you didn't import that account into your browser wallet. Go ahead and hit `Submit` on the `reset` function.
318+
You can try this out by invoking it from the Debugger in your browser. The admin is `me`, but you didn't import that account into your browser wallet. Go ahead and hit `Submit` on the `reset` function. You should see the transaction fail.
319319

320320
You could also try this out in the CLI. Create a non-admin identity to see how it fails:
321321

@@ -383,12 +383,13 @@ use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Sy
383383
#[contract]
384384
pub struct GuessTheNumber;
385385

386-
const THE_NUMBER: Symbol = symbol_short!("n");
387-
pub const ADMIN_KEY: &Symbol = &symbol_short!("ADMIN");
386+
const THE_NUMBER: &Symbol = &symbol_short!("n");
387+
const ADMIN_KEY: &Symbol = &symbol_short!("ADMIN");
388388

389389
#[contractimpl]
390390
impl GuessTheNumber {
391-
pub fn __constructor(env: &Env, admin: &Address) {
391+
/// Constructor to initialize the contract with an admin and a random number
392+
pub fn __constructor(env: &Env, admin: Address) {
392393
Self::set_admin(env, admin);
393394
Self::set_random_number(env);
394395
}
@@ -399,22 +400,22 @@ impl GuessTheNumber {
399400
Self::set_random_number(env);
400401
}
401402

402-
/// Guess a number between 1 and 10
403+
/// Guess a number between 1 and 10, inclusive
403404
pub fn guess(env: &Env, a_number: u64) -> bool {
404405
a_number == Self::number(env)
405406
}
406407

407408
/// Private helper function to generate and store a new random number
408409
fn set_random_number(env: &Env) {
409410
let new_number: u64 = env.prng().gen_range(1..=10);
410-
env.storage().instance().set(&THE_NUMBER, &new_number);
411+
env.storage().instance().set(THE_NUMBER, &new_number);
411412
}
412413

413414
/// readonly function to get the current number
414415
fn number(env: &Env) -> u64 {
415416
// We can unwrap because the number is set in the constructor
416417
// and then only reset by the admin
417-
unsafe { env.storage().instance().get(THE_NUMBER).unwrap_unchecked() }
418+
unsafe { env.storage().instance().get::<_, u64>(THE_NUMBER).unwrap_unchecked() }
418419
}
419420

420421
/// Upgrade the contract to new wasm. Only callable by admin.
@@ -428,13 +429,13 @@ impl GuessTheNumber {
428429
env.storage().instance().get(ADMIN_KEY)
429430
}
430431

431-
/// Set a new admin. Only callable by admin.
432-
fn set_admin(env: &Env, admin: &Address) {
433-
// Check if admin is already set
434-
if env.storage().instance().has(ADMIN_KEY) {
432+
/// set a new admin. only callable by admin.
433+
pub fn set_admin(env: &env, admin: address) {
434+
// check if admin is already set
435+
if env.storage().instance().has(admin_key) {
435436
panic!("admin already set");
436437
}
437-
env.storage().instance().set(ADMIN_KEY, admin);
438+
env.storage().instance().set(admin_key, &admin);
438439
}
439440

440441
/// Private helper function to require auth from the admin
@@ -454,7 +455,7 @@ Let's test that our improvements work. You should still have the `npm start` pro
454455
1. `stellar scaffold watch --build-clients`: watches for any changes in your `contracts/` folders, then rebuilds and redeploys them
455456
2. `vite`: watches for any changes in your `src/` folder and hot-reloads the UI
456457

457-
That means any time you add a method, tweak arguments, or even add documentation, everything is immediately reflected on the local network, your application in the browser, and in the Contract Explorer. Let's add some info to the `guess` method's documentation:
458+
That means any time you add a method, tweak arguments, or even add documentation, everything is immediately reflected on the local network, your application in the browser, and in the Debugger. Let's add some info to the `guess` method's documentation:
458459

459460
```rust
460461
/// Guess a number between 1 and 10, inclusive. Returns a boolean.
@@ -465,7 +466,7 @@ As soon as you hit save, watch the Contract Explorer reload with the new text. N
465466

466467
### How to Write Unit Tests
467468

468-
TODO
469+
_🏗️✨ Coming soon._
469470

470471
## What We've Learned
471472

website/docs/tutorial/03-adding-payments.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ This creates real stakes and makes the game much more engaging!
2525

2626
## Step 1: 🪙 Add Asset Import
2727

28-
First, we need the `import_asset` macro from Stellar Registry. Add the following to your imports at the top of `lib.rs`:
28+
First, we need the `import_asset` macro from Stellar Registry. Add the following to your imports at the top of your contract `lib.rs` file:
2929

3030
```diff
3131
#![no_std]
3232
use soroban_sdk::{contract, contractimpl, symbol_short, Address, BytesN, Env, Symbol};
3333
+use stellar_registry::import_asset;
34-
+import_asset!(xlm);
34+
+import_asset!("xlm");
3535
```
3636

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).
37+
Stellar Registry integrates with Scaffold Stellar, giving names and versions to contracts and 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).
3838

3939
## Step 2: 💰 Add Funds to the Contract
4040

@@ -46,9 +46,9 @@ Whenever the admin resets the number, we need to transfer some funds to the cont
4646
env.storage().instance().set(&THE_NUMBER, &new_number);
4747

4848
// Seed the initial pot
49-
let x = xlm::client(env);
49+
let x = xlm::token_client(env);
5050
let admin = Self::admin(env).expect("admin not set");
51-
x.transfer(10_000_000_0, &admin, env.current_contract_address());
51+
x.transfer(&admin, env.current_contract_address(), &10_000_000_0);
5252
}
5353
```
5454

@@ -75,21 +75,15 @@ pub fn guess(env: &Env, guesser: Address, a_number: u64) -> bool {
7575
}
7676

7777
// pay full pot to `guesser`, whether they sent the transaction or not
78-
let tx = xlm_client.transfer(
78+
xlm_client.transfer(
79+
&guesser,
7980
env.current_contract_address(),
80-
guesser,
81-
xlm_client.balance(env.current_contract_address()),
81+
&xlm_client.balance(&env.current_contract_address()),
8282
);
83-
if tx.is_err() {
84-
panic!("transfer failed!");
85-
}
8683
} else {
8784
// Before transferring their funds, make sure guesser is actually the one calling this function
8885
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-
}
86+
xlm_client.transfer(&guesser, &env.current_contract_address(), &1_000_000_0);
9387
}
9488

9589
guessed_it

0 commit comments

Comments
 (0)