What is Fluent?
The First Blended Execution Network
Fluent is the first blended execution network - an Ethereum L2 and framework that blends Wasm, EVM and (soon) SVM-based smart contracts into a unified execution environment.
Smart contracts from different VM targets can directly call each other on Fluent. Fluent is in public devnet and currently supports apps composed of Solidity, Vyper, and Rust contracts.
Get Started |
Developers | Technology |
Connect to the Fluent Devnet
Fluent Public Devnet
This is the second devnet version of the Fluent L2, explicitly designed for deploying and interacting with EVM and Rust-based smart contracts. Solidity and Rust contracts can be deployed independently, or call each other as part of a blended application that utilizes both types of contracts.
⚠️ Caution
Due to possible issues in devnet implementations, we can reset the network at any time to upgrade it to a more stable version.
Network Parameters
Network Parameters | Resources |
---|---|
Network Name | Fluent Developer Preview |
RPC URL | https://rpc.dev.gblend.xyz/ |
Chain ID | 20993 |
Symbol | ETH |
To quickly integrate Fluent with MetaMask, visit our Dev Homepage
Fluent Developer Preview Resources
Resources | Links |
---|---|
Faucet | https://faucet.dev.gblend.xyz/ |
RPC | https://rpc.dev.gblend.xyz/ |
Explorer | https://blockscout.dev.gblend.xyz/ |
Supported Languages
The Fluentbase SDK currently supports writing smart contracts in:
- Rust
- Solidity
- Vyper
Future iterations will introduce more language support.
Current Features and Limitations
In this version, the offering includes a basic Reth implementation, where the execution environment is substituted with Fluent's VM, rWasm. Rollup and zk functionalities are not available at this stage.
It's essential to note that significant changes might occur in the VM structure, host SDK, and execution layer in subsequent releases.
Build with the Fluentbase SDK
Deploy on the Fluent Public Devnet
⚠️ Caution
The Fluentbase SDK is in experimental development and is still a work in progress.
All bindings, methods, and naming conventions within the codebase are not standardized and may change significantly. Additionally, the codebase has not been audited or fully tested, which could lead to potential vulnerabilities or crashes.
Modules
A crate with a binary application that is used for translating Wasm apps to the Fluent rWasm VM. It’s required only for creating system precompile contracts where direct translation from Wasm to rWasm is needed.
Contains all Fluentbase modules.
Crates | Explanation |
---|---|
| A crate with a custom ABI codec for encoding/decoding input messages. This codec is optimized for random reads that are used to extract only required information from passed system context. It’s very similar to Solidity ABI encoding, but uses a more Wasm friendly binary encoding and alignment. |
| A crate with all system precompiled contracts that brings support of different execution environment (EE) compatibility, including the EVM, SVM, Wasm and all corresponding system contracts like blake2, sha256, etc. |
| Core of EE runtimes with EVM, SVM, and Wasm support including deployment logic, AOT translation and contract execution. |
| Contains EVM AOT compiler. |
| A program for creating genesis files for the Fluent L2 network with precompiled system and compatibility contracts. |
| Library for poseidon hashing. |
| The fork of revm crate, but optimized and adapted for Fluentbase SDK methods and which maps the original revm’s database objects into Fluentbase’s structures. It’s needed to execute EVM transactions inside reth. |
| A basic execution runtime of rWasm that enables Fluentbase’s host functions. |
| A basic repository for developers where they can explore all required types and methods to develop their applications. It also includes macroses, definition of entrypoint, allocator, etc. |
| Contains basic primitive types for all crates. |
| Implementation of the zktrie (sparse merkle binary trie). |
A set of e2e tests for testing EVM transition and other Wasm features.
Supported Languages
The Fluentbase SDK currently supports writing smart contracts in:
- Rust
- Solidity
- Vyper
Example Code
Fluentbase can be used to develop different types of applications; in most cases, the same interface is utilized. Here is one simple application that can be developed using Fluentbase.
#![cfg_attr(target_arch = "wasm32", no_std)] extern crate fluentbase_sdk; use fluentbase_sdk::{basic_entrypoint, SharedAPI}; #[derive(Default)] struct GREETING; impl GREETING { fn deploy<SDK: SharedAPI>(&self) { // any custom deployment logic here } fn main<SDK: SharedAPI>(&self) { // b"Hello, world": "b" here tells the compiler that the string should be treated as a byte sequence. // This is called a byte string literal. const HELLO: &[u8] = b"Hello, world"; SDK::write(HELLO.as_ptr(), HELLO.len() as u32); } } basic_entrypoint!(GREETING);
Fluentbase Operation
Fluentbase operates using Fluent's rWasm VM (reduced WebAssembly). This VM employs a 100% compatible Wasm binary representation optimized for zk operations. The instruction set is reduced, and sections are embedded inside the binary to simplify the proving process.
Limitations and Future Enhancements
As of now, Fluentbase does not support floating-point operations. However, this feature is on the roadmap for future enhancements.
If you are not redirected automatically, click here.
If you are not redirected automatically, click here.
Developer Quickstart Guides
Get started building on Fluent
Fluent allows developers to write smart contracts in Rust, Solidity, or Vyper. Each language has its unique advantages and syntax, enabling developers to choose the best fit for their projects.
This guide will walk you through creating a HelloWorld
contract in each of these three languages and deploying it to Fluent.
Get Started
To simplify the setup process, your project can be bootstrapped using Fluent’s scaffold CLI tool. This tool automates the creation of the project structure and necessary directories for smart contract development.
Install Fluent scaffold CLI tool
To install the Fluent scaffold CLI tool, simply run the following command in your terminal:
cargo install gblend
To create a project, run the following in your terminal:
gblend init
This will prompt you to choose from the available setup options:
- Solidity Smart Contract
- Vyper Smart Contract
- Rust Smart Contract
Select the option that aligns with your preferred programming language for smart contract development, and the CLI tool will set up the project structure accordingly.
Smart Contracts on Fluent Guides
Solidity |
Rust |
Vyper |
Solidity Developer Guide
Prerequisites
Before getting started, make sure to install the following:
- npm >= 19
- Fluent build tool
Install Fluent Scaffold CLI Tool
To install the Fluent scaffold CLI tool, run the following command in your terminal:
cargo install gblend
To create a project, run the following in your terminal:
gblend init
This will prompt you to choose from the available setup options. You can opt for either Hardhat, JavaScript or TypeScript; in this guide, we'll proceed with JavaScript.
Project Structure
.
├── contracts
│ ├── hello.sol (our solidity hello world smart contract)
│ └── hello-v.vy
├── hardhat.config.js (contains Fluent devnet config and plugins)
├── package.json
└── scripts
├── deploy-solidity.js (deployment script for solidity smart contract)
└── deploy-vyper.js
Getting Started
Before we interact with our helloworld
smart contract, run the below command to install all dependencies in the package.json
file.
npm install
Hardhat Configs
To first get a quick sense of Fluent's network parameters, head over to the hardhat.config.js
file in the root directory.
You will find the configuration for connecting to the Fluent Devnet.
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-vyper");
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
networks: {
fluent_devnet1: {
url: 'https://rpc.dev.gblend.xyz/',
chainId: 20993,
accounts : [
`0x${"ADD YOUR PRIVATE KEY HERE"}` ], // Replace with the private key of the deploying account
},
},
solidity: {
version: '0.8.19',
},
vyper: {
version: "0.3.0",
},
};
Within the networks
object, you can see the fluent_devnet1
configuration. This specifies the URL to connect to the Fluent Devnet, along with the chain ID and the accounts available for transactions.
ℹ️ Note
Use Fluent Faucet to request test tokens.
Next, let's explore how you can compile and deploy your first smart contract to the Fluent Devnet.
Compiling the Smart Contract
If you take a look in the contracts/
folder, you'll see hello.sol
file:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Hello {
function greeting() public pure returns (string memory) {
return "Hello, Solidity!";
}
}
To compile it, simply run:
npm run compile
Deploying the Solidity contract
In the scripts
folder is the deployment script deploy-solidity.js
:
const { ethers } = require("hardhat");
async function main() {
const [deployer] = await ethers.getSigners();
const network = await ethers.provider.getNetwork();
console.log("Deploying contract...");
console.log("Chain ID:", network.chainId);
console.log("Deployer address:", deployer.address);
console.log(
"Deployer balance:",
ethers.utils.formatEther(await deployer.getBalance()),
"ETH"
);
const ContractFactory = await ethers.getContractFactory("Hello");
const contract = await ContractFactory.deploy();
// Access the address property directly
console.log("Contract address:", contract.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
To deploy the compiled solidity smart contract, run:
npx hardhat run scripts/deploy-solidity.js --network fluent_devnet1
for output:
# Deploying contract...
# Chain ID: 20993
# Deployer address:
# Deployer balance:
# Contract address:
To view your deployed contract on Fluent, navigate to the Fluent Devnet Explorer. From there, you can input your token address to explore your deployed contract.
Rust Developer Guide
For Rust smart contracts, any no_std
compatible crate (e.g., rand
, alloc
) can be used. Standard library crates are not supported. Verified libraries can be found on crates.io.
Prerequisites
Before getting started, ensure you have the following dependencies installed on your system:
-
Clang and CMake: Install these if they are not already on your system.
-
For macOS users, LLVM can be installed via Homebrew:
brew install llvm
Install Fluent Scaffold CLI tool
To install the Fluent scaffold CLI tool, run the following command in your terminal:
cargo install gblend
To create a project, run the following in your terminal:
gblend init
After installing the Fluent build tool, you can initialize a new project and choose the Rust
option to bootstrap the starter project.
Project Structure
.
├── Cargo.toml
└── lib.rs
Getting Started
In this guide, we will be working with the lib.rs
file located in the src
folder. The structure for any Rust smart contract will be as follows
lib.rs
// 1. Set the target to wasm32 and enable no_std for compatibility. // no_std is required since WebAssembly's minimal runtime lacks support for Rust’s standard library. #![cfg_attr(target_arch = "wasm32", no_std)] // 2. Import FluentBase SDK extern crate fluentbase_sdk; use fluentbase_sdk::{basic_entrypoint, derive::Contract, SharedAPI}; // 3. Define Contract Struct with SDK: Acts as the core of the contract’s logic. #[derive(Contract)] struct GREETING<SDK> { sdk: SDK, } impl<SDK: SharedAPI> GREETING<SDK> { // 4. Deployment Logic: Placeholder for setup during deployment. fn deploy(&mut self) { // Add any custom deployment logic here } // 5. Core Contract Logic: writes "Hello, World" message to output. fn main(&mut self) { self.sdk.write("Hello, World".as_bytes()); } } // 6. Set Entry Point: Connects GREETING struct to the WASM runtime. basic_entrypoint!(GREETING);
This snippet shows how simple it is to interact with the Fluent VM: just call the SDK, initialize your contract, add the functions, and, if needed, define a custom deployment process. For an in-depth look, check this guide.
Compiling the Rust Smart Contract
Understanding the deployment process starts with comprehending the role of the Makefile. This file compiles Rust code into Wasm & rWasm and generates the necessary binaries that will be embedded in a tx for deployment.
gblend build rust -r
Executing this command compiles the code and generates a file named lib.wasm
in the bin
directory.
Deploying the Contract
To deploy contract, use gblend cli:
gblend deploy --private-key <PRIVATE_KEY> --dev lib.wasm --gas-limit 300000000
Upon successful deployment, the receipt of your deployment transaction will be displayed, confirming the smart contract deployment on Fluent using the Fluent SDK.
To view your deployed Fluent contract, navigate to the Fluent Devnet Explorer. From there, you can input your contract address to explore your deployed contract.
Vyper Developer Guide
Prerequisites
Before getting started, make sure to install the following:
- npm >= 19
- Fluent build tool
Install Fluent Scaffold CLI Tool
To install the Fluent scaffold CLI tool, run the following command in your terminal:
cargo install gblend
To create a project, run the following in your terminal:
gblend init
This will prompt you to choose from the available setup options. Choose Vyper.
You can opt for either Hardhat JavaScript or TypeScript; in this guide, we'll proceed with JavaScript.
Project Structure
.
├── contracts
│ ├── hello.sol
│ └── hello-v.vy (contains our vyper hello world smart contract)
├── hardhat.config.js (contains Fluent devnet config and plugins)
├── package.json
└── scripts
├── deploy-solidity.js
└── deploy-vyper.js (deployment script for vyper smart contract)
Getting Started
Before we interact with our helloworld
smart contract, run the below command to install all dependencies in the package. json
file.
npm install
Hardhat Configs
To first get a quick sense of Fluent's network parameters, head over to the hardhat.config.js
file in the root directory. You will find the configuration for connecting to the Fluent Devnet.
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-vyper");
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
networks: {
fluent_devnet1: {
url: 'https://rpc.dev.gblend.xyz/',
chainId: 20993,
accounts : [
`0x${"ADD YOUR PRIVATE KEY HERE"}` ], // Replace with the private key of the deploying account
},
},
solidity: {
version: '0.8.19',
},
vyper: {
version: "0.3.0",
},
};
Within the networks
object, you can see the fluent_devnet1
configuration. This specifies the URL to connect to the Fluent Devnet, along with the chain ID and the accounts available for transactions.
ℹ️ Note
Use Fluent Faucet to request test tokens.
Next, let's explore how you can compile and deploy your first smart contract to the Fluent Devnet.
Compiling the Smart Contract
If you take a look in the contracts/
folder, you'll see hello-v.v
file:
# @version ^0.3.0
# Create a string variable that can store a maximum of 100 characters
greet: public(String[100])
@external
def __init__():
self.greet = "Hello, World!"
To compile it, simply run:
npm run compile
Deploying the Vyper contract
In the scripts
folder is the deployment script deployvyper.js
:
const { ethers } = require("hardhat");
async function main() {
const [deployer] = await ethers.getSigners();
const network = await ethers.provider.getNetwork();
console.log("Deploying contract...");
console.log("Chain ID:", network.chainId);
console.log("Deployer address:", deployer.address);
console.log(
"Deployer balance:",
ethers.utils.formatEther(await deployer.getBalance()),
"ETH"
);
const ContractFactory = await ethers.getContractFactory("hello-v");
const contract = await ContractFactory.deploy();
// Access the address property directly
console.log("Contract address:", contract.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
To deploy the compiled Vyper smart contract, run:
npx hardhat run scripts/deploy-vyper.js --network fluent_devnet1
for output:
# Deploying contract...
# Chain ID: 20993
# Deployer address:
# Deployer balance:
# Contract address:
To view your deployed Fluent contract, navigate to the Fluent Devnet Explorer. From there, you can input your token address to explore your deployed contract.
Building a Blended App
Introduction
This guide provides detailed instructions on how to build a blended HelloWorld
application on Fluent. It combines a Rust smart contract to print “Hello” and a Solidity smart contract to print “World.”
This setup demonstrates:
- composability between different programming languages (Solidity and Rust)
- and interoperability between different virtual machine targets (EVM and Wasm)
within a single execution environment.
Prerequisites
Ensure you have the following installed:
- Node.js and npm
- Rust and Cargo
- Hardhat
- pnpm (install via npm:
npm install -g pnpm
)
ℹ️ Note
You can also set up your first blended app with
gblend init
Install Fluent Scaffold CLI Tool
To install the Fluent scaffold CLI tool, run the following command in your terminal:
cargo install gblend
To create a project, run the following in your terminal:
gblend init
Step 1: Start Rust Contract
1.1 Set Up the Rust Project
cargo new --lib greeting
cd greeting
1.2 Configure the Rust Project
Cargo.toml
[package]
edition = "2021"
name = "greeting"
version = "0.1.0"
[dependencies]
alloy-sol-types = {version = "0.7.4", default-features = false}
fluentbase-sdk = {git = "https://github.com/fluentlabs-xyz/fluentbase", default-features = false}
[lib]
crate-type = ["cdylib", "staticlib"] #For accessing the C lib
path = "src/lib.rs"
[profile.release]
lto = true
opt-level = 'z'
panic = "abort"
strip = true
[features]
default = []
std = [
"fluentbase-sdk/std",
]
1.3 Write the Rust Smart Contract
src/lib.rs
#![allow(unused)] #![cfg_attr(target_arch = "wasm32", no_std)] fn main() { extern crate alloc; use alloc::string::{String, ToString}; use fluentbase_sdk::{ basic_entrypoint, derive::{function_id, router, Contract}, SharedAPI, }; #[derive(Contract)] struct ROUTER<SDK> { sdk: SDK, } pub trait RouterAPI { fn greeting(&self) -> String; } #[router(mode = "solidity")] impl<SDK: SharedAPI> RouterAPI for ROUTER<SDK> { #[function_id("greeting()")] fn greeting(&self) -> String { "Hello".to_string() } } 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.
alloc
is a core library that provides heap allocation functionality.fluentbase_sdk
is 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_entrypoint
is a macro for defining the main entry point of the contract.router
andfunction_id
are macros for routing function calls and defining function signatures.Contract
Trait enabling contract functionality.SharedAPI
is 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 thegreeting
method, 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.4 Build the Wasm Project
Run:
gblend build rust -r
Step 2: Start Solidity Contract
2.1 Create Your Project Directory
mkdir typescript-wasm-project
cd typescript-wasm-project
npm init -y
2.2 Install Dependencies
npm install --save-dev typescript ts-node hardhat hardhat-deploy ethers dotenv @nomicfoundation/hardhat-toolbox @typechain/ethers-v6 @typechain/hardhat @types/node
pnpm install
npx hardhat
# Follow the prompts to create a basic Hardhat project.
2.3 Configure TypeScript and Hardhat
2.3.1 Update Hardhat Configuration
hardhat.config.ts
import { HardhatUserConfig } from "hardhat/types";
import "hardhat-deploy";
import "@nomicfoundation/hardhat-toolbox";
import "./tasks/greeting"
require("dotenv").config();
const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY || "";
const config: HardhatUserConfig = {
defaultNetwork: "dev",
networks: {
dev: {
url: "https://rpc.dev.gblend.xyz/",
accounts: [DEPLOYER_PRIVATE_KEY],
chainId : 20993,
},
},
solidity: {
version: "0.8.24",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
namedAccounts: {
deployer: {
default: 0,
},
},
};
export default config;
2.3.2 Update package.json
package.json
{
"name": "blendedapp",
"version": "1.0.0",
"description": "Blended Hello, World",
"main": "index.js",
"scripts": {
"compile": "npx hardhat compile",
"deploy": "npx hardhat deploy"
}
,
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@openzeppelin/contracts": "^5.0.2",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/node": "^20.12.12",
"dotenv": "^16.4.5",
"hardhat": "^2.22.4",
"hardhat-deploy": "^0.12.4",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
},
"dependencies": {
"ethers": "^6.12.2",
"fs": "^0.0.1-security"
}
}
2.4 Set Up Environment Variables
- Create a
.env
file:
DEPLOYER_PRIVATE_KEY=your-private-key-here
- Replace
your-private-key-here
with your actual private key.
2.5 Write the Solidity Contracts
ℹ️ Note
In this section, we'll create two Solidity smart contracts:
IFluentGreeting
GreetingWithWorld
The interface contract allows the Solidity contract to call the Rust function, demonstrating interoperability between Solidity and Rust within a single execution environment. The final contract
GreetingWithWorld
provides a composable solution that combines the outputs of both the Rust and Solidity contracts.
- Create a
contracts
directory and add the following:
2.5.1 Define the Interface
contracts/IFluentGreeting.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFluentGreeting {
function greeting() external view returns (string memory);
}
Detailed Code Explanation
Interface Definition:
The IFluentGreeting
interface declares a single function greeting()
that is external and viewable, meaning it does not modify the state of the blockchain and returns a string. This function will be implemented by another contract and is used to interact with the Rust smart contract.
Interaction with Rust Code:
The greeting
function defined in this interface matches the Rust function that returns a greeting message. The Solidity interface allows the Solidity contract to call the Rust smart contract's function.
2.5.2 Implement the Greeting Contract
contracts/GreetingWithWorld.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./IFluentGreeting.sol";
contract GreetingWithWorld {
IFluentGreeting public fluentGreetingContract;
constructor(address _fluentGreetingContractAddress) {
fluentGreetingContract = IFluentGreeting(_fluentGreetingContractAddress);
}
function getGreeting() external view returns (string memory) {
string memory greeting = fluentGreetingContract.greeting();
return string(abi.encodePacked(greeting, " World"));
}
}
Detailed Code Explanation
Import Statement: Imports the IFluentGreeting
interface defined earlier.
Contract Definition: Defines a contract GreetingWithWorld
.
State Variable: Declares a state variable fluentGreetingContract
of type IFluentGreeting
. This variable will hold the address of the deployed Rust smart contract.
Constructor:
- Takes an address
_fluentGreetingContractAddress
as a parameter. - Initializes the
fluentGreetingContract
with the provided address.
- Function
getGreeting
:- Calls the
greeting
function of thefluentGreetingContract
to get the greeting message from the Rust contract. - Concatenates the greeting message with ", World" using
abi.encodePacked
and returns the resulting string.
- Calls the
Interaction with Rust Code:
- The
GreetingWithWorld
contract interacts with the Rust smart contract by calling thegreeting
function via theIFluentGreeting
interface. - When
getGreeting
is called, it fetches the greeting message ("Hello") from the Rust contract, concatenates it with ", World", and returns the complete greeting ("Hello, World").
How Solidity and Rust Interact:
- Rust Smart Contract Deployment: The Rust smart contract is compiled to Wasm and deployed to the blockchain. It contains a function that returns the greeting "Hello".
- Solidity Interface (
IFluentGreeting
): The Solidity interface declares agreeting
function that matches the function in the Rust contract. - Solidity Implementation (
GreetingWithWorld
):- The
GreetingWithWorld
contract uses theIFluentGreeting
interface to interact with the Rust contract. - It initializes with the address of the deployed Rust contract.
- It calls the
greeting
function of the Rust contract to fetch the greeting message. - It concatenates the Rust greeting with ", World" and returns the result.
- The
Step 3: Deploy Blended Contracts
3.1 Create the Deployment Script
This deployment script is responsible for deploying both the Rust smart contract (compiled to Wasm) and the Solidity smart contract (GreetingWithWorld
).
deploy/01_deploy_contracts.ts
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { ethers } from "ethers";
import fs from "fs";
import crypto from "crypto";
import path from "path";
require("dotenv").config();
const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY || "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts, ethers, config, network } = hre;
const { deploy, save, getOrNull } = deployments;
const { deployer: deployerAddress } = await getNamedAccounts();
console.log("deployerAddress", deployerAddress);
// Deploy WASM Contract
console.log("Deploying WASM contract...");
const wasmBinaryPath = "./greeting/lib.wasm";
// @ts-ignore
const provider = new ethers.JsonRpcProvider(network.config.url);
const deployer = new ethers.Wallet(DEPLOYER_PRIVATE_KEY, provider);
const checkmateValidatorAddress = await deployWasmContract(wasmBinaryPath, deployer, provider, getOrNull, save);
//Deploy Solidity Contract
console.log("Deploying GreetingWithWorld contract...");
const fluentGreetingContractAddress = checkmateValidatorAddress;
const greetingWithWorld = await deploy("GreetingWithWorld", {
from: deployerAddress,
args: [fluentGreetingContractAddress],
log: true,
});
console.log(`GreetingWithWorld contract deployed at: ${greetingWithWorld.address}`);
};
async function deployWasmContract(
wasmBinaryPath: string,
deployer: ethers.Wallet,
provider: ethers.JsonRpcProvider,
getOrNull: any,
save: any
) {
const wasmBinary = fs.readFileSync(wasmBinaryPath);
const wasmBinaryHash = crypto.createHash("sha256").update(wasmBinary).digest("hex");
const artifactName = path.basename(wasmBinaryPath, ".wasm");
const existingDeployment = await getOrNull(artifactName);
if (existingDeployment && existingDeployment.metadata === wasmBinaryHash) {
console.log(`WASM contract bytecode has not changed. Skipping deployment.`);
console.log(`Existing contract address: ${existingDeployment.address}`);
return existingDeployment.address;
}
const gasPrice = (await provider.getFeeData()).gasPrice;
const transaction = {
data: "0x" + wasmBinary.toString("hex"),
gasLimit: 300_000_000,
gasPrice: gasPrice,
};
const tx = await deployer.sendTransaction(transaction);
const receipt = await tx.wait();
if (receipt && receipt.contractAddress) {
console.log(`WASM contract deployed at: ${receipt.contractAddress}`);
const artifact = {
abi: [],
bytecode: "0x" + wasmBinary.toString("hex"),
deployedBytecode: "0x" + wasmBinary.toString("hex"),
metadata: wasmBinaryHash,
};
const deploymentData = {
address: receipt.contractAddress,
...artifact,
};
await save(artifactName, deploymentData);
} else {
throw new Error("Failed to deploy WASM contract");
}
return receipt.contractAddress;
}
export default func;
func.tags = ["all"];
3.2 Create the Hardhat Task
tasks/get-greeting.ts
import { task } from "hardhat/config";
task("get-greeting", "Fetches the greeting from the deployed GreetingWithWorld contract")
.addParam("contract", "The address of the deployed GreetingWithWorld contract")
.setAction(async ({ contract }, hre) => {
const { ethers } = hre;
const GreetingWithWorld = await ethers.getContractAt("GreetingWithWorld", contract);
const greeting = await GreetingWithWorld.getGreeting();
console.log("Greeting:", greeting);
});
3.3 Compile and Deploy the Contracts
Run the following commands to compile and deploy your contracts:
pnpm hardhat compile
pnpm hardhat deploy
pnpm hardhat get-greeting --contract <CONTRACT_ADDRESS>
Fluent Overview
Fluent is the first blended execution network. The project encompasses a zero-knowledge virtual machine (zkVM), Layer-2 network, and development framework for building diverse blockchain-based applications on Ethereum.
Fluent’s unique value proposition lies in its ability to:
- simulate the execution environment (EE) of multiple virtual machines (VMs),
- enabling real-time composability of smart contracts pertaining to different VMs (the EVM, the SVM, Wasm, etc.),
- and written in various programming languages (e.g., Solidity, Rust, and more),
- on a shared state execution environment.
Fluent supports atomic composability between apps targeting different VMs, and “blended” apps composed of smart contracts mixed and matched between them. Interaction between the different types of contracts the network supports happens under the hood and is both atomic and happens in real-time.
The Virtual Machine |
The Layer 2 Network |
The Development Framework |
The Fluent VM
The Fluent VM is a minimally modified version of the WebAssembly (Wasm) binary instruction format designed for verifiable applications. It is a reduced version of Wasm, called rWasm, which maintains compatibility with the original Wasm instruction set yet is optimized for zero-knowledge (zk) operations. Sections of the instruction set are embedded inside the binary, enhancing the efficiency and ease of verification of Wasm programs in a zk-rollup.
rWasm features support for multiple VM targets at the execution layer. Its account and state structure are managed by specialized system contracts that represent each supported VM. Ultimately, these VMs are simulated and compile down to rWasm for execution. The design is extensible in that support for additional VMs can be added over time. It also ensures atomic and synchronous composability between smart contracts pertaining to the different VMs it supports.
The Compatibility Contracts
The Fluent VM houses verified libraries, or system contracts, known as the compatibility contracts. These Wasm-based contracts define each supported VM’s EE standards and provides an API to access these functions. They effectively act as middleware allowing contracts from each supported VM to make calls and retrieve information from one another.
The compatibility contracts are similar to Near’s Aurora implementation (but optimized for zk-operations), which simulates EVM transactions and blocks. For zk proving, the compatibility contracts are supported by an additional library, the Journaled ZK Trie (JZKT) which, found in memory/storage, makes proving the Fluent STF across standards more efficient.
The JZKT is the same as a standard zk trie with one major difference, that it is reversible. This ensures atomicity of transactions across different standards. If an operation fails as part of a multi operation transaction, all previous transactions will revert as well. Additionally, by housing this process in a unified library, the surface area of each standard is wrapped into one larger circuit, making it easier to prove.
The Fluent L2 Network
The Fluent L2 is a zk-rollup to run Wasm, EVM and SVM apps in one place. It supports blended execution of different VM targets on a shared state execution environment for real-time composability between apps from different ecosystems. The network is both EVM and SVM-compatible, maintaining ABI encodings for all contracts, and introducing no additional overhead for deploying apps in Solidity, Vyper, or Solana Rust.
Ultimately, all VMs on Fluent are simulated at the execution layer and compile down to the Fluent rWasm VM for execution. Each VM is represented by a core Wasm-based system contract (the VM’s “compatibility contract”) which defines its EE standards and provides an API to access these functions. While Fluent will initially support Wasm, EVM, and SVM-based contracts, its design is extensible, enabling support for additional VM integrations.
App Deployment Models
The Fluent L2 will support two types of apps: shared and dedicated.
-
Shared Apps: These are smart contract apps that share state on Fluent’s execution environment. Note that all shared apps on the Fluent L2 compose in real-time, even across different VM targets and programming languages (e.g. Rust and Solidity).\
-
Dedicated Apps: These apps are customizable, independent state machines that can leverage Fluent for proof aggregation and verification. Developers can customize sovereign app runtimes, modular layers (e.g. DA, sequencing) and more.
The Fluentbase Framework
The Fluentbase framework is used to deploy smart contracts on the Fluent L2 as well as blockchains and verifiable compute environments that compile to rWasm. It introduces an SDK and a proving system for the Fluent state transition function (STF). It is the basis for the Fluent L2 and can used to build:
- Blended apps: Smart contracts on the Fluent L2 written in the various programming languages that the network supports.
- Blended execution networks: blockchain-based networks (L2s, L3s, etc.) which simulates multiple VM targets on a shared execution layer.
- Arbitrary compute environments: off-chain dedicated compute environments with arbitrary logic.
Fluentbase supports integrations to build modular blockchains including DA layers (Celestia, Avail, etc.), shared sequencers (Espresso, Nodekit), and more.
Glossary
Short descriptions of Fluent specific keywords and concepts.
The Fluent VM
Fluent's rWasm (reduced WebAssembly) virtual machine (VM) is a versatile VM that simulates the execution environment (EE) of multiple distinct VMs including Wasm, the EVM, the SVM, and more.
This allows for atomically composable apps using distinct programming language(s) and standards on a shared state execution environment. Each supported EE compiles to rWasm for execution, which employs a fully compatible Wasm binary representation optimized for zero-knowledge (zk) operations.
The Fluent L2
The Fluent L2 is an Ethereum-centric zk-rollup for Wasm, EVM, and SVM based apps. It supports real-time composability between apps targeting different VMs, and “blended” apps composed of smart contracts mixed and matched between them. Interaction between the different types of contracts on the L2 happens under the hood and is both atomic and happens in real-time.
The Fluentbase Framework
The Fluentbase framework is used to deploy smart contracts on Fluent as well as blockchains and verifiable compute environments that compile to rWasm. The framework’s execution layer supports arbitrary compute and the emulation of multiple VM targets.
Fluentbase is optimized for proving efficiency and integrates with modular components (sequencers, DA layers, etc.) for the deployment of customizable blended execution networks.
Virtual Machine (VM)
A virtual machine (VM) in the context of blockchains is a sandbox environment that executes smart contracts. Examples include the Ethereum Virtual Machine (EVM) and the Solana Virtual Machine (SVM).
Execution Environment (EE)
The execution environment (EE) refers to the entire system where blockchain transactions are processed. It encompasses the state transition function (STF) of a protocol, which includes the virtual machine (VM) and additional protocol-specific checks and balances necessary for the network’s operation.
These checks may involve gas calculations, nonce verification, and balance updates to ensure the proper execution of transactions.
Blended Execution Network
A blended execution network supports real-time composability between apps targeting different VMs on a shared execution layer. It enables applications written for different VMs to interact seamlessly within the same state machine.
This allows developers to leverage the best features and tools from various VMs without fragmenting user experience. In a blended execution environment, smart contracts from different VMs share state; interoperability between these contracts is atomic and synchronous, providing a unified platform for diverse smart contract applications.
Zk-Rollup
Zk-rollups are blockchain-based execution environments that post compressed data to the same onchain data availability network as the one responsible for verifying its cryptographic proofs. Proofs are examined and validated by a "verifier," which cryptographically ensures the integrity of all transactions.
Wasm
Wasm (WebAssembly) is a low-level, portable binary format and compilation target for high-level programming languages. General-purpose programming languages, such as Rust, TypeScript, and C++ compile to Wasm.
It is efficient, secure, and flexible, enabling high-performance web applications like 3D graphics, video editing, gaming, and more. In the blockchain context, networks like Cosmos, Near, Polkadot, and Tezos leverage Wasm for execution.
Community
If you are not redirected automatically, click here.