> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Circuit Registry

> Address, content addressing, storage, ABI, and proof wire for CircuitRegistry

## Constants

| Constant | Value | Meaning |
| - | - | - |
| Registry address | `0x00000000000000000000000000000000000000F0` | Genesis system contract |
| `ULTRAHONK` | `0x02` | The only accepted backend tag |
| `MIN_VK_BYTES` | `256` | Minimum verification-key length |
| `MAX_VK_BYTES` | `24,574` | Maximum verification-key length |
| Verifier precompile | `0x00000000000000000000000000000000000000F2` | Stateless UltraHonk verification |

`MAX_VK_BYTES` leaves room for a leading `STOP` byte and a backend byte
under EIP-170's 24,576-byte runtime-code limit.

## Circuit ID

A circuit ID commits to the backend and exact verification-key bytes:

```text theme={null}
circuitId = keccak256(backend || verificationKey)
```

For UltraHonk, `backend` is the single byte `0x02`. Recompiling a circuit may
change its verification key and circuit ID. Circuit names and versions remain
offchain.

## ABI

```solidity theme={null}
function register(uint8 backend, bytes calldata vk)
    external
    returns (bytes32 circuitId, address pointer);

function verificationKey(bytes32 circuitId)
    external
    view
    returns (uint8 backend, bytes memory vk);

function verify(bytes calldata request) external view returns (bool);

function pointerOf(bytes32 circuitId) external view returns (address);
```

| Function | Behavior |
| - | - |
| `register` | Validates the backend and key length, stores a key if it is new, and returns its circuit ID and bytecode pointer |
| `verificationKey` | Returns the backend and verification-key bytes for a registered circuit |
| `verify` | Resolves the key, forwards the complete proof wire to `0xF2`, and returns the verification result |
| `pointerOf` | Returns the bytecode pointer for a circuit ID, or the zero address when the circuit is unknown |

## Registration and storage

`register` performs these steps:

1. Require backend `0x02` and a verification key between 256 and 24,574 bytes.
2. Compute the circuit ID from `backend || vk`.
3. Return the existing pointer if the circuit ID is already registered.
4. Deploy a `CircuitRegistryBlob` with CREATE2, using the circuit ID as its
   salt.
5. Record the pointer and emit `CircuitRegistered`.

Each pointer contains this runtime code:

```text theme={null}
0x00 || backend || verificationKey
```

The leading `0x00` is `STOP`. A call to the pointer halts before the EVM interprets
`backend || verificationKey` as instructions. The registry skips the `STOP` byte and
reads the remaining bytes as data using `EXTCODECOPY`.

## Proof request wire

Pass the following `request` to the ABI function `verify(bytes)`:

| Field | Size | Encoding | | |
| - | -: | - | - | - |
| `circuitId` | 32 bytes | Content hash of \`backend | | verificationKey\` |
| `proofLen` | 4 bytes | Big-endian proof length | | |
| `proof` | `proofLen` bytes | UltraHonk proof bytes | | |
| `nPub` | 4 bytes | Big-endian public-input count | | |
| `publicInputs` | `nPub × 32` bytes | Ordered field elements | | |

```text theme={null}
circuitId || be32(proofLen) || proof || be32(nPub) || publicInputs
```

`verify` resolves `pointerOf[circuitId]`, reads the registered key, and inserts
the key material before forwarding this wire to `0xF2`:

```text theme={null}
circuitId
|| be32(keyLen)
|| backend
|| verificationKey
|| be32(proofLen)
|| proof
|| be32(nPub)
|| publicInputs
```

`keyLen` includes the one-byte backend tag. The precompile recomputes
`keccak256(backend || verificationKey)` and requires it to equal `circuitId`
before verifying the proof.

Send the application wire as the argument to `verify(bytes)`. Sending that wire
directly to `0xF0` without the ABI selector does not invoke verification.

## Verification result

`verify` returns `true` only when `0xF2` returns the exact 32-byte encoding of
`1`. It returns `false` for:

* a request shorter than 32 bytes;
* an unknown circuit ID;
* malformed proof lengths, public-input counts, or trailing bytes;
* invalid stored key shape;
* a backend or key that does not match the circuit ID;
* an invalid proof; or
* a failed or malformed precompile response.

Treat every `false` result as proof rejection. The result does not identify
which check failed.

The `0xF2` precompile charges:

```text theme={null}
64 × (400,000 + 16 × verificationKeyBytes + 5,000 × publicInputCount)
```

The factor of 64 is Metal's CPU precompile gas multiplier. This excludes the
EVM overhead of calling `CircuitRegistry` and copying the stored key.

## Event

```solidity theme={null}
event CircuitRegistered(
    bytes32 indexed circuitId,
    address indexed pointer,
    uint8 backend,
    uint256 vkLength
);
```

The event is emitted for a new registration. An idempotent call returns the
existing values without emitting another event.

## Errors

| Error | Raised by | Meaning |
| - | - | - |
| `UnknownBackend(uint8 backend)` | `register` | The backend is not UltraHonk `0x02` |
| `InvalidVerificationKeyLength(uint256 length)` | `register`, `verificationKey` | The verification key is outside the supported range or the stored blob has an invalid shape |
| `UnknownCircuit(bytes32 circuitId)` | `verificationKey` | No pointer is registered for the circuit ID |

Malformed requests, unknown circuits, and invalid proofs produce a `false`
result from `verify`.

> [card] [Circuits and proofs](https://docs.metalntwx.com/learn/mip410/proof-and-verification.md)
>
> Understand how MIP410 composes its proof system.

> [card] [Shielded payment circuits](https://docs.metalntwx.com/reference/privacy/transfer-circuit.md)
>
> Inspect the sender and recipient proof boundaries.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.