# Rust SDK Guide
###### tags: `FuelJourney`
## Introduction
The Rust SDK is the recommended method for testing anything built in Sway. If you're new to Rust, don't worry! This guide will take you from 0 to confidently building tests in Rust.
## Rust Basics
### Installation
If you don't already have `rustup` installed, and you're on macOS, Linux, or another Unix-like OS, you can install it with
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
You can find other installation methods [here](https://www.rust-lang.org/tools/install).
To check to see if it was installed correctly, open a new terminal and run
```shell
rustup --version
```
You should also now have `cargo`, and `rustc`, the rust compiler, installed.
You can check the version of `cargo` with
```shell
cargo --version
```
And the rust compiler with
```shell
rustc --version
```
### Syntax
The syntax for Sway is based on Rust, so if you've already written a Sway contract, you already should be familiar with most of the syntax you'll need.
### Useful Macros
(Testing Basics page from docs, covers assert and print macros)
### Results and Options
> Note: Refer to the Sway guide for how the `Result` and `Option` types work.
One difference between the `Result` and `Option` types in Rust and Sway is the `?` operator, which can be used in place of the `unwrap` method within async functions that return a `Result` or `Option`.
(from transfering assets page in docs)
```rust
let wallet_2_final_coins = wallets[1].get_coins(BASE_ASSET_ID).await?;
```
Some of the examples in this guide and the Rust SDK Docs and this guide may use the `?` operator. Note that these examples won't work if you paste them into a function that does not return a `Result` or `Option` type.
### Memory and References
With Rust you have a lot of control over how memory is used. One reason this might get confusing is not understanding Rust's "ownership" system for managing memory.
From the [Rust Book](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html), the main rules that govern ownership in Rust are:
1. Each value in Rust has an owner.
2. There can only be one owner at a time.
3. When the owner goes out of scope, the value will be dropped.
For example, if you declare a string variable called `s1`, and then create another variable called `s2` that is equal to `s1`, the value of the string is moved in memory.
If you try to print the `s1` variable, the compiler will error.
```rust
let s1 = String::from("hello");
let s2 = s1;
// this won't work
// println!("{}, world!", s1);
```
> Note: For some types that implement the `Copy` trait, this won't cause any issue as the value will be copied in memory instead of moved.
One solution to this problem is to use references, which allow you to point at a variable without moving it. References are prefaced with the `&` character.
```rust
let s1 = String::from("hello");
let s2 = &s1;
println!("{}, world!", s1);
```
You can reference something multiple times unless the reference is mutable. You can only use a mutable reference once.
```rust
let mut s1 = String::from("hello");
let s2 = &mut s1;
let s3 = &mut s1;
// this will fail
// the mutable ref moved to s3
// println!("{}, world!", s2);
```
Another solution is to use the `clone` method, which makes a copy of the value in memory.
```rust
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{}, world!", s1);
println!("{}, world!", s2);
```
## Setup
Now that you have the basics of Rust down, you can start setting up your contract tests.
The setup and testing rely on the binary files in the `out` folder created when you run `forc build` in your contract folder. Make sure you have built your current contract before moving on.
### Generate a test template
You can use a tool called `cargo generate` to generate a test template for your contract.
First, you need to install cargo generate with
```shell
cargo install cargo-generate
```
Use `cargo generate` to grab the latest Rust test from the Fuel Labs GitHub. Copy the command below and replace `<Project name>` with the name of the parent folder for your `Forc.toml` file.
```shell
cargo generate --init fuellabs/sway templates/sway-test-rs --name <Project name> --force
```
This will create a basic test harness at `tests/harness.rs` and a cargo configuration file called `Cargo.toml`.
Make sure the version for fuels is set to `0.34` **{USE CURRENT_VERSION VARIBALE?}** in your `Cargo.toml` file. If if isn't you can update it directly.
### Run your test
Run the example test with
```shell
cargo test
```
### Updating versions
If you need to update the version of the Fuels Rust SDK used to in your tests, you can change the version number in your `Cargo.toml` file and then run `cargo update`.
## Understanding the Template
### Template Structure
There are two functions in the template code in `harness.rs`.
`get_contract_instance` is a helper function that returns a contract instance and contract ID.
`can_get_contract_id` is your test function. It has the `#[tokio::test]` annotation above it, which defines the function as a test.
### Imports
The Rust SDK also has a prelude which includes a standard set of common types and methods, and a large library of specialty ones.
In your `harness.rs` file, the prelude and `ContractId` type are imported in the first line.
```rust
use fuels::{prelude::*, tx::ContractId};
```
### Loading the ABI
Next in your test, there is a line that starts with `abigen!`.
```rust
abigen!(Contract(name="MyContract", abi="out/debug/counter-contract-abi.json"));
```
`abigen!` is a macro (a special type of function in Rust) that is included with the fuels prelude that generates the types needed to interact with your contract.
### Wallets
Inside the `get_contract_instance` function, the `launch_custom_provider_and_get_wallets` method is used to generate an vector of wallets called `wallets`, which are preloaded with some coins. This method is included with the fuels prelude.
There are two main types of wallet types: unlocked and locked wallets.
You can think of an unlocked wallet as a wallet that you own and have full control over. A locked wallet would be like someone else's wallet that you can only use for read-only transactions.
The `launch_custom_provider_and_get_wallets` method returns a vector of unlocked wallets, which you can use in your tests to simulate transactions from different wallets.
The `WalletsConfig` passed in determines the number of wallets, number of coins, and amount per coin to load into each wallet.
Next, the last, and in this case only, wallet in `wallets` is removed with the `pop` method and stored as the variable `wallet`. Because the `pop` method returns an `Option` type, the `unwrap` method is also used to get the unlocked wallet.
### Locally Deploying Your Contract
`Contract::deploy` deploys your contract to a local Fuel node and returns the contract id.
You can then use `MyContract::new` to get an instance of the contract with the wallet popped from the `wallets` vector.
### `can_get_contract_id` test
The `get_contract_instance` method is used to return the contract instance and id. In Rust, unused variables *must* be prefaced with an underscore. Because the are unused here, they are named `_instance` and `_id`.
## Using Different Wallets
Function calls in your tests will by default be called with the wallet that was used to create the contract instance.
However, it would be nice if you could have a few different wallets to call your contract with to simulate different users.
Currently there is only one wallet generated in your test, and it isn't returned from the `get_contract_instance` function. Let's update this function so you have access to three wallets in our tests.
- [ ] Edit the return tuple type for the `get_contract_instance` to also return a `Vec<WalletUnlocked>`
- [ ] Add `wallets` to the returned tuple
- [ ] Find the line that has the `Single wallet` comment, and update it to create four wallets instead. (Remember that one gets removed before `wallets` is returned)
- [ ] In the `can_get_contract_id` test, add the wallets vector as a returned value `wallets`
To verify that this worked, add the line below to the end of the `can_get_contract_id` test:
```rust
println!("NUMBER OF WALLETS: {}", wallets.len());
```
Run the test with the `nocapture` flag like shown below to allow the `println` macro to print to the console during the tests.
```shell
cargo test -- --nocapture
```
You should see that the length is 3:
```shell
running 1 test
NUMBER OF WALLETS: 3
test can_get_contract_id ... ok
```
To have quick access to a wallet, you can use the `get` method to extract the first wallet in the vector.
```rust
let wallet_1 = wallets.get(0).unwrap();
```
You can use the `with_wallet` function on the contract instance to call a function from one of the wallets in the `wallets` vector.
**(example from https://fuellabs.github.io/fuels-rs/master/calling-contracts/calls-with-different-wallets.html)**
## Calling a contract
In Rust you can chain together a series of function calls like this:
```rust
fuel.network().sway().blockchain();
```
The basic structure for calling a contract functions looks like this:
```rust
let _response = instance
.methods()
.function_name(parameter)
.call()
.await;
```
To access the contract functions, you can use the `methods` function on the contract instance.
Next, you can use the particular function you want to call and pass in the function parameters.
Finally, you can use the `call` method and the `await` keyword to send the transaction to the local node and await the response.
The response type will look like this:
```rust
Result<FuelCallResponse<type>, Error>
```
**[definion of FuelCallResponse from docs]**
### Error handling
You can use the `is_ok` and `is_err` methods to check if a contract call response is ok or contains an error. These methods will return either `true` or `false`.
```rust
let is_ok = resp.is_ok();
let is_error = resp.is_err();
```
If `is_err` returns `true`, you can use the `unwrap_err` method to unwrap the error message.
```rust
if is_error {
let err = resp.unwrap_err();
println!("ERROR: {:?}", err);
};
```
### Receipts & Logs
You can get the values logged a function either from the receipts array, or with the `get_logs` or `get_logs_with_type` methods. Due to possible performance hits, it is not recommended to use `get_logs()` outside of a debugging scenario.
[example from rust docs]
[types of log receipts from docs]
## Making a New Player
The first function we will want to call is the `new_player` function.
Because the contract mints tokens for the sender of the function, the transaction will produce a variable output.
You can use the `append_variable_outputs` method on the transaction before the using `call`.
If you try to use the `call` method without appending variable outputs, the transaction will fail.
Here is how we can call the `new player` function with `wallet_1` and assert that the transaction was successful.
```rust
let response = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.new_player()
.append_variable_outputs(1)
.call()
.await;
assert!(response.is_ok());
```
### Checking a Wallet Balance
There are several methods that exist on a wallet type, including a method to check the current balance of a wallet for a given asset.
Let's use the `balance` method to make sure that tokens were minted to `wallet_1` when we called the `new_player` function.
First, use the `id` returned from the `get_contract_instance` function to create a new asset id type called `contract_asset`. We'll do this by using the `new` method in the `AssetId` type.
```rust
let contract_asset: AssetId = AssetId::new(*id);
```
Then you can pass the `contract_asset` into the `get_asset_balance` method as a reference using the `&` operator. The `get_asset_balance` method returns a `Result` type, so you can use the `unwrap` method to access the actual balance value.
```rust
let initial_balance = wallet_1.get_asset_balance(&contract_asset).await.unwrap();
```
Next, use the `assert_eq` macro to make sure that `1000000` of your contract's coins were minted to `wallet_1`.
```rust
assert_eq!(initial_balance, 1000000);
```
## Read Only Calls
**(https://fuellabs.github.io/fuels-rs/master/calling-contracts/read-only.html)**
To make sure the new player was stored, you can call the `get_player` function and check if the `farming_skill` is equal to `1`.
For this function we need to pass in an `Identity` type, but the `wallet_1` variable is the type `WalletUnlocked`. You can convert this to an `Identity` type like this
```rust
let wallet_1_id = Identity::Address(wallet_1.address().into());
```
Now you can use `wallet_1_id` to call the `get_player` function.
```rust
let player = instance
.methods()
.get_player(wallet_1_id.clone())
.call()
.await
.unwrap();
assert_eq!(player.value.farming_skill, 1);
```
## Buying Seeds
The `buy_seeds` function is a payable function, which means that in order to call the function a wallet must also send some tokens with the call.
You can use the `call_params` method to send an asset with a contract call. This method accepts a `CallParameters` struct.
To create a new `CallParameters` struct instance, you must pass in `Option` types for the amount of coins to be sent, the asset id for the coins, and the amount of gas forwarded.
```rust
let price = 750;
let amount = 5;
let call_params = CallParameters::new(Some(price * amount), Some(contract_asset.into()), None);
```
**(gas forwarded explanation from call parameters docs)**
Now we can use the call parameters in the `buy_seeds` call from `wallet_1`.
```rust
let buy_seeds_resp = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.buy_seeds(FoodType::tomatoes(), amount)
.call_params(call_params)
.call()
.await;
assert!(buy_seeds_resp.is_ok());
```
We can check the balance again here to make sure the coins were sent in the call.
```rust
let planted_balance = wallet_1.get_asset_balance(&contract_asset).await.unwrap();
assert_eq!(planted_balance, initial_balance - (amount * price));
```
We can also call `get_seed_amount` to check if the same amount of seeds are returned.
```rust
let seed_amount = instance
.methods()
.get_seed_amount(wallet_1_id.clone(), FoodType::tomatoes())
.call()
.await
.unwrap();
assert!(seed_amount.value == amount);
```
## Planting Seeds
Now that `wallet_1` has some seeds, we can have them plant them.
```rust
let plant_seeds_resp = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.plant_seeds(FoodType::tomatoes(), amount)
.call()
.await;
assert!(plant_seeds_resp.is_ok());
```
You can call the `get_planted_seeds_length` function to check if the right amount of seeds were planted.
```rust
let planted_seeds_length = instance
.methods()
.get_planted_seeds_length(wallet_1_id.clone())
.call()
.await
.unwrap();
assert_eq!(planted_seeds_length.value, amount);
```
## Harvesting Items
Now that the seeds have been planted, `wallet_1`'s player can call the `harvest` function. We can define the response as mutable with the `mut` keyword to harvest multiple times with the same response name.
```rust
let mut harvest_resp = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.harvest(0) // harvest at index 0
.append_variable_outputs(1)
.call()
.await;
assert!(harvest_resp.is_ok());
```
Make sure the `get_item_amount` function returns 1.
```rust
let item_amount = instance
.methods()
.get_item_amount(wallet_1_id.clone(), FoodType::tomatoes())
.call()
.await
.unwrap();
assert_eq!(item_amount.value, 1);
```
And harvest another item
```rust
harvest_resp = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.harvest(0)
.append_variable_outputs(1)
.call()
.await;
assert!(harvest_resp.is_ok());
```
## Selling Items
Now that `wallet_1` has harvested two items, they can sell them. Again, we'll need to use the `append_variable_outputs` method because the function transfers coins to the seller.
```rust
let sell_resp = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.sell_item(FoodType::tomatoes(), 2)
.append_variable_outputs(1)
.call()
.await;
assert!(sell_resp.is_ok());
```
## Leveling Up
Next you can use the `can_level_up` method to make sure `wallet_1`'s player has enough experience to level up.
```rust
let can_level_up = instance
.methods()
.can_level_up(wallet_1_id.clone())
.call()
.await
.unwrap();
assert_eq!(can_level_up.value, true);
```
Next, call the `level_up` function to increase the player's level.
```rust
let level_up_rep = instance
.with_wallet(wallet_1.clone())
.unwrap()
.methods()
.level_up()
.call()
.await;
assert!(level_up_rep.is_ok());
```
To make sure the `value_sold` and `farming_skill` fields have been updated correctly, you can get the player's current status again with the `get_player` function.
```rust
let player = instance
.methods()
.get_player(wallet_1_id.clone())
.call()
.await
.unwrap();
assert_eq!(player.value.total_value_sold, 15000);
assert_eq!(player.value.farming_skill, 2);
```
Finally, you can check the balance of `wallet_1` again to make sure the balance is equal to the previous balance plus the amount sold.
```rust
let final_balance = wallet_1.get_asset_balance(&contract_asset).await.unwrap();
assert_eq!(final_balance, planted_balance + 15000);
```