Rust is a fast and memory-efficient language, it can power performance-critical services, 
run on embedded devices, and easily integrate with other languages.

> This tutorial helps to get started on Rootstock using Rust by performing basic operations such as sending transactions and calling contracts using Alloy crate, similiar to ethers. 

## Introduction to Alloy

[Alloy](https://github.com/alloy-rs/alloy) connects applications to blockchains, it serves as an entry point to connect with evm compatible blockchains. It is a rewrite of [ethers-rs](https://github.com/gakonst/ethers-rs) library from the ground up, with new features, high performance, etc. An example is [Foundry](https://github.com/foundry-rs/foundry), a tool written in Rust which uses Alloy as a dependency to connect with blockchains. 

For more information, see [Alloy Examples](https://github.com/alloy-rs/examples) to help you get started.

## Prerequisites

* Rust
    * Install the latest version of [Rust](https://www.rust-lang.org/tools/install). If you already have Rust installed, make sure to use the latest version or update using the `rustup` toolchain. 

## Getting Started

### Create a Rust project

Run the command below using cargo to create a starter project.

```bash
cargo new rootstock-rs
```

> Next step is to update `cargo.toml` file with dependencies explained in next section.

## Setup Alloy 

To install [Alloy](https://github.com/alloy-rs/alloy) run the following command below in the root directory of the project:

```bash
cd rootstock-rs
cargo add alloy --git https://github.com/alloy-rs/alloy
```

Find more about [Alloy setup](https://github.com/alloy-rs/alloy#readme) and the [Alloy book](https://alloy.rs/).

> Note: All the dependencies required are mentioned in the `.toml` file below. Copy and paste into the `cargo.toml` file.

```bash
[package]
name = "rootstock-alloy"
version = "0.1.0"
edition = "2021"

[dependencies]
alloy = { git = "https://github.com/alloy-rs/alloy", version = "0.1.3", default-features = true, features = ["providers", "rpc-client", "transport-http", "sol-types", "json", "contract", "rpc-types", "rpc-types-eth", "network", "signers", "signer-local"] }
eyre = "0.6.12"
futures-util = "0.3.30"
tokio = { version = "1", features = ["full"] }
```

> The types and import statements in [Alloy](https://github.com/alloy-rs/alloy) dependencies are expected to change. If you face any type related errors while running the given examples in this tutorial, its recommended to check the [Alloy](https://github.com/alloy-rs/alloy) repo and [documentation](https://alloy.rs/).

## Connect to the Rootstock node

To connect to the Rootstock node., we will require a provider setup. A [Provider](https://alloy.rs/) is an abstraction of a connection to the Rootstock network, it provides a concise, and consistent interface to standard Ethereum node functionality. 

To run this program, use `cargo run` in the root of the project:

```bash
cd rootstock-rs
cargo run
```

The response should look like this:

```bash
Finished `dev` profile [unoptimized + debuginfo] target(s) in 29.28s
    Running `target/debug/rootstock-alloy`
Hello, world!
```

Next, update the `rootstock-rs/src/main.rs` file with the program below:

:::info[Info]

Replace `API_KEY` with your RPC API Key. To get an API_KEY, see the [RPC Docs](/developers/rpc-api/rootstock/setup/). 

:::

```rs
use alloy::providers::{ Provider, ProviderBuilder };
use eyre::Result;

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // Set up the HTTP transport which is consumed by the RPC client.
    let rpc_url = "https://rpc.testnet.rootstock.io/{YOUR_APIKEY}".parse()?;

    // Create a provider with the HTTP transport using the `reqwest` crate.
    let provider = ProviderBuilder::new().on_http(rpc_url);

    // Get chain id
    let chain_id = provider.get_chain_id().await?;

    println!("chain id: {chain_id}");

    // Get latest block number.
    let latest_block = provider.get_block_number().await?;

    println!("Latest block number: {latest_block}");

    Ok(())
}
```

The response should look like this:

```bash
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.43s
    Running `target/debug/rootstock-alloy`
chain id: 31
Latest block number: 5292505
```

## Get rBTC / RIF balance

After setting up the provider, interact with Rootstock node, fetch balance of an address or call a contract. Now, copy and paste the code below.

We will do the following: 
* Codegen from ABI file to interact with the contract.
* Create an abi directory in the root of the project and put RIF token abi in rif.json file.

Run the below commands in the root folder:

```bash
mkdir abi
touch rif.json
```

Replace `rootstock-rs/abi/rif.json file` with the RIF Abi below:

```bash
[ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]
```

Update the `rootstock-rs/src/main.rs` file with this program:

```rs 
use alloy::providers::{Provider, ProviderBuilder};
use alloy::sol;
use alloy::primitives::{ address, utils::format_units  };
 
sol!(
    #[allow(missing_docs)]
    #[sol(rpc)]
    RIF,
    "abi/rif.json"
);

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // Set up the HTTP transport which is consumed by the RPC client.
    let rpc_url = "https://rpc.testnet.rootstock.io/{YOUR_APIKEY}".parse()?;

    // Create a provider with the HTTP transport using the `reqwest` crate.
    let provider = ProviderBuilder::new().on_http(rpc_url);

    // Address without 0x prefix
    let alice = address!("8F1C0185bB6276638774B9E94985d69D3CDB444a");

    let rbtc = provider.get_balance(alice).await?;

    let formatted_balance: String = format_units(rbtc, "ether")?;

    println!("Balance of alice: {formatted_balance} rbtc");

    // Using rif testnet contract address
    let contract = RIF::new("0x19f64674D8a5b4e652319F5e239EFd3bc969a1FE".parse()?, provider);


    let RIF::balanceOfReturn { balance } = contract.balanceOf(alice).call().await?;

    println!("Rif balance: {:?}", balance);

    Ok(())
}
```

:::info[Info]

Replace `API_KEY` with your RPC API Key. To get an API_KEY, see the [RPC Docs](/developers/rpc-api/rootstock/setup/). Also replace RIF Testnet contract addresses with your own address as you would be required to use a private key later.

:::

Note: Run the cargo command in the root of the project:

```bash
cd rootstock-rs
cargo run
```

You should get the following response:

```bash
Finished `dev` profile [unoptimized + debuginfo] target(s) in 3.01s
    Running `target/debug/rootstock-alloy`
Balance of alice: 0.315632721175825996 rbtc
Rif balance: 183000000000000000000
```

## Send a transaction

The following program sends tRBTC from one account to the other using `TransactionRequest` Builder. 

Running this program will require setting your `PRIVATE_KEY` env variable and then run the program using `cargo run` in root.   

```bash
cd rootstock-rs
PRIVATE_KEY=0x12... cargo run
```

Replace `PRIVATE_KEY` with your private key in the command above to run this program.

You should see the following response:

```bash
% cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s
    Running `target/debug/rootstock-alloy`
Balance of alice: 0.315632721175825996 rbtc
Rif balance: 183000000000000000000
```

Next, update the `rootstock-rs/src/main.rs` file with this program:

```rs
use alloy::{
    network::{EthereumWallet, TransactionBuilder},
    primitives::{address, U256},
    providers::{Provider, ProviderBuilder},
    rpc::types::TransactionRequest,
    signers::local::PrivateKeySigner,
};
use eyre::Result;
use std::env;

#[tokio::main]
async fn main() -> Result<()> {

    // Get private key
    let mut pk = String::new();

    match env::var("PRIVATE_KEY") {
        Ok(value) => {
            pk.push_str(value.as_str());
        },
        Err(_e) => {
            panic!("Private key not setup");
        },
    }
    
    // Set up the HTTP transport which is consumed by the RPC client.
    let rpc_url = "https://rpc.testnet.rootstock.io/{YOUR_APIKEY}".parse()?;

    let signer: PrivateKeySigner = pk.parse().unwrap();

    let wallet = EthereumWallet::from(signer);

    // Create a provider with the HTTP transport using the `reqwest` crate.
    let provider = ProviderBuilder::new()
    .wallet(wallet)
    .on_http(rpc_url);

    // Get chain id
    let chain_id = provider.get_chain_id().await?;

   // Create two users, Alice and Bob.
   // Address without 0x prefix
   let alice = address!("8F1C0185bB6276638774B9E94985d69D3CDB444a");
   let bob = address!("8F1C0185bB6276638774B9E94985d69D3CDB444a");

   let nonce = provider.get_transaction_count(alice).await?;

    // Build a transaction to send 100 wei from Alice to Bob.
    let tx = TransactionRequest::default()
        .with_from(alice)
        .with_to(bob)
        .with_chain_id(chain_id)
        .with_nonce(nonce)
        .with_value(U256::from(100))  // To see value in rbtc: 100 / 10 ** 18 RBTC
        .with_gas_price(65_164_000) // provider.estimate_gas(&tx).await? * 1.1 as u128 / 100)
        .with_gas_limit(21_000);    // Change this value if you face gas related issues

    // Send the transaction and wait for the receipt.
    let pending_tx = provider.send_transaction(tx).await?;

    println!("Pending transaction... {}", pending_tx.tx_hash());

    // Wait for the transaction to be included.
    
    let receipt = pending_tx.get_receipt().await?;

    println!(
        "Transaction included in block {}",
        receipt.block_number.expect("Failed to get block number")
    );

    // assert_eq!(receipt.from, alice);
    // assert_eq!(receipt.to, Some(bob));

    Ok(())
}
```

- ERROR: deserialization error: missing field effectiveGasPrice at line 1 column 959
    - It's expected that you will encounter a missing `effectiveGasPrice` error. 
        - Kindly ignore above error. RSKj team is familiar with this error and fix would be part of new release. This error does not block the sending of a transaction. Transaction will be mined successfully. 

## Transfer ERC20 Token

This program setups up wallet with provider and sends RIF from one account to the other. Run this program using: `cargo run`.

Update the `rootstock-rs/src/main.rs` file with this program:

```rs
use alloy::{
    network::{EthereumWallet},
    primitives::{address, U256},
    providers::{Provider, ProviderBuilder},
    signers::local::PrivateKeySigner,
};
use eyre::Result;
use std::env;
use alloy::sol;

// Codegen from ABI file to interact with the contract.
// Make a abi directory in the root of the project and put RIF token abi in rif.json file.
 
sol!(
    #[allow(missing_docs)]
    #[sol(rpc)]
    RIF,
    "abi/rif.json"
);

// See the contents of rootstock-rs/abi/rif.json file below.
/*
[ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" } ], "name": "balanceOf", "outputs": [ { "name": "balance", "type": "uint256" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [ { "name": "_to", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "transfer", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" } ]
*/

#[tokio::main]
async fn main() -> eyre::Result<()> {
    // Get private key
    let mut pk = String::new();

    match env::var("PRIVATE_KEY") {
        Ok(value) => {
            pk.push_str(value.as_str());
        }
        Err(_e) => {
            panic!("Private key not setup");
        }
    }

    // Set up the HTTP transport which is consumed by the RPC client.
    let rpc_url = "https://rpc.testnet.rootstock.io/{YOUR_APIKEY}".parse()?;

    let signer: PrivateKeySigner = pk.parse().unwrap();

    let wallet = EthereumWallet::from(signer);

    // Create a provider with the HTTP transport using the `reqwest` crate.
    let provider = ProviderBuilder::new()
        .wallet(wallet)
        .on_http(rpc_url);

    // Address without 0x prefix
    let alice = address!("8F1C0185bB6276638774B9E94985d69D3CDB444a");

    let nonce = provider.get_transaction_count(alice).await?;
    let chain_id = provider.get_chain_id().await?;

    let contract = RIF::new(
        "0x19f64674D8a5b4e652319F5e239EFd3bc969a1FE".parse()?,
        provider,
    );

    let RIF::balanceOfReturn { balance } = contract.balanceOf(alice).call().await?;

    println!("Rif balance: {:?}", balance);

    // Transfer
    let amount = U256::from(100);
    let receipt = contract
        .transfer(alice, amount)
        .chain_id(chain_id)
        .nonce(nonce)
        .gas_price(65_164_000) // gas price: provider.estimate_gas(&tx).await? * 1.1 as u128 / 100)
        .gas(25_000)  // Change this value according to tx type if you face gas related issues
        .send()
        .await?
        .get_receipt()
        .await?;

    println!("Send transaction: {}", receipt.transaction_hash);

    Ok(())
}
```

Run the below command to transfer an ERC20 Token:

```bash
cd rootstock-rs
PRIVATE_KEY=0x12... cargo run
```

> Note to replace `PRIVATE_KEY` with your private key in the command above to run this program.

For more details, see the [complete code example](https://github.com/alloy-rs/examples/blob/f223a201c72334216bef763331a8924636021e38/examples/transactions/examples/transfer_erc20.rs)

See [foundry](https://github.com/foundry-rs/foundry) codebase for more advanced usage of Rust and Alloy to interact with EVM compatible blockchains including Rootstock.

## Useful Resources

- See [Alloy reference documentation](https://github.com/alloy-rs)
- Code examples using Alloy [visit this repo](https://github.com/alloy-rs/examples)
- Alloy GitHub [repo](https://github.com/alloy-rs)