diff --git a/src/ch01-00-getting-started.md b/src/ch01-00-getting-started.md index 32a232b8..ed5e2eb6 100644 --- a/src/ch01-00-getting-started.md +++ b/src/ch01-00-getting-started.md @@ -39,7 +39,7 @@ The script below is a simple `Ownable` contract pattern written in Cairo for Sta ### Cairo Example Contract -```rust +```rust,noplayground use starknet::ContractAddress; #[starknet::interface] diff --git a/src/ch02-00-starknet-tooling.md b/src/ch02-00-starknet-tooling.md index 219939ea..691eb71e 100644 --- a/src/ch02-00-starknet-tooling.md +++ b/src/ch02-00-starknet-tooling.md @@ -22,8 +22,8 @@ In this chapter, you’ll explore: - **Frameworks:** Build using Starknet-Foundry -- **SDKs:** Discover multi-language support through Starknet.js, - Starknet-rs, Starknet_py, and Cairo +- **SDKs:** Discover multi-language support through [Starknet.js](https://github.com/starknet-io/starknet.js), + [Starknet.rs](https://github.com/xJonathanLEI/starknet-rs), [Starknet.py](https://github.com/software-mansion/starknet.py), and [Cairo](https://github.com/starkware-libs/cairo) - **Front-end Development:** Use Starknet.js and React diff --git a/src/ch02-01-basic-installation.md b/src/ch02-01-basic-installation.md index 526f83be..1d08b1b8 100644 --- a/src/ch02-01-basic-installation.md +++ b/src/ch02-01-basic-installation.md @@ -12,7 +12,7 @@ Essential tools to install: manager that compiles code to [Sierra](https://docs.starknet.io/documentation/architecture_and_concepts/Smart_Contracts/cairo-and-sierra), a mid-level language between Cairo and CASM. -3. [Katana](https://github.com/dojoengine/dojo) - Katana is a Starknet node, built for local development. +3. [Katana](https://book.dojoengine.org/toolchain/katana) - Katana is a Starknet node, built for local development. For support or queries, visit our [GitHub Issues](https://github.com/starknet-edu/starknetbook/issues) or contact @@ -36,6 +36,8 @@ starkli --version To upgrade Starkli, simply repeat the steps. +*Note:* to install from source refer to the [installation instructions](https://book.starkli.rs/installation#install-from-source) + ## Scarb Package Manager Installation Scarb is also Cairo's package manager and is heavily inspired by [Cargo](https://doc.rust-lang.org/cargo/), @@ -69,13 +71,13 @@ asdf plugin add scarb This will allow you to download specific versions: ```bash -asdf install scarb 2.5.4 +asdf install scarb 2.6.4 ``` and set a global version: ```bash -asdf global scarb 2.5.4 +asdf global scarb 2.6.4 ``` Otherwise, you can simply run the following command in your terminal, and follow the onscreen instructions. This @@ -89,9 +91,9 @@ curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh ```bash scarb --version -scarb 2.5.4 (28dee92c8 2024-02-14) -cairo: 2.5.4 (https://crates.io/crates/cairo-lang-compiler/2.5.4) -sierra: 1.4.0 +scarb 2.6.4 (c4c7c0bac 2024-03-19) +cairo: 2.6.3 (https://crates.io/crates/cairo-lang-compiler/2.6.3) +sierra: 1.5.0 ``` For Windows, follow manual setup in the [Scarb @@ -114,4 +116,6 @@ katana --version To upgrade Katana, rerun the installation command. +*Note:* to install from source refer to the [installation instructions](https://book.dojoengine.org/toolchain/katana#installing-from-source) + You are now set to code in Cairo and deploy to Starknet. diff --git a/src/ch02-02-01-Starknet-devnet-rs.md b/src/ch02-02-01-Starknet-devnet-rs.md index 9f68e8e1..a087bb5b 100644 --- a/src/ch02-02-01-Starknet-devnet-rs.md +++ b/src/ch02-02-01-Starknet-devnet-rs.md @@ -23,7 +23,7 @@ don’t, refer to Basic Installation in this chapter. - Install [rust](#https://www.rust-lang.org/tools/install) - The required Rust version is specified in [rust-toolchain.toml](#https://github.com/0xSpaceShard/starknet-devnet-rs/blob/main/rust-toolchain.toml) and handled automatically by cargo. -- Run `rustc --version ` to comfirm the rust version. +- Run `rustc --version` to comfirm the rust version. ## Run as a binary diff --git a/src/ch02-02-starkli-scarb-katana.md b/src/ch02-02-starkli-scarb-katana.md index 2eb20cd1..134117f4 100644 --- a/src/ch02-02-starkli-scarb-katana.md +++ b/src/ch02-02-starkli-scarb-katana.md @@ -17,15 +17,15 @@ don’t, refer to Basic Installation in this chapter. **Important:** Before we proceed with this example, please ensure that the versions of both `katana` and `starkli` match the specified versions provided below. ```console - katana --version # 0.6.0-alpha.7 - starkli --version # 0.2.8 (f59724e) + katana --version # 0.7.0-alpha.5 + starkli --version # 0.2.9 (0535f44) ``` If this is not your case, you have to install them like this: ```sh - dojoup -v 0.6.0-alpha.7 - starkliup -v 0.2.8 + dojoup -v 0.7.0-alpha.5 + starkliup -v 0.2.9 ``` Now begin by initiating a Scarb project: @@ -49,14 +49,14 @@ Amend the `Scarb.toml` file to integrate the `starknet` dependency and introduce ```toml [dependencies] - starknet = ">=2.5.4" + starknet = ">=2.6.3" [[target.starknet-contract]] ``` For streamlined Starkli command execution, establish environment variables. Two primary variables are essential: -- One for your account, a pre-funded account on the local development network +- One for your account, a pre-funded account on the local development network (you can refer to [this chapter](/ch01-00-getting-started.html#smart-wallet-setup)) - Another for designating the network, specifically the local katana devnet In the `src/` directory, create a `.env` file with the following: @@ -77,7 +77,7 @@ Deploying a Starknet smart contract requires two primary steps: Begin with the `src/lib.cairo` file, which provides a foundational template. Remove its contents and insert the following: -```rust +```rust,noplayground #[starknet::interface] trait IHello { fn get_name(self: @T) -> felt252; @@ -134,7 +134,7 @@ katana To declare your contract, execute: ```bash -starkli declare target/dev/my_contract_hello.contract_class.json +starkli declare target/dev/my_contract_hello.contract_class.json --compiler-version 2.6.2 ``` Facing an "Error: Invalid contract class"? It indicates a version mismatch between Scarb's compiler and Starkli. Refer to the earlier steps to sync the versions. Typically, Starkli supports compiler versions approved by mainnet, even if the most recent Scarb version isn't compatible. @@ -144,7 +144,7 @@ unique hash serves as the identifier for your contract class within Starknet. For example: ```bash -Class hash declared: 0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 +Class hash declared: 0x01df629957ef5568db6dab46618947063e7a28dad5e1ffbac97ba643d3c861cc ``` Consider this hash as the contract class's _address_. @@ -153,7 +153,7 @@ If you try to declare an already existing contract class, don't fret. Just proce ```bash Not declaring class as its already declared. Class hash: -0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 +0x01df629957ef5568db6dab46618947063e7a28dad5e1ffbac97ba643d3c861cc ``` ## Deploying Starknet Smart Contracts @@ -193,17 +193,18 @@ Now deploy using a class hash and constructor input: ```bash starkli deploy \ - 0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 \ + 0x01df629957ef5568db6dab46618947063e7a28dad5e1ffbac97ba643d3c861cc \ 0x737461726b6e6574626f6f6b ``` After running, expect an output similar to: ```bash - Deploying class 0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 with salt 0x054645c0d1e766ddd927b3bde150c0a3dc0081af7fb82160c1582e05f6018794... - The contract will be deployed at address 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44 - Contract deployment transaction: 0x00413d9638fecb75eb07593b5c76d13a68e4af7962c368c5c2e810e7a310d54c - Contract deployed: 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44 + Deploying class 0x01df629957ef5568db6dab46618947063e7a28dad5e1ffbac97ba643d3c861cc with salt 0x07976cd9b291256305e866ed7fcce7dec2930d939ea8b62ae6da7f5c6d344306... + The contract will be deployed at address 0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6 + Contract deployment transaction: 0x01a699a18c1586f72f467bb8a144d74d646e90c3c9ff88bc58bcf8cf831c2ad5 + Contract deployed: + 0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6 ``` ## Interacting with Starknet Contracts @@ -220,12 +221,11 @@ requires no arguments: ```bash starkli call \ - \ + 0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6 \ get_name ``` -Replace `` with the address of your contract. The -command will return the owner’s address, which was initially set during +The command will return the owner’s address, which was initially set during the contract’s deployment: ```bash @@ -252,14 +252,14 @@ To alter the contract's state, use the `invoke` command. For instance, if you wa ```bash starkli invoke \ - \ + 0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6 \ set_name \ ``` Where: -- **``** is the address of your contract. +- **`0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6`** is the address of your contract (from output of `starkli deploy`). - **``** is the new value for the **`name`** field, in felt252 format. For example, to update the name to "Omar", first convert the string "Omar" to its felt252 representation: @@ -277,7 +277,7 @@ This will return: Now, proceed with the `invoke` command: ```bash - starkli invoke 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44 set_name 0x4f6d6172 + starkli invoke 0x0111595c7be27d2096ecfb49eee217d562303562cbfbfdefcc6f84936967e8f6 set_name 0x4f6d6172 ``` Bravo! You've adeptly modified and interfaced with your Starknet contract. diff --git a/src/ch02-03-scarb.md b/src/ch02-03-scarb.md index 9ce9dcea..99defe86 100644 --- a/src/ch02-03-scarb.md +++ b/src/ch02-03-scarb.md @@ -71,13 +71,15 @@ we can use, and a `Scarb.toml` file in the top-level directory. Within the `Scarb.toml` file, you might have: +```toml [package] name = "my_package" version = "0.1.0" [dependencies] - starknet = ">=2.0.1" + starknet = ">=2.6.3" snips = { path = "snips" } +``` Here starknet and snips are the dependencies of the package. The `starknet` dependency is hosted on the Scarb registry (we do not need to @@ -103,6 +105,7 @@ inside, and initialize a new Git repository with a `.gitignore` file. Upon opening `Scarb.toml` in a text editor, you should see something similar to the code snippet below: +```toml [package] name = "hello_scarb" version = "0.1.0" @@ -110,22 +113,27 @@ similar to the code snippet below: # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html [dependencies] # foo = { path = "vendor/foo" } +``` # Building a Scarb Project Clear all content in `src/lib.cairo` and replace with the following: - // src/lib.cairo - mod hello_scarb; + +```rust,noplayground +// src/lib.cairo +mod hello_scarb; +``` Next, create a new file titled `src/hello_scarb.cairo` and add the following: - // src/hello_scarb.cairo - use debug::PrintTrait; - fn main() { - 'Hello, Scarb!'.print(); - } +```rust,noplayground +// src/hello_scarb.cairo +fn main() { + println!("Hello, Scarb!"); +} +``` In this instance, the `lib.cairo` file contains a module declaration referencing _hello_scarb_, which includes the _hello_scarb.cairo_ @@ -178,7 +186,7 @@ After adding the dependency, remember to save the file. Alternatively, you can use the `scarb add` command to add dependencies to your project. Open your terminal and execute the following command: - $ scarb add alexandria_math --git https://github.com/keep-starknet-strange/alexandria.git + scarb add alexandria_math --git https://github.com/keep-starknet-strange/alexandria.git This command will add the alexandria_math dependency from the specified Git repository to your project. @@ -197,12 +205,15 @@ For example, let’s assume you have added the alexandria_math dependency. Now, you can import and utilize functions from the alexandria_math library in your `src/hello_scarb.cairo` file: - // src/hello_scarb.cairo - use alexandria_math::fibonacci; - fn main() -> felt252 { - fibonacci::fib(0, 1, 10) - } +```rust,noplayground +// src/hello_scarb.cairo +use alexandria_math::fibonacci; + +fn main() -> felt252 { + fibonacci::fib(0, 1, 10) +} +``` In the above example, we import the fibonacci function from the alexandria_math library and utilize it in the main function. @@ -235,11 +246,11 @@ commands: One of the most important features since `scarb 2.3.0` version is `Components`. Think of components as Lego blocks. They allow you to enrich your contracts by plugging in a module that you or someone else wrote. -Lets see and example. Recover our project from [Testnet Deployment](./ch02-05-testnet-deployment.md) section. We used the `Ownable-Starknet` example to interact with the blockchain, now we are going to use the same project, but we will refactor the code in order to use `components` +Lets see and example. Recover our project from [Getting Started](./ch01-00-getting-started.html#cairo-example-contract) section. We used the `Ownable-Starknet` example to interact with the blockchain, now we are going to use the same project, but we will refactor the code in order to use `components` This is how our smart contract looks now -```rust +```rust,noplayground // ...rest of the code #[starknet::component] diff --git a/src/ch02-04-katana.md b/src/ch02-04-katana.md index 45212ad7..26f8ca44 100644 --- a/src/ch02-04-katana.md +++ b/src/ch02-04-katana.md @@ -14,7 +14,7 @@ subchapter. The `starknet-devnet` is a public testnet, maintained by the these tools offer an effective environment for development and testing. For an example of how to use `katana` to deploy and interact with a -contract, see the introduction subchapter of this Chapter or a voting contract example in [The Cairo Book](https://book.cairo-lang.org/ch99-01-04-01-voting-contract.html). +contract, see the introduction subchapter of this Chapter or a voting contract example in [The Cairo Book](https://book.cairo-lang.org/ch16-06-01-deploying-and-interacting-with-a-voting-contract.html). ## Understanding RPC in Starknet @@ -33,20 +33,7 @@ used. ## Getting Started with Katana -To install Katana, use the `dojoup` installer from the command line: - -```bash -curl -L https://install.dojoengine.org | bash -dojoup -``` - -After restarting your terminal, verify the installation with: - -```bash -katana --version -``` - -To upgrade Katana, rerun the installation command. +To install Katana refer to [this chapter](/ch02-01-basic-installation.html#katana-node-installation). To initialize a local Starknet node, execute the following command: diff --git a/src/ch02-05-testnet-deployment.md b/src/ch02-05-testnet-deployment.md index 9492d9af..db4632fe 100644 --- a/src/ch02-05-testnet-deployment.md +++ b/src/ch02-05-testnet-deployment.md @@ -207,8 +207,13 @@ file is a JSON file that contains the details of your smart wallet. We also have ```bash starkli account fetch --output ~/.starkli-wallets/deployer/my_account_1.json --rpc https://starknet-sepolia.public.blastapi.io/rpc/v0_7 ``` +or -Note: Here we used the Public RPC Endpoint v0.7 Starknet (Sepolia) Testnet from **Blast**. If you don't specify the rpc provider, Starkli will use Blast Sepolia endpoint anyway. +```bash + starkli account fetch --output ~/.starkli-wallets/deployer/my_account_1.json --rpc https://free-rpc.nethermind.io/sepolia-juno/rpc/v0_7 +``` + +Note: Here we used the Public RPC Endpoint v0.7 Starknet (Sepolia) Testnet from **Blast** and **Nethermind**. If you don't specify the rpc provider, Starkli will use Blast Sepolia endpoint anyway. > ⚠️ **Contract not found?** > @@ -312,8 +317,8 @@ section. ``` This creates a compiled contract in `target/dev/` as -`ownable_starknet_ownable.compiled_contract_class.json` (in Chapter 2 of the book we will learn -more details about Scarb). +`ownable_starknet_ownable.compiled_contract_class.json` ([this section](/ch02-03-scarb.html) +contains more details about Scarb). ### Declaring Your Contract @@ -343,20 +348,20 @@ unique hash serves as the identifier for your contract class within Starknet. For example: ```bash - Class hash declared: 0x04c70a75f0246e572aa2e1e1ec4fffbe95fa196c60db8d5677a5c3a3b5b6a1a8 + Class hash declared: 0x05f5a609d87c3e6d8846e5b33ecd0cb999aca9462c166cc8f59bd600d6668a7c ``` You can think of this hash as the contract class’s _address._ Use a block explorer like -[StarkScan](https://testnet.starkscan.co/class/0x04c70a75f0246e572aa2e1e1ec4fffbe95fa196c60db8d5677a5c3a3b5b6a1a8) -to verify this hash on the blockchain. +[StarkScan](https://sepolia.starkscan.co/class/0x05f5a609d87c3e6d8846e5b33ecd0cb999aca9462c166cc8f59bd600d6668a7c) +to verify this hash on the blockchain (*Note:* it may take around 5 minutes to see the declared class in a block explorer). If the contract class you’re attempting to declare already exists, it is ok we can continue. You’ll receive a message like: ```bash Not declaring class as its already declared. Class hash: - 0x04c70a75f0246e572aa2e1e1ec4fffbe95fa196c60db8d5677a5c3a3b5b6a1a8 + 0x05f5a609d87c3e6d8846e5b33ecd0cb999aca9462c166cc8f59bd600d6668a7c ``` ## Deploying Smart Contracts on Starknet @@ -387,7 +392,7 @@ can invoke the transfer_ownership function later): ```bash starkli deploy \ - 0x04c70a75f0246e572aa2e1e1ec4fffbe95fa196c60db8d5677a5c3a3b5b6a1a8 \ + 0x05f5a609d87c3e6d8846e5b33ecd0cb999aca9462c166cc8f59bd600d6668a7c \ 0x02cdAb749380950e7a7c0deFf5ea8eDD716fEb3a2952aDd4E5659655077B8510 ``` @@ -395,16 +400,17 @@ After executing the command and entering your password, you should see output like the following: ```bash - Deploying class 0x04c70a75f0246e572aa2e1e1ec4fffbe95fa196c60db8d5677a5c3a3b5b6a1a8 with salt 0x065034b27a199cbb2a5b97b78a8a6a6c6edd027c7e398b18e5c0e5c0c65246b7... - The contract will be deployed at address 0x02a83c32d4b417d3c22f665acbc10e9a1062033b9ab5b2c3358952541bc6c012 - Contract deployment transaction: 0x0743de1e233d38c4f3e9fb13f1794276f7d4bf44af9eac66e22944ad1fa85f14 + Deploying class 0x05f5a609d87c3e6d8846e5b33ecd0cb999aca9462c166cc8f59bd600d6668a7c with salt 0x029331d11dc5c9f045071465ce6a0c0a5ff0119bafd6720ad64364df88d6706c... + The contract will be deployed at address 0x065e9ffa67bf5e0cc3ccadeccf84393c3c42892f241065402683f1b1c7bea076 + Contract deployment transaction: 0x04cfddad24431373ad29352140e5733cca5137106a541ab59cbe75444b550736 Contract deployed: - 0x02a83c32d4b417d3c22f665acbc10e9a1062033b9ab5b2c3358952541bc6c012 + 0x065e9ffa67bf5e0cc3ccadeccf84393c3c42892f241065402683f1b1c7bea076 ``` The contract is now live on the Starknet testnet. You can verify its status using a block explorer like -[StarkScan](https://testnet.starkscan.co/contract/0x02a83c32d4b417d3c22f665acbc10e9a1062033b9ab5b2c3358952541bc6c012). +[StarkScan](https://sepolia.starkscan.co/contract/0x065e9ffa67bf5e0cc3ccadeccf84393c3c42892f241065402683f1b1c7bea076) +(*Note:* it may take around 5 minutes to see the deployed contract in a block explorer). On the "Read/Write Contract" tab, you’ll see the contract’s external functions. @@ -459,7 +465,7 @@ the contract: Execution was reverted; failure reason: [0x43616c6c6572206973206e6f7420746865206f776e6572]. ``` -The failure reason is encoded as a felt. o decode it, use the starkli’s +The failure reason is encoded as a felt. To decode it, use the starkli’s `parse-cairo-string` command. ```bash diff --git a/src/ch02-06-01-connection-script.md b/src/ch02-06-01-connection-script.md index bd2500c4..f61f7bb0 100644 --- a/src/ch02-06-01-connection-script.md +++ b/src/ch02-06-01-connection-script.md @@ -41,11 +41,11 @@ bash script_devnet You will see output details from the devnet. -## Goerli Testnet +## Sepolia Testnet -**Description**: This script connects to the Goerli testnet, reads the latest block number, and retrieves the transaction receipt for a specific transaction hash. +**Description**: This script connects to the Sepolia testnet, reads the latest block number, and retrieves the transaction receipt for a specific transaction hash. -For Goerli testnet interactions, create a file named `script_testnet`: +For Sepolia testnet interactions, create a file named `script_testnet`: ```bash touch script_testnet @@ -60,7 +60,7 @@ chain=$(starkli chain-id --rpc $url) echo "Connected to the Starknet testnet with chain id: $chain" block=$(starkli block-number --rpc $url) -echo "The latest block number on Goerli is: $block" +echo "The latest block number on Sepolia is: $block" echo "Input your transaction hash: " read hash @@ -74,6 +74,7 @@ Run the script: bash script_testnet ``` -You will need to input a `testnet API URL` and a `transaction hash`. Example hash: 0x2dd73eb1802aef84e8d73334ce0e5856b18df6626fe1a67bb247fcaaccaac8c. +You will need to input a `testnet API URL` and a `transaction hash`. Example +url `https://free-rpc.nethermind.io/sepolia-juno/rpc/v0_7` and hash: `0x18bfdf15e0c46cd729551988004e2ba7a8b4f64f74820105cb538074b025e4e`. These are brief examples but you get the idea. You can create custom Bash scripts to customize your interactions with Starknet. diff --git a/src/ch02-06-starkli.md b/src/ch02-06-starkli.md index 60f9247a..dc6b656a 100644 --- a/src/ch02-06-starkli.md +++ b/src/ch02-06-starkli.md @@ -2,7 +2,7 @@ [Starkli](https://book.starkli.rs/) is a Command Line Interface (CLI) tool designed for Starknet interaction, utilizing the capabilities of [starknet-rs](https://github.com/xJonathanLEI/starknet-rs). This tool simplifies querying and executing transactions on Starknet. -> **NOTE:** Before continuing with this chapter, make sure you have completed the Basic Installation subchapter of Chapter 2. This includes the installation of Starkli. +> **NOTE:** Before continuing with this chapter, make sure you have completed the [Basic Installation subchapter](/ch02-01-basic-installation.html#starkli-installation) of Chapter 2. This includes the installation of Starkli. In the next subchapter we will create a short Bash script using Starkli to query Starknet. It's just an example, however, creating your own Bash scripts to interact with Starknet would be very useful in practice. @@ -122,7 +122,7 @@ This command will return a response like: 896360 ``` -You can confirm this result by checking [Starkscan](https://testnet.starkscan.co/), where you'll find matching data. +You can confirm this result by checking [Starkscan](https://sepolia.starkscan.co/), where you'll find matching data. Starkli also streamlines the process of invoking commands. For instance, to transfer 1000 Wei of ETH to address 0x1234, first set up your environment variables: diff --git a/src/ch02-07-starknet-devnet.md b/src/ch02-07-starknet-devnet.md index 09fadb0b..2071d6d0 100644 --- a/src/ch02-07-starknet-devnet.md +++ b/src/ch02-07-starknet-devnet.md @@ -1,6 +1,6 @@ # Starknet Devnet -Starknet Devnet is a development network (devnet) implemented in Rust, similar to the Python-based [`starknet-devnet-rs`](https://0xspaceshard.github.io/starknet-devnet/docs/intro). +[Starknet Devnet](https://0xspaceshard.github.io/starknet-devnet-rs/) is a development network (devnet) implemented in Rust, similar to the Python-based starknet-devnet. ## Installation @@ -98,36 +98,7 @@ cargo run -- --dump-path ./dumps/contract_1 --seed 912753742 ##### Additional options ```shell -Options: - --accounts - Specify the number of accounts to be predeployed; [default: 10] - --account-class - Specify the class used by predeployed accounts; [default: cairo0] [possible values: cairo0, cairo1] - --account-class-custom - Specify the path to a Cairo Sierra artifact to be used by predeployed accounts; - -e, --initial-balance - Specify the initial balance in WEI of accounts to be predeployed; [default: 1000000000000000000000] - --seed - Specify the seed for randomness of accounts to be predeployed; if not provided, it is randomly generated - --host - Specify the address to listen at; [default: 127.0.0.1] - --port - Specify the port to listen at; [default: 5050] - --timeout - Specify the server timeout in seconds; [default: 120] - --gas-price - Specify the gas price in wei per gas unit; [default: 100000000000] - --chain-id - Specify the chain ID; [default: TESTNET] [possible values: MAINNET, TESTNET] - --dump-on - Specify when to dump the state of Devnet; [possible values: exit, transaction] - --dump-path - Specify the path to dump to; - -h, --help - Print help - -V, --version - Print version - +cargo run -- --help ``` > However, the main difference for the Rust version is the syntax for flags. For example, use `cargo run -- --port 5006` or `cargo run -- --dump-on exit ...` for the Rust Devnet. Other flags can be used in the standard format. diff --git a/src/ch02-08-01-deployment-script.md b/src/ch02-08-01-deployment-script.md index d0e38367..063de4b4 100644 --- a/src/ch02-08-01-deployment-script.md +++ b/src/ch02-08-01-deployment-script.md @@ -12,16 +12,15 @@ Please note that this is a basic example. You should adapt it to suit your speci ```bash # scarb --version -scarb 2.4.3 -cairo: 2.4.3 -sierra: 1.4.0 +scarb 2.6.4 +cairo: 2.6.3 +sierra: 1.5.0 # snforge --version -snforge 0.14.0 +snforge 0.24.0 # sncast --version -sncast 0.14.0 -The Rust Devnet +sncast 0.24.0 ``` #### Additional Tools diff --git a/src/ch02-08-foundry-cast.md b/src/ch02-08-foundry-cast.md index d4e8fde2..4b0f44c8 100644 --- a/src/ch02-08-foundry-cast.md +++ b/src/ch02-08-foundry-cast.md @@ -19,24 +19,23 @@ In this section, we'll delve into `sncast`. ```bash # scarb --version -scarb 2.4.3 -cairo: 2.4.3 -sierra: 1.4.0 +scarb 2.6.4 +cairo: 2.6.3 +sierra: 1.5.0 # snforge --version -snforge 0.14.0 +snforge 0.24.0 # sncast --version -sncast 0.14.0 -The Rust Devnet +sncast 0.24.0 ``` ## Step 1: Sample Smart Contract The following code sample is sourced from `starknet foundry`(You can find the source of the example [here](https://foundry-rs.github.io/starknet-foundry/testing/contracts.html)). -If yo desire to get the files you can do it from [Foundry Example Code](https://github.com/starknet-edu/starknetbook/tree/main/examples/foundry-example) +If you desire to get the files you can do it from [Foundry Example Code](https://github.com/starknet-edu/starknetbook/tree/main/examples/foundry-example) -```rust +```rust,noplayground #[starknet::interface] trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); @@ -71,13 +70,13 @@ Here are the associated tests: Take a keen look onto the imports ie -```rust +```rust,noplayground use casttest::{IHelloStarknetDispatcherTrait, IHelloStarknetDispatcher} ``` `casttest` from the above line is the name of the project as given in the `scarb.toml` file -```rust +```rust,noplayground #[cfg(test)] mod tests { use casttest::{IHelloStarknetDispatcherTrait, IHelloStarknetDispatcher}; @@ -112,9 +111,12 @@ To execute tests, follow the steps below: 1. Ensure `snforge` is listed as a dependency in your `Scarb.toml` file, positioned beneath the `starknet` dependency. Your dependencies section should appear as (make sure to use the latest version of `snforge` and `starknet`): -```txt -starknet = "2.4.1" -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.14.0" } +```toml +[dependencies] +starknet = ">=2.6.3" + +[dev-dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.24.0" } ``` 2. Run the command: @@ -209,7 +211,7 @@ Although several options can accompany the `add` command (e.g., `--name, --addre Choose an account from the **`starknet-devnet`**, for demonstration, we'll select account **`#0`**, and execute: ```sh -sncast --url http://localhost:5050/rpc account add --name account1 --address 0x5f...60ba --private-key 0xc...0acc --add-profile +sncast --url http://localhost:5050/rpc account add --name account1 --address 0x5f...60ba --private-key 0xc...0acc --add-profile account1 ``` Points to remember: @@ -230,7 +232,7 @@ Creating a new account involves a few more steps than using an existing one, but To create a new account, use (you can use `sncast account create --help` to see the available options): ```sh -sncast --url http://localhost:5050/rpc account create --name new_account --class-hash 0x19...8dd6 --add-profile +sncast --url http://localhost:5050/rpc account create --name new_account --class-hash 0x19...8dd6 --add-profile new_account ``` Wondering where the `--class-hash` comes from? It's visible in the output from the `starknet-devnet` command under the Predeclared Starknet CLI account section. For example: @@ -247,7 +249,7 @@ Class hash: 0x195c984a44ae2b8ad5d49f48c0aaa0132c42521dcfc66513530203feca48dd6 To fund the new account, replace the address in the following command with your new one: ```sh -curl -d '{"amount":8646000000000, "address":"0x6e...eadf"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:5050/mint +curl -d '{"amount":864600000000000000000000000, "address":"0x6e...eadf"}' -H "Content-Type: application/json" -X POST http://127.0.0.1:5050/mint ``` Note: The **amount** is specified in the previous command's output. @@ -263,14 +265,14 @@ A successful fund addition will return: Deploy the account to the **`starknet devnet`** local node to register it with the chain: ```sh -sncast --url http://localhost:5050/rpc account deploy --name new_account --max-fee 0x64a7168300 +sncast --url http://localhost:5050/rpc account deploy --name new_account --max-fee 0x64a71683000000 ``` A successful deployment provides a transaction hash. If it doesn't work, revisit your previous steps. 4. Setting a Default Profile -You can define a default profile for your **`sncast`** actions. To set one, edit the **`Scarb.toml`** file. To make the **`new_account`** the default profile, find the section **`[tool.sncast.new_account]`** and change it to **`[tool.sncast]`**. This means **`sncast`** will default to using this profile unless instructed otherwise. +You can define a default profile for your **`sncast`** actions. To set one, edit the **`snfoundry.toml`** file. To make the **`new_account`** the default profile, find the section **`[sncast.account1]`** and change it to **`[sncast.default]`**. This means **`sncast`** will default to using this profile unless instructed otherwise. ## Step 4: Declaring and Deploying our Contract @@ -318,7 +320,7 @@ sncast --profile account1 declare --contract-name HelloStarknet > **Note:-** that we've omitted the **`--url`** option. Why? When using **`--profile`**, as seen here with **`account1`**, it's not necessary. Remember, earlier in this guide, we discussed adding and creating new accounts. You can use either **`account1`** or **`new_account`** and achieve the desired result. -> **Hint:** You can define a default profile for sncast actions. Modify the `Scarb.toml` file to set a default. For example, to make `new_account` the default, find `[tool.sncast.new_account]` and change it to `[tool.sncast]`. Then, there's no need to specify the profile for each call, simplifying your command to: +> **Hint:** You can define a default profile for sncast actions. Modify the `snfoundry.toml` file to set a default. For example, to make `new_account` the default, find `[sncast.new_account]` and change it to `[sncast.default]`. Then, there's no need to specify the profile for each call, simplifying your command to: ```sh sncast declare --contract-name HelloStarknet @@ -354,7 +356,7 @@ transaction_hash: 0x6bdf6cfc8080336d9315f9b4df7bca5fb90135817aba4412ade6f942e9db However, you may encounter some issues, such as: -**Error: RPC url not passed nor found in Scarb.toml**. This indicates the absence of a default profile in the **`Scarb.toml`** file. To remedy this: +**Error: RPC url not passed nor found in snfoundry.toml**. This indicates the absence of a default profile in the **`snfoundry.toml`** file. To remedy this: - Add the **`--profile`** option, followed by the desired profile name, as per the ones you've established. - Alternatively, set a default profile as previously discussed in the "Declaring the Contract" section under "Hint" or as detailed in the "Adding, Creating, and Deploying Account" subsection. @@ -449,12 +451,12 @@ Execute multiple calls Usage: sncast multicall Commands: - run Execute multicall using a .toml file - new Create a template for the multicall .toml file - help Display help for subcommand(s) + run Execute a multicall from a .toml file + new Generate a template for the multicall .toml file + help Print this message or the help of the given subcommand(s) Options: - -h, --help Show help + -h, --help Print help ``` To delve deeper, initiate the `new` subcommand: diff --git a/src/ch02-13-foundry-forge.md b/src/ch02-13-foundry-forge.md index dd7a8d53..830cf518 100644 --- a/src/ch02-13-foundry-forge.md +++ b/src/ch02-13-foundry-forge.md @@ -37,7 +37,7 @@ The project structure is as follows: Ensure the CASM and SIERRA code generation is active in the `Scarb.toml` file: -```shell +```toml # ... [[target.starknet-contract]] casm = true @@ -61,7 +61,7 @@ Follow the instructions and then run: 3. Check your `snforge` version, run : `snforge --version` -At the time of this tutorial, we used `snforge` version `snforge 0.21.0` which is the latest at this time. +At the time of this tutorial, we used `snforge` version `snforge 0.24.0` which is the latest at this time. ### Test @@ -70,11 +70,11 @@ Run tests using `snforge test`: ```shell snforge test -Collected 2 test(s) from testing package +Collected 2 test(s) from project_name package Running 0 test(s) from src/ Running 2 test(s) from tests/ -[PASS] tests::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~1839) -[PASS] tests::test_contract::test_increase_balance (gas: ~3065) +[PASS] tests::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~104) +[PASS] tests::test_contract::test_increase_balance (gas: ~170) Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ``` @@ -82,10 +82,10 @@ Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out For those with an established Scarb project who wish to incorporate `snforge`, ensure the `snforge_std` package is declared as a dependency. Insert the line below in the [dependencies] section of your `Scarb.toml`: -```shell +```toml # ... -[dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.21.0" } +[dev-dependencies] +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.24.0" } ``` Ensure the tag version corresponds with your `snforge` version. To verify your `snforge` version: @@ -97,7 +97,7 @@ snforge --version Or, add this dependency using the `scarb` command: ```shell -scarb add snforge_std --git https://github.com/foundry-rs/starknet-foundry.git --tag v0.21.0 +scarb add snforge_std --git https://github.com/foundry-rs/starknet-foundry.git --tag v0.24.0 ``` With these steps, your existing Scarb project is now **`snforge`**-ready. @@ -118,11 +118,11 @@ Sample output might resemble: ```shell -Collected 2 test(s) from testing package +Collected 2 test(s) from project_name package Running 0 test(s) from src/ Running 2 test(s) from tests/ -[PASS] tests::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~1839) -[PASS] tests::test_contract::test_increase_balance (gas: ~3065) +[PASS] tests::test_contract::test_cannot_increase_balance_with_zero_value (gas: ~104) +[PASS] tests::test_contract::test_increase_balance (gas: ~170) Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out ``` @@ -130,9 +130,9 @@ Tests: 2 passed, 0 failed, 0 skipped, 0 ignored, 0 filtered out The example provided below demonstrates how to test a Starknet contract using `snforge`. -``` +```rust,noplayground #[starknet::interface] -trait IHelloStarknet { +pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; } @@ -141,7 +141,7 @@ trait IHelloStarknet { mod HelloStarknet { #[storage] struct Storage { - balance: felt252, + balance: felt252, } #[abi(embed_v0)] @@ -156,7 +156,6 @@ mod HelloStarknet { } } } - ``` Remember, the identifier following `mod` signifies the contract name. Here, the contract name is `HelloStarknet`. @@ -165,24 +164,25 @@ Remember, the identifier following `mod` signifies the contract name. Here, the Below is a test for the **`HelloStarknet`** contract. This test deploys **`HelloStarknet`** and interacts with its functions: -``` +```rust,noplayground use starknet::ContractAddress; use snforge_std::{declare, ContractClassTrait}; -use testing::IHelloStarknetSafeDispatcher; -use testing::IHelloStarknetSafeDispatcherTrait; -use testing::IHelloStarknetDispatcher; -use testing::IHelloStarknetDispatcherTrait; +use .::IHelloStarknetSafeDispatcher; +use .::IHelloStarknetSafeDispatcherTrait; +use .::IHelloStarknetDispatcher; +use .::IHelloStarknetDispatcherTrait; -fn deploy_contract(name: felt252) -> ContractAddress { - let contract = declare(name); - contract.deploy(@ArrayTrait::new()).unwrap() +fn deploy_contract(name: ByteArray) -> ContractAddress { + let contract = declare(name).unwrap(); + let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); + contract_address } #[test] fn test_increase_balance() { - let contract_address = deploy_contract('HelloStarknet'); + let contract_address = deploy_contract("HelloStarknet"); let dispatcher = IHelloStarknetDispatcher { contract_address }; @@ -196,18 +196,17 @@ fn test_increase_balance() { } #[test] +#[feature("safe_dispatcher")] fn test_cannot_increase_balance_with_zero_value() { - let contract_address = deploy_contract('HelloStarknet'); + let contract_address = deploy_contract("HelloStarknet"); let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; - #[feature("safe_dispatcher")] let balance_before = safe_dispatcher.get_balance().unwrap(); assert(balance_before == 0, 'Invalid balance'); - #[feature("safe_dispatcher")] match safe_dispatcher.increase_balance(0) { - Result::Ok(_) => panic_with_felt252('Should have panicked'), + Result::Ok(_) => core::panic_with_felt252('Should have panicked'), Result::Err(panic_data) => { assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0)); } @@ -232,15 +231,15 @@ There are several methods to test smart contracts, such as unit tests, integrati ## ERC20 Contract Example -After setting up your foundry project, add the following dependency to your `Scarb.toml` (in this case we are using version 0.8.1 of the OpenZeppelin Cairo contracts, due to the fact that it uses components): +After setting up your foundry project, add the following dependency to your `Scarb.toml` (in this case we are using version 0.13.0 of the OpenZeppelin Cairo contracts, due to the fact that it uses components): -```shell -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.8.1" } +```toml +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.13.0" } ``` Here's a basic ERC20 contract: -``` +```rust,noplayground use starknet::ContractAddress; #[starknet::interface] trait IERC20 { @@ -492,7 +491,7 @@ This contract allows minting tokens to a recipient during deployment, checking b Organize your test file and include the required imports: -``` +```rust,noplayground #[cfg(test)] mod test { use core::serde::Serde; @@ -517,39 +516,35 @@ Get it using the declare function from [Starknet Foundry](#https://foundry-rs.gi Supply values for the constructor arguments when deploying -``` - - fn deploy_contract() -> ContractAddress { - let erc20contract_class = declare(' - ERC20Token'); - let file = FileTrait::new('data/constructor_args.txt'); - let constructor_args = read_txt(@file); - let contract_address = erc20contract_class.deploy(@constructor_args).unwrap(); - contract_address - } - +```rust,noplayground +fn deploy_contract() -> ContractAddress { + let erc20contract_class = declare(' + ERC20Token'); + let file = FileTrait::new('data/constructor_args.txt'); + let constructor_args = read_txt(@file); + let contract_address = erc20contract_class.deploy(@constructor_args).unwrap(); + contract_address +} ``` Generate an address -``` - - mod Account { - use starknet::ContractAddress; - use core::traits::TryInto; - - fn User1() -> ContractAddress { - 'user1'.try_into().unwrap() - } - fn User2() -> ContractAddress { - 'user2'.try_into().unwrap() - } +```rust,noplayground +mod Account { + use starknet::ContractAddress; + use core::traits::TryInto; - fn admin() -> ContractAddress { - 'admin'.try_into().unwrap() - } + fn User1() -> ContractAddress { + 'user1'.try_into().unwrap() + } + fn User2() -> ContractAddress { + 'user2'.try_into().unwrap() } + fn admin() -> ContractAddress { + 'admin'.try_into().unwrap() + } +} ``` Use `declare` and `ContractClassTrait` from `snforge_std`. Then, initialize the `supply` and `recipient`, declare the contract, compute the calldata, and deploy. @@ -560,182 +555,182 @@ Use `declare` and `ContractClassTrait` from `snforge_std`. Then, initialize the To begin, test the deployment helper function to confirm the details provided: -``` +```rust,noplayground #[test] - fn test_constructor() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - // let name = dispatcher.get_name(); - let name = dispatcher.get_name(); - - assert(name == 'ERC20Token', 'name is not correct'); - } - - #[test] - fn test_decimal_is_correct() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - let decimal = dispatcher.get_decimals(); +fn test_constructor() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + // let name = dispatcher.get_name(); + let name = dispatcher.get_name(); - assert(decimal == 18, 'Decimal is not correct'); - } - - #[test] - fn test_total_supply() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - let total_supply = dispatcher.get_total_supply(); + assert(name == 'ERC20Token', 'name is not correct'); +} - assert(total_supply == 1000000, 'Total supply is wrong'); - } +#[test] +fn test_decimal_is_correct() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + let decimal = dispatcher.get_decimals(); - #[test] - fn test_address_balance() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - let balance = dispatcher.get_total_supply(); - let admin_balance = dispatcher.balance_of(Account::admin()); - assert(admin_balance == balance, Errors::INVALID_BALANCE); + assert(decimal == 18, 'Decimal is not correct'); +} - start_prank(CheatTarget::One(contract_address), Account::admin()); +#[test] +fn test_total_supply() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + let total_supply = dispatcher.get_total_supply(); - dispatcher.transfer(Account::user1(), 10); - let new_admin_balance = dispatcher.balance_of(Account::admin()); - assert(new_admin_balance == balance - 10, Errors::INVALID_BALANCE); - stop_prank(CheatTarget::One(contract_address)); + assert(total_supply == 1000000, 'Total supply is wrong'); +} - let user1_balance = dispatcher.balance_of(Account::user1()); - assert(user1_balance == 10, Errors::INVALID_BALANCE); - } +#[test] +fn test_address_balance() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + let balance = dispatcher.get_total_supply(); + let admin_balance = dispatcher.balance_of(Account::admin()); + assert(admin_balance == balance, Errors::INVALID_BALANCE); + + start_prank(CheatTarget::One(contract_address), Account::admin()); + + dispatcher.transfer(Account::user1(), 10); + let new_admin_balance = dispatcher.balance_of(Account::admin()); + assert(new_admin_balance == balance - 10, Errors::INVALID_BALANCE); + stop_prank(CheatTarget::One(contract_address)); + + let user1_balance = dispatcher.balance_of(Account::user1()); + assert(user1_balance == 10, Errors::INVALID_BALANCE); +} - #[test] - #[fuzzer(runs: 22, seed: 38)] - fn test_allowance(amount: u256) { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +#[fuzzer(runs: 22, seed: 38)] +fn test_allowance(amount: u256) { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(contract_address, 20); + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(contract_address, 20); - let currentAllowance = dispatcher.allowance(Account::admin(), contract_address); + let currentAllowance = dispatcher.allowance(Account::admin(), contract_address); - assert(currentAllowance == 20, Errors::NOT_ALLOWED); - stop_prank(CheatTarget::One(contract_address)); - } + assert(currentAllowance == 20, Errors::NOT_ALLOWED); + stop_prank(CheatTarget::One(contract_address)); +} - #[test] - fn test_transfer() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +fn test_transfer() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - // Get original balances - let original_sender_balance = dispatcher.balance_of(Account::admin()); - let original_recipient_balance = dispatcher.balance_of(Account::user1()); + // Get original balances + let original_sender_balance = dispatcher.balance_of(Account::admin()); + let original_recipient_balance = dispatcher.balance_of(Account::user1()); - start_prank(CheatTarget::One(contract_address), Account::admin()); + start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.transfer(Account::user1(), 50); + dispatcher.transfer(Account::user1(), 50); - // Confirm that the funds have been sent! - assert( - dispatcher.balance_of(Account::admin()) == original_sender_balance - 50, - Errors::FUNDS_NOT_SENT - ); + // Confirm that the funds have been sent! + assert( + dispatcher.balance_of(Account::admin()) == original_sender_balance - 50, + Errors::FUNDS_NOT_SENT + ); - // Confirm that the funds have been recieved! - assert( - dispatcher.balance_of(Account::user1()) == original_recipient_balance + 50, - Errors::FUNDS_NOT_RECIEVED - ); + // Confirm that the funds have been recieved! + assert( + dispatcher.balance_of(Account::user1()) == original_recipient_balance + 50, + Errors::FUNDS_NOT_RECIEVED + ); - stop_prank(CheatTarget::One(contract_address)); - } + stop_prank(CheatTarget::One(contract_address)); +} - #[test] - fn test_transfer_from() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +fn test_transfer_from() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(Account::user1(), 20); - stop_prank(CheatTarget::One(contract_address)); + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(Account::user1(), 20); + stop_prank(CheatTarget::One(contract_address)); - assert(dispatcher.allowance(Account::admin(), Account::user1()) == 20, Errors::NOT_ALLOWED); + assert(dispatcher.allowance(Account::admin(), Account::user1()) == 20, Errors::NOT_ALLOWED); - start_prank(CheatTarget::One(contract_address), Account::user1()); - dispatcher.transfer_from(Account::admin(), Account::user2(), 10); - assert( - dispatcher.allowance(Account::admin(), Account::user1()) == 10, Errors::FUNDS_NOT_SENT - ); - stop_prank(CheatTarget::One(contract_address)); - } + start_prank(CheatTarget::One(contract_address), Account::user1()); + dispatcher.transfer_from(Account::admin(), Account::user2(), 10); + assert( + dispatcher.allowance(Account::admin(), Account::user1()) == 10, Errors::FUNDS_NOT_SENT + ); + stop_prank(CheatTarget::One(contract_address)); +} - #[test] - #[should_panic(expected: ('Amount Not Allowed',))] - fn test_transfer_from_should_fail() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(Account::user1(), 20); - stop_prank(CheatTarget::One(contract_address)); - - start_prank(CheatTarget::One(contract_address), Account::user1()); - dispatcher.transfer_from(Account::admin(), Account::user2(), 40); - } +#[test] +#[should_panic(expected: ('Amount Not Allowed',))] +fn test_transfer_from_should_fail() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(Account::user1(), 20); + stop_prank(CheatTarget::One(contract_address)); + + start_prank(CheatTarget::One(contract_address), Account::user1()); + dispatcher.transfer_from(Account::admin(), Account::user2(), 40); +} - #[test] - #[should_panic(expected: ('You have no token approved',))] - fn test_transfer_from_failed_when_not_approved() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::user1()); - dispatcher.transfer_from(Account::admin(), Account::user2(), 5); - } +#[test] +#[should_panic(expected: ('You have no token approved',))] +fn test_transfer_from_failed_when_not_approved() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; + start_prank(CheatTarget::One(contract_address), Account::user1()); + dispatcher.transfer_from(Account::admin(), Account::user2(), 5); +} - #[test] - fn test_approve() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +fn test_approve() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(Account::user1(), 50); - assert(dispatcher.allowance(Account::admin(), Account::user1()) == 50, Errors::NOT_ALLOWED); - } + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(Account::user1(), 50); + assert(dispatcher.allowance(Account::admin(), Account::user1()) == 50, Errors::NOT_ALLOWED); +} - #[test] - fn test_increase_allowance() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +fn test_increase_allowance() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(Account::user1(), 30); - assert(dispatcher.allowance(Account::admin(), Account::user1()) == 30, Errors::NOT_ALLOWED); + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(Account::user1(), 30); + assert(dispatcher.allowance(Account::admin(), Account::user1()) == 30, Errors::NOT_ALLOWED); - dispatcher.increase_allowance(Account::user1(), 20); + dispatcher.increase_allowance(Account::user1(), 20); - assert( - dispatcher.allowance(Account::admin(), Account::user1()) == 50, - Errors::ERROR_INCREASING_ALLOWANCE - ); - } + assert( + dispatcher.allowance(Account::admin(), Account::user1()) == 50, + Errors::ERROR_INCREASING_ALLOWANCE + ); +} - #[test] - fn test_decrease_allowance() { - let contract_address = deploy_contract(); - let dispatcher = IERC20Dispatcher { contract_address }; +#[test] +fn test_decrease_allowance() { + let contract_address = deploy_contract(); + let dispatcher = IERC20Dispatcher { contract_address }; - start_prank(CheatTarget::One(contract_address), Account::admin()); - dispatcher.approve(Account::user1(), 30); - assert(dispatcher.allowance(Account::admin(), Account::user1()) == 30, Errors::NOT_ALLOWED); + start_prank(CheatTarget::One(contract_address), Account::admin()); + dispatcher.approve(Account::user1(), 30); + assert(dispatcher.allowance(Account::admin(), Account::user1()) == 30, Errors::NOT_ALLOWED); - dispatcher.decrease_allowance(Account::user1(), 5); + dispatcher.decrease_allowance(Account::user1(), 5); - assert( - dispatcher.allowance(Account::admin(), Account::user1()) == 25, - Errors::ERROR_DECREASING_ALLOWANCE - ); - } + assert( + dispatcher.allowance(Account::admin(), Account::user1()) == 25, + Errors::ERROR_DECREASING_ALLOWANCE + ); +} ``` Running `snforge test` produces: diff --git a/src/img/ch01-remix-artifacts.png b/src/img/ch01-remix-artifacts.png new file mode 100644 index 00000000..44ab828f Binary files /dev/null and b/src/img/ch01-remix-artifacts.png differ diff --git a/src/img/ch01-remix-compilation.png b/src/img/ch01-remix-compilation.png new file mode 100644 index 00000000..1bab51fc Binary files /dev/null and b/src/img/ch01-remix-compilation.png differ