Step 1 - Start Rust Contract
Step 1: Start Rust Contract
This guide is based off of the template blended application in this Github repo.
Make sure the gblend tool is installed to continue. Check with:
gblend --version
1.1 Project Initialisation
To start the project, you'll follow similar steps as in the Rust contract dev guide (only with different names).
# Create a project called my-blended-template
gblend init my-blended-template
You can do some housekeeping by removing default files:
rm src/BlendedCounter.sol
rm script/BlendedCounter.s.sol
rm test/BlendedCounter.t.sol
In this step of the guide we'll focus on the Rust contract, which can be found at src/power-calculator.
Rename the Rust contract folder and package:
- rename 
src/power-calculatorfolder tosrc/rust-evm-test 
mv src/power-calculator src/rust-evm-test
- rename package name in 
src/rust-evm-test/Cargo.tomlto "rust-evm-test" 
awk '
  BEGIN { in_package = 0 }
  /^\[package\]/ { in_package = 1; print; next }
  /^\[.*\]/ { in_package = 0 }
  in_package && /^name *= *".*"/ { print "name = \"rust-evm-test\""; next }
  { print }
' src/rust-evm-test/Cargo.toml > tmp.toml && mv tmp.toml src/rust-evm-test/Cargo.toml
1.2 Write the Rust Smart Contract with Fluentbase SDK
Now you'll move on to writing a Rust smart contract with Fluentbase SDK. Find the contract source file in src/rust-evm-test/src/lib.rs. This is the file you'll edit...
This contract demonstrates how to use Fluentbase SDK to enable interoperability between Solidity and Rust (WASM) contracts, allowing Solidity contracts to call Rust functions with compatible data types.
Effectively, we provide router functionality. Please also refer to the Fluentbase router macro documentation for more detailed educational materials.
In the new contract, you'll define a struct for the contract which will derive the Contract trait:
#[derive(Contract)]
struct ROUTER<SDK> {
    sdk: SDK,
}
and define a RouterAPI trait to define Solidity interfaces. The contract then needs to implement the trait and define the Solidity function implementations.
pub trait RouterAPI {
    // Define function signatures that correspond to Solidity interface you'll call from.
    ...
}
...
impl<SDK: SharedAPI> RouterAPI for ROUTER<SDK> {
    // Here you'll implement the methods defined in the trait
    ...
}
Full Contract Source Code
Paste the following source code in lib.rs and examine it closely (there a collapsible compenent with detailed explanation below the code block).
#![cfg_attr(target_arch = "wasm32", no_std)]
extern crate alloc;
use alloc::string::{String, ToString};
use fluentbase_sdk::{
    basic_entrypoint,
    derive::{router, Contract},
    SharedAPI,
    U256,    // alloy Solidity type for uint256
    I256,    // alloy Solidity type for int256
    Address, // alloy Solidity type for address
    address, // alloy Solidity marco to define values for type Address
    Bytes,   // alloy Solidity type for bytes
    B256,    // alloy Solidity type for bytes32
    b256     // alloy Solidity marco to define values for type B256
};
#[derive(Contract)]
struct ROUTER<SDK> {
    sdk: SDK,
}
pub trait RouterAPI {
    // Make sure type interfaces are defined here or else there will be a compiler error.
    fn rust_string(&self) -> String;
    fn rust_uint256(&self) -> U256;
    fn rust_int256(&self) -> I256;
    fn rust_address(&self) -> Address;
    fn rust_bytes(&self) -> Bytes;
    fn rust_bytes32(&self) -> B256;
    fn rust_bool(&self) -> bool;
}
#[router(mode = "solidity")]
impl<SDK: SharedAPI> RouterAPI for ROUTER<SDK> {
    // ERC-20 with Fluent SDK example:
    // https://github.com/fluentlabs-xyz/fluentbase/blob/devel/contracts/examples/erc20/lib.rs
    #[function_id("rustString()")]
    fn rust_string(&self) -> String {
        let string_test = "Hello".to_string();
        return string_test;
    }
    #[function_id("rustUint256()")]
    fn rust_uint256(&self) -> U256 {
        let uint256_test = U256::from(10);
        return uint256_test;
    }
    #[function_id("rustInt256()")]
    fn rust_int256(&self) -> I256 {
        // Declare Signed variables in alloy.rs:
        // https://docs.rs/alloy-primitives/latest/alloy_primitives/struct.Signed.html#method.from_dec_str
        let int256_test = I256::unchecked_from(-10);
        return int256_test;
    }
    #[function_id("rustAddress()")]
    fn rust_address(&self) -> Address {
        let address_test: Address = address!("d8da6bf26964af9d7eed9e03e53415d37aa96045"); // vitalik.eth 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
        return address_test;
    }
    
    #[function_id("rustBytes()")]
    fn rust_bytes(&self) -> Bytes {
        let bytes_test = Bytes::from("FLUENT");
        return bytes_test;
    }
    #[function_id("rustBytes32()")]
    fn rust_bytes32(&self) -> B256 {
        let bytes256_test = b256!("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
        return bytes256_test;
    }
    #[function_id("rustBool()")]
    fn rust_bool(&self) -> bool {
        let bool_test = true;
        return bool_test;
    }
}
impl<SDK: SharedAPI> ROUTER<SDK> {
    fn deploy(&self) {
        // any custom deployment logic here
    }
}
basic_entrypoint!(ROUTER);
Detailed Code Explanation
1. #![cfg_attr(target_arch = "wasm32", no_std)]
This line is a compiler directive. It specifies that if the target architecture is wasm32 (WebAssembly 32-bit), the code should be compiled without the standard library (no_std). This is necessary for WebAssembly, which doesn't have a full standard library available.
2. extern crate alloc; and extern crate fluentbase_sdk;
These lines declare external crates (libraries) that the code depends on.
allocis a core library that provides heap allocation functionality.fluentbase_sdkis the SDK provided by Fluent for writing contracts.
3. use alloc::string::{String, ToString};
This line imports the String and ToString types from the alloc crate. This is necessary because the standard std library, which normally includes these, is not available in no_std environments.
4. use fluentbase_sdk::{ basic_entrypoint, derive::{router, function_id, Contract}, SharedAPI };
This line imports various items from the fluentbase_sdk crate:
basic_entrypointis a macro for defining the main entry point of the contract.routerandfunction_idare macros for routing function calls and defining function signatures.ContractTrait enabling contract functionality.SharedAPIis a trait that abstracts the API shared between different environments.
5. #[derive(Contract)] struct ROUTER;
This line defines a struct named ROUTER and derives a contract implementation for it. The ROUTER struct will implement the logic for our contract.
6. pub trait RouterAPI { fn greeting(&self) -> String; }
This defines a trait named RouterAPI with a single method greeting. This method returns a String.
7. #[router(mode = "solidity")] impl<SDK: SharedAPI> RouterAPI for ROUTER<SDK> { ... }
This block implements the RouterAPI trait for the ROUTER struct. The #[router(mode = "solidity")] attribute indicates that this implementation is for a Solidity-compatible router.
Inside the Implementation:
#[function_id("greeting()"]specifies the function signature in Solidity syntax. This tells the router how to call this function from Solidity.fn greeting<SDK: SharedAPI>(&self) -> String { "Hello".to_string() }is the implementation of thegreetingmethod, which simply returns the string "Hello".
8. impl<SDK: SharedAPI> ROUTER<SDK> { fn deploy(&self) { // any custom deployment logic here } }
This block provides an additional method deploy for the ROUTER struct. This method can include custom deployment logic. Currently, it's an empty placeholder.
9. basic_entrypoint!(ROUTER);
This macro invocation sets up the ROUTER struct as the main entry point for the contract. It handles necessary boilerplate code for contract initialization and invocation.
Summary
This Rust code defines a smart contract that will be compiled to WebAssembly. The contract implements a single function greeting that returns the string "Hello". The contract is designed to be called from a Solidity environment, showcasing interoperability between different virtual machines. The basic_entrypoint! macro ties everything together, making ROUTER the entry point for the contract.
1.3 Build the Wasm Project
Generate the WASM binary file with:
gblend build
Note: the first build might take a few minutes and should take a few seconds after the first run.
We will deploy the compiled Rust contract with the WASM binary file later in this guide.
Note: to update Rust crate fluentbase-sdk if there are issues:
# From src/rust-evm-test/
cargo clean
cargo update -p fluentbase-sdk
Next Up
In the following step you'll switch to the Solidity part of the blended project. You've now got the Rust functions the Solidity contract can call, time to implement the calling contract.