Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .github/workflows/ci.yaml

This file was deleted.

20 changes: 20 additions & 0 deletions .github/workflows/dusk_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
on:
pull_request:
push:
branches:
- main

name: Continuous integration

jobs:
code-quality:
name: Code quality
uses: dusk-network/.github/.github/workflows/run-make.yml@main
with:
make_target: cq

test:
name: Run tests
uses: dusk-network/.github/.github/workflows/run-make.yml@main
with:
make_target: test
105 changes: 105 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Forge

Smart contract development framework for the Dusk network. Provides a `#[contract]` proc-macro that generates WASM exports and contract schemas, a CLI for scaffolding and building contract projects, and integration tests exercising every macro code path.

## Workspace Layout

```
forge/
├── src/ # dusk-forge — re-exports and schema types
├── contract-macro/ # dusk-forge-contract — proc-macro (#[contract])
├── cli/ # dusk-forge-cli — CLI binary (new, build, test, schema, call, verify)
├── tests/types/ # types — helper types for integration tests
├── tests/test-contract/ # test-contract — general-purpose macro exerciser
├── contract-template/ # Template for scaffolding new contract projects
├── docs/ # Design documents
├── Makefile # Workspace-level targets
└── rust-toolchain.toml # Stable toolchain with wasm32 target
```

| Directory | Crate | Kind |
|-----------|-------|------|
| `/` (root) | `dusk-forge` | Library |
| `contract-macro/` | `dusk-forge-contract` | Proc-macro |
| `cli/` | `dusk-forge-cli` | Binary |
| `tests/types/` | `types` | Library (test helper) |
| `tests/test-contract/` | `test-contract` | Contract (integration test) |

## Commands

Run `make help` to see all available targets.

## Architecture

### `#[contract]` Macro

The `#[contract]` attribute macro (in `contract-macro/`) transforms a Rust module into a Dusk WASM smart contract:

- **Parsing** (`extract.rs`, `resolve.rs`) — extracts method signatures, trait impls, and state types from the annotated module
- **Validation** (`validate.rs`) — enforces contract rules (e.g., mutually exclusive feature gates for `contract` vs `data-driver`)
- **Code generation** (`generate.rs`) — emits `#[no_mangle]` WASM export wrappers with rkyv (de)serialization
- **Schema generation** (`data_driver.rs`) — produces a `CONTRACT_SCHEMA` constant describing the contract's ABI for data-driver builds

Trait methods with empty bodies signal the macro to use trait defaults.

### CLI

The `dusk-forge` CLI (`cli/`) provides project management commands:

- `new` — scaffold a contract project from the template
- `build` — compile contract and data-driver WASM binaries
- `test` / `check` / `clean` / `expand` — development workflow wrappers
- `schema` — extract and display the contract schema from a data-driver WASM
- `call` / `verify` — invoke contract methods and verify results via wasmtime

### Test Contract

`tests/test-contract/` is a general-purpose macro exerciser that covers every `#[contract]` code path: owned methods, borrowed methods, trait implementations, associated functions, and schema generation.

## Conventions

- **`no_std`** for contract crates. The proc-macro and CLI are `std`.
- **`--release` for tests**: integration tests in `tests/test-contract/` build WASM binaries, which require release mode.
- **Edition 2024** with MSRV 1.85 (stable toolchain).
- **Serialization**: `rkyv` for contract state and arguments, `serde` for schema JSON.
- **Feature gates**: Contracts use mutually exclusive features (`contract` vs `data-driver` / `data-driver-js`) to produce different WASM binaries from the same source.

## Change Propagation

| Changed | Also verify |
|---------|-------------|
| `dusk-forge` / `contract-macro` | `tests/test-contract`, `duskevm-contracts`, downstream contract repos |

## Git Conventions

- Default branch: `main`
- License: MPL-2.0

### Commit messages

Format: `<scope>: <Description>` — imperative mood, capitalize first word after colon.

**One commit per scope per concern.** Each commit touches one logical scope and one concern. Don't bundle unrelated changes.

Canonical scopes:

| Scope | Directory |
|-------|-----------|
| `forge` | Root crate (`src/`) |
| `macro` | `contract-macro/` |
| `cli` | `cli/` |
| `test-contract` | `tests/test-contract/` |
| `types` | `tests/types/` |
| `workspace` | Root `Cargo.toml`, Makefile, `rust-toolchain.toml` |
| `ci` | `.github/workflows/` |
| `chore` | Formatting, config files, tooling |

Examples:
- `macro: Add support for generic trait impls`
- `cli: Fix data-driver feature detection`
- `test-contract: Add borrowed-state method test`
- `workspace: Update dusk-core dependency`

### Changelog

Maintain `CHANGELOG.md` with entries under `[Unreleased]` using [keep-a-changelog](https://keepachangelog.com/) format. If a change traces to a GitHub issue, reference it as a link: `[#42](https://github.com/dusk-network/forge/issues/42)`. Only link to GitHub issues.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Move workspace to Rust edition 2024 on the stable toolchain (MSRV 1.85). Generated contract wrappers now use `#[unsafe(no_mangle)]`.
- Remove `-Z build-std=core,alloc` from contract builds (no longer needed on stable).
- Replace EVM-flavored test-bridge with a general-purpose test contract that exercises every `#[contract]` macro code path without domain-specific types.
- Make local forge path overrides opt-in for release builds and harden CLI template/path handling across platforms.

Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Workspace Makefile for dusk-forge

.PHONY: all test test-unit test-integration clippy fmt clean help
.PHONY: all test test-unit test-integration clippy cq fmt check doc clean help

all: test

Expand All @@ -14,8 +14,19 @@ test-unit: ## Run unit tests
test-integration: ## Run integration tests (test-contract)
@$(MAKE) -C tests/test-contract test

fmt: ## Format all Rust source files
@cargo fmt --all
fmt: ## Format code (requires nightly)
@rustup component add --toolchain nightly rustfmt 2>/dev/null || true
@cargo +nightly fmt --all $(if $(CHECK),-- --check,)

check: ## Run cargo check on all targets
@cargo check --all-targets

doc: ## Generate documentation
@cargo doc --no-deps

cq: ## Run code quality checks (formatting + clippy)
@$(MAKE) fmt CHECK=1
@$(MAKE) clippy

clippy: ## Run clippy on all workspace members
@echo "Running clippy..."
Expand Down
22 changes: 9 additions & 13 deletions cli/src/build_runner/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
pub mod wasm_opt;

use std::{
env,
ffi::OsStr,
path::{Path, PathBuf},
process::{Command, Stdio},
};

use crate::{
error::{CliError, Result},
project::detect,
project::metadata::ProjectMetadata,
toolchain::{self, WASM_TARGET},
};
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

use crate::error::{CliError, Result};
use crate::project::detect;
use crate::project::metadata::ProjectMetadata;
use crate::toolchain::{self, WASM_TARGET};

const CONTRACT_FEATURE: &str = "contract";
const STACK_SIZE: u32 = 65_536;
Expand Down
3 changes: 2 additions & 1 deletion cli/src/build_runner/wasm_opt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{path::Path, process::Command};
use std::path::Path;
use std::process::Command;

use crate::error::{CliError, Result};
use crate::tools;
Expand Down
11 changes: 4 additions & 7 deletions cli/src/commands/build.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use std::fs;

use crate::{
build_runner,
cli::BuildArgs,
error::Result,
project::{detect, metadata},
toolchain, ui,
};
use crate::cli::BuildArgs;
use crate::error::Result;
use crate::project::{detect, metadata};
use crate::{build_runner, toolchain, ui};

pub fn run(args: BuildArgs) -> Result<()> {
let project = metadata::load(&args.project.path)?;
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/call.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{cli::CallArgs, error::Result};

use crate::cli::CallArgs;
use crate::error::Result;
#[cfg(feature = "schema")]
use crate::{
build_runner::{self, BuildTarget},
Expand Down
10 changes: 4 additions & 6 deletions cli/src/commands/check.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::{
cli::ProjectOptions,
error::{CliError, Result},
project::{detect, metadata},
toolchain, ui,
};
use crate::cli::ProjectOptions;
use crate::error::{CliError, Result};
use crate::project::{detect, metadata};
use crate::{toolchain, ui};

pub fn run(args: ProjectOptions) -> Result<()> {
let project = metadata::load(&args.path)?;
Expand Down
10 changes: 4 additions & 6 deletions cli/src/commands/clean.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::fs;

use crate::{
cli::ProjectOptions,
error::Result,
project::{detect, metadata},
ui,
};
use crate::cli::ProjectOptions;
use crate::error::Result;
use crate::project::{detect, metadata};
use crate::ui;

pub fn run(args: ProjectOptions) -> Result<()> {
let project = metadata::load(&args.path)?;
Expand Down
6 changes: 2 additions & 4 deletions cli/src/commands/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ use std::io;
use clap::CommandFactory;
use clap_complete::generate;

use crate::{
cli::{Cli, CompletionsArgs},
error::Result,
};
use crate::cli::{Cli, CompletionsArgs};
use crate::error::Result;

pub fn run(args: CompletionsArgs) -> Result<()> {
let mut cmd = Cli::command();
Expand Down
13 changes: 5 additions & 8 deletions cli/src/commands/expand.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use std::process::{Command, Stdio};

use crate::{
build_runner,
cli::ExpandArgs,
error::{CliError, Result},
project::{detect, metadata},
toolchain::{self, WASM_TARGET},
tools, ui,
};
use crate::cli::ExpandArgs;
use crate::error::{CliError, Result};
use crate::project::{detect, metadata};
use crate::toolchain::{self, WASM_TARGET};
use crate::{build_runner, tools, ui};

pub fn run(args: ExpandArgs) -> Result<()> {
let project = metadata::load(&args.project.path)?;
Expand Down
21 changes: 9 additions & 12 deletions cli/src/commands/new.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use std::{fs, path::Path, process::Command};

use crate::{
build_runner,
cli::{NewArgs, TemplateChoice},
error::{CliError, Result},
template::{
embedded::TemplateKind,
engine::{render_template, validate_contract_name},
},
toolchain, ui,
};
use std::fs;
use std::path::Path;
use std::process::Command;

use crate::cli::{NewArgs, TemplateChoice};
use crate::error::{CliError, Result};
use crate::template::embedded::TemplateKind;
use crate::template::engine::{render_template, validate_contract_name};
use crate::{build_runner, toolchain, ui};

pub fn run(args: NewArgs) -> Result<()> {
let parsed_name = validate_contract_name(&args.name)?;
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/schema.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{cli::SchemaArgs, error::Result};

use crate::cli::SchemaArgs;
use crate::error::Result;
#[cfg(feature = "schema")]
use crate::{
build_runner::{self, BuildTarget},
Expand Down
12 changes: 5 additions & 7 deletions cli/src/commands/test.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
use std::process::{Command, Stdio};

use crate::{
build_runner::{self, BuildTarget},
cli::TestArgs,
error::{CliError, Result},
project::{detect, metadata},
toolchain, ui,
};
use crate::build_runner::{self, BuildTarget};
use crate::cli::TestArgs;
use crate::error::{CliError, Result};
use crate::project::{detect, metadata};
use crate::{toolchain, ui};

pub fn run(args: TestArgs) -> Result<()> {
let project = metadata::load(&args.project.path)?;
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/verify.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[cfg(feature = "schema")]
use std::fs;

use crate::{cli::VerifyArgs, error::Result};

use crate::cli::VerifyArgs;
use crate::error::Result;
#[cfg(feature = "schema")]
use crate::{
build_runner::{self, BuildTarget},
Expand Down
3 changes: 2 additions & 1 deletion cli/src/project/detect.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{fs, path::Path};
use std::fs;
use std::path::Path;

use toml::Value;

Expand Down
6 changes: 2 additions & 4 deletions cli/src/project/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::{
fs,
path::{Path, PathBuf},
};
use std::fs;
use std::path::{Path, PathBuf};

use cargo_metadata::{MetadataCommand, Package};

Expand Down
Loading