Overview
A chip is a static descriptor of a reusable circuit building block. It declares the columns, gate expressions, lookup arguments, and sub-chip dependencies that the block contributes to a circuit. The estimator aggregates these descriptors to compute proof size, verification key size, and verifier cost — no proof generation required.

Each chip is a Rust struct that implements the Chip trait (defined in src/plutus_gen/stats/chips/mod.rs). All methods have default implementations returning empty vectors, so you only need to override the ones relevant to your chip.
Chip trait methods
Method Required? Description
advice_columns() optional Witness columns. Each entry declares its rotation set (which rows are queried) and whether it participates in copy constraints.
fixed_columns() optional Shared fixed columns (MDS matrices, round constants). These are merged by position across chips: if two chips both declare a shared fixed column at index 0, only one physical column is allocated.
extra_columns() optional Exclusive fixed columns, selectors, and complex selectors that belong solely to this chip and are never shared.
gate_args() optional Gate expressions from meta.create_gate(). Each Argument is a vector of ScalarExpression values, one per expression in the gate group.
lookup_args() optional Lookup expressions from meta.lookup() or meta.lookup_any().
trash_args() optional Trashcan expressions from Constraints::with_additive_selector() (partial-round style gates with a single additive selector).
chip_deps() optional Direct sub-chips this chip depends on. Transitive deps are resolved automatically; each chip is counted once regardless of how many composites declare it.
nr_pow2range() optional Number of pow2range lookup arguments needed by this chip. Defaults to 0.
Step-by-Step
Follow these eight steps in order. Steps 1–4 are Rust source changes; steps 5–6 wire the chip into the public APIs; steps 7–8 update the generated artifacts consumed by the web interface.
1
Create the chip file
Add a new .rs file under src/plutus_gen/stats/chips/primitives/ (or a subdirectory such as hash/ or curve/) and implement the Chip trait. Only override the methods your chip uses; the rest default to empty.
src/plutus_gen/stats/chips/primitives/hash/my_chip.rs
use super::super::super::{Argument, Chip, Column, RotationSet, ScalarExpression, SupportedChips}; pub(crate) struct MyChip; impl Chip for MyChip { fn advice_columns() -> Vec<Column> { vec![ // Columns queried at current and next row, copy-constrained Column::advice(RotationSet::new(false, false, true, true, false), true), Column::advice(RotationSet::curr(), true), ] } fn extra_columns() -> Vec<Column> { vec![ Column::selector(), // q_enable ] } fn gate_args() -> Vec<Argument> { vec![ // q_enable * (w0_curr + w1_curr - w0_next) vec![ScalarExpression::gate_expression(2, 1, 2, 0, 1, 0)], ] } fn chip_deps() -> Vec<SupportedChips> { vec![SupportedChips::Native] } }
ScalarExpression::gate_expression(degree, nb_neg, nb_add, nb_sub, nb_mul, nb_from_int)
Records the algebraic cost of one polynomial expression in the constraint. These counts are used to estimate the number of field operations the verifier must perform when evaluating the circuit's constraints.
2
Export from primitives/mod.rs
Re-export your struct so it is visible to the parent chips module.
src/plutus_gen/stats/chips/primitives/mod.rs
pub(crate) mod hash; pub(crate) use hash::{Poseidon, MyChip}; // add MyChip here
If your chip lives in a new subdirectory, declare the module with pub(crate) mod my_dir; and add the corresponding mod.rs inside it following the pattern in hash/mod.rs.
3
Add a variant to SupportedChips
Register the new chip in the enum. The strum attribute controls the string identifier used in the CLI, WASM API, and chip_profiles.json.
src/plutus_gen/stats/chips/mod.rs
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, strum::EnumIter, strum::Display)] pub enum SupportedChips { #[strum(to_string = "native")] Native, #[strum(to_string = "poseidon")] Poseidon, // ... existing variants ... #[strum(to_string = "my_chip")] // ← add this MyChip, }
4
Wire into the dispatch macro
The impl_supported_chips! macro generates all the match arms that delegate trait methods to the concrete struct. Add one line per chip.
src/plutus_gen/stats/chips/mod.rs
impl_supported_chips! { Native => Native, Poseidon => Poseidon, HashToCurve => HashToCurve, EdwardsJubjub => EdwardsJubjub, WeierstrassBls12381 => WeierstrassBls12381, WeierstrassSecp256k1 => WeierstrassSecp256k1, MyChip => MyChip, // ← add this }
At this point cargo build should compile cleanly. Run it now to catch any type errors in your trait implementation before proceeding.
5
Expose via the CLI
Add a boolean flag to EstimateCliArguments and push the variant in the chips() method. The flag name becomes the --my-chip long option (Clap converts underscores to hyphens automatically).
src/plutus_gen/stats/cli.rs
// In EstimateCliArguments: #[arg(long, help_heading = "Chips", help = "Include the MyChip chip.")] pub my_chip: bool, // In chips(): if self.my_chip { chips.push(SupportedChips::MyChip); }
6
Expose via the WASM API
The web interface calls the WASM estimate() function with a JSON array of chip names. Add your chip's string identifier to the chip_from_str parser so the browser can select it.
src/wasm.rs
fn chip_from_str(s: &str) -> Option<SupportedChips> { match s { "Native" => Some(SupportedChips::Native), "Poseidon" => Some(SupportedChips::Poseidon), "HashToCurve" => Some(SupportedChips::HashToCurve), "EdwardsJubjub" => Some(SupportedChips::EdwardsJubjub), "WeierstrassBls12381" => Some(SupportedChips::WeierstrassBls12381), "WeierstrassSecp256k1"=> Some(SupportedChips::WeierstrassSecp256k1), "MyChip" => Some(SupportedChips::MyChip), // ← add this _ => None, } }
The string key you use here is what the JavaScript in cost-estimator.html passes to estimate(). Keep it consistent with the variant name to avoid confusion.
7
Regenerate chip_profiles.json
The chips reference page and the web estimator both read docs/chip_profiles.json, which is generated from the live Rust estimator. Run the dump binary to rebuild it after any chip change.
cargo run --bin dump_profiles
This writes docs/chip_profiles.json with an entry for every SupportedChips variant (via strum::IntoEnumIterator), including your new chip.
8
Rebuild the WASM module
The interactive cost estimator at docs/cost-estimator.html runs entirely in-browser via WebAssembly. Rebuild and copy the WASM output whenever you change any code that affects estimation results.
# Install wasm-pack if not already present cargo install wasm-pack # Build the WASM target wasm-pack build --target web --out-dir docs/wasm
Commit the regenerated docs/wasm/ and docs/chip_profiles.json together with your Rust changes so the web interface stays in sync with the code.
Checklist
Use this list to verify you haven't missed anything before opening a pull request.
Tips & Patterns
Shared vs exclusive fixed columns
Declare columns as shared (Column::shared_fixed) when they carry data that logically spans chips — for example MDS matrix constants that both a hash chip and an ECC chip might reuse. The merger allocates only one physical column per shared position. Use exclusive (Column::exclusive_fixed) or selectors for anything that belongs solely to your chip; these are always appended and never merged.
Delegating to a generic chip
The Weierstrass curve chips delegate to a generic EccChip<T> parameterised by field and curve constants. If your chip is a specialisation of a general construction (foreign-field arithmetic, range checks, etc.) consider following the same pattern: implement a generic struct with trait parameters and have the concrete chip call into it. See src/plutus_gen/stats/chips/primitives/curve/ecc/ for a worked example.
Verifying your cost numbers
Run the estimator against a real circuit that uses your chip to sanity-check the numbers:
# Estimate from CLI flags cargo run -- --my-chip --pi 1 # Compare against a real proof (requires a circuit example) cargo run --example my_example
Proof size is deterministic for a given set of chips and circuit parameters. If the estimated size does not match the actual proof, the most common cause is a missing or over-counted advice column, or an incorrect rotation set declaration.