Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Contract macro and tooling for Dusk Network WASM smart contracts.
The #[contract] macro eliminates boilerplate by automatically generating WASM exports, schemas, and data-driver implementations from a single annotated contract module.
Quick Start
1. Create a New Contract
Copy the contract-template directory and rename it:
Update Cargo.toml:
- Replace
YOUR_CONTRACT_NAMEwith your contract name (e.g.,my-contract) - Add any additional dependencies your contract needs
2. Write Your Contract
Edit src/lib.rs:
extern crate alloc;
3. Build the Contract
The template includes a Makefile that handles building, optimization, and testing:
The contract WASM will be at target/contract/wasm32-unknown-unknown/release/my_contract.wasm
Note: The template enables
overflow-checks = truein release builds. This is critical for contract security - never disable it.
Contract Structure
The #[contract] macro expects:
| Element | Requirement |
|---|---|
| Module | Annotated with #[dusk_forge::contract] |
| Struct | Single public struct (the contract state) |
| Constructor | pub const fn new() -> Self |
| Methods | pub fn methods become contract functions |
Method Visibility
Parameter Handling
| Signature | Input Type | Output Type |
|---|---|---|
fn get(&self) -> u64 |
() |
u64 |
fn set(&mut self, v: u64) |
u64 |
() |
fn transfer(&mut self, to: Address, amount: u64) |
(Address, u64) |
() |
Multiple parameters are automatically tupled.
Events
Emit events using abi::emit:
use abi;
Events are automatically detected and included in the contract schema.
Trait Implementations
Expose trait methods using the expose attribute:
- Only methods listed in
exposebecome contract functions - Empty method bodies signal the macro to use the trait's default implementation
- Methods with actual implementations use your code
Streaming Functions
For functions that stream data via abi::feed():
/// Streams all pending items to the host.
The feeds attribute tells the data-driver what type to decode.
Data-Driver
The data-driver is a separate WASM build that provides JSON encoding/decoding for external tools (wallets, explorers, etc.).
Building the Data-Driver
The data-driver WASM will be at target/data-driver/wasm32-unknown-unknown/release/my_contract.wasm
Data-Driver WASM Exports
The data-driver WASM exports these functions:
| Export | Description |
|---|---|
init |
Initialize the driver (call once at startup) |
get_schema |
Returns the contract schema as JSON |
encode_input_fn |
Encodes JSON input for a contract function call |
decode_output_fn |
Decodes rkyv output to JSON |
decode_event |
Decodes rkyv event data to JSON |
For JavaScript integration, use w3sper which provides a high-level API for working with data-drivers.
Custom Serialization
For types requiring custom encoding/decoding:
Contract Schema
The macro generates a CONTRACT_SCHEMA constant with metadata:
// Access the schema
let schema_json = CONTRACT_SCHEMA.to_json;
The schema includes:
- Contract name
- All public functions with their input/output types
- Doc comments
- Events with topics and data types
- Import paths for type resolution
Cargo.toml Configuration
Contracts have two build targets from the same source:
- Contract WASM - Runs on-chain in the Dusk VM
- Data-driver WASM - Runs off-chain for JSON encoding/decoding
Dependencies
All runtime dependencies go in the WASM-only section because contracts are gated by #![cfg(target_family = "wasm")]:
[]
= "1.4"
= { = "0.3", = true } # Only for data-driver
= "0.1"
[]
= "1.4" # Same types, but for host-side tests
= "0.1" # To run contract in tests
Features
[]
# Contract WASM - uses custom allocator for on-chain execution
= ["dusk-core/abi-dlmalloc"]
# Data-driver WASM - enable serde for JSON serialization
= [
"dusk-core/serde",
"dep:dusk-data-driver",
"dusk-data-driver/wasm-export",
]
# Data-driver with memory exports for JavaScript
= ["data-driver", "dusk-data-driver/alloc"]
The contract and data-driver features are mutually exclusive - never enable both at the same time. The Makefile handles this by explicitly selecting one feature per build target.
Adding Dependencies
| Dependency Type | Where to Add | Feature Flags |
|---|---|---|
| Both builds | WASM-only section | None needed |
| Contract-only | WASM-only section with optional = true |
Add dep:name to contract feature |
| Data-driver-only | WASM-only section with optional = true |
Add dep:name to data-driver feature |
If a dependency has types used in function signatures, also add name/serde to the data-driver feature to enable JSON serialization.
Overflow Checks
Always enable overflow checks for contract safety:
[]
= true
This prevents integer overflow vulnerabilities. The contract template includes this by default - never remove it.
Makefile Targets
The contract template includes a Makefile with the following targets:
| Target | Description |
|---|---|
make wasm |
Build optimized contract WASM |
make wasm-dd |
Build optimized data-driver WASM |
make all-wasm |
Build both contract and data-driver |
make test |
Build WASMs and run tests |
make clippy |
Run clippy with strict warnings |
make expand |
Show macro-expanded contract code |
make expand-dd |
Show macro-expanded data-driver code |
make clean |
Clean all build artifacts |
make help |
Show all targets and configuration |
Configuration
Override Makefile variables as needed:
Prerequisites
- Rust nightly toolchain with
wasm32-unknown-unknowntarget jq(for parsing cargo metadata)wasm-opt(optional, for smaller binaries - install via binaryen)cargo-expand(optional, formake expand- install viacargo install cargo-expand)
Project Structure
dusk-forge/
├── src/lib.rs # Re-exports the contract macro
├── contract-macro/ # Proc-macro implementation
├── contract-template/ # Template for new contracts
├── tests/test-bridge/ # Integration tests
└── docs/
└── design.md # Detailed macro internals
Development
# Run all tests
# Run clippy
# Show available commands
License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. Please see the LICENSE for further details.
The included illustration is created by Regisha Dauven and is used with exclusive permission. Redistribution, modification, or reuse of the illustration by third parties is prohibited without explicit permission from the creator.