Compile Plugin¶
The compile plugin (hardhat-arb-compile) handles Stylus contract compilation. It discovers Rust contracts, compiles them via cargo-stylus, and generates Hardhat-compatible artifacts.
Architecture¶
flowchart TD
Task["arb:compile task"] --> Discovery["Discovery"]
Discovery --> Validation{"Host or Docker?"}
Validation -->|Host| LocalPath["Toolchain Validation"]
Validation -->|Docker| DockerPath["Image + Volumes"]
LocalPath --> TempNode1["Start Temp Node"]
DockerPath --> Network["Create Docker Network"]
Network --> TempNode2["Start Temp Node on Network"]
TempNode1 --> CompileLoop1["For each contract"]
TempNode2 --> CompileLoop2["For each contract"]
CompileLoop1 --> Check1["cargo stylus check"]
Check1 --> Build1["cargo stylus build"]
Build1 --> Artifact1["Generate Artifact"]
CompileLoop2 --> Check2["cargo stylus check"]
Check2 --> Build2["cargo stylus build"]
Build2 --> Artifact2["Generate Artifact"]
Artifact1 --> Cleanup1["Stop Node"]
Artifact2 --> Cleanup2["Stop Node + Remove Network"]
Modules¶
src/
├── index.ts # Stable entrypoint (re-exports plugin)
├── plugin/
│ ├── index.ts # Plugin definition and task registration
│ ├── type-extensions.ts # Extends StylusConfig with compile options
│ ├── hooks/
│ │ └── config.ts # Config hook
│ └── tasks/
│ └── compile.ts # Main task orchestration
├── config/ # Configuration resolution
├── services/
│ ├── compiler/
│ │ ├── host.ts # Host compilation
│ │ ├── container.ts # Docker compilation
│ │ └── types.ts # Compile result types
│ └── artifacts/
│ └── stylus-artifact.ts # Artifact generation and saving
└── utils/
└── index.ts # Public compile utility exports
Discovery¶
discoverStylusContracts() scans the contracts/ directory:
- Recursively finds directories with
Cargo.toml(skipstarget/and hidden dirs) - Parses
Cargo.tomlwithsmol-toml- checks ifstylus-sdkis in dependencies - Reads
rust-toolchain.tomlto get the required Rust version - Optionally filters by contract name (from
--contractsflag)
const contracts = await discoverStylusContracts(contractsDir, {
contracts: ['my-counter'], // optional filter
});
// Returns: [{ name: 'my-counter', path: '/abs/path', toolchain: '1.93.0' }]
Non-Stylus Cargo projects are silently skipped. Missing rust-toolchain.toml throws an error.
Compilation: Host Path¶
When --host is used:
- Validate toolchains -
validateAllToolchains()checks all requirements upfront: rustupinstalledcargo-stylusinstalled- Each required toolchain installed
-
wasm32-unknown-unknowntarget installed per toolchain -
Start temp node - Calls
arb:node startwithdetach: trueand random ports -
For each contract:
cargo +{toolchain} stylus check- Validates against the running nodecargo +{toolchain} stylus build- Compiles to WASM-
Generate artifact (ABI + WASM hex)
-
Cleanup - Stops the temporary node
// host.ts - core compile flow
await execWithProgress(`cargo +${toolchain} stylus check`, {
cwd: contractPath,
});
await execWithProgress(`cargo +${toolchain} stylus build`, {
cwd: contractPath,
});
// WASM output: target/wasm32-unknown-unknown/release/{name}.wasm
// Note: Cargo converts hyphens to underscores in the filename
Compilation: Docker Path¶
When using Docker (the default):
-
Ensure volumes - Creates
stylus-compile-rustupandstylus-compile-cargoDocker volumes for caching -
Build image -
ensureCompileImage()buildsstylus-compile:latestfromrust:slimwithcargo-stylus. Only builds once (cached). -
Create network - A temporary Docker network lets the compile container reach the node container by hostname
-
Start temp node - Same as host mode, but attached to the Docker network
-
For each contract:
- Install toolchain in container (
rustup toolchain install {version}) - cached in volume - Add
wasm32-unknown-unknowntarget - cached in volume cargo stylus check --endpoint http://{nodeContainer}:8547cargo stylus build-
Generate artifact using container for ABI export too
-
Cleanup - Stops node, removes Docker network
// Shared utility from @cobuilders/hardhat-arb-utils/stylus
await runInStylusContainer(
imageName,
contractPath,
['cargo', `+${toolchain}`, 'stylus', 'check', '--endpoint', rpcEndpoint],
{ ...options, containerPrefix: 'stylus-compile-tmp' },
);
Each runInStylusContainer call starts a temporary container with the contract directory mounted at /workspace and the cache volumes mounted.
Artifact Generation¶
After successful compilation:
- ABI export - Runs
cargo stylus export-abito get a Solidity interface - Parse ABI -
parseAbiFromSolidity()(from@cobuilders/hardhat-arb-utils/stylus) converts the Solidity interface to JSON ABI format (functions, events, parameter types) - Read WASM - Reads the compiled
.wasmfile and converts to hex - Save - Writes
artifacts/contracts/{name}/{name}.json
// Artifact format
{
_format: 'hh3-stylus-artifact-1',
contractName: 'my-counter',
sourceName: 'contracts/my-counter',
abi: [...], // Parsed from Solidity interface
bytecode: '0x...', // WASM as hex
deployedBytecode: '0x...', // Same as bytecode
linkReferences: {},
deployedLinkReferences: {}
}
Artifact generation is best-effort - if ABI export fails, the artifact still gets an empty ABI. If the entire artifact step fails, a warning is printed but compilation is considered successful.
Integration Points¶
The compile plugin depends on two sibling packages:
hardhat-arb-node- Providesarb:node startfor temporary nodes, plusgenerateTempContainerName,registerTempContainer, andcleanupTempContainerfor lifecycle managementhardhat-arb-utils- ProvidesDockerClientfor Docker operations andcreatePluginErrorfor standardized error handling