Anatomy of a Clarinet project

Understand the complete structure and configuration of a Clarinet project


A Clarinet project follows a carefully designed structure that separates contracts, tests, and configuration. Understanding this structure helps you organize code effectively and configure tools for optimal development workflow.

Core project layout

Every Clarinet project contains these essential directories and files:

main.clar
traits.clar
Devnet.toml
Mainnet.toml
Testnet.toml
main.test.ts
.gitignore
Clarinet.toml
package.json
tsconfig.json
vitest.config.js

Each component serves a specific purpose in your development workflow. Let's explore how they work together to create a complete development environment.

The project manifest

Clarinet.toml

The Clarinet.toml file is the heart of your project. It defines project metadata and tracks all contracts:

Clarinet.toml
[project]
name = "defi-protocol"
description = "Decentralized finance protocol for Stacks"
authors = ["alice@example.com"]
telemetry = true
requirements = []
boot_contracts = ["traits", "token", "pool"]
[contracts.traits]
path = "contracts/traits.clar"
clarity_version = 3
epoch = 3.1
[contracts.token]
path = "contracts/token.clar"
clarity_version = 3
epoch = 3.1
depends_on = ["traits"]
[contracts.pool]
path = "contracts/pool.clar"
clarity_version = 3
epoch = 3.1
depends_on = ["traits", "token"]

The manifest handles several critical functions:

  • Contract registration: Every contract must be listed here
  • Dependency management: Defines deployment order through depends_on
  • Version control: Specifies Clarity version and epoch for each contract
  • Boot sequence: Lists contracts to deploy on clarinet devnet start

Testing infrastructure

Package configuration

The package.json defines your testing environment and dependencies:

package.json
{
"name": "my-clarinet-project",
"version": "1.0.0",
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:report": "vitest run --reporter=verbose"
},
"devDependencies": {
"@hirosystems/clarinet-sdk": "^2.9.0",
"@stacks/transactions": "^7.0.0",
"@types/node": "^22.10.2",
"chokidar-cli": "^3.0.0",
"typescript": "^5.7.2",
"vite": "^6.0.3",
"vitest": "^3.0.1",
"vitest-environment-clarinet": "^2.2.0"
},
"type": "module"
}
PackagePurpose
@hirosystems/clarinet-sdkWebAssembly-compiled Clarinet for Node.js
@stacks/transactionsClarity value manipulation in TypeScript
vitestModern testing framework with native TypeScript support
vitest-environment-clarinetSimnet bootstrapping for tests

Vitest configuration

The vitest.config.js configures the testing framework:

vitest.config.js
/// <reference types="vitest" />
import { defineConfig } from "vite";
import { vitestSetupFilePath, getClarinetVitestsArgv } from "@hirosystems/clarinet-sdk/vitest";
export default defineConfig({
test: {
environment: "clarinet",
pool: "forks",
poolOptions: {
forks: { singleFork: true }
},
setupFiles: [
vitestSetupFilePath,
"./tests/setup.ts" // Custom setup
],
environmentOptions: {
clarinet: {
...getClarinetVitestsArgv(),
manifest: "./Clarinet.toml",
coverageReports: ["lcov", "html"]
}
}
}
});

This configuration enables:

  • Clarinet environment: Automatic simnet setup for each test
  • Single fork mode: Efficient test execution with proper isolation
  • Coverage tracking: Generate reports in multiple formats
  • Custom setup: Add project-specific test utilities

TypeScript configuration

The tsconfig.json provides TypeScript support:

tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"types": ["node", "vitest/globals"],
"baseUrl": ".",
"paths": {
"@/*": ["./tests/*"],
"@contracts/*": ["./contracts/*"]
}
},
"include": [
"node_modules/@hirosystems/clarinet-sdk/vitest-helpers/src",
"tests/**/*",
"contracts/**/*.ts"
]
}

Path aliases simplify imports in tests:

tests/token.test.ts
import { accounts } from "@/helpers";
import { TokenErrors } from "@contracts/token-types";

Network configurations

Environment settings

Each network has its own configuration file in the settings directory:

settings/Devnet.toml
[network]
name = "devnet"
deployment_fee_rate = 10
chain_id = 2147483648
orchestrator_port = 20445
bitcoin_node_p2p_port = 18444
bitcoin_node_rpc_port = 18443
stacks_node_p2p_port = 20444
stacks_node_rpc_port = 20443
stacks_api_port = 3999
stacks_api_events_port = 3700
bitcoin_explorer_port = 8001
stacks_explorer_port = 8000
postgres_port = 5432
postgres_username = "postgres"
postgres_password = "postgres"
postgres_database = "postgres"
bitcoin_node_username = "devnet"
bitcoin_node_password = "devnet"
miner_mnemonic = "twice kind fence tip hidden..."
miner_derivation_path = "m/44'/5757'/0'/0/0"
faucet_mnemonic = "shadow private easily thought..."
faucet_derivation_path = "m/44'/5757'/0'/0/0"
[accounts.deployer]
mnemonic = "twice kind fence tip hidden..."
balance = 100_000_000_000_000
[accounts.wallet_1]
mnemonic = "sell invite acquire kitten..."
balance = 10_000_000_000_000

These settings control:

  • Network ports: API, RPC, and explorer endpoints
  • Account configuration: Test wallets with STX balances
  • Chain parameters: Network-specific blockchain settings
Security Notice

Never commit mainnet private keys or mnemonics. Use environment variables for production credentials.

Common issues

Next steps

Now that you understand project structure: