Cannon package manager for Ethereum deployments. Use when building, testing, deploying, or inspecting Cannon packages. Covers cannonfile syntax, CLI commands...
Some Cannon commands are IRREVERSIBLE and handle real value. Be careful with:
cannon build (without --dry-run) — deploys contracts on real networkscannon publish — publishes to on-chain registry (permanent)cannon register — registers package names on-chaincannon publishers — manages package publisher permissionsSafe commands: cannon build --dry-run, cannon inspect, cannon run, cannon test, cannon decode, cannon trace, cannon clean
Rules:
--dry-run first — Simulate before executing on real networksCANNON_PRIVATE_KEY env var or --impersonate (recommended)Use state-of-the-art models for Cannon tasks. Deployment scripts handle real value — don't skimp on model quality. Prefer Claude, GPT-4, or equivalent high-capability models over cheaper alternatives.
For complete cannonfile syntax validation and autocomplete, refer to the official JSON schemas:
For editor validation, add the schema reference at the top of your cannonfile.toml:
#:schema https://raw.githubusercontent.com/usecannon/cannon/refs/heads/dev/packages/lsp/src/schema.json
name = "my-package"
version = "1.0.0"
...
This enables autocomplete and validation in editors with TOML LSP support (like taplo).
Cannon is a package manager and deployment system for Ethereum smart contracts. It uses declarative cannonfiles to define deployment workflows and supports both local development and on-chain deployments.
Before using this skill, ensure these tools are installed:
npm install -g pnpmcurl -L https://foundry.paradigm.xyz | bash && foundryuppnpm add -g @usecannon/cliVerify installation:
node --version && pnpm --version
forge --version && anvil --version
cannon --version
For complete CLI options, see references/cli.md.
cannon build # Build package locally (starts anvil, deploys contracts)
cannon run <pkg:ver> # Run a deployed package (shorthand: cannon <pkg:ver>)
cannon test # Run forge tests with deployment context
cannon inspect <pkg:ver> # View package details
cannon publish # Publish to on-chain registry + IPFS ⚠️ IRREVERSIBLE
cannon clean # Delete cache directories
cannon verify # Verify contracts on Etherscan/Sourcify
<package-name>:<version>@<preset>
Examples:
greeter-foundry:2.24.0safe:1.4.1synthetix-omnibus:3.1.4@mainname = "my-package"
version = "1.0.0"
description = "My package description"
tags = ["defi", "token"]
preset = "main"
# Include additional files containing actions (for larger packages)
include = ["./deposits.toml", "./withdrawals.toml"]
Use include to split large cannonfiles into multiple files. Included files use the fragment schema and can contain any actions (deploy, invoke, clone, etc.).
Modern syntax uses [var.label] for settings (deprecated: [setting.name]).
[var.chainId]
defaultValue = 1
[var.owner]
defaultValue = "0x..."
[deploy.MyContract]
artifact = "MyContract"
args = ["<%= settings.owner %>"]
[invoke.initialize]
target = ["<%= contracts.MyContract.address %>"]
func = "initialize"
args = ["<%= settings.owner %>"]
[clone.safe]
source = "safe:1.4.1"
target = "safe"
When to use clone vs import:
clone — Use another package as a "blueprint" to deploy it anew. Always set target appropriately (same as source if you own it, or a new name if not).import — Pull in data from an already-deployed package without re-deploying.[pull.usdc]
source = "usdc:1.0.0@main"
Access: <%= imports.usdc.contracts.USDC.address %>
Use ERB-style templates to reference values:
<%= settings.varName %> — settings<%= contracts.ContractName.address %> — deployed contract address<%= contracts.ContractName.abi %> — contract ABI<%= imports.pkg.contracts.Contract.address %> — imported contract| Action | Description |
|---|---|
deploy | Deploy a contract |
invoke | Call a contract function |
clone | Deploy another package as a blueprint |
import | Pull data from an already-deployed package |
pull | (deprecated) Alias for import |
var | Define computed variables |
router | Create a router contract to bypass size limits (pairs well with UUPS proxy) |
diamond | Create an EIP-2535 Diamond with facets |
⚠️ Always use chain 13370 (Cannon Network) for local testing before deploying to target chains.
Default chain ID: 13370 (Cannon Network)
# Build with local anvil
cannon build
# Build for specific chain
cannon build --chain-id 1 --rpc-url $RPC_URL
# Dry run (simulation only)
cannon build --dry-run --impersonate-all
# Run a package locally
cannon run greeter-foundry:2.24.0
⚠️ Always use --dry-run first to verify deployments before executing on real networks.
# Deploy to mainnet
cannon build --chain-id 1 --rpc-url $RPC_URL --private-key $KEY
# Publish to registry
cannon publish --chain-id 1 --rpc-url $RPC_URL --private-key $KEY
# For simulation before actual deploy
cannon build --chain-id 1 --rpc-url $RPC_URL --dry-run
# Run tests with forge
cannon test
# Test specific contract
cannon test --match-path "test/MyContract.t.sol"
Use cannon-std in Forge tests:
import {Cannon} from "cannon-std/Test.sol";
contract MyTest is Cannon {
function setUp() public {
// Load deployed contracts
address myContract = getAddress("MyContract");
}
}
| Directory | Contents |
|---|---|
~/.local/share/cannon/tags/ | Package reference files |
~/.local/share/cannon/ipfs_cache/ | Cached IPFS artifacts |
~/.local/share/cannon/build_results/ | Build outputs |
~/.local/share/cannon/blobs/ | Large binary blobs |
Create a router contract that efficiently passes calls to downstream contracts. Powerful when combined with a UUPS proxy.
[deploy.CoreImplementation]
artifact = "Core"
[deploy.AnotherImplementation]
artifact = "Another"
[router.CoreRouter]
dependencies = ["CoreImplementation", "AnotherImplementation"]
[deploy.Diamond]
artifact = "Diamond"
[deploy.FacetA]
artifact = "FacetA"
[deploy.FacetB]
artifact = "FacetB"
[diamond.Diamond]
facets = ["FacetA", "FacetB"]
[deploy.Library]
artifact = "Library"
[deploy.Contract]
artifact = "Contract"
libraries = { Library = "<%= contracts.Library.address %>" }
Cannon provides commands to decode bytecode, trace transactions, and interact with deployed contracts.
Decode hex data (function calls, events, errors) using package ABIs:
cannon decode synthetix-omnibus --chain-id 8453 --preset main 0x...
Get human-readable stack traces for transactions:
cannon trace <tx-hash> --chain-id 1 --rpc-url $RPC_URL
Send transactions to deployed contracts through the CLI:
cannon interact synthetix-omnibus --chain-id 8453 --contract CoreProxy
alter)The alter command modifies existing Cannon packages outside the regular build process. Use for troubleshooting, migrations, or fixing broken package state.
⚠️ Only use alter when no other option exists.
| Command | Description |
|---|---|
import | Import existing artifacts into a deployment step (for migrations) |
set-contract-address | Change a contract's address in the deployment |
mark-complete | Mark a deployment step as complete |
mark-incomplete | Mark a deployment step as incomplete |
set-url | Update the deployment URL reference |
set-misc | Update miscellaneous data URL |
clean-unused | Remove unused deployment states |
migrate-212 | Migrate packages from version 2.12 format |
# Import a deployed contract by its creation transaction
cannon alter my-package:1.0.0 --chain-id 1 import deploy MyContract 0x...txhash
# Import an executed transaction
cannon alter my-package:1.0.0 --chain-id 1 import invoke initialize 0x...txhash
Cannon supports GitOps-style deployments through the website interface.
Deploy packages directly from GitHub repositories or IPFS hashes via the Cannon website:
See: https://usecannon.com/deploy
Create a dedicated Git repository for deployment configurations (separate from source code):
Migrating from hardhat-deploy, Foundry scripts, or other deployment frameworks:
cannon build (save the IPFS hash)# Set the package URL to local template
cannon alter my-package --chain-id 1 set-url <ipfs-hash>
# Import each deployed contract/transaction
cannon alter my-package --chain-id 1 import deploy MyContract 0x...txhash
cannon build --chain-id 1For detailed information on:
| Issue | Solution |
|---|---|
| "deployment not found" | Package not published for this chain ID. Check --chain-id |
| Build fails with "artifact not found" | Run forge build first, or check artifact path |
| IPFS timeout | Check network connection, may need IPFS gateway |
| Registry publish fails | Verify you have write permissions for the package name |
| Wrong chain deployed | Always double-check --chain-id — deployments cannot be undone |
| Published incorrect package | Registry publishes are permanent — verify version and artifacts first |
cannon build (chain 13370)cannon test--dry-run for target chain ⚠️ ALWAYS DO THIS FIRSTcannon build --chain-id <id> ⚠️ IRREVERSIBLE on non-local networks (safe on chain 13370)cannon verifycannon publish ⚠️ PERMANENT ON-CHAIN| File | Purpose |
|---|---|
cannonfile.toml | Package definition |
cannonfile.lock | Locked dependencies |
.cannon/ | Build cache (gitignored) |
deployments/ | Deployment artifacts |
ZIP package — ready to use