# Overview

<figure><img src="/files/AKuif8TGMbDQzcgK5wNX" alt=""><figcaption></figcaption></figure>

Zircuit Finance is a secure platform bringing institutional-grade strategies onchain.

The platform was created to address persistent credit and security risks by combining traditional risk frameworks, enforceable protections, and transparent reporting with onchain execution. Zircuit Finance integrates trusted institutions and top DeFi protocols to diversify exposure across regulated and decentralized venues.

Our institutional partners include: Fidelity, Monarq Asset Management, Forteus, FalconX, B2C2 and more.

**Key Platform Specs**

* 8-11% APR on USDT and USDC
* 0% Management Fee
* 20% Performance Fee
* No Deposit Minimums
* 14-21 Day Withdrawal
* Base & Ethereum Network Support

**These are the yield sources for Zircuit Finance:**

<table><thead><tr><th>Yield Source</th><th width="96.3436279296875">APR</th><th>Strategy</th><th width="137.1475830078125">Withdrawal Liquidity</th><th>Website</th></tr></thead><tbody><tr><td>Monarq</td><td>11-15%</td><td>Quantitative Delta Neutral Fund</td><td>T+14</td><td><a href="https://www.monarq-am.com/">Link</a></td></tr><tr><td>Fidelity</td><td>3-4%</td><td>Money Market Fund</td><td>T+1</td><td><a href="https://institutional.fidelity.com/app/funds-and-products/9053/fidelity-treasury-digital-fund-onchain-class-fyoxx.html">Link</a></td></tr><tr><td>Aave</td><td>2-4%</td><td>DeFi Lending</td><td>Instant</td><td><a href="https://aave.com/">Link</a></td></tr><tr><td>Morpho</td><td>2-4%</td><td>DeFi Lending</td><td>Instant</td><td><a href="https://morpho.org/">Link</a></td></tr></tbody></table>

Zircuit plans to onboard additional strategies for diversification, including:

<table><thead><tr><th width="147.3983154296875">Yield Source</th><th width="130.4322509765625">APR</th><th width="190.9141845703125">Strategy</th><th>Website</th></tr></thead><tbody><tr><td>FalconX</td><td>8-15%</td><td>Lending</td><td><a href="https://www.falconx.io/">Link</a></td></tr><tr><td>RockawayX</td><td>8-10%</td><td>Market Neutral Fund</td><td><a href="https://rockawayx.com/">Link</a></td></tr><tr><td>Forteus</td><td>5-15%</td><td>Multi-manager Delta Neutral Fund</td><td><a href="https://forteus.com/">Link</a></td></tr><tr><td>B2C2</td><td>5-12%</td><td>Options</td><td><a href="https://www.b2c2.com/">Link</a></td></tr></tbody></table>

And also Zircuit plans to adding support for Bitcoin (BTC) and Ethereum (ETH) vaults.


# Zircuit Finance Vaults

Zircuit Finance Vaults lets you deposit funds into a single, universal vault. This vault automatically finds and invests in the best yield-generating strategies across multiple blockchains, handling all the complex cross-chain logic for you. It's designed to be highly modular, making it easy to add new strategies and bridging technologies.

#### Key Features

* **Single-Chain UX, Multi-Chain Execution**: For example, deposit on one chain, earn on another, withdraw to Ethereum
* **Unified Accounting**: One source of truth for vault valuation and share pricing
* **Modular Strategy System**: Easy to add new yield strategies without modifying core vault logic
* **Multi-Bridge Support**: Pluggable bridge adapters (LayerZero, native L2 bridges, etc.)
* **Security-First Design**: Role-based access control, replay protection, queue-based withdrawals

### Architecture Overview

<table><thead><tr><th width="172.07421875">Term</th><th>Definition</th></tr></thead><tbody><tr><td>Accounting Chain</td><td>The single chain that hosts the canonical Vault and maintains price-per-share. Acts as source of truth. </td></tr><tr><td>Strategy Chain</td><td>Any chain where yield strategies are deployed. Has <code>StrategyManager</code> and actual strategy contracts.</td></tr><tr><td>Entrypoint Chain</td><td>Any chain where users can deposit/withdraw. Has <code>VaultToken</code> (shares) and UnderlyingOFTAdapter (asset wrapper).</td></tr><tr><td>VaultToken (e.g. zvUSDC)</td><td>The omnichain-enabled share token representing ownership in the vault (exists on entrypoint chains)</td></tr></tbody></table>

### User Flows

#### Flow 1: Deposit from Entrypoint Chain

{% @mermaid/diagram content="sequenceDiagram
participant User
participant Frontend
participant EC as Entrypoint Chain
participant Zircuit as Accounting Chain<br/>(Zircuit)

```
User->>Frontend: Initiate deposit (1000 USDC)
Frontend->>Frontend: Calculate LayerZero fee
Frontend->>EC: Approve USDC to UnderlyingOFTAdapter
User->>EC: Confirm transaction
Frontend->>EC: UnderlyingOFTAdapter.send(...)
User->>EC: Confirm transaction (with LZ fee)

EC->>Zircuit: LayerZero message
Zircuit->>Zircuit: Mint zvUSDC shares
Zircuit->>EC: Send shares to user

Frontend->>Frontend: Poll for shares balance
Frontend->>User: Show success (950 zvUSDC)" %}
```

#### Flow 2: Withdrawal to Destination Chain

{% @mermaid/diagram content="sequenceDiagram
participant User
participant Frontend
participant EC as Entrypoint Chain
participant Zircuit as Accounting Chain<br/>(Zircuit)

```
User->>Frontend: Initiate withdrawal (500 zvUSDC)
Frontend->>Frontend: Calculate LayerZero fee
Frontend->>EC: VaultToken.send(...)
User->>EC: Confirm transaction

EC->>Zircuit: Send shares to Zircuit
Note over Zircuit: Withdrawal queued<br/>Requires keeper processing

Frontend->>Frontend: Poll withdrawal status
Note over Frontend: May take minutes to hours

Zircuit->>EC: Keeper processes, sends USDC
EC->>User: Receive 526 USDC

Frontend->>User: Show success" %}
```

{% hint style="info" %}
The use of Zircuit as the accounting chain on this page is just as example; the actual accounting chain will differ.
{% endhint %}


# Vault Integration Guide

The OVault SDK simplifies depositing and redeeming tokens on OVaults, which are cross-chain ERC-4626 vaults built on top of LayerZero.

1. [Overview](#overview)
2. [Installation](#installation)
3. [Core Concepts](#core-concepts)
4. [Quick Start](#quick-start)
5. [SDK guide repository](#sdk-guide-repository)
6. [Operations](#operations)
7. [Input Parameter Reference](#input-parameter-reference)
8. [Slippage and Fee Buffer](#slippage-and-fee-buffer)
9. [Tracking Transactions](#tracking-transactions)
10. [Error Reference](#error-reference)

### Overview

* Quoting vault outputs (`previewDeposit` / `previewRedeem`)
* Building LayerZero send parameters including compose messages
* Calculating cross-chain messaging fees
* Determining required token approvals (including EIP-2612 permit support)
* Resolving all contract addresses, EIDs, and decimals from a built-in registry
* Tracking deposits & redeems

### Installation

```bash
npm install @zircuit/ovault-evm
# or
pnpm add @zircuit/ovault-evm
```

### Core Concepts

#### Hub and Spoke Architecture

OVaults use a hub-and-spoke model:

* **Hub chain** — the chain where the ERC-4626 vault contract lives. All deposits and redeems ultimately execute here. The hub chain is always **Base**.
* **Spoke chains** — other supported chains where users hold the underlying asset or vault shares (as OFTs). example: **Ethereum**.
* **Source chain** — the chain the user is initiating the transaction from.
* **Destination chain** — the chain where the user wants to receive tokens after the operation completes.

#### Chain Topology Shorthand

The SDK internally handles four routing scenarios, named by the relationship between source, hub, and destination:

| Scenario | Source = Hub | Destination = Hub | Description                                                                     |
| -------- | ------------ | ----------------- | ------------------------------------------------------------------------------- |
| **BBB**  | Yes          | Yes               | All on the hub chain. Single transaction, no cross-chain messaging.             |
| **BBA**  | Yes          | No                | User is on the hub chain, tokens go to a spoke chain after vault operation.     |
| **ABB**  | No           | Yes               | User is on a spoke chain, tokens arrive on the hub chain after vault operation. |
| **ABA**  | No           | No                | Fully cross-chain: spoke → hub → spoke (or another chain).                      |

#### Simplified Inputs

The SDK provides a built-in address registry for all deployed contracts. You only need to specify chain names, a token name, and the operation.

### Quick Start

```typescript
import { createWalletClient, http, publicActions } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import {
  OVaultSyncMessageBuilder,
  OVaultSyncOperations,
  CHAINS,
} from "@zircuit/ovault-evm";

const account = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

const walletClient = createWalletClient({
  account,
  chain: mainnet,
  transport: http(),
}).extend(publicActions);

// 1. Build the inputs
const inputs = await OVaultSyncMessageBuilder.generateOVaultInputs({
  sourceChain: "ethereum",
  destinationChain: "ethereum",
  token: "usdc",
  operation: OVaultSyncOperations.DEPOSIT,
  amount: "1",
  walletAddress: account.address,
  slippage: 0.01,
  buffer: 0.3,
  referralCode: "ZIRCUIT",
});

// 2. Approve if needed
if (inputs.approval && !inputs.approval.usePermit) {
  const approveTx = await walletClient.writeContract({
    address: inputs.approval.tokenAddress,
    abi: [{
      name: "approve", type: "function", stateMutability: "nonpayable",
      inputs: [{ name: "spender", type: "address" }, { name: "amount", type: "uint256" }],
      outputs: [{ type: "bool" }],
    }],
    functionName: "approve",
    args: [inputs.approval.spender, inputs.approval.amount],
  });
  await walletClient.waitForTransactionReceipt({ hash: approveTx });
}

// 3. Send the main transaction
const txRequest = OVaultSyncMessageBuilder.encodeTransactionRequest(inputs);
const txHash = await walletClient.sendTransaction(txRequest);

console.log("Submitted:", txHash);
```

### SDK Guide Repository

<https://github.com/zircuit-labs/vault-integration-guide>

### Operations

#### Deposit

Set `operation: OVaultSyncOperations.DEPOSIT`.

* The user provides the **underlying asset** amount.
* The vault mints **shares** in exchange.
* Shares are sent cross-chain to the destination chain if different from the hub.

**What `generateOVaultInputs` selects internally:**

| Source = Hub? | EIP-2612? | Contract           | Function                   |
| ------------- | --------- | ------------------ | -------------------------- |
| No            | No        | OFT (`oftAddress`) | `send`                     |
| No            | Yes       | OFT (`oftAddress`) | `sendWithPermit`           |
| Yes           | No        | OVaultComposerSync | `depositAndSend`           |
| Yes           | Yes       | OVaultComposerSync | `depositAndSendWithPermit` |

#### Redeem

Set `operation: OVaultSyncOperations.REDEEM`.

* The user provides the **share** amount to burn.
* The vault returns the **underlying asset** in exchange.
* Assets are sent cross-chain to the destination chain if different from the hub.

**What `generateOVaultInputs` selects internally:**

| Source = Hub? | Contract           | Function        |
| ------------- | ------------------ | --------------- |
| No            | VaultToken OFT     | `send`          |
| Yes           | OVaultComposerSync | `redeemAndSend` |

**Withdrawal queue**

Redemptions are **not executed immediately**. When the user calls `redeemAndSend` (either directly on the hub or via a cross-chain compose), the VaultComposer enqueues the request into a **withdrawal queue** instead of redeeming from the vault in the same transaction.

**How it works:**

1. The user's shares are transferred into the VaultComposer and a `WithdrawalRequest` event is emitted with a queue `index`.
2. A **keeper** (an account with the `WITHDRAWAL_MANAGER` role) calls `processWithdrawals(ids, extraFees)` to batch-fulfill one or more queued entries. This is the transaction that actually calls `vault.redeem()` and sends the resulting underlying assets to the user's destination chain.
3. The user may **cancel** a pending withdrawal by calling `cancelWithdrawal(id)`, which returns their shares and prepaid fees. Cancellation is only possible before the keeper has processed the entry.

### Input Parameter Reference

All parameters are passed as a single `OVaultCoreInputs` object to `generateOVaultInputs`.

#### Required

| Parameter          | Type                   | Description                                                                         |
| ------------------ | ---------------------- | ----------------------------------------------------------------------------------- |
| `sourceChain`      | `"ethereum" \| "base"` | Chain where the user submits the transaction.                                       |
| `destinationChain` | `"ethereum" \| "base"` | Chain where the user wants to receive tokens.                                       |
| `token`            | `"usdc" \| "usdt"`     | Token to deposit or redeem.                                                         |
| `operation`        | `OVaultSyncOperations` | `DEPOSIT` or `REDEEM`.                                                              |
| `amount`           | `string`               | Human-readable unscaled amount (e.g. `"1.23"`).                                     |
| `walletAddress`    | `` `0x${string}` ``    | The user's wallet address. Also used as the default refund and destination address. |

#### Optional

| Parameter              | Type                                          | Default         | Description                                                                                              |
| ---------------------- | --------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
| `dstAddress`           | `` `0x${string}` ``                           | `walletAddress` | Where tokens are delivered on the destination chain.                                                     |
| `slippage`             | `number`                                      | `0.01`          | Fractional slippage tolerance. Minimum `0.001` (0.1%). Example: `0.01` = 1%.                             |
| `buffer`               | `number`                                      | `0.3`           | Additional fractional buffer on the LayerZero messaging fee. `0.3` = +30%.                               |
| `supportsEip2612`      | `boolean`                                     | `false`         | Attempt gasless permit instead of a separate `approve` tx. Only applies to deposits.                     |
| `hubLzComposeGasLimit` | `bigint`                                      | `300_000n`      | Gas limit for the `lzCompose` call on the hub chain.                                                     |
| `referralCode`         | `string`                                      | —               | Optional referral code forwarded via `oftCmd` (max 32 bytes).                                            |
| `rpcUrls`              | `Partial<Record<SupportedChainName, string>>` | —               | Custom RPC URLs keyed by chain name (e.g. `{ ethereum: "https://..." }`). Avoids public RPC rate limits. |

### Tracking Transactions

After submitting a transaction, use `trackOVaultSyncTransaction` to poll the cross-chain status:

```typescript
import { trackOVaultSyncTransaction, CHAINS } from "@zircuit/ovault-evm";

const status = await trackOVaultSyncTransaction(
  txHash,
  {
    sourceChain: CHAINS.ethereum.viemChain,
    hubChain:    CHAINS.base.viemChain,
    dstChain:    CHAINS.ethereum.viemChain,
  },
);

console.log(status.step);
```

#### Transaction Steps

The returned `step` is an `OVaultTransactionStep` enum value:

| Step                           | Description                                                           |
| ------------------------------ | --------------------------------------------------------------------- |
| `SOURCE_CHAIN_TRANSACTION`     | The source-chain transaction is pending or failed.                    |
| `SOURCE_TO_HUB_LZ_TRANSACTION` | Waiting for LayerZero to deliver the message to the hub.              |
| `HUB_CHAIN_TRANSACTION`        | The hub-chain compose is pending, executing, or failed.               |
| `HUB_TO_DST_LZ_TRANSACTION`    | Waiting for LayerZero to deliver the output to the destination chain. |
| `DST_CHAIN_TRANSACTION`        | The destination-chain receive is pending.                             |
| `COMPLETED`                    | The full operation is complete.                                       |

#### Returned Fields

```typescript
{
  step: OVaultTransactionStep;
  failureReason?: OVaultFailureReason; // "unknown" — the only value currently
  refunded?: boolean;                  // true if the hub refunded the user's tokens
  destinationTxHash?: `0x${string}`;   // final tx hash on the destination chain
  hubTxHash?: `0x${string}`;           // compose tx hash on the hub chain
}
```

### Slippage and Fee Buffer

#### Slippage

`slippage` controls the minimum acceptable output from the vault operation. It applies to the vault's `previewDeposit` / `previewRedeem` quote:

```
minDstAmount = previewOutput - (previewOutput × slippage)
```

The contract reverts with `SlippageExceeded` if the actual output is below `minDstAmount`.

* Minimum allowed: `0.001` (0.1%)
* Default: `0.01` (1%)

#### Fee Buffer

`buffer` adds a safety margin on top of the quoted LayerZero messaging fee. This protects against fee fluctuations between quote time and execution time:

```
effectiveFee = quotedFee + (quotedFee × buffer)
```

* Default: `0.3` (30%)
* Any excess native fee is refunded to `walletAddress` by LayerZero.

#### Tracking Withdrawals

When multiple redemptions are processed in a single transaction, pass `withdrawalInfo` so the tracker selects the correct LayerZero message:

```typescript
const status = await trackOVaultSyncTransaction(
  txHash,
  { sourceChain: CHAINS.base.viemChain, hubChain: CHAINS.base.viemChain, dstChain: CHAINS.ethereum.viemChain },
  {
    index:     withdrawalIndex,   // bigint | number | string
    initiator: walletAddress,     // address that initiated the redeem
  },
);
```

### Error Reference

#### SDK-level errors (thrown before transaction submission)

| Error message                                         | Cause                                                                                      |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `"Unsupported token: <token>"`                        | The token is not in the built-in registry.                                                 |
| `"Token <token> is not deployed on <chain>"`          | The token/chain combination does not exist in the registry.                                |
| `"Amount is too small"`                               | After LayerZero dust removal, the scaled amount rounded down to zero. Increase the amount. |
| `"Output amount is too small"`                        | `previewDeposit` / `previewRedeem` returned zero. The amount is below the vault's minimum. |
| `"Slippage must be greater or equal to 0.001 (0.1%)"` | `slippage` was set below the minimum.                                                      |
| `"inputs cannot be null"`                             | `amount` or `tokenHubDecimals` was `null` / `undefined`.                                   |


# Zircuit Finance Addresses

***

## **Base**

**USDC**

| Contract           | Address                                    |
| ------------------ | ------------------------------------------ |
| AccountingReceiver | 0x7fCeB53b2959861D29057361158a2B41cAAffD68 |
| StrategyManager    | 0xf7E745658fa6f1fe8f2CAb47861a273991Cd3374 |
| UnderlyingOFT      | 0xd7aBC360dfcF1B6dD0a03138235e12A2bc1c1C8B |
| VaultComposer      | 0xBb801ED781dF31F660cC743bEf7Bb9D04B030923 |
| Vault              | 0x03067bbD0d41E3Fe4A0bb6ca67c99e7352Da4CAE |

**USDT**

| Contract           | Address                                    |
| ------------------ | ------------------------------------------ |
| AccountingReceiver | 0x43c4a95788997a3E02F6Ca7E5CC4c23dbDE66c9C |
| StrategyManager    | 0x075193D36693DA7BA3Bb709cF63bEf070BA04D94 |
| UnderlyingOFT      | 0x264D6474802Ef8bc1bc05F89F7d640D1E93c5330 |
| VaultComposer      | 0x16cE6D9576A411911e62b6073F1cc9d1347ad96B |
| Vault              | 0x25d90ABd6c1E8DCCD40932D2fdD2Cd381bfc832D |

***

## **Ethereum**

**USDC**

| Contract             | Address                                    |
| -------------------- | ------------------------------------------ |
| StrategyManager      | 0xf7E745658fa6f1fe8f2CAb47861a273991Cd3374 |
| UnderlyingOFTAdapter | 0xd58E8c1c83d598aD76B5f0E26B4a25cdB885d190 |
| VaultToken           | 0x07C898e77310870770f88d18a01009cB65A1c1a9 |

**USDT**

| Contract             | Address                                    |
| -------------------- | ------------------------------------------ |
| StrategyManager      | 0x075193D36693DA7BA3Bb709cF63bEf070BA04D94 |
| UnderlyingOFTAdapter | 0x2D342De4c58a871b3525740C58a1C112D5835865 |
| VaultToken           | 0x8ADbeA709B31A564f9750A280EC2690aD1cf470A |

## **Institutional Contracts**

| Description               | Address                                    | Network  |
| ------------------------- | ------------------------------------------ | -------- |
| Monarq USDC Lender        | 0xe83ef4375d806c02387069f1b753b2ab76ab1dc5 | Base     |
| Monarq USDT Lender        | 0x1a48cec817bcb5436efe99bab6dde228cc37e1cc | Base     |
| Fidelity FDIT ERC20-Token | 0x48aB4e39AC59F4E88974804B04A991b3a402717f | Ethereum |


# Security

**Audit Reports**

{% file src="/files/8WbnyPY4KEeLGmmMF30y" %}

{% file src="/files/wLPZj6mL3u0oX9QM810E" %}

{% file src="/files/Srhgb7ihJVb0k3xmfbhH" %}

{% file src="/files/TSFoK3hLVjqdXNMcRGPo" %}

{% file src="/files/KCpiPAdrIK8441NH5cNK" %}


# Quick Start

{% hint style="info" icon="triangle-exclamation" %}

## Note that [SLS](https://docs.zircuit.com/info/architecture/sls) is not enabled while we [focus](https://www.zircuit.com/blog/a-new-chapter-for-zircuit-from-l2-to-defi) on Zircuit Finance. Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

## Connecting to Zircuit

There are only a few simple steps to get started with Zircuit:

1. Install a Browser Wallet
2. Add Zircuit Network to Your Wallet
3. Get Ether (ETH)
4. Deposit ETH from Ethereum to Zircuit (L1 -> L2)

**Install a Browser Wallet**

First, you'll need to Ethereum wallet such as [MetaMask](https://metamask.io/) or [Rabby](https://rabby.io/). Add the corresponding extension to your browser.

## Add Zircuit to Your Wallet

To start using Zircuit, you need to add the chain's RPC endpoint to your wallet. Navigate to the corresponding Chainlist page and add the Zircuit Network directly.

<figure><img src="/files/Tuw6pCgDsicK3TKAim3E" alt=""><figcaption></figcaption></figure>

|                 | Zircuit Mainnet                     | Garfield Testnet                                 |
| --------------- | ----------------------------------- | ------------------------------------------------ |
| RPCs            | <https://chainlist.org/chain/48900> | <https://chainlist.org/chain/48898>              |
| Chain ID        | 48900                               | 48898                                            |
| Currency Symbol | ETH                                 | ETH                                              |
| Block Explorer  | <https://explorer.zircuit.com>      | <https://explorer.garfield-testnet.zircuit.com/> |


# Bridge to Zircuit

{% hint style="info" icon="triangle-exclamation" %}

## Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

## Mainnet

Zircuit uses ETH for gas. If you don't already have some in your wallet, you'll need to acquire some

{% embed url="<https://ethereum.org/en/get-eth/>" %}

### Deposit ETH from Ethereum to Zircuit (L1 -> L2)

You can bridge your ETH to Zircuit via the Zircuit Bridge. To do so, simply:

1. Connect your wallet on <https://bridge.zircuit.com/>
2. Make sure you're connected to Ethereum
3. Enter how much ETH you would like to bridge to Zircuit
4. Click `Bridge`
5. Review the transaction and click `Approve transaction in your wallet`
6. Click `Confirm` in the MetaMask popup
7. After a few seconds, your transaction should be confirmed, and you should see the updated balance

## Garfield Testnet

If you are deploying on Zircuit Garfield Testnet, you can get Testnet ETH from the faucet located [here](https://ethglobal.com/faucet/zircuit-garfield-testnet-48898).

{% hint style="info" %}
If you get Garfield Testnet ETH this way, you do not need to bridge.
{% endhint %}

If you have [Sepolia ETH](https://chainstack.com/sepolia-faucet/) and would like to bridge:

### Deposit ETH from Sepolia to Garfield Testnet (L1 -> L2)

You can bridge your Sepolia ETH to Garfield Testnet via the Zircuit Bridge. To do so, simply:

1. Connect your wallet on <https://bridge.garfield-testnet.zircuit.com/>
2. Make sure you're connected to Sepolia
3. Enter how much Sepolia ETH you would like to bridge to Zircuit
4. Click `Bridge`
5. Review the transaction and click `Approve transaction in your wallet`
6. Click `Confirm` in the popup
7. After a few seconds, your transaction should be confirmed, and you should see the updated balance


# Deploy on Zircuit

A tutorial for deploying a smart contract on Zircuit using Foundry

{% hint style="info" icon="triangle-exclamation" %}

## Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

This tutorial will guide you through the complete process of setting up Foundry, creating a smart contract, and deploying it to the Zircuit testnet. Foundry is a powerful, fast, and portable toolkit for Ethereum application development written in Rust.

### Prerequisites

Before starting, ensure you have:

* A Unix-like operating system (macOS, Linux, or WSL on Windows)
* Git installed
* A wallet with some ETH on Zircuit
* Basic understanding of Solidity and blockchain concepts

### What is Foundry?

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development. It consists of:

* **Forge**: Ethereum testing framework (like Truffle, Hardhat)
* **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data
* **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network
* **Chisel**: Fast, utilitarian, and verbose Solidity REPL

{% stepper %}
{% step %}
**Set Up Foundry**

First, we'll install Foundry using the official installer script:

```bash
# Download and install Foundry
curl -L https://foundry.paradigm.xyz | bash

# Reload your shell configuration to make foundryup available
source ~/.zshrc  # for zsh users
# OR
source ~/.bashrc  # for bash users

# Install the latest version of Foundry tools
foundryup
```

**What this does:**

* Downloads the Foundry installer script
* Installs `foundryup`, the Foundry toolchain installer
* Updates your shell to recognize the new commands
* Installs or updates `forge`, `cast`, `anvil`, and `chisel`

**Verify installation:**

```bash
forge --version
```

{% endstep %}

{% step %}
**Create a New Counter Project**

Initialize a new Foundry project with the counter template:

```bash
# Create a new Foundry project called 'counter'
forge init counter

# Navigate into the project directory
cd counter
```

**What this creates:**

```
counter/
├── lib/               # Dependencies (like node_modules)
├── script/            # Deployment scripts
│   └── Counter.s.sol  # Deployment script
├── src/               # Smart contract source files
│   └── Counter.sol    # Main contract
├── test/              # Test files
│   └── Counter.t.sol  # Contract tests
└── foundry.toml       # Foundry configuration file
```

{% endstep %}

{% step %}
**Set Up Your Private Key for Deployment**

{% hint style="warning" %}
Never use your mainnet private key for testing. Always use a separate wallet for testnet development.
{% endhint %}

You need to securely store your private key for contract deployment:

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast wallet import mainnetKey --interactive
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast wallet import testnetKey --interactive
```

{% endtab %}
{% endtabs %}

**What this does:**

* Prompts you to enter your private key securely (won't display on screen)
* Encrypts and stores the key locally under the name "defaultKey"
* Requires a password to encrypt the stored key
  {% endstep %}

{% step %}
**Compile Your Smart Contract**

Compile all contracts in your project:

```bash
forge compile
```

**What this does:**

* Compiles all `.sol` files in the `src/` directory
* Generates ABI (Application Binary Interface) files
* Creates bytecode for deployment
* Checks for compilation errors and warnings
* Outputs artifacts to `out/` directory

**Expected output:**

```
[⠊] Compiling...
[⠃] Compiling 23 files with Solc 0.8.30
[⠊] Solc 0.8.30 finished in 889.13ms
Compiler run successful!
```

{% endstep %}

{% step %}
**Review the Contract and Deployment Script**

Let's examine the smart contract and deployment script in detail:

**src/Counter.sol**

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
    // State variable to store the counter value
    uint256 public number;

    // Function to set the counter to a specific value
    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    // Function to increment the counter by 1
    function increment() public {
        number++;
    }
}
```

**Contract explanation:**

* `uint256 public number`: A public state variable that automatically generates a getter function
* `setNumber()`: Allows setting the counter to any value
* `increment()`: Increases the counter by 1
* All functions are `public`, meaning anyone can call them

{% code title="script/Counter.s.sol" %}

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script} from "forge-std/Script.sol";
import {Counter} from "../src/Counter.sol";

contract CounterScript is Script {
    Counter public counter;

    function setUp() public {}

    function run() public {
        // Start recording transactions for broadcast
        vm.startBroadcast();
        
        // Deploy the Counter contract
        counter = new Counter();
        
        // Stop recording transactions
        vm.stopBroadcast();
    }
}
```

{% endcode %}

**Deployment script explanation:**

* Inherits from `Script` to access deployment utilities
* `vm.startBroadcast()`: Begins recording transactions to broadcast to the network
* `new Counter()`: Deploys a new instance of the Counter contract
* `vm.stopBroadcast()`: Stops recording transactions
  {% endstep %}

{% step %}
**Deploy Your Smart Contract**

Customize your `foundry.toml` for different networks:

```toml
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# Zircuit RPC configuration
[rpc_endpoints]
zircuit_mainnet = "https://mainnet.zircuit.com"
garfield_testnet = "https://garfield-testnet.zircuit.com"
```

Deploy the contract to Zircuit

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
forge script script/Counter.s.sol:CounterScript \
    --rpc-url zircuit_mainnet \
    --account mainnetKey \
    --broadcast
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
forge script script/Counter.s.sol:CounterScript \
    --rpc-url garfield_testnet \
    --account testnetKey \
    --broadcast
```

{% endtab %}
{% endtabs %}

**Command breakdown:**

* `forge script`: Command to run deployment scripts
* `script/Counter.s.sol:CounterScript`: Path to script file and contract name
* `--rpc-url`: The RPC endpoint
* `--account <key>`: Uses the imported private key
* `--broadcast`: Actually sends transactions to the network

**Expected output (Garfield Testnet):**

```
[⠊] Compiling...
No files changed, compilation skipped
Enter keystore password:
Script ran successfully.

## Setting up 1 EVM.

==========================

Chain 48898

Estimated gas price: 0.000000509 gwei

Estimated total gas used for script: 203856

Estimated amount required: 0.000000000103762704 ETH

==========================

##### 48898
✅  [Success] Hash: 0x59bf94e4055ee2c4a71b9e6a7b7589ad3a5831ac38717c5f0d488eb4ed365a77
Contract Address: 0x6E69d4f9bc6a3E2f67d2D86877800482A8cdca40
Block: 8549829
Paid: 0.000000000039987315 ETH (156813 gas * 0.000000255 gwei)

✅ Sequence #1 on 48898 | Total Paid: 0.000000000039987315 ETH (156813 gas * avg 0.000000255 gwei)
```

**Copy the Contract Address** from the output - you'll need it for the next step!
{% endstep %}

{% step %}
**Interact with Your Smart Contract**

Now that your contract is deployed, you can interact with it using `cast`.

**Execute a State-Changing Function**

To call the `increment()` function (which costs gas):

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast send <CONTRACT_ADDRESS> "increment()" \
    --rpc-url https://mainnet.zircuit.com \
    --account mainnetKey
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast send <CONTRACT_ADDRESS> "increment()" \
    --rpc-url https://garfield-testnet.zircuit.com \
    --account testnetKey
```

{% endtab %}
{% endtabs %}

**Replace `<CONTRACT_ADDRESS>` with your actual contract address from step 6.**

**What this does:**

* Sends a transaction to call the `increment()` function
* Uses your imported private key to sign the transaction
* Pays gas fees for the transaction

**Expected output (Garfield Testnet):**

```
blockHash            0x97162a12dc900daf598e18c7a026d0b7bea5b121fc20bd99600292b53ba8148b
blockNumber          8550305
contractAddress      
cumulativeGasUsed    91965
effectiveGasPrice    255
from                 0xbd9B49deFc88AC16D7fC0F7FE6Eb7E0F54F6317f
gasUsed              43482
...
```

**Read Contract State**

To read the current value of `number` (free, no gas required):

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast call <CONTRACT_ADDRESS> "number()" \
    --rpc-url https://mainnet.zircuit.com
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast call <CONTRACT_ADDRESS> "number()" \
    --rpc-url https://garfield-testnet.zircuit.com
```

{% endtab %}
{% endtabs %}

**Expected output:**

```
0x0000000000000000000000000000000000000000000000000000000000000001
```

This hexadecimal output represents the number `1`, showing that our increment worked!

**To convert hex to decimal:**

```bash
cast --to-dec 0x0000000000000000000000000000000000000000000000000000000000000001
# Output: 1
```

**Additional Interaction Examples**

Set the counter to a specific value:

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast send <CONTRACT_ADDRESS> "setNumber(uint256)" 42 \
    --rpc-url https://mainnet.zircuit.com \
    --account mainnetKey
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast send <CONTRACT_ADDRESS> "setNumber(uint256)" 42 \
    --rpc-url https://garfield-testnet.zircuit.com \
    --account testnetKey
```

{% endtab %}
{% endtabs %}

Check the balance of your deployer address:

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast balance <YOUR_WALLET_ADDRESS> \
    --rpc-url https://mainnet.zircuit.com
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast balance <YOUR_WALLET_ADDRESS> \
    --rpc-url https://garfield-testnet.zircuit.com
```

{% endtab %}
{% endtabs %}

Get transaction details:

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
cast tx <TRANSACTION_HASH> \
    --rpc-url https://mainnet.zircuit.com
```

{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
cast tx <TRANSACTION_HASH> \
    --rpc-url https://garfield-testnet.zircuit.com
```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Verifying Contracts**

After doing programmatic interactions with the smart contract, you can also verify the contract using `forge`.

{% tabs %}
{% tab title="Zircuit Mainnet" %}

```bash
forge verify-contract <CONTRACT_ADDRESS> <SOURCE_FILE>:<CONTRACT_NAME> \
--chain-id 48900 \
--verifier sourcify \
--verifier-url https://sourcify.dev/server
```

[https://explorer.zircuit.com/address](https://explorer.zircuit.com/address/)/\<CONTRACT\_ADDRESS>
{% endtab %}

{% tab title="Garfield Testnet" %}

```bash
forge verify-contract <CONTRACT_ADDRESS> <SOURCE_FILE>:<CONTRACT_NAME> \
--chain-id 48898 \
--verifier sourcify \
--verifier-url https://sourcify.dev/server
```

<https://explorer.garfield-testnet.zircuit.com/address>/\<CONTRACT\_ADDRESS>
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}
**Advanced Features**

**Running Tests**

Foundry includes a powerful testing framework:

```bash
# Run all tests
forge test

# Run tests with verbose output
forge test -vvv

# Run specific test
forge test --match-test testIncrement
```

**Code Coverage**

Generate test coverage reports:

```bash
forge coverage
```

**Gas Reporting**

Get detailed gas usage reports:

```bash
forge test --gas-report
```

**Gas Reporting**

Get detailed gas usage reports:

```bash
forge test --gas-report
```

{% endstep %}
{% endstepper %}


# Run Zircuit

{% hint style="info" icon="triangle-exclamation" %}

## Note that [SLS](https://docs.zircuit.com/info/architecture/sls) is not enabled while we [focus](https://www.zircuit.com/blog/a-new-chapter-for-zircuit-from-l2-to-defi) on Zircuit Finance. Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

## Overview

Run your own Public Zircuit Node or Partner Zircuit Node on Mainnet or Garfield-Testnet. This guide covers setup, configuration, and operations for both public and partner node operators.

### Prerequisites

#### System Requirements

* **OS**: Linux or macOS
* **Python**: 3.11 or higher
* **Storage**:
  * **Type**: Solid State
  * **Garfield-Testnet**: 200-300 GB initially (grows over time as network progresses)
  * **Mainnet**: 750-1000 GB initially (grows over time as network progresses)
* **CPU**: 4 cores recommended
* **RAM**: 16 GB recommended
* **Software**:
  * Docker + Docker Compose (required),
  * lz4 (for snapshot decompression, required),
  * aria2c (for accelerating snapshot downloads, optional but **strongly recommended**)
  * sha256 utils (for verifying snapshot integrity, required)

For testing on a desktop computer, both Docker Desktop (with Compose extension) and Podman Desktop (with Compose extension) should work.

## Quick Start

### Get the Script

Make a new directory and download the node controller script to it:

```bash
mkdir -p my-public-zircuit-node
cd my-public-zircuit-node
curl -sS https://bootstrap.mainnet.zircuit.com/zircuit -o zircuit
chmod +x zircuit
```

{% hint style="info" %}
The script doesn't support to be run as the `root` user. Use a regular user instead.
{% endhint %}

### Initialize as a Public Node

#### **Testnet (default)**

```bash
./zircuit init
```

#### **Mainnet**

```bash
./zircuit init --network mainnet
```

The above commands will:

* Synchronize the latest chaindata [snapshot](#snapshots).
* Generate node identity files (JWT secret, P2P key).
* Create configuration files for use with Docker Compose.
* Request a unique node name from our bootstrap service.
* Load network-specific configuration from embedded defaults and remote bootstrap server.

After initialization, your directory will contain:

```
test-node/
├── docker-compose.yml          # Docker services configuration
├── env-l2geth                  # Environment variables for l2geth
├── env-op_node                 # Environment variables for op-node
├── env-partner                 # Environment variables for partner mode, only if partner mode is enabled
├── env-zircuit                 # Environment variables for zircuit
├── gethconfig.toml             # Geth configuration
├── jwt-secret.txt              # Authentication secret
├── l2-replica-data/            # l2geth data directory
├── op-replica-log/             # op-node data directory
├── p2p-node-key.txt            # P2P identity key
├── zircuit-garfield-...tar.lz4 # Downloaded snapshot (compressed)
├── zircuit-garfield-...tar.txt # Fingerprint (sha256) for downloaded snapshot
└── zircuit                     # Node controller script
```

<figure><img src="/files/MttiBKauTcrJCSP9NuIZ" alt=""><figcaption></figcaption></figure>

### Start Node

```bash
./zircuit up
```

This command will:

* Check that the Docker daemon is running.
* Start the Docker containers (l2geth and op-node).
* Begin syncing with the Zircuit network.
* Connect to P2P peers.
* Start serving RPC endpoints.

#### Monitor Logs

```bash
./zircuit logs
```

This shows the last 1000 lines and then follows (live-tails) new logs. Press `Ctrl+C` to exit.

### Available Commands

| Command             | Description                                      |
| ------------------- | ------------------------------------------------ |
| `./zircuit init`    | Initialize the node from scratch                 |
| `./zircuit sync`    | Sync a new chaindata snapshot                    |
| `./zircuit up`      | Start the node                                   |
| `./zircuit down`    | Stop the node                                    |
| `./zircuit restart` | Restart the node                                 |
| `./zircuit logs`    | View the node logs (last 1000 lines + live tail) |
| `./zircuit update`  | Update the node version and node configuration   |

### Configuration Options

#### General Options

```bash
./zircuit --help
```

Key options:

* `-d|--debug`: Raises the log level from INFO to DEBUG.

#### Initialization Options

```bash
./zircuit init --help
```

Key options:

* `-env|-n KEY=VALUE`: Sets extra environment variables for both l2geth and op-node.
* `--force`: Forces complete (re-)initialization of Zircuit Node.
* `--network|-n garfield-testnet|mainnet`: Selects the Zircuit network to operate in. (default: `garfield-testnet`)
* `--mode|-m public|partner` : Select the mode to operate in. (default: `public`)
* `--skip-snapshot-sync`: Skips downloading the latest snapshot.
* `--snapshot-name`: Use an already downloaded snapshot.
* `--version <VERSION>`: Overrides the version of Zircuit Node to run.
* `--version-stream release|pre-release`: Sets the version stream to follow. (default: `release`)

**Examples:**

```bash
# Default initialization
./zircuit init

# Initialize without downloading snapshot
./zircuit init --skip-snapshot-sync

# Initialize with existing snapshot
./zircuit init --skip-snapshot-sync --snapshot-name snapshot-garfield.tar.lz4

# Initialize mainnet node
./zircuit init --network mainnet
```

### Public vs. Partner Nodes

#### **Public Nodes (Default)**

* Ready to use without additional configuration.
* Uses public L1 RPC endpoints (rate-limited).
* Defaults to Garfield-Testnet (use `--network mainnet` for Mainnet).
* Configuration loaded from embedded defaults and bootstrap server

#### **Partner Nodes (Advanced)**

```bash
./zircuit init --mode partner
```

Requires configuration of:

* Node name for ETH Stats (if integration is enabled)
* ETH Stats shared secret (if integration is enabled)
* Custom L1 RPC endpoint
* Custom L1 Beacon URL
* Zircuit L2 RPC endpoint

For the L1 endpoints, you may set the endpoints provided by PublicNode but the same performance caveat applies.

## Important Notes

### Performance Warnings

The script will display warnings when using public infrastructure:

* "Your Zircuit Node is using a rate-limited L1 RPC endpoint"
* "Your Zircuit Node is using a rate-limited L1 Beacon endpoint"

For optimal performance, consider configuring dedicated L1 endpoints.

### Updates

The node automatically checks for updates every 30 minutes (configurable). When updates are available:

1. Check logs for update notifications
2. Run `./zircuit update`
3. Run `./zircuit restart` to apply changes

### Snapshots

Snapshots are provided by Liquify and automatically managed:

* Latest snapshots are automatically downloaded during initialization
* SHA256 fingerprint verification ensures integrity
* Snapshots use lz4 compression for faster downloads
* Download methods:
  * **aria2c**: Used if detected on system (faster, parallel downloads)
  * **Built-in Python**: Fallback method if aria2c not available (fallback, more compatible)
* Optionally skip download with `--skip-snapshot-sync`
* Optionally use existing snapshot with `--snapshot-name`

### Configuration Cascade

The node uses a configuration cascade system:

1. **Embedded defaults**: Network-specific public bootstrap configuration built into the script.
2. **Remote configuration**: Network-specific configuration fetched from the bootstrap server.
3. **Local overrides**: Custom settings via command-line options or environment variables.

This ensures nodes always have working defaults while allowing customization and remote updates.

## Troubleshooting

### Common Issues

* **Docker not running**: Ensure Docker daemon is started.
* **Insufficient disk space**: Ensure sufficient free space (see [System Requirements](https://app.gitbook.com/o/FAE3Bv5wcSjxEUOFI86x/s/p2pPzGBdConDaqw5tnHs/~/changes/379/build/run-a-zircuit-node#system-requirements) above).
* **Missing lz4**: Install lz4 for snapshot decompression.
* **Network connectivity**: Check firewall settings for P2P ports.
* **Interrupted snapshot download**: The built-in Python download method does NOT support resuming interrupted downloads. If your download fails, you must restart from the beginning. To avoid this:
  * Install `aria2c` for more reliable downloads with support for parallel downloads and resume.
  * Ensure stable network connection before starting large downloads (especially Mainnet's \~200 GB snapshot)
  * Consider using `--skip-snapshot-sync` and downloading the snapshot manually (see below).
* **429 Too Many Requests**: If you're seeing this message in the op-node logs, you need to get a PublicNode token. Sign up at <https://www.allnodes.com/publicnode>, generate links for Sepolia RPC and Sepolia Beacon APIs, and re-initialize your Zircuit Node with the following two additional enviromnent variables:

  * `OP_NODE_L1_BEACON`, and
  * `OP_NODE_L1_ETH_RPC` .

  For example, if the links generated by AllNodes are:

  * `https://ethereum-sepolia-beacon-api.publicnode.com/d7d55ad98cbb19d147fa42a559ee7ebd02e2026115fb5b819dae81db3b25f544,`and
  * `https://ethereum-sepolia-rpc.publicnode.com/d7d55ad98cbb19d147fa42a559ee7ebd02e2026115fb5b819dae81db3b25f544`

  then add the following to your `init` command: `-e OP_NODE_L1_BEACON=https://ethereum-sepolia-beacon-api.publicnode.com/d7d55ad98cbb19d147fa42a559ee7ebd02e2026115fb5b819dae81db3b25f544 -e OP_NODE_L1_ETH_RPC=https://ethereum-sepolia-rpc.publicnode.com/d7d55ad98cbb19d147fa42a559ee7ebd02e2026115fb5b819dae81db3b25f544` . (Note that these aren't real tokens and **will not work**.)

### Manual Snapshot Download

If you experience issues with automatic snapshot downloads, you can download snapshots manually:

#### **Garfield-Testnet**

```bash
# Get the latest snapshot filename
SNAPSHOT=$(curl -sS https://zircuit-snapshot.liquify.com/files/garfield-testnet/latest_compressed_zircuit.txt)

# Download the snapshot
wget https://zircuit-snapshot.liquify.com/files/garfield-testnet/$SNAPSHOT
# or use aria2c for resume support:
aria2c -x 16 -s 16 https://zircuit-snapshot.liquify.com/files/garfield-testnet/$SNAPSHOT

# Download the verification file
wget https://zircuit-snapshot.liquify.com/files/garfield-testnet/$SNAPSHOT.sha256

# Initialize with the downloaded snapshot
./zircuit init --skip-snapshot-sync --snapshot-name $SNAPSHOT
```

#### **Mainnet**

```bash
# Get the latest snapshot filename
SNAPSHOT=$(curl -sS https://zircuit-snapshot.liquify.com/files/mainnet/latest_compressed_zircuit.txt)

# Download the snapshot
wget https://zircuit-snapshot.liquify.com/files/mainnet/$SNAPSHOT
# or use aria2c for resume support (recommended for 200 GB download):
aria2c -x 16 -s 16 https://zircuit-snapshot.liquify.com/files/mainnet/$SNAPSHOT

# Download the verification file
wget https://zircuit-snapshot.liquify.com/files/mainnet/$SNAPSHOT.sha256

# Initialize with the downloaded snapshot
./zircuit init --network mainnet --skip-snapshot-sync --snapshot-name $SNAPSHOT
```

**Note:** The `aria2c` command uses 16 connections (`-x 16`) and splits the download into 16 segments (`-s 16`) for faster downloads and automatic resume on interruption.

### Network Information

<table><thead><tr><th width="144.4921875"></th><th width="324.8515625" valign="middle">Mainnet</th><th>Garfield-Testne</th></tr></thead><tbody><tr><td>Network Name</td><td valign="middle">Zircuit Mainnet</td><td>Zircuit Testnet</td></tr><tr><td>Chain ID</td><td valign="middle">48900</td><td>48898</td></tr><tr><td>Snapshot Size</td><td valign="middle">~200 GB (compressed)</td><td>~60 GB (compressed)</td></tr><tr><td>L1</td><td valign="middle">Ethereum Mainnet</td><td>Ethereum Sepolia</td></tr><tr><td>L1 RPC</td><td valign="middle"><a href="https://ethereum-rpc.publicnode.com/">https://ethereum-rpc.publicnode.com</a></td><td><a href="https://ethereum-sepolia-rpc.publicnode.com">https://ethereum-sepolia-rpc.publicnode.com</a></td></tr><tr><td>L1 Beacon RPC</td><td valign="middle"><a href="https://ethereum-beacon-api.publicnode.com/">https://ethereum-beacon-api.publicnode.com</a></td><td><a href="https://ethereum-sepolia-beacon-api.publicnode.com/">https://ethereum-sepolia-beacon-api.publicnode.com</a></td></tr><tr><td>L2 RPC</td><td valign="middle"><a href="https://mainnet.zircuit.com/">https://mainnet.zircuit.com</a></td><td><a href="https://garfield-testnet.zircuit.com/">https://garfield-testnet.zircuit.com</a></td></tr><tr><td>Public RPC</td><td valign="middle"><a href="https://mainnet.zircuit.com/">https://mainnet.zircuit.com</a></td><td><a href="https://garfield-testnet.zircuit.com/">https://garfield-testnet.zircuit.com</a></td></tr><tr><td>Ethstats Servers</td><td valign="middle"><ul><li><a href="https://ethstats-public.mainnet.zircuit.com/">https://ethstats-public.mainnet.zircuit.com</a></li><li><a href="https://ethstats.mainnet.zircuit.com/">https://ethstats.mainnet.zircuit.com</a></li></ul></td><td><ul><li><a href="https://ethstats-public.garfield-testnet.zircuit.com/">https://ethstats-public.garfield-testnet.zircuit.com</a></li><li><a href="https://ethstats-us.garfield-testnet.zircuit.com/">https://ethstats-us.garfield-testnet.zircuit.com</a></li></ul></td></tr><tr><td>Bootstrap Server</td><td valign="middle">https://bootstrap.mainnet.zircuit.com</td><td>https://bootstrap.garfield-testnet.zircuit.com</td></tr><tr><td>P2P Peers</td><td valign="middle"><ul><li>node1-eu-p2p.mainnet.zircuit.com</li><li>node2-eu-p2p.mainnet.zircuit.com</li><li>node3-eu-p2p.mainnet.zircuit.com</li></ul></td><td><ul><li>node1-us-p2p.garfield-testnet.zircuit.com</li><li>node2-us-p2p.garfield-testnet.zircuit.com</li><li>node1-eu-p2p.garfield-testnet.zircuit.com</li></ul></td></tr></tbody></table>

## Need Help?

If you encounter an issue not covered here, you can:

* Join the [Zircuit Discord Community](https://discord.com/invite/zircuit)'s [#support](https://discord.com/channels/1166855734236024852/1167143938889617448) channel.
* Contact us via email: `bootstrap _at_ zircuit.com`


# Zircuit Token (ZRC)

## **Introduction:**

Zircuit Token (ZRC) has a Total Supply of 10,000,000,000 tokens.

| Network  | Contract Address                                                                                                              |
| -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Ethereum | [0xfd418e42783382e86ae91e445406600ba144d162](https://etherscan.io/address/0xfd418e42783382e86ae91e445406600ba144d162)         |
| Zircuit  | [0xfd418e42783382e86ae91e445406600ba144d162](https://explorer.zircuit.com/address/0xfd418e42783382e86ae91e445406600ba144d162) |

ZRC serves as the foundation of Zircuit’s architecture, enabling participants to receive additional rewards, participate in network app fair launches, and drive its growth. As the ecosystem’s cornerstone, ZRC aligns incentives across developers and users, fostering active collaboration and innovation.

* **Coingecko**: <https://www.coingecko.com/en/coins/zircuit>
* **CoinmarketCap**: <https://coinmarketcap.com/currencies/zircuit/>

## Season 1 Airdrop:

7% of the total Zircuit Token (ZRC) supply was allocated for Season 1 points, with 262,200 unique addresses eligible for claiming. The Season 1 snapshot was taken on July 7, 2024 at 16:00:00 UTC.

## Season 2 Airdrop:

3% of the total Zircuit Token supply was allocated for Season 2, the snapshot was taken on November 20 at 5:00:00 AM UTC.

## **Allocation & Vesting**:

* 21.00% Airdrop & Community Rewards:

  > 7.00% Season 1 Airdrop
  >
  > 3.00% Season 2 Airdrop
  >
  > 2.45% Campaigns (Fairdrop, Catizen, Binance Web3, etc.)
  >
  > 8.55% Future Airdrops & Rewards: 6 and 12 month cliffs, then 24 month linear vesting
* 13.08% Community Provisions: 1 year cliff, then 24 months linear vesting
* 17.93% Ecosystem Development: 1 year cliff, then 24 months linear vesting
* 18.70% Foundation: 1 year cliff, then 24 months linear vesting
* 18.74% Team: 1 year cliff, then 24 months linear vesting
* 10.55% Investors: 1 year cliff, then 24 months linear vesting

<figure><img src="/files/y3dSDpdiswjSS3iDLcxh" alt=""><figcaption></figcaption></figure>

## **Unlocked at TGE**

* 7.00% - Season 1 Airdrop
* 3.00% - Season 2 Airdrop
* 2.45% - Fairdrop, Catizen, Binance Web3 campaigns
* 2.50% - Community Provisions
* 2.00% - Ecosystem Development
* 5.00% - Foundation
* Total: 21.95%


# LST/LRT Liquidity Hub

## Intro

A Liquid Staking Token (LST) is a blockchain token that represents the staked amount of cryptocurrency on a Proof-of-Stake blockchain (e.g., Ethereum) or in a liquidity pool. An LST can be transferred and used in DeFi activities despite the underlying asset being locked. Similarly, a Liquid Restaking Token (LRT) represents an asset that has been used in multiple staking or DeFi activities (i.e., restaked), e.g., to secure multiple protocols simultaneously. Both LSTs and LRTs allow for improved capital efficiency of staked capital.

Although users have been able to use LSTs for their staked ETH for a few years now, the landscape of LRTs is still nascent and rapidly evolving. New LRT protocols constantly emerge (e.g., allowing users to restake their BTC), making it challenging for even experienced users to track and choose the best and the safest protocols for deploying their capital.

Zircuit aims to address this problem by becoming a major liquidity hub for restaked ETH, BTC, LSTs and LRTs where users can easily allocate their capital and have peace-of-mind knowing that funds are deployed to the safest and highest quality protocols. As of June 2024, Zircuit has demonstrated its ability to attract capital by collecting nearly [$3.5B](https://defillama.com/protocol/zircuit-staking#information) in ETH, LSTs, and LRTs deposits within a few months.

On the technical level, Zircuit is an Ethereum Virtual Machine (EVM)-compatible Zero-Knowledge Layer 2 rollup launching on the Ethereum network. Apart from offering much lower network fees and higher transaction throughput than Ethereum mainnet, Zircuit's main focus is security. Zircuit prevents malicious transactions and hacks by utilizing its novel AI-enabled feature: Sequencer Level Security (SLS). We envision Zircuit as a central hub for restaked assets that features unparalleled security and allows users to earn industry-leading yields natively.

## Zircuit and LST/LRTs

### Bootstrapping Zircuit Liquidity via LST/LRTs

LST and LRT protocols have successfully attracted users and significant Total Value Locked (TVL) as they were able to strike a balance between their relatively high yields, convenience and safety. Zircuit has partnered with leading LST and LRT protocols (and others; for a full list see [Zircuit Stake](https://stake.zircuit.com/)) to provide users with boosted Zircuit rewards on top of native LST/LRT yields. Users stake a compatible asset into the [Zircuit staking contract](https://etherscan.io/address/0xf047ab4c75cebf0eb9ed34ae2c186f3611aeafa6) (on Ethereum mainnet) and immediately start earning Zircuit rewards, in addition to the underlying LST/LRT yields, while also helping to bootstrap Zircuit’s liquidity.

### Yield

Assets staked in Zircuit earn yield from:

* Staking rewards from securing Ethereum network;
* Rewards from Actively Validated Services (AVSs) being secured by restaking (e.g., EigenLayer points);
* Rewards from LST/LRT partners (e.g., Renzo points);
* Rewards from Zircuit (e.g., Zircuit points); and
* Partner-specific reward multipliers and boosts.

A list of rewards distributed by LST/LRT partners can be found in the [FAQ](https://stake.zircuit.com/faq).

### Assets Staked in Zircuit

Assets staked in Zircuit remain untampered and no additional risk is incurred. Zircuit staking contract is minimalistic and has been audited internally and externally (public security audit reports can be found at [Zircuit Stake](https://stake.zircuit.com/) page).

Once the Zircuit L2 mainnet is live, there will be an opt-in migration process where depositors will be incentivized to move their assets staked on Ethereum mainnet onto the Zircuit L2 rollup. Users will be able to choose to either:

* Participate in the migration process; or
* Withdraw and receive all earned rewards, points, and yield.

### Withdrawal Period

Users can withdraw their assets at any time and retain any Zircuit rewards earned up to that moment (without any lockups or withdrawal periods). Please note that although Zircuit has no withdrawal periods, there may be withdrawal periods for the LST/LRT partners. Please check with the respective partner for details.

## Endgame: Zircuit L2 as a Liquidity Hub

LST protocols have already demonstrated success due to their yield, simplicity, and safety. We expect LRTs to follow a similar adoption path. As LRTs mature, they will likely differentiate based on yield rates, the AVS they secure, operators, decentralization factors, and slashing risks. It is important to note that LRTs are exposed to inherently more risks than LSTs since they represent assets that are staked in multiple projects simultaneously. Hence, a single slashing event may have a much larger impact than on LSTs (where the impact is contained). We expect that navigating this landscape will become more complex over time.

\
Zircuit will continue to support the development and adoption of both LSTs and LRTs by strategically partnering with high-quality protocols and by helping them implement rewards tracking directly on the rollup. Additionally, through various integrations, Zircuit will enable native staking on its network to secure other networks and protocols that are deployed outside of the rollup. Zircuit will offer the safest and the most convenient yield opportunities to its users in a secure environment that improves upon Ethereum’s transaction throughput and cost. Zircuit intends to become a major liquidity hub for restaked ETH, BTC, LSTs and LRTs, where protocols and users are matched easily and efficiently.\\


# (Deprecated) Grants Progam

⚠️ The Grants Program has been discontinued.

We are no longer accepting grant applications. This page is kept for reference only and the program is no longer active.

If you are interested in working with us or contributing to the ecosystem, please explore other opportunities in our documentation.


# Block Explorer

{% hint style="info" %}
The block explorer currently does not provide any external APIs for clients.
{% endhint %}

## Mainnet

<a href="https://explorer.zircuit.com/" class="button primary" data-icon="arrow-up-right-from-square"><https://explorer.zircuit.com/></a>

## Garfield Testnet

<a href="https://exp.garfield-testnet.zircuit.com/" class="button primary" data-icon="arrow-up-right-from-square"><https://exp.garfield-testnet.zircuit.com/></a>

## Verifying Contracts

To verify contracts, follow the [steps](https://docs.conduit.xyz/chains/explorer/verify-contracts) provided by Conduit.


# Verifying Contracts

Verifying your smart contract's source code on the [block explorer](https://explorer.zircuit.com) allows others to see your contract's code and verify that it operates as intended. The Zircuit block explorer provides two ways of verifying source code:

* automated verification using [Hardhat](https://hardhat.org/getting-started/) or [Foundry](https://book.getfoundry.sh/getting-started/installation.html)
* manual verification through direct upload of Solidity code from your `.sol` files or the compiler artifacts to [Sourcify](https://verify.sourcify.dev/)

### Automated Verification

If you are using Hardhat or Foundry to develop your project, you can use a plug-in to verify the smart contracts:

#### Foundry

Install [Foundry](https://book.getfoundry.sh/getting-started/installation.html) if not already installed, and deploy your smart contracts to Zircuit. Then:

Use the `forge` command to verify the contract:

```bash
forge verify-contract <deployed-contract-address> <source-file>:<contract-name>  \
--chain-id 48900 # or 48898 if deploying to Garfield Testnet
--verifier sourcify \
--verifier-url https://sourcify.dev/server
```

Replace `<deployed-contract-address>`, and `<source-file>:<contract-name>` with your details.

If you need to specify the constructor arguments, use the `--constructor-args` option with the ABI-encoded argument string. You can refer to the `verify-contract` [subcommand documentation](https://book.getfoundry.sh/reference/forge/forge-verify-contract) for more details.

If the command runs successfully, your contract's page on <https://explorer.zircuit.com> will now display the source code and a **Contract source code verified** badge.

#### Hardhat

Before you start, install [Hardhat](https://hardhat.org/getting-started/) if you haven't already, and deploy your smart contracts to Zircuit. Then:

1. Install the Hardhat plug-in:

`npm install --save-dev @nomicfoundation/hardhat-verify`

2. Configure Hardhat by adding the plug-in to your `hardhat.config.js`:

```javascript
import { defineConfig } from "hardhat/config";
import hardhatVerify from "@nomicfoundation/hardhat-verify";

export default defineConfig({
  plugins: [hardhatVerify],
  networks: {
    zircuit: {
      url: 'https://mainnet.zircuit.com', // or 'https://garfield-testnet.zircuit.com'
      // ... rest of the network config
    },
  },
  verify: {
    etherscan: {
      enabled: false
    },
    blockscout: {
      enabled: false
    },
    sourcify: {
      enabled: true,
      apiUrl: 'https://sourcify.dev/server',
    }
  },
  // ... rest of the config
});
```

3. Run the verification command from your terminal:

```
npx hardhat verify --network zircuit <deployed-contract-address> [constructor-arguments]
```

Replace `<deployed-contract-address>`, and `[constructor-arguments]` with your specific details.

If the command runs successfully, your contract's page will now display the source code and a **Contract source code verified** badge.

### Manual Verification

You will require the following:

1. Deployed Smart Contract: The contract's Zircuit address
2. Solidity Source Code: The solc artifacts in the standard JSON format, or the exact source code as it was at the time of contract deployment + their metadata.json file(s)
3. Compiler Version: The Solidity compiler version used to compile the deployed contract
4. Constructor Arguments: The contract’s constructor arguments during the deployment, if any

Follow these steps for verification:

1. Navigate to your smart contract on <https://explorer.zircuit.com> (or [https://explorer.garfield-testnet.zircuit.com](https://explorer.garfield-testnet.zircuit.com/)) and use the search bar to find the smart contract for which you would like to verify the source code.
2. On the contract's page, click on the **Contract** tab. Click on the **Verify and Publish** link.
3. On the next screen, select the compiler version used to compile source code, and upload the source code files. Those can be the [standard json input/output files for solc](https://docs.soliditylang.org/en/v0.8.15/using-the-compiler.html#compiler-api), or Solidity and metadata.json file(s). Click **next**.
4. On the next page, enter the constructor argument data types and values. Use the ABI encoded format or the form, and the system will convert them for you. if using the .sol files, you will also need to upload their respective metadata.json files. When finished, upload the source files and click **Verify and Publish**.

If successful, your contract's page will now display the source code and a **Contract Source Code Verified** badge. If the process fails, double-check the compiler version, constructor parameters, and exactness of the source code.


# RPCs

## Mainnet

### Zircuit Load-Balanced Endpoint

This is the link for Zircuit load-balanced endpoint, that combined all nodes listed in the "Zircuit Isolated Endpoints" section.

<table><thead><tr><th width="325">URL</th><th>Supported By</th></tr></thead><tbody><tr><td><a href="https://mainnet.zircuit.com">https://mainnet.zircuit.com</a><br>wss://mainnet.zircuit.com</td><td>Zircuit Team and DRPC (<a href="https://drpc.org">drpc.org</a>)</td></tr></tbody></table>

The Chain Id is `48900`.

### Paid Nodes For Advanced Usage

The DRPC ([https://drpc.org](https://drpc.org/)) team provides paid node access, offering unlimited high-performance and cost-effective access to the Zircuit network (<https://drpc.org/pricing>). If your application requires higher performance and support, you can get them here.

The paid RPC endpoints support debug traces.

## Garfield Testnet

### Zircuit Load-Balanced Endpoint

This is the link for Zircuit load-balanced endpoint, that combined all nodes listed in the "Zircuit Isolated Endpoints" section.

<table><thead><tr><th width="325">URL</th><th>Support By</th></tr></thead><tbody><tr><td><a href="https://garfield-testnet.zircuit.com/">https://garfield-testnet.zircuit.com</a><br>wss://garfield-testnet.zircuit.com</td><td>Zircuit Team and DRPC (<a href="https://drpc.org">drpc.org</a>)</td></tr></tbody></table>

The Chain Id is `48898`.


# Running a Node

Instructions for running a Zircuit network node

Zircuit nodes are maintained by [Conduit](https://www.conduit.xyz/). Conduit provides [instructions](https://docs.conduit.xyz/chains/getting-started/run-a-node/op-stack-nodes) for running network nodes on their website.

***


# Oracles

Oracles are third-party services that provide smart contracts with external information. They act as a bridge between blockchains and the outside world, enabling smart contracts to access off-chain data. Oracles are essential in expanding the functionality of smart contracts by enabling them to interact with data sources beyond their native networks.

There are oracle providers are available on Zircuit, you can learn more about them below.

## Redstone

{% embed url="<https://docs.redstone.finance/docs/get-started/price-feeds>" %}

RedStone is a modular oracle specializing in providing gas-optimized data feeds and offering price feeds for new LSTs, LRTs, Bitcoin LSTs, and emerging types of stablecoins. Their services are available cross-chain including EVM and non-EVM chains, rollups, and appchains.

## **API3**

{% embed url="<https://docs.api3.org/>" %}

API3 provides price feed services to smart contracts in a decentralized, trust-minimized way, using first-party oracles, with each source being verifiable on-chain. A key feature of API3 oracles is the ability to fast-track the most accurate asset value through API3's Oracle Extractable Value (OEV) network. API3 focuses on maximizing security and minimizing latency.


# Data & Indexing

## Goldsky

{% embed url="<https://docs.goldsky.com/subgraphs/introduction>" %}

High-performance subgraph hosting and realtime data replication pipelines.

## Sentio

{% embed url="<https://docs.sentio.xyz/docs/quickstart>" %}

Building the next-generation infrastructure for crypto data, purpose-built for the speed, scale, and complexity of modern blockchain use cases.

## Envio

{% embed url="<https://docs.envio.dev/docs/HyperSync/hypersync-quickstart>" %}

A specialized indexing solution that focuses on speed.

## SubQuery

{% embed url="<https://subquery.network/doc/indexer/quickstart/quickstart.html>" %}

An open-source data indexer that provides you with custom APIs for your web3 project.


# Relayers

A relayer allows users to initiate blockchain transactions without holding the native token required for gas fees. It verifies the user's signature for security, then submits the transaction on their behalf, abstracting away gas payments to enable a smoother user experience.

## Zircuit Relayer Service

{% content-ref url="/pages/JJSqglFsocKbvhm6axce" %}
[Zircuit Relayer Service](/infra/relayers/zircuit-relayer-service)
{% endcontent-ref %}

The Zircuit Relayer service is available on Garfield Testnet and allows to batch multiple calls in a single user operation on the Zircuit Block Explorer. Users have to manually delegate to a EIP7702 Contract Template and are then automatically tapped into the feature on the explorer frontend.

## Gelato

{% embed url="<https://docs.gelato.cloud/Relay/Introduction/Overview>" %}

Gelato Relay is available on Zircuit Mainnet and facilitates secure, gasless transactions by relaying signed messages from users and handling gas payments through multiple supported methods.


# Zircuit Relayer Service

The Zircuit Relayer service enables click‑to‑build smart‑account transactions directly from our Block Explorer. Users compose ERC‑4337 user operations in the UI and submit them through our managed relayer, with gas fully sponsored for eligible accounts or charged as a small ZRC fee without any ETH required.

Behind the scenes, we handle the EIP‑7702 set‑code flow, validation, paymaster signing, bundling, and submission to EntryPoint, so you ship faster without running infrastructure. The result is smoother, cheaper, and safer on‑chain actions—making it effortless to transact more efficiently on the Explorer.

## What You Are Authorizing

### EIP 7702 Relay Service for Sponsored User Operations

By signing the delegation tuple you:

* **Grant power only to the&#x20;*****Zircuit Smart Contract template*** (`ZircuitRelayerContract`).
* **Limit scope** strictly to user operations that you explicitly sign in the Explorer.

### Authorization Signature Required

Your signature authorizes the relay contract to forward operations *exactly as you reviewed them*. It cannot modify or craft new calls.

## Security & Control

* **Funds never leave your wallet** until the EntryPoint executes the batch.
* The relayer **cannot** initiate calls or move funds without your signed intent.
* **Revocation:** Submit a fresh EIP‑7702 transaction that clears or changes the delegation at any time.

## Frequently Asked Questions

**Q: Do I ever need ETH?**\
\&#xNAN;*A:* No. Either Zircuit sponsors 100 % of gas or you pay with ZRC.

**Q: Can the relayer rug me?**\
\&#xNAN;*A:* No! Only if you sign a malicious batch. Always review operations in the side drawer and before signing the user operation in your wallet.

**Q: How do I revoke the delegation?**\
\&#xNAN;*A:* Send an EIP‑7702 transaction setting the target address to 0x...0 (cast az).


# Enable Relaying Using EIP-7702

To batch transactions and use gas‑sponsored UserOperations with **Zircuit** you must first **delegate your account to the Zircuit Relayer Account** via an **EIP‑7702 `SetCode` transaction**. The guide below shows how to do this securely with Foundry’s `cast` CLI—without ever pasting your private key into the browser.

## Why Delegation Is Required

EIP‑7702 lets you temporarily replace your externally‑owned account’s code for a single transaction batch—perfect for ERC‑4337 wallets. Until mainstream wallets such as MetaMask or Rabby add native support for sending a `SetCode` transaction to *arbitrary* addresses, you have to perform the delegation yourself.

## Security Principles

* **Never** paste your private key into any website form or browser console.
* Export the key only in a hardened, preferably offline shell session.
* Once delegation is confirmed, remove the key from your environment.

## Prerequisites

Foundry must be installed as described in the official installation guide [here](https://getfoundry.sh/introduction/installation/).

{% stepper %}
{% step %}
**Export your private key in a local terminal**

```bash
export PRIVATE_KEY=0xYOUR_PRIVATE_KEY
```

{% endstep %}

{% step %}
**Send the SetCode transaction to delegate to ZircuitRelayerAccount**

{% code overflow="wrap" %}

```bash
cast send $(cast az) --auth <ZIRCUIT_RELAYER_ADDRESS> --private-key $PRIVATE_KEY --rpc-url https://mainnet.zircuit.com
```

{% endcode %}
{% endstep %}

{% step %}
**Verify the delegation on** [**https://explorer.zircuit.com**](https://explorer.zircuit.com/)**, verifying your address has been successfully delegated**
{% endstep %}
{% endstepper %}

## Next Steps

### Contract Details

**Zircuit Relayer Account address**:

Garfield Testnet: [0xD4f99Ef25e5aAB3A0575D9C2dB2E4f09f18442D8](https://explorer.garfield-testnet.zircuit.com/address/0xD4f99Ef25e5aAB3A0575D9C2dB2E4f09f18442D8?activeTab=3)

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.28;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/interfaces/IERC1271.sol";
import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../core/Helpers.sol";
import "../core/BaseAccount.sol";

/**
 * ZircuitRelayerAccount.sol
 * A minimal account to be used with EIP‑7702 (for batching) and ERC‑4337 (for gas sponsoring)
 */
contract ZircuitRelayerAccount is BaseAccount, IERC165, IERC1271, ERC1155Holder, ERC721Holder {
    address public immutable ZIRCUIT_TOKEN_ADDRESS;
    address public immutable ZIRCUIT_PAYMASTER_ADDRESS;
    IEntryPoint public immutable ENTRY_POINT;

    constructor(address _zircuitTokenAddress, IEntryPoint _entryPoint, address _zircuitPaymasterAddress) {
        ZIRCUIT_TOKEN_ADDRESS = _zircuitTokenAddress;
        ENTRY_POINT = _entryPoint;
        ZIRCUIT_PAYMASTER_ADDRESS = _zircuitPaymasterAddress;
    }

    // address of entryPoint v0.8
    function entryPoint() public view override returns (IEntryPoint) {
        return ENTRY_POINT;
    }

    /**
     * Make this account callable through ERC‑4337 EntryPoint.
     * The UserOperation should be signed by this account's private key.
     */
    function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
        internal
        virtual
        override
        returns (uint256 validationData)
    {
        if (!_checkSignature(userOpHash, userOp.signature)) {
            return SIG_VALIDATION_FAILED;
        }

        bytes calldata paymasterAndData = userOp.paymasterAndData;
        // Check if paymasterAndData exists and has at least the 52‑byte prefix
        if (paymasterAndData.length >= 52) {
            // Extract paymaster address from prefix (needed for approve)
            address paymasterAddress = address(bytes20(paymasterAndData[0:20]));

            if (paymasterAddress == ZIRCUIT_PAYMASTER_ADDRESS) {
                // Bytes 52‑83: Packed data (mode + validUntil + validAfter)
                // Bytes 84‑115: Amount (32 bytes)

                // Extract packed data (bytes 52‑83)
                bytes32 packedData;
                assembly {
                    packedData := calldataload(add(paymasterAndData.offset, 52))
                }

                // Extract mode from packed data (first byte)
                uint8 mode = uint8(uint256(packedData) >> 248);

                require(mode <= 1, "Unsupported paymaster mode");

                // If mode is 1, approve the paymaster for the specified amount
                if (mode == 1) {
                    require(paymasterAddress != address(0), "Invalid paymaster address"); // Sanity check
                    uint256 amount;
                    assembly {
                        amount := calldataload(add(paymasterAndData.offset, 84))
                    }

                    IERC20(ZIRCUIT_TOKEN_ADDRESS).approve(paymasterAddress, amount);
                }
            }
        }

        return SIG_VALIDATION_SUCCESS;
    }

    function isValidSignature(bytes32 hash, bytes memory signature) public view returns (bytes4 magicValue) {
        return _checkSignature(hash, signature) ? this.isValidSignature.selector : bytes4(0xffffffff);
    }

    function _checkSignature(bytes32 hash, bytes memory signature) internal view returns (bool) {
        return ECDSA.recover(hash, signature) == address(this);
    }

    function _requireForExecute() internal view virtual override {
        require(msg.sender == address(this) || msg.sender == address(ENTRY_POINT), "not from self or EntryPoint");
    }

    function supportsInterface(bytes4 id) public pure override(ERC1155Holder, IERC165) returns (bool) {
        return id == type(IERC165).interfaceId || id == type(IAccount).interfaceId || id == type(IERC1271).interfaceId
            || id == type(IERC1155Receiver).interfaceId || id == type(IERC721Receiver).interfaceId;
    }

    // accept incoming calls (with or without value), to mimic an EOA.
    fallback() external payable {}

    receive() external payable {}
}
```


# Using the Zircuit Relayer Service

{% stepper %}
{% step %}
**Open the Explorer**

Navigate to [**https://explorer.zircuit.com/user-operations**](https://explorer.zircuit.com/user-operations)
{% endstep %}

{% step %}
**Delegate Your Account**

Ensure your wallet’s account is delegated to **`ZircuitRelayerContract`** (one‑time EIP‑7702 delegation).
{% endstep %}

{% step %}
**Compose Your Batch**

<figure><img src="/files/gIcxGiVxdw5b2Jjde6yS" alt=""><figcaption></figcaption></figure>

For every *write* call you would normally send as a transaction, click **“Add Call to User Operation”** instead.
{% endstep %}

{% step %}
**Review & Edit**

<figure><img src="/files/h03AAq72389Jx2QKJ7Ue" alt=""><figcaption></figcaption></figure>

Use the side drawer to inspect and, if needed, edit each operation in the batch before signing.
{% endstep %}

{% step %}
**Submit**

* **Gas‑sponsored:** Send the batch for free, fully sponsored by Zircuit.
* **Low‑fee:** Pay a small amount of **ZRC** (never ETH) for fee processing.

Your user operation will appear on‑chain once it passes through the public **EntryPoint** and the **Zircuit Paymaster**
{% endstep %}
{% endstepper %}


# Bridges

Bridges provide seamless interoperability between Zircuit and other blockchain networks, enabling the efficient transfer of assets and data across different chains

### Providers

<table><thead><tr><th width="249"></th><th>Networks</th><th>Tokens</th></tr></thead><tbody><tr><td><a href="https://bridge.zircuit.com/">Canonical Zircuit Bridge</a></td><td>Ethereum</td><td>ETH, ZRC, WETH, GUD, lsETH, USDT, mETH, wstETH, weETH, weETHs, LBTC, WBTC, FBTC, USDC, xPufETH, rswETH, swETH, ENA, rsETH, sUSDe, USDe, egETH, mstETH, mwBETH, mswETH, ezETH, pzETH, pumpBTC, inwstETHs, STONE, Re7LRT, steakLRT, amphrETH, mBTC, USDa, sUSDa</td></tr><tr><td><a href="https://app.rhino.fi/bridge?mode=pay&#x26;chainIn=ETHEREUM&#x26;chainOut=ZIRCUIT&#x26;token=ETH&#x26;tokenOut=ETH">Rhino.fi</a></td><td><a href="https://docs.rhino.fi/general/supported-chains">https://docs.rhino.fi/general/supported-chains</a></td><td>ETH, GUD, USDT</td></tr><tr><td><a href="https://nexus.hyperlane.xyz/?origin=ethereum&#x26;destination=zircuit">Hyperlane</a></td><td>Ethereum, Arbitrum, Base, Berachain, BSC, Blast, Fraxtal, Linea, Mode, Optimism, Sei, Swell, Taiko, Unichain, World Chain</td><td>ezETH, pzETH, rstETH</td></tr><tr><td><a href="https://stargate.finance/bridge?srcChain=ethereum&#x26;srcToken=0xf951E335afb289353dc249e82926178EaC7DEd78&#x26;dstChain=zircuit&#x26;dstToken=0x850CDF416668210ED0c36bfFF5d21921C7adA3b8">Stargate</a></td><td>Ethereum, Avalanche, Aptos, Arbitrum, Base, Blast, Berachain, Hemi, HyperEVM, Kava, Linea, Manta, Metis, Mode, Morph, Optimism, Plasma, Scroll, Solana, Sonic, Swell, TAC, TON, Unichain, X Layer, zkSync</td><td>swETH, rsETH, rswETH, USDe, sUSDe, ENA</td></tr><tr><td><a href="https://relay.link/bridge/zircuit">Relay</a></td><td><a href="https://docs.relay.link/references/api/api_resources/supported-chains">https://docs.relay.link/references/api/api_resources/supported-chains</a></td><td>ETH, WETH</td></tr><tr><td><a href="https://www.orbiter.finance/bridge/Arbitrum/Zircuit?token=ETH">Orbiter Finance</a></td><td><a href="https://docs.orbiter.finance/supported-chains">https://docs.orbiter.finance/supported-chains</a></td><td>ETH</td></tr></tbody></table>


# Canonical Bridge

## Zircuit Mainnet

{% embed url="<https://bridge.zircuit.com>" %}

The bridge is available for both deposits and withdrawals. For Zircuit deposits, you can also directly transfer ETH to `0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8`.

{% hint style="danger" %}
Do not transfer ERC20 tokens or any tokens other that native ETH to this address.

Smart contract method calls need to be used for such assets. A direct transfer will result in these tokens getting stuck in the canonical bridge smart contract.
{% endhint %}

## Garfield Testnet

{% embed url="<https://bridge.garfield-testnet.zircuit.com>" %}

The bridge is available for both deposits and withdrawals. For deposits, you can also directly transfer Sepolia ETH to `0x87a7E2bCA9E35BA49282E832a28A6023904460D8`.

{% hint style="danger" %}
Do not transfer ERC20 tokens or any tokens other that native Sepolia ETH to this address.

Smart contract method calls need to be used for such assets. A direct transfer will result in these tokens getting stuck in the canonical bridge smart contract.
{% endhint %}


# Bridging ERC20 Tokens and Withdrawing Manually

This page explains how to create and bridge ERC20 tokens from Ethereum (L1) to Zircuit (L2) and back programmatically using JavaScript with the ethers and zircuit-viem libraries.

### Create Node Environment

To get started, we need a node environment and install the `ethers` and `zircuit-viem`. Zircuit is built in part on the OP Stack, but certain withdrawal mechanisms have been modified to reduce user-side costs. To support these changes, we provide our own fork of the `viem` library called `zircuit-viem` for communicating with the network.

```
npm init -y
npm install @zircuit/zircuit-viem
```

### Connect to Smart Contracts on L1 and L2

Next, we need to set the following variables in a new file. The following adapted code snippets can be saved to a file called `bridge.js`. You can run the script using Node.js. This tutorial expects, that the ERC20 contract has already been deployed on L1 and the user has a token balance. Next, we begin the setup. We connect the signing wallet to the L1 contract. RPC URLs, the private key as well as the address of the ERC20 contract have to be adapted.

```javascript
import { createWalletClient, createPublicClient, http, parseEventLogs, encodeFunctionData } from '@zircuit/zircuit-viem'
import { privateKeyToAccount } from '@zircuit/zircuit-viem/accounts'
import { mainnet, zircuit } from '@zircuit/zircuit-viem/chains'
// The L1 Address of the ERC20 Contract to be bridged
const l1Erc20ContractAddress = "0xYourERC20TokenAddressHere";
// Partial ABI
const l1Erc20ContractABI = [
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "guy",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "wad",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  }
];


// Set up L1 and L2 with the correct URLs
const L1_RPC_URL = "L1_RPC_URL";
const L2_RPC_URL = "L2_RPC_URL";

// Import account from private key
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY')
// Create clients
const walletClientL1 = createWalletClient({
  account,
  chain: mainnet,
  transport: http(L1_RPC_URL)
})

const walletClientL2 = createWalletClient({
  account,
  chain: zircuit,
  transport: http(L2_RPC_URL)
})

const publicClientL1 = createPublicClient({
  chain: mainnet,
  transport: http(L1_RPC_URL)
})

const publicClientL2 = createPublicClient({
  chain: zircuit,
  transport: http(L2_RPC_URL)
})
```

We also need to connect to the ERC20 Factory on Zircuit L2 to bridge ERC20s. The `OptimismMintableERC20FactoryAddress` is a predeployed contract on L2. The name, symbol and decimals of the ERC20 have to be adapted accordingly.

```javascript
// Connect to the ERC20 Factory contract on Zircuit
// This is a predeployed contract to create ERC20s on L2
const OptimismMintableERC20FactoryAddress = "0x4200000000000000000000000000000000000012";
// ERC20 name, symbol, and decimals
// Set these according to your ERC20
const tokenName = "TOKEN_NAME"
const tokenSymbol = "TOKEN_SYMBOL"
const tokenDecimals = 18

// We only provide the partial ABI with createOptimismMintableERC20WithDecimals and the StandardL2TokenCreated event
// The complete ABI can be extracted from the file attached above
const OptimismMintableERC20FactoryABI = [
    {
        "type": "function",
        "name": "createOptimismMintableERC20WithDecimals",
        "inputs": [
            {
                "name": "_remoteToken",
                "type": "address",
                "internalType": "address"
            },
            {
                "name": "_name",
                "type": "string",
                "internalType": "string"
            },
            {
                "name": "_symbol",
                "type": "string",
                "internalType": "string"
            },
            {
                "name": "_decimals",
                "type": "uint8",
                "internalType": "uint8"
            }
        ],
        "outputs": [
            {
                "name": "",
                "type": "address",
                "internalType": "address"
            }
        ],
        "stateMutability": "nonpayable"
    },
    {
        "type": "event",
        "name": "StandardL2TokenCreated",
        "inputs": [
            {
                "name": "remoteToken",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            },
            {
                "name": "localToken",
                "type": "address",
                "indexed": true,
                "internalType": "address"
            }
        ],
        "anonymous": false
    }
];
```

### Create ERC20 contract on Zircuit L2

Next we make a call `createOptimismMintableERC20WithDecimals` to the L2 factory contract to create the ERC20 on Zircuit L2.

```javascript
let l2Erc20ContractAddress= "";
(async () => {
  try {
    // Send transaction
    const hash = await walletClientL2.writeContract({
      address: OptimismMintableERC20FactoryAddress,
      abi: OptimismMintableERC20FactoryABI,
      functionName: 'createOptimismMintableERC20WithDecimals',
      args: [l1Erc20ContractAddress, tokenName, tokenSymbol, tokenDecimals]
    })

    // Wait for confirmation
    const receipt = await publicClientL2.waitForTransactionReceipt({ hash })

    // Parse logs to find StandardL2TokenCreated event
    const events = parseEventLogs({
      abi: OptimismMintableERC20FactoryABI,
      logs: receipt.logs,
      eventName: 'StandardL2TokenCreated'
    })

    l2Erc20ContractAddress = events[0].args.localToken
    console.log('L2 ERC20 Contract Address:', l2Erc20ContractAddress)
  } catch (error) {
    console.error('Error:', error)
  }
})()
```

Congratulations, you have successfully deployed the contract on Zircuit on the returned address.

## Bridge ERC20 Tokens to L2

With the ERC20 Token deployed on L1 and L2, a user can now bridge actual tokens from that L1 contract to the L2 contract. To bridge tokens, we need to connect to the `L1StandardBridge` contract, approve the bridge amount and then bridge the tokens to L2. The `L1StandardBridgeProxy` address can be found on this page [https://github.com/zircuit-labs/docs/blob/main/infra/bridges/canonical-bridges/broken-reference/README.md](https://github.com/zircuit-labs/docs/blob/main/infra/bridges/canonical-bridges/broken-reference/README.md "mention"). Also, the token amount needs to be set.

{% hint style="info" %}
Double check all addresses before executing this code!
{% endhint %}

<pre class="language-javascript"><code class="lang-javascript">// Amount of Tokens to bridge
const tokenAmount = 100n 

// Proxy Address of L1StandardBridge, double check this!
const L1StandardBridgeProxyAddress = "0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8";

// Partial ABI of the L1StandardBridge, complete ABI can be extracted from the files above
const L1StandardBridgeABI = [
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_localToken",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_remoteToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_amount",
        "type": "uint256"
      },
      {
        "internalType": "uint32",
        "name": "_minGasLimit",
        "type": "uint32"
      },
      {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }
    ],
    "name": "bridgeERC20",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
];

// Bridge tokens
<strong>(async () => {
</strong>  const approvalHash = await walletClientL1.writeContract({
    address: l1Erc20ContractAddress,
    abi: l1Erc20ContractABI,
    functionName: 'approve',
    args: [L1StandardBridgeProxyAddress, tokenAmount]
  })

  await publicClientL1.waitForTransactionReceipt({ hash: approvalHash })
  console.log('Approval transaction confirmed:', approvalHash)
  
  // Encode bridgeERC20 call
  const txData = encodeFunctionData({
    abi: L1StandardBridgeABI,
    functionName: 'bridgeERC20',
    args: [
      l1Erc20ContractAddress,
      l2Erc20ContractAddress,
      tokenAmount,
      80000n, // GasLimit
      '0x'    // Extra data
    ]
  })

  // Prepare unsigned transaction
  const tx = {
    to: L1StandardBridgeProxyAddress,
    data: txData
  }

  // Estimate gas
  const estimatedGasLimit = await publicClientL1.estimateGas({
    account: walletClientL1.account,
    ...tx
  })
  console.log(`Estimated Gas Limit: ${estimatedGasLimit}`)

  // Send with gas limit
  const bridgeHash = await walletClientL1.sendTransaction({
    ...tx,
    gas: estimatedGasLimit
  })

  await publicClientL1.waitForTransactionReceipt({ hash: bridgeHash })
  console.log('Bridge transaction confirmed:', bridgeHash)
})();
</code></pre>

Congratulations, the ERC20 Tokens are now bridged to Zircuit.

## Withdrawing ERC20 Tokens from L2 to L1

As with normal withdrawals, withdrawing assets is a three step process of initiating a withdrawal on L2, and then proving and finalizing the withdrawal transaction on L1. You can place the following code into a new file called `withdrawal.js`. Again, the RPC URLs and the private key needs to be set. Also, the token amount and the contract addresses of the ERC20 need to be set.

#### Start your withdrawal on L2

```javascript
import { createWalletClient, createPublicClient, http, encodeFunctionData } from '@zircuit/zircuit-viem'
import { privateKeyToAccount } from '@zircuit/zircuit-viem/accounts'
import { mainnet, zircuit } from '@zircuit/zircuit-viem/chains'

import {
  getL2TransactionHashes,
  getWithdrawals,
  publicActionsL1,
  publicActionsL2,
  walletActionsL1,
  walletActionsL2,
} from '@zircuit/zircuit-viem/op-stack';

const L2StandardBridgeAddress = '0x4200000000000000000000000000000000000010'
const L2StandardBridgeAbi = [
  {
    inputs: [
      { internalType: 'address', name: '_localToken', type: 'address' },
      { internalType: 'address', name: '_remoteToken', type: 'address' },
      { internalType: 'uint256', name: '_amount', type: 'uint256' },
      { internalType: 'uint32', name: '_minGasLimit', type: 'uint32' },
      { internalType: 'bytes', name: '_extraData', type: 'bytes' }
    ],
    name: 'bridgeERC20',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function'
  }
]

const l2Token = '0xYourL2ERC20TokenAddressHere'
const l1Token = '0xYourL1ERC20TokenAddressHere'
const tokenAmount = 100n

const L1_RPC_URL = "L1_RPC_URL";
const L2_RPC_URL = 'L2_RPC_URL'

const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY')

const walletClientL1 = createWalletClient({
  account,
  chain: mainnet,
  transport: http(L1_RPC_URL)
}).extend(walletActionsL1());

const walletClientL2 = createWalletClient({
  account,
  chain: zircuit,
  transport: http(L2_RPC_URL)
}).extend(walletActionsL2());

const publicClientL1 = createPublicClient({
  chain: mainnet,
  transport: http(L1_RPC_URL)
}).extend(publicActionsL1());

const publicClientL2 = createPublicClient({
  chain: zircuit,
  transport: http(L2_RPC_URL)
}).extend(publicActionsL2());

let withdrawalHash = ''
await (async () => {
  try {
    // Encode bridgeERC20 call data
    const data = encodeFunctionData({
      abi: L2StandardBridgeAbi,
      functionName: 'bridgeERC20',
      args: [l2Token, l1Token, tokenAmount, 500000, '0x']
    })

    const tx = {
      to: L2StandardBridgeAddress,
      data
    }

    // Send transaction
    const txHash = await walletClientL2.sendTransaction(tx)

    // Wait for confirmation
    const receipt = await publicClientL2.waitForTransactionReceipt({ hash: txHash })
    withdrawalHash = receipt.transactionHash;
    console.log('withdrawalTransactionHash:', withdrawalHash)
  } catch (error) {
    console.error('Error during token bridging:', error)
  }
})();
```

```javascript
const optimismPortalAddress= '0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1'
const l2OutputOracleAddress= '0x92Ef6Af472b39F1b363da45E35530c24619245A4'
const optimismPortalAbi = [
  {
    inputs: [
      {
        internalType: 'bytes32',
        name: '',
        type: 'bytes32'
      }
    ],
    name: 'finalizedWithdrawals',
    outputs: [
      {
        internalType: 'bool',
        name: '',
        type: 'bool'
      }
    ],
    stateMutability: 'view',
    type: 'function'
  }
]
```

#### Wait until the withdrawal is ready to prove

The second step to withdrawing tokens from L2 to L1 is to prove to the bridge on L1 that the withdrawal happened on L2. You first need to wait until the rollup process of the previous L2 transaction is finished and the withdrawal is ready to prove. This may take up to an hour to finish the rollup process.

```javascript
// Can take up to 1 hour
console.log("Waiting for Rollup Process to finish");
const receipt = await publicClientL2.getTransactionReceipt({
  hash: withdrawalHash
})
try {
  const status = await publicClientL1.getWithdrawalStatus({
    receipt,
    targetChain: publicClientL2.chain
  });
  console.log("status: ", status);
} catch (error) {
  console.error("Status not avaliable yet.");
}
```

#### Prove and release the withdrawal on L1

Once the withdrawal is ready to be proven, you will send an L1 transaction to prove that the withdrawal happened on L2, and to release it. This can only happen after the finalization period elapsed. The finalization period lasts 5 hours on Zircuit. When the withdrawal is ready to be relayed, you can finally complete the withdrawal process.

```javascript
if (status == 'ready-to-prove') {
  // If your transaction contains multiple withdrawals you can
  // extract each withdrawal with getWithdrawals and process it separately
  const [withdrawal] = getWithdrawals(receipt);
  const finalizationPeriodSeconds = await publicClientL1.readContract({
    address: l2OutputOracleAddress,
    abi: l2OutputOracleAbi,
    functionName: 'finalizationPeriodSeconds',
  });

  // 5 Hours on Zircuit at the moment
  console.log(
    `Waiting for finalization period to pass: ${finalizationPeriodSeconds}`
  );

  // Once the withdrawal is ready to be relayed, you can finally complete the withdrawal process.
  // First you fetch the first rolled up state after the withdrawal submission
  const output = await publicClientL1.getL2Output({
    l2BlockNumber: receipt.blockNumber,
    targetChain: zircuit
  });
  // Then you compute the withdrawal proof
  const args  = await publicClientL2.buildProveZircuitWithdrawal({
    output,
    receipt,
    withdrawal,
  });
  // And finally you send the withdrawal release transaction
  const proveHash = await walletClientL1.proveWithdrawal(args);
  const proveReceipt = await publicClientL1.waitForTransactionReceipt({ hash: proveHash })
  console.log('proveWithdrawal:', proveReceipt.transactionHash)

  // Now you simply wait until the message is relayed.
  const isFinalized = await publicClientL1.readContract({
    abi: optimismPortalAbi,
    address: optimismPortalAddress,
    functionName: 'finalizedWithdrawals',
    args: [withdrawal.withdrawalHash],
  });
  console.log(isFinalized);
}

```

Congrats! You've just deposited and withdrawn tokens on Zircuit.


# Bridging Behaviors with EIP-7702

**To avoid the risk of potential fund loss, always use the `L1StandardBridge` and `L2StandardBridge` contracts when bridging ETH and ERC-20 tokens, and use the `L1ERC721Bridge` and `L2ERC721Bridge` contracts when bridging ERC-721 tokens.**

EIP-7702 enables EOAs to execute smart contract code directly. However, this comes with some nuances to be wary of when it comes to bridging.

### Bridging ETH

* ETH may be temporarily locked on the destination network if the receiving delegated account lacks a function to accept ETH (e.g., `receive() external payable`). To resolve this, either undelegate the code or update it to include an ETH-receiving function, then replay the message on the destination network.
* The `L1StandardBridge` and `L2StandardBridge` contracts include a convenience `receive()` function that initiates the bridging process with a fixed gas limit for the destination network. If the receiving delegated account implements a `receive()` function with complex logic that consumes significant gas, it may trigger an out-of-gas error. To avoid unexpected behavior in such cases, use `bridgeETH` or `bridgeETHTo` instead, specifying a custom gas limit that ensures successful execution on the destination chain.

### Bridging ERC-721

* ERC-721 may be temporarily locked on the destination network if the receiving delegated account lacks a correctly implemented `onERC721Received()` function. To resolve this, either undelegate the code or update it to include an `onERC721Received()` function, then replay the message on the destination network.

### Recovering Temporarily Locked ETH/ERC-721 on L1

1. For delegated 7702 EOAs on L1, unset the code first.
2. Go to the `OptimismPortalProxy` page on Etherscan. You can find the Mainnet link [here](https://etherscan.io/address/0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1).
3. Open the transaction hash page where the `proveWithdrawalTransaction` errors occurred.
4. Go to the `Internal Txns` tab.
5. Open and decode input data for the `Relay Message` method.
6. Go to the `L1CrossDomainMessengerProxy` page on Etherscan. You can find the Mainnet link [here](https://etherscan.io/address/0x2a721cBE81a128be0F01040e3353c3805A5EA091#writeProxyContract).
7. Send a `relayMessage` transaction using the exact same decoded input data from Step 6. Set the `payableAmount` to 0. Once the transaction is confirmed, the funds should appear in your wallet.

### Recovering Temporarily Locked ETH/ERC-721 on L2

1. For delegated 7702 EOAs on L2, unset the code first.
2. Go to the `L2CrossDomainMessenger` page on Zircuit explorer. You can find the Mainnet link [here](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000007).
3. Open the transaction hash page where the `relayMessage` error occurred.
4. Copy the `Input Data` and run the folllowing command:

   ```
   cast calldata-decode "relayMessage(uint256,address,address,uint256,uint256,bytes)" $INPUT_DATA
   ```
5. Go to the `L2CrossDomainMessenger` page on Etherscan. You can find the Mainnet link [here](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000007?activeTab=3).
6. Send a `relayMessage` transaction using the exact same decoded input data from Step 4. Set the `payableAmount` to 0. Once the transaction is confirmed, the funds should appear in your wallet.


# ERC20 Tokens with Zircuit Canonical Bridge

Zircuit's canonical bridge is derived from the bridge in the OP-stack framework. Therefore, the ERC20 tokens on Zircuit with their counterparts deployed on Ethereum mainnet follow the same architecture. If you would like to deploy an ERC20 token with no special logic, you can do so by using the prepared factory available on address [0x4200000000000000000000000000000000000012](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000012) on Zircuit.

In some cases, developers may desire to deploy ERC20 contracts with additional functionality that is not available in the templated contract that the factory creates, and they may want for these contracts to work with Zircuit's canonical bridge. This is certainly possible --- you may use any custom implementation provided that it meets the following conditions (you can reference the template implementation show below):

* The token implements the `IOptimismMintableERC20` interface together with EIP165, and reports that this interface is implemented. See function `supportsInterface` in the sample implementation below.
* The canonical bridge is given the privilege to mint tokens to the users when they lock their tokens on L1 by calling the `depositERC20` function on the [L1StandardBridge contract](https://etherscan.io/address/0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8), and to burn tokens from the users' accounts when they call `bridgeERC20` function on the [L2StandardBridge contract](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000010). See the modifier `onlyBridge`, and functions `mint` and `burn` in the reference implementation.\
  \
  Note that upon deposit, the bridge on L1 locks the respective amount of tokens and mints them to the user on L2. Upon withdrawal, it burns the respective number of tokens on L2 from the user's balance, and releases them on L1. **The canonical bridge inherently assumes that the number of tokens in circulation on L2 matches the number of tokens locked in the bridge on L1. The developers should not violate this principle.** This might happen by allowing parties other than the bridge to mint and burn tokens, or by implementing features such as rebasing, or fees on transfer.
* The `REMOTE_TOKEN`, `BRIDGE`, and `DECIMALS` storage variables are properly initialized. The `REMOTE_TOKEN` address should match the token deployed on L1 as the bridge performs a check while bridging. The address of the `L2StandardBridge` on Zircuit is [0x4200000000000000000000000000000000000012](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000012).

Below is a template implementation of an ERC20 that can be further extended with custom logic.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IOptimismMintableERC20 is IERC165 {
    function remoteToken() external view returns (address);

    function bridge() external returns (address);

    function mint(address _to, uint256 _amount) external;

    function burn(address _from, uint256 _amount) external;
}

contract MyZircuitERC20 is IOptimismMintableERC20, ERC20 {
    /// @notice Address of the corresponding version of this token on the remote chain.
    address public immutable REMOTE_TOKEN;

    /// @notice Address of the StandardBridge on this network.
    address public immutable BRIDGE;

    /// @notice Decimals of the token
    uint8 private immutable DECIMALS;

    /// @notice Emitted whenever tokens are minted for an account.
    /// @param account Address of the account tokens are being minted for.
    /// @param amount  Amount of tokens minted.
    event Mint(address indexed account, uint256 amount);

    /// @notice Emitted whenever tokens are burned from an account.
    /// @param account Address of the account tokens are being burned from.
    /// @param amount  Amount of tokens burned.
    event Burn(address indexed account, uint256 amount);

    /// @notice A modifier that only allows the bridge to call
    modifier onlyBridge() {
        require(msg.sender == BRIDGE, "Only bridge can mint and burn");
        _;
    }

    /// @param _bridge      Address of the L2 standard bridge.
    /// @param _remoteToken Address of the corresponding L1 token.
    /// @param _name        ERC20 name.
    /// @param _symbol      ERC20 symbol.
    constructor(
        address _bridge,
        address _remoteToken,
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    )
        ERC20(_name, _symbol)
    {
        REMOTE_TOKEN = _remoteToken;
        BRIDGE = _bridge;
        DECIMALS = _decimals;
    }

    /// @notice Allows the StandardBridge on this network to mint tokens.
    /// @param _to     Address to mint tokens to.
    /// @param _amount Amount of tokens to mint.
    function mint(
        address _to,
        uint256 _amount
    )
        external
        virtual
        override(IOptimismMintableERC20)
        onlyBridge
    {
        _mint(_to, _amount);
        emit Mint(_to, _amount);
    }

    /// @notice Allows the StandardBridge on this network to burn tokens.
    /// @param _from   Address to burn tokens from.
    /// @param _amount Amount of tokens to burn.
    function burn(
        address _from,
        uint256 _amount
    )
        external
        virtual
        override(IOptimismMintableERC20)
        onlyBridge
    {
        _burn(_from, _amount);
        emit Burn(_from, _amount);
    }

    /// @notice ERC165 interface check function.
    /// @param _interfaceId Interface ID to check.
    /// @return Whether or not the interface is supported by this contract.
    function supportsInterface(bytes4 _interfaceId) external pure virtual returns (bool) {
        bytes4 iface1 = type(IERC165).interfaceId;
        // Interface corresponding to the updated OptimismMintableERC20 (this contract).
        bytes4 iface3 = type(IOptimismMintableERC20).interfaceId;
        return _interfaceId == iface1 || _interfaceId == iface3;
    }

    /// @notice Getter for the remote token. Legacy, also use REMOTE_TOKEN
    function l1Token() public view returns (address) {
        return REMOTE_TOKEN;
    }

    /// @notice Getter for the bridge. Legacy, also use BRIDGE
    function l2Bridge() public view returns (address) {
        return BRIDGE;
    }

    /// @notice Getter for the remote token. Legacy, also use REMOTE_TOKEN
    function remoteToken() public view returns (address) {
        return REMOTE_TOKEN;
    }

    /// @notice Getter for the bridge. Legacy, also use BRIDGE.
    function bridge() public view returns (address) {
        return BRIDGE;
    }

    /// @dev Returns the number of decimals used to get its user representation.
    /// For example, if `decimals` equals `2`, a balance of `505` tokens should
    /// be displayed to a user as `5.05` (`505 / 10 ** 2`).
    /// NOTE: This information is only used for _display_ purposes: it in
    /// no way affects any of the arithmetic of the contract, including
    /// {IERC20-balanceOf} and {IERC20-transfer}.
    function decimals() public view override returns (uint8) {
        return DECIMALS;
    }
}
```


# Bridging ZRC to Zircuit

## Easiest Option: Get ZRC on an Exchange, then withdraw to Zircuit

1. **Bybit:** <https://www.bybit.com/en/coin-price/zircuit/>
2. **Bitget:** <https://www.bitget.com/price/zircuit>
3. **Crypto.com:** <https://crypto.com/price/zircuit>
4. **Gate:** <https://www.gate.io/state_compliance_tips>
5. **Hashkey Global:** <https://global.hashkey.com/en-US/spot/ZRC_USDT>

Bridge \~$1 of ETH to Zircuit for transactions & gas fees

* Use Rhino <https://app.rhino.fi/bridge?chain=ARBITRUM&token=ETH&chainOut=ZIRCUIT>
* Or the Native Bridge <https://bridge.zircuit.com/>

## Most Affordable: Use Rhino to bridge ETH, then swap for ZRC

1. Bridge ETH or stablecoins to Zircuit, from a low gas-fee network (e.g. Base or Solana) using Rhino
   * <https://app.rhino.fi/bridge?chain=BASE&token=ETH&chainOut=ZIRCUIT>
   * If you're bridging stables, don't forget to bridge a bit of ETH for gas by selecting *"Get $1 ETH on Zircuit for transactions"*
2. Swap ETH or stables for ZRC using a DEX
   * Zuit: <https://zuit.xyz/swap>
   * Ocelex: <https://app.ocelex.fi/swap>
   * Dodo: <https://app.dodoex.io/swap/network/zircuit-mainnet>

## On Ethereum: Get ZRC on Uniswap, then Bridge to Zircuit

* Get ZRC on Uniswap on Ethereum
  * <https://app.uniswap.org/explore/tokens/ethereum/0xfd418e42783382e86ae91e445406600ba144d162>
* Bridge ZRC to Zircuit using the Native Bridge:
  * <https://bridge.zircuit.com/>
* Bridge \~$1 of ETH to Zircuit for transactions & gas fees
  * Use Rhino <https://app.rhino.fi/bridge?chain=ARBITRUM&token=ETH&chainOut=ZIRCUIT>
  * Or the Native Bridge <https://bridge.zircuit.com/>


# Bridged Token Addresses

Ethereum ERC-20 tokens that have corresponding bridged representations on the Zircuit Mainnet can be found on the Zircuit Block Explorer [website](https://explorer.zircuit.com/tokens).

A JSON list of the token mappings is also [available](https://bridge.zircuit.com/api/tokens).


# Escape Hatch

{% hint style="info" %}
This page is necessary for the Sepolia-based Zircuit Legacy Testnet, which was deprecated in June 2025. Escape hatch functionality is available on the mainnet though it is only usable if the chain if offline for a sufficiently long period of time.
{% endhint %}

## Overview

This page describes the necessary steps to withdraw funds from the Zircuit network in the event blocks cease to be produced for a long period of time. These steps bypass the conventional withdrawal mechanisms of Zircuit and allow for a forced withdrawal of assets.

These steps utilise functionally called an *escape hatch*, which allows users to remove Ether and tokens from a Zircuit network in case a state update has not occurred in the past 30 days. Assets that are withdrawn this way are said to be *escaped***.** For the purpose of this page, the L2 (layer 2) network is the Zircuit Legacy Testnet and the L1 (layer 1) is Sepolia.

{% hint style="info" %}
For the intentionally deprecated Legacy Testnet, the delay required to use this functionality is intentionally set to 5 days.
{% endhint %}

## Eligible Assets for Escape

The Zircuit Legacy Testnet escape hatch allows the withdrawal of Legacy Testnet ETH and WETH from `OptimismPortal`, and any ERC20 tokens that were bridged using Zircuit `L1StandardBridge` on Sepolia. These funds account for the majority of assets present on the Legacy Testnet.

Throughout this page, several assumptions are made in order to simplify the instructions to withdraw the assets described above. Future versions of this functionality will support additional assets; be sure to use the instructions for the right escape hatch.

## Escape Hatch Tooling

Scripts that implement the functionality necessary to escape ETH and ERC20 tokens can be found [here](https://github.com/zircuit-labs/zircuit-escape-hatch-tooling), which implement the functionality described below. Users who are not interested in understanding how the escape hatch works can simply use the scripts.

## Escaping with EOAs

These instructions can be used once the escape hatch functionality is enabled, due to a lack of state root update.

### Escaping ETH

Users can initiate ETH withdrawals via the `escapeETH()` function on the `OptimismPortalProxy` contract. This process requires generating and verifying inclusion proofs to reconstruct the user's balance on L2 at the last known valid state.

```solidity
    /// @notice Function to withdraw ETH that user had on L2 if more than 30 days passed since output root was
    /// published.
    /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root.
    /// @param _accountState State of user account on L2.
    /// @param _proof Proof of account state on L2.
    function escapeETH(
        Types.OutputRootProof calldata _outputRootProof,
        Types.AccountState calldata _accountState,
        bytes[] calldata _proof
    )
        external
```

Here is a TypeScript/ethers.js example showing how to prepare and submit an `escapeETH()` transaction.

```typescript
import * as ethers from "ethers";
import L2OutputOracle from "./L2OutputOracle.json";
import OptimismPortal from "./OptimismPortal.json";

const accountForEscapeAddress = "<EOA ADDRESS>"

const l1Provider = new ethers.providers.JsonRpcProvider("<L1 PROVIDER>")
const l2Provider = new ethers.providers.JsonRpcProvider("<L2 PROVIDER>")

const l2tol1MessagePasserAddress = "0x4200000000000000000000000000000000000016"
const L2OutputOracleAddress = "<L2OuputOracle ADDRESS>"
const OptimismPortalAddress = "<OptimismPortal ADDRESS>"
const L2OutputOracleContract = new ethers.Contract(L2OutputOracleAddress, L2OutputOracle.abi, l1Provider);
const OptimismPortalContract = new ethers.Contract(OptimismPortalAddress, OptimismPortal.abi, l1Provider);

// Fetch the most recent output root published 
const latestOutputIndex = await L2OutputOracleContract.latestOutputIndex();
const latestOutput = await L2OutputOracleContract.getL2Output(latestOutputIndex);
let l2OutputBlockNumber = latestOutput.l2BlockNumber

// Obtain the arguments required to verify the state root of the L2 agains the output root that was published in the L2OutputOracle
const l2Block  = await l2Provider.send('eth_getBlockByNumber', [l2OutputBlockNumber.toHexString(), false]);

let messagePasserStorageRoot = (await l2Provider.send('eth_getProof', [
  l2tol1MessagePasserAddress,
  [],
  l2OutputBlockNumber.toHexString()
])).storageHash


let outputRoot = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode([ "uint", "uint","uint","uint"],["0x0",l2Block.stateRoot,messagePasserStorageRoot,l2Block.hash]))
if (outputRoot !== latestOutput.outputRoot) {
    throw new Error("Roots do not match!")
}

// Obtain the merkle proof of the account state that we are trying to escape
let accountProof = await l2Provider.send('eth_getProof', [
      accountForEscapeAddress,
      [],
      l2OutputBlockNumber.toHexString()
])

let escapeTx = await OptimismPortalContract.populateTransaction.escapeETH(
    {
        version: ethers.constants.HashZero,
        stateRoot: l2Block.stateRoot,
        messagePasserStorageRoot: messagePasserStorageRoot,
        latestBlockhash: l2Block.hash
    },
    {
        nonce: accountProof.nonce,
        balance: accountProof.balance,
        storageRoot: accountProof.storageHash,
        codeHash: accountProof.keccakCodeHash,
    },
    accountProof.accountProof
)
```

### Escaping ERC20s

Unlike ETH, which is tracked via account balances, ERC20 token balances are stored in smart contract storage slots. Escaping ERC20s therefore requires not only proving the state of the ERC20 contract itself, but also providing a Merkle proof of the user’s token balance stored in the contract's storage trie.

The escape mechanism for ERC20s is available via the `escapeERC20()` function on the **`L1StandardBridgeProxy`** contract on Ethereum.

```solidity
    /// @notice Allows users to escape ERC20 tokens if no output root has been published for over 30 days.
    /// @param _localToken Address of the token on L1.
    /// @param _remoteToken Addres of the corresponding token on L2.
    /// @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root.
    /// @param _accountState State of the ERC20 token contract on L2.
    /// @param _stateProof Proof of the ERC20 contract state.
    /// @param _tokenBalance Balance the user had of the ERC20 on L2.
    /// @param _storageProof Proof of value on the storage slot with the user balance.
    function escapeERC20(
        address _localToken,
        address _remoteToken,
        Types.OutputRootProof calldata _outputRootProof,
        Types.AccountState calldata _accountState,
        bytes[] calldata _stateProof,
        uint256 _tokenBalance,
        bytes[] calldata _storageProof
    )
```

Here’s a TypeScript/ethers.js script that shows how to gather the necessary proofs and invoke the `escapeERC20()` function.

```typescript
import * as ethers from "ethers";
import L2OutputOracle from "./L2OutputOracle.json";
import L1StandardBridge from "./L1StandardBridge.json";

const accountForEscapeAddress = "<EOA ADDRESS>"
const l2ERC20ForEscape = "<ADDRESS OF ERC20 ON L2>"
const l1ERC20ForEscape = "<ADDRESS OF ERC20 ON L1>"
const l1Provider = new ethers.providers.JsonRpcProvider("<L1 PROVIDER>")
const l2Provider = new ethers.providers.JsonRpcProvider("<L2  PROVIDER>")

const l2tol1MessagePasserAddress = "0x4200000000000000000000000000000000000016"
const L2OutputOracleAddress = "<L2OuputOracle ADDRESS>"
const L1StandardBridgeAddress = "<L1StandarBridge ADDRESS>"

const L2OutputOracleContract = new ethers.Contract(L2OutputOracleAddress, L2OutputOracle.abi, l1Provider);
const L1StandardBridgeContract = new ethers.Contract(L1StandardBridgeAddress, L1StandardBridge.abi, l1Provider);

// Fetch the most recent output root published 
const latestOutputIndex = await L2OutputOracleContract.latestOutputIndex();
const latestOutput = await L2OutputOracleContract.getL2Output(latestOutputIndex);
let l2OutputBlockNumber = latestOutput.l2BlockNumber

// Obtain the arguments required to verify the state root of the L2 agains the output root that was published in the L2OutputOracle
const l2Block  = await l2Provider.send('eth_getBlockByNumber', [l2OutputBlockNumber.toHexString(), false]);

let messagePasserStorageRoot = (await l2Provider.send('eth_getProof', [
  l2tol1MessagePasserAddress,
  [],
  l2OutputBlockNumber.toHexString()
])).storageHash

let outputRoot = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode([ "uint", "uint","uint","uint"],["0x0",l2Block.stateRoot,messagePasserStorageRoot,l2Block.hash]))
if (outputRoot !== latestOutput.outputRoot) {
    throw new Error("Roots do not match!")
}

// Determine the storage slot were the user balance was stored in the L2 ERC20
const UserERC20BalanceStorageSlot = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["uint","uint"],[accountForEscapeAddress,0]))

// Get merkle proof of the ERC20 L2 account state and merkle proof of user balance
let ERC20AccountProof = await l2Provider.send('eth_getProof', [
      l2ERC20ForEscape,
      [UserERC20BalanceStorageSlot],
      l2OutputBlockNumber.toHexString()
])

let escapeTx = await L1StandardBridgeContract.populateTransaction.escapeERC20(
    l1ERC20ForEscape,
    l2ERC20ForEscape,
    {
        version: ethers.constants.HashZero,
        stateRoot: l2Block.stateRoot,
        messagePasserStorageRoot: messagePasserStorageRoot,
        latestBlockhash: l2Block.hash
    },
    {
        nonce: ERC20AccountProof.nonce,
        balance: ERC20AccountProof.balance,
        storageRoot: ERC20AccountProof.storageHash,
        codeHash: ERC20AccountProof.keccakCodeHash,
    },
    ERC20AccountProof.accountProof,
    ERC20AccountProof.storageProof[0].value,
    ERC20AccountProof.storageProof[0].proof
)
```

In this example, it is assumed that ERC20 balances are stored in a `mapping` at slot `0` as this is the slot of the default mintable ERC20 used by the `StandardBridge`.

## Escaping Assets Held in Smart Contracts

When users deposit assets into smart contracts on L2 (e.g., staking pools or token vaults), those assets are often pooled. This makes escape more complex than with EOAs, since:

* The contract may hold tokens on behalf of many users.
* Each user's entitlement must be calculated from contract state, not just simple balances.

To solve this, Zircuit introduces *resolver contracts* on L1, which encapsulate the logic needed to read the L2 contract’s storage and compute how much a user can rightfully claim. Resolver contracts are explained in this [paper](https://arxiv.org/abs/2503.23986).

{% hint style="info" %}
Resolver contracts are not supported for the Legacy Testnet escape hatch.
{% endhint %}

## Further Reading

Additional details can be found in the [paper](https://arxiv.org/abs/2503.23986) describing the escape hatch mechanism.


# Simulation

Enables developers to test, debug, and analyze transactions before executing them on the live network

### Overview

Transaction simulation allows you to preview the exact outcomes of your transactions without actually submitting them to the blockchain. This is invaluable for:

* **Pre-execution Testing**: Verify transaction success and gas usage before committing
* **Smart Contract Debugging**: Identify potential issues in contract interactions
* **State Change Analysis**: Understand how transactions will affect blockchain state
* **Gas Optimization**: Fine-tune gas limits and pricing strategies
* **Risk Management**: Prevent failed transactions and unexpected behaviors

### Providers

#### Tenderly

{% embed url="<https://docs.tenderly.co/node/rpc-reference/zircuit#integrations>" %}


# GUD Trading Engine (Beta)

### What's the Trading Engine?

The Trading Engine is a cross-chain trading infrastructure that aggregates liquidity across multiple blockchain networks and bridge protocols to provide users with the best possible rates for their trades.

#### Key Features

* **Multi-Protocol Integration**: Connects to leading cross-chain protocols like Across, deBridge, and more to ensure competitive pricing
* **Best Quote Aggregation**: Automatically compares quotes across all integrated protocols and returns the most favorable rate
* **Direct Wallet Execution**: Users maintain full custody - trades are executed directly from their wallets without intermediaries
* **Gasless Options**: Integrators can provide users with a gasless trading experience using off-chain EIP712 signatures
* **Universal Token Support**: Trade any token across supported chains with automatic routing and bridging
* **Fee Transparency**: Clear fee structure with customizable integrator fees


# API Endpoint

## Core Endpoints

### Base URL

```
https://trading.ai.zircuit.com/api/engine/v1
```

### Authentication

All API requests require authentication using a Bearer token.

```bash
Authorization: Bearer API_KEY
```

> <mark style="color:blue;">**ⓘ Info**</mark>
>
> Contact the team to get the API\_KEY, these are also shared in hackathons!

### Methods

#### 1. Get Trade Estimate

Get a quote and all necessary data to execute a trade directly from a user's wallet, or make your user sign it and use your own relayer to provide gasless experience.

```http
POST /order/estimate
```

**Request Body**

For example, a trade of 1 USDC from Base to USDT in Optimism:

```json
{
  "srcChainId": 8453,
  "srcToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
  "srcAmountWei": "1000000",
  "destChainId": 10,
  "destToken": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
  "slippageBps": "100",
  "userAccount": "0x...",
  "destReceiver": "0x...",
  "feeRecipient": "0x...",
  "feeBps": "100"
}
```

> <mark style="color:blue;">**ⓘ Info**</mark>
>
> Use this special addresses for native tokens: `0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE`

**Parameters**

| Parameter      | Type   | Required | Description                                               |
| -------------- | ------ | -------- | --------------------------------------------------------- |
| `srcChainId`   | number | Yes      | Source chain ID                                           |
| `srcToken`     | string | Yes      | Source token contract address                             |
| `srcAmountWei` | string | Yes      | Amount in wei (string to handle large numbers)            |
| `destChainId`  | number | Yes      | Destination chain ID                                      |
| `destToken`    | string | Yes      | Destination token contract address                        |
| `slippageBps`  | number | Yes      | Slippage tolerance over the estimated output token amount |
| `userAccount`  | string | Yes      | User's wallet address                                     |
| `destReceiver` | string | Yes      | Recipient address on destination chain                    |
| `feeRecipient` | string | No       | Fee recipient address on SOURCE chain                     |
| `feeBps`       | string | No       | Fee percentage over the input token amount                |

**Example response**

```json
{
  "message": "Trade estimate determined",
  "data": {
    "trade": {
      "tradeId": "0x1234...abcd",
      "nonce": "1",
      "userAccount": "0x1234567890123456789012345678901234567890",
      "destReceiver": "0x1234567890123456789012345678901234567890",
      "srcToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "srcTokenAmount": "1000000000",
      "srcChainId": 8453,
      "destToken": "0x7F5c764cBc14f9669B88837ca1490cCa17c31607",
      "destTokenAmount": "995000000",
      "destTokenMinAmount": "993000000",
      "destChainId": 10,
      "adapter": "0xAdapterAddress",
      "protocolNativeFee": "0",
      "data": "0x...",
      "deadline": 1699127056,
      "fees": [
        {
          "bps": 100,
          "recipient": "0x1234567890123456789012345678901234567890"
        },
        {
          "bps": 25,
          "recipient": "0xprotocol_fee_recipient"
        }
      ],
      "guardianSignature": "0xabcde...",
      "eip712": {
        "types": {
          "EIP712Domain": [...],
          "Fee": [...],
          "Trade": [...]
        },
        "domain": {
          "name": "GudEngine",
          "version": "1.0.0",
          "chainId": 8453,
          "verifyingContract": "0xGudEngineAddress"
        },
        "message": {
          "tradeId": "0x1234...abcd",
          "nonce": 1,
          "userAccount": "0x1234567890123456789012345678901234567890",
          "destReceiver": "0x0x0000000000000000000000001234567890123456789012345678901234567890",
          "srcToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
          "srcTokenAmount": "1000000000",
          "srcChainId": 8453,
          "destToken": "0x0x0000000000000000000000007F5c764cBc14f9669B88837ca1490cCa17c31607",
          "destTokenMinAmount": "993000000",
          "destChainId": 10,
          "adapter": "0xAdapterAddress",
          "protocolNativeFee": "0",
          "data": "0x...",
          "deadline": 1699127056,
          "fees": [...],
          "guardianSignature": "0xabcde..."
        },
        "primaryType": "Trade"
      }
    },
    "tx": {
      "chainId": 8453,
      "data": "0x1234abcd...", // Encoded calldata for GudEngine.execute()
      "from": "0x1234567890123456789012345678901234567890",
      "to": "0xGudEngineContractAddress",
      "value": "0" // ETH value to send (usually 0 for ERC20)
    }
  }
}
```

### Fee Structure

#### Integrator Fees

You can specify optional custom fees for your integration. The user will be charged a percentage of input tokens:

* `feeRecipient`: Your fee recipient address in **source** chain
* `feeBps`: Fee percentage in basis points (1 BPS = 0.01%, 10000 BPS = 100%)

`feeRecipient` will receive the fees in the same transaction.

#### Protocol Fees

The protocol supports adding a fee to support the infrastructure. It is not enabled at the moment.


# Supported Chains & Contract Addresses

## Mainnets

<table><thead><tr><th width="172.12890625">Chain</th><th width="98.0703125">ID</th><th>GudEngine Address</th></tr></thead><tbody><tr><td>Ethereum Mainnet</td><td>1</td><td>0x0792C46723d479D4C29De5D78D93C0146EdF3f5B</td></tr><tr><td>Base</td><td>8453</td><td>0x0792C46723d479D4C29De5D78D93C0146EdF3f5B</td></tr><tr><td>Zircuit</td><td>48900</td><td>0x0792C46723d479D4C29De5D78D93C0146EdF3f5B</td></tr><tr><td>OP Mainnet</td><td>10</td><td>0x0792C46723d479D4C29De5D78D93C0146EdF3f5B</td></tr><tr><td>Arbitrum One</td><td>42161</td><td>0x0792C46723d479D4C29De5D78D93C0146EdF3f5B</td></tr></tbody></table>

## Testnets

<table><thead><tr><th width="171.63671875">Chain</th><th width="97.390625">ID</th><th>GudEngine Address</th></tr></thead><tbody><tr><td>Sepolia</td><td>11155111</td><td>0x7Fd9F1BBFF0f12822Dce087D4020310b06a01F70</td></tr><tr><td>Base Sepolia</td><td>84532</td><td>0x7Fd9F1BBFF0f12822Dce087D4020310b06a01F70</td></tr><tr><td>OP Sepolia</td><td>11155420</td><td>0x7Fd9F1BBFF0f12822Dce087D4020310b06a01F70</td></tr><tr><td>Arbitrum Sepolia</td><td>421614</td><td>0x7Fd9F1BBFF0f12822Dce087D4020310b06a01F70</td></tr></tbody></table>

> <mark style="color:blue;">**ⓘ Info**</mark>
>
> In Testnets the liquidity is limited, and the majority of the tokens will not be available. Slippage/fees can be high in some providers to avoid abuse. Consider using just native ETH as source and destination in small amounts for cross chain testing.


# Development Guide

This page describes how to execute GUD Engine trade in a few steps.

## Step 1: Get Trade Estimate

Call the `/order/estimate` endpoint to get a quote and transaction data.

## Step 2: Handle ERC20 Approvals

For ERC20 tokens, remember to check if the user has sufficient allowance for the GudEngine contract:

```javascript
// Check current allowance
const allowance = await erc20Contract.allowance(userAddress, gudEngineAddress);

if (allowance < srcAmountWei) {
  // Request approval
  await erc20Contract.approve(gudEngineAddress, srcAmountWei);
}
```

## Step 3: Transaction Execution

Gud Engine allows your users to execute trade - either directly by themselves or in a gasless fashion. We will explain both cases below.

### Case 1: User executing the transaction

Signatures are not needed if the address calling the engine is the trader. The backend provide calldata ready to be executed:

```javascript
const txHash = await userWallet.sendTransaction({
  to: tx.to,
  data: tx.data,
  value: tx.value
});
```

### Case 2: Gasless Execution For Your Users

Protocol allow relayers to execute ERC-20 trades on behalf of traders by using off-chain signatures. User trading must sign the data contained in `trade.eip712` object using user's wallet. `trade.eip712` object is formatted to be compatible with main blockchain libraries like viem or ethers.

> <mark style="color:blue;">**ⓘ Info**</mark>\
> This flow is only available for trades with ERC-20 tokens as source token. For trades with native ETH as source token, `trade.eip712` object will be `undefined` in the estimated quote.

```javascript
const userSignature = await userWallet.signTypedData({
  types: eip712.types,
  domain: eip712.domain,
  message: eip712.message,
  primaryType: eip712.primaryType
});
```

Update the calldata with the user's signature and send the transaction:

```javascript
const tradeStruct = {
  ...estimate.data.trade.eip712.message, // This contains all the trade data
  signature: userSignature,              // User's signature of the trade
};

// Encode the final calldata
const calldata = encodeFunctionData({
  abi: gudEngineAbi, // Find it at the end of this document
  functionName: 'execute',
  args: [tradeStruct]
});

// Send the transaction from any account
const txHash = await relayerWallet.sendTransaction({
  to: estimate.data.tx.to,
  data: calldata,
  value: 0
  // WARNING: Your relayer should not send native ETH in behalf of users. Otherwise, your relayer account will be paying the trade instead of the user!
  // A trade with native ETH as input token could drain your relayer account!
});
```

> **⚠ Warning**\
> Your relayer should not send native ETH in behalf of users. Otherwise, your relayer account will be paying the trade instead of the user!

### Error Handling

#### Common Error Codes

<table><thead><tr><th width="143.02734375">Status Code</th><th>Error</th><th>Description</th></tr></thead><tbody><tr><td>400</td><td>Bad Request</td><td>Invalid request parameters</td></tr><tr><td>401</td><td>Unauthorized</td><td>Missing or invalid API key</td></tr><tr><td>404</td><td>Not Found</td><td>No quotes found for the requested trade</td></tr><tr><td>429</td><td>Too Many Requests</td><td>Rate Limit Exceeded</td></tr><tr><td>500</td><td>Internal Server Error</td><td>Server error during processing</td></tr></tbody></table>

#### Error Response Format

```json
{
  "error": "Error description",
  "message": "Detailed error message"
}
```

> <mark style="color:blue;">**ⓘ Info**</mark>\
> As the Trading Engine API is in beta, our API could limit or ban you if the number of requests is excessive. If you think you are being rate limited or have specific needs, please contact us.


# L1 Bridge

Contracts involved in canonical messaging between Ethereum and Zircuit

Contracts involved in canonical messaging between Ethereum and Zircuit

## Ethereum

| Contract Name                       | Contract Address                                                                                                      |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `L1CrossDomainMessengerProxy`       | [0x2a721cBE81a128be0F01040e3353c3805A5EA091](https://etherscan.io/address/0x2a721cBE81a128be0F01040e3353c3805A5EA091) |
| `L1StandardBridgeProxy`             | [0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8](https://etherscan.io/address/0x386B76D9cA5F5Fb150B6BFB35CF5379B22B26dd8) |
| `ProxyAdmin`                        | [0x5B1Ef673d9c316b3eE9Ed3B4E3cC84952bfC5257](https://etherscan.io/address/0x5B1Ef673d9c316b3eE9Ed3B4E3cC84952bfC5257) |
| `SystemConfigProxy`                 | [0x30F82a1Ca89226E8b8815d6EbB728e3b18a428ff](https://etherscan.io/address/0x30F82a1Ca89226E8b8815d6EbB728e3b18a428ff) |
| `SP1VerifierGateway`                | [0xf35A4088eA0231C44B9DB52D25c0E9E2fEe31f67](https://etherscan.io/address/0xf35A4088eA0231C44B9DB52D25c0E9E2fEe31f67) |
| `OptimismPortalProxy`               | [0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1](https://etherscan.io/address/0x17bfAfA932d2e23Bd9B909Fd5B4D2e2a27043fb1) |
| `OptimismMintableERC20FactoryProxy` | [0xc77ece87C91C44AFb5f19638f9a0F75b5d90E932](https://etherscan.io/address/0xc77ece87C91C44AFb5f19638f9a0F75b5d90E932) |
| `L2OutputOracleProxy`               | [0x92Ef6Af472b39F1b363da45E35530c24619245A4](https://etherscan.io/address/0x92Ef6Af472b39F1b363da45E35530c24619245A4) |
| `L1ERC721BridgeProxy`               | [0x994eEb321F9cD79B077a5455fC248c77f30Dd244](https://etherscan.io/address/0x994eEb321F9cD79B077a5455fC248c77f30Dd244) |
| `SuperchainConfigProxy`             | [0x745393Cc03b5fE668ECd52c0E625f59aAD6D3Da0](https://etherscan.io/address/0x745393Cc03b5fE668ECd52c0E625f59aAD6D3Da0) |
| `ResolverRegistryProxy`             | [0x6c89104690452AD7e209f0ab72287C2561d5cF0E](https://etherscan.io/address/0x6c89104690452AD7e209f0ab72287C2561d5cF0E) |
| `CrisisControlRegistryProxy`        | [0x7d43EB137185aEa81A020563099e940Bb380F35e](https://etherscan.io/address/0x7d43EB137185aEa81A020563099e940Bb380F35e) |

## Sepolia

| Contract Name                       | Contract Address                                                                                                              |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `L1CrossDomainMessengerProxy`       | [0x6aC894b2a32fED0DC09c2c617277c2F2BF1cf130](https://sepolia.etherscan.io/address/0x6aC894b2a32fED0DC09c2c617277c2F2BF1cf130) |
| `L1StandardBridgeProxy`             | [0x87a7E2bCA9E35BA49282E832a28A6023904460D8](https://sepolia.etherscan.io/address/0x87a7E2bCA9E35BA49282E832a28A6023904460D8) |
| `ProxyAdmin`                        | [0x540fF7ab7bb9894E408bb650ed4f060c390c9B6d](https://sepolia.etherscan.io/address/0x540fF7ab7bb9894E408bb650ed4f060c390c9B6d) |
| `SystemConfigProxy`                 | [0x3D3fC87Ec70705Ba6FDDAcc72D5C71440F64463F](https://sepolia.etherscan.io/address/0x3D3fC87Ec70705Ba6FDDAcc72D5C71440F64463F) |
| `SP1VerifierGateway`                | [0xb9985758eed32892441fAF6FE852B8c6d6847205](https://sepolia.etherscan.io/address/0xb9985758eed32892441fAF6FE852B8c6d6847205) |
| `OptimismPortalProxy`               | [0x4E21A71Ac3F7607Da5c06153A17B1DD20E702c21](https://sepolia.etherscan.io/address/0x4E21A71Ac3F7607Da5c06153A17B1DD20E702c21) |
| `OptimismMintableERC20FactoryProxy` | [0x9641B86870bbe53264492854Ad7af32E39079dfe](https://sepolia.etherscan.io/address/0x9641B86870bbe53264492854Ad7af32E39079dfe) |
| `L2OutputOracleProxy`               | [0xd69D3AC5CA686cCF94b258291772bc520FEAf211](https://sepolia.etherscan.io/address/0xd69D3AC5CA686cCF94b258291772bc520FEAf211) |
| `L1ERC721BridgeProxy`               | [0x987d6008BB5C1e94a72b60a4bbFaAF67Ef09746E](https://sepolia.etherscan.io/address/0x987d6008BB5C1e94a72b60a4bbFaAF67Ef09746E) |
| `SuperchainConfigProxy`             | [0x9da6c219742518AE98E364184cE32fe81c08Ba2A](https://sepolia.etherscan.io/address/0x9da6c219742518AE98E364184cE32fe81c08Ba2A) |
| `ResolverRegistryProxy`             | [0x2B49136665Dc78347893BFaCf3F9e2af546A1069](https://sepolia.etherscan.io/address/0x2B49136665Dc78347893BFaCf3F9e2af546A1069) |
| `CrisisControlRegistryProxy`        | [0xBEa946FF59F97Eec54e4B32a01e68d67fb4Df5A6](https://sepolia.etherscan.io/address/0xBEa946FF59F97Eec54e4B32a01e68d67fb4Df5A6) |

## Definitions

### L1Standard Bridge

The `L1StandardBridge` handles ETH and ERC20 transfers between L1 and L2. For L1-native ERC20 tokens, they're escrowed in this contract, while L2-native ones are burnt. ETH is stored in the OptimismPortal contract.

*NOTE: The contract doesn't support all ERC20 variations, e.g., those with transfer fees, rebasing tokens, or blocklists.*

#### Functions:

`bridgeETH`: Sends ETH the sender's address on L2.

`bridgeETHTo`: Sends ETH to a receiver's address on L2.

`bridgeERC20`: Sends ERC20 tokens to the sender's address onon L2.

`depositERC20To`: Deposit an ERC20 for a target on L2.

`receive`: Bridge ETH via direct send to the bridge.

`finalizeBridgeETH`: Finalizes an ETH bridge.

`finalizeBridgeERC20`: Finalizes an ERC20 bridge.

### L1CrossDomainMessenger

The contract serves as an interface for managing cross-layer communication between L1 and L2. It provides functionalities to send and receive messages, potentially facilitating interactions like token transfers or contract calls between different layers of the network.

### L1ERC721Bridge

The `L1ERC721Bridge` contract facilitates ERC721 token transfers between Ethereum and Zircuit by escrowing tokens on L1, liaising with the L2 bridge, and ensuring secure, transparent transfers. It's vital for scaling NFTs and ERC721 assets across blockchain layers.

### L2OutputOracle

The contract holds an array of L2 state outputs, each representing the L2 chain state at specific block numbers. It permits adding new outputs and removing existing ones based on conditions.

#### Events:

`OutputProposed`: Announces a new output with its details.

#### Functions:

`proposeL2Output`: Submits a new L2 output, ensuring block number and timestamp accuracy.

`getL2Output`: Retrieves an output by index.

`getL2OutputIndexAfter` & `getL2OutputAfter`: Uses binary search to find and return outputs based on block numbers.

`finalizationPeriodSeconds`: The minimum time (in seconds) that must elapse before a withdrawal can be finalized.

### OptimismPortal

The OptimismPortal is responsible for managing cross-chain communication between L1 and Zircuit L2. It handles depositing messages from L1 to L2 and proving and finalizing withdrawal messages from L2 to L1.

#### Functions

`minimumGasLimit`: Computes the minimum gas limit for a deposit.

`receive()`: Allows users to send ETH directly to the contract and have the funds deposited to their address on L2.

`donateETH`: Accepts ETH without triggering a deposit to L2.

`proveWithdrawalTransaction`: Allows a user to prove a withdrawal transaction by providing necessary proofs, upon which the withdrawn funds are released and forwarded to the targeted receiver.

`depositTransaction`: Accepts deposits of ETH and data, and triggers a TransactionDeposited event.

`isOutputFinalized`: Checks if a given L2 output is finalized.

### Verifier

The `Verifier` is responsible for checking Zircuit validity and template proofs.


# L2 Predeploys

A contract placed in the L2 genesis state. They operate natively within the EVM instead of executing as external native code outside the EVM environment

A contract placed in the L2 genesis state. They operate natively within the EVM instead of executing as external native code outside the EVM environment

## Zircuit Mainnet & Garfield Testnet

| Contract Name                   | Contract Address                                                                                                              |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `BaseFeeVault`                  | [0x4200000000000000000000000000000000000019](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000019) |
| `GasPriceOracle`                | [0x420000000000000000000000000000000000000F](https://explorer.zircuit.com/address/0x420000000000000000000000000000000000000F) |
| `L1Block`                       | [0x4200000000000000000000000000000000000015](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000015) |
| `L1FeeVault`                    | [0x420000000000000000000000000000000000001A](https://explorer.zircuit.com/address/0x420000000000000000000000000000000000001A) |
| `L2CrossDomainMessenger`        | [0x4200000000000000000000000000000000000007](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000007) |
| `L2ERC721Bridge`                | [0x4200000000000000000000000000000000000014](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000014) |
| `L2StandardBridge`              | [0x4200000000000000000000000000000000000010](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000010) |
| `L2ToL1MessagePasser`           | [0x4200000000000000000000000000000000000016](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000016) |
| `OptimismMintableERC20Factory`  | [0x4200000000000000000000000000000000000012](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000012) |
| `OptimismMintableERC721Factory` | [0x4200000000000000000000000000000000000017](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000017) |
| `ProxyAdmin`                    | [0x4200000000000000000000000000000000000018](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000018) |
| `SequencerFeeVault`             | [0x4200000000000000000000000000000000000011](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000011) |
| `WETH9`                         | [0x4200000000000000000000000000000000000006](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000006) |
| `SchemaRegistry`                | [0x4200000000000000000000000000000000000020](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000020) |
| `EAS`                           | [0x4200000000000000000000000000000000000021](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000021) |
| `L2Controller`                  | [0x4200000000000000000000000000000000000100](https://explorer.zircuit.com/address/0x4200000000000000000000000000000000000100) |

## Definitions

### GasPriceOracle

This contract offers an API for querying how large the L1 portion of the transaction fee will be, providing transparency and predictability in fee computation.

#### Functions

`getL1Fee(bytes memory _data)`: Computes the L1 portion of the fee for a given RLP-encoded transaction.

`gasPrice()`: Retrieves the current L2 gas price (base fee).

`baseFee()`: Retrieves the current L2 base fee.

`overhead()`: Retrieves the current fee overhead from the L1Block contract.

`scalar()`: Retrieves the current fee scalar from the L1Block contract.

`l1BaseFee()`: Retrieves the latest known L1 base fee from the L1Block contract.

`decimals()`: Retrieves the number of decimals used in the scalar.

`getL1GasUsed(bytes memory _data)`: Computes the amount of L1 gas used for a given transaction, considering the overhead and padding.

### L1Block

The `L1Block`contract allows users on L2 to access information about the latest L1 block.

### L2CrossDomainMessenger

The `L2CrossDomainMessenger` contract is instrumental in facilitating communication between L1 and L2, allowing for messages to be sent from L2 to L1 and ensuring the validity and safety of these messages. It abstracts lower-level details, providing a more user-friendly interface for developers working with cross-domain communication.

### L2ERC721Bridge

Overall, the `L2ERC721Bridge` contract plays a crucial role in the cross-layer functionality of ERC721 tokens, allowing them to be transferred between L1 and L2 while maintaining the integrity and ownership of the tokens.

The contract acts as a minter for new tokens on L2 when it detects deposits into the L1 ERC721 bridge.

It also acts as a burner for tokens being withdrawn from L2 to L1.

It ensures seamless cross-layer functionality for ERC721 tokens, enabling them to be transferred between Ethereum and Zircuit without losing ownership or metadata.

#### Events

Emits `ERC721BridgeFinalized` when a deposit is finalized on L2.

Emits `ERC721BridgeInitiated` when a withdrawal is initiated from L2.

### L2StandardBridge

This contract manages asset transfers between L2 and L1. For L2-native ERC20 tokens, withdrawals escrow them in this contract, while L1-native ERC20s are burnt on L2. Deposits from L1 to L2 are finalized by minting or releasing assets on L2.

#### Functions:

`withdraw` & `withdrawTo`: Initiate L2 to L1 withdrawals. The contract discerns between ETH and ERC20 withdrawals.

**Limitations:** The contract may not support all ERC20 types, especially those with transfer fees, rebasing, or blocklists.

### L2toL1MessagePasser

This contract serves as the storage and management system for these L2-to-L1 messages, ensuring they are uniquely identified and can be processed on L1.

#### Events:

`MessagePassed`: Triggered when a withdrawal is initiated. Provides details about the message, including the sender, target, value, and the unique withdrawal hash.

#### Functions:

`receive` function that allows users to send ETH directly to it, which in turn initiates a withdrawal.

`initiateWithdrawal`: This is the primary function that users or other contracts will call to send a message from L2 to L1. It constructs a withdrawal message based on parameters like target address on L1, gas limit, data, and the value (ETH amount). The function then computes the hash of this message and stores it in an append-only Merkle tree with root `messageRoot`. An event `MessagePassed` is then emitted with details of the message.

`messageNonce`: Returns the next nonce for a message to be sent. The nonce also embeds the message version in its upper two bytes. This approach ensures that different message structures can be distinguished and handled accordingly in the future.

`isUsedNonceForWithdrawal` : Checks if a nonce was used for withdrawals.


# L2 Precompiles

A special type of smart contract built directly into the EVM that executes common cryptographic operations with greater efficiency than contracts written in high-level languages like Solidity

A special type of smart contract built directly into the EVM that executes common cryptographic operations with greater efficiency than contracts written in high-level languages like Solidity

Note that the support for these precompiles may change as the Zircuit proof system evolves. They should be used with caution. If you have any questions, please reach out via discord.

## EVM Precompiles

List of precompiles for both Zircuit Mainnet and Garfield Testnet:

| Contract Name           | Address |
| ----------------------- | ------- |
| ECRecover               | 0x01    |
| SHA256                  | 0x02    |
| RIPEMD                  | 0x03    |
| Identity                | 0x04    |
| ModExp                  | 0x05    |
| ECAdd                   | 0x06    |
| ECScalarMul             | 0x07    |
| ECPairing               | 0x08    |
| BLAKE2b                 | 0x09    |
| Point evaluation        | 0x0a    |
| BLS12\_G1ADD            | 0x0b    |
| BLS12\_G1MSM            | 0x0c    |
| BLS12\_G2ADD            | 0x0d    |
| BLS12\_G2MSM            | 0x0e    |
| BLS12\_PAIRING\_CHECK   | 0x0f    |
| BLS12\_MAP\_FP\_TO\_G1  | 0x10    |
| BLS12\_MAP\_FP2\_TO\_G2 | 0x11    |
| P256VERIFY              | 0x100   |


# L2 Preinstalls

General use contracts made available to improve Zircuit's UX

General use contracts made available to improve Zircuit's UX

## Zircuit Mainnet & Garfield Testnet

| Contract Name                                                                                                                       | Contract Address                                                                                                              |
| ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| [`Safe`](https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafe.sol)                                    | [0x69f4D1788e39c87893C980c06EdF4b7f686e2938](https://explorer.zircuit.com/address/0x69f4D1788e39c87893C980c06EdF4b7f686e2938) |
| [`SafeL2`](https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/GnosisSafeL2.sol)                                | [0xfb1bffC9d739B8D520DaF37dF666da4C687191EA](https://explorer.zircuit.com/address/0xfb1bffC9d739B8D520DaF37dF666da4C687191EA) |
| [`Multicall3`](https://github.com/mds1/multicall/tree/main)                                                                         | [0xcA11bde05977b3631167028862bE2a173976CA11](https://explorer.zircuit.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) |
| [`MultiSend`](https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/libraries/MultiSend.sol)                      | [0x998739BFdAAdde7C933B942a68053933098f9EDa](https://explorer.zircuit.com/address/0x998739BFdAAdde7C933B942a68053933098f9EDa) |
| [`MultiSendCallOnly`](https://github.com/safe-global/safe-smart-account/blob/v1.3.0/contracts/libraries/MultiSendCallOnly.sol)      | [0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B](https://explorer.zircuit.com/address/0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B) |
| [`SafeSingletonFactory`](https://github.com/safe-global/safe-singleton-factory/blob/main/source/deterministic-deployment-proxy.yul) | [0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7](https://explorer.zircuit.com/address/0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7) |
| [`DeterministicDeploymentProxy`](https://github.com/Arachnid/deterministic-deployment-proxy)                                        | [0x4e59b44847b379578588920cA78FbF26c0B4956C](https://explorer.zircuit.com/address/0x4e59b44847b379578588920cA78FbF26c0B4956C) |
| [`create2deployer`](https://github.com/pcaversaccio/create2deployer)                                                                | [0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2](https://explorer.zircuit.com/address/0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2) |
| [`permit2`](https://github.com/Uniswap/permit2)                                                                                     | [0x000000000022D473030F116dDEE9F6B43aC78BA3](https://explorer.zircuit.com/address/0x000000000022D473030F116dDEE9F6B43aC78BA3) |
| [`ERC-4337 EntryPoint`](https://github.com/eth-infinitism/account-abstraction/blob/v0.6.0/contracts/core/EntryPoint.sol)            | [0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789](https://explorer.zircuit.com/address/0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789) |
| [`ERC-4337 SenderCreator`](https://github.com/eth-infinitism/account-abstraction/blob/v0.6.0/contracts/core/SenderCreator.sol)      | [0x7fc98430eAEdbb6070B35B39D798725049088348](https://explorer.zircuit.com/address/0x7fc98430eAEdbb6070B35B39D798725049088348) |


# CREATE2 Deployments

In the Ethereum Virtual Machine, `CREATE2` is an opcode that allows for the deployment of smart contracts with deterministic addresses. This is particularly useful in complex decentralized applications (dApps) where the ability to predict the address of a contract before it is deployed can simplify interactions between multiple contracts and improve security.

It is especially valuable in the context of L2, as it enables consistent contract addresses across L1 and L2, simplifying cross-layer interactions.

To facilitate safe usage of the `CREATE2` opcode, there is a `create2deployer` Preinstall deployed on Zircuit, which can be used for deterministic deployment addresses of smart contracts. The Preinstalls are accessible on the addresses specified on [https://github.com/zircuit-labs/docs/blob/main/addresses/preinstalls/broken-reference/README.md](https://github.com/zircuit-labs/docs/blob/main/addresses/preinstalls/broken-reference/README.md "mention").

[create2deployer](https://github.com/pcaversaccio/create2deployer) provides two functions. `computeAddress(bytes32 salt, bytes32 codeHash)` returns the address where a contract will be stored if deployed through the deploy function. The `salt` is a 32-byte value used to add additional entropy to the contract creation process and prevent collisions with the default `CREATE` opcode. The `codeHash` is the *keccak256* hash of the contract's bytecode that will be deployed. This includes the compiled bytecode of the contract itself, potentially combined with any constructor arguments encoded into it. Any change in `salt` or `codeHash` will result in a new address. After computing the address, the `deploy(uint256 value, bytes32 salt, bytes memory code)` function can be used to deploy the contract on the address computed before. The `value` field in the `deploy` function is used to specify the amount of Ether (in wei) that should be sent along with the contract deployment transaction. This parameter serves several purposes:

1. Funding the new contract: If the contract being deployed needs initial Ether to function, this value will be transferred to the new contract's balance upon creation.
2. Payable constructors: If the contract being deployed has a `payable` constructor, this `value` can be used to send Ether to the constructor function during deployment.

The `code` parameter in the `deploy` function should be the complete bytecode including encoded constructor arguments.

Apart from this, some smart contract development tools like Foundry automatically make use of a `CREATE2` Proxy like this and the steps above do not have to be done manually necessarily.


# Architecture

## Introduction

Zircuit is a EVM-compatible zero-knowledge (zk) rollup that delivers robust security guarantees through validity proofs built on proven rollup infrastructure. We combine the battle-tested OP Stack foundation with [OP-Succinct](https://succinctlabs.github.io/op-succinct/) built with a modified version of [Kona](https://github.com/op-rs/kona/tree/main/bin/client) to support [Sequencer-Level Security (SLS)](/info/architecture/sls-deep-dive). SLS represents a distinctive capability that sets Zircuit apart from other networks. This feature proactively examines each transaction prior to block inclusion, detecting and preventing security threats and malicious behavior.

## Core Architecture

Zircuit's architecture is built around three fundamental components that work together to process transactions, generate proofs, and maintain secure interaction with Ethereum Layer 1:

### Sequencer

The sequencer layer is responsible for ordering and batching transactions to construct Layer 2 blocks. Built on the [OP Stack](https://docs.optimism.io/stack/getting-started) infrastructure, sequencers ensure reliable transaction processing and block production while maintaining compatibility with existing Ethereum tooling and development workflows.

### Provers

Zircuit integrates [Succinct's SP1](https://docs.succinct.xyz/docs/sp1/introduction) zkVM to generate zero-knowledge validity proofs for each block. These provers create cryptographic evidence that all transactions in a block have been executed correctly according to the protocol rules, enabling trustless verification without requiring full re-execution of the transactions.

### Smart Contracts

The Layer 1 smart contract infrastructure manages the core protocol functions, including state root updates, proof verification, and withdrawal processing. These contracts serve as the trust anchor between Zircuit's Layer 2 execution environment and Ethereum.

### Key Benefits

The hybrid architecture delivers several advantages over traditional rollup designs:

* **Fast Finality**: Validity proofs provide immediate cryptographic finality without waiting periods
* **No Challenge Period**: Withdrawals can be processed immediately upon proof verification
* **Battle-Tested Foundation**: Built on OP Stack's proven infrastructure for maximum reliability
* **Enhanced Security**: Zero-knowledge proofs provide mathematical guarantees of execution correctness

This documentation will guide you through the technical implementation details, integration patterns, and development workflows for building on Zircuit's rollup architecture.


# Transaction Flow

<figure><img src="https://lh7-us.googleusercontent.com/BGexrJnT4xLpUStqzmCDYeqlSsYVpeKwATjfqFaVyHiO9jB8-05gDHNeCCDs54BJGC1RPXu3-8HQu2C18dfLumU9j7Vszy3oJyR284348-9ze5pRgBnbZ8L4O3E6wiGUSES1Fl5J7CU8nFQUF5LbuWY" alt=""><figcaption><p>Transaction flow of Zircuit</p></figcaption></figure>

Transactions on the rollup may originate from L1 or directly from the L2.

A transaction that originates on the L1 may be a **deposit transaction**, where ETH or other assets are bridged onto the L2, or a **cross domain function call**. An L2 transaction may be a transfer of assets between L2 accounts or a call to a contract deployed on the L2 from an account with funds on the L2. In either case, the transaction is processed by the rollup node, which consists of the sequencer, the execution engine, and the batcher.

The sequencer directs the execution engine as to which transactions should be in a block by adding deposit transactions generated from smart contract events on L1.

The batcher submits L2 transaction batches to the L1, so that users have full data availability. Users can use this data as this is a so-called soft commitment that their transaction is completed. A soft commitment is a commitment that the transaction will be included in the L2 chain, but it is not considered final yet as its execution has not yet been proved.

The execution engine processes the transactions in the batches and results in a new L2 state. The execution engine processes these transactions by putting them in L2 blocks.

These L2 blocks are then processed by Zircuit's zkVM-based proving system using a two-phase approach. In the first phase, Zircuit generates **range proofs** for several L2 blocks using SP1 (Succinct's zkVM) to prove Kona's execution. These range proofs guarantee that each state transition between L2 blocks was executed correctly - that balances are computed properly, smart contract opcodes execute as intended, and so forth. In the second phase, Zircuit performs **aggregate proofs** by verifying and combining multiple range proofs together to maximize the number of L2 transactions that get rolled up in a single L1 proof verification.

The proofs for batches of L2 blocks are verified on Ethereum via a smart contract. After verification succeeds, the L2 state root is updated on the relevant contracts, recording the changes contained in the L2 blocks included in the batch whose proof was just verified. At this time, those L2 blocks are considered final. Withdrawals from the L2 are now possible without an additional delay.

The verification of the validity proof concludes the transaction flow at a glance. The next two sections will dive into the processing of deposit, L2, and withdrawal transactions.

### Deposits and L2 Transactions

This section covers the architecture related to deposits and L2 transactions in greater detail. A deposit transaction bridges assets like Ether or ERC-20 tokens from Ethereum to Zircuit. An L2 transaction is one that originates on Zircuit itself, like a transfer of Ether between accounts, or smart contract calls on Zircuit.

The following image highlights the specific components of Zircuit that are involved in a deposit or L2 transaction.

<figure><img src="https://lh7-us.googleusercontent.com/sS_LHktmy1MuM47qsFDpfuw-6bwRbazb4NykEliU1f8hcImQ6GCQadCRp9_h6ROJGWJc67ie5V_IW0ijeYIgYpxwjXN9HG_pUyCJ3pcFyeY5OUOvvCYHDpf9mvqy-pL0yyEVTRrmcwpBWJck841cDxc" alt=""><figcaption><p>Deposits in Zircuit</p></figcaption></figure>

Deposit transactions are initiated by calling a smart contract on L1. Two contracts can be used to deposit ETH onto Zircuit: the `L1StandardBridge` contract and the `OptimismPortal` smart contract. The `L1StandardBridge` contract can be used to deposit Ether by calling the appropriate function. The `OptimismPortal` contract bridges Ether by implementing the `receive` function, meaning any Ether sent to this contract without specifying a function to call will automatically be bridged.

Only the `L1StandardBridge` can be used to bridge ERC-20 tokens, and ERC-721 tokens can be bridged via the `L1ERC721Bridge` contract.

Transactions that originate on L2 naturally do not need to call these smart contracts. Instead, they are sent to the chain via an RPC call or a wallet and are processed directly by the rollup node.

Once the rollup node observes a deposit or receives an L2 transaction, it constructs blocks that contain those transactions via its sequencer functionality.

The rollup node will create deposit transactions for each deposit event observed on L1 and pass these transactions along with any L2 transactions to the execution engine. The execution engine is a modified version of Geth, which supports the deposit transaction type and other minor changes necessary for rollup operation.

The rollup node also passes the transactions included in the blocks to the batcher service, which posts the transactions and their arguments as `calldata` on the L1, providing full data availability.

At this point, the transaction will be included on the L2 after the provers receive the relevant blocks that contain the transactions; no further interaction from the transaction’s sender is needed.

### Withdrawals

Withdrawal transactions take assets like ETH off of Zircuit and return them to the L1. A withdrawal transaction is initiated by first sending an L2 transaction to the `L2StandardBridge` contract calling the appropriate function.

Once that transaction is included in the L2, the components in the following diagram are relevant to complete the withdrawal transaction.

<figure><img src="https://lh7-us.googleusercontent.com/QwdVd0mN5m0LXFpOdI0nNNK3fNTPMz1M9jESYbwMPKM3Y7J3fdAJDRSLdAoAMbsmLB0XuSxVgX6xjJM519rj66fT_aU7c0jtHHzIazQCC1Fs_k6HitkmQ_U3JQTwCjAtKLYldVJBdny8Ngpi1eruXo4" alt=""><figcaption><p>Withdrawals in Zircuit</p></figcaption></figure>

The withdrawal can be completed when the state root containing the L2 withdrawal transaction is included on L1 with a corresponding proof. A proof is necessary to complete the withdrawal function, as it ensures that the account withdrawing the funds had the funds in the first place.

When the batches have been proven, the resulting state root is pushed to L1 via the `L2OutputOracle` smart contract alongside a validity proof for the state transition from the last state root. Once the verifier verifies the proof, the state root is recorded as final. Any withdrawals that depend on it can be processed immediately.


# Transaction Statuses

Transactions on Zircuit pass through several stages before they are fully recorded on the blockchain, which are described on this page.

## Pending

When a transaction is sent to the sequencer, it first appears as `pending`. At this stage, the transaction has been accepted by the sequencer but has not yet been added to a block. This initial status means the transaction is awaiting inclusion in a batch of transactions that will eventually be submitted to the layer one. During this time, the transaction is not yet part of the blockchain, and there is no guarantee that it will be included in the final blockchain.

Pending transactions are held in a mempool (memory pool), where they await sequencing. The sequencer processes these transactions, prioritising them based on factors such as gas fees and the order of arrival. The transaction will remain in this state until the sequencer includes it in a block.

To view all pending transactions, use the JSON-RPC method [`eth_getBlockByNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) with the param set to `pending`. Monitoring the pending state can help in diagnosing issues like transaction congestion or delays. It also allows users to cancel or replace transactions if they need to adjust gas fees to expedite processing.

## Quarantined

{% hint style="info" icon="triangle-exclamation" %}

## Note that [SLS](https://docs.zircuit.com/info/architecture/sls) is not enabled while we [focus](https://www.zircuit.com/blog/a-new-chapter-for-zircuit-from-l2-to-defi) on Zircuit Finance. Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

Zircuit protects users at the sequencer level by monitoring the mempool for malicious transactions and preventing their inclusion into a block. This novel approach means that our sequencer scrutinises transactions for potential malicious intent before they are finalized on layer two. This functionality is described on the [Sequencer Level Security](/info/architecture/sls-deep-dive) page.

Zircuit monitors the mempool for new transactions and if it finds out that a transaction is considered risky, it is quarantined. This means it will be blocked from being including in layer two blocks. As a result, any polling or wait for a receipt will eventually time out. This transaction's status will be `quarantined`.

The quarantined transaction can either be released or retired depending on the matching criteria. Zircuit continuously monitors for release criteria which may result in the transaction's inclusion in layer two blocks. Release criteria are also described on the [Sequencer Level Security](/info/architecture/sls-deep-dive) page.

To check, whether a transaction's status is quarantined, we have introduced an additional RPC call to the Geth client. `zirc_isQuarantined` takes a transaction hash as a parameter and outputs the quarantine status of the transaction. The RPC call can be done through any tool of choice, such as Foundry's `cast`:

```bash
cast rpc zirc_isQuarantined 
--rpc-url https://zircuit1-mainnet.p2pify.com/
 0x9b629147b75dc0b275d478fa34d97c5d4a26926457540b15a5ce871df36c23fd 
```

```javascript
{
  "IsQuarantined": true,
  "Quarantine": {
    "TransactionHash": "0x9b629147b75dc0b275d478fa34d97c5d4a26926457540b15a5ce871df36c23fd",
    "QuarantinedAt": "2024-07-24T13:15:22.644132Z",
    "ExpiresOn": "2024-07-25T13:15:22.644131Z",
    "ReleasedReason": "",
    "QuarantinedBy": "Oracle",
    "QuarantinedReason": "transaction simulation returned a risk",
    "ReleasedBy": ""
  }
}
```

**Key points about "Quarantined" status:**

1. You can view the list of quarantined transactions on the [explorer](https://explorer.zircuit.com/transactions-quarantined) page.
2. The quarantined transaction can be automatically released or retried. Zircuit uses AI enabled features to quarantine a transaction and in some cases a genuine transaction can be quarantined as well. If you need help with releasing your transaction, please contact to us via [discord](https://discord.com/invite/zircuit).

## Sequencer Confirmed / Unsafe

A transaction becomes "sequencer confirmed" or "unsafe" when the sequencer adds it to a block, but that block has not yet been published to layer one. This status means the transaction is included in a provisional block that resides on the layer two chain, but it hasn't been fully secured by being anchored to the underlying layer one. During this period, the transaction is more likely to be included in the blockchain, but it is not yet certain.

At this stage, the block containing the transaction is stored locally by the sequencer and is accessible to users and applications interacting with the Zircuit. However, the block's contents can still be altered or discarded if the sequencer encounters issues or fails to publish the block within the defined sequencing window.

To view the latest "sequencer confirmed" block, use the JSON-RPC method [`eth_getBlockByNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) with the parameter `safe` and compare this to the latest block number. If the safe block is not the most recent block, the next immediate block after the safe block is considered the earliest "sequencer confirmed" block.

**Key points about the "sequencer confirmed" / "unsafe" status:**

1. The transaction is provisionally included in the layer two blockchain but has not yet been anchored to the underlying layer one. This means the transaction could be reversed if not published in time.
2. Applications should inform users about the provisional nature of this status to manage expectations about the transaction's finality.
3. There is a small window during which the transaction could be excluded if the block fails to be published to the underlying layer one within the required time frame.

## Published to Ethereum / Safe

A transaction is deemed "safe" once it is included in a block by the sequencer and that block has been published to the underlying layer one, but not yet finalized. At this stage, the block containing the transaction has been posted to the underlying layer one, significantly increasing the likelihood that the transaction will be part of the final blockchain. However, there is still a slight chance that it could be excluded if the layer one blockchain undergoes a reorganisation.

**Key points about the "Published to Ethereum / Safe" status:**

1. Although not yet finalized, the transaction is now highly likely to be included in the final blockchain, providing a higher degree of certainty.
2. Users and applications can interact with the transaction data with increased confidence, knowing it has been anchored to the underlying layer one.
3. Users can track the status of their transactions using various block explorers or by calling the JSON-RPC method [`eth_getBlockByNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) with `safe` as the block number to see the latest "safe" block.

## Finalized

A transaction reaches the `finalized` status when it has been included in a block by the sequencer, that block has been published to the underlying layer one, and the block has been finalized. Finalisation provides the highest level of certainty that the transaction is immutably part of the Zircuit blockchain. This status guarantees that the transaction cannot be reversed or altered, providing complete assurance of its inclusion in the blockchain.

**Key points about the "Finalized" status:**

1. Finalized transactions are permanently recorded in the blockchain and cannot be altered or removed, offering the highest level of security and certainty.
2. Applications and users requiring the utmost certainty for high-value or critical transactions should wait for this status before considering the transaction complete.
3. Users can verify the finalization of a transaction using various block explorers or by calling the JSON-RPC method [`eth_getBlockByNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getblockbynumber) with `finalized` as the block number to see the latest "finalized" block.


# (Deprecated) Sequencer Level Security (SLS)

{% hint style="info" icon="triangle-exclamation" %}

## Note that [SLS](https://docs.zircuit.com/info/architecture/sls) is not enabled while we [focus](https://www.zircuit.com/blog/a-new-chapter-for-zircuit-from-l2-to-defi) on Zircuit Finance. Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

As a developer building on Zircuit, you benefit from our **Sequencer Level Security (SLS)** protocol that protects your applications and users at the infrastructure level. Unlike traditional blockchain security that relies on post-execution measures, we've built protection directly into our sequencer to prevent malicious transactions from ever reaching your smart contracts.

## What We've Built for You

We've fundamentally reimagined blockchain security by moving protection upstream in the transaction lifecycle. Instead of dealing with malicious transactions after they've caused damage (requiring hard forks or block reversions), our sequencer actively monitors the mempool and prevents harmful transactions from being included in blocks in the first place.

## How Our SLS Protocol Works

<figure><img src="/files/Nntnykn1fGg8ixASxJzt" alt=""><figcaption></figcaption></figure>

When you deploy on Zircuit, your applications automatically benefit from our three-layer security system:

### 1. Malice Detection

We analyze every transaction before it reaches your contracts:

* **Parallel Simulation**: We simulate each transaction against the current blockchain state
* **Advanced Detection**: Our AI-powered algorithms use program analysis, machine learning, and rule-based methods to identify threats
* **Dependency Analysis**: We understand how transactions interact with each other to detect complex, multi-step attacks
* **Block-Level Analysis**: We evaluate the combined effects of all transactions within a proposed block

### 2. Quarantine System

Rather than rejecting suspicious transactions outright, we quarantine them:

* **Time-Based Release**: Transactions can be released after a specified period
* **Failure Criterion**: Transactions that would fail due to state changes are safely released
* **Administrative Override**: Our security experts can manually release false positives
* **Smart Retirement**: Invalid transactions are automatically removed from the mempool

### 3. Controlled Execution

We ensure smooth operation when quarantined transactions are released:

* Released transactions follow standard sequencing rules
* We prevent duplicate quarantining of resubmitted transactions
* Your applications continue running without interruption

## Current Implementation Status

We're currently operating with conservative quarantine periods and manual release processes. This cautious approach ensures maximum security as we prove our system's effectiveness in production. Contact us on Discord for assistance with quarantined deposits.

## Best Practices

While we provide robust sequencer-level security, we recommend you continue following standard security practices:

* Conduct thorough security audits of your smart contracts
* Implement proper access controls and validation
* Consider SLS as an additional security layer, not a replacement for good engineering

## Learn More

{% content-ref url="/pages/mgzou1iadcViaTubiXgm" %}
[(Deprecated) Sequencer Level Security Deep Dive](/info/architecture/sls-deep-dive)
{% endcontent-ref %}


# (Deprecated) Sequencer Level Security Deep Dive

{% hint style="info" icon="triangle-exclamation" %}

## Note that [SLS](https://docs.zircuit.com/info/architecture/sls) is not enabled while we [focus](https://www.zircuit.com/blog/a-new-chapter-for-zircuit-from-l2-to-defi) on Zircuit Finance. Zircuit Finance does not rely on the Zircuit chain.

{% endhint %}

## Overview

Zircuit will protect users at the sequencer level by monitoring the mempool for malicious transactions and preventing their inclusion into a block. In comparison to typical security efforts that focus on the application and smart contract levels, Zircuit’s revolutionary approach goes directly to the underlying sequencer level.

This approach is called "Sequencer Level Security" (SLS) and adds an extra layer of security to the Zircuit network. This novel approach means that our sequencer scrutinizes transactions for potential malicious intent before they are finalized on layer 2. By enabling early detection and quarantine of suspicious transactions, the SLS protocol enhances the security of smart contracts and the layer 2 without necessitating the contentious measures of hard forks or block reversion.

## Detailed Description

Zircuit adds another layer of security at the sequencer level. The sequencer is a privileged node that collects users’ transactions, similar to Ethereum, and also orders them based on predefined rules.

<figure><picture><source srcset="/files/Nntnykn1fGg8ixASxJzt" media="(prefers-color-scheme: dark)"><img src="/files/xNd9dFuQYPTHdFQsx7mM" alt=""></picture><figcaption><p>Overview of the SLS protocol</p></figcaption></figure>

The figure above presents an overview of the protocol. It contains three main components: (1) Malice Detection, (2) Quarantine-Release Criterion, and (3) Transaction Execution.

Upon arrival at the SLS sequencer, transactions from the mempool are initially routed to Malice Detection module. In our talks, we refer to this module as the “[oracle](https://en.wikipedia.org/wiki/Oracle_machine).” It identifies whether a transaction is benign or potentially malicious. Benign transactions are promptly queued for block inclusion, adhering to standard sequencing protocols. Conversely, transactions flagged as malicious are diverted to the Quarantine-Release Criterion module, which acts as an intermediary holding area. Here, they undergo a rigorous verification process against specific release criteria. Transactions that meet these criteria are then forwarded to the Transaction Execution module. The Transaction Execution module executes the transactions against the blockchain state at the forthcoming L2 block. Successfully executed transactions are cycled back to the SLS sequencer for inclusion in the forthcoming L2 block.

### Malice Detection

Malice Detection is done via the following steps:

1. **Choosing Transactions by the Sequencer:** The sequencer selects a list of transactions for potential inclusion in the upcoming block. This step is the same as other standard sequencing protocols. This includes both transactions from the mempool, and deposit transactions that have origin on L1.
2. **Parallel Simulation on the Tip of the Chain:** Each transaction is independently simulated using the current state at the tip of the blockchain. This step allows for parallel processing of transactions. The outcomes of these simulations provide essential data for future dependency analysis and malice detection: (1) Simulation results of each transaction (2) The blockchain states read and written by each transaction.
3. **Transaction Dependency Analysis:** We perform the analysis on the state read and written by each transaction and identify the dependencies between all transactions. Informally, one transaction is dependent on another if executing one may change the outcome of executing the other.
4. **Parallel Detection for Independent Transactions and Sequential Detection for Dependent Transactions:** For any transaction that is not dependent (a.k.a. independent) on any other prior transaction, the sequencer can perform parallel detection on their simulation results. Other dependent transactions are queued for sequential simulation and detection within the block context.
5. **Block-Level Detection:** In addition to analyzing individual transactions, the sequencer also evaluates the combined effects of all transactions within the proposed block. This enables detection of multi-step attacks that rely on multiple transactions executing within the same block.
6. **Transaction Inclusion:** The sequencer finalizes the block by including all transactions identified as benign. Dependent transactions that could not be fully analyzed due to time constraints or complexity are deferred to the next cycle. The same detection process will be applied in the next round when these transactions are considered again for inclusion.

The algorithms used by the sequencer to identify malicious include program analysis, machine learning, and rule-based methods.

### Quarantine-Release Criterion

While in quarantine, the transaction does not get executed and cannot be included in the blocks. The sequencer maintains the information about when the transaction has been placed in the quarantine. The transaction will either be dropped from the mempool once it meets one of the retirement criteria or be released from the quarantine if it meets one of the release criteria.

The exact retirement criteria and release criteria can be defined by the sequencer.

**Mempool Retirement Criteria:**

* **Nonce criterion.** This criterion is met if the transaction can no longer be included in a block because the nonce is no longer valid.
* **Time criterion and memory constraints.** This criterion is met if the transaction clutters the mempool that the node maintains. Such a transaction can be resubmitted to the network and re-enter the mempool (in the quarantined state).

The current implementation of Zircuit ensures that transactions in quarantine are being checked periodically on the retirement criteria by the sequencer. The following list of quarantine-release criteria are viable from the security standpoint.

**Release Criteria:**

* **Time criterion.** The time criterion represents the reaction time that the sequencer offers to the users to react to a malicious transaction. If the transaction has been quarantined for longer than required, it can be released and considered for block inclusion. The exact amount of time required for the transaction to stay in the quarantine is a configuration parameter.
* **Failure criterion.** If a transaction fails due to changes in the chain’s state, it can be safely included in the block since it will result in a revert. Reverted transactions do not alter the blockchain state.
* **Administrative criterion.** It is expected that the detection of malice will occasionally produce false positives. Under such circumstances, the sequencer operational team, comprising security experts, can administratively override decisions to release transactions.

We refer to the [Sequencer-Level Security paper](https://arxiv.org/pdf/2405.01819) for other possible release criteria.

At the launch time, the Zircuit sequencer uses only the Time criterion set to a very long period of time (in the order of years). During this period, the sequencer waits for a privileged party to trigger the release criteria. Contact us on discord for assistance to claim quarantined deposits.

### Transaction Execution

Upon releasing from the quarantine, the sequencer can consider including the transaction when forming the next block. This is subject to the regular sequencing Rules. For a transaction that was resubmitted due to being originally underpriced for the current chain state, the transaction should not be quarantined again. The SLS protocol has to use the account address, and the transaction data (the function selector and call data), and value, to establish whether a new incoming transaction is a duplicate of a transaction that has been already released from the quarantine.

### Requirements for Builders

The SLS protocol is implemented in Zircuit natively. Therefore, every smart contract deployed on Zircuit is by default included and protected by the SLS. However, SLS is a best-effort service powered by AI, so it can make mistakes. The developers are therefore encouraged to follow best engineering practices and scrutinize the security of their code. The sequencer-level security protocol should be considered an added secondary security measure.

SLS requires pricing information for detecting malice and protecting assets. Tokens natively deployed on Zircuit should be listed on [CoinGecko](https://www.coingecko.com/). The technology will automatically recognize such price feeds and tokens. Tokens that are deployed on other networks and bridged to Zircuit are advised to contact the Zircuit team via Discord to ensure rapid inclusion in the oracle’s pricing system.

#### Building with SLS

To build with SLS, we added two additional RPC calls to the Geth client, `zirc_isQuarantined` as well as `zirc_getQuarantined`.

`zirc_isQuarantined` takes a transaction hash as a parameter and outputs the quarantine status of the transaction. The RPC call can be done through any tool of choice, such as Foundry's `cast`:

```bash
cast rpc zirc_isQuarantined 
--rpc-url https://zircuit1-mainnet.p2pify.com/
 0x9b629147b75dc0b275d478fa34d97c5d4a26926457540b15a5ce871df36c23fd 
```

Using `zirc_getQuarantined`, you can also query all quarantined transactions and filter them by addresses.

```bash
cast rpc zirc_getQuarantined 
--rpc-url https://zircuit1-mainnet.p2pify.com/ 
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
```

A quick overview of quarantined transactions is also available on the [Quarantined Transactions page](https://explorer.zircuit.com/transactions-quarantined) on the block explorer.

It's important to note that a transaction that gets quarantined will also not return a transaction receipt, so if you are used to sending a transaction using the Ethers library and waiting for a receipt, but the transaction was put into quarantine, the `wait()` call will not terminate.

```typescript
const sentTx = await signer.sendTransaction(txBody);
console.log('Transaction sent:', sentTx.hash);
// Waiting for transaction receipt, if TX gets quarantined this will time out
// and quarantine status can be checked through zirc_isQuarantined RPC call
const receipt = await sentTx.wait();
```

In the more advanced code snippet below, we proactively check the quarantine status using the `zirc_isQuarantined` using two concurrent promises to either wait for a transaction receipt or a RPC call result, that the transaction is in fact quarantined.

```typescript
let continueChecking = true;

async function isQuarantined(txHash) {
    const response = await provider.send('zirc_isQuarantined', [txHash]);
    return response.IsQuarantined;
}

// Continuously check quarantine status (10 second timeout)
async function checkQuarantine(txHash) {
    while (continueChecking) {
        if (await isQuarantined(txHash)) {
            throw new Error('Transaction is quarantined.');
        }
        await new Promise(resolve => setTimeout(resolve, 10000)); // Check every 10 seconds
    }
}

// Main function to send TX
async function sendTransaction() {
    try {
        const txResponse = await wallet.sendTransaction(tx);
        console.log('Transaction sent:', txResponse.hash);

        // Wait for either the transaction to be mined or for it to be quarantined
        const result = await Promise.race([
            txResponse.wait().then(receipt => {
                continueChecking = false; // Stop the quarantine check to prevent infinite promise
                return receipt;
            }), // Waits for the transaction to be mined
            checkQuarantine(txResponse.hash) // Continuously checks if transaction is quarantined
        ]);

        console.log('Transaction mined:', result);
    } catch (error) {
        continueChecking = false; // Ensure the check is stopped in case of errors too
        console.error('Error:', error.message);
    }
}

sendTransaction();
```

If your transaction is quarantined, refer to the mempool retirement and release criteria mentioned earlier. Security experts may release false positives (Administrative criterion), but you can also attempt to resubmit the transaction with a different gas limit to reevaluate its status. If you are sending the exact same transaction without any changes you will get an `already known` error response, so a simple retry is not enough to remove a false positive from the quarantine.

In cases where a quarantined transaction is blocking subsequent ones—such as due to a nonce mismatch—you can resolve the issue by sending a zero-value transaction to yourself. This action can update the nonce and help release the quarantined transaction.

You can perform this manually using MetaMask or programmatically using Ethereum libraries like Cast or Ethers.js.

```bash
cast send 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 
--value 0ether 
--rpc-url https://zircuit1-mainnet.p2pify.com/ 
--private-key $yourPrivateKey
```

Using ethers, assuming you have set up a `signer` using RPC URL and private key.

```typescript
const tx = {
  to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  value: ethers.utils.parseEther("0"),
  gasLimit: 21000, // Standard gas limit for a basic transaction
};

try {
  const transaction = await signer.sendTransaction(tx);
  console.log(`Transaction hash: ${transaction.hash}`);
  await transaction.wait();
  console.log('Transaction confirmed');
} catch (error) {
  console.error('Error sending transaction:', error);
}
```

## Learn More

Technical details can be found in our [pre-print](https://arxiv.org/pdf/2405.01819):

{% file src="/files/JL5AuzWi3KnZbNPE4obm" %}
Zircuit SLS preprint
{% endfile %}

You can also learn more by watching some of our [talks](/info/research/talks-and-panels) on the topic:

* our [talk](https://www.youtube.com/watch?v=xpwhTC2G1WI) at ETH Prague 2024,
* our [talk](https://youtu.be/IhmtmXuAFO8?si=c2yv0Ue_2sMVfZWa) at Ethereum Zurich 2024, or
* our [talk](https://youtu.be/8RiLNXNEGs4?si=BnWntMoYGHUkNjIF) at ETH Denver 2024.


# (Deprecated) Modular Prover Design

Zircuit operates a comprehensive zero-knowledge proving system built on a modular architecture to efficiently process L2 blocks and submit proofs to L1.

## Core Architecture

The **Proof Orchestrator** serves as the central coordinator, monitoring L2 blocks and organizing them into proving batches. It uses NATS as a message bus to queue blocks awaiting proof, stores proof artifacts in a database, and manages block traces in S3. The team is transitioning to a more modular, configuration-driven version that decouples the orchestrator from specific proving strategies.

The **Proposer** handles the final step of submitting output roots and their corresponding proofs to L1 smart contracts. Running every 5 minutes, it consumes proofs from the orchestrator, forms transactions, signs them with a secure private key, and submits to L1 nodes via proxyd connections.

## Proving Technology Stack

Zircuit has evolved from dedicated zkEVM circuits to a zkVM-based approach. This represents a significant architectural improvement, allowing any program to be proven by simply running it in the VM rather than requiring hardcoded circuit constraints. The system uses **Kona** (implementing Optimism's state transition) as the program being proven, with Zircuit-specific modifications layered on top.

#### Two-Phase Proving Pipeline

1. **Range Proofs**: Generate proofs for multiple L2 blocks, ensuring correct state transitions, balance computations, and smart contract execution
2. **Aggregate Proofs**: Combine multiple range proofs to maximize the number of L2 transactions rolled up in a single L1 proof verification

## Advanced Components

We use a zkVM abstraction layer enabling programs to run across interchangeable backends (currently SP1). It separates execution into three components:

* **Program (P)**: Core computation running in the zkVM
* **Host Program (HP)**: External setup, key generation, input preparation
* **Guest Program (GP)**: In-zkVM execution reading hints and committing outputs

The **Modular Orchestrator** represents the next-generation architecture with four key components:

* **Dispatcher**: Processes individual messages and updates status
* **Collector**: Groups messages using strategies (Sequential for blocks, Match for shared fields)
* **Message Bus**: Handles message delivery and state management
* **Executor**: Encapsulates actual execution logic (currently expects JSON input/output binaries)

## Development Considerations

The architecture emphasizes modularity and flexibility, allowing for easy updates to proving strategies and backend systems without requiring complete infrastructure overhauls. This design supports our goal of maintaining cost-effective, frequent state root submissions while handling the computational demands of ZK proof generation.


# Gas Pricing & Transaction Fees

This page provides a detailed look at exactly how transaction fees work on Zircuit. Zircuit transaction fees are composed of an Execution Gas Fee and an L1 Data Fee. The total cost of a transaction is the sum of these two fees. The following paragraphs describe how the cost for each of these are derived.

### Execution Gas Fee

A transaction's execution gas fee is exactly the same fee that you would pay for the same transaction on Ethereum. This fee is equal to the amount of gas used by the transaction multiplied by the gas price attached to the transaction. Like Ethereum, Zircuit uses the EIP-1559 mechanism to set the base fee for transactions. The total price per unit gas that a transaction pays is the sum of the base fee and the optional additional priority.

The gas used by a transaction on Zircuit is exactly the same as the gas used by the same transaction on Ethereum. If a transaction costs 100,000 gas on Ethereum, it will also cost 100,000 on Zircuit. However, the gas **price** for that transaction is expected to be much lower on Zircuit than the gas price on Ethereum so you end up paying much less ETH in total. The EIP-1559 parameters used by Zircuit differ from those used by Ethereum as follows.

<table><thead><tr><th>Parameter</th><th width="221">Zircuit value</th><th>Ethereum value (for reference)</th></tr></thead><tbody><tr><td>Block gas limit</td><td>30,000,000 gas</td><td>45,000,000 gas</td></tr><tr><td>Block gas target</td><td>3,000,000 gas</td><td>22,500,000 gas</td></tr><tr><td>EIP-1559 elasticity multiplier</td><td>10</td><td>2</td></tr><tr><td>EIP-1559 denominator</td><td>250</td><td>8</td></tr><tr><td>Block time in seconds</td><td>2</td><td>12</td></tr></tbody></table>

#### Base Fee

The base fee is the minimum price per unit of gas that a transaction must pay to be included in a block. Transactions must specify a maximum base fee higher than the block base fee to be included. The actual fee charged is the block base fee, even if the transaction specifies a higher maximum base fee. The Zircuit base fee behaves exactly like the Ethereum base fee with a few small parameter changes to account for shorter block times.

#### Priority Fee

Just like on Ethereum, Zircuit transactions can specify a **priority fee**. This priority fee is a price per unit of gas that is paid on top of the base fee. For example, if the block base fee is 1 gwei and the transaction specifies a priority fee of 1 gwei, the total price per unit of gas is 2 gwei. The priority fee is an optional component of the execution gas fee and can be set to 0.

**The Zircuit sequencer will prioritize transactions with a higher priority fee** and execute them before any transactions with a lower priority fee. If transaction speed is important to your application, you may want to set a higher priority fee to ensure that your transaction is included more quickly. The [`eth_maxPriorityFeePerGas`](https://docs.alchemy.com/reference/eth-maxpriorityfeepergas) RPC method can be used to estimate a priority fee that will get your transaction included quickly.

### L1 Data Fee

The L1 Data Fee is the only part of the Zircuit transaction fee that differs from the Ethereum transaction fee. This fee arises from the fact that the transaction data for all Zircuit transactions is published to Ethereum. This guarantees that the transaction data is available for nodes to download and execute. The L1 Data Fee accounts for the cost to publish a Zircuit transaction to Ethereum and is primarily determined by the current base fee on Ethereum. With the introduction of [blobs](https://www.eip4844.com/) on Ethereum, a new transaction type for posting this kind of data was introduced. The current Ethereum blob data gas price will largely determine the L1 data fee.

#### Mechanism

The L1 Data Fee is automatically charged for any transaction that is included in a Zircuit block. This fee is deducted directly from the address that sent the transaction. The exact amount paid depends on the estimated size of the transaction in bytes after compression, the current Ethereum blob gas price, and several small parameters.

The L1 Data Fee is most heavily influenced by the Ethereum base fee that is continuously relayed from Ethereum to Zircuit. Short-term fluctuations of the L1 Data Fee are generally quite small and should not impact the average transaction.

The L1 Data Fee is charged automatically. **It is currently not possible to limit the maximum L1 Data Fee that a transaction is willing to pay.**

### Formula

#### Ecotone

The pricing functions for the L1 Data Fee uses the following parameters:

* The **signed** transaction serialized according to [the standard Ethereum transaction RLP encoding](https://github.com/ethereum-optimism/op-geth/blob/11a890f1ee0348a17687149abc72f394f9faa5ce/core/types/transaction.go#L131-L141).
* The current Ethereum base fee and/or blob base fee.
* Two new scalar parameters that independently scale the **base fee** and **blob base fee**.

The L1 Data Fee calculation begins with counting the number of zero bytes and non-zero bytes in the transaction data. Each zero byte costs 4 gas and each non-zero byte costs 16 gas. This value, when divided by 16, can be thought of as a rough estimate of the size of the transaction data after compression.

```javascript
tx_compressed_size = [(count_zero_bytes(tx_data)*4 + count_non_zero_bytes(tx_data)*16)] / 16
```

Next, the two scalars are applied to the base fee and blob base fee parameters to compute a weighted gas price multiplier.

```javascript
weighted_gas_price = 16*base_fee_scalar*base_fee + blob_base_fee_scalar*blob_base_fee
```

The L1 Data Fee is then:

```javascript
l1_data_fee = tx_compressed_size * weighted_gas_price
```

### Fjord

The pricing function changes with the Fjord upgrade because of the FastLZ compression estimator, which more accurately charges for L1 data usage on a per-transaction basis. The updated function uses the following parameters:

* The FastLZ-compressed size of the signed transaction.
* The current Ethereum base fee and/or blob base fee (trustlessly relayed from Ethereum).

The Fjord L1 Data Fee calculation begins with estimating the transaction size using a linear model over the FastLZ-compressed transaction size.

```
estimatedSizeScaled = max(minTransactionSize * 1e6, intercept + fastlzCoef*fastlzSize)
```

The model parameters `intercept` and `fastlzCoef` were determined by performing a linear regression analysis over a dataset of previous L2 transactions minimizing the root mean square error against the change in batch size, when compressed with Brotli, over historical OP mainnet data. These parameters are fixed in Fjord. The `minTransactionSize`, `intercept`, and `fastlzCoef` values are scaled by 1e6.

Next, the two chain parameters **base fee scalar** and **blob base fee scalar** are used to compute a weighted gas price multiplier.

```
1FeeScaled = baseFeeScalar * l1BaseFee * 16 + blobFeeScalar * l1BlobBaseFee
```

Both scalars are scaled by 1e6. The final L1 Data Fee is then

```
l1Cost = estimatedSizeScaled * l1FeeScaled / 1e12
```

### Operator Fee

Zircuit supports an **Operator fee** in addition to the execution gas fee and the L1 data fee. Compute it as

```
operatorFee = (gasUsed × operatorFeeScalar ÷ 1e6) + operatorFeeConstant
```

where operatorFeeScalar (uint32, scaled by 1e6) and operatorFeeConstant (uint64) are chain parameters. The total transaction cost is then:

```
 operatorFee + gasUsed × (baseFee + priorityFee) + l1Fee
```

Deposit transactions are not charged an operator fee. Clients/contracts can read the current parameters from the L1 Block Info contract at `0x4200000000000000000000000000000000000015` via the getters `operatorFeeScalar()` and `operatorFeeConstant()` . Transactions must have sufficient balance to cover worst-case costs (including the operator fee); any unused operator-fee amount is refunded after execution, and the spent portion is sent to the Operator Fee Vault.


# L1 Data Fee Calculation (Fjord)

This page describes how to calculate the L1 data fee for a specific transaction on a Zircuit network post Fjord upgrade.

#### Retrieve the parameters

To perform the calculation the following parameters are needed:

* L1 base fee
* L1 base fee scalar
* L1 blob base fee
* L1 blob base fee scalar

They are available by calling the Gas Price Oracle smart contract predeployed on every Zircuit network on this address: `0x420000000000000000000000000000000000000F`

To retrieve them we can use Foundry's cast. You will need the block number in which the transaction was included to retrieve the parameters from the right point in time. You will also need a valid RPC url. For example you'll find the RPC urls on this page:

{% content-ref url="/pages/fEv1Wrfl0hPzbT380Xlr" %}
[RPCs](/infra/rpcs)
{% endcontent-ref %}

L1 base fee:

```sh
cast call 0x420000000000000000000000000000000000000F "l1BaseFee()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 base fee scalar:

```sh
cast call 0x420000000000000000000000000000000000000F "baseFeeScalar()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 blob base fee:

```sh
cast call 0x420000000000000000000000000000000000000F "blobBaseFee()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 blob base fee scalar:

```sh
cast call 0x420000000000000000000000000000000000000F "blobBaseFeeScalar()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

Get the raw RLP-encoded signed transaction:

```sh
cast tx $TX_HASH --rpc-url $RPC_URL --raw
```

| intercept          | -42\_585\_600 | Linear regression intercept (scaled by 1e6)   |
| ------------------ | ------------- | --------------------------------------------- |
| fastlzCoef         | 836\_500      | Linear regression coefficient (scaled by 1e6) |
| minTransactionSize | 100           | Minimum transaction size in bytes             |

[fastLzSize](https://github.com/zircuit-labs/l2-geth-public/blob/main/core/types/rollup_cost.go#L684-L760) can be calculated using the bytes represented by the raw RLP-encoded signed transaction as the parameter.

Now that you have all the info you need, you can just apply the following formula:

```
estimatedSizeScaled = max(minTransactionSize * 1e6, intercept + fastlzCoef*fastlzSize)
```

<pre><code><strong>1FeeScaled = baseFeeScalar * l1BaseFee * 16 + blobFeeScalar * l1BlobBaseFee
</strong></code></pre>

```
l1Cost = estimatedSizeScaled * l1FeeScaled / 1e12
```


# L1 Data Fee Calculation (Ecotone)

This page describes how to calculate the L1 data fee for a specific transaction on a Zircuit network post Ecotone upgrade.

#### Retrieve the parameters

To perform the calculation the following parameters are needed:

* L1 base fee
* L1 base fee scalar
* L1 blob base fee
* L1 blob base fee scalar

They are available by calling the Gas Price Oracle smart contract predeployed on every Zircuit network on this address: `0x420000000000000000000000000000000000000F`

To retrieve them we can use Foundry's cast. You will need the block number in which the transaction was included to retrieve the parameters from the right point in time. You will also need a valid RPC url. For example you'll find the RPC urls on this page:

{% content-ref url="/pages/fEv1Wrfl0hPzbT380Xlr" %}
[RPCs](/infra/rpcs)
{% endcontent-ref %}

L1 base fee:

```sh
cast call 0x420000000000000000000000000000000000000F "l1BaseFee()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 base fee scalar:

```sh
cast call 0x420000000000000000000000000000000000000F "baseFeeScalar()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 blob base fee:

```sh
cast call 0x420000000000000000000000000000000000000F "blobBaseFee()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

L1 blob base fee scalar:

```sh
cast call 0x420000000000000000000000000000000000000F "blobBaseFeeScalar()(uint256)" --rpc-url $RPC_URL --block $BLOCK_NUMBER
```

Get the raw RLP-encoded signed transaction:

```sh
cast tx $TX_HASH --rpc-url $RPC_URL --raw
```

Zero-bytes and non zero-bytes are accounted differently, so the next step is to count all the zero bytes and all the non-zero bytes in the tx. Here is a shell script that does it. Remember to remove the "0x" at the beginning when you provide it the raw tx.

```sh
#!/usr/bin/env bash

# Usage: ./count_zero_bytes_hex.sh <hex string>
# Example: ./count_zero_bytes_hex.sh 00A1FF0033

hex="$1"

# Ensure we got a parameter
if [ -z "$hex" ]; then
  echo "Usage: $0 <hex string>"
  exit 1
fi

# Normalize to uppercase to keep comparison simple
hex_upper=$(echo "$hex" | tr '[:lower:]' '[:upper:]')

# The hex string length must be an even number for whole bytes (2 hex chars = 1 byte)
len=${#hex_upper}
if (( len % 2 != 0 )); then
  echo "Error: Hex string must have an even number of characters."
  exit 1
fi

zero_bytes=0
other_bytes=0

# Process two hex characters at a time
for ((i=0; i<len; i+=2)); do
  byte="${hex_upper:$i:2}"  # Extract 2-hex-character chunk

  if [ "$byte" == "00" ]; then
    ((zero_bytes++))
  else
    ((other_bytes++))
  fi
done

echo "Hex string:  $hex"
echo "Zero bytes:  $zero_bytes"
echo "Other bytes: $other_bytes"
```

In the [Gas Pricing & Transaction Fees](/info/architecture/gas-pricing-and-transaction-fees) it's specified the cost of zero bytes is 4 and the cost of non-zero bytes is 16. So your `calldatagas` will be zero-bytes\*4 + non-zero-bytes\*16.

Now that you have all the info you need, you can just apply the following formula:

```
(calldataGas/16)*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/1e6
```

That is equivalent to the following formula better fitted for precision under integer arithmetic:

```
calldataGas*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/16e6
```


# Supported Transaction Types

[EIP-155](https://eips.ethereum.org/EIPS/eip-155) introduced a simple replay attack protection mechanism for Ethereum transactions. It was implemented to prevent transactions on one Ethereum-based chain from being valid on another chain. Zircuit does support standard legacy transactions, which comply with EIP-155 (including chain ID).

At the moment, Zircuit does **not** support pre-EIP-155 transactions (without a chain ID). Attempting to submit such transactions will result in rejection.

Zircuit supports [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) transactions. EIP-2930 further extends this functionality by introducing "Access List" transactions, which allow for explicit inclusion of accounts and storage keys that a transaction will access. This addition helps in optimizing gas fees and improving the predictability of transaction costs.

[EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) transactions are also supported on Zircuit. EIP-1559 introduces a new transaction pricing mechanism aimed at improving fee market efficiency. It includes a base fee, which is a minimum amount of gas that must be paid for a transaction to be included in a block, and adjusts dynamically based on network congestion. Along with the base fee, users can include a "priority fee" to incentivize the sequencer to prioritize their transactions

[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) transactions type are supported on Zircuit. EIP-7702 is part of Ethereum's Pectra upgrade and allows Externally Owned Accounts (EOAs) to temporarily have code associated with them. It enables account abstraction capabilities for EOAs, allowing features like batched transactions and sponsored gas fees.


# Research

## Intro

Zircuit started from the desire to explore new ideas, technologies, and approaches to securely scale Ethereum. Founded by a team of experienced researchers in the web3 space, Zircuit arose from the desire to take new ideas and build them into a novel solution to make the space more accessible and secure for everyone.

Zircuit’s research is wide-reaching but focuses on a two areas:

* **Security:** Zircuit is built with advanced security protocols and is always looking for ways to bring additional security to web3 users on and off of our network. See the [Sequencer Level Security page ](/info/architecture/sls-deep-dive)for more on our first innovation.
* **Performance:** To truly scale the Ethereum ecosystem, the zero-knowledge technology we use must be as cheap and fast as possible to maximize chain interoperability and minimize user costs.

Zircuit researchers are also interested in other related to rollups, blockchain adoption, and web3 in general. We’re interested in providing the best user experience, exploring and developing web3 standards, proper software engineering, and education.

## Highlights & Recognition

Zircuit researchers have been fortunate to receive recognition from their peers through grants and paper publications. In this section, we highlight some concrete research projects that the team members have contributed to.

* **Sequencer Level Security.** Zircuit pioneered the novel concept of [Sequencer Level Security (SLS)](/info/architecture/sls-deep-dive) for rollups. An SLS-enabled sequencer considers the effect that transactions have on-chain, and if it is undesirable, it temporarily prevents transactions from being executed. This provides the smart contract operators and users with an opportunity to take mitigation steps before suffering harm.
* **Correctness of Halo2 Circuits.** Zircuit researchers leveraged lightweight formal methods to catch large classes of bugs and errors that can present themselves during development with the Halo2 library. Such methods can help detect unused gates, unconstrained cells, unused columns, and under-constrained circuits, among other potential vulnerabilities. This work presents a pioneering approach to PLONKish arithmetization and Halo2 circuit analysis, combining abstract interpretation, bounded-model checking, and more. Zcash awarded a grant for Improvements to this work.
* **Rollup Security.** This project explored security issues that are closely related to rollups. Researchers explored unique features of these systems, like fraud proof validation, escape hatches, and approaches to data availability. As rollups and these features mature, the time to review and understand these features -- both at a high level and at the code level -- is now. This work is influencing planned features for Zircuit and was funded by the Ethereum Foundation.
* **Improved Mathematical Primitives.** Our research has looked at optimizing Multi-Scalar Multiplication (MSM) operations used within Halo2 (and other proof systems). This work looked at optimal window sizing, parallelism techniques (including for CPU, GPU, FPGA, and ASIC), pre-computation methods, elliptic curve point representations, and more. This work is being incorporated into Zircuit and will be shared in the future.

For more details, see our [selected publications](/info/research/publications-and-grants) as well as our [talks and presentations](/info/research/talks-and-panels).\\


# Publications & Grants

On this page we list blockchain-related academic papers that the Zircuit researchers have authored and grants that teammembers have been awarded.

## Selected Publications

1. **Attacking Poseidon via Graeffe-Based Root-Finding over NTT-Friendly Fields**
   * Antonio Sanso, Giuseppe Vitto
   * IACR Cryptology ePrint Archive, 2025
     * [ePrint 2025/937](https://eprint.iacr.org/2025/937)
2. **A Practical Rollup Escape Hatch Design**
   * Francisco G. Figueira, Martin Derka, Ching Lun Chiu, Jan Gorzny
   * IEEE International Conference on Blockchain and Cryptocurrency (ICBC), Pisa, Italy, June 2-6, 2025
     * DOI: *coming soon*
     * Preprint: [arXiv:2503.23986](https://arxiv.org/abs/2503.23986)
3. **Instrumenting transaction trace properties in smart contracts:**\
   **Extending the EVM for real-time security**
   * Zhiyang Chen, Jan Gorzny, Martin Derka
   * International Workshop on Blockchain Oriented Software Engineering (IWBOSE), co-located with the IEEE International Conference on Software Analysis, Evolution and Reengineering (SANER), Montreal, Quebec, Canada, March 4-7, 2025
     * DOI: *coming soon*
     * Preprint: [arXiv:2408.14621](https://arxiv.org/abs/2408.14621)
4. **A Methodology for Replicating Historical Exploits on EVM-Compatible Blockchains**
   * Zhiyang Chen, Phillip Kemper, Yi Liu, Jan Gorzny, Diego Siqueira, Yuekang Li, Donato Pellegrino and Martin Derka.
   * International Workshop on Emerging Trends in Software Engineering for Blockchain (WETSEB) co-located with the IEEE/ACM International Conference on Software Engineering (ICSE), Ottawa, Ontario, Canada, April 27-May 3, 2025
     * DOI: *coming soon*
5. **Temporarily Restricting Solidity Smart Contract Interactions**
   * Valerian Callens, Zeeshan Meghji, Jan Gorzny
   * IEEE International Conference on Decentralized Applications and\
     Infrastructures (DAPPS), Shanghai, China, July 15-18, 2024
     * [DOI: 10.1109/DAPPS61106.2024.00008](https://doi.org/10.1109/DAPPS61106.2024.00008)
     * Preprint: [arXiv:2405.09084](https://arxiv.org/abs/2405.09084)
6. **Requirements Engineering Challenges for Blockchain Rollups**
   * Jan Gorzny, Martin Derka
   * International Workshop on Requirements Engineering and Web3 systems (RE4WEB3), co-located with the IEEE International Requirements Engineering Conference (RE), Reykjavik, Iceland, June 24-25, 2024
     * [DOI: 10.1109/REW61692.2024.00052](https://doi.org/10.1109/REW61692.2024.00052)
7. **A Rollup Comparison Framework**
   * Jan Gorzny, Martin Derka
   * ChainScience 2024, Zurich, Switzerland, April 5-6, 2024
     * [arXiv:2404.16150](https://arxiv.org/abs/2404.16150)
8. **SoK: A Review of Cross-Chain Bridge Hacks in 2023**
   * Nikita Belenkov, Valerian Callens, Alexandr Murashkin, Kacper Bak, Jan Gorzny, Martin Derka, Sung-Shine Lee
     * Preprint: [arXiv:2501.03423](https://arxiv.org/abs/2501.03423)
9. **SoK: Compression in Rollups**
   * Roshan Palakkal, Jan Gorzny, Martin Derka
   * IEEE International Conference on Blockchain and Cryptocurrency (ICBC), Dublin, Ireland, May 27-31, 2024
     * [DOI: 10.1109/ICBC59979.2024.10634469](https://doi.org/10.1109/ICBC59979.2024.10634469)
10. **Attacks on Rollups**
    * Adrian Koegl, Zeeshan Meghji, Donato Pellegrino, Kacper Bak, Jan Gorzny, Martin Derka
    * International Workshop on Distributed Infrastructure for the Common Good (DICG), Bologna, Italy, December 12, 2023
      * [DOI: 10.1145/3631310.3633493](https://dl.acm.org/doi/10.1145/3631310.3633493)
11. **Ideal Properties of Rollup Escape Hatches**
    * Jan Gorzny, Po-An Lin, Martin Derka
    * International Workshop on Distributed Infrastructure for the Common Good (DICG), Quebec, QC, Canada, November 7, 2022
      * [DOI: 10.1145/3565383.3566107](https://doi.org/10.1145/3565383.3566107)
12. **Automated Analysis of Halo2 Circuits**
    * Fatemeh Heidari Soureshjani, Mathias Hall-Andersen, MohammadMahdi Jahanara, Jeffrey Kam, Jan Gorzny, Mohsen Ahmadvand
    * International Workshop on Satisfiability Modulo Theories (SMT), co-located with the International Conference on Automated Deduction (CADE), Rome, Italy, July 5-6, 2023
      * [CEUR-WS Vol-3429](https://ceur-ws.org/Vol-3429/paper3.pdf)
      * Preprint: [ePrint 2023/1051](https://eprint.iacr.org/2023/1051)
13. **SoK: Not Quite Water Under the Bridge: Review of Cross-Chain Bridge Hacks**
    * Sung-Shine Lee, Alexandr Murashkin, Martin Derka, Jan Gorzny
    * IEEE International Conference on Blockchain and Cryptocurrency (ICBC), Dubai, United Arab Emirates, May 1-5, 2023
      * [DOI: 10.1109/ICBC56567.2023.10174993](https://doi.org/10.1109/ICBC56567.2023.10174993)
      * Preprint: [arXiv:2210.16209](https://arxiv.org/abs/2210.16209)
14. **Constant-Time Updates Using Token Mechanics**
    * Sebastian Banescu, Martin Derka, Jan Gorzny, Sung-Shine Lee, Alex Murashkin
    * IEEE International Conference on Blockchain (BLOCKCHAIN), Rhodes, Greece, November 2-6, 2020
      * [DOI: 10.1109/Blockchain50366.2020.00044](https://doi.org/10.1109/Blockchain50366.2020.00044)

## Grants

Zircuit researchers have been awarded four grants: three from the Ethereum Foundation and one from the ZCash Foundation.

* **G1**: Improved Lightweight Formal Verification of Halo2 Proof Systems. ZCash Foundation - ZCash Minor Grants Program, 2023.
* **G2**: Evaluating Rollup Compression. Ethereum Foundation - Ethereum Ecosystem Support Program, 2023. Grant No. FY23-0922.
* **G3**: Rollup Security Framework. Ethereum Foundation - Ethereum Ecosystem Support Program, 2023. Grant No. FY23-0898.
* **G4**: Back-End API Standard for L2 Block Explorers. Ethereum Foundation - Ethereum Ecosystem Support Program, 2023. Grant No. FY23-0882.


# Talks & Panels

Zircuit researchers have given over fifty talks on rollups, bridges, and security. This page lists them including links to recordings when they are available.

1. **Optimizing ZK-Rollups: Unlocking Cost-Effective Proving Infrastructure**
   * [EthCC\[8\]](https://youtu.be/ClrgV6WLiis?feature=shared), *Cannes, France,* 2025.
2. **Designing for Change: The Modular Architecture of Zircuit Prover**
   * [EthCC\[8\]](https://www.youtube.com/live/boG_5zYWWW0?feature=shared), *Cannes, France,* 2025.
3. **Challenges of Replicating Historical Exploits**
   * [EthCC\[8\]](https://www.youtube.com/live/uicp15MoF1I?feature=shared), *Cannes, France,* 2025.
4. **Smart Contract Families in Solidity**
   * [EthCC\[8\]](https://www.youtube.com/live/8i0jmvkWqQw?feature=shared), *Cannes, France,* 2025.
5. **EIP-7702: Programmable Accounts and the Future of UX on Ethereum and Rollups**
   * [EthCC\[8\]](https://www.youtube.com/live/lBEpmGy-ojk?feature=shared), *Cannes, France,* 2025.
6. **An Introduction to EIP-7702**
   * [ETHDenver](https://www.youtube.com/watch?feature=shared\&v=WG_0EiHtKlc), *Denver, Colorado, USA,* 2025.
7. **Challenges of Bringing a zkTrie to the OP Stack**
   * [ETHDenver](https://www.youtube.com/watch?feature=shared\&v=HydzerzxNHA), *Denver, Colorado, USA,* 2025.
8. **Rollups Unfiltered: What We Wish We Knew While Building Zircuit**
   * [ETHDenver](https://www.youtube.com/watch?feature=shared\&v=oGVHPWnYwtM), *Denver, Colorado, USA*, 2025.
9. **Using AI to Secure Dapps and Users**
   * [L2con at DevCon 7](https://www.youtube.com/watch?v=8wsCwqdDTec), *Bangkok, Thailand, 2024.*
   * [ETHGlobal Pragma](https://www.youtube.com/watch?v=lRAb0_T6Ml8\&list=PLXzKMXK2aHh4LcwDpGf_itZrAu7XRNb07\&index=4\&t=58s), *San Francisco, CA, USA, 2024.*
10. **Panel: Will All Optimistic Rollups become zkRollups?**
    * ZK Hub at DevCon 7, *Bangkok, Thailand, 2024.*
11. **Panel: Modularity - Along with Avail, Swell**
    * AltLayer Rollup Day at DevCon 7, *Bangkok, Thailand, 2024.*
12. **Enhancing L2 Security with Sequencer-Level Protection: Insights from the Zircuit Network**
    * DeFi Security Summit, *Bangkok, Thailand, 2024.*
13. **Panel: Integrating L2 Innovations: Cross-Chain Collaborations and Hybrid Models**
    * [L2con at DevCon 7](https://www.youtube.com/watch?v=oY425x0e2M8), *Bangkok, Thailand, 2024.*
14. **Workshop: Getting Started with Zircuit: Building a Secure & Scalable DApp on Layer 2**
    * ZK Hub at DevCon 7, *Bangkok, Thailand, 2024.*
15. **Accelerating Zero-Knowledge Proofs Computations with GPUs**
    * ZKAccelerate at DevCon 7, *Bangkok, Thailand, 2024.*
16. **Polynomial Commitment Schemes for Zero-Knowledge Proof Systems**
    * DevCon 7, *Bangkok, Thailand, 2024.*
17. **Robust, Distributed, and Prover-Agnostic Proof Orchestration**
    * Invisible Garden - Antalpha Hacker House, *Bangkok, Thailand, 2024.*
18. **Designing for Change: The Modular Architecture of Zircuit Prover**
    * [Aggregation Summit at DevCon 7,](https://www.youtube.com/watch?v=9jWyxNElCxc) *Bangkok, Thailand, 2024.*
19. **Abstract Interpretation for PLONKish Circuits**
    * 34th International Conference on Collaborative Advances in Software and COmputiNg (CASCON), *Toronto, ON, Canada, 2024.*
20. **Smart Contract Families in Solidity**
    * 34th International Conference on Collaborative Advances in Software and COmputiNg (CASCON), *Toronto, ON, Canada, 2024.*
21. **State of the Art EVM: Techniques and Benchmarks Building L2s**
    * Google Zero Knowledge (ZK) Summit, *Sunnyvale, CA, USA, 2024.*
22. **Panel: Interoperability and Cross-Chain Solutions: Enhancing Web3 Ecosystem in APAC**
    * Aggregation Cave at EDCON 2024, *Tokyo, Japan, 2024.*
23. **Introduction to Layer Three Networks**
    * Aggregation Cave at EDCON 2024, *Tokyo, Japan, 2024.*
24. **Panel: ZK Proofs: Pioneering Privacy, Efficiency and Transformative Applications**
    * Proof of Talk, *Paris, France, 2024.*
    * Panelists: Kurt Hemecker (CEO, MINA), Diego Fernandez (Innovation & Digital Department, QuarkID), Anthony Day (Head of Strategy & Marketing - Midnight, IOHK), Martin Derka (Co-Founder, Zircuit).
25. **Panel: ZK Proof Generation Infrastructure: Overhyped or Underutilized?**
    * [ETH Prague](https://www.youtube.com/watch?v=fNaVJhmBiNo), *Prague, Czech Republic, 2024.*
    * Panelists: Martin Derka (Co-Founder, Zircuit), Tomas Eminger (Head of Staking Infrastructure, RockawayX), zpedro (Aztek Network), Tibor Tribus (Co-Founder, Maya-ZK).
26. **Panel: L2 Panel @ ETH Taipei**
    * [ETH Taipei](https://www.youtube.com/watch?v=9RlyWo2fDPI), *Taipei, Taiwan, 2024.*
    * *Panelists:* Jordi Baylina (Co-founder, Polygon-Herme&#x7A;*),* Vitalik Buterin (Ethereum Foundation), Martin Derka (Co-Founder, Zircuit), Karl Floersch (CEO, OP Labs), Queenie Wu.
27. **Sequencer Level Security**
    * [ETH Prague](https://www.youtube.com/watch?v=xpwhTC2G1WI), *Prague, Czech Republic, 2024.*
28. **SoK: Compression in Rollups**
    * IEEE International Conference on Blockchain and Cryptocurrency (ICBC), *Dublin, Ireland, 2024.*
29. **Panel: ZK Season in 2024 : Hype VS. Reality**
    * L3 Summit at Token 2049, *Dubai, UAE, 2024.*
30. **Panel: Unlocking Scalability: Founders' Perspectives on ZK**
    * Google Web3 Roadshow Dubai, *Dubai, UAE, 2024.*
31. **Panel: EVM Equivalence in Rollups**
    * Ethereum Zurich, *Zurich, Switzerland, 2024.*
32. **A Rollup Comparison Framework**
    * ChainScience 2024, *Zurich, Switzerland, 2024.*
33. **Sequencer Level Security**
    * [Ethereum Zurich](https://youtu.be/IhmtmXuAFO8?si=b6pAZiglw9rAgqO3), *Zurich, Switzerland, 2024.*
    * ETH Denver, *Denver, CO, USA, 2024.*
34. **Zircuit: A Security-Focused ZK Rollup Built on the OP Stack**
    * ETH Denver, *Denver, CO, USA, 2024.*
35. **Zircuit: Challenges of Building an EVM Equivalent ZK Rollup on the OP-Stack**
    * [Epic Infra Day](https://www.youtube.com/watch?v=RUQerNv9-MM), *Istanbul, Turkiye, 2023.*
    * ETH Denver, *Denver, CO, USA, 2024.*
36. **Attacks on Rollups**
    * 4th International Workshop on Distributed Infrastructure for the Common Good (DICG), *Bologna, Italy, 2023.*
37. **A Rollup Security Framework**
    * Epic L2 Day, *New York City, NY, USA, 2023.*
    * L2Warsaw, *Warsaw, Poland, 2023.*
38. **Lightweight Formal Methods in dApp Development**
    * [ETH Warsaw](https://www.youtube.com/watch?v=6WD_gvx1ei4), *Warsaw, Poland, 2023.*
39. **Automated Analysis of Halo2 Circuits**
    * 21st International Workshop on Satisfiability Modulo Theories (SMT), *Rome, Italy, 2023.*
40. **Towards Satisfactory Web3 Software Engineering**
    * 14th Pragmatics of SAT International Workshop, *Alghero, Italy, 2023.*
41. **Evaluating Rollup Compression**
    * ETH Portland, *Portland, OR, USA, 2023.*
42. **Automated Flash Loan Attack Synthesis**
    * [DeFi Security Summit](https://www.youtube.com/watch?v=e6VR7Rv-jiY), *Paris, France, 2023.*
43. **Quantstamp's Ethereum Grants**
    * [ETHGlobal Pragma](https://www.youtube.com/watch?v=VlzK0UYussQ), *Paris, France, 2023.*
44. **Smart Contract Development**
    * [ETHGlobal Waterloo](https://www.youtube.com/watch?v=hZX8u736Q9k), *Waterloo, ON, Canada, 2023.*
45. **Linting Halo2 Circuits**
    * ZKSummit 9, *Lisbon, Portugal, 2023.*
    * [ETH Denver](https://www.youtube.com/watch?v=66gtzO-G1IA\&t=1s), *Denver, CO, USA, 2023.*
46. **Why L2s Are the Key to Onboarding Billions**
    * [The Rollup](https://www.youtube.com/watch?v=RXrN8hmtXik), *online, 2023.*
47. **Not Quite Water Under the Bridge**
    * IEEE International Conference on Blockchain and Cryptocurrency (ICBC), *Dubai, UAE, 2023.*
    * Berkeley Blockchain Xcelerator, *Berkeley, CA, USA, 2023.*
    * [ETH Portland (2022)](https://www.youtube.com/watch?v=BafvOY3PXSU), *Portland, OR, USA, 2022.*
    * [ETH Denver](https://www.youtube.com/watch?v=f4GOa4XwCjY), *Denver, CO, USA, 2022.*
48. **The Impact of Chain Forks and Reorgs on Cross-chain Bridges**
    * [ETH Denver](https://www.youtube.com/watch?v=nkIxK3zn_To), *Denver, CO, USA, 2023.*
49. **Protecting Bridges from Most Recent Hacks**
    * [ETH Denver](https://www.youtube.com/watch?v=5a7ekBGuGks), *Denver, CO, USA, 2023.*
50. **Ideal Properties of Rollup Escape Hatches**
    * 3rd International Workshop on Distributed Infrastructure for the Common Good (DICG), *Quebec, QC, Canada, 2022.*
    * [DevCon 6](https://www.youtube.com/watch?v=xjEK8PrH9kQ), *Bogota, Colombia, 2022.*
51. **An Overview and Wishlist of Rollup Escape Hatches**
    * [DevCon 6](https://www.youtube.com/watch?v=xjEK8PrH9kQ), *Bogota, Colombia, 2022.*
52. **The Blockchain Bridge You Dream About**
    * [DevCon 6](https://www.youtube.com/watch?v=X_GE6VrmDvM), *Bogota, Colombia, 2022.*
53. **The Proper Treatment of Randomness**
    * [ETH Denver](https://www.youtube.com/watch?v=b2fo8uiVHWk), *Denver, CO, USA, 2022.*
54. **EVM-to-EVM Bridges: The Good, The Bad, and The Ugly**
    * [ETH Denver](https://www.youtube.com/watch?v=Oa-b6mROCeI\&t=1s), *Denver, CO, USA, 2022.*
55. **How to Hack a Bridge in 2022**
    * [ETH Denver](https://www.youtube.com/watch?v=5a7ekBGuGks), *Denver, CO, USA, 2022.*


# Security

Zircuit aims to raise the web3 security bar by adding cutting edge real-time security features to create a more secure transaction environment. By focusing on security at the sequencer level, Zircuit is able to provide additional security to Zircuit transactions and the contracts deployed on it.

### Zircuit Team Expertise

Developed over the past year and a half by experts in blockchain security and technology, Zircuit is built with a security-first mindset. We’ve been at the forefront of research into topics like rollup security tooling, rollup compression, and scaling cryptography. This work has earned us multiple L2 research grants from the Ethereum Foundation and the Zcash foundation. Zircuit's security experts have audited everything from layer one networks to layer twos, and everything in between.

### Base Level Security

As with other L2 networks on Ethereum, Zircuit inherits security from Ethereum. As a zero-knowledge rollup, Zircuit's state is tied to cryptographic proofs posted on the underlying blockchain.

### Security at the Sequencer Level

Zircuit will protect users at the [sequencer level](/info/architecture/sls-deep-dive) by monitoring the mempool for malicious transactions and preventing their inclusion into a block when catastrophic issues or attacks are found and verified. Zircuit’s protocol is revolutionizing web3 security by moving attack prevention to the underlying sequencer level in addition to security efforts focused on the application and smart contract level.

### Secure Native Bridge

Zircuit’s native bridge infrastructure incorporates best-in-class security architecture and safety practices. The canonical bridge is straightforward and easy to use while maximizing user security, and will also incorporate real-time threat prevention techniques.


# Privileged Roles

This page lists the addresses that operate the Zircuit network.

## Operators

### Batcher

This is a hot wallet that continuously submits transactions onchain. It’s the component that submits new transaction batches and lets the L2 blockchain finalize new blocks.

* **Mainnet (Ethereum) address**: [`0xAF1E4f6a47af647F87C0Ec814d8032C4a4bFF145`](https://etherscan.io/address/0xAF1E4f6a47af647F87C0Ec814d8032C4a4bFF145)
* **Testnet (Sepolia) address**: [`0xa07FA473B87D7ADee161f458aF300255B65F33f6`](https://sepolia.etherscan.io/address/0xa07FA473B87D7ADee161f458aF300255B65F33f6)

### Proposer

This is a hot wallet that continuously submits transactions onchain. It’s the component that submits new state roots for the L2 outputs that are essential for users to withdraw their funds on the L1.

* **Mainnet (Ethereum) address**: [`0xE8C20EA8eF100d7aa3846616E5D07A5aBb067C65`](https://etherscan.io/address/0xE8C20EA8eF100d7aa3846616E5D07A5aBb067C65)
* **Testnet (Sepolia) address**: [`0x79B1a59c9d510213Dce55C106fcBc64D2F98f34E`](https://sepolia.etherscan.io/address/0x79B1a59c9d510213Dce55C106fcBc64D2F98f34E)

## Admin roles

### Challenger ([multisig 1](#multisig-addresses))

This role is authorized to call `deleteL2Outputs()` to remove a state commitment. This only works for non-finalized outputs and is for emergency purposes only.

### System Config Owner ([multisig 1](#multisig-addresses))

This is the address authorized to change the settings in the `SystemConfig` contract. These settings are:

* The blocks signer: L2 blocks propagated among Zircuit nodes via peer-to-peer communication must be signed by this wallet to be considered valid.
* The batcher hash: identifier for the batcher operator.
* The gas config: overhead and scalar values used to determine the L1 costs.
* The gas limit: the maximum amount of gas allowed in every L2 block.
* The resource config: the configuration for the [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) based curve for the deposit gas market.

### L1 ProxyAdmin Owner ([multisig 1](#multisig-addresses))

This is the owner of the ProxyAdmin contract deployed on the L1 that controls most of the L1 contracts and can upgrade them if necessary. The ProxyAdmin contract is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy, based on the OpenZeppelin implementation.

### L2 ProxyAdmin Owner ([multisig 1](#multisig-addresses))

This is the owner of the ProxyAdmin contract deployed on the L2 that controls most of the L2 contracts and can upgrade them if necessary. The ProxyAdmin contract is an auxiliary contract meant to be assigned as the admin of an ERC1967 Proxy, based on the OpenZeppelin implementation.

## Emergency roles

### Guardian ([multisig 2](#multisig-addresses))

The `OptimismPortal` is pausable as a backup safety mechanism that allows a specific `GUARDIAN` address to temporarily halt deposits and withdrawals to mitigate security issues if necessary.

### **Monitor**

Addresses with the `MONITOR` role can pause message passing from L1 to L2, which includes ERC20, ERC721 and ETH deposits and withdrawals. Similarly on L2, this role can also pause message passing from L2 to L1.

### Operator

Addresses with the `OPERATOR` role have all the capabilities of the `MONITOR` role, and additionally they can unpause message passing, reenabling bridging. This role can also throttle the amount of ETH that is allowed to be deposited from L1 to L2 and similarly the amount of ETH that can be withdrawn from L2 to L1.

## Multisig addresses

To execute a transaction from these contracts, a certain threshold of owners needs to sign a message unique to each particular transaction. `6/8` means at least six out of eight owners need to sign the message.

* Multisig 1: `6/8`
  * Ethereum: [`0xC463EaC02572CC964D43D2414023E2c6B62bAF38`](https://etherscan.io/address/0xC463EaC02572CC964D43D2414023E2c6B62bAF38)
  * Zircuit: [`0xC463EaC02572CC964D43D2414023E2c6B62bAF38`](https://explorer.zircuit.com/address/0xC463EaC02572CC964D43D2414023E2c6B62bAF38)
* Multisig 2: `2/5`
  * Ethereum: [`0x2c0B27F7C8F083B539557a0bA787041BF22DB276`](https://etherscan.io/address/0x2c0B27F7C8F083B539557a0bA787041BF22DB276)
  * Zircuit: [`0x2c0B27F7C8F083B539557a0bA787041BF22DB276`](https://explorer.zircuit.com/address/0x2c0B27F7C8F083B539557a0bA787041BF22DB276)


# Bug Bounty

We are committed to maintaining the highest security standards for our systems and our users. As part of this commitment, we are announcing the following Bug Bounty Program, detailed below. This program is designed to encourage and reward security researchers and ethical hackers for identifying and reporting potential security vulnerabilities in our systems.

**Scope:**

We welcome submissions that identify vulnerabilities in our smart contracts. Submissions should focus on finding significant security issues that could potentially impact the integrity or availability of our data, or users' funds and data.

A separate bug bounty program exists for Zircuit Core, and can be found [here](/info/security/bug-bounty-zircuit-core).

**Rewards:**

**For smart contract vulnerabilities:**

* Critical - up to $100,000 USDC
* High - up to $50,000 USDC
* Medium - up to $10,000 USDC
* Low - up to $5,000 USDC

**Rules:**

* Do not engage in any activity that can harm Zircuit or its users.
* Provide us with a reasonable amount of time to fix the issue before any public disclosure.
* Do not exploit any vulnerability you find for any reason.
* Adhere to all applicable laws and regulations.

**Legal:**

Participation in our Bug Bounty Program is subject to our terms and conditions. By submitting a report, you agree to these terms, which include confidentiality provisions and a release of claims against Zircuit.

**Submission:**

Bug submissions can be disclosed to <bugbounty@zircuit.com>.


# Bug bounty - Zircuit network

## Zircuit Bug Bounty Program: Zircuit core

**Last updated on July 6, 2026**

{% hint style="info" icon="triangle-exclamation" %}
Zircuit is currently [migrating](https://x.com/Zircuit/status/2072772314446565594) its network infrastructure to [Conduit](https://www.conduit.xyz/), a rollup-as-a-service provider. We encourage researchers to continue reporting potential vulnerabilities. However, during the migration period, we reserve the right to be more selective in determining bounty eligibility and reward amounts. Once the migration is complete on mainnet, this page will be updated and reports affecting the network infrastructure should instead be submitted to [Conduit's responsible disclosure program](https://www.conduit.xyz/security).&#x20;
{% endhint %}

### Responsible Disclosure Guidelines

* Do not disclose vulnerabilities publicly or test them on production networks. Violating this policy will forfeit your right to a reward and may put users at risk.
* Do not file public issues disclosing the vulnerability.
* Do not test vulnerabilities on public testnets or mainnets.
* Submit all findings via the approved disclosure channel below.

### Reporting Process

* All vulnerabilities must be reported via email to: <bugbounty@zircuit.com>
* Reports must include:
  * Impacted repository and commit SHA where the vulnerable code exists
  * A detailed description of the issue, including a justification of the severity level (impact, feasibility and likelihood)
  * Steps to reproduce
  * A working and reproducible Proof of Concept (PoC)
  * How to fix the issue
* We strongly recommend encrypting your report using our PGP public key (instructions below) to protect sensitive details
* Our team will acknowledge the reception of your submission, assess its validity, may ask for further clarification if needed and will close the topic with a final decision regarding the validity of the submission.
* Provide us with a reasonable amount of time to fix the issue before any public disclosure.

### Scope of the Program

#### In-Scope Components

Zircuit Smart Contracts, excluding escape hatch functionality.

* <https://github.com/zircuit-labs/zkr-monorepo-public/packages/contracts-bedrock/src> (main branch)
* On-chain addresses:
  * <https://docs.zircuit.com/addresses/l1-bridge>
  * <https://docs.zircuit.com/addresses/l2-predeploys>

#### Out-of-Scope

* Code that is not used in Production
* Vulnerabilities already known internally by the Zircuit security team at the time of submission. Zircuit maintains privately timestamped internal records of all known issues to ensure fairness and consistency when determining eligibility.
* Known issues in the OP Stack, Geth, Kona, or OP-Succinct
* Zircuit's escape hatch smart contracts, except in cases where an exploit can result in lost funds prior to the intended enabling of the escape hatch functionality. For additional clarity, any exploits that only apply after the Zircuit sequencer stops posting state roots for the prescribed time — which enables the escape hatch functionality — are not currently in scope.
* Best practices or non-impactful code preferences
* Experimental or undeployed features
* Front-end, infrastructure bugs and Zircuit staking
* Social engineering or physical attacks against our employees or customers
* Purely theoretical issues without a Proof of Concept
* The program covers only the code that Zircuit has modified, or added in the repositories listed above. The parts of the code that were not modified from the original upstream projects (for example: OP Stack, Geth, Kona, OP-Succinct and other “vanilla” upstream sources) are **out of scope** (we recommend reporting in a responsible manner to the responsible team) **unless** the reporter either:
  * demonstrates a vulnerability introduced by Zircuit’s modifications (i.e., the issue is present in Zircuit’s forked repository because of changes made by Zircuit), or
  * provides a working Proof-of-Concept that shows an exploit path that is only exploitable in Zircuit’s production environment (for example due to Zircuit’s configuration, bundled dependencies, packaging, integration, or feature flags).

#### Unscoped findings

We welcome any relevant vulnerabilities **outside of the official scope**. Rewards will be granted **at our discretion** for significant and relevant findings. Note that a Proof of Concept is required in that case as well.

### Severity Classification System

* **Critical**:
  * Large amounts of funds permanently lost
  * Ability to cause the protocol to finalize an invalid state transition or to accept a corrupted canonical state
  * Denial of service (more than 24 hours) with broad impact (e.g., withdrawals delayed, core operations impaired) without requiring extraordinary attacker cost
* **High**:
  * Medium amounts of funds permanently lost (e.g. only affecting individual users or edge cases)
  * Large amounts of funds temporarily frozen (more than 6 hours) without requiring extraordinary attacker cost.
  * Temporary denial of service (more than 6 hours) with broad impact (e.g., withdrawals delayed, core operations impaired) without requiring extraordinary attacker cost.
* **Medium**:
  * Limited amounts of funds permanently lost (e.g., rounding errors, improper fee calculation, unlikely edge cases)
  * Medium amounts of funds temporarily frozen (more than 6 hours and only affecting individual users or edge cases) without requiring extraordinary attacker cost.

#### Disclaimers

For the triage, we will consider the following aspects to assess the correctness of the claimed severity:

* **Privileges & Preconditions**: If exploit requires privileged role (owner, admin), or only works under very rare chain states, that may reduce severity.
* **Scope / Breadth**: Whether only one user / one contract is affected vs many users / core contracts / the protocol’s treasury.
* **Recovery / Mitigations**: If the protocol has pre-stated mechanisms (pausing, emergency withdrawal, ability to upgrade) that can limit damage, or if vulnerabilities are mitigated at an operational level, that may reduce severity.

### Rewards

The reward amount is determined by the **severity of the bug** and **funds at risk**. We follow an internal model to assess risk and impact.

* **Critical**: Up to $50,000
* **High**: Up to $15,000
* **Medium**: Up to $5,000

Reward amounts may vary based on factors such as:

* The exploitability of the vulnerability
* The impact on funds and system integrity
* The quality of the report and proof of concept

**For payouts, we**:

* We offer payments in USDC, USDT and USD
* We require invoices
* We require KYC

### Eligibility & Legal

Participation in this program is open to security researchers, but must comply with applicable laws.

For a given issue, the first eligible and valid report submitted for the root cause of that issue will get the reward.

Any prior direct or indirect access to confidential information may lead to ineligibility to participate. If unsure, ask us by email to: <bugbounty@zircuit.com>.

**Participation in our Bug Bounty Program is subject to our terms and conditions. By submitting a report, you agree to these terms, which include confidentiality provisions and a release of claims against Zircuit.**

### Program Governance

Zircuit reserves the right to:

* Bypass this policy and disclose sooner if necessary for user protection
* Privately notify selected downstream users before a public disclosure
* Adjust this policy and its terms as the program evolves

### Resources

#### Testing Guidelines

Please do not test on production. We recommend setting up a local environment for safe testing. Local forks of Mainnet as PoCs are accepted, if they demonstrate the feasibility of the attack. For testing guidelines, please look at the README files.

#### Documentation

Zircuit Developer Docs - <https://docs.zircuit.com/>

#### PGP encryption

If you want to share an encrypted submission, we suggest the formats .txt, .md, .pdf or .zip

1. Install GnuPG On macOS: `brew install gnupg` On Ubuntu/Debian: `sudo apt-get update && sudo apt-get install -y gnupg` On Windows: `Download Gpg4win`
2. Verify the key Fingerprint: `8852ABB76E1598994AB1C5772CFF7C61FC8EDC03` Full key: <https://keys.openpgp.org>
3. Encrypt the file report.txt: `gpg --encrypt --armor -r bugbounty@zircuit.com report.txt`
4. Send an email to <bugbounty@zircuit.com> with the encrypted file attached


# Audit Reports

**Security is the foundation of Zircuit's innovation.** We prioritize safeguarding our users and the integrity of our platform. In addition to conducting **rigorous internal audits** by our experts, we collaborate with **trusted, industry-leading external partners** to ensure our systems are secure against vulnerabilities. These external audits provide an additional layer of assurance, ensuring that Zircuit meets the highest standards of security and reliability in the Web3 ecosystem.

Here is the link to our audits: <https://github.com/zircuit-labs/audit-report/tree/main>

<table><thead><tr><th width="159">Auditor</th><th width="221">Description</th><th width="206">Audit Date</th><th>Report</th></tr></thead><tbody><tr><td>Quantstamp</td><td>L2 Upgradeable Contract</td><td>28 September 2024</td><td><a href="https://certificate.quantstamp.com/full/zircuit-l-2-upgradeable-contract/e765bc0e-0cbc-40f8-9a84-a7cfbd91cae0/index.html">Link</a></td></tr><tr><td>Quantstamp</td><td>Lido's wstETH</td><td>23 September 2024</td><td><a href="https://certificate.quantstamp.com/full/zircuit-lido-new-proposal/a8c75e25-29ae-4628-ab06-4c3a35123652/index.html">Link</a></td></tr><tr><td>Secure3</td><td>USDCAdapter</td><td>19 September 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit-USDCAdapter_Secure3_Audit_Report-updated.pdf">Link</a></td></tr><tr><td>Secure3</td><td>Token and Migration</td><td>18 September 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit_zrc_token_Secure3_Audit_Report.pdf">Link</a></td></tr><tr><td>Salus Security</td><td>Liquidity Hub</td><td>14 September 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit-Labs_Zkr-Staking_report_2024-09-17.pdf">Link</a></td></tr><tr><td>Halborn</td><td>Token and Migration</td><td>2 August 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit_Labs_zkr_staking_Migration_Contracts_Smart_Contract_Security.pdf">Link</a></td></tr><tr><td>Decurity</td><td>Token and Migration</td><td>31 July 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit-ZRC-Token-audit-report-2024-1.1.pdf">Link</a></td></tr><tr><td>Dedaub</td><td>Bridge</td><td>24 July 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/Zircuit_OP_Bridge_July_24%2C_2024_Dedaub_Audit_Reports_gdoc.pdf">Link</a></td></tr><tr><td>Dedaub</td><td>Staking Pool</td><td>11 March 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/dedaub-audit-zkr-staking-ztakingpool.pdf">Link</a></td></tr><tr><td>OtterSec</td><td>Staking Pool</td><td>15 February 2024</td><td><a href="https://github.com/zircuit-labs/audit-report/blob/main/ztakingpool_ottersec.pdf">Link</a></td></tr></tbody></table>

### Acknowledged Issues

* `eth_call` , `eth_estimateGas` and similar rpc calls do not include the L1 fee in their estimation. This can lead to transactions succeeding on the rpc call that later fail due to insufficient funds. We recommend [manually computing the l1 fee](/info/architecture/gas-pricing-and-transaction-fees/l1-data-fee-calculation) when operating with low-balance accounts.

Last updated on 14 August, 2025


# Frequently Asked Questions (FAQ)

### What is Zircuit?

Zircuit is an EVM-compatible zero-knowledge rollup that unlocks web3's complete capabilities. Built on cutting-edge L2 research with zkVM provers, it delivers accelerated transactions, lower costs, and rapid finalization. As the pioneering rollup to implement AI-driven security through Sequencer-Level Security (SLS), Zircuit proactively blocks malicious transactions before they can reach the blockchain

### What is Garfield?

Garfield is the second testnet for Zircuit, a version of the Zircuit network released in February 2025, deprecating the legacy testnet. Garfield is hardforked to use zkVMs and as such, is Ethereum equivalent.

### What are the main features of Zircuit?​

**Pioneering Research:** Over the past year and a half, we’ve been at the forefront of research into topics such as rollup security tooling, rollup compression, and scaling cryptography. This work has earned us multiple L2 research grants from the Ethereum Foundation.

**Security at the Sequencer Level:** Zircuit will protect users at the sequencer level by monitoring the mempool for malicious transactions and preventing their inclusion into a block. In comparison to typical security efforts that focus on the application and smart contract levels, Zircuit’s revolutionary approach goes directly to the underlying sequencer level.

**Secure Native Bridge:** Zircuit’s native bridge infrastructure incorporates best-in-class security architecture and safety practices. The canonical bridge is straightforward and easy to use while maximizing user security.

**Cutting-Edge Performance:** By decomposing circuits into specialized parts and aggregating proofs, Zircuit achieves greater efficiency and lower operating costs. Combined with larger transaction batches and accelerated proof processing, users benefit from faster and cheaper transactions.

**Ethereum Application Compatibility:** Zircuit works with all your favorite Ethereum apps. It supports all major wallets such as MetaMask, as well as tools like Hardhat. Deploy Ethereum dApps seamlessly without the need to learn a new programming language or framework.

### What does it mean that Zircuit is EVM-compatible?

For the developers and users of our network, this means that tooling and wallets will work just as they are used to, resulting in minimal development overhead. Anyone can deploy Ethereum dApps seamlessly without the need to learn a new programming language or framework—simply change the deployment endpoints when you’re ready to go live. All gas fees on Zircuit are paid in ETH. Zircuit can deploy smart contract code that is compatible with the Ethereum Virtual Machine (EVM), which powers the Ethereum network itself.

### How do I join the Zircuit community as a member or builder?

You can find us on [Twitter/X](http://twitter.com/zircuitL2) or on [Discord](https://zircuit.com/discord).

### Which wallets are supported?

Zircuit supports all major wallets such as MetaMask and Rabby Wallet

### What tech stack is Zircuit built on?

Zircuit is composed of best-in-class technology improved by proprietary research efforts. The tech stack consists of:

* Geth (Ethereum’s go client)
* Code from the OP Stack (the battle-tested framework for rollups)
* A modified version of Kona in OP-Succinct, specifically to support Zircuit’s Sequencer Level Security functionality as well as other general-purpose needs

### How does Zircuit work in a nutshell?​

There are three actors involved in a rollup chain: Sequencer, Prover, and User.

We maintain the account Merkle tree on-chain, which can be updated by sending SNARK proofs. Users send transactions to the sequencer with signatures via RPC/APIs. The Sequencer collects all the transactions and creates a batch, which is processed by the zkVM Prover. If the zkVM Prover finds all transactions in the batch to be valid, it emits a proof. This validity proof will be submitted and verified on-chain, which signifies that the state was updated properly off-chain. This updates the on-chain account Merkle tree.

### What does "security at the sequencer level" mean?

Zircuit services will constantly monitor the mempool for malicious transactions, exploit contracts, and more. Once detected and verified as malicious, Zircuit can prevent their inclusion into the next block. This means that every transaction on Zircuit will go through security checks, resulting in a more secure chain for projects and end users.

### Does Zircuit have a token?

Zircuit uses native ETH for gas. Zircuit also has a token, see details here: <https://docs.zircuit.com/tokenomics/zircuit-token-zrc>

### Can I bridge NFTs to Zircuit?

Yes, Zircuit supports NFTs (ERC721) being bridged from Ethereum by deploying the `OptimismMintableERC721` contract using the `OptimismMintableERC721Factory` that is found <https://docs.zircuit.com/addresses/predeploys>, and then using the `L1ERC721BridgeProxy` on Ethereum <https://docs.zircuit.com/addresses/bridge>. \
Bridging Zircuit native NFTs over to Ethereum is currently not supported by the `ERC721Bridge` .

<br>


# Concepts

This page provides a glossary of Zircuit concepts and terms.

Some of these concepts are unique to Zircuit, while others are common rollup terms.

**Bridge transaction** - a cross chain transaction that requires a bridge component to manage it. Typically bridge transactions lock up assets like ETH or ERC-20 tokens on one chain and mint corresponding assets on another chain, like Zircuit. A **deposit transaction** is a special case where the source chain is Ethereum and the target chain is Zircuit, while a **withdrawal transaction** is the special case where the source chain is Zircuit and the target chain is Ethereum.

**Cross domain message** - data relayed across chains. Typically a cross domain message would contain information representing a function call on a contract on the other chain. Such a function call can be an ERC-20 token transfer, for example.

**Sending a cross domain message** - the act of sending the message to the cross domain messaging contract on the source chain. Sending a cross domain message requires placing a message on the source chain in the respective smart contract’s state so that it can be processed further. For example, one can send a cross domain message from Ethereum to Zircuit.

**Relaying a cross domain message** - the act of taking a message that was already sent on the source chain and applying it to the target chain in the form of a transaction. This is the finalization step of cross domain messaging.

**Replaying a cross domain message** - the act of re-sending an existing Ethereum to Zircuit message with a different gas limit to the cross domain messaging contract on Ethereum. Replaying is not applicable to the Zircuit to Ethereum direction.

**Sequencer** - an off-chain component which provides transactions to the L2 execution engine. Transactions posted by the batch submitter to Ethereum are determined by the state of L2 derived by the sequencer node. As part of the main rollup node, it also watches Ethereum to derive the state of the L2 from the previously posted batches and state roots, and to observe deposit transactions. The L2 derivation includes checking that previous state roots and transactions were recorded on Ethereum, as well as tracking things like gas price on Ethereum to update relevant values on L2.

**Batch submitter** - an off-chain component which writes transaction batches, determined by the sequencer, to Ethereum. The batcher provides full data availability on Ethereum for Zircuit. In turn, users have a soft commitment that their transaction will be included and proven in the L2 chain.

**Verifier smart contract** - a smart contract deployed on Ethereum. It accepts or rejects zero-knowledge proofs constructed for batches of L2 blocks.

**Prover** - an off-chain component responsible for generating zero-knowledge proofs of state transitions or parts of them. A prover may be very specific or very general. A specific prover may prove a Keccak operation, while a more general one may prove that a block is constructed correctly.

[**Proof aggregation**](https://vitalik.ca/general/2021/11/05/halo.html) is the process of collecting multiple proofs, possibly from multiple different circuits, and creating a single proof for all these proofs. More general Zircuit provers may take other proofs as input and may aggregate other proofs, to create a single output. The Zircuit provers are organized by a component called the **proof orchestrator**, which keeps track of which transitions need to be proved and which proofs need to be aggregated. The input to the proving pipeline is the L2 blocks, and the ultimate result is a **validity proof** for a batch of blocks.

**Proposer** - an off-chain component which writes proven updates to the Zircuit state on L1. After the provers generate a validity proof for a batch of blocks, the proposer writes a new state root for the L2. It does this by providing the state root computed by the state transitions within the batch and the validity proof to the Zircuit contracts on Ethereum. The state is accepted when the verifier, as part of the proposer’s call to the contract, verifies the validity proof.

**Block status** - an L2 block can have one of the following statuses: **Waiting for Proof** or **Proved**. A block that has the status **Waiting for Proof** means that the sequencer has produced the block, but the provers have not yet submitted a state root for a batch which contains the block. A block with the **Proved** status is one that has been created by the sequencer and is contained in a block batch which was submitted to the verifier contract with a validity proof.

As Zircuit is built in part on the OP Stack, the [Optimism glossary](https://github.com/ethereum-optimism/optimism/blob/develop/specs/glossary.md) may also be helpful.


# Media Kit

**Zircuit Finance (Gold)**

{% file src="/files/jcY65ct0Ho8PdEihE2ph" %}

{% file src="/files/0rVjkY5Dk4BYb6UQ9t1Q" %}

{% file src="/files/lkP1BxyfiWpcCzYLx9fo" %}

{% file src="/files/ADrA4e7R84yece31GRj3" %}

**Zircuit Logo**

{% file src="/files/26Igm0nqPvkSrfLIeLzX" %}

{% file src="/files/8wBzWOQZgRC87QSnDgZt" %}

{% file src="/files/NuJonnGDE96iirmK64E0" %}

{% file src="/files/WHKjADxtnsKnY7uDgVu0" %}

{% file src="/files/er1O0sPGEcJHA2Nh175f" %}

**Zircuit Icon**

{% file src="/files/JO85SlEkcg0Vks1J5jqx" %}

{% file src="/files/wnAXkw8Xrc2fIAXBLp43" %}

{% file src="/files/WcOpbKqwMQINPy0PUjJT" %}

{% file src="/files/XgRkRUZWO7TLoEpxPted" %}

{% file src="/files/x7GIsE9CzhXlSTrWEdUD" %}

**Examples:**

<figure><img src="/files/z6eM2iombyMQ9JSxqL0l" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/aUN7rofZsYdR27GSRxEp" alt=""><figcaption></figcaption></figure>


