CDM is the deploy, registry, and dependency tool for PVM smart contracts on Polkadot. It builds contracts in dependency order, deploys them to Asset Hub, publishes metadata to Bulletin, registers package names in the on-chain registry, and installs typed ABIs for downstream projects.
Browse published contracts at contracts.dot.li.
curl -fsSL https://raw.githubusercontent.com/paritytech/contract-dependency-manager/main/install.sh | bashThis installs the cdm binary, the Rust nightly toolchain with rust-src, and cargo-pvm-contract.
# Scaffold a new workspace
cdm template shared-counter
# Generate a deploy account and map it for pallet-revive
cdm init -n paseo
cdm account map -n paseo
# Build, deploy, publish metadata, and register all workspace contracts
cdm deploy -n paseoBefore deploying a template, change every [package.metadata.cdm] package = "@example/..." namespace in the contracts' Cargo.toml files to one you own, such as @myteam/counter. Package names are global per registry target.
Use CDM for contract lifecycle and dependency resolution:
- Build PVM contracts.
- Deploy contracts to Asset Hub.
- Publish ABI/readme metadata to Bulletin.
- Register
@org/name -> address + metadatain the ContractRegistry. - Install published contracts into
cdm.json,.cdm/contracts.d.ts, and generated Solidity import files.
Use product-sdk in apps and frontends:
- Do not import
@dotdm/cdmin frontend/runtime app code. - Use
@parity/product-sdk-contractsandContractManagerto resolve contracts fromcdm.json. - Use
@parity/product-sdk-signerfor Product Account / Triangle host signing. - Use
@parity/product-sdk-chain-clientfor host chain connections. - Use
@parity/product-sdk-txwhen batching.prepare()calls with other Asset Hub transactions.
cdm install generates type augmentation for @parity/product-sdk-contracts, so ContractManager.getContract("@org/name") is typed after .cdm/contracts.d.ts is included by TypeScript. Solidity projects also get address-backed interfaces under .cdm/solidity/.
Declare each Rust contract's CDM package name in Cargo.toml:
[package.metadata.cdm]
package = "@yourorg/mycontract"Then define the contract with the pvm_contract_sdk macros:
#[pvm_contract_sdk::contract(allocator = "pico", allocator_size = 1024)]
mod mycontract {
pub struct MyContract;
impl MyContract {
#[pvm_contract_sdk::constructor]
pub fn new(&mut self) {}
#[pvm_contract_sdk::method]
pub fn do_something(&self) -> u32 { 42 }
}
}To call another CDM contract from Rust:
cdm::import!("@someorg/other-contract");
let other = other_contract::OtherContract::cdm_lookup();
other.do_something().call(self).expect("OtherCallFailed");For workspace-local packages, cdm::import! resolves the ABI through Cargo metadata when the provider crate declares the matching [package.metadata.cdm] package. For external packages, run cdm i -n paseo @someorg/other-contract first; the macro falls back to the flat cdm.json snapshot and materializes any ABI file it needs under the project-local .cdm/ directory.
Solidity contracts use NatSpec for their own CDM package name:
/// @custom:cdm @yourorg/mycontract
contract MyContract {}To call an installed CDM contract from Solidity, import the generated interface:
import ".cdm/solidity/someorg/other-contract.sol";
contract Caller {
function callOther() external returns (uint256) {
return SomeorgOtherContract.ref().doSomething();
}
}The generated file contains an interface plus a small library with ADDRESS, ref(), and cdm(). Running cdm i updates that generated file from the registry, so contract source does not need an address edit.
Install published contract ABIs:
cdm i -n paseo @yourorg/counter @yourorg/counter-writerThis updates cdm.json, stores ABI/metadata artifacts under project-local .cdm/contracts/, writes .cdm/contracts.d.ts, and generates Solidity imports under .cdm/solidity/. Keep .cdm/**/* in tsconfig.json so product-sdk contract handles are typed.
Install the product-sdk app/runtime packages:
pnpm add @parity/product-sdk-chain-client@^0.5.2 \
@parity/product-sdk-contracts@^0.6.2 \
@parity/product-sdk-descriptors@^0.5.1 \
@parity/product-sdk-signer@^0.5.0 \
@parity/product-sdk-tx@^0.2.6 \
polkadot-api@^2.1.2In frontend code, use product-sdk:
import { getChainAPI } from "@parity/product-sdk-chain-client";
import {
ContractManager,
ensureContractAccountMapped,
type CdmJson,
} from "@parity/product-sdk-contracts";
import { paseo_asset_hub } from "@parity/product-sdk-descriptors/paseo-asset-hub";
import { SignerManager } from "@parity/product-sdk-signer";
import cdmJson from "./cdm.json";
const DOT_NS_IDENTIFIER = "my-app.dot";
const signerManager = new SignerManager({ dappName: "my-app" });
await signerManager.connect();
const productAccount = await signerManager.getProductAccount(DOT_NS_IDENTIFIER);
if (!productAccount.ok) throw productAccount.error;
const chain = await getChainAPI("paseo");
const contracts = ContractManager.fromClient(
cdmJson as CdmJson,
chain.raw.assetHub,
paseo_asset_hub,
);
await ensureContractAccountMapped(
contracts.getRuntime(),
productAccount.value.address,
productAccount.value.getSigner(),
);
contracts.setDefaults({
origin: productAccount.value.address,
signer: productAccount.value.getSigner(),
});
const counter = contracts.getContract("@yourorg/counter");
const { value } = await counter.getCount.query();
await counter.increment.tx();Every contract method exposes:
.query(...args, opts?)for read-only dry-runs..tx(...args, opts?)for signed contract calls..prepare(...args, opts?)for batch-ready calls.
Batch contract calls with other Asset Hub transactions:
import { batchSubmitAndWatch } from "@parity/product-sdk-tx";
const a = counter.increment.prepare();
const counterWriter = contracts.getContract("@yourorg/counter-writer");
const b = counterWriter.writeIncrement.prepare();
await batchSubmitAndWatch([a, b], chain.raw.assetHub, productAccount.value.getSigner());The shared-counter template demonstrates a three-contract dependency graph:
counterstores a shared count value.counter-writercallscounter.increment()through a CDM reference.counter-readerqueriescounter.get_count()through a CDM reference.
cdm template shared-counter
cdm deploy -n paseo
cdm i -n paseo @yourorg/counter @yourorg/counter-writer @yourorg/counter-readerCDM includes first-pass Solidity scaffolds for the two supported Solidity toolchains:
cdm template hardhat-counter
cdm template foundry-counterhardhat-counter uses @parity/hardhat-polkadot and compiles with pnpm build.
foundry-counter uses the Polkadot Foundry fork and compiles with forge build --resolc.
These templates are compile-ready starter projects and can be built, deployed, published, registered, installed, and consumed through CDM.
Build all contracts with the selected ContractRegistry address baked in.
cdm build
cdm build --contracts counter counter_writer
cdm build -n paseo
cdm build --root /path/to/workspaceBuild, deploy, publish metadata, and register all workspace contracts.
cdm deploy -n paseo
cdm deploy -n paseo --suri //Bob
cdm deploy --registry-address 0x... --assethub-url wss://... --bulletin-url wss://...Supported deploy presets are paseo and local. Use explicit URLs with --assethub-url, --bulletin-url, and --registry-address for custom networks. The paseo preset points at Paseo v2.
Install published contracts for Rust imports, Solidity imports, and product-sdk TypeScript usage.
cdm i -n paseo @polkadot/contexts @polkadot/profiles
cdm i -n paseo @yourorg/package:3cdm install queries the registry, fetches metadata from the configured Bulletin IPFS gateway, updates the flat cdm.json, installs ABI/metadata artifacts under project-local .cdm/contracts/, regenerates .cdm/contracts.d.ts, and writes Solidity interfaces under .cdm/solidity/.
Scaffold an example project into ./<template-name> by default. Pass a target directory to override it; use . to scaffold into the current directory.
cdm template shared-counter # writes ./shared-counter
cdm template instagram # writes ./instagram
cdm template hardhat-counter # writes ./hardhat-counter
cdm template foundry-counter # writes ./foundry-counter
cdm template instagram . # writes current directory
cdm template instagram ./apps/ig # creates ./apps/ig recursively
cd instagram && cdm template . # infers the instagram template from cwdThe Instagram template is the current browser app example. It uses product-sdk host flows, Product Account signing, Bulletin uploads, and ContractManager resolution from cdm.json.
git clone https://github.com/paritytech/contract-dependency-manager.git
cd contract-dependency-manager
make setup
# Run the CLI in dev mode
bun run src/apps/cli/src/cli.ts --help
# Run the frontend; rebuilds local workspace deps first
make frontend
# Run tests
make test # unit tests only (fast)
pnpm test:e2e # end-to-end: spawns revive-dev-node, deploys
# the registry, exercises every method.
# Requires `revive-dev-node` and `bun` on $PATH:
# cargo install --git https://github.com/paritytech/polkadot-sdk --bin revive-dev-node
# curl -fsSL https://bun.sh/install | bash
# Build native binary
make compile
# Cross-compile for all platforms
make compile-allsrc/
apps/
cli/ CLI tool (Commander.js, Bun runtime)
frontend/ Contract Hub web dashboard (React, Vite)
lib/
contracts/ @dotdm/contracts: detection, deploy, publish, registry, install helpers
env/ @dotdm/env: chain connections, signers, presets
utils/ @dotdm/utils: shared constants and utilities
scripts/ build/deploy helper scripts
cdm/ Rust macro + TypeScript compatibility package
contract/ ContractRegistry (Rust/PolkaVM)
templates/ Project scaffolding templates
Apache-2.0