Push Contracts
Use the push-contracts command to push the smart contracts in a Hardhat or Foundry project to your Stagenet. Each contract becomes a pending Workspace — a dedicated dashboard showing transactions, balances, TVL, storage, and tracked data over time.
Re-run after each rebuild to keep your Stagenet in sync — unchanged contracts are no-ops, changed contracts get a new version.
Usage
With a contract.dev.js at your project root (see Setup):
- Compile your contracts:
# Foundry
forge build
# Hardhat
npx hardhat compile- Push the compiled contracts to your Stagenet:
npx contract.dev push-contracts- Deploy your contracts to your Stagenet using your normal deployment script:
# Foundry
forge script script/Deploy.s.sol --rpc-url <YOUR_STAGENET_RPC_URL> --broadcast
# Hardhat
npx hardhat run scripts/deploy-contract.js --network stagenetEach deployment will now have its own dashboard.
How it works
The CLI scans your compiled build artifacts:
- Foundry:
out/ - Hardhat:
artifacts/contracts/
For each deployable contract, it uploads ABI and bytecode metadata to your Stagenet and creates a pending Workspace.
Pushed contracts appear immediately in your project’s Imported Contracts list — useful for verifying the push landed before you deploy.
When a contract is deployed to your Stagenet, its deployed bytecode is compared against the uploaded artifacts. If there is a match, the corresponding Workspace is activated.
If the matching contract has already been deployed, it is detected immediately and has an active Workspace created for it.
The CLI does not build your project. Run forge build or npx hardhat compile first.
Source and artifact directories
The CLI scans your compiled build artifacts, but only pushes contracts that come from the source directory defined by your project config.
- Foundry: reads
[profile.default].srcandoutfromfoundry.toml(default tosrc/andout/). - Hardhat: reads
paths.sourcesandpaths.artifactsfromhardhat.config.{ts,js,cjs,mjs}(default tocontracts/andartifacts/contracts/).
Contracts outside the source directory are skipped.
This prevents imported dependencies, such as OpenZeppelin contracts or libraries from node_modules, from being treated as part of your project.
To override either path, set contracts and/or artifacts in your contract.dev.js:
/** @type {import('contract.dev').Config} */
module.exports = {
rpcUrl: process.env.STAGENET_RPC_URL,
contracts: "src/protocol", // overrides paths.sources / [profile.default].src
artifacts: "build/artifacts/src/protocol", // overrides paths.artifacts / [profile.default].out
};Dynamic paths
The CLI doesn’t execute your Hardhat config — it reads paths.sources and paths.artifacts with a string-literal regex. That keeps startup fast and avoids loading every Hardhat plugin, but it means only quoted string literals are picked up:
// ✅ detected
paths: { sources: "src/protocol" }
// ❌ not detected — the CLI falls back to the Hardhat default
paths: { sources: process.env.CONTRACTS_DIR || "src/protocol" }
paths: { sources: path.join("src", "protocol") }
paths: { sources: SOURCES_DIR } // imported constant
paths: { sources: `${pkg}/contracts` } // template literalWhen a dynamic value in hardhat.config falls back to the default, the CLI prints which path it couldn’t resolve and points you here. Set the resolved paths in contract.dev.js.
contract.dev.js is a regular Node module (loaded with require()) so you can use the same dynamic patterns you’d use in hardhat.config — env vars, path.join, imports, template literals — and the CLI will see the resolved values:
const path = require("path");
const SRC = process.env.CONTRACTS_DIR || "src/protocol";
/** @type {import('contract.dev').Config} */
module.exports = {
rpcUrl: process.env.STAGENET_RPC_URL,
contracts: SRC,
artifacts: path.join("build/artifacts", SRC),
};For Hardhat, artifacts should be the base artifacts directory plus your sources subpath — Hardhat mirrors the sources tree under the artifacts root, so a paths.sources of src/protocol and paths.artifacts of build/artifacts produces artifacts at build/artifacts/src/protocol/.
If your package.json has "type": "module", use contract.dev.cjs (or export default { ... } in contract.dev.js).
Notes
- Interfaces and abstract contracts are skipped.
- Contracts without deployable bytecode are skipped.
- Workspaces remain inactive until matching contracts are deployed to your Stagenet.