@fluent.xyz/hardhat-plugin v1.0.1
hardhat-plugin
A Hardhat plugin for seamless development of blended smart contract applications that combine Rust and Solidity contracts in the Fluent blockchain ecosystem.
- hardhat-plugin
What
This plugin simplifies the development of blended applications by:
- Enabling seamless integration between Rust and Solidity contracts
- Automatically compiling Rust contracts to WebAssembly
- Generating Hardhat-compatible artifacts
- Providing unified testing environment for both Rust and Solidity contracts
Installation
# npm
npm install @fluent.xyz/hardhat-plugin
# pnpm
pnpm add @fluent.xyz/hardhat-pluginImport the plugin in your hardhat.config.js:
require("@fluent.xyz/hardhat-plugin");Or if you are using TypeScript, in your hardhat.config.ts:
import "@fluent.xyz/hardhat-plugin";Prerequisites
- Node.js 16+
- pnpm 3+
- Rust and Cargo
- Docker (for local testing with Fluent node)
Documentation
Tasks
The plugin adds several tasks to Hardhat for working with Rust contracts:
hardhat compile and compile:rust
Compiles Rust contracts to WebAssembly and generates Hardhat artifacts. The compile task is extended to include Rust compilation in the standard Hardhat compilation flow.
npx hardhat compile # Compile full project (Solidity + Rust)
npx hardhat compile:rust # Compile only Rust contractshardhat test and test:rust
Executes Rust contract unit tests and presents results in Mocha format. The test task includes Rust tests in the standard Hardhat test suite.
npx hardhat test # Run all tests
npx hardhat test --network local # Run with local Fluent node
npx hardhat test --skip-wasm-tests # Skip Rust tests
npx hardhat test --skip-solidity-tests # Skip Solidity testsNote: The Hardhat network doesn't support WASM execution - use Fluent local node for running tests.
hardhat node:fluent
Starts a local Fluent node for development and testing.
npx hardhat node:fluent # Start local node with default configurationThe node runs in a Docker container and is preconfigured for development use. Node data (chain data, logs, and configurations) is persisted in the .local-node directory, which should be added to your .gitignore:
# .gitignore
.local-node/This ensures consistent state between restarts while keeping repository clean from local development data.
hardhat clean
Removes compilation artifacts and build directories:
- Rust
targetdirectories - Generated WASM files
- Hardhat artifacts
npx hardhat clean # Clean build artifactsAll tasks can be configured through the plugin settings in your hardhat.config.ts. See the Configuration section for details.
Configuration
The plugin can be configured through the hardhat.config.ts file. All configuration options are optional and come with sensible defaults.
Basic Configuration
import { HardhatUserConfig } from "hardhat/config";
import "@fluent.xyz/hardhat-plugin";
const config: HardhatUserConfig = {
solidity: "0.8.19",
fluent: {
// Plugin configuration goes here
}
};
export default config;Contract Discovery
The plugin offers two ways to configure contracts: automatic discovery (default) and manual configuration.
Auto-Discovery
By default, the plugin automatically discovers Rust contracts in your project - you don't need to configure anything. The following configuration is applied automatically:
fluent: {
discovery: {
enabled: true, // Enable/disable auto-discovery
paths: ['contracts', 'src'], // Directories to search
ignore: ['**/target/**', '**/node_modules/**'] // Patterns to ignore
}
}This means writing:
const config: HardhatUserConfig = {
solidity: "0.8.19",
fluent: {}
};is equivalent to explicitly configuring auto-discovery as shown above.
You can customize these settings by overriding any of the discovery options:
fluent: {
discovery: {
paths: ['contracts', 'custom-contracts'], // Add custom search paths
ignore: ['**/target/**', '**/tests/**'] // Custom ignore patterns
}
}Manual Contract Configuration
You can explicitly configure contracts instead of using auto-discovery. When contracts are manually configured, auto-discovery is automatically disabled.
fluent: {
contracts: [
{
path: "contracts/my-contract",
interface: {
path: "contracts/IMyContract.sol"
},
// Optional contract-specific settings
compile: {
debug: false, // Override global compile settings
},
test: {
timeout: 10000, // Override global test settings
}
}
]
}Global Settings
Compilation Settings
Control how your Rust contracts are compiled to WebAssembly:
fluent: {
compile: {
target: "wasm32-unknown-unknown", // Target architecture
debug: false, // Enable debug mode
options: [ // Cargo build options
"--release",
"--target=wasm32-unknown-unknown",
"--no-default-features",
"-C link-arg=-zstack-size=131072",
"-C target-feature=+bulk-memory",
"-C opt-level=z",
"-C strip=symbols"
]
}
}Test Settings
Configure how contract tests are executed:
fluent: {
test: {
command: "cargo test", // Test command
options: ["--release"], // Command options
timeout: 5000, // Test timeout in milliseconds
retries: 0 // Number of retry attempts
}
}Local Node Settings
Configure the local Fluent node for development and testing:
fluent: {
node: {
docker: {
image: "ghcr.io/fluentlabs-xyz/fluent",
tag: "latest",
pull: "if-not-present" // Docker pull policy
},
network: {
chain: "dev", // Chain configuration
dataDir: "./datadir", // Data directory
blockTime: "5sec", // Block time
port: 30305, // P2P port
httpPort: 8545 // HTTP RPC port
}
}
}Environment Variables
Set environment variables for the compilation and test processes:
fluent: {
env: {
RUST_LOG: "info"
// Add any other environment variables
}
}Configuration Inheritance
Settings are merged with the following precedence (highest to lowest):
- Contract-specific settings (
contracts[].compile,contracts[].test) - Global plugin settings (
compile,test, etc.) - Default settings
Example of inheritance:
fluent: {
// Global settings
compile: {
debug: true
},
test: {
timeout: 5000
},
// Contract-specific settings override global ones
contracts: [
{
path: "contracts/contract-a",
interface: {
path: "contracts/IContractA.sol"
},
compile: {
debug: false // Overrides global debug setting
},
test: {
timeout: 10000 // Overrides global timeout setting
}
}
]
}Default Values
The plugin comes with the following default settings:
{
compile: {
target: "wasm32-unknown-unknown",
debug: false,
options: [
"--release",
"--target=wasm32-unknown-unknown",
"--no-default-features",
"-C link-arg=-zstack-size=131072",
"-C target-feature=+bulk-memory",
"-C opt-level=z",
"-C strip=symbols",
],
},
test: {
command: "cargo test",
options: ["--release", "--test-threads=1"],
timeout: 5000,
retries: 0,
},
node: {
docker: {
image: "ghcr.io/fluentlabs-xyz/fluent",
tag: "latest",
pull: "if-not-present",
},
network: {
chain: "dev",
dataDir: "./datadir",
blockTime: "5sec",
port: 30305,
httpPort: 8545,
},
},
discovery: {
enabled: true,
paths: ["contracts", "src"],
ignore: ["**/target/**", "**/node_modules/**"],
},
env: {
RUST_LOG: "info",
},
}Project Structure
Recommended project structure:
contracts/
├── IRandomGenerator.sol # Solidity interface for Rust contract
├── Lottery.sol # Solidity contract
└── random-generator/ # Rust contract
├── Cargo.toml
└── lib.rs
test/
├── Lottery.ts # Integration tests
└── RandomGenerator.ts
hardhat.config.ts # Hardhat configurationExamples
Check out our example projects in the examples directory:
Lottery Example
A complete lottery application demonstrating:
- Integration between Rust and Solidity contracts
- Random number generation in Rust
- Contract deployment using Ignition
- Integration tests
- Local development setup
cd examples/lottery
pnpm install
pnpm hardhat compile
# In a separate terminal
pnpm hardhat node:fluent
pnpm hardhat test --network localNotes
- The Hardhat network doesn't support WASM execution - use Fluent local node
Learn More
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a Pull Request
Code of Conduct
We follow the Contributor Covenant. We expect all contributors to be respectful and inclusive in all interactions. Key points:
- Use welcoming and inclusive language
- Respect different viewpoints and experiences
- Accept constructive criticism gracefully
- Focus on what's best for the community
Release Guide
Prepare the Codebase
Ensure the code is ready for release:- Run
pnpm run lintto check for linting issues. - Run
pnpm run lint:fixto automatically fix formatting issues. - Run all tests:
pnpm run test.
- Run
Build the Project
Runpnpm run buildto compile the project.Update the Version
Choose the appropriate version bump:- For a patch release:
pnpm run version:patch - For a minor release:
pnpm run version:minor - For a major release:
pnpm run version:major
- For a patch release:
Push Changes
Push the updated code and tags to the main branch:
git push origin main --follow-tags
Automatic Deployment
GitHub Actions will handle publishing the new version upon detecting the pushed tag.
That’s it! 🎉
License
MIT License