-
Notifications
You must be signed in to change notification settings - Fork 23
feat: first draft of tutorial #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 21 commits
5db870d
c225eab
c853625
deb88e7
a17d50f
048c92d
33fc8e3
6c810fb
aed4a40
fe5be68
86c63fa
6d366f3
3b730b5
9d58623
9bdddc1
20f9809
a493fc4
0b542c8
7830e5c
b13395e
98c9188
d35192d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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. | ||
|
|
||
| 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: | ||||||
|
|
||||||
|
|
@@ -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 | ||||||
|
|
@@ -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 | ||||||
chadoh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| ``` | ||||||
|
|
||||||
| Preferred installation is binstall | ||||||
|
|
||||||
|
|
||||||
| <a id="init"></a> | ||||||
| ## 🏗️ Initialize Your Project | ||||||
|
|
||||||
|
|
@@ -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 | ||||||
|
|
@@ -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 "</> 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: | ||||||
|
|
||||||
|
|
@@ -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"); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I always prefer |
||||||
| ``` | ||||||
|
|
||||||
|
|
@@ -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); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
chadoh marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| 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); | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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") | ||||||
| } | ||||||
zachfedor marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| ``` | ||||||
|
|
||||||
| 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. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we should go into details
Suggested change
|
||||||
|
|
||||||
| ```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. | ||||||
chadoh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
|
|
||||||
| ### 👷 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. | ||||||
|
|
||||||
|
|
@@ -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 | ||||||
|
|
||||||
|
|
@@ -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 | ||||||
|
|
||||||
There was a problem hiding this comment.
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