# ADI's Mission & Vision

ADI Chain's mission, vision, and role in compliant onchain infrastructure.

If you’re reading this, you’re likely familiar with the profound impact blockchain technology has made on the global financial industry over the past decade - as well as blockchain’s immense potential to serve as the rails for the future of finance.

But even the blockchain industry’s most ardent proponents (including ADI Foundation) can acknowledge that the blockchain revolution is not yet truly global in scope. Most blockchain activity - and the corresponding benefits it can provide governments, businesses and citizens - is still concentrated in relatively few geographic markets.

While ADI Chain was developed to co-write the next chapter of financial history, much of the world has missed out on the last couple. Today, approximately one quarter of the world’s population remains unbanked and billions more are completely disconnected from the digital transformations that developed markets have experienced since the advent of the internet.

The ADI Foundation’s goal is to play a major role in leveling this playing field by bringing one billion people onchain in emerging markets by 2030 with the launch of ADI Chain.

ADI Chain is a modular, highly performant, customizable, and cost-effective Layer-2 (L2) blockchain secured by Ethereum. Built on zkSync OS and the Airbender proving system, ADI Chain executes transactions off-chain, verifies them via zero-knowledge validity proofs, and finalizes on Ethereum — combining high throughput with L1-grade security.

ADI Chain is EVM-compatible, allowing developers to deploy Ethereum smart contracts and dApps with minimal modifications, while benefiting from dramatically lower fees and faster finality.

ADI Chain is designed to empower institutions and governments to leverage blockchain-enabled innovations — spanning stablecoins, cross-border remittances, decentralized financial networks, tokenized real-world assets (RWAs), digital payments, healthcare data, and logistics — to unlock new forms of trust, efficiency, and inclusion that benefit citizens and societies

And, unlike the vast majority of market entrants, ADI Chain is built to provide L3 chains with built-in compliance capabilities to ensure networks can be fully compliant with the laws and regulations governing their operations.

### Governments and institutions need Different Ideas

#### Meet the digital upgrade for legacy systems

Legacy infrastructure - from government services to banking to digital identity - remains fragmented and opaque. This is especially true across many parts of the Middle East, Africa and Asia - regions that account for approximately 75% of the world’s people.

Services don’t connect. Personal data isn’t secure. Using money can be as difficult as earning it.

While solving challenges of this scale and complexity will require contributions from all levels of society, government and institutions will play a key role - just like they did paving the road to growth for today’s developed economies a century ago.

Yet, governments and institutions have generally shied away from blockchain technology because current systems lack the compliance features needed to govern their nations. ADI Chain was designed specifically to bridge those gaps.

#### Delivering compliant innovation across the globe

ADI Chain marries innovation and compliance with its capability to build modular Layer‑3s (L3) on top of the network. Entities can operate their own compliance-optimized L3 domain, segmented by jurisdiction, sector, or policy, while staying securely connected to the ADI Chain L2. Every project built on ADI Chain will receive the benefits of a transparent, trackable blockchain, economic security from the Ethereum L1 and the flexible compliance features required for its particular use case and jurisdiction.

The long-term vision is an **interconnected system of institutional and government L3s**, that are connected through the ADI chain to the broader Web3 ecosystem. This should see trillions of dollars move onchain and flow both cross-business and cross-border, in a transparent, trackable and secure way, with same day settlement.

{% hint style="info" %}
ADI Chain supports a growing ecosystem of compliant institutional and government applications. Browse the full [ADI Ecosystem Map](https://ecosystem.adi.foundation/) to see deployed projects, partners, and interconnected L3 networks.
{% endhint %}

Efficient. Connected. Fully compliant.


# ADI Mainnet

Get connected to ADI Network Mainnet

<a href="https://adi-foundation-labs.github.io/adi-mainnet-connect/connect.html" class="button primary" data-icon="wallet">Add ADI Network to Wallet</a>

### Manually Add ADI Network

To manually add ADI Network Mainnet as a custom network in your wallet, follow these steps:

1. Find the “Add Network” option in your wallet (in MetaMask, you can find this in the networks dropdown).
2. Click on “Add Network" and then "Add network manually."
3. Fill in the following details for the ADI Network mainnet environment:

#### Network details

<table><thead><tr><th width="267.19921875">PROPERTY</th><th>VALUE</th></tr></thead><tbody><tr><td><strong>Network Name</strong></td><td><code>ADI Network</code></td></tr><tr><td><strong>RPC URL*</strong></td><td><a href="https://rpc.adifoundation.ai/">https://rpc.adifoundation.ai/</a></td></tr><tr><td><strong>Chain ID</strong></td><td><code>36900</code></td></tr><tr><td><strong>Currency Symbol</strong></td><td><code>ADI</code></td></tr><tr><td><strong>Block Explorer URL</strong></td><td><a href="https://explorer.adifoundation.ai/">https://explorer.adifoundation.ai/</a></td></tr><tr><td><strong>Alternative Block Explorer URL</strong></td><td><a href="https://explorer-bls.adifoundation.ai/">https://explorer-bls.adifoundation.ai/</a></td></tr><tr><td><strong>L1 ADI token contract</strong></td><td><a href="https://etherscan.io/address/0x8b1484d57abbe239bb280661377363b03c89caea"><code>0x8b1484d57abbe239bb280661377363b03c89caea</code></a></td></tr><tr><td><strong>Bridgehub</strong></td><td><a href="https://etherscan.io/address/0xcf1c73439c85f7eb9d4439daf398fd6392d176e6"><code>0xcf1c73439c85f7eb9d4439daf398fd6392d176e6</code></a></td></tr><tr><td><strong>Asset Router</strong></td><td><a href="https://etherscan.io/address/0x47eec6f57c7e84391ba6c9ac976537d0db0bb257"><code>0x47eec6f57c7e84391ba6c9ac976537d0db0bb257</code></a></td></tr><tr><td><strong>Protocol version</strong></td><td><code>v0.30.1</code></td></tr><tr><td><strong>Sequencer version</strong></td><td><code>v0.21.1-b4</code></td></tr><tr><td><strong>External node version</strong></td><td><code>v0.21.1-b4</code></td></tr></tbody></table>

\*RPC endpoint is rate-limited. You can also access the RPC through [Alchemy](https://dashboard.alchemy.com/?utm_source=chain_partner\&utm_medium=referral\&utm_campaign=adi) or [NowNodes](https://nownodes.io/nodes/adi-adi). NowNodes does not support WebSocket for ADI. It may return HTTP status errors instead of JSON-RPC error codes.

{% hint style="info" %}
**Building an L3 on ADI?** Send us the IP addresses of your sequencer and prover, and we will whitelist them for full-speed RPC access. See [L3 Chains](/adi-network-components/overview-1).
{% endhint %}

#### Bridging ADI and ETH

You can use the [Bridge](https://bridge.adifoundation.ai/) to bridge ADI and ETH to mainnet.

### Important notes

{% hint style="warning" %}
What is not supported currently:

* ERC-7702
* ERC-4844
* `debug_traceCall` RPC call with custom tracers
  {% endhint %}


# ADI Testnet

Get connected to ADI Network Testnet

<a href="https://adi-foundation-labs.github.io/adi-testnet-connect/connect.html" class="button primary" data-icon="wallet">Add ADI Testnet to Wallet</a>

### Manually add ADI Network Testnet

To manually add ADI Network Testnet as a custom network in your wallet, follow these steps:

1. Find the “Add Network” option in your wallet (in MetaMask, you can find this in the networks dropdown).
2. Click on “Add Network" and then "Add network manually."
3. Fill in the following details for the ADI Network Testnet environment:

#### Network details

<table><thead><tr><th width="267.19921875">PROPERTY</th><th>VALUE</th></tr></thead><tbody><tr><td><strong>Network Name</strong></td><td><code>ADI Network AB Testnet</code></td></tr><tr><td><strong>RPC URL*</strong></td><td><a href="https://rpc.ab.testnet.adifoundation.ai/">https://rpc.ab.testnet.adifoundation.ai/</a></td></tr><tr><td><strong>Chain ID</strong></td><td><code>99999</code></td></tr><tr><td><strong>Currency Symbol</strong></td><td><code>ADI</code></td></tr><tr><td><strong>Block Explorer URL</strong></td><td><a href="https://explorer.ab.testnet.adifoundation.ai/">https://explorer.ab.testnet.adifoundation.ai/</a></td></tr><tr><td><strong>L1 ADI token contract (Ethereum Sepolia)</strong></td><td><a href="https://sepolia.etherscan.io/token/0x2a98b46fe31ba8be05ef1ce3d36e1f80db04190d?a=0xf5B0Ae14b62454782F79559aD28394213401d59B#readProxyContract"><code>0x2a98b46fe31ba8be05ef1ce3d36e1f80db04190d</code></a></td></tr><tr><td><strong>Faucet</strong></td><td><a href="http://faucet.ab.testnet.adifoundation.ai/">http://faucet.ab.testnet.adifoundation.ai/</a></td></tr><tr><td><strong>Bridgehub</strong></td><td><a href="https://sepolia.etherscan.io/address/0xFd3cE61C65dDd1039e6e9e07FB6D6e16388d1cc7"><code>0xFd3cE61C65dDd1039e6e9e07FB6D6e16388d1cc7</code></a></td></tr><tr><td><strong>Asset Router</strong></td><td><a href="https://sepolia.etherscan.io/address/0x5BA5B3a62745cD4eC5a59A22c988b2Dc2ae06573"><code>0x5BA5B3a62745cD4eC5a59A22c988b2Dc2ae06573</code></a></td></tr><tr><td><strong>Protocol version</strong></td><td><code>v0.30.1</code></td></tr><tr><td><strong>Sequencer version</strong></td><td><code>v0.21.1-b4</code></td></tr><tr><td><strong>External node version</strong></td><td><code>v0.21.1-b4</code></td></tr></tbody></table>

\*You can also access the RPC through [Alchemy](https://dashboard.alchemy.com/?utm_source=chain_partner\&utm_medium=referral\&utm_campaign=adi). NowNodes does not currently support ADI Testnet.

### Get Testnet funds for your wallet.

Once you have your wallet connected to the ADI Network Testnet environment, you can get testnet funds from [testnet faucet](http://faucet.ab.testnet.adifoundation.ai/).

**Third-party test assets**

Some ecosystem applications operate their own faucets for application-specific test assets. Pearl Digital provides a third-party faucet and onboarding service for its P3 environment, including PRLUSD and FlashD test assets:

* [Pearl P3 testnet faucet](https://faucet.pearldigital.com/)

Pearl’s faucet does not replace the official ADI Testnet faucet. Use the official faucet for native ADI test tokens required for general network gas.

{% hint style="warning" %}
Third-party faucets are independently operated. They may have their own eligibility, onboarding, rate-limit, and availability requirements.
{% endhint %}

#### Bridging testnet ADI and ETH

You can use the [The Bridge](https://bridge.ab.testnet.adifoundation.ai/) to bridge testnet ADI and ETH over to the testnet.

### Important notes

{% hint style="warning" %}
What is not supported currently:

* ERC-7702
* ERC-4844
* `debug_traceCall` RPC call with custom tracers
  {% endhint %}


# Quickstart

Getting starting developing with ADI Network Testnet

You can use all standard EVM tooling for ADI Network Testnet. You can find instructions for bridging testnet ADI and ETH to the ADI Network Testnet in the [Network Details](/adi-networks/adi-testnet) page.

To get started with Foundry or Hardhat, follow the steps below:

## Deploying to ADI Network Testnet

Below are setup examples for **Foundry** and **Hardhat 3** (with Viem or Ethers).\
Select your preferred setup tab:

***

{% tabs %}
{% tab title="Foundry Setup" %}

## Foundry Setup

#### 1. The Counter contract code

```solidity
contract Counter {
    uint256 public number;

    function setNumber(uint256 newNumber) public {
        number = newNumber;
    }

    function increment() public {
        number++;
    }
}
```

#### 2. Create a new Foundry project

```bash
forge init Counter
cd Counter
```

#### 3. Build the project

```bash
forge build
```

#### 4. Set your private key for deploying, ensure that it has some balance

```bash
export TESTNET_PRIVATE_KEY="0x..."
```

#### 5. Create `counter.sol` file and put the contract code inside.

#### 6. Create `counter.s.sol` and put the script inside, the script will deploy the counter contract

```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 {
        vm.startBroadcast();

        counter = new Counter();

        vm.stopBroadcast();
    }
}
```

#### 7. Deploy the contract

{% hint style="success" %}
Command returns the address of the deployed contract. This address, in further steps, is expressed as `$DEPLOYED_CONTRACT_ADDRESS`
{% endhint %}

```bash
forge script script/Counter.s.sol \
--rpc-url https://rpc.ab.testnet.adifoundation.ai \
--broadcast --private-key $TESTNET_PRIVATE_KEY
```

#### 8. Set the number value

```bash
cast send $DEPLOYED_CONTRACT_ADDRESS \
"setNumber(uint256)" 5 \
--rpc-url https://rpc.ab.testnet.adifoundation.ai \
--private-key $TESTNET_PRIVATE_KEY
```

#### 9. Get the latest number value

```bash
cast call $DEPLOYED_CONTRACT_ADDRESS \
"number()" \
--rpc-url https://rpc.ab.testnet.adifoundation.ai
```

{% endtab %}

{% tab title="Hardhat 3 with Viem" %}

## Hardhat 3 with Viem

#### 1. Create a new project folder

```bash
mkdir hardhat-example
cd hardhat-example
```

#### 2. Initialize a new Hardhat 3 project with Node Test Runner and Viem.

```bash
npx hardhat --init
```

#### 3. Add ADI Network Testnet to `hardhat.config.ts` file and configure the ignition required confirmations.

```ts
  ignition: {
    requiredConfirmations: 1,
  },
  networks: {
    adiTestnet: {
      type: 'http',
      chainType: 'generic',
      url: 'https://rpc.ab.testnet.adifoundation.ai',
      accounts: [configVariable('TESTNET_PRIVATE_KEY')],
    },
  },
```

#### 4. Ensure that the module file `ignition/modules/counter.ts` contains the following code

```typescript
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';

export default buildModule('CounterModule', (m) => {
  const counter = m.contract('Counter');

  m.call(counter, 'increment', []);

  return { counter };
});
```

#### 5. Add your private key to the keystore as `TESTNET_PRIVATE_KEY` .

```bash
npx hardhat keystore set $TESTNET_PRIVATE_KEY
```

#### 6. Compile and deploy the example contract

{% hint style="success" %}
Command returns the address of the deployed contract. This address, in further steps, is expressed as `$DEPLOYED_CONTRACT_ADDRESS` , if it appears inside the scripts described below, replace it with the actual contract address.
{% endhint %}

```bash
npx hardhat compile
npx hardhat ignition deploy ignition/modules/Counter.ts --network adiTestnet
```

#### 7. Create a new script file in the `scripts` folder called `increment.ts` .

```bash
touch scripts/increment.ts
```

#### 8. Copy/paste the script below.

```typescript
import { network } from 'hardhat';
import { type Abi, defineChain } from 'viem';

const CONTRACT_ADDRESS = '$DEPLOYED_CONTRACT_ADDRESS';

const adiChain = defineChain({
  id: 99999,
  name: 'ADI Chain',
  network: 'adiTestnet',
  nativeCurrency: { name: 'ADI', symbol: 'ADI', decimals: 18 },
  rpcUrls: { default: { http: ['https://rpc.ab.testnet.adifoundation.ai'] } },
});

const { viem } = await network.connect('adiChain');

const publicClient = await viem.getPublicClient({ chain: adiChain });
const [senderClient] = await viem.getWalletClients({ chain: adiChain });
if (!senderClient) throw new Error('No wallet client. Set TESTNET_PRIVATE_KEY in hardhat config.');

const counterContract = await viem.getContractAt('Counter', CONTRACT_ADDRESS, {
  client: { public: publicClient, wallet: senderClient },
});

const initialCount = await publicClient.readContract({
  address: CONTRACT_ADDRESS,
  abi: counterContract.abi as Abi,
  functionName: 'number',
});
console.log('Initial count:', initialCount);

const tx = await senderClient.writeContract({
  address: CONTRACT_ADDRESS,
  abi: counterContract.abi as Abi,
  functionName: 'increment',
});
await publicClient.waitForTransactionReceipt({ hash: tx });
console.log('Transaction sent successfully');

const newCount = await publicClient.readContract({
  address: CONTRACT_ADDRESS,
  abi: counterContract.abi as Abi,
  functionName: 'number',
});
console.log('New count:', newCount);
```

#### 9. Run the script

```bash
npx hardhat run scripts/increment.ts
```

{% endtab %}

{% tab title="Hardhat 3 with Ethers" %}

## Hardhat 3 with Ethers

#### 1. Create a new project folder

```bash
mkdir hardhat-example
cd hardhat-example
```

#### 2. Initialize a new Hardhat 3 project with Mocha and Ethers.js.

```bash
npx hardhat --init
```

#### 3. Add ADI Network Testnet to the `hardhat.config.ts` file and configure the ignition required confirmations.

```bash
  ignition: {
    requiredConfirmations: 1,
  },
  networks: {
    adiTestnet: {
      type: 'http',
      chainType: 'generic',
      url: 'https://rpc.ab.testnet.adifoundation.ai/',
      accounts: [configVariable('TESTNET_PRIVATE_KEY')],
    },
  },
```

#### 4. Add your private key to the keystore as `TESTNET_PRIVATE_KEY` .

```bash
npx hardhat keystore set TESTNET_PRIVATE_KEY
```

#### 5. Ensure that the module file `ignition/modules/counter.ts` contains the following code

```typescript
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';

export default buildModule('CounterModule', (m) => {
  const counter = m.contract('Counter');

  m.call(counter, 'increment', []);

  return { counter };
});
```

#### 6. Compile and deploy the example contract.

{% hint style="success" %}
Command returns the address of the deployed contract. This address, in further steps, is expressed as `$DEPLOYED_CONTRACT_ADDRESS` , if it appears inside the scripts described below, replace it with the actual contract address.
{% endhint %}

```ts
npx hardhat compile
npx hardhat ignition deploy ignition/modules/Counter.ts --network adiTestnet
```

#### 7. Create a new script file in the `scripts` folder called `increment.ts`.

```bash
touch scripts/increment.ts
```

#### 8. Copy/paste the script below.

```typescript
import { network } from 'hardhat';

const CONTRACT_ADDRESS = '$DEPLOYED_CONTRACT_ADDRESS';

const { ethers } = await network.connect({
  network: 'adiTestnet',
  chainType: 'generic',
});

const [sender] = await ethers.getSigners();

const contract = await ethers.getContractAt('Counter', CONTRACT_ADDRESS, sender);

const initialCount = await contract.x();
console.log('Initial count:', initialCount);

const tx = await contract.increment();
await tx.wait();
console.log('Transaction sent successfully');

const newCount = await contract.number();
console.log('New count:', newCount);
```

#### 9. Run the script

```bash
npx hardhat run scripts/increment.ts
```

{% endtab %}
{% endtabs %}

***


# Block explorer

Explore the functionality of Block Explorer, a comprehensive tool for monitoring activities on the ADI Network.

[The Block Explorer](https://explorer.adifoundation.ai/) is a tool designed to provide comprehensive insights into all activities occurring on the ADI Network. This tool is especially useful for users and developers who need to monitor or interact with the blockchain. Block Explorer consists of three main components:

* **Block Explorer App:** This is the user interface that enables users and developers to navigate and examine transactions, blocks, batches, contracts, tokens, and other elements within the ADI Network.
* **Block Explorer API:** This component provides an HTTP API for accessing structured data from the ADI Network. It retrieves data from the database maintained by the Block Explorer Worker.
* **Alternative Block Explorer URL:** <https://explorer-bls.adifoundation.ai/>
* **Testnet Explorer API:** <https://explorer-api.ab.testnet.adifoundation.ai/>

{% hint style="warning" %}
Contract verification is broken on the primary explorer at `explorer.adifoundation.ai`. Use the [BLS mainnet explorer](https://explorer-bls.adifoundation.ai/) or the [ADI Network AB Testnet explorer](https://explorer.ab.testnet.adifoundation.ai/) instead. Verification works on both.
{% endhint %}

See [Verify smart contracts](/how-to-start/verify-smart-contracts) for supported verification workflows.


# The Bridge

How the ADI Canonical Bridge moves assets between Ethereum and ADI Chain — securely, trustlessly, and with zero-knowledge proofs.

[The Bridge](https://bridge.adifoundation.ai/) is the native, protocol-level bridge that connects **Ethereum (L1)** with the **ADI Chain**. Unlike third-party bridges that rely on external validators or multisigs, the canonical bridge inherits the full security of the Ethereum mainnet — every cross-chain message is verified through zero-knowledge proofs, making it as secure as Ethereum itself.

The bridge serves two primary purposes:

* **Deposits (L1 → L2)**: Move assets from Ethereum to ADI Chain, where they can be used for transactions, DeFi, and other on-chain activities with significantly lower fees and faster confirmations.
* **Withdrawals (L2 → L1)**: Move assets back from ADI Chain to Ethereum, verified by cryptographic proofs that guarantee correctness without trusting any intermediary.

To drive the canonical bridge programmatically, use the [ADI Chain MCP Server](/core-components/ai-agent-infrastructure/tools-for-agents/adi-chain-mcp-server).

The bridge supports:

* **Native ADI deposits** — the chain's base gas token
* **ERC20 token deposits** — any standard ERC20
* **First-time token bridging** — automatic contract deployment on the destination chain

### What Makes ADI Different

ADI Chain uses **ADI as its native gas token** instead of ETH. This means:

* All L2 transaction fees are paid in ADI
* When you deposit ADI from L1, it becomes native balance on L2 (like ETH on Ethereum)
* ADI exists as an ERC20 token on Ethereum, and as the native currency on ADI Chain
* Other ERC20 tokens can also be bridged and are represented as wrapped tokens on L2

***

## How It Works — The Big Picture

**Deposit (L1 → L2)** — lock tokens on Ethereum, mint on ADI Chain:

```mermaid
%%{init: {'theme': 'dark'}}%%
graph LR
    A((User)) -->|approve + deposit| B[Bridgehub]
    B --> C[Asset Router]
    C -->|lock| D[Token Vault]
    B -->|priority tx| E[Mailbox]
    E -.->|L1→L2| F[L2 Asset Router]
    F -->|mint| G((User on L2))

    style A fill:#4a90d9,color:#fff
    style G fill:#5bb974,color:#fff
```

**Withdrawal (L2 → L1)** — burn tokens on L2, prove to Ethereum, claim:

```mermaid
%%{init: {'theme': 'dark'}}%%
graph LR
    A((User on L2)) -->|withdraw| B[Base Token / Asset Router]
    B -->|burn| C[L1 Messenger]
    C -.->|ZK-proven batch| D[Executor]
    D -->|store root| E[Nullifier]
    E -->|verify proof| F[Asset Router]
    F -->|release| G[Token Vault]
    G --> H((User on L1))

    style A fill:#4a90d9,color:#fff
    style H fill:#5bb974,color:#fff
```

### Core Contracts

| Contract                  | Layer | Address                 | Role                                                                         |
| ------------------------- | ----- | ----------------------- | ---------------------------------------------------------------------------- |
| **Bridgehub**             | L1    | Deployed per ecosystem  | Central registry and routing for all L2 chains. Entry point for deposits     |
| **L1 Asset Router**       | L1    | Deployed per ecosystem  | Routes asset operations (lock/unlock) to appropriate asset handlers          |
| **L1 Native Token Vault** | L1    | Deployed per ecosystem  | Holds locked L1 tokens. Deploys bridged token contracts for L2-native tokens |
| **L1 Nullifier**          | L1    | Deployed per ecosystem  | Verifies Merkle proofs and prevents double-claiming of withdrawals           |
| **Mailbox**               | L1    | Per-chain Diamond Proxy | Serializes L1→L2 transactions and manages the priority queue                 |
| **Executor**              | L1    | Per-chain Diamond Proxy | Handles batch commit, prove, and execute lifecycle                           |
| **L2 Asset Router**       | L2    | `0x0000...00010003`     | Routes deposits to NTV on L2. Entry point for ERC20 withdrawals              |
| **L2 Native Token Vault** | L2    | `0x0000...00010004`     | Mints/burns bridged tokens. Deploys new token contracts on first bridge      |
| **L1 Messenger**          | L2    | `0x0000...00008008`     | System contract that sends L2→L1 messages (included in batch proofs)         |
| **L2 Base Token**         | L2    | `0x0000...0000800A`     | System contract representing the chain's base token (ADI)                    |
| **Contract Deployer**     | L2    | `0x0000...00008006`     | System contract for deterministic CREATE2 deployments on L2                  |

***

## Deposits: Moving Assets to ADI Chain (L1 → L2)

Deposits are fast — typically complete within **\~15 seconds**. Since Ethereum is the source of truth, the ADI sequencer can trust L1 events immediately and process them on L2.

### How a Deposit Works

```mermaid
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
    actor User
    participant BH as Bridgehub
    participant NTV as Token Vault
    participant MB as Mailbox
    participant L2 as L2 Contracts

    User->>BH: approve + requestL2Transaction
    BH->>NTV: lock tokens (bridgeBurn)
    BH->>MB: create priority tx
    MB-->>MB: emit NewPriorityRequest

    Note over MB,L2: ~15 seconds

    L2->>L2: finalizeDeposit → bridgeMint
    L2-->>User: tokens available on L2
```

### Step by Step

**Step 1 — Approve** Before depositing, you authorize the bridge to transfer your tokens. For ADI, this is a standard ERC20 `approve()`. For ERC20 tokens, a separate approval is needed for both the token and ADI (to cover L2 gas).

**Step 2 — Initiate the Deposit** You submit your deposit through the **Bridgehub** — the central entry point that coordinates all cross-chain transactions. The Bridgehub validates your request and routes it to the appropriate bridge contracts.

**Step 3 — Tokens Are Locked** The **L1 Asset Router** directs your tokens to the **L1 Native Token Vault**, where they are securely locked. A strict accounting system tracks every token locked per chain, ensuring 1:1 backing at all times.

**Step 4 — Priority Transaction Created** The **Mailbox** contract registers your deposit as a priority transaction — an L1→L2 message that the sequencer *must* process. This emits a `NewPriorityRequest` event on Ethereum.

**Step 5 — L2 Execution** The ADI sequencer detects the event, includes it in the next L2 block, and executes it:

* For **ADI deposits**: the bootloader mints native ADI balance to your L2 address
* For **ERC20 deposits**: the L2 Asset Router calls the L2 Native Token Vault, which mints a bridged token representation on L2. If this is the first time a token is being bridged, a new ERC20 contract is automatically deployed on L2 (see [First-Time Token Bridging](/adi-network-components/canonical-bridge#first-time-token-bridging)).

**Step 6 — Complete** Your tokens are now available on ADI Chain. The entire process takes roughly 15 seconds.

### Two Deposit Paths

The bridge uses different entry points depending on the token being deposited:

| Aspect               | Direct (`requestL2TransactionDirect`)      | Two Bridges (`requestL2TransactionTwoBridges`)       |
| -------------------- | ------------------------------------------ | ---------------------------------------------------- |
| **Use case**         | ADI base token deposits                    | ERC20 token deposits                                 |
| **Why**              | Only one token operation needed (lock ADI) | Two token operations: lock ADI for gas + lock ERC20  |
| **L2 sender**        | `msg.sender` (user, aliased if contract)   | `secondBridgeAddress` (L1 Asset Router)              |
| **L2 target**        | Caller-specified                           | Set by second bridge (L2 Asset Router)               |
| **L2 calldata**      | Caller-specified                           | Generated by second bridge (`finalizeDeposit`)       |
| **Deposit tracking** | None                                       | `depositHappened` stored for failed deposit recovery |

***

## Withdrawals: Moving Assets Back to Ethereum (L2 → L1)

Withdrawals require more time — typically **about 75 minutes** — because the bridge must generate a zero-knowledge proof to convince Ethereum that your L2 transaction actually happened. This proof-based approach is what gives the canonical bridge its security: no one can fabricate a withdrawal.

### The Three Phases

```mermaid
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
    actor User
    participant L2 as L2 Contracts
    participant Server as ADI Server
    participant L1 as L1 Executor
    participant NUL as Nullifier
    participant NTV as Token Vault

    Note over User,L2: Phase 1 — Initiate (~2 sec)
    User->>L2: withdraw()
    L2->>L2: burn tokens + sendToL1(message)

    Note over Server,L1: Phase 2 — Prove (~75 min)
    Server->>L1: commit batch
    Server->>L1: submit ZK proof
    Server->>L1: execute → store Merkle root

    Note over User,NTV: Phase 3 — Claim (~12 sec)
    User->>NUL: submit Merkle proof
    NUL->>NUL: verify + mark finalized
    NUL->>NTV: release tokens
    NTV-->>User: tokens returned on L1
```

### Phase 1 — Initiate the Withdrawal on L2

You submit a withdrawal transaction on ADI Chain:

* For **ADI**: call `withdraw()` on the L2 Base Token contract, sending your ADI as `msg.value`
* For **ERC20 tokens**: call `withdraw()` on the L2 Asset Router with the token's asset ID

Your tokens are burned on L2, and the **L1 Messenger** system contract records a withdrawal message. This message is cryptographically committed into a Merkle tree that will later be verified on Ethereum.

### Phase 2 — Batch Processing & Proving

This is where the zero-knowledge magic happens:

1. **Batching**: The ADI server groups multiple L2 blocks into a single batch
2. **Commitment**: The batch data (including the Merkle root of all L2→L1 messages) is posted to Ethereum
3. **Proving**: A zero-knowledge proof is generated, mathematically proving that every transaction in the batch was executed correctly
4. **Execution**: The proof is verified on Ethereum and the batch is finalized. The Merkle root is now stored on-chain, making your withdrawal provable

### Phase 3 — Claim on Ethereum

Once the batch is finalized, you can claim your tokens:

1. The portal fetches a **Merkle proof** for your specific withdrawal
2. You submit a claim transaction to the **L1 Nullifier** contract
3. The Nullifier verifies your Merkle proof against the on-chain root
4. If valid, it marks the withdrawal as finalized (preventing double-claims) and routes the tokens through the **L1 Asset Router** to the **Token Vault**, which releases your tokens

### Why Withdrawals Take Longer Than Deposits

|                  | Deposits (L1 → L2)                                   | Withdrawals (L2 → L1)                            |
| ---------------- | ---------------------------------------------------- | ------------------------------------------------ |
| **Trust model**  | L1 is the source of truth — L2 trusts it immediately | L2 must *prove* its state to L1                  |
| **Verification** | None needed — L1 events are canonical                | ZK proof + Merkle inclusion proof required       |
| **Time**         | \~15 seconds                                         | \~75 minutes                                     |
| **Why**          | Sequencer processes L1 events instantly              | ZK proof generation is computationally intensive |

***

## The Settlement Waterfall

Every withdrawal goes through a strict sequence of on-chain verification steps before tokens can be released. This "waterfall" ensures that no invalid state transition can ever result in tokens being unlocked on L1.

```mermaid
%%{init: {'theme': 'dark'}}%%
graph TD
    A[L2 Transaction] --> B[Block Inclusion]
    B --> C[Batch Accumulation]
    C --> D["Commit — post batch data"]
    D --> E["Prove — verify ZK proof"]
    E --> F["Execute — store Merkle root"]
    F --> G[Claim Available]
    G --> H[Tokens Released]

    style A fill:#4a90d9,stroke:#3a7bc8,color:#fff
    style B fill:#4a90d9,stroke:#3a7bc8,color:#fff
    style C fill:#f5a623,stroke:#d4911e,color:#000
    style D fill:#f5a623,stroke:#d4911e,color:#000
    style E fill:#f5a623,stroke:#d4911e,color:#000
    style F fill:#5bb974,stroke:#4a9f6a,color:#000
    style G fill:#5bb974,stroke:#4a9f6a,color:#000
    style H fill:#5bb974,stroke:#4a9f6a,color:#000
```

Each step in this waterfall is irreversible and verifiable on-chain:

* **Commitment** makes the batch data publicly available
* **Proving** cryptographically guarantees every transaction in the batch is valid
* **Execution** stores the Merkle root, enabling individual withdrawal proofs
* **Claiming** verifies a specific withdrawal against the stored root

### Batch Phases in Detail

| Phase       | Function                       | What Happens                                                                                                                                       | State After               | Avg. Gas  |
| ----------- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | --------- |
| **Commit**  | `commitBatchesSharedBridge()`  | Batch data posted to L1. L2 system logs validated (timestamps, priority ops hash, L2→L1 message root). DA proof checked.                           | `totalBatchesCommitted++` | \~136,000 |
| **Prove**   | `proveBatchesSharedBridge()`   | ZK proof verified on-chain. Proof covers state transition from previous batch commitment to current.                                               | `totalBatchesVerified++`  | \~494,000 |
| **Execute** | `executeBatchesSharedBridge()` | Batch finalized. L2→L1 logs root stored in `l2LogsRootHashes[batchNumber]`. Priority operations marked as processed. Withdrawals become claimable. | `totalBatchesExecuted++`  | \~117,000 |

Gas figures are measured against the L1 settlement layer; the same call pattern applies at any settlement boundary, so the magnitudes are broadly representative.

Withdrawals cannot be claimed until the batch completes all three phases. Attempting to claim before execution reverts with `LocalRootIsZero` (batch not yet executed) or returns a null proof (batch not yet committed).

***

For a deeper technical dive into the contract methods, token deployment, and security model, see [Canonical Bridge Technical Reference](/adi-network-components/canonical-bridge).


# Gas Abstraction & Account Abstraction

Supported account abstraction paths, paymaster options, and deployed entry points.

You can leverage either native EVM flows or overlay ERC-4337-style account abstraction.

We do not ship audited “default paymaster” contracts. You are free to integrate with external AA infrastructure or deploy and own your own paymaster / smart-account stack.

{% hint style="warning" %}
What is not supported currently:

* ERC-7702
  {% endhint %}

### Supported AA Paths

For both paths, we recommend auditing as a final step.

#### ERC-4337 via Pimlico & `permissionless.js`

Using [Pimlico](https://docs.pimlico.io/) + `permissionless.js` gives you a fully functional smart-account and paymaster stack on any EVM-compatible network, including zkSync OS.

We recommend using the battle-tested [Pimlico Bundler](https://docs.pimlico.io/references/bundler).

{% hint style="info" %}
**Note**: at time of writing, the Bundler does not work in safe mode due to missing `debug_trace` support on the RPC, therefore other bundlers will likely also not work if they require safe mode by default.
{% endhint %}

Smart account that works flawlessly on ADI Chain (`v3.1` is used for the official non-custodial ADI Wallet), MIT licensed: <https://github.com/zerodevapp/kernel>

#### Custom Paymaster / Smart-Account Deployment

You may deploy your own paymaster contracts and smart accounts. If you choose this path:

* Use a battle-tested base (e.g. a known ERC-4337 Paymaster template)
* Adapt to your tokenomics, gas-sponsorship rules, UI requirements

### Contracts

Entrypoints `V0.7` and `V0.8` are deployed on the network:

* `V0.7`: `0x0000000071727De22E5E9d8BAf0edAc6f37da032`
* `V0.8`: `0x4337084d9e255ff0702461cf8895ce9e3b5ff108`

### Further Reading

### Series of Articles from Alchemy on AA

* <https://www.alchemy.com/blog/account-abstraction>
* <https://www.alchemy.com/blog/account-abstraction-paymasters>
* <https://www.alchemy.com/blog/account-abstraction-wallet-creation>
* <https://www.alchemy.com/blog/account-abstraction-aggregate-signatures>

#### More Solidity-Focused Docs from OpenZeppelin

* On smart accounts: <https://docs.openzeppelin.com/community-contracts/account-modules>
* On paymasters: <https://docs.openzeppelin.com/community-contracts/paymasters>

#### Paymaster Examples

* Pimlico: <https://github.com/pimlicolabs/singleton-paymaster>
* Baseline example from `eth-infinitism` : <https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/core/BasePaymaster.sol>


# Safe on ADI Chain

Official Safe interfaces and transaction services for ADI Chain.

ADI Chain supports **Safe** (formerly Gnosis Safe), the leading smart account infrastructure for secure multi-signature and account abstraction setups.

#### Official Safe Interfaces & Services

* **Safe UI (Web Interface)**: <https://safe.adifoundation.ai/>\
  Use this to create, manage, and interact with Safe accounts on ADI Chain (Mainnet and Testnet).
* **Mainnet Transaction Service**: <https://transaction.safe.adifoundation.ai>\
  Handles off-chain transaction proposing, signing, and indexing for Mainnet.
* **Testnet Transaction Service**: <https://transaction-testnet.safe.adifoundation.ai>\
  Same as above, but for the ADI Chain Testnet.

#### How to Use

1. Go to the [Safe UI](https://safe.adifoundation.ai/) and connect your wallet or create a new Safe.
2. The interface automatically uses the correct Transaction Service for ADI Chain.
3. For developers integrating Safe SDKs:
   * Use the Transaction Service URLs above when initializing `SafeApiKit` with a custom `txServiceUrl`.

Example with Safe Core SDK (TypeScript):

```ts
import SafeApiKit from '@safe-global/api-kit';

const apiKit = new SafeApiKit({
  chainId: <ADI_CHAIN_ID>,           // your ADI mainnet or testnet chainId
  txServiceUrl: 'https://transaction.safe.adifoundation.ai'  // or the testnet URL
});
```

{% hint style="info" %}
Always verify you are on the official domains listed above to avoid phishing risks.
{% endhint %}


# Run Your Own Node

How to run an ADI external node and monitor sync.

The [ADI Stack Setup repository](https://github.com/ADI-Foundation-Labs/ADI-Stack-EN-Setup-script) provides a one-command setup for running an ADI (external) node (ZK rollup follower) plus two helper sidecars:

* `external_node`: replays L2 blocks from the canonical main node, maintains local state/RPC, and serves JSON-RPC + status/metrics.
* `proof-sync`: periodically syncs proving artifacts from the foundation-hosted Azure Blob storage into your local `<chain>_data/db/shared`.

{% hint style="info" %}
What it does **not** do:

* It does not generate new proofs or participate as a validator/sequencer. It locally replays and verifies the chain state and serves RPC for your own queries. Proofs are downloaded, not produced.
* It does not sequence or produce blocks. That is the sequencer's role.

What it **does** do for transactions:

* It accepts `eth_sendRawTransaction` calls and adds transactions to its local mempool.
* It **forwards transactions to the main node/sequencer** through `general_main_node_rpc_url`.
* If the sequencer rejects a transaction, the node removes it from its mempool and returns the error. You can submit transactions to any external node and they will reach the network.
  {% endhint %}

### Requirements

* Docker + Docker Compose.
* An archive-capable Ethereum L1 RPC endpoint (required to fetch historical state for genesis/upgrade discovery).
* Sufficient disk space for `<chain>_data` (RocksDB + synced proofs).

### Network Selection

* Default network: `mainnet`.
* Prefer CLI flags: `--testnet` / `--network testnet`. You may export `NETWORK=testnet` if you like, but the flag avoids lingering env state.
* Network-specific defaults (L2 RPC, replay host, proof storage URL, container prefix, data dir) are injected automatically; no need to swap compose files.

### Quickstart (mainnet)

#### Single Command

1. Start the stack:

   ```bash
   ./external-node.sh start --l1-rpc-url <your-archive-l1-rpc>
   ```

#### Stepwise

1. Export an archive L1 RPC URL:

   ```bash
   export GENERAL_L1_RPC_URL="<your-archive-l1-rpc>"
   ```
2. (Optional) Prefetch proofs to speed first start:

   ```bash
   ./external-node.sh download
   ```

   If you skip this, the `proof-sync` sidecar will pull proofs automatically after start.
3. Start the stack:

   ```bash
   ./external-node.sh start
   ```
4. Watch logs (optional):

   ```bash
   ./external-node.sh logs
   ```
5. Check RPC tip matches the canonical RPC (requires `ADI_RPC_URL`):

   ```bash
   curl -s -X POST -H 'Content-Type: application/json' \
     --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
     http://localhost:3050
   curl -s -X POST -H 'Content-Type: application/json' \
     --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
     $ADI_RPC_URL
   ```

   When both hex values match, you are synced.

### Quickstart (testnet)

Testnet may upgrade frequently; join [Discord](https://discord.gg/adi-foundation) for announcements.

#### Single Command

1. Start with testnet flag:

   ```bash
   ./external-node.sh --testnet start --l1-rpc-url <your-archive-l1-rpc>
   ```

   (Optional) If you prefer envs: `export NETWORK=testnet` then run the same commands without the flag.

#### Stepwise

1. Provide an archive L1 RPC URL:

   ```bash
   export GENERAL_L1_RPC_URL="<your-archive-l1-rpc>"
   ```
2. (Optional) Prefetch proofs (respects the selected network):

   ```bash
   ./external-node.sh --testnet download
   ```
3. Start:

   ```bash
   ./external-node.sh --testnet start
   ```

### Transaction forwarding

The Docker Compose setting `general_main_node_rpc_url` controls the transaction forwarding target. The default points to the ADI sequencer endpoint.

When this setting is unset, the node accepts transactions into its local mempool. It does not forward them, so they remain local-only.

### Monitoring

* JSON-RPC: `http://localhost:3050`
  * `eth_syncing` → should return `false` when caught up.
  * `eth_blockNumber` → compare with the ADI RPC.
* Metrics: `http://localhost:3312/metrics` (Prometheus text format). Search for `replay`, `last_block`, `state_block_range`, or `tree_last_block` to infer replay progress.
* Logs: look for `Replay block <n>` lines; once they stop and `eth_blockNumber` matches, the node is at tip.

### Data Locations

* `CHAIN_DATA_DIR` defaults per network (`./mainnet_data` or `./testnet_data`); overridable via env. Mapped to `/chain` in the containers.
* Proofs: `chain_data/db/shared` (synced by `proof-sync`).
* RocksDB state: `chain_data/db/node1/...`.

### Common Issues

* **Pruned L1 RPC**: startup panics with `... state at block is pruned ...` then use an archive L1 RPC.
* **Permissions**: ensure the host data directory is writable (the script `ensure_container_dir` sets permissive permissions).
* **Behind on replay**: allow time to catch up (monitor via `eth_blockNumber` and logs).

### What This Node Provides You

* A self-hosted ADI L2 RPC endpoint, backed by locally replayed and verified state.
* Locally stored proofs (synced from the foundation blob), not generated locally.
* No participation in sequencing, proving, or validator duties; it is for verification, data availability, and private querying.


# Mainnet Network Contracts

Mainnet core, bridge, and infrastructure contract addresses.

## L2 contracts

### Tokens

<table><thead><tr><th width="359.5859375">Contract</th><th>Address</th></tr></thead><tbody><tr><td>DDSC</td><td><code>0x1211f0cfe66739433c1330e21f4951B80E813479</code></td></tr><tr><td>USDC</td><td><code>0x9cb8142aEBBcdc60AF7c97Af897A67A8f3CA71C2</code></td></tr><tr><td>WADI</td><td><code>0xBa7A1DA3d3C9F9bE5A1A56d25014932C9166D09C</code></td></tr></tbody></table>

### Infrastructure

<table data-header-hidden><thead><tr><th width="360.13671875">Contract</th><th>Address</th></tr></thead><tbody><tr><td>Permit2</td><td><code>0x000000000022D473030F116dDEE9F6B43aC78BA3</code></td></tr><tr><td>EntryPoint v0.7</td><td><code>0x0000000071727De22E5E9d8BAf0edAc6f37da032</code></td></tr></tbody></table>

## L1 contracts (Ethereum mainnet)

### System infrastructure

#### Core Contracts

<table><thead><tr><th width="359.74609375">Component</th><th>Address</th></tr></thead><tbody><tr><td>Base Token (ERC20)</td><td><code>0x8b1484d57abbe239bb280661377363b03c89caea</code></td></tr><tr><td>Diamond Proxy</td><td><code>0x0583ef2b6416cb7b287406438b940e4d99680c5b</code></td></tr><tr><td>Dual Verifier</td><td><code>0x28e31e2b74bc38c6cd58cf282807fcba8c00c529</code></td></tr><tr><td>Multicall3</td><td><code>0xca11bde05977b3631167028862be2a173976ca11</code></td></tr><tr><td>Default Upgrade</td><td><code>0x5400558dc0f72da34260c946147deb8711cde491</code></td></tr><tr><td>Governance</td><td><code>0x7200b8b09950b27bdf785c4c8e592c4ab141e5f7</code></td></tr><tr><td>Chain Admin</td><td><code>0x2d6e82f1f8fba89a67cc8d742b12633db4732ca7</code></td></tr><tr><td>Proxy Admin</td><td><code>0x8140abb60c9afb5241d90af948cfa7644b2d3217</code></td></tr><tr><td>Validator Timelock</td><td><code>0x406f329645e323b1bd1c020a219e30e6daf4f899</code></td></tr><tr><td>Rollup DA Validator</td><td><code>0x45d594304087a359dc60a502f5c35d62dfecdda7</code></td></tr><tr><td>Avail DA Validator</td><td><code>0x2a7343629ba7125bc7822aa132fee77bc0516db4</code></td></tr><tr><td>Validium DA Validator</td><td><code>0x3dbbd9f8e558e36398c9b366435121642b5d7aae</code></td></tr></tbody></table>

#### Bridge Contracts

<table><thead><tr><th width="359.96875">Component</th><th>Address</th></tr></thead><tbody><tr><td>ERC20 Bridge L1</td><td><code>0xfa8b5ea9b8d36a72eb0ba66cc7abc83d9deec3b8</code></td></tr><tr><td>L1 Asset Router</td><td><code>0xf25227efad2046d19777a4ca540b5c016df7fe7a</code></td></tr><tr><td>L1 Nullifier</td><td><code>0x5e5a72077dfb354dfe61200b8f31fa491f9b9cea</code></td></tr></tbody></table>

#### System Infrastructure

<table><thead><tr><th width="360.0078125">Component</th><th>Address</th></tr></thead><tbody><tr><td>Bridgehub Proxy</td><td><code>0x7a38c18a229ef8a0ae7104ba272a46280f2d59cb</code></td></tr><tr><td>Message Root Proxy</td><td><code>0x783e8cb57366888f84d815fd53c3aee99b2d6d37</code></td></tr><tr><td>Chain Type Manager Proxy</td><td><code>0x08a1d2962fc29aa46e869a1e7561112cc1026efa</code></td></tr><tr><td>Native Token Vault</td><td><code>0x0a0f8912162ff83a036883dbada42eff647a3065</code></td></tr><tr><td>Bytecode Supplier</td><td><code>0x6e369e616dd5a5cb0cd410f541c784033de89042</code></td></tr><tr><td>Server Notifier</td><td><code>0xd477bd7f14f9a26ebd51827efb1d40a41f71b70c</code></td></tr></tbody></table>


# Verify smart contracts

Verify Solidity contracts on ADI Mainnet and ADI Network AB Testnet.

Use the working Blockscout explorers to publish and verify contract source code. Blockscout is open, so verification does not require an API key.

### Choose an explorer

| Network                                                                         | Verification status | Chain ID | API URL                                                |
| ------------------------------------------------------------------------------- | ------------------- | -------- | ------------------------------------------------------ |
| [Primary mainnet explorer](https://explorer.adifoundation.ai)                   | Broken              | `36900`  | —                                                      |
| [BLS mainnet explorer](https://explorer-bls.adifoundation.ai)                   | Working             | `36900`  | `https://explorer-bls.adifoundation.ai/api`            |
| [ADI Network AB Testnet explorer](https://explorer.ab.testnet.adifoundation.ai) | Working             | `99999`  | `https://explorer-api.ab.testnet.adifoundation.ai/api` |

Use the BLS explorer for ADI Mainnet. Use the testnet explorer for ADI Network AB Testnet. The primary mainnet explorer cannot verify contracts.

The BLS explorer runs Blockscout with the smart-contract-verifier and Sourcify backend.

### Verify in the explorer

1. Open the working explorer for your network.
2. Paste the deployed contract address into the search field.
3. Open the **Contract** tab.
4. Select **Verify & Publish**.
5. Select the compiler version and license.
6. Upload your source using one of these formats:
   * A single Solidity file.
   * Multiple source files.
   * Standard JSON input.

Use the exact compiler version, optimizer settings, and constructor arguments from deployment.

### Foundry

Configure the BLS explorer in `foundry.toml`. The empty `key` is intentional. Blockscout does not require an API key.

```toml
[etherscan]
adi = { key = "", url = "https://explorer-bls.adifoundation.ai/api", chain = 36900 }
```

Deploy and verify on ADI Mainnet:

```bash
forge create --verify --chain 36900 src/Contract.sol:Contract
```

Or explicitly select Blockscout:

```bash
forge create --verify --verifier blockscout \
  --verifier-url https://explorer-bls.adifoundation.ai/api \
  --chain 36900 src/Contract.sol:Contract
```

For ADI Network AB Testnet, use chain ID `99999` and its API endpoint:

```toml
[etherscan]
adiTestnet = { key = "", url = "https://explorer-api.ab.testnet.adifoundation.ai/api", chain = 99999 }
```

```bash
forge create --verify --verifier blockscout \
  --verifier-url https://explorer-api.ab.testnet.adifoundation.ai/api \
  --chain 99999 src/Contract.sol:Contract
```

### Hardhat

Configure `hardhat-verify` with custom ADI network definitions. Empty API keys are correct for Blockscout.

```javascript
require("@nomicfoundation/hardhat-verify");

module.exports = {
  networks: {
    adi: {
      url: "https://rpc.adifoundation.ai",
      chainId: 36900,
    },
    adiTestnet: {
      url: "https://rpc.ab.testnet.adifoundation.ai",
      chainId: 99999,
    },
  },
  etherscan: {
    apiKey: {
      adi: "",
      adiTestnet: "",
    },
    customChains: [
      {
        network: "adi",
        chainId: 36900,
        urls: {
          apiURL: "https://explorer-bls.adifoundation.ai/api",
          browserURL: "https://explorer-bls.adifoundation.ai",
        },
      },
      {
        network: "adiTestnet",
        chainId: 99999,
        urls: {
          apiURL: "https://explorer-api.ab.testnet.adifoundation.ai/api",
          browserURL: "https://explorer.ab.testnet.adifoundation.ai",
        },
      },
    ],
  },
};
```

Then run:

```bash
npx hardhat verify --network adi <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGUMENTS>
```

Verify on ADI Network AB Testnet:

```bash
npx hardhat verify --network adiTestnet <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGUMENTS>
```

The testnet network name is `ADI Network AB Testnet`. Its chain ID is `99999`.

You can also use the `blockscout-hardhat-verify` plugin. It supports Blockscout verification directly.

### Sourcify

Sourcify provides an additional verification backend for the BLS mainnet explorer. You can submit matching contract metadata and source files through Sourcify. The explorer can then display the verified result.

### Network endpoints

| Network                | RPC URL                                   | Explorer API URL                                       |
| ---------------------- | ----------------------------------------- | ------------------------------------------------------ |
| ADI Mainnet            | `https://rpc.adifoundation.ai`            | `https://explorer-bls.adifoundation.ai/api`            |
| ADI Network AB Testnet | `https://rpc.ab.testnet.adifoundation.ai` | `https://explorer-api.ab.testnet.adifoundation.ai/api` |


# Signing Malicious Transactions

How to spot malicious transactions and reduce wallet signing risk.

### Why You Should Care About Spoof / Malicious Transactions

* Even though the blockchain is immutable and secure by design, the *interface* — what you see when you sign a transaction — can be manipulated. You may think you are approving one thing (e.g. a swap, or an NFT mint) when in fact you are signing a malicious or a phishing transaction. This is often called [“UI spoofing” or “phishing contract” attacks](https://www.cyfrin.io/blog/secure-dapps-against-ui-spoofing-part-1-decoding-transactions?utm_source=chatgpt.com).
* Recent incidents are rising. For example, signature-based exploits have led to [significant losses in 2025 via phishing flows](https://www.bitget.com/news/detail/12560604954396?utm_source=chatgpt.com).
* Some scams do not rely on private key theft; they trick you into approving a contract that later drains tokens. Once you sign, [blockchain rules make the execution irreversible](https://www.ledger.com/academy/somethings-phishy-how-to-keep-your-crypto-safe-against-scams?utm_source=chatgpt.com).

### Common Attacks

| Attack Type                                                  | Description / Risk                                                                                                                                                                                                                                                                                                               |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **UI Spoofing / Phishing Contracts**                         | A dApp or malicious site shows a benign transaction interface but encodes malicious calldata (e.g. unlimited token approvals, fund draining). You see something benign, signs, and get exploited. ([cyfrin.io](https://www.cyfrin.io/blog/secure-dapps-against-ui-spoofing-part-1-decoding-transactions?utm_source=chatgpt.com)) |
| **Address Poisoning / Spoofed Addresses**                    | A scammer sends tiny amounts from addresses that mimic legitimate ones. You copy the wrong address, sending funds to attacker. ([Ledger](https://www.ledger.com/academy/topics/security/what-are-address-poisoning-attacks-in-crypto-and-how-to-avoid-them?utm_source=chatgpt.com))                                              |
| **Unrestricted/Unlimited Approvals / Blind Signatures**      | Granting unlimited permissions to unknown or untrusted contracts gives them ongoing access to your funds or tokens. ([netcoins.com](https://www.netcoins.com/blog/ethereum-wallet-security-best-practices?utm_source=chatgpt.com))                                                                                               |
| **Malicious or Unaudited Smart Contracts / Unaudited dApps** | Interacting with dApps without a public audit or community reputation increases the risk of hidden malicious behavior. ([startupdefense.io](https://www.startupdefense.io/cyberattacks/dapp-phishing?utm_source=chatgpt.com))                                                                                                    |

### Best Practices

Here’s what you should do to protect yourself.

* **Always review transaction details before signing**: Check recipient address, amounts, allowances, and whether the transaction matches your intent. Use whitelists for addresses in your wallets as much as possible. If transaction text looks unreadable or unclear, do not sign. ([BlockSec](https://blocksecteam.medium.com/demystifying-phishing-contracts-on-ethereum-and-how-to-avoid-them-cf1a4218f6fc?utm_source=chatgpt.com))
* **Use hardware wallets (cold wallets) if possible**. This ensures the private key never touches the internet or a potentially compromised device. ([Ledger](https://www.ledger.com/academy/somethings-phishy-how-to-keep-your-crypto-safe-against-scams?utm_source=chatgpt.com))
* **Prefer minimal permissions / scope-based approvals**: Avoid granting unlimited permission; only approve what is strictly required for a given operation. Periodically review and revoke unnecessary approvals. ([startupdefense.io](https://www.startupdefense.io/cyberattacks/dapp-phishing?utm_source=chatgpt.com))
* **Segregate assets across multiple wallets**: Keep long-term holdings in a “cold / safe” wallet; use a separate “interaction wallet” for risky or experimental dApps. ([Ledger](https://www.ledger.com/academy/somethings-phishy-how-to-keep-your-crypto-safe-against-scams?utm_source=chatgpt.com))
* **Avoid suspicious links, bookmark trusted sites, verify URLs** — don’t use search results casually; phishing often comes via fake sites or typosquatted domains. ([Ledger](https://www.ledger.com/academy/somethings-phishy-how-to-keep-your-crypto-safe-against-scams?utm_source=chatgpt.com))
* **Limit exposure: avoid interacting with unknown or un-audited dApps**. Prefer dApps with audit reports, good reputation, and visible community feedback. ([startupdefense.io](https://www.startupdefense.io/cyberattacks/dapp-phishing?utm_source=chatgpt.com))
* **Keep your Wallets and Extensions Updated**: Wallet app, browser extensions, OS — patching helps prevent exploitation of known vulnerabilities. ([onekey.so](https://onekey.so/blog/ecosystem/enable-blind-signing-why-when-and-how-to-stay-safe/?utm_source=chatgpt.com))

### Further Reading

* What Are Address Poisoning Attacks in Crypto and How to Avoid Them by Ledger ([Blog](https://www.ledger.com/academy/topics/security/what-are-address-poisoning-attacks-in-crypto-and-how-to-avoid-them?utm_source=chatgpt.com))
* Demystifying Phishing Contracts on Ethereum and How to Avoid Them by BlockSec ([Medium](https://blocksecteam.medium.com/demystifying-phishing-contracts-on-ethereum-and-how-to-avoid-them-cf1a4218f6fc?utm_source=chatgpt.com))
* Secure dApps Against UI Spoofing (Part 1): Decoding Transactions by Cyfrin. ([cyfrin.io](https://www.cyfrin.io/blog/secure-dapps-against-ui-spoofing-part-1-decoding-transactions?utm_source=chatgpt.com))
* How to Spot Malicious dApps by Trust Wallet. ([Trust Wallet](https://trustwallet.com/blog/security/how-to-spot-malicious-dapps?utm_source=chatgpt.com))
* How to Secure My Crypto Wallet: Essential Tips by Tangem Wallet blog ([Tangem Wallet](https://tangem.com/en/blog/post/how-to-secure-my-crypto-wallet/?utm_source=chatgpt.com))
* Crypto Phishing Scams And How To Avoid Them by Ledger’s “Something’s Phishy” guide ([Ledger](https://www.ledger.com/academy/somethings-phishy-how-to-keep-your-crypto-safe-against-scams?utm_source=chatgpt.com))


# Protect Yourself From Imposters

How to verify official channels and avoid phishing, scam sites, and fake support.

Fraudsters target new ecosystems with cloned sites, fake support desks, and social accounts that look real. The guidelines below help you stay safe when interacting with the network.

### Verify Official Domains

* Always type official URLs into your browser.
* Bookmark the correct domain once verified.
* Avoid links from DMs, comments, or forwarded messages.
* Check for subtle spelling changes in domain names.
* Confirm certificates by clicking the browser lock icon.

### Confirm Official Social Channels

* Use links published only on the main website and GitHub.
* Treat any handle that DMs you first as a scam.
* Compare follower counts, post history, and join dates.
* Ignore accounts that promise airdrops, fast support, or guaranteed profits.

### Never Share Sensitive Data

* No admin will ever ask for seed phrases, private keys, or wallet screenshots.
* Do not paste keys or signer outputs into chat rooms.
* If a support post asks you to “verify your wallet,” assume it is fake.

### Validate Announcements

* Cross check any upgrade, airdrop, or migration announcement with the main website and GitHub.
* Real updates are published across all official channels at the same time.
* Fake announcements usually include urgency, countdowns, or pressure to connect your wallet.

### Protect Wallet Interactions

* Inspect every transaction in your wallet before signing.
* If the origin domain looks off, reject the request.
* Use hardware wallets for material value.
* Revoke old approvals periodically using reknown popular tools.

### Use Trusted Downloads Only

* Install browser extensions, wallets, and tooling from verified vendor pages.
* Never install packages sent over private messages on Telegram, Discord, or X.

### Secure Your Communication Path

* Treat Discord DMs as compromised by default.
* Disable DM permissions in community servers.
* Use official forums or GitHub Issues for technical support when possible.

### Call Out Suspicious Activity

* Share posts about unusual behavior in Discord and socials.
* Contact the channel managers or admins.

### Quick Checklist

* Verify domain
* Verify social handle
* Reject unsolicited DMs
* Never share keys
* Cross check announcements
* Inspect every signature
* Use trusted downloads
* Report scams

Stay strict with verification. Scams rely on speed and distraction. Slow down and confirm each surface before you interact.

***

## Examples

Below are just a few real world examples, imposters are creative and will use more channels than the ones below given the chance:

### Scam DMs

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2FzuAlL6yzhalYXxGVgdGX%2Fimage.png?alt=media&amp;token=11f1b1a3-7c91-4ad1-9233-684847428cb3" alt=""><figcaption></figcaption></figure>

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2Fzwe78ux21lL1VDhwDnRE%2Fimage.png?alt=media&amp;token=c3e5bdc9-8fe2-4ead-aa37-3c6a53683081" alt=""><figcaption></figcaption></figure>

**Read More:**\
<https://me-en.kaspersky.com/blog/twitter-fake-crypto-scheme/20549>

***

### Scam Exchanges

#### CEX

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2FrpRsMGtoSIMuMPjAwRrg%2Fimage.png?alt=media&amp;token=01fe3fe7-659d-4d44-ac05-8dd010d48b3c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2FOhm27oIrNMSiYvS02pMl%2Fimage.png?alt=media&amp;token=124468c6-4bef-4692-ad88-a0bc1e71d829" alt=""><figcaption></figcaption></figure>

**Read More:**\
<https://techforing.com/resources/articles/list-of-fake-crypto-exchanges>\
<https://bingx.com/en/learn/article/top-crypto-phishing-scams-to-know-and-how-to-spot-them>

#### DEX

Decentralized exchanges can be hacked, or even worse, be developed to steal your funds.

To minimise risk:

* Verify the exchange has been audited by multiple independent auditors.
* Verify contracts are verified on block explorers.
* Verify the team behind the exchange has been identified.
* Verify withdrawals work as reliably as deposits using small amounts.

**Read More:**\
<https://www.binance.com/ar/blog/security/7315134930728698372>\
<https://www.mexc.com/en-NG/news/hypervaultfi-goes-for-suspected-rug-pull-takes-3-6m-from-hyperliquid-users-and-nukes-x-account/110461>

***

### Scam Posts

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2FutzSmKe2MnhPGRngYJiW%2Fimage.png?alt=media&amp;token=70e2c1de-bbe1-4276-bbdd-fd8c58ccf349" alt=""><figcaption></figcaption></figure>

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2F5njiVyxVI0lQgHyaugx4%2Fimage.png?alt=media&amp;token=6c3ea476-88b3-40fe-82cb-53317392a6b3" alt=""><figcaption></figcaption></figure>

* Popular accounts are impersonated or copied, including company accounts, with slightly different names.
* Sometimes social and company accounts get hacked, then post about a new coin that is a scam.

**Read More:**\
<https://www.certik.com/resources/blog/social-media-crypto-scams>


# What is MEV? How Can You Avoid It?

What MEV is, how it affects users, and how ADI reduces exposure.

When you use the blockchain, you might hear the term **MEV** — it stands for *Maximal Extractable Value*. In simple terms: MEV is the additional value that can be captured by a block-producer (or someone who can influence block ordering) beyond the standard rewards and transaction fees.

### Why MEV matters

* On a blockchain like ADI or Ethereum, every transaction goes into a pool (mempool, where pending transactions are placed) where it waits before being included in a block.
* Whoever builds the next block has some discretion: they can **include**, **exclude**, or **re-order** transactions.
* That discretion creates an opportunity: by changing the order or by inserting their own transactions, someone can capture extra profit.
* For you, MEV means your transaction outcome could be affected (for example, worse price, higher slippage).

### What MEV Means for You

* Your transaction might **face higher cost** (more slippage, worse execution) because of bots or searchers extracting value ahead of you.
* You might **experience delay or surprise ordering**: your transaction may not execute in the order you expected.

## How Can You Avoid MEV?

### ADI Keeps Your Transactions Private

ADI uses a private pool. This means your pending transaction is not visible to public searchers. This blocks classic mempool sniping and most sandwich setups. It does not prevent economic MEV that comes from protocol rules, liquidity design, or block construction choices.

#### What You Should Do on Ethereum or Other Chains

ADI's private mempool protection works only when your transaction is executed on ADI. If you bridge assets to Ethereum or use other chains, your transactions enter public mempools or non private L2 sequencing pipelines. We recommend using the ADI wallet (to be released soon) for ensuring you are protected when interacting with the wider Ethereum ecosystem.

**ADI Wallet (Coming Soon)**

The ADI wallet is the official wallet to use with the ADI ecosystem of projects. It provides:

* **Within ADI**: native staking for ADI and the Dirham stablecoin
* **Broader Ethereum Ecosystem**: built-in MEV protection through OneInch transaction routing for swaps included in the wallet

### Further Reading

#### How MEV works

1. A user submits a transaction → it lands in the mempool.
2. A “searcher” (algorithm or bot) watches the mempool and identifies profitable opportunities (e.g., two DEXes have a price mismatch).
3. The searcher builds a *bundle* (set or group) of transactions or chooses a transaction ordering. They may pay higher fees to ensure inclusion.
4. The block-builder (and ultimately the validator) selects which transactions (and in what order) to include.
5. Value is extracted beyond normal fees: e.g., via arbitrage, sandwiching, or liquidation trades.
   * This extra profit is what we call MEV.
   * Some of that profit is passed to the validator as higher gas/tip; some is captured by searchers.

#### Common MEV strategies

* **Arbitrage**: Exploit a price difference between two markets or liquidity pools. Example: Buy token X on DEX A at a lower price, sell on DEX B at higher price, all within one block.
* **Sandwich attacks**: A searcher sees your large trade pending. They place a buy *before* your trade (paying high fee) to push the price up, then your trade executes at worse price, then they sell right after you — capturing profit at your expense.
* **Front-running / Back-running**: In front-running you get someone else's transaction’s effect ahead of them; in back-running you follow it to capture the after-effect.
* **Liquidation capture**: On lending protocols, when someone’s collateral becomes under-collateralised, an attacker can jump in, liquidate and profit.

**Further Resources**

* [A16zCrypto’s “MEV explained”](https://a16zcrypto.com/posts/article/mev-explained/)
* [Chainlink Education Hub’s “What is MEV?” deep dive](https://chain.link/education-hub/maximal-extractable-value-mev)
* [Arkham 2025 “MEV: A 2025 guide to Maximal Extractable Value in crypto”](https://info.arkm.com/research/beginners-guide-to-mev)


# Development Engine

Overview of the modular zkEVM framework that powers ADI Chain.

A modular zkEVM framework, based on the zkSync stack, powers ADI’s scalability, security, and Ethereum equivalence.

* Sequencer. Orders and executes transactions, runs in a high-availability setup with hot failover, and uses restricted networking to mitigate spam and denial attacks.
* Prover. Based on Airbender and using STARK to SNARK compression. It is GPU-accelerated and optimized to deliver sub-minute proofs, with the capability of sub-second generation for smaller batches.
* L1 Verifier & Bridge. Ethereum contracts verify submitted batches and update the state root. The Outbox/Inbox mechanism supports secure message execution, enabling near-instant withdrawals after proof acceptance.


# How ADI Chain is Validated

How ADI Chain Uses Validity Proofs and Ethereum Settlement Instead of a Validator Set

ADI Chain is a zkRollup that settles to Ethereum. It does not use an ADI validator set to approve blocks.

A sequencer orders transactions and produces L2 blocks. A prover then proves each batch executed according to ADI's rules. Ethereum accepts the resulting state only after its verifier contract accepts that proof.

### Votes vs. Cryptographic Receipts

Traditional L1 networks use a consensus set. Ethereum Proof of Stake, BNB PoSA, and Tron DPoS use different membership and voting models.

In each model, participating validators propose or vote on blocks. The network accepts a block when its consensus rules are met. Its safety relies on the relevant honesty threshold holding.

ADI has a different settlement model:

1. The sequencer executes transactions and seals L2 blocks.
2. The prover generates a zero-knowledge validity proof for a batch.
3. Ethereum verifies that proof through an L1 verifier contract.

The proof is a cryptographic receipt for the computation. It attests that the batch transforms the prior state into the claimed new state.

If the proof is invalid, the verifier contract rejects the batch. No ADI validator vote can override that result.

{% hint style="info" %}
Ethereum validators still secure Ethereum's own consensus and transaction inclusion. They do not vote on whether an ADI batch is valid. The verifier contract makes that decision from the proof.
{% endhint %}

### How ADI proves a batch

ADI runs the same execution logic in the sequencer and proving environments. The sequencer uses the normal execution target. The prover runs the deterministic RISC-V target.

[Airbender](/adi-network-components/airbender) produces a STARK proof for that execution. A FFLONK SNARK wrapper compresses the final proof for efficient Ethereum verification.

The L1 batch lifecycle is:

```mermaid
flowchart LR
    A["Pending\nsequencer seals block"] --> B["Committed\nbatch data reaches Ethereum"]
    B --> C["Executed\nproof verified on Ethereum"]
```

* **Pending** means the sequencer sealed the block.
* **Committed** means the batch is committed on Ethereum.
* **Executed** means Ethereum verified the proof. This is fully final.

See [Finality](/references/json-rpc-api/finality) for the corresponding RPC stages and block tags.

### Independent Verification is Open to Everyone

Proof verification is not restricted to an operator role. Anyone can inspect the public L2 data and the proof submitted to Ethereum.

Airbender's proving workflow is reproducible. An exchange, institution, auditor, or community operator can independently replay the public execution data, regenerate the proof, and verify the submitted result.

This needs no stake, validator seat, or permissioned membership. A mismatch between the published data, proof, and claimed state transition is detectable.

This differs from the *acceptance mechanism* on a validator-based L1. An L1 observer can run a node and validate its rules independently. Yet canonical block acceptance still depends on the network's consensus threshold. ADI batch acceptance at settlement depends on the verifier contract accepting a proof.

### A Web2 Mental Model

Think of validator consensus as an approval workflow. Authorized participants vote, and the system trusts the required threshold.

Think of a validity proof as a cryptographic receipt. Ethereum checks the receipt against the execution rules. It accepts the batch only when the check passes.

This avoids optimistic-rollup dispute windows. ADI does not wait for someone to challenge a batch. Finality follows proof generation, Ethereum inclusion, and on-chain verification.

Actual timing depends on batch formation, proof generation, and Ethereum processing. It is not a fixed validator-vote settlement period.

### What each component does

* **Sequencer:** orders transactions and produces L2 blocks. It provides fast pending confirmations.
* **Prover:** creates the Airbender STARK and FFLONK proof for a batch.
* **Ethereum verifier:** rejects invalid proofs and accepts valid state transitions.
* **Independent observers:** reproduce and check the public computation without joining a validator set.

The sequencer affects liveness and transaction ordering. It cannot make Ethereum accept an invalid state transition.

For architecture details, see [Appendix A — Technical Deep Dive](/appendix/appendix-a-technical-deep-dive), [Airbender](/adi-network-components/airbender), and [Finality](/references/json-rpc-api/finality).


# zkSync: foundation of the stack

How zkSync underpins ADI Chain's scalability, security, and EVM equivalence.

zkSync is a Layer‑2 blockchain that uses zero‑knowledge proofs to make transactions faster and cheaper, while retaining Ethereum’s security. Its open, modular framework powers ADI’s scalability, EVM equivalence, and compliance‑oriented features.

<br>


# Onramping Institutions and Governments

How ADI Chain helps institutions and governments onboard users and connect services.

ADI Chain provides the infrastructure for institutions to onboard their users and clients and interconnect services across sectors, such as payments, identity, registries, healthcare, energy, and logistics. By using blockchain as a neutral settlement and data layer, institutions can link existing systems, expand across borders, and enforce policy‑compliant operations via L3 networks.


# Shared Incentives

How ADI Chain aligns incentives across institutions, developers, and users.

ADI Chain aligns incentives across institutions, developers, and end users. By batching transactions and compressing proofs, the network lowers the total cost of operations — reducing fees for institutions while passing savings on to citizens and businesses. Connectivity is built in: every participant gains access not only to services within the ADI ecosystem but also to the wider universe of DeFi applications and digital services, creating a seamless bridge between traditional and next-generation markets. And with Layer-3 customization, policymakers and enterprises retain control where they need it most — tailoring rules for specific sectors or jurisdictions while still remaining interoperable with the broader network.


# Opportunities for Key Stakeholders

How builders, institutions, governments, and users benefit from ADI Chain.

**For Builders,** ADI Chain offers a low-cost, scalable environment that is fully compatible with Ethereum standards. Developers can use familiar tools such as Hardhat and Foundry to deploy applications quickly, while zk-based proofs ensure fast finality with L1-grade security.

**For Institutions,** ADI Chain provides compliance-specialized Layer-3 customization. This allows banks, fintechs, and enterprises to build optimized rails for payments, digital identity, tokenization of real-world assets, and secure data registries — all while meeting sector- or jurisdiction-specific requirements.

**For Governments,** ADI Chain delivers the frameworks required to establish digital asset infrastructure, national registries, and fully auditable program delivery. This creates new ways to drive efficiency, accountability, and transparency in public services.

**For Users,** ADI Chain enables retail access to compliant, secure, and interoperable financial and data services. Citizens benefit from lower costs, faster access, and inclusion in the digital economy without compromising trust or security.


# ADI Chain Infrastructure & Ecosystem Features

Key infrastructure, stablecoin, interoperability, and L3 features across the ADI ecosystem.

{% hint style="info" %}
ADI Chain supports a growing ecosystem of compliant institutional and government applications. Browse the full [ADI Ecosystem Map](https://ecosystem.adi.foundation/) to see deployed projects, partners, and interconnected L3 networks.
{% endhint %}

While $ADI powers the network, the broader ADI Chain infrastructure introduces features that enhance utility and adoption:

#### 1. Stablecoin Rail

ADI Chain supports DDSC, which is listed in [Mainnet Network Contracts](/how-to-start/network-contracts#tokens) at `0x1211f0cfe66739433c1330e21f4951B80E813479`.

{% hint style="warning" %}
DDSC's issuer identity, regulatory status, reserve terms, and transaction volume require sign-off.
{% endhint %}

#### 2. EVM Compatibility & Interoperability

ADI Chain supports ERC-20 and ERC-721 standards, enabling seamless bridging and composability across Ethereum and other L2/L3 networks.

#### 3. Enterprise & Government L3 Enablement

ADI Chain is designed to support scalable, compliant L3s for use cases such as:

1. Digital identity
2. Registries
3. Healthcare
4. Payments
5. Logistics

These domains benefit from the security and performance of ADI Chain while leveraging $ADI for gas and settlement.

#### 4. Trusted AI Agent Infrastructure

ADI Chain supports trusted, verifiable AI agents through the ERC-8004 registry layer for identity, reputation, and validation.

For the full architecture, deployed contracts, and institutional design considerations, see [AI Agents Infrastructure on ADI Chain](/core-components/ai-agent-infrastructure).


# Tokenized Deposits on ADI Chain

Architecture considerations for banks issuing tokenized deposits on ADI Chain.

A stablecoin is a claim on its issuer's reserve. A tokenized deposit is a claim on the issuing bank's balance sheet.

Both can use the same settlement rail. Their liability, ledger integration, and regulatory treatment remain distinct.

{% hint style="warning" %}
Regulatory classification, licensing requirements, and accounting treatment require bank and regulatory sign-off for each deployment.
{% endhint %}

### What a tokenized deposit is

A tokenized deposit represents a deposit liability of the issuing bank. The bank maintains the relationship between the token and its deposit ledger.

This model can keep client funds as bank deposits until exchange. It can also support settlement outside banking hours without prefunding a separate settlement asset.

The token contract should reflect the bank's product terms, eligibility rules, and redemption process. Legal, accounting, and regulatory treatment remain the bank's responsibility.

### The control model

The bank owns its token contract and its administrative permissions. It controls issuance, redemption, holder eligibility, and transfer rules.

ADI network operators run network infrastructure. They perform upgrades, batching, and proving. They cannot mint, freeze, or move a bank's token without authority granted by the bank's contract.

The bank depends on ADI for network availability. It retains control over its own liability and token permissions.

[How ADI Chain is Validated](/core-components/how-adi-chain-is-validated) explains the sequencer, proving, and Ethereum settlement roles.

### Atomic exchange against DDSC

A deposit token and DDSC can exchange on the same execution layer. Contract logic executes both legs together or reverts both legs.

Eligibility checks run at the moment of exchange. The transaction completes only when each leg satisfies its rules.

For example, a corporate payment at 21:00 on Friday can exchange a bank-issued deposit token against DDSC. Both legs settle atomically on ADI Chain, rather than entering a bank processing queue for Monday.

DDSC is listed in [Mainnet Network Contracts](/how-to-start/network-contracts#tokens) at `0x1211f0cfe66739433c1330e21f4951B80E813479`.

{% hint style="warning" %}
The availability and legal settlement effect of out-of-hours payment workflows require product, operational, and regulatory sign-off.
{% endhint %}

### Finality-stage selection

ADI confirmations arrive in stages. A bank's ledger policy must define which stage marks a payment as settlement-final.

| Stage     | Block tag   | Use in a bank workflow                        |
| --------- | ----------- | --------------------------------------------- |
| Pending   | `latest`    | Near-instant inclusion for client experience. |
| Committed | `safe`      | The batch is committed to Ethereum.           |
| Executed  | `finalized` | L1 execution provides irreversible finality.  |

[Finality](/references/json-rpc-api/finality) describes the `pending`, `committed`, and `executed` stages and their matching block tags.

{% hint style="warning" %}
Choose ledger posting, reconciliation, and exception handling rules with the bank's risk, operations, and compliance teams.
{% endhint %}

### L2 vs dedicated L3

Place a deposit token on the shared L2 when counterparties and DDSC already transact there. This keeps the exchange on one execution layer.

Use a dedicated L3 when operating data must remain isolated from other participants. Examples include bilateral terms, client position data, and internal liquidity workflows. This adds a settlement hop between the L3 and L2.

ADI supports three L3 infrastructure models: ADI-managed, client-operated, and hybrid. Chain contract ownership can transfer to the client.

[ADI Network components](/adi-network-components/overview) introduces the execution layer. [L3 Chains](/adi-network-components/overview-1#infrastructure-models) details the operating models and ownership options.

{% hint style="warning" %}
Confirm data visibility, privacy controls, and cross-layer settlement requirements for the selected topology.
{% endhint %}

### Fee sponsorship

Counterparties do not need to hold ADI directly. The bank can fund transaction fees through a treasury balance or sponsor fees through an ERC-4337 paymaster.

ADI does not provide an audited default paymaster. A bank can integrate external account-abstraction infrastructure or deploy and own its own paymaster stack.

[Gas Abstraction & Account Abstraction](/how-to-start/gas-abstraction-and-account-abstraction) covers supported account-abstraction paths and deployed entry points.


# AI Agent Infrastructure

Trusted, verifiable AI agents and ERC-8004 registries for institutional deployments

ADI Chain provides a production-ready infrastructure layer for trusted, verifiable AI agents operating across organizational boundaries. It combines private transaction flow, jurisdiction-aware L3s, and onchain registries for agent identity, reputation, and validation.

{% hint style="info" %}
Best for institutional decision-makers, enterprise architects, government technology teams, and compliance officers evaluating blockchain-based agent infrastructure.
{% endhint %}

## Getting started

#### Third-party testnet integration: Pearl Digital P3

Pearl Digital operates the Pearl Path Protocol (P3), a third-party test environment available on ADI Testnet (`99999`). P3 provides permissioned agent onboarding, Know Your Agent (KYA) controls, delegation credentials, agent discovery, reputation integration, rolling transfer limits, x402 payments, and test payment assets.

Developers can use the following Pearl-operated resources:

* [P3 Agentic Registry API](https://testagentic.pearldigital.com/docs)
* [Supported chains and deployed contract addresses](https://testagentic.pearldigital.com/api/v1/chains)
* [Pearl testnet faucet and onboarding service](https://faucet.pearldigital.com/)

{% hint style="warning" %}
Pearl P3 is a third-party testnet integration operated by Pearl Digital. Its contracts are separate from ADI Chain’s canonical ERC-8004 Identity, Reputation, and Validation registries. Availability, contract addresses, test assets, and access requirements may change while the service remains in public testnet.
{% endhint %}

**Running an x402 facilitator**

An x402 facilitator verifies signed payment authorizations and submits settlement transactions onchain. Facilitators are optional and independently operated. ADI Foundation does not operate a public facilitator.

Hosted x402 facilitators may not support ADI Testnet. Self-host a facilitator with the official [x402 reference implementations](https://github.com/x402-foundation/x402/tree/main/examples) when you need ADI support. x402 supports arbitrary EIP-155 networks; see the [network support](https://docs.x402.org/core-concepts/network-and-token-support) and [facilitator](https://docs.x402.org/core-concepts/facilitator) documentation.

For ADI Testnet, configure:

| Setting            | Value                                           |
| ------------------ | ----------------------------------------------- |
| Network identifier | `eip155:99999`                                  |
| RPC URL            | `https://rpc.ab.testnet.adifoundation.ai/`      |
| Gas token          | `ADI`                                           |
| Explorer           | `https://explorer.ab.testnet.adifoundation.ai/` |

Use a dedicated settlement signer with sufficient ADI for transaction fees. Start with assets that support EIP-3009 `transferWithAuthorization`, such as Pearl’s PRLUSD and FlashD test tokens. The x402 Permit2 settlement proxy contracts are not currently deployed on ADI Testnet. Do not assume arbitrary ERC-20 payments work through Permit2.

#### For institutions

1. Read the [ERC-8004 specification](https://eips.ethereum.org/EIPS/eip-8004).
2. Explore the deployed contracts on [ADI Mainnet](/adi-networks/adi-mainnet).
3. Check current availability for [ADI Testnet](/adi-networks/adi-testnet).
4. Evaluate whether agents should run on ADI L2 or a dedicated [L3 chain](/adi-network-components/overview-1).
5. Use the [ADI DLT Framework](/adi-dlt-framework) to assess governance and operational controls.

#### For developers

* Register an agent with `register(agentURI)` on the Identity Registry
* Post feedback with `giveFeedback(agentId, value, valueDecimals, ...)` on the Reputation Registry
* Request validation with `validationRequest(validatorAddress, agentId, requestURI, requestHash)` on the Validation Registry
* Query summaries with `getSummary(agentId, clientAddresses, tag1, tag2)` on the Reputation Registry

All common EVM tooling works on ADI Chain. Start with [Quickstart](/how-to-start/quickstart).


# Why Agents

Why institutional AI agents need verifiable identity, trust, and compliance controls.

AI agents are moving beyond single-organization workflows. They now act across institutions, jurisdictions, and compliance domains.

An agent may:

* Negotiate and execute settlement across banks
* Verify compliance data across supply chains
* Manage tokenized assets for qualified investors
* Coordinate energy balancing across utilities

Protocols such as MCP and A2A handle discovery and messaging. They do not provide the trust and control model institutions need.

| Missing capability                    | Why it matters                                                             |
| ------------------------------------- | -------------------------------------------------------------------------- |
| **Cryptographic identity**            | Agent actions must be attributable to a verifiable onchain identity.       |
| **Reputation without centralization** | Trust should come from verifiable history, not platform-controlled scores. |
| **Compliance at the agent level**     | Regulatory controls must bind the agent, not just the application.         |
| **Cross-organizational audit**        | Regulators need durable evidence years after execution.                    |
| **Jurisdiction-aware operation**      | Agent permissions must adapt to the rules of each operating environment.   |

ADI Chain and ERC-8004 fill these gaps.


# Registration & Agent Lifecycle

Register agents, build reputation, validate actions, and settle on ADI Chain.

Public-mempool chains expose pending transactions and agent activity to bots and searchers. That model does not fit regulated workflows.

ADI Chain gives institutions stronger execution privacy, cryptographic attribution, and an auditable trust layer. The foundation is **ERC-8004**, a lightweight protocol for agent identity, reputation, and validation deployed on ADI Chain and available across the ADI L3 ecosystem.

#### Agent lifecycle

{% stepper %}
{% step %}

### Register

An institution creates an agent identity on the Identity Registry on ADI L2 or on its own ADI-connected L3. The registration file advertises endpoints, supported trust models, and operational scope.
{% endstep %}

{% step %}

### Build reputation

As the agent completes tasks, counterparties post feedback to the Reputation Registry. Over time the agent builds a verifiable onchain history.
{% endstep %}

{% step %}

### Request validation

For higher-value actions, the agent requests validation through the Validation Registry. A validator records the result and optional evidence.
{% endstep %}

{% step %}

### Settle

The action settles on ADI Chain with validity proofs and finalizes on Ethereum. The record remains auditable long after execution.
{% endstep %}

{% step %}

### Evolve

Ownership, endpoints, and trust settings can change over time. Those changes remain onchain and auditable.
{% endstep %}
{% endstepper %}

### ERC-8004 Registries

ERC-8004 defines three onchain registries. ADI Chain deploys them as singleton contracts on L2. L3s in the ecosystem can use the same shared trust layer.

#### Identity Registry

The Identity Registry is an ERC-721-based discovery layer. Each agent is minted as an NFT with a resolvable URI that points to a registration file.

It provides:

* **Portable identity** through a global identifier such as `eip155:36900:{identityRegistry}:{agentId}`
* **Service discovery** for A2A, MCP, OASF, ENS, DID, and email endpoints
* **Transferable ownership** through wallet-based control, delegation, and revocation
* **Verified payment routing** through EIP-712 or ERC-1271 proof of wallet control

**Registration file example**

```json
{
  "type": "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
  "name": "myAgentName",
  "description": "Regulatory reporting agent for UAE Central Bank filings",
  "services": [
    {
      "name": "A2A",
      "endpoint": "https://agent.example/.well-known/agent-card.json",
      "version": "0.3.0"
    },
    {
      "name": "MCP",
      "endpoint": "https://mcp.agent.example/",
      "version": "2025-06-18"
    },
    {
      "name": "email",
      "endpoint": "agent@example.com"
    }
  ],
  "registrations": [
    {
      "agentId": 22,
      "agentRegistry": "eip155:36900:0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
    }
  ],
  "supportedTrust": ["reputation", "crypto-economic", "tee-attestation"]
}
```

#### Reputation Registry

The Reputation Registry provides a standard interface for posting and querying feedback about agents. Feedback is stored as a signed fixed-point number with optional tags and offchain evidence.

It provides:

* **Tamper-evident history** indexed by agent and client
* **Flexible scoring** through `value` and `valueDecimals`
* **Tag-based filtering** for metrics such as `uptime` and `complianceScore`
* **Self-feedback prevention** enforced against the Identity Registry
* **Revocation and response** so clients can retract feedback and agents can append context

**Common institutional tags**

| Tag                  | What it measures                   | Example       |
| -------------------- | ---------------------------------- | ------------- |
| `complianceScore`    | Regulatory compliance audit result | `95/100`      |
| `settlementFinality` | Average time to final settlement   | `120 seconds` |
| `dataAccuracy`       | Accuracy of agent-provided data    | `99.97%`      |
| `uptime`             | Service availability               | `99.99%`      |
| `auditPass`          | Pass or fail for a periodic audit  | `1` for pass  |

#### Validation Registry

The Validation Registry records independent checks on agent actions. Validators can be staked re-executors, zkML verifiers, TEE oracles, or trusted third parties.

It provides:

* **A request-response protocol** where agents request validation and validators return a score from `0` to `100`
* **Pluggable trust models** so institutions choose the validator design that matches the risk
* **Progressive finality** through multiple responses to the same request
* **An onchain audit trail** for every validation request and response

### Deployed contracts on ADI Mainnet

All three ERC-8004 registries are deployed at vanity addresses on ADI Mainnet.

| Registry               | Address                                                                                                                              |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **IdentityRegistry**   | [`0x8004A169FB4a3325136EB29fA0ceB6D2e539a432`](https://explorer.adifoundation.ai/address/0x8004A169FB4a3325136EB29fA0ceB6D2e539a432) |
| **ReputationRegistry** | [`0x8004BAa17C55a88189AE136b182e5fdA19dE9b63`](https://explorer.adifoundation.ai/address/0x8004BAa17C55a88189AE136b182e5fdA19dE9b63) |
| **ValidationRegistry** | [`0x8004Cc8439f36fd5F9F049D9fF86523Df6dAAB58`](https://explorer.adifoundation.ai/address/0x8004Cc8439f36fd5F9F049D9fF86523Df6dAAB58) |

* **Version:** `2.0.0`
* **Owner:** `0x547289319C3e6aedB179C0b8e8aF0B5ACd062603`

{% hint style="warning" %}
As of July 2026, the canonical ERC-8004 Identity, Reputation, and Validation singleton contracts are not deployed on ADI Testnet. Third-party agent infrastructure may use separate registries and integration contracts; those deployments should not be treated as the canonical ERC-8004 registry set.
{% endhint %}

### How agents use the ADI stack

#### Settlement hierarchy

```mermaid
flowchart TB
    subgraph L3["L3 Chains (Agent Execution Environments)"]
        A["Agent A (Bank)"]
        B["Agent B (Regulator)"]
        C["Agent C (Market Maker)"]
        REG["ERC-8004 Registries<br/>(Identity, Reputation, Validation)"]
        A & B & C --> REG
    end

    subgraph L2["ADI Chain (L2) — Settlement and Registry Layer"]
        L2C["• ERC-8004 singleton contracts<br/>• Bridgehub, Diamond Proxy, STM<br/>• Private mempool"]
    end

    subgraph L1["Ethereum (L1) — Final Settlement"]
        L1F["Verifier Contract"]
    end

    REG -->|validity proofs| L2C
    L2C -->|validity proofs| L1F
```


# Tools for Agents

MCP servers, agent frameworks, and protocols for building agents on ADI Chain.

ADI Chain is EVM-equivalent. Every MCP server and agent framework built for Ethereum works here by pointing it at an ADI RPC endpoint.

This page covers agent-specific tooling not documented elsewhere. For network setup, see [ADI Testnet](/adi-networks/adi-testnet).

{% hint style="info" %}
Use existing Ethereum integrations without an ADI-specific adapter. Configure the RPC URL and chain ID.
{% endhint %}

### MCP servers

Model Context Protocol (MCP) servers give agents blockchain data and onchain actions. The ADI-native server encodes bridging constraints that generic EVM servers do not, including `nativeTokenBridgingOnly`, CCIP-only USDC.e, and no DEX liquidity on Mainnet. Any EVM-compatible MCP server also works with ADI Chain.

| MCP server                                                                                             | What it does                                                                                                                                           | ADI setup                                                                                                                |
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| [ADI Chain MCP Server](/core-components/ai-agent-infrastructure/tools-for-agents/adi-chain-mcp-server) | First-party MCP server for ADI Chain — canonical bridge (deposit/withdraw/claim), Chainlink CCIP transfers, funding-path planning, and ERC-8004 reads. | See [ADI Chain MCP Server](/core-components/ai-agent-infrastructure/tools-for-agents/adi-chain-mcp-server).              |
| [EVM MCP Server](https://github.com/mcpdotdirect/evm-mcp-server)                                       | Provides 22 tools across 60+ EVM networks. Read and write contracts, transfer tokens, query balances, and fetch ABIs from explorers.                   | Add ADI Testnet (`99999`) or Mainnet (`36900`) through `CUSTOM_NETWORKS`. Use `https://rpc.ab.testnet.adifoundation.ai`. |
| [EVM MCP](https://github.com/JamesANZ/evm-mcp)                                                         | Provides 20+ RPC methods, contract calls, gas estimation, and transaction simulation with state overrides.                                             | Set `CUSTOM_PROVIDERS` to an ADI RPC URL. No chain-specific configuration is needed.                                     |
| [Ethereum JSON-RPC MCP](https://github.com/john0n1/ethereum-mcp)                                       | Proxies an Ethereum JSON-RPC endpoint as MCP tools. Includes admin, debug, and txpool tools.                                                           | Point it at an ADI RPC endpoint.                                                                                         |
| [Blockscout MCP](https://github.com/blockscout/mcp-server)                                             | Exposes balances, tokens, NFTs, transactions, and contract verification.                                                                               | Use the [ADI Testnet explorer](https://explorer.ab.testnet.adifoundation.ai).                                            |
| [The Graph MCP](https://thegraph.com/docs/en/subgraphs/querying/mcp/)                                  | Queries indexed blockchain data through subgraphs.                                                                                                     | Deploy or use subgraphs indexing ADI Chain contracts.                                                                    |
| [GOAT SDK MCP](https://github.com/goat-sdk/goat)                                                       | Provides 200+ onchain actions across EVM chains.                                                                                                       | Point it at an ADI RPC endpoint.                                                                                         |

#### Run EVM MCP Server on ADI Testnet

```json
{
  "mcpServers": {
    "evm": {
      "command": "npx",
      "args": ["-y", "@mcpdotdirect/evm-mcp-server"],
      "env": {
        "CUSTOM_NETWORKS": "[{\"name\":\"ADI Testnet\",\"slug\":\"adi-testnet\",\"chainId\":99999,\"rpc\":\"https://rpc.ab.testnet.adifoundation.ai\",\"explorer\":\"https://explorer.ab.testnet.adifoundation.ai\"}]",
        "DEFAULT_NETWORK": "adi-testnet"
      }
    }
  }
}
```

#### Build a custom MCP server

For deeper integration, wrap ERC-8004 registry interactions with the MCP SDK. The `@adi-devtools/sdk` provides typed providers and contract ABIs.

Expose tools such as:

* `register_agent(uri)` — register an agent on the Identity Registry.
* `query_reputation(agentId, tags)` — query the Reputation Registry.
* `request_validation(validator, agentId, uri, hash)` — request validation.
* `get_balance(address)` — check an ADI balance.
* `call_contract(address, abi, function, args)` — call any contract.

See [Registration & Agent Lifecycle](/core-components/ai-agent-infrastructure/registration-and-agent-lifecycle) for registry behavior and deployed Mainnet contracts.

### Agent frameworks and protocols

| Framework                                                                                                       | ADI support                          | Notes                                                                                                                          |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| [Eliza](https://github.com/elizaOS/eliza)                                                                       | Full EVM support                     | Configure the character file with an ADI Testnet RPC endpoint and funded wallet. Agents can interact with ERC-8004 registries. |
| [LangChain](https://github.com/langchain-ai/langchain) / [LangGraph](https://github.com/langchain-ai/langgraph) | Use `ethers` or `viem` tools         | Point the tool integration at an ADI RPC endpoint. No adapter is needed.                                                       |
| [CrewAI](https://github.com/crewAIInc/crewAI)                                                                   | Custom EVM tools                     | Add a tool that wraps `ethers` or `viem` calls to ADI Chain.                                                                   |
| [A2A](https://a2a-protocol.org/)                                                                                | Identity Registry endpoint discovery | Advertise an A2A endpoint URI in the registration file. Counterparties discover it through `agentURI` resolution.              |
| MCP                                                                                                             | Registration-file endpoint URIs      | Agents can expose MCP servers and discover each other's MCP endpoints through the Identity Registry.                           |

{% hint style="info" %}
Registration files support A2A, MCP, DID, ENS, and email service endpoints. Publish these endpoints with the agent identity.

For agents using ERC-8004 and `did:adi` (Apeiro/IDA), cross-link through the registration file's `services[]` array and the DID Document's `service[]` array. See the [ERC-8004 specification](https://eips.ethereum.org/EIPS/eip-8004) for the registration file format.
{% endhint %}


# ADI Chain MCP Server

## ADI Chain MCP Server

> An MCP server exposing ADI Chain bridging, funding-path planning, and network data as executable tools for AI agents. Canonical bridge (deposit/withdraw/claim), Chainlink CCIP transfers, and ERC-8004 agent profile reads, backed by a domain model verified against live chain state.

### Overview

The server encodes ADI's cross-chain domain knowledge as a typed model and exposes it through the Model Context Protocol. It exists because route selection on ADI is constraint-driven, not liquidity-driven: `nativeTokenBridgingOnly` on the Native Token Vault, a CCIP burn/mint USDC.e with no on-chain exit liquidity, and no DEX on ADI Mainnet. An agent without this model will route into dead ends. The server refuses them at the tool boundary and states why.

Two operating modes, enforced at registration:

* **Read-only** (default): 13 read/query/advisory tools. No signing capability exists; the write tools are not registered.
* **Signer** (`ADI_MCP_PRIVATE_KEY` env): adds four write tools. The key is held in memory for the process lifetime, never persisted, never logged.

### Trust model

| Concern                | Handling                                                                                                                                       |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Key storage            | In-memory only, from env. No file writes, no logs                                                                                              |
| Withdrawal claims      | Idempotent: `claim_withdrawal` checks finalization state via the Nullifier before submitting and skips already-claimed withdrawals             |
| Advisory vs executable | Swap services (SimpleSwap, LetsExchange, ChangeHero) are advisory only; the server cannot and does not execute them                            |
| Route guarantees       | `plan_funding_path` ranks routes with explicit caveats; constraints are encoded in the domain model, not prompted in                           |
| Address integrity      | All contract addresses and ABIs verified against live RPC (`zks_getBridgehubContract`), the Chainlink CCIP directory, and the bridge UI bundle |

### Networks

|                       | Mainnet                                      | Testnet (AB)                                   |
| --------------------- | -------------------------------------------- | ---------------------------------------------- |
| Chain ID              | `36900`                                      | `99999`                                        |
| L2 RPC                | `https://rpc.adifoundation.ai`               | `https://rpc.ab.testnet.adifoundation.ai`      |
| L1                    | Ethereum                                     | Ethereum Sepolia                               |
| Explorer              | `https://explorer.adifoundation.ai`          | `https://explorer.ab.testnet.adifoundation.ai` |
| L1 ADI (ERC-20)       | `0x8b1484d57abbe239bb280661377363b03c89caea` | `0x2a98B46fe31BA8Be05ef1cE3D36e1f80Db04190D`   |
| L1 Bridgehub          | `0x7a38c18a229ef8a0ae7104ba272a46280f2d59cb` | `0xFd3cE61C65dDd1039e6e9e07FB6D6e16388d1cc7`   |
| L1 Asset Router       | `0xf25227efad2046d19777a4ca540b5c016df7fe7a` | `0x5BA5B3a62745cD4eC5a59A22c988b2Dc2ae06573`   |
| L1 Native Token Vault | `0x0a0f8912162ff83a036883dbada42eff647a3065` | not published                                  |
| L1 Nullifier          | `0x5e5a72077dfb354dfe61200b8f31fa491f9b9cea` | not published; claims unsupported on testnet   |
| CCIP Router           | `0x010771998A1F4736BD844939d0bf01ac5cA0f8fa` | `0x5D445DF89674096B6A138565cAE955FF816f352D`   |
| CCIP Chain Selector   | `4059281736450291836`                        | `9418205736192840573`                          |

L2 system contracts are the standard zksync-os addresses: base token `0x…800A`, L2 Asset Router `0x…10003`, L2 Native Token Vault `0x…10004`.

Deployed configuration diverges from in-repo contract code; this domain model is maintained against live chain state, not repo source. Re-verify after any bridge protocol upgrade.

### Tool surface

#### Query

| Tool                    | Returns                                                                                                   |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| `get_network_info`      | RPC, chain ID, explorer, bridge and CCIP contract addresses                                               |
| `get_token_info`        | Token addresses (L1/L2), decimals, bridge support                                                         |
| `get_balance`           | Native ADI or ERC-20 balance on ADI Chain                                                                 |
| `get_bridge_options`    | Viable bridges for a token route with per-bridge constraints                                              |
| `get_ccip_lanes`        | Live CCIP lanes and supported tokens per lane                                                             |
| `get_bridge_status`     | Transaction finality: pending (sealed) → committed (on L1) → executed                                     |
| `get_finality_status`   | Node finality frontiers: last sealed/committed/executed block and batch                                   |
| `estimate_deposit`      | L1 base cost (`mintValue`) of a canonical deposit, in ADI                                                 |
| `get_withdrawal_params` | Claim parameters reconstructed from an executed batch: batch number, message index, message, Merkle proof |
| `list_withdrawals`      | An address's L2→L1 withdrawals with stage and claim status; stateless, derived from on-chain state        |
| `get_agent_profile`     | ERC-8004 identity, reputation, and validation summary (mainnet only; testnet registries are not deployed) |

#### Advisory

| Tool                | Returns                                                               |
| ------------------- | --------------------------------------------------------------------- |
| `plan_funding_path` | Ranked routes onto ADI Chain with steps, costs, and caveats           |
| `get_swap_services` | Non-custodial swap services delivering ADI directly to an ADI address |

#### State-changing (signer required)

| Tool               | Effect                                                                                                                                                                            |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bridge_deposit`   | Approves the Native Token Vault (ADI) or Asset Router (ERC-20), deposits via `requestL2TransactionDirect` on Bridgehub. Returns L1 and canonical L2 hashes. L2 finality in \~15 s |
| `bridge_withdraw`  | L2 → L1 withdrawal of native ADI or a bridged ERC-20. Commit → prove → execute, \~75 min, then L1 claim                                                                           |
| `claim_withdrawal` | Submits the claim on L1 via the Nullifier using params from `get_withdrawal_params` or rebuilt from a tx hash                                                                     |
| `ccip_transfer`    | `ccipSend` on the CCIP router for USDC.e, LINK (mainnet) or CCIP-BnM (testnet)                                                                                                    |

Deposit gas defaults: L2 gas limit 1,000,000 with `gasPerPubdataByteLimit` 800, matching the bridge UI. `estimate_deposit` returns the required L1 value before you commit funds.

### Flows

**Canonical funding cycle** (the reliable path to native ADI):

1. `plan_funding_path { network: "mainnet", target: "native-ADI", source: "USDC" }` returns ranked routes with cost and time estimates.
2. `bridge_deposit` moves L1 ADI ERC-20 to native ADI. Approve + deposit in one call; the canonical L2 hash tracks via `get_bridge_status`.
3. `bridge_withdraw` reverses direction. After \~75 min the batch executes on L1; `get_withdrawal_params` rebuilds the Merkle proof, `claim_withdrawal` settles through the Nullifier. `list_withdrawals` sweeps an address for claimable withdrawals without local state.

**CCIP lane transfer**: USDC.e and LINK move over mainnet lanes to Ethereum, Arbitrum One, Base, and Solana. The server rejects tokens without a CCIP pool rather than constructing a doomed `ccipSend`.

**The stablecoin constraint**: USDC.e on ADI Mainnet is a CCIP burn/mint token. The canonical bridge cannot touch it, and with no on-chain DEX liquidity it cannot be swapped to native ADI in place. `plan_funding_path` models this honestly: CCIP back to Ethereum → swap → canonical deposit, or an advisory swap service delivering ADI directly.

### Encoded constraints

Enforced in `src/domain.ts`; routing and error messages derive from these:

* Mainnet runs `nativeTokenBridgingOnly`: only ADI is registered in the Native Token Vault. Two-Bridges ERC-20 deposits are protocol-supported but unconfigured; the server rejects them and points to CCIP.
* USDC.e moves only via CCIP lanes. Canonical bridge interaction is impossible by token-pool design.
* No on-chain DEX with liquidity exists on ADI Mainnet.
* Testnet CCIP carries only CCIP-BnM. USDC is not on testnet lanes, so stablecoin test flows cannot run end-to-end via CCIP.
* Testnet is CCIP-only for writes. Canonical deposit, withdraw, and claim are rejected because the canonical bridge and L1 Nullifier are unpublished there (addresses are `0x0`).

### Architecture

```
src/
  cli.ts        stdio MCP server entry
  tools.ts      tool definitions, zod schemas, registration
  domain.ts     networks, tokens, bridges, constraints (source of truth)
  routing.ts    funding-path planner
  providers.ts  viem clients, Blockscout v1 API, zks_* RPC, L1 fallbacks
  bridge.ts     deposit/withdraw/claim construction
  ccip.ts       ccipSend construction
  erc8004.ts    ERC-8004 registry reads
  abis/         vendored ABIs, verified against deployed contracts
```

TypeScript on `@modelcontextprotocol/sdk` and viem. Runs over stdio; any MCP client works. Tests: unit (`bun test`), live smoke against mainnet RPC (`bun run test/smoke.ts`), write-path construction (`bun run test/write.ts`), and a live deposit → withdraw → claim round trip (`test/live.ts`).

### Install

Build from source off the tagged release — read the source before running it.

```bash
git clone https://github.com/ADI-Foundation-Labs/adi-mcp-server.git
cd adi-mcp-server
git checkout v1.0.1
bun i -g .          # or: npm i -g .
adi-mcp-server      # stdio MCP server
```

```json
{
  "mcpServers": {
    "adi": {
      "command": "adi-mcp-server",
      "env": { "ADI_MCP_PRIVATE_KEY": "0x..." }
    }
  }
}
```

Update: `git pull && git checkout v1.0.1 && bun i -g .`.

### Links

* Repository: <https://github.com/ADI-Foundation-Labs/adi-mcp-server>
* Release (v1.0.1): <https://github.com/ADI-Foundation-Labs/adi-mcp-server/releases/tag/v1.0.1>
* Bridge UI: <https://bridge.adifoundation.ai>
* Transporter (CCIP): <https://app.transporter.io>
* Explorer: <https://explorer.adifoundation.ai>


# Institutional Considerations

Compliance, trust, privacy, and governance considerations for institutional agent deployments.

### Institutional considerations

#### Compliance by design

ADI Chain is built for regulated environments. For agent infrastructure, that means:

* **Private mempool** that reduces front-running, sandwiching, and pending-order leakage
* **Jurisdiction-aware L3s** that enforce local policy while inheriting ADI settlement security
* **Ex-ante compliance** through integrations such as SettleMint DALP or Zoniqx zCompliance
* **Onchain auditability** for registrations, trust signals, and validation events

#### Trust model selection

Institutions can choose a trust model that matches the value at risk.

| Value at risk | Recommended trust model               | How it works                                                      |
| ------------- | ------------------------------------- | ----------------------------------------------------------------- |
| Low           | Reputation only                       | Trust comes from historical feedback by known counterparties.     |
| Medium        | Reputation + TEE attestation          | The agent runs in a TEE and the attestation is recorded onchain.  |
| High          | Reputation + stake-secured validation | Validators stake capital and face slashing for bad validation.    |
| Maximum       | Reputation + zkML + TEE               | Multiple independent validation paths reduce trust concentration. |

#### Data privacy

* Registration files can use IPFS or HTTPS URIs
* Sensitive endpoints can stay off the public record and be shared bilaterally
* Detailed feedback context can remain offchain and be referenced by hash
* Validation evidence can point to encrypted payloads with controlled access
* The private mempool reduces transaction visibility during submission

#### Governance

* ERC-8004 registries are upgradeable through the Diamond Proxy pattern
* Registry-level upgrades are managed by the contract owner
* Agent-level governance remains with token owners and delegated operators
* L3s can extend base registries with jurisdiction-specific logic
* The [ADI DLT Framework](/adi-dlt-framework) defines the broader governance and operational control model


# Use Cases

Institutional agent use cases for payments, assets, compliance, energy, and registries.

#### Regulated cross-border payments

A bank can run a settlement agent on its own L3. The agent negotiates FX, coordinates with counterparties, settles on ADI infrastructure, and leaves a regulator-verifiable record.

#### Tokenized asset portfolio management

An asset manager can run agents that monitor portfolios, rebalance positions, and distribute coupon payments. Validators can approve actions before settlement.

#### Supply chain compliance

A public-sector or enterprise operator can run compliance agents that verify import and export documentation across jurisdictions. Customs or counterparties can query the validation record before accepting a transaction.

#### Energy grid coordination

Utility operators can run agents that negotiate purchases, verify consumption data, and settle payments on a dedicated L3 with predictable throughput.

#### National registry management

A registry authority can run agents that process title transfers, verify supporting documents, and route approvals to authorized validators such as notaries.


# ADI Token Utility

Core utility, gas, settlement, and staking roles of the ADI token.

The $ADI token is the core utility token of the ADI Chain ecosystem, designed to support both Web3-native and enterprise-grade use cases. Its functions are explicitly tied to network operations and ecosystem incentives:

#### 1. Native Gas & Operational Utility

$ADI is the primary gas token for all transactions on ADI Chain (L2) and its associated L3 domains. All smart contract executions, dApp interactions, and transfers require $ADI for gas fees. This is enabled by zkStack’s Custom Gas Token capability, eliminating the need for ETH management.

#### 2. Medium of Exchange

$ADI acts as the settlement currency within the ecosystem, facilitating payments between enterprises, developers, validators, and users.

#### 3. Staking & Yield

Token holders can stake $ADI into a treasury-backed pool to earn rewards. This model avoids minting and supports long-term token value preservation.

{% hint style="info" %}
Discover projects, partners, institutional L3s, dApps, and tools built on or connected to ADI Chain in the [ADI Ecosystem Map](https://ecosystem.adi.foundation/).
{% endhint %}


# ADI Tokenomics Overview

Genesis supply, allocation, vesting, and unlock schedule for the ADI token.

Genesis supply: 999,999,999

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2FAvsGg4DH6LqgqwlGpRqA%2F1.png?alt=media&amp;token=7978a6d4-19e7-4ed9-8893-d81b6e4e2449" alt=""><figcaption></figcaption></figure>

#### Allocation

| Category              | Alloc. % |          Tokens | Cliff  | Vest    | % Bullet at Cliff | Vested at TGE, % | Circulating at TGE % |      TGE Circ. |
| --------------------- | -------: | --------------: | ------ | ------- | ----------------: | ---------------: | -------------------: | -------------: |
| Team                  |      10% |   99,999,999.90 | 12 mo. | 72 mo.  |            0.000% |           0.000% |               0.000% |              — |
| Private Investors     |      12% |  119,999,999.88 | 12 mo. | 72 mo.  |            0.000% |           0.000% |               0.000% |              — |
| Treasury Reserves     |      25% |  249,999,999.75 | None   | 108 mo. |            5.000% |           1.250% |               0.625% |   6,249,999.99 |
| Token Incentivization |       4% |   39,999,999.96 | None   | Immed.  |          100.000% |           4.000% |               1.000% |   9,999,999.99 |
| Partnerships          |      10% |   99,999,999.90 | 12 mo. | 72 mo.  |            0.000% |           0.000% |               0.000% |              — |
| Liquidity             |       4% |   39,999,999.96 | None   | Immed.  |          100.000% |           4.000% |               4.000% |  39,999,999.96 |
| Community Fund        |      35% |  349,999,999.65 | None   | 72 mo.  |            1.370% |           0.480% |               0.240% |   2,397,260.27 |
| **TOTAL**             | **100%** | **999,999,999** |        |         |                   |                  |           **5.865%** | **58,647,260** |

\
Note: During the first year, tokens unlock monthly on the 9th of each month.


# ADI Tokenomics Paper

Read more about ADI Tokenomics

{% embed url="<https://drive.google.com/file/d/1FwMxsvjKobJK5LBCkQlQOlJVeGmVBvOm/view?usp=sharing>" %}


# Appendix A - Technical Deep Dive

Technical overview of the ADI zkRollup architecture, lifecycle, and L3 expansion.

#### A1. Rollup Specification (Summary)

ADI Network is a zkRollup: A Layer-2 technology that uses zero-knowledge validity proofs to guarantee the correctness of every state transition. All transactions are executed on ADI L2 and collected into batches. For each batch, a zkProof is generated, demonstrating that the resulting state is valid.

This proof is then submitted to Ethereum L1, where it is verified by a dedicated contract. Only if the proof is valid does Ethereum finalize the new state, which means invalid batches can never be accepted.

#### A2. Transaction Lifecycle — From ADI to Ethereum Finality

<figure><img src="https://64618043-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FaY6Y0qQjP3BUSOiQFsEV%2Fuploads%2F1KRN0OumfoGKs8qm34vz%2Funknown.png?alt=media&amp;token=103f8fc8-36f1-411b-9bca-97a16c03fec8" alt=""><figcaption></figcaption></figure>

#### A3. Key Features of the ADI zkRollup Architecture

* Scalability. Each ADI L2 instance supports roughly 2,000–10,000 TPS. Where additional throughput or specialization is needed, separate L3 chains can be deployed on top.
* Gas Fees. By batching transactions and compressing proofs, the system reduces fees by 90–95% compared with L1 Ethereum. Gas is paid in ADI via the Custom Gas Token model, removing the need to manage ETH separately.\
  ZK Finality & Security. Soft confirmations by Sequencer. Final settlement on Ethereum once the proof is verified.
* Ethereum Compatibility & Bridging. Contracts, tooling, and EVM standards work without modification, ensuring smooth deployment and migration. ADI supports seamless bridging and messaging between L1 and L2, and includes trust-minimized pathways.

#### A6. Cross‑Chain Messaging

A unified bridging portal interface allows users to deposit or withdraw assets between Ethereum (L1), ADI (L2), and specialized L3s. All communication is trust-minimized: messages are proven as part of the ZK proof, ensuring rapid finality when moving data or assets back to L1.

#### A7. Enterprise Middleware (Hyperledger FireFly)

Middleware is included to simplify integration for enterprises and governments. It auto-generates REST APIs for deployed smart contracts, provides reliable event streaming with guaranteed delivery, and integrates with DID/KYC services.

#### A8. Modularity & L3 Expansion

The network supports additional L3 chains that applications, technical needs, privacy needs, or jurisdictions can tailor. These L3s inherit ADI L2 security while offering compliance segmentation, higher privacy, or performance tuning. This approach allows governments, enterprises, or sector-specific networks to operate their own domain while remaining part of the ADI ecosystem.<br>


# Appendix B - Governance

How governance activation works and what remains dormant at launch.

At launch, no tokenholder governance is active for the ADI Chain. While the Charter establishes a comprehensive governance framework, this framework remains dormant until formally activated by an Activation Resolution. Activation will occur following the deployment and audit of the relevant governance smart contracts, publication of detailed Governance Specifications, and the filing of updated documents with the ADGM Registrar within the required regulatory window.

<br>


# FATF and ADGM Travel Rule Compliance

Travel Rule compliance responsibilities for L3 operators and ecosystem participants.

The Foundation provides blockchain infrastructure and does not itself act as a Virtual Asset Service Provider (VASP) as defined by the Financial Action Task Force (FATF) Recommendation 16 (“Travel Rule”) and the Abu Dhabi Global Market (ADGM) regulatory framework.

However, any Layer-3 (L3) operators, partners, or ecosystem participants who provide custodial services, operate exchanges, or act as transfer agents on the ADI Chain are required to comply with the FATF Travel Rule and all relevant ADGM regulations. The Foundation may provide technical tools and guidance to support L3 operators in meeting these obligations.

<br>


# Overview

Introduction to ADI Network

**ADI Network**, utilizing the zkSync stack and based on two complementary pillars:\
\
[**zkSync Airbender**](/adi-network-components/airbender), a next-generation proof system and a modular execution layer based on the zkSync OS stack.

In this section, we will deep dive into the ADI Network execution layer.

{% hint style="warning" %}
The ADI network and zkSync OS are under active development; you can track the updates for [**zkSync OS**](https://docs.zksync.io/zksync-protocol/zksyncos/overview) here.
{% endhint %}

### ADI Network: The Execution Layer

ADI Network execution layer is a system-level implementation for the state transition function.

It takes block data and an initial state as input and computes the new state after the application of the block.

ADI Network execution layer is implemented as a Rust program that will be compiled to two targets. This first one, x86, is used for running in the sequencer. The second, RISC-V, is fed as an input to the Airbender prover to produce the validity proof of the state transition.

### Components of ADI Network execution layer

The main components of the ADI Network execution layer are:

* [**Bootloader**](/adi-network-components/bootloader): The entry point program. It initializes the system and then runs transactions using two components: the system and the execution environment interpreters.
* [**Execution Environment**](/adi-network-components/execution-environment): Regular interpreters that take bytecode, calldata, resources (similar to gas), and some other call context values as its input. Interpreters are instantiated with some local state to execute a frame. When an interpreter sees a call to another contract, return/revert from the current frame, or contract creation, it triggers special functionality to process it, as a potentially different interpreter should be run.
* [**System**](/adi-network-components/system): Provides an abstract interface for low-level handling of IO (storage, events, L1 messages, oracles) and memory management. The system communicates with the external oracle (non-determinism source), which is needed to read block data , and also for some IO operations, e.g., to perform the initial read for a storage slot.

#### Running Environments

The two running environments are:

1. **Forward Running Mode:** To be used in the sequencer. In such mode, we expect code to be run on the usual platform with OS, so the default memory allocator can be used (as it’s part of the OS). For non-determinism source, we can pass Oracle's Rust implementation as a bootloader input. Some code can also be skipped in this mode (e.g., Merkle proof verification for storage reads).
2. **Proving Running Mode:** To be used during proof generation. The code runs on a pure RISC-V platform without an execution environment, so memory management must be handled manually. Also, special care is needed to pass external data into the RISC-V machine because of the absence of standard non-determinism sources. All behavior must be fully deterministic and provable.

***

### System Resources

In ADI Network, the concept of "resources" is required to limit and charge for both computation (primarily proving) and data usage. This is more complex than it may initially appear: ADI Network is designed to be EVM gas-equivalent, meaning that EVM code execution should follow the same gas schedule as on Ethereum.

However, the EVM gas schedule does not accurately reflect the cost of ZK proof generation. To address this mismatch, we are using [double accounting](/adi-network-components/double-resource-accounting): tracking both Execution Environment (EE) is gas-equivalent to EVM gas, and a "native" computational resource that models the cost of proving.

### L1 Integration

ADI Network, is designed for use in ZK rollups and validiums, where state transition correctness must be verified on the settlement layer (referred to here as L1 for simplicity).

Specifically, a state commitment is stored on L1. For each block or batch, a proof is generated to verify that a valid state transition has occurred from a known L1 state commitment to a new one based on some set of inputs. This means the state pre and post transition must be included in the public inputs (or preimage) of the ZK proof.

In addition, the public input will include other components required for:

* Messaging
* Data availability (DA) validation
* Input validation


# Canonical Bridge

Technical reference for the ADI Canonical Bridge — contract methods, call chains, token deployment, asset IDs, and security guarantees.

This page covers the contract-level details of the ADI Canonical Bridge. For a high-level overview of how deposits and withdrawals work, see [The Bridge](/how-to-start/the-bridge).

***

## Bridge Methods Reference

### Deposits (L1 → L2)

#### ADI Base Token Deposit

The native path for depositing ADI — the chain's base token.

| Step | Called By    | Method                                                        | Contract               | Description                                                                                                  |
| ---- | ------------ | ------------------------------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
| 1    | User         | `approve(spender, amount)`                                    | ADI ERC20 (L1)         | Authorize the bridge to spend your ADI                                                                       |
| 2    | User         | `requestL2TransactionDirect(request)`                         | Bridgehub              | Initiate the deposit. `msg.value` must be 0 (ADI is ERC20, not ETH)                                          |
| 3    | Bridgehub    | `bridgehubDepositBaseToken(chainId, assetId, caller, amount)` | L1 Asset Router        | Routes the base token deposit                                                                                |
| 4    | Asset Router | `bridgeBurn(chainId, msgValue, assetId, caller, data)`        | L1 Native Token Vault  | Locks ADI via `safeTransferFrom()`. Updates `chainBalance` accounting                                        |
| 5    | Bridgehub    | `bridgehubRequestL2Transaction(request)`                      | Mailbox                | Creates the L2 priority transaction. Validates `mintValue >= baseCost + l2Value`. Emits `NewPriorityRequest` |
| 6    | Bootloader   | `mint(account, amount)`                                       | L2 Base Token (0x800A) | Mints native ADI balance on L2. Only callable by the bootloader                                              |

**Key parameter**: `mintValue` — the total amount of ADI to mint on L2. Must cover both L2 gas costs (`baseCost`) and the deposit value (`l2Value`).

#### ERC20 Token Deposit

Uses the **Two Bridges** pattern — one bridge handles ADI for L2 gas, the other handles the ERC20 token.

| Step | Called By    | Method                                           | Contract                        | Description                                                                                             |
| ---- | ------------ | ------------------------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| 1    | User         | `approve(spender, amount)`                       | ERC20 Token (L1)                | Authorize for the token amount                                                                          |
| 2    | User         | `approve(spender, mintValue)`                    | ADI ERC20 (L1)                  | Authorize for L2 gas costs                                                                              |
| 3    | User         | `requestL2TransactionTwoBridges(request)`        | Bridgehub                       | Initiates dual-bridge deposit                                                                           |
| 4    | Bridgehub    | `bridgehubDepositBaseToken(...)`                 | L1 Asset Router                 | First bridge: locks ADI for L2 gas                                                                      |
| 5    | Bridgehub    | `bridgehubDeposit(chainId, caller, value, data)` | L1 Asset Router                 | Second bridge: locks ERC20. Rejects if `assetId == baseTokenAssetId`                                    |
| 6    | Asset Router | `bridgeBurn(...)`                                | L1 Native Token Vault           | Locks ERC20 via `safeTransferFrom()`. Fetches token metadata (name, symbol, decimals) for L2 deployment |
| 7    | Bridgehub    | `bridgehubRequestL2Transaction(request)`         | Mailbox                         | Creates L2 priority tx targeting L2 Asset Router                                                        |
| 8    | Bootloader   | `finalizeDeposit(chainId, assetId, data)`        | L2 Asset Router (0x10003)       | Receives the deposit on L2                                                                              |
| 9    | Asset Router | `bridgeMint(chainId, assetId, data)`             | L2 Native Token Vault (0x10004) | If first time: deploys `BridgedStandardERC20` via CREATE2. Then mints tokens to recipient               |

After the Mailbox creates the L2 transaction, the L1 Asset Router records the deposit via `bridgehubConfirmL2Transaction()`, storing `depositHappened[chainId][canonicalTxHash] = txDataHash`.

{% hint style="info" %}
The bridge UI defaults to showing the settlement layer's base token, which is ADI on ADI deployments. To bridge custom ERC20 tokens through the UI, add them to the bridge `config.js` `tokens` array. For direct integrations, call `requestL2TransactionTwoBridges`.
{% endhint %}

### Withdrawals (L2 → L1)

#### ADI Base Token Withdrawal

| Step | Called By     | Method                                    | Contract               | Description                                                                               |
| ---- | ------------- | ----------------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------- |
| 1    | User          | `withdraw(l1Receiver)`                    | L2 Base Token (0x800A) | Payable — send ADI as `msg.value`. Burns the ADI on L2                                    |
| 2    | L2 Base Token | `sendToL1(message)`                       | L1 Messenger (0x8008)  | Records 56-byte withdrawal message. Creates L2→L1 log with `sender=0x8008`, `key=0x800A`  |
| —    | ADI Server    | *Batch processing*                        | Executor               | Commit → Prove → Execute batch on L1                                                      |
| 3    | User          | `finalizeDeposit(params)`                 | L1 Nullifier           | Verifies Merkle proof. Sets `isWithdrawalFinalized = true`. Parses the withdrawal message |
| 4    | Nullifier     | `finalizeDeposit(chainId, assetId, data)` | L1 Asset Router        | Routes to Token Vault                                                                     |
| 5    | Asset Router  | `bridgeMint(chainId, assetId, data)`      | L1 Native Token Vault  | Decreases `chainBalance`. Calls `safeTransfer()` to send ADI ERC20 to the L1 receiver     |

**Withdrawal message format** (56 bytes, `abi.encodePacked`):

```
+------------+----------------------+----------------------------------+
|  4 bytes   |      20 bytes        |           32 bytes               |
|  selector  |    l1Receiver        |       amount (wei)               |
| 0x6c0960f9 |  (packed address)    |        (uint256)                 |
+------------+----------------------+----------------------------------+
```

#### ERC20 Token Withdrawal

| Step | Called By    | Method                                                 | Contract                        | Description                                                                                               |
| ---- | ------------ | ------------------------------------------------------ | ------------------------------- | --------------------------------------------------------------------------------------------------------- |
| 1    | User         | `withdraw(assetId, assetData)`                         | L2 Asset Router (0x10003)       | `assetData = encode(amount, l1Receiver, l2TokenAddress)`                                                  |
| 2    | Asset Router | `bridgeBurn(chainId, msgValue, assetId, caller, data)` | L2 Native Token Vault (0x10004) | Calls `BridgedStandardERC20.bridgeBurn()` to destroy tokens. Encodes `bridgeMintData` with token metadata |
| 3    | Asset Router | `sendToL1(message)`                                    | L1 Messenger (0x8008)           | Records L2→L1 message with `key = L2AssetRouter address`                                                  |
| —    | ADI Server   | *Batch processing*                                     | Executor                        | Commit → Prove → Execute batch on L1                                                                      |
| 4    | User         | `finalizeDeposit(params)`                              | L1 Nullifier                    | Verifies proof. Parses the Asset Router message format. `l2Sender` = L2 Asset Router (0x10003)            |
| 5    | Nullifier    | `finalizeDeposit(chainId, assetId, data)`              | L1 Asset Router                 | Routes to Token Vault                                                                                     |
| 6    | Asset Router | `bridgeMint(chainId, assetId, data)`                   | L1 Native Token Vault           | Decreases `chainBalance`. Calls `safeTransfer()` to send ERC20 to the L1 receiver                         |

**Note on L2-native tokens**: If the token is native to L2, the user must approve `L2NativeTokenVault (0x10004)` to pull tokens (because NTV uses `safeTransferFrom` rather than burn).

***

## How It Works on Contracts

This section provides a deeper technical look at the smart contract architecture — the exact contracts involved, their roles, and how they interact.

### Contract Architecture

```mermaid
%%{init: {'theme': 'dark'}}%%
graph TB
    subgraph L1["Ethereum L1"]
        BH[Bridgehub]
        subgraph Diamond["Diamond Proxy"]
            MB[Mailbox]
            EX[Executor]
        end
        AR1[L1 Asset Router]
        NTV1[L1 Token Vault]
        NUL[L1 Nullifier]
    end

    subgraph L2["ADI Chain L2"]
        BT["Base Token (0x800A)"]
        AR2["Asset Router (0x10003)"]
        NTV2["Token Vault (0x10004)"]
        MSNG["L1 Messenger (0x8008)"]
        BSE[BridgedStandardERC20]
    end

    BH --> AR1 --> NTV1
    BH --> MB
    NUL --> AR1
    MB -. "priority tx" .-> AR2 --> NTV2 --> BSE
    BT --> MSNG
    AR2 --> MSNG
    EX -. "batch data" .-> NUL
```

### L1 Contracts

#### Bridgehub

The central coordination hub. All deposits enter through one of two functions:

| Function                                  | When Used               | Key Behavior                                                                                                   |
| ----------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------- |
| `requestL2TransactionDirect(request)`     | ADI base token deposits | Requires `msg.value == 0` for non-ETH base token. Calls `bridgehubDepositBaseToken()` then creates priority tx |
| `requestL2TransactionTwoBridges(request)` | ERC20 token deposits    | First bridge locks ADI for gas, second bridge locks ERC20. Creates priority tx targeting L2 Asset Router       |

Both functions end by calling the **Mailbox** to queue the L2 transaction.

#### L1 Asset Router

Routes deposits and withdrawals to the appropriate **asset handler** (usually the Native Token Vault):

| Function                                                      | Access         | Purpose                                                                            |
| ------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------- |
| `bridgehubDepositBaseToken(chainId, assetId, caller, amount)` | Bridgehub only | Handles base token deposits                                                        |
| `bridgehubDeposit(chainId, caller, value, data)`              | Bridgehub only | Handles ERC20 deposits. Guards against depositing the base token through this path |
| `finalizeDeposit(chainId, assetId, transferData)`             | Nullifier only | Processes withdrawal claims — releases tokens via the Token Vault                  |

Supports two encoding versions for deposit calldata: `0x00` (legacy) and `0x01` (new format with assetId).

#### L1 Native Token Vault

Custodies all locked tokens and enforces strict accounting:

| Function                                               | Access            | Purpose                                                                                                             |
| ------------------------------------------------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `bridgeBurn(chainId, msgValue, assetId, caller, data)` | Asset Router only | Locks tokens via `safeTransferFrom()`. Increments `chainBalance[chainId][assetId]`. Returns `bridgeMintData` for L2 |
| `bridgeMint(chainId, assetId, data)`                   | Asset Router only | Releases tokens via `safeTransfer()`. Decrements `chainBalance`. Validates sufficient chain balance                 |
| `ensureTokenIsRegistered(token)`                       | Public            | Auto-registers unknown tokens. Computes assetId and stores mappings                                                 |

The `chainBalance` mapping is the core security invariant: **withdrawals from chain X can never exceed deposits to chain X**, enforced per-asset.

#### L1 Nullifier

The gatekeeper for withdrawal claims:

| Function                                   | Access | Purpose                                                      |
| ------------------------------------------ | ------ | ------------------------------------------------------------ |
| `finalizeDeposit(FinalizeL1DepositParams)` | Public | Verifies proof, marks as finalized, forwards to Asset Router |

Uses a triple-nested mapping `isWithdrawalFinalized[chainId][batchNumber][messageIndex]`. Once set to `true`, the same withdrawal can never be claimed again — even if the same proof is resubmitted.

Supports three withdrawal message formats:

| Format           | Selector                               | Used By                               |
| ---------------- | -------------------------------------- | ------------------------------------- |
| Base token       | `0x6c0960f9` (`finalizeEthWithdrawal`) | L2 Base Token withdrawals             |
| Legacy ERC20     | `finalizeWithdrawal`                   | Old L2 shared bridge withdrawals      |
| New Asset Router | `finalizeDeposit`                      | ERC20 withdrawals via L2 Asset Router |

#### Mailbox (Diamond Facet)

Handles L1↔L2 message passing:

| Function                                                | Purpose                                                                                                                       |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `bridgehubRequestL2Transaction(request)`                | Creates L2 priority transaction. Validates `mintValue >= baseCost + l2Value`. Derives L2 gas price with ADI/ETH exchange rate |
| `proveL2MessageInclusion(batch, index, message, proof)` | Verifies L2→L1 message Merkle proof against stored `l2LogsRootHashes[batchNumber]`                                            |

#### Executor (Diamond Facet)

Processes the batch lifecycle on L1:

| Function                       | Purpose                                                                                                 |
| ------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `commitBatchesSharedBridge()`  | Posts batch data to L1. Validates L2 system logs, DA proof                                              |
| `proveBatchesSharedBridge()`   | Verifies ZK proof via on-chain verifier contract                                                        |
| `executeBatchesSharedBridge()` | Finalizes batch. Stores `l2LogsRootHashes` for Merkle proof verification. Processes priority operations |

### L2 Contracts

#### L2 Base Token (0x800A)

The native ADI system contract:

| Function                                  | Purpose                                                                                           |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `withdraw(address _l1Receiver) payable`   | Burns `msg.value` of ADI. Creates 56-byte withdrawal message via L1 Messenger. Emits `Withdrawal` |
| `mint(address _account, uint256 _amount)` | Called only by bootloader during deposits. Increases native balance                               |

#### L2 Asset Router (0x10003)

Processes ERC20 deposits and initiates ERC20 withdrawals:

| Function                                  | Access                       | Purpose                                                       |
| ----------------------------------------- | ---------------------------- | ------------------------------------------------------------- |
| `finalizeDeposit(chainId, assetId, data)` | Aliased L1 Asset Router only | Receives L1→L2 deposit. Routes to L2 Native Token Vault       |
| `withdraw(assetId, assetData)`            | Public                       | Initiates ERC20 withdrawal. Burns tokens, sends L2→L1 message |

#### L2 Native Token Vault (0x10004)

Manages bridged ERC20 token representations:

| Function                                               | Access            | Purpose                                                                                      |
| ------------------------------------------------------ | ----------------- | -------------------------------------------------------------------------------------------- |
| `bridgeMint(chainId, assetId, data)`                   | Asset Router only | For first-time tokens: deploys `BridgedStandardERC20` via CREATE2. Mints tokens to recipient |
| `bridgeBurn(chainId, msgValue, assetId, caller, data)` | Asset Router only | Burns `BridgedStandardERC20` tokens. Encodes `bridgeMintData` for L1 claim                   |

#### L1 Messenger (0x8008)

Records all L2→L1 messages and builds the Merkle tree:

| Function                        | Purpose                                                                                                                  |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `sendToL1(message)`             | Records message. Creates `L2ToL1Log{sender=0x8008, key=msg.sender, value=keccak256(message)}`. Adds to batch Merkle tree |
| `publishPubdataAndClearState()` | Called by bootloader at end of batch. Builds a fixed-size Merkle tree of 16,384 leaves. Sends root to L1                 |

***

## First-Time Token Bridging

When a token is bridged to a chain where it has never existed before, the bridge automatically deploys a new ERC20 contract. This uses the **BeaconProxy** pattern — all bridged tokens share a single upgradeable implementation, making coordinated upgrades possible.

### How It Works

When the L2 Native Token Vault receives a deposit for a token that doesn't yet exist on L2, it:

1. Computes a deterministic address using CREATE2
2. Deploys a `BridgedStandardERC20` as a BeaconProxy
3. Initializes the token with metadata (name, symbol, decimals) from the deposit data
4. Mints the deposited amount to the recipient

```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart TD
    A[L1 NTV bridgeBurn — encode metadata] --> B{Token exists on L2?}
    B -->|Yes| F[bridgeMint to recipient]
    B -->|No| C[_deployBridgedToken]
    C --> D[CREATE2 — deploy BeaconProxy]
    D --> E[bridgeInitialize — set name, symbol, decimals]
    E --> F

    style C fill:#f5a623,stroke:#d4911e,color:#000
    style D fill:#f5a623,stroke:#d4911e,color:#000
    style E fill:#f5a623,stroke:#d4911e,color:#000
    style F fill:#5bb974,stroke:#4a9f6a,color:#000
```

The same process works in reverse: if an L2-native token is bridged to L1, the L1 Native Token Vault deploys a `BridgedStandardERC20` using standard Ethereum CREATE2.

### Deterministic Addresses

Bridged token addresses are fully deterministic — given the same inputs, the same token will always deploy to the same address on any chain.

Both L1 and L2 use the standard EVM CREATE2 formula:

```
address = keccak256(
    0xff ||
    NTV ||
    salt ||
    keccak256(BeaconProxy.creationCode || abi.encode(beaconAddress, ""))
)
```

* `NTV` — the Native Token Vault deploying the proxy: `L1NativeTokenVault` on L1, `0x10004` on L2.
* `beaconAddress` — the `UpgradeableBeacon` for `BridgedStandardERC20` (same on every chain in the ecosystem).
* `salt` depends on which chain the token is native to:
  * L1-native token — `bytes32(uint256(uint160(tokenAddress)))`, the L1 address left-padded to 32 bytes.
  * Token native to any non-L1 chain (e.g. a different L2 in the ecosystem) — `keccak256(abi.encode(originChainId, tokenAddress))`. Origin chain id is folded in so addresses don't collide across origins.

Because the L2 deployer (`0x10004`), beacon, and salt are reproducible across any chain in the ecosystem, the same L1 token bridged to multiple L2 chains lands at **identical L2 addresses**.

### Return Trip: No New Deployment

When a token returns to its origin chain (e.g., withdraw an L1-native token back to L1), no new contract is deployed:

```mermaid
%%{init: {'theme': 'dark'}}%%
flowchart LR
    A{Token native to this chain?} -->|Yes| B[Transfer originals from vault]
    A -->|No| C[Deploy if needed + mint]

    style B fill:#5bb974,stroke:#4a9f6a,color:#000
    style C fill:#f5a623,stroke:#d4911e,color:#000
```

***

## Asset ID System

Every bridged token is identified by a deterministic `bytes32` asset ID:

```
assetId = keccak256(abi.encode(originChainId, L2_NATIVE_TOKEN_VAULT_ADDRESS, tokenAddress))
```

Where `L2_NATIVE_TOKEN_VAULT_ADDRESS` = `0x10004` is used as the asset deployment tracker on both L1 and L2, ensuring the same asset ID resolves identically on both chains.

| Token               | originChainId                           | tokenAddress         |
| ------------------- | --------------------------------------- | -------------------- |
| ADI (base token)    | L1 chain ID (e.g. 11155111 for Sepolia) | L1 ADI ERC20 address |
| Any L1-native ERC20 | L1 chain ID                             | L1 token address     |
| L2-native ERC20     | L2 chain ID (e.g. 36900)                | L2 token address     |

### Chain Balance Tracking

The L1 Native Token Vault maintains strict per-chain, per-token accounting:

```solidity
mapping(uint256 chainId => mapping(bytes32 assetId => uint256)) public chainBalance;
```

* Incremented on deposit (tokens locked on source chain)
* Decremented on withdrawal finalization (tokens released on destination)
* Prevents cross-chain theft: one ZK chain can't drain another's deposits
* Skipped when token originates from that chain (`_hasInfiniteBalance`)

***

## Sequencer Event Tracking

The ADI Server (sequencer) actively monitors Ethereum for bridge-related events. This is the infrastructure that keeps L2 in sync with L1 — processing deposits, tracking batch finality, and enabling withdrawal proofs.

### How It Works

The sequencer runs a set of **L1 Watchers** that continuously poll Ethereum blocks for specific contract events. When an event is detected, it is parsed and forwarded to the appropriate internal component via channels.

```mermaid
%%{init: {'theme': 'dark'}}%%
graph TD
    L1["Ethereum L1 Blocks"] -->|poll| W[L1 Watcher]
    W -->|filter logs| E{Event Type}

    E -->|NewPriorityRequest| TW[L1 Tx Watcher]
    E -->|BlockCommit| CW[Commit Watcher]
    E -->|BlockExecution| EW[Execute Watcher]
    E -->|BatchRange| BW[Batch Range Watcher]

    TW -->|L1PriorityEnvelope| SEQ[Sequencer]
    CW -->|committed batch| FIN[Finality Tracker]
    EW -->|executed batch| FIN
    BW -->|batch metadata| STORE[Batch Storage]

    SEQ -->|include in L2 block| L2[L2 Block Production]

    style L1 fill:#4a90d9,stroke:#3a7bc8,color:#fff
    style L2 fill:#5bb974,stroke:#4a9f6a,color:#000
```

### Events Tracked

| Event                               | Emitted By                      | Watcher                 | What It Does                                                                                                                                                                               |
| ----------------------------------- | ------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `NewPriorityRequest`                | Mailbox facet of Diamond Proxy  | **L1 Tx Watcher**       | Detects L1→L2 deposit transactions. Parses the priority operation and forwards it to the sequencer for inclusion in the next L2 block. Tracks `next_l1_priority_id` to prevent duplicates. |
| `BlockCommit`                       | Executor facet of Diamond Proxy | **Commit Watcher**      | Detects when a batch is committed to L1. Updates `last_committed_batch` and `last_committed_block` in the finality tracker.                                                                |
| `BlockExecution`                    | Executor facet of Diamond Proxy | **Execute Watcher**     | Detects when a batch is fully executed (ZK-proven and finalized) on L1. Updates `last_executed_batch`. After this event, withdrawals in that batch become claimable.                       |
| `ReportCommittedBatchRangeZKsyncOS` | Executor facet of Diamond Proxy | **Batch Range Watcher** | Provides metadata about committed batch ranges. Used for batch replay and synchronization.                                                                                                 |

***

## Security Guarantees

The canonical bridge provides the following security properties:

**Trustless verification** — Every withdrawal is backed by a zero-knowledge proof verified on Ethereum. No trusted operator, multisig, or oracle is involved in the verification process.

**Double-claim prevention** — The nullifier pattern ensures each withdrawal can be claimed exactly once. The `isWithdrawalFinalized[chainId][batchNumber][messageIndex]` mapping is irreversible.

**1:1 asset backing** — The `chainBalance` accounting in the L1 Native Token Vault guarantees that tokens released on L1 never exceed tokens deposited from L1. This is enforced at the contract level per-chain and per-asset.

**Address aliasing** — L1 contract addresses are aliased on L2 (offset by `0x1111...1111`) to prevent impersonation attacks where an L1 contract could forge L2 messages.

**Access control** — Every sensitive function is protected by strict modifiers: `onlyBridgehub`, `onlyNullifier`, `onlyAssetRouter`, `onlyCallFromBootloader`. No external actor can directly mint, burn, or release tokens.

**Deterministic token deployment** — Bridged token addresses are computed via CREATE2, making them predictable and verifiable before any transaction is sent.

**Pausability** — All bridge contracts inherit OpenZeppelin's `PausableUpgradeable`, allowing emergency stops if a critical issue is discovered.


# Double Resource Accounting

Introduction to Double Resource Accounting

ADI Network implements a double-resource accounting model, where both an Execution Environment resource (e.g., EVM gas) and a native resource are tracked.

### Execution Environment Resource: Ergs

The EE resource is called Ergs, and corresponds to a model for computation performed by Execution Environments. The current design assumes every EE will use essentially the same resource for this, but potentially with a different conversion rate.

The gas-related fields of ADI Network transactions are interpreted as referring to EVM gas. In general, any reference to the word "gas" in the codebase or docs refers to EVM gas.

Ergs passed to a new EE frame during a call/deployment can be limited as in EVM, in which case the caller will keep the rest of the ergs available.

### Native Resource

The native resource models the off-chain cost of processing a transaction. Currently, this is primarily dominated by the process of proving and publishing data. A good intuition for it is "how many RISC-V cycles it takes to prove a given computation".

If a transaction execution runs out of native resources, the entire transaction is reverted. If the same happens during transaction validation, the transaction is considered invalid.

The native resources are passed fully from frame to frame; a call cannot set a limit on how much of it the callee can spend.

#### Native Resource Limit

To avoid using a new custom transaction format, the native resource limit (or native limit) is derived from the transaction's gas limit and fee. That is, the native limit can be incremented by either increasing the gas limit or the gas price of the transaction (using priority fee). The idea of using both these fields is to allow users to reuse presigned transactions with a fixed EVM-computed gas limit.

However, if a wallet uses the standard `eth_estimateGas` RPC, the gas estimation will assume no priority fee is added, and will add enough gas to ensure the transaction has sufficient native resources to complete.

More precisely, the logic for native resource limit derivation and charging is:

Let:

* `gasPrice` is the transaction's gas price (base fee + priority fee).
* `nativePrice` is a constant set by the operator, reflecting the "cost of processing a single cycle". Note: for L1->L2 transactions, we use a code constant instead of one provided by the operator.
* `gasLimit` is the transaction's (EVM) gas limit.

First, we define the ratio between EVM gas and native resource as: `nativePerGas := gasPrice/nativePrice`

> Note: For the call simulation, we use a constant for it, as gasPrice might be set to 0.

Next, we define the limit for the native resource as: `nativeLimit := gasLimit * nativePerGas`

Then we process the transaction, charging both Ergs for EE execution and native resources for any kind of computation (EE, bootloader, or system work).

If execution doesn't run out of native resources, we first charge for pubdata from native resources. Then, we compute the difference between the implicit gas used, derived from native resource consumption, and the gas used by EEs, based on the ergs used. We call this value `deltaGas`.

`deltaGas := (nativeUsed / nativePerGas) - gasUsed`

If `deltaGas > 0`, we add it to `gasUsed` and charge it from ergs. This ensures that gas estimation will include additional gas to cover native resources using just the base fee. We expect the base fee to be enough to cover most transactions without the need for additional gas. Finally, any remaining gas is refunded as usual.


# Bootloader

Learn about Bootloader, the entry point of the system

The bootloader is the component responsible for implementing the general blockchain protocol. Roughly, this means:

1. Initializing the system.
2. Reading the block context from the oracle.
3. Reading and parsing the first transaction.
4. Validating the transaction.
5. Executing the transaction.
6. Saving the transaction result.
7. Repeat from 3 until there are no more transactions.
8. Finalizing the block.

#### Configuration

The bootloader can be configured with the following parameters:

* `ONLY_SIMULATE`: skips the [validation](/adi-network-components/processing-of-transactions#validation) step when processing a transaction. Used for call simulation in the node.
* `IS_PROVING_ENVIRONMENT`: to skip some checks during sequencing.
* `SPECIAL_ADDRESS_SPACE_BOUND`: the range of addresses (starting from 0) where system contracts can be deployed.
* `AA_ENABLED`: whether native account abstraction is enabled.

In addition, the `basic_bootloader` crate has the following compilation flags:

* `code_in_kernel_space`: to enable normal contract execution for addresses in the range `[0, SPECIAL_ADDRESS_SPACE_BOUND]`.
* `transfers_to_kernel_space`: to enable token transfers to addresses in the range `[0, SPECIAL_ADDRESS_SPACE_BOUND]`. Note: the bootloader itself has a special formal address (0x8001) that is always allowed to receive token transfers. This is used to collect fees.
* `charge_priority_fee`: to enable charging for the EIP-1559 tip (priority fee) on top of the base fee.
* `evm-compatibility`: enables all the previous flags, needed for the EVM test suite.

#### Code Execution

For transaction execution, the bootloader has to execute some contract code. This contract code corresponds to one of the supported VMs, and is executed through the [Execution Environment](/adi-network-components/execution-environment) (EE) module.

A contract call is executed through an interplay between the bootloader and (potentially different) execution environments. Indeed, a contract executing in a given EE can call to contracts that run on a different EE or to a [System Hook](/adi-network-components/system-hooks). This interplay is described under [Runner Flow](/adi-network-components/runner-flow).

#### Block Header

At the end of the execution, the bootloader outputs the block header.

For the block header, the Ethereum format will be used. However, some fields will be set differently in the initial version for simplification.

The block header should fully determine the block, i.e., include all the necessary inputs to execute the block.

| Ethereum Field Name | Ethereum value                                                                                 | ADI Network value                                                                 | Comments                                |
| ------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | --------------------------------------- |
| parent\_hash        | previous block hash                                                                            | previous block hash                                                               |                                         |
| owners\_hash        | <p>0x1dcc4de8dec75d7aab85b567<br>b6ccd41ad312451b948a7413f<br>0a142fd40d49347 (post merge)</p> | <p>0x1dcc4de8dec75d7aab<br>85b567b6ccd41ad312451b<br>948a7413f0a142fd40d49347</p> | hash of empty RLP list                  |
| beneficiary         | block proposer                                                                                 | Operator (fee) address                                                            |                                         |
| state\_root         | state commitment                                                                               | 0                                                                                 |                                         |
| transactions\_root  | transactions trie (Patricia Merkle tree) root                                                  | transactions rolling hash                                                         |                                         |
| receipts\_root      | receipts trie (Patricia Merkle tree) root                                                      | 0                                                                                 |                                         |
| logs\_bloom         | 2048-bit bloom filter over logs’ addresses and topics                                          | 0                                                                                 |                                         |
| difficulty          | 0 (post merge)                                                                                 | 0                                                                                 |                                         |
| number              | block number                                                                                   | block number                                                                      |                                         |
| gas\_limit          | block gas limit                                                                                | constant, not defined yet, 10–15 M most likely                                    |                                         |
| gas\_used           | block gas used                                                                                 | block gas used                                                                    | TBD — with or without pubdata           |
| timestamp           | block timestamp                                                                                | block timestamp                                                                   |                                         |
| extra\_data         | any extra data included by proposer                                                            | TBD, possibly gas\_per\_pubdata                                                   |                                         |
| mix\_hash           | beacon-chain-provided random, (post merge)                                                     | 0                                                                                 | after consensus will be provided random |
| nonce               | 0 (post merge)                                                                                 | 0                                                                                 |                                         |
| base\_fee\_per\_gas | base\_fee\_per\_gas                                                                            | base\_fee\_per\_gas                                                               |                                         |


# Execution Environment

Learn about the ADI Network Execution Environment (EE)

Execution Environment (EE) is ADI's abstraction for a VM. They define their own bytecode format, interpreter, resource consumption and internal frame state. They are initialized and managed by the bootloader. When an EE reaches an exit state (call into another contract, deployment, or return), it yields to the bootloader to handle it.

#### Launch Parameters

An EE needs some initial data to be run. This data includes:

* Resources for execution,
* Bytecode to be run,
* Call information (caller, callee, modifier),
* Call values (calldata, token value).

Execution environment provides a method for creating a new instance (or frame) based on some initial data. Once launched with this data, the EE will execute the bytecode until reaching a *preemption point*.

#### Preemption Points and their Continuation

Preemption points are the possible states on which an EE will yield back to the bootloader.

These are:

* External call request,
* Deployment request,
* External call completed, and
* Deployment completed.

The last two marks indicate that the execution of the EE is complete, and declare the resources returned and the execution result. For the first two, the EE expects the bootloader to do some preparation work (see [Runner Flow](/adi-network-components/runner-flow) for more details) and launch a new EE frame for the call/constructor execution. After this sub-frame is finished, the bootloader will continue the execution of the original EE frame, forwarding the result of the sub-frame.

Thus, the execution environment has to provide methods for the bootloader to continue its execution after a call or deployment request. These Methods need to take back the resources returned by the sub-call, handle its result, and continue executing its own bytecode until reaching another preemption point.

#### EE-specific Functionality for the Bootloader

The execution environment also provides methods that don't involve bytecode execution. Instead, they expose certain information that is specific to the EE, or expose some element of the inner frame state. These include:

* Whether a call modifier is supported,
* Whether the context is static,
* How caller gas should be adjusted before passing it to the callee (think 63/64 rule for EVM),
* How to prepare for a deployment (init code checks, address derivation).


# System

Learn about the system

The *System* is passed to all EEs by the bootloader. It provides access to memory, IO, and oracles. Oracles are the interface through which the system accesses outside information.

The System struct is generic over memory, IO, oracles and allocators and provides little else besides access to those and block metadata. The *Concrete system implementations* are two instances of those components to result in full system implementations, one for the sequencer and the other for the prover (as mentioned in the [Overview](/adi-network-components/overview)).

#### System Struct

For convenience, the system is parameterized with `S: SystemTypes`, which bundles all the types the system is generic over.

#### Limited Capabilities

`SystemTypes` only requires `IO` to be an `IOSubsystem`. To access the all io functionality, `IOSubsystemExt` is required. The weaker version is meant to be passed to Execution environments, while the bootloader may use the stronger version of the trait by requiring `S::IO: IOSubsystemExt`.

#### Resources and IO types configuration

The system is generic in its representation of computational resources. It provides a reference implementation for

EE (an amount of ergs, consumed as gas in Ethereum) and native resources (used for proving). More details about resource accounting can be found in Double resource accounting.

It is also generic over the types used for IO—such as addresses, storage keys and values, balances, etc.

While many parts of the codebase require EthereumLikeTypes, maintaining type-level abstraction ensures that components not dependent on these specific types remain isolated and do not require modification when type definitions change.

#### System Functions

The system also describes a set of pure functions that should be part of any system implementation.

Most of them are used to implement precompiles as [System Hooks](/adi-network-components/system-hooks).

#### IO and Memory Subsystems

Most of the functionality of the system is related to IO or memory. These two functionalities are defined as their own subsystems.

* **The IO** subsystem is designed to abstract over the underlying storage implementation. This decision allows BoojumOS to be instantiated with different storage implementations depending on the chain needs. Each implementation is responsible for charging resources, and the charging can be different for each Execution Environment type.
* **The Memory Subsystem** provides execution environments with heap buffers in an efficient manner. The heaps are valid for the lifetime of the execution environment, so return data needs to be moved to a dedicated memory area.

#### User-Facing Methods

* Start and finish io-frames (frames without separate memory, like near-calls in Era),
* Query the block metadata from the oracle.

If you have access to a System with the \*Ext versions of the subsystems available, you can also use the following methods.

* Start and finish global execution frames (memory and io),
* Query the oracle to read the next transaction,
* Deploy bytecode,
* Initialize the system from the oracle,
* Finalize the system after the block processing is done.

#### Implementations of the System's Parts

These parts differ between forward-running and proving:

* Allocator
* Stack implementations
* IOOracle
* Logger, a simple trait to log data about the system execution, omitted in proving.


# Processing of Transactions

Learn how transactions are processed

This section describes how transactions are processed. There are 2 flows of transaction processing, one for L1 transactions and one for L2 transactions. We'll describe the latter first, and the **L1 -> L2 transactions** section will explain the differences in the former.

1. **Parsing**: The transaction is parsed to extract its details.
2. **Resource Calculation**: The bootloader computes the transaction's resource usage, including gas limits and intrinsic costs.
3. **Nonce Validation**: The bootloader ensures the transaction's nonce is unused.
4. **Fee Charging**: The required transaction fees are calculated and deducted.
5. **Execution**: The transaction is executed, either as a call or a deployment.
6. **Refunding**: Unused resources are refunded.

### Resources

Transaction processing starts with the parsing of the transaction.

After parsing the transaction, the bootloader computes how many resources the transaction can spend. This resource calculation involves taking the transaction's gas limit, checking it's bounded by the block's gas limit and subtracting calldata and intrinsic gas costs.

### Validation

The first step of validation done by the bootloader is checking that the nonce is not used. This is done by calling the corresponding account model method. We use incremental nonces, similar to those in Ethereum. Note that for this operation, and many like it, the bootloader needs to access the storage through the System.

Then the account model validation method is called. This performs the balance check for the token transfer, signature verification, and nonce increase.

The next step is charging the fees. This involves computing the required funds (`gas_price * gas_limit`), and performing the transfer to the bootloader's formal address.

#### Pubdata Considerations

The ADI Network must publish data to the settlement layer to ensure the rollup's state can be recovered at any time. This is what we call **"pubdata"**, and includes state diffs after a block application and some required preimages. Given that making this data available costs money, we need to ensure that a transaction has enough resources to pay for its share of pubdata. Thus, the final step of validation is charging for the pubdata size produced after validation.

### Execution

Validation can fail for several reasons, as detailed in the `InvalidTransaction` type in the Errors module. If it succeeds, the bootloader proceeds to execute the transaction.

This step is straightforward: the bootloader simply calls the account mode's execution method.

This consists of determining whether it's a normal call or a deployment. In the first case, the call is processed by the `run_single_interaction` helper. In the case of deployment, the bootloader will charge some extra intrinsic gas and perform the check on the init code size. After this, the deployment is processed with a call to `run_till_completion.`

After transaction execution, the bootloader checks if there are enough resources left for pubdata.

### Refunding the caller

The last step in the transaction processing is the refund of unused resources.

### L1 -> L2 transactions

The main difference in the processing of L1 transactions is that there is no validation step. The value for this transaction is minted, as those funds are now locked in the L1 bridge. Currently, deployment transactions from L1 are not supported.


# System Hooks

Learn about the System Hooks

System hooks are special functions that can be triggered by a call on a specific system address. The space for this special address is specified in the [bootloader](/adi-network-components/bootloader) configuration.

System hooks have two distinct use cases:

* Implementing precompiles like EVM. We currently support the following precompiles at their EVM addresses:
  * ecrecover
  * sha256
  * ripemd-160
  * identity
  * modexp
  * ecadd
  * ecmul
  * ecpairing
* Implementing system contracts: formal contracts that implement some system functionality.
  * L1 messenger system hook
  * L2 Base token system hook
  * Contract deployer system hook

### L1 Messenger System Hook

The L1 messenger system hook is responsible for sending messages to the L1. Users can call it using the special interface. The input should be encoded as calldata for the `sendToL1(bytes)` method following the Solidity ABI.

The implementation of the L1 messenger system hook does two things: decodes the input and records the message using the system method.

### L2 Base Token System Hook

The L2 base token system implements only two methods: `withdraw(address)`, `withdrawWithMessage(address,bytes)`. These are required to support base token withdrawals.

### Contract Deployer System Hook

The contract deployer system hook implements only one method: `setDeployedCodeEVM(address,bytes)`.

It allows setting arbitrary deployed EVM bytecode at any address, but can only be called by a special system address. It is used exclusively by protocol upgrade transactions approved through governance.


# Runner Flow

Learn about the Runner Flow

This section describes how the bootloader interacts with the execution environments to run contract code. This section is complemented by [Execution Environment](/adi-network-components/execution-environment).

### Entrypoints

The bootloader implements and uses two entrypoints for code execution.

The first one is `run_till_completion` from the `runner` module of the bootloader. This function implements the main execution loop given an initial request (either external call or deployment), which is explained in the next section.

The second one, `run_single_interaction`, is just a simple wrapper over the previous to simplify external calls from the bootloader. It just adds the logic for starting and finishing the topmost execution frame and prepares the inputs for `run_till_completion`.

### Runner Structure

The runner's responsibility is to coordinate calls into the execution environments. For this, the runner keeps a callstack of execution environment states and will be responsible of starting and finishing system frames. Frames are used to take snapshots for storage and memory to which the system can revert to in case of a failure.

The runner is implemented as an infinite loop that dispatches the spawn requests returned by the execution environment. As a reminder, these are:

* External call request and
* Deployment request.

The runner breaks out of the infinite loop after processing the completion of the initial request (when the callstack becomes empty).

#### Call Request

For the external call request, the bootloader needs to:

1. Start a frame for call.
2. Run the call preparation in the system, which will return the callee's bytecode, transfer token value, and charge gas according to the EE's policy.
3. Create a new EE state for the call and push it to the callstack.
4. Call into the newly created EE to start executing the frame.

There's a special case in which the callee is a special address (for example, precompile or system contract). In this case the flow is similar, but there's no new EE. Instead, the [System Hooks](/adi-network-components/system-hooks) are used.

After the first call returns a preemption point, the runner will either recursively handle a new spawn request (for a nested call/deployment) or the completion of the original call.

#### Deployment Request

For deployment request, the bootloader needs to:

1. Start a frame for deployment preparation.
2. Call into the EE to run the deployment preparation. This will compute the deployed address, perform some checks, and charge gas.
3. Create the new EE state for the constructor using the output from the previous call and push it to the callstack.
4. Start a frame for the constructor execution.
5. Set nonce to 1 (see EIP-161).
6. Perform token transfer.
7. Call into the newly created EE to start executing the constructor frame.

After the constructor returns a preemption point, the runner will either recursively handle a new spawn request (for a nested call/deployment) or the completion of the constructor. In this last situation, the runner has to:

1. If the constructor execution was successful, ask the system to actually deploy the code.
2. Finish the constructor frame, reverting if the constructor ended in a revert state.
3. Pop the deployer from the callstack.
4. Copy return data into the return memory of the deployer.
5. Continue execution of the deployer.


# Airbender

Introduction to Airbender

* zkSync Airbender is a new ZK proof system.
* [**ADI Network**](/adi-network-components/overview) utilizes an execution layer based on **zkSync OS**.

### What is Airbender?

Airbender is zkSync's next-generation proof system, purpose-built to enable efficient ZK proofs of RISC-V bytecode execution. Built on the foundation of highly optimized STARK/FRI implementations, Airbender is designed to support long-term scaling strategy by being fast, cheap, and flexible across a wide range of use cases without compromising security.

### Key Features

* RISC-V 32I+M instruction set
* AIR constraints
* Optimized DEEP STARK/FRI proofs
* Mersenne31 prime field
* Blake2s + Blake3 hash function

The name “Airbender” is a reference to AIR constraints (Arithmetic Intermediate Representation).

### Proving Pipeline

The proving process follows a structured six-stage pipeline designed for optimal performance and resource utilization:

* **Stage 1 - Witness Generation**: Computes Low-Degree Extensions (LDEs) of witness data and generates trace commitments. This stage establishes the foundational data structures for the proof.
* **Stage 2 - Lookup and Memory Argument**: Configures lookup tables and memory arguments, establishing the infrastructure for efficient verification of memory operations and table lookups.
* **Stage 3 - STARK Quotient Polynomial**: Computes the primary STARK quotient polynomial, which encodes the constraint satisfaction proof for the main circuit.
* **Stage 4 - DEEP Polynomial**: Implements FRI batching optimizations to reduce the overall proof size and verification complexity.
* **Stage 5 - FRI IOPP Generation**: Produces the final Interactive Oracle Proof of Proximity (FRI proof) that enables efficient verification.
* **Stage 6 - SNARK Wrapper:** Wrap the final FRI proof into a final FFLONK proof, which gets posted and verified on-chain.


# L3 Chains

Technical architecture for L3 chain deployment on ADI ecosystem

## Overview

An L3 chain is a Layer 3 ZK rollup that settles on the ADI Chain (L2), which itself settles on Ethereum Mainnet (L1). This creates a layered security model where L3 transactions inherit cryptographic guarantees from both L2 and L1.

{% hint style="info" %}
L3 chains inherit security guarantees from both the ADI Chain (L2) and Ethereum Mainnet (L1) through validity proofs at each layer.
{% endhint %}

{% hint style="warning" %}
The mainnet RPC at `https://rpc.adifoundation.ai/` enforces per-IP rate limits for L3 traffic. If sequencer startup hits HTTP `429` with Cloudflare `1015`, send us the outbound IPs of your operator infrastructure for allowlisting. Testnet `https://rpc.ab.testnet.adifoundation.ai/` does not enforce these limits.
{% endhint %}

**Settlement hierarchy:**

```mermaid
flowchart LR
    L3["L3 Chains (Client)"] -->|"validity proofs"| L2["ADI Chain (L2)"]
    L2 -->|"validity proofs"| L1["Ethereum Mainnet (L1)"]
```

Each L3 chain operates as an independent ZK rollup with its own sequencer, prover, and state. Multiple L3 chains can be deployed within a single L3 ecosystem, sharing common infrastructure contracts while maintaining isolated execution environments.

## Architecture

The following diagram illustrates the multi-chain L3 ecosystem architecture:

```mermaid
flowchart TB
    subgraph L1["Ethereum (L1)"]
        L1V["Verifier"]
    end

    subgraph L2["ADI Chain (L2)"]
        L2STM["STM + Bridgehub"]
        L2Contracts["Chain Contracts"]
        L2STM --> L2Contracts
    end

    subgraph L3["L3 Chains"]
        ChainA["Chain A"]
        ChainB["Chain B"]
        ChainN["Chain ..."]
    end

    ChainA & ChainB & ChainN -->|"proofs"| L2Contracts
    L2 -->|"proofs"| L1V
```

### Component Responsibilities

| Component              | Layer | Purpose                                          |
| ---------------------- | ----- | ------------------------------------------------ |
| Sequencer              | L3    | Orders transactions, produces L3 blocks          |
| Prover                 | L3    | Generates validity proofs for L3 batches         |
| Chain Contracts        | L2    | Verifies L3 proofs, stores L3 state roots        |
| StateTransitionManager | L2    | Manages chain registration and upgrades          |
| Bridgehub              | L2    | Central registry for all L3 chains               |
| L2 Prover              | L2    | Proves L2 state (including L3 settlements) to L1 |

## Settlement Flow

Each layer processes transactions through a batch lifecycle with three stages: **commit**, **prove**, and **execute**.

### Batch Lifecycle Stages

```mermaid
flowchart LR
    subgraph L3["L3 Chain"]
        TX["Transactions"] --> Blocks["Blocks"]
        Blocks --> Batches["Batches"]
    end

    subgraph Settlement["Settlement to L2"]
        Commit["1. Commit<br/>(state diffs)"]
        Prove["2. Prove<br/>(validity proof)"]
        Execute["3. Execute<br/>(finalize)"]

        Commit --> Prove --> Execute
    end

    Batches --> Commit
```

{% tabs %}
{% tab title="1. Commit" %}
The sequencer submits batch data to L2. This includes state diffs (changes to storage slots) rather than full state, optimizing data costs.

**Data submitted:**

* Storage slot changes (key-value pairs)
* Contract deployments
* L2 → L3 message hashes
  {% endtab %}

{% tab title="2. Prove" %}
The prover generates a validity proof for the committed batch. This proof cryptographically guarantees that the state transition is valid according to the L3 execution rules.

**Proof contains:**

* Execution correctness attestation
* State root transition validity
* Public inputs for verification
  {% endtab %}

{% tab title="3. Execute" %}
After proof verification, the batch is finalized on L2. The L3 state root is updated in the chain contracts.

**Result:**

* New state root stored on L2
* Batch marked as finalized
* Withdrawals become processable
  {% endtab %}
  {% endtabs %}

### Finality Propagation

Finality flows upward through the layers:

| Event                              | Confirmation Type | Timing  |
| ---------------------------------- | ----------------- | ------- |
| Transaction included in L3 block   | Soft confirmation | Seconds |
| L3 batch committed to L2           | L2 confirmation   | Minutes |
| L3 batch proved and executed on L2 | L2 finality       | Minutes |
| L2 batch proved and executed on L1 | L1 finality       | Hours   |

{% hint style="info" %}
Soft confirmations provide immediate usability. Users can transact with transferred assets immediately after submission, while cryptographic finality propagates through the layers over time.
{% endhint %}

## Proving

Both L3 and L2 layers generate validity proofs using the [Airbender](/adi-network-components/airbender) proving system (FRI/STARK → FFLONK SNARK pipeline).

### L3 vs L2 Proving

| Aspect     | L3 Proving         | L2 Proving                    |
| ---------- | ------------------ | ----------------------------- |
| Proves     | L3 batch execution | L2 execution + L3 settlements |
| Settles to | ADI Chain (L2)     | Ethereum (L1)                 |
| Operator   | Client or ADI      | ADI                           |

Each L3 chain operates its own prover independently. The L2 prover aggregates all L3 settlement activity along with native L2 transactions into proofs submitted to Ethereum.

### Hardware Requirements

Proof generation requires GPU acceleration.

### Hardware Specifications

| Component    | Minimum     | Recommended   |
| ------------ | ----------- | ------------- |
| GPU Memory   | 70 GB       | 140 GB        |
| GPU Model    | NVIDIA H100 | NVIDIA H200   |
| FRI Provers  | 1           | 2 (parallel)  |
| SNARK Prover | 1 (33 GB)   | 1 (dedicated) |

**Performance characteristics:**

* FRI proving time scales inversely with GPU memory
* SNARK proving requires approximately 33 GB regardless of total memory
* Parallel FRI + SNARK execution improves throughput by 15-20%
* Target throughput: \~15-20 TPS per prover configuration

{% hint style="info" %}
For production deployments, running FRI and SNARK provers in parallel on separate GPU partitions maximizes batch throughput.
{% endhint %}

## Key Contracts

The L3 ecosystem deploys several contract types on the settlement layer (L2).

### Bridgehub

Central registry that tracks all chains in the ecosystem. Provides chain ID to contract address mapping, cross-chain message routing, and ecosystem-wide configuration.

### StateTransitionManager (STM)

Manages the state transition logic for chains. Registers new chains in the ecosystem, handles protocol upgrades, and maintains shared verification parameters.

### Diamond Proxy

Each L3 chain has a Diamond Proxy contract implementing batch commitment and verification, state root storage, validator management, and upgrade mechanisms via the facet pattern. The Diamond pattern enables modular upgrades where individual facets (execution, getters, admin) can be replaced independently.

### Validator Timelock

Security mechanism that enforces a delay between batch commitment and execution. This prevents immediate finalization of malicious batches, allows time for monitoring systems to detect anomalies, and supports configurable delay periods per chain.

## Deployment Options

L3 chains support flexible deployment and operational models.

### Infrastructure Models

{% tabs %}
{% tab title="ADI-Managed" %}
ADI operates the full infrastructure stack:

| Component | Responsibility                         |
| --------- | -------------------------------------- |
| Sequencer | ADI operates sequencer infrastructure  |
| Prover    | ADI generates proofs for batches       |
| Contracts | ADI holds governance and operator keys |

**Best for:** Clients who want turnkey operation without infrastructure overhead.
{% endtab %}

{% tab title="Client-Operated" %}
Client operates their own infrastructure:

| Component | Responsibility                            |
| --------- | ----------------------------------------- |
| Sequencer | Client runs own sequencer nodes           |
| Prover    | Client operates GPU prover infrastructure |
| Contracts | Keys transferred to client wallets        |

**Best for:** Clients who require full control over chain operations.
{% endtab %}

{% tab title="Hybrid" %}
Split responsibilities between ADI and client:

| Component | Responsibility                            |
| --------- | ----------------------------------------- |
| Sequencer | Client or ADI (configurable)              |
| Prover    | Client or ADI (configurable)              |
| Contracts | Governance to client, operations with ADI |

**Best for:** Clients who want governance control with managed operations.
{% endtab %}
{% endtabs %}

### Ownership Configuration

Contracts are deployed with role-based access:

| Role             | Responsibility                                             |
| ---------------- | ---------------------------------------------------------- |
| Governor         | Protocol upgrades, parameter changes                       |
| Admin            | Emergency operations, validator management                 |
| Operator         | Batch commitment (`PRECOMMITTER`, `COMMITTER`, `REVERTER`) |
| Prove Operator   | Proof submission (`PROVER` role)                           |
| Execute Operator | Batch execution (`EXECUTOR` role)                          |

**Ownership transfer options:**

* **Full transfer**: All roles moved to client multisig
* **Partial transfer**: Governance to client, operations with ADI
* **Gradual handover**: Phased transition over time

### Ecosystem Model

The L3 ecosystem follows a shared-infrastructure model:

```
L3 Ecosystem (deployed once)
├── Bridgehub (shared)
├── StateTransitionManager (shared)
└── Chains
    ├── Chain A (client-specific contracts)
    ├── Chain B (client-specific contracts)
    └── Chain N (client-specific contracts)
```

New chains are added incrementally to an existing ecosystem deployment. Each chain has isolated state and execution while sharing the ecosystem registry and upgrade infrastructure.

## Technical Requirements

### Settlement Layer

* **RPC Endpoint**: ADI Chain (L2) JSON-RPC URL
* **Chain ID**: Settlement layer chain ID for transaction signing
* **Gas Funding**: L2 native token (ADI) for operator transactions

### Prover Infrastructure

The prover generates validity proofs for L3 batches. GPU acceleration is required for production workloads.

| Requirement | Specification                          |
| ----------- | -------------------------------------- |
| GPU         | NVIDIA H100/H200 with 70-140 GB memory |
| CPU         | Modern multi-core processor            |
| RAM         | 64+ GB system memory                   |
| Storage     | NVMe SSD for witness data              |
| Network     | Low-latency connection to L2 RPC       |

### Sequencer Infrastructure

The sequencer orders transactions and produces L3 blocks. It must be highly available to maintain chain liveness.

| Requirement | Specification                              |
| ----------- | ------------------------------------------ |
| CPU         | Multi-core processor (8+ cores)            |
| RAM         | 32+ GB                                     |
| Storage     | SSD with sufficient capacity for state     |
| Network     | Public endpoint for transaction submission |

### Operator Wallets

Multiple wallets are required to operate an L3 chain. Each serves a specific role in the batch lifecycle.

| Wallet           | Purpose                     | Funding                       |
| ---------------- | --------------------------- | ----------------------------- |
| Operator         | Commits and reverts batches | L2 Native token (ADI) for gas |
| Prove Operator   | Submits validity proofs     | L2 Native token (ADI) for gas |
| Execute Operator | Executes verified batches   | L2 Native token (ADI) for gas |
| Governor         | Protocol governance         | Minimal (infrequent use)      |

## Average Gas per Settlement Stage

Approximate gas consumed by each stage of the settlement flow:

| Stage     | Avg. Gas      |
| --------- | ------------- |
| Commit    | \~136,000     |
| Prove     | \~494,000     |
| Execute   | \~117,000     |
| **Total** | **\~747,000** |

Figures are measured against the L1 settlement layer; the same call pattern applies at any settlement boundary, so the magnitudes are broadly representative.


# CLI

Deploy and manage L3 chains using the ADI CLI

The ADI CLI is a Rust-based tool that manages the full lifecycle of L3 chain deployment. It runs all operations inside pre-built Docker toolkit containers (zkstack, foundry-zksync, era-contracts) and outputs the resulting state files and generated wallets to your host machine.

**Source**: [ADI-Foundation-Labs/ADI-CLI](https://github.com/ADI-Foundation-Labs/ADI-CLI)\
**Changelog**: [CHANGELOG.md](https://github.com/ADI-Foundation-Labs/ADI-CLI/blob/main/CHANGELOG.md)

## Prerequisites

| Requirement   | Details                                                                                                                                                              |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Docker        | Running daemon. The CLI pulls and runs toolkit images automatically                                                                                                  |
| Rust          | Install via [rustup](https://rustup.rs/)                                                                                                                             |
| Funded wallet | \~270 ADI tokens on the settlement layer for deployment gas costs. Testnet faucet: [faucet.ab.testnet.adifoundation.ai](https://faucet.ab.testnet.adifoundation.ai/) |

## Installation

```bash
cargo install --git https://github.com/ADI-Foundation-Labs/ADI-CLI
```

Verify the installation:

```bash
adi version
```

Optionally generate shell completions:

{% tabs %}
{% tab title="Zsh" %}

```bash
mkdir -p ~/.oh-my-zsh/completions
adi completions zsh > ~/.oh-my-zsh/completions/_adi
```

{% endtab %}

{% tab title="Bash" %}

```bash
mkdir -p ~/.local/share/bash-completion/completions/
adi completions bash > ~/.local/share/bash-completion/completions/adi
```

{% endtab %}
{% endtabs %}

Restart your shell to activate:

{% tabs %}
{% tab title="Zsh" %}

```bash
source ~/.zshrc
```

{% endtab %}

{% tab title="Bash" %}

```bash
source ~/.bashrc
```

{% endtab %}
{% endtabs %}

## Configuration

The CLI reads configuration from a YAML file. Default location is `~/.adi_cli/.adi.yml` — the same directory that holds ecosystem state. The legacy `~/.adi.yml` location still works as a fallback, but is deprecated.

{% hint style="info" %}
Override the config path with the `--config` flag or `ADI_CONFIG` environment variable.
{% endhint %}

### Minimal Config

```yaml
protocol_version: v0.30.1

ecosystem:
  name: my_ecosystem
  rpc_url: https://rpc.ab.testnet.adifoundation.ai
  chains:
    - name: my_chain
      chain_id: 222
      prover_mode: gpu   # no-proofs (testing) | gpu (production)
      base_token_address: "0x..." # custom gas token address on the settlement layer
```

{% hint style="info" %}
Ecosystem and chain names must be **snake\_case** (lowercase letters, digits, single underscores) — the CLI rejects names like `my-chain`.
{% endhint %}

{% hint style="info" %}
`protocol_version` determines which Docker toolkit image the CLI uses. Each protocol version is tied to a specific server version (e.g., protocol `v0.30.1` works with server `v13.1.0-b1`).
{% endhint %}

{% hint style="info" %}
If `base_token_address` is omitted, ADI is the gas token. You cannot change this after deployment. To use a custom token, set `base_token_address` to your ERC20 address on the settlement layer before `adi init`.
{% endhint %}

Wallet funding amounts and the funder key are not required in the config — they can be provided via CLI flags or environment variables during `adi deploy`. The only required config fields are the ecosystem definition and chain parameters.

### Key Options

| Option                       | Scope           | Description                                                                        |
| ---------------------------- | --------------- | ---------------------------------------------------------------------------------- |
| `settlement`                 | ecosystem       | `l1` (chain is an L2) or `l2` (chain is an L3; default). For L3s on ADI, keep `l2` |
| `prover_mode`                | chain           | `no-proofs` (testing, default) or `gpu` (production)                               |
| `pubdata_mode`               | chain           | `blobs`, `calldata` (default), or `custom_da`. `blobs` requires `settlement: l1`   |
| `base_token_address`         | chain           | Custom Gas Token (CGT) contract address on the settlement layer                    |
| `governor_cgt_units`         | funding         | Amount of CGT to fund the governor wallet                                          |
| `operators.operator`         | chain/global    | Address for batch commit/revert roles                                              |
| `operators.prove_operator`   | chain/global    | Address for proof submission                                                       |
| `operators.execute_operator` | chain/global    | Address for batch execution                                                        |
| `ownership.new_owner`        | ecosystem/chain | Transfer ownership to this address after deploy                                    |
| `ownership.private_key`      | ecosystem/chain | If provided, ownership is accepted automatically                                   |
| `gas_multiplier`             | global          | Gas price buffer percentage (default: 200)                                         |

Set the funder wallet private key via environment variable:

```bash
export ADI_FUNDER_KEY="0x..."
```

{% hint style="warning" %}
Never put private keys directly in the config file. Use `ADI_FUNDER_KEY` for the funder and `ownership.private_key` only when automatic acceptance is needed.
{% endhint %}

For the full annotated config, environment variables, and S3 state sync, see the [CLI Configuration Reference](/references/cli-configuration).

### Ownership Transfer

{% hint style="warning" %}
When `ownership.new_owner` is set:

* **Address only** — ownership is transferred after deployment. The new owner must run `adi accept` to complete the transfer.
* **Address + `private_key`** — ownership is transferred and accepted automatically during deployment.
  {% endhint %}

Verify the merged configuration:

```bash
adi config
```

## Deployment Workflow

### 1. Initialize

The `init` command creates the ecosystem and chain configuration. It spins up a Docker container, generates wallets and configs inside it, then drops the resulting state files to your host.

```bash
adi init --chain my_chain
```

| Flag      | Description                                 |
| --------- | ------------------------------------------- |
| `--chain` | Select chain from config `chains[]` by name |
| `--force` | Overwrite existing ecosystem state          |
| `--yes`   | Skip confirmation prompts                   |

{% hint style="info" %}
If you have a single ecosystem and chain configured, the CLI selects them automatically. Otherwise it will prompt you to choose. You can also specify `--chain` explicitly.
{% endhint %}

Example output:

```
┌   ADI Init
│
✱  Select a chain
│  my_chain
│
◈  Validating chain ID against settlement layer...
│
◆  Chain ID 222 validated (settlement layer: 99999)
│
✱  Protocol version: v0.30.1 ──────╮
│                                  │
│  Ecosystem: adi_ecosystem        │
│  Chain: my_chain (ID: 222)       │
│  Prover mode: gpu                │
├──────────────────────────────────╯
│
◈  Connecting to Docker...
│
◈  Running zkstack ecosystem create...
│
◆  Completed in 0s
│
◈  Importing ecosystem state...
│
◆  Ecosystem state imported successfully
│
◈  Validating imported state...
│
◆  State validated: 1 chain(s) found
│
◈  Location: ~/.adi_cli/state/adi_ecosystem
│
└   Ecosystem 'adi_ecosystem' initialized successfully!
```

After init, state is written to `~/.adi_cli/state/<ecosystem>/`:

```
~/.adi_cli/state/my_ecosystem/
├── ZkStack.yaml
├── configs/
│   ├── wallets.yaml        # generated ecosystem wallets
│   ├── contracts.yaml
│   └── ...
└── chains/my_chain/
    ├── ZkStack.yaml
    └── configs/
        ├── wallets.yaml    # generated chain wallets (operators)
        ├── genesis.yaml
        └── ...
```

{% hint style="info" %}
Wallets are plain EOAs. You can reuse operator keys across L3 deployments by copying `wallets.yaml` and `secrets.yaml` from a previous deployment. Do not reuse `genesis.yaml` or `contracts.yaml`. Those files are chain-specific.
{% endhint %}

{% hint style="info" %}
The generated `genesis.json` used by the server includes `l1_chain_id` for your settlement layer. For L3s on ADI, that is `36900` on mainnet or `99999` on testnet. `adi init` sets this automatically. Do not edit it manually.
{% endhint %}

### 2. Deploy

The `deploy` command funds the generated wallets and deploys all contracts to the settlement layer (L2).

The funder private key can be provided in three ways (in priority order): `--funder-key` flag, `ADI_FUNDER_KEY` env var, or `funding.funder_key` in config.

```bash
export ADI_FUNDER_KEY="0x..."
adi deploy
```

Example output:

```
┌   ADI Deploy
│
✱  Deployment target ───────────────────────────────────────────────╮
│                                                                   │
│  Ecosystem: adi_ecosystem                                         │
│  Chain: my_chain (L3)                                             │
│  Settlement layer RPC: https://rpc.ab.testnet.adifoundation.ai/   │
├───────────────────────────────────────────────────────────────────╯
│
...
│
✱  Funding Summary ──────────────────────╮
│                                        │
│  Transfers needed: 6                   │
│  Total ETH to transfer: 270.0000 ETH   │
│  Estimated gas cost: 0.1401 ETH        │
│  Total ETH required: 270.1401 ETH      │
│  Funder balance: 1979.2569 ETH         │
│  Status: Sufficient balance            │
├────────────────────────────────────────╯
│
...
│
✱  Deployed Contracts ──────────────────────────────────────────────╮
│                                                                   │
│  Diamond proxy: 0x5b0323f95682228DE5b0db338bA9470D2D511F80        │
│  Validator timelock: 0x686B1825b998007825ad4A34E0c00fF361D0667c   │
│  Chain admin: 0x195Ec0826b0686D03E21De802588a28f253F69C7          │
├───────────────────────────────────────────────────────────────────╯
│
✱  Deployment Summary ─────────────────────────────────────────╮
│                                                              │
│  Ecosystem: adi_ecosystem                                    │
│  Chain: my_chain                                             │
│  Diamond proxy: 0x5b0323f95682228DE5b0db338bA9470D2D511F80   │
├──────────────────────────────────────────────────────────────╯
│
└   Deployment complete! You can now start containers and operate the rollup.
```

This command:

1. Connects to the settlement layer and checks wallet balances
2. Funds deployer, governor, and operator wallets from the funder
3. Deploys ecosystem and chain contracts (Bridgehub, STM, Diamond Proxy)
4. Configures validator roles on chain contracts
5. Handles ownership transfer if configured

## State

All state lives in `~/.adi_cli/state/`. This directory contains wallets (with private keys), deployed contract addresses, and chain configurations. Back it up.

State can optionally sync to S3-compatible storage with `adi state sync` / `adi state restore`. See the [CLI Configuration Reference](/references/cli-configuration#s3-state-synchronization) for details.

## Next Steps

Once contracts are deployed, set up the chain infrastructure (sequencer, prover, explorer, bridge) using Docker Compose:

{% content-ref url="/pages/yKijt6uTQjETWKIBRKDy" %}
[Run a Rollup with Docker Compose](/adi-network-components/overview-1/run-a-rollup)
{% endcontent-ref %}

Additional CLI commands for post-deployment:

| Command             | Purpose                                                   |
| ------------------- | --------------------------------------------------------- |
| `adi verify`        | Verify deployed contracts on block explorers              |
| `adi server-params` | Output server parameters for docker-compose configuration |
| `adi owners`        | Display current contract owners                           |
| `adi transfer`      | Transfer ownership to a new address                       |
| `adi upgrade`       | Upgrade contracts to a new protocol version               |


# Run a Rollup with Docker Compose

Deploy a complete ADI Rollup — sequencer, external node, GPU provers, bridge, and block explorers — using Docker Compose.

This guide walks you through deploying a complete ADI Rollup stack using Docker Compose. The deployment is modular — start the core chain, then layer on infrastructure services as needed.

The stack is split into three compose files:

* [**Core Stack**](/adi-network-components/overview-1/run-a-rollup/core-stack) — Sequencer, External Node, and GPU Provers (FRI + SNARK)
* [**Infrastructure Stack**](/adi-network-components/overview-1/run-a-rollup/infrastructure-stack) — Block Explorer and Bridge
* [**Blockscout**](/adi-network-components/overview-1/run-a-rollup/blockscout) — Optional alternative block explorer with contract verification

```mermaid
%%{init: {'theme': 'dark'}}%%
graph TB
    subgraph Core["Core Chain Stack"]
        SEQ["Sequencer :3050"]
        EN["External Node :3051"]
        FRI["FRI Prover (GPU)"]
        SNARK["SNARK Prover (GPU)"]
        SEQ -->|block replay| EN
        SEQ -->|prover API :3320| FRI
        SEQ -->|prover API :3320| SNARK
    end

    subgraph Infra["Infrastructure Stack"]
        BR["Bridge :3000"]
        EXP["Block Explorer :3010"]
        API["Explorer API :3020"]
        WRK["Explorer Worker"]
        DF["Data Fetcher :3040"]
        PG["PostgreSQL"]
        WRK --> PG
        API --> PG
        WRK --> DF
        EXP --> API
        BR -->|requires| API
    end

    subgraph BS["Blockscout (Optional)"]
        BSF["Frontend :4000"]
        BSB["Backend"]
        BSV["Verifier"]
        BSST["Stats"]
        BSF --> BSB
        BSB --> BSV
        BSB --> BSST
    end

    SEQ -->|RPC| WRK
    SEQ -->|RPC| DF
    SEQ -->|RPC| BSB
    SEQ -.->|L1 settlement| L1["Ethereum L1"]

    style Core fill:#1a1a2e,stroke:#4a90d9,color:#fff
    style Infra fill:#1a1a2e,stroke:#5bb974,color:#fff
    style BS fill:#1a1a2e,stroke:#f5a623,color:#fff
    style L1 fill:#4a90d9,stroke:#3a7bc8,color:#fff
```

> **Warning:** The **Bridge requires the Block Explorer** to function. Blockscout is an additional explorer with contract verification support, but it cannot replace the Block Explorer for bridge operations.

***

## Prerequisites

### Hardware Requirements

| Component                          | CPU      | RAM    | GPU VRAM | Storage    |
| ---------------------------------- | -------- | ------ | -------- | ---------- |
| Sequencer                          | 8+ cores | 16 GB+ | —        | 100 GB SSD |
| External Node                      | 4+ cores | 8 GB+  | —        | 100 GB SSD |
| FRI Prover                         | 4+ cores | 16 GB+ | 32 GB+   | 50 GB      |
| SNARK Prover                       | 4+ cores | 32 GB+ | 32 GB+   | 50 GB      |
| Infrastructure (Explorer + Bridge) | 2+ cores | 4 GB+  | —        | 20 GB      |

> **Note:** The entire stack (sequencer + provers) can run on a single machine with \~80 GB of system RAM. SNARK provers with less than 32 GB VRAM will fail with out-of-memory errors.

### Software Requirements

* **Docker Engine** 24.0+ with Compose V2
* **NVIDIA Container Toolkit** — required for GPU provers
* **NVIDIA drivers** with CUDA 12.2+ support
* A `genesis.json` configuration file for your chain
* A settlement-layer RPC endpoint. For L3s on ADI, use ADI Chain

### Verify Docker and GPU Setup

```bash
# Docker
docker --version
docker compose version

# NVIDIA (on prover nodes)
nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.2.0-base-ubuntu22.04 nvidia-smi
```

> **Tip:** If both commands return version information and the GPU test container runs successfully, your environment is ready.

***

## Directory Structure

Create the following layout on your server:

```
adi-rollup/
├── docker-compose.yml              # Core stack
├── docker-compose.infra.yml        # Infrastructure stack
├── docker-compose.blockscout.yml   # Optional Blockscout
├── .env                            # Environment variables
├── genesis.json                    # Chain genesis configuration
└── volumes/                        # Persistent data (auto-created)
    ├── chain/                      # Sequencer state
    ├── en_chain/                   # External Node state
    ├── shared/                     # Shared prover artifacts
    └── prover/                     # Prover output
```

```bash
mkdir -p adi-rollup && cd adi-rollup
mkdir -p volumes/{chain,en_chain,shared,prover}
```

## Save `genesis.json`

Place your chain’s `genesis.json` file in the project root so the compose files can mount it into the server containers:

```bash
# From your chain configs
cp /path/to/your/chain/configs/genesis.json ./genesis.json
```

{% hint style="info" %}
`l1_chain_id` in `genesis.json` is the chain ID of your settlement layer. For L3s on ADI, use `36900` on mainnet or `99999` on testnet. `adi init` sets this automatically. Do not edit it manually.
{% endhint %}

***

## Environment Configuration

Create a `.env` file with your chain parameters. All compose files read from this shared configuration.

```bash
cat > .env << 'EOF'
# ── Chain Identity ──────────────────────────────────
CHAIN_NAME="My ADI Rollup"
CHAIN_SHORT_NAME=myrollup
CHAIN_ID=444
CHAIN_CURRENCY_SYMBOL=ADI
CHAIN_CURRENCY_NAME="ADI Token"

# ── Settlement Layer Connection ───────────────────
L1_RPC_URL=https://rpc.ab.testnet.adifoundation.ai

# ── Contract Addresses (from chain deployment) ─────
BRIDGEHUB_ADDRESS=0x274e31b8fc1ef5de0de9efcefa1b7097c1cc4560
BYTECODE_SUPPLIER_ADDRESS=0xa21084dd19e51ab1fdbd21fab4fcc2f73eb3cea0
DIAMOND_PROXY_ADDRESS=0xfea43989ac9cc0163eab599e1fccd47c478641ed

# ── L1 Operator Keys ──────────────────────────────
OPERATOR_COMMIT_PK=0x<your-commit-private-key>
OPERATOR_PROVE_PK=0x<your-prove-private-key>
OPERATOR_EXECUTE_PK=0x<your-execute-private-key>

# ── Image Versions ─────────────────────────────────
SERVER_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/server:v0.13.0-b1
PROVER_FRI_IMAGE=ghcr.io/matter-labs/zksync-os-prover-fri:v0.7.0
PROVER_SNARK_IMAGE=ghcr.io/matter-labs/zksync-os-prover-snark:v0.7.0
EXPLORER_APP_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/explorer/app:v1.0.0
EXPLORER_API_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/explorer/api:v1.0.0
EXPLORER_WORKER_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/explorer/worker:v1.0.0
EXPLORER_DATA_FETCHER_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/explorer/data-fetcher:v1.0.0
BRIDGE_IMAGE=harbor.sde.adifoundation.ai/adi-public/chain/bridge:v1.0.0

# ── GPU Devices ────────────────────────────────────
# Run `nvidia-smi -L` to list GPU or MIG instance UUIDs
GPU_DEVICE_FRI=GPU-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
GPU_DEVICE_SNARK=GPU-yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy

# ── Blockscout (optional) ──────────────────────────
SECRET_KEY_BASE=<generate-with: openssl rand -hex 32>
BLOCKSCOUT_DB_PASSWORD=<strong-password>
EOF
```

{% hint style="info" %}
For L3 chains, `L1_RPC_URL` points to your settlement layer. On ADI, that means ADI Chain. For L2 external nodes, it points to Ethereum. The variable name is inherited from zkSync OS upstream.
{% endhint %}

> **Warning:** Never commit `.env` to version control. It contains private keys and secrets. Use dedicated operator wallets — never use keys holding significant funds.

***

## Next Steps

Once your `.env` is configured, proceed to deploy each stack:

1. [Core Stack](/adi-network-components/overview-1/run-a-rollup/core-stack) — Sequencer, External Node, and GPU Provers
2. [Infrastructure Stack](/adi-network-components/overview-1/run-a-rollup/infrastructure-stack) — Block Explorer and Bridge
3. [Blockscout Explorer](/adi-network-components/overview-1/run-a-rollup/blockscout) — Optional, for contract verification
4. [Operations](/adi-network-components/overview-1/run-a-rollup/operations) — Running, monitoring, GPU configuration, and troubleshooting
5. [Configuration Reference](/adi-network-components/overview-1/run-a-rollup/configuration-reference) — Full list of environment variables


# Core Stack

Deploy the core chain — sequencer, external node, and GPU provers for ZK proof generation.

The core stack runs the **Sequencer** (block production), **External Node** (read replica), and **GPU Provers** (ZK proof generation).

```mermaid
%%{init: {'theme': 'dark'}}%%
sequenceDiagram
    participant L1 as Settlement Layer
    participant SEQ as Sequencer
    participant EN as External Node
    participant FRI as FRI Prover
    participant SNARK as SNARK Prover

    L1->>SEQ: L1 events (deposits, batches)
    SEQ->>SEQ: produce blocks
    SEQ->>EN: block replay (:3053)
    SEQ->>FRI: prover jobs (:3320)
    FRI->>SEQ: FRI proofs
    SEQ->>SNARK: SNARK jobs (:3320)
    SNARK->>SEQ: final proofs
    SEQ->>L1: commit → prove → execute
```

***

## Docker Compose

Create `docker-compose.yml`:

```yaml
services:
  # ─────────────────────────────────────────────
  # Sequencer — produces blocks and batches
  # ─────────────────────────────────────────────
  server:
    image: ${SERVER_IMAGE}
    user: "0:0"
    container_name: ${CHAIN_SHORT_NAME}-sequencer
    restart: unless-stopped
    network_mode: host
    working_dir: /app
    environment:
      # Fee overrides
      sequencer_base_fee_override: "0x3e8"
      sequencer_pubdata_price_override: "0x1"
      sequencer_native_price_override: "0x1"
      sequencer_block_dump_path: "/chain/db/block_dumps"
      sequencer_block_replay_server_address: "0.0.0.0:3053"

      # Storage
      general_rocks_db_path: "/chain/db/node1"

      # Settlement-layer connection
      # For L3s on ADI, L1_RPC_URL points to ADI Chain.
      general_l1_rpc_url: "${L1_RPC_URL}"

      # Genesis
      genesis_bridgehub_address: "${BRIDGEHUB_ADDRESS}"
      genesis_bytecode_supplier_address: "${BYTECODE_SUPPLIER_ADDRESS}"
      genesis_genesis_input_path: "/genesis/genesis.json"
      genesis_chain_id: ${CHAIN_ID}

      # L1 gas
      l1_sender_max_fee_per_blob_gas_gwei: 20

      # Prover API
      prover_api_object_store_file_backed_base_path: "/shared"
      prover_api_fake_fri_provers_enabled: "false"
      prover_api_fake_snark_provers_enabled: "false"
      prover_api_address: "0.0.0.0:3320"
      prover_input_generator_maximum_in_flight_blocks: 120
      prover_api_fri_job_timeout: "1200s"
      prover_api_snark_job_timeout: "1200s"

      # Batching
      batcher_batch_timeout: "60s"
      batcher_blocks_per_batch_limit: 1400
      sequencer_max_transactions_in_block: 3000

      # L1 operator keys
      l1_sender_operator_commit_pk: ${OPERATOR_COMMIT_PK}
      l1_sender_operator_prove_pk: ${OPERATOR_PROVE_PK}
      l1_sender_operator_execute_pk: ${OPERATOR_EXECUTE_PK}
      l1_sender_max_fee_per_gas_gwei: 20
      l1_sender_max_priority_fee_per_gas_gwei: 10
      l1_sender_fusaka_upgrade_timestamp: 0
      l1_sender_pubdata_mode: "Calldata"

      RUST_LOG: "info,zksync_os_server=info,zksync_os_sequencer=info,zksync_os_merkle_tree=info,zksync_os_priority_tree=info"
    volumes:
      - ./volumes/chain:/chain
      - ./volumes/shared:/shared
      - ./genesis.json:/genesis/genesis.json:ro

  # ─────────────────────────────────────────────
  # External Node — syncs from sequencer
  # ─────────────────────────────────────────────
  external-node:
    image: ${SERVER_IMAGE}
    user: "0:0"
    container_name: ${CHAIN_SHORT_NAME}-external-node
    restart: unless-stopped
    network_mode: host
    working_dir: /app
    depends_on: [server]
    environment:
      # EN mode
      sequencer_block_replay_download_address: "http://127.0.0.1:3053"
      general_main_node_rpc_url: "http://127.0.0.1:3050"

      # EN service ports (avoid conflicts with sequencer)
      sequencer_block_replay_server_address: "0.0.0.0:3054"
      rpc_address: "0.0.0.0:3051"
      status_server_address: "0.0.0.0:3073"
      observability_prometheus_port: "3316"

      # Fee overrides (must match sequencer)
      sequencer_base_fee_override: "0x3E8"
      sequencer_pubdata_price_override: "0x1"
      sequencer_native_price_override: "0x1"

      # Storage
      general_rocks_db_path: "/chain/db/node1"
      sequencer_block_dump_path: "/chain/db/block_dumps"
      prover_api_object_store_file_backed_base_path: "/shared"

      # Settlement layer / Genesis
      general_l1_rpc_url: "${L1_RPC_URL}"
      genesis_bridgehub_address: "${BRIDGEHUB_ADDRESS}"
      genesis_bytecode_supplier_address: "${BYTECODE_SUPPLIER_ADDRESS}"
      genesis_genesis_input_path: "/genesis/genesis.json"
      genesis_chain_id: ${CHAIN_ID}

      # Private API
      private_api_address: "127.0.0.1:8547"

      RUST_LOG: "info,zksync_os_server=info,zksync_os_sequencer=info,zksync_os_merkle_tree=info,zksync_os_priority_tree=info"
    volumes:
      - ./volumes/en_chain:/chain
      - ./volumes/shared:/shared
      - ./genesis.json:/genesis/genesis.json:ro

  # ─────────────────────────────────────────────
  # FRI Prover — generates FRI proofs (GPU)
  # ─────────────────────────────────────────────
  fri-prover:
    image: ${PROVER_FRI_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-fri-prover
    restart: unless-stopped
    network_mode: host
    depends_on: [server]
    environment:
      RUST_LOG: "info"
    volumes:
      - ./volumes/prover:/prover
    command:
      - "--base-url"
      - "http://127.0.0.1:3320"
      - "--app-bin-path"
      - "/multiblock_batch.bin"
      - "--enabled-logging"
      - "--prover-name"
      - "fri-prover-1"
      - "--prometheus-port"
      - "3124"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["${GPU_DEVICE_FRI}"]
              capabilities: [gpu]

  # ─────────────────────────────────────────────
  # SNARK Prover — generates final SNARK proofs (GPU)
  # ─────────────────────────────────────────────
  snark-prover:
    image: ${PROVER_SNARK_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-snark-prover
    restart: unless-stopped
    network_mode: host
    depends_on: [server]
    environment:
      RUST_MIN_STACK: "267108864"
    volumes:
      - ./volumes/prover:/prover
    command:
      - "run-prover"
      - "--sequencer-url"
      - "http://127.0.0.1:3320"
      - "--binary-path"
      - "/multiblock_batch.bin"
      - "--trusted-setup-file"
      - "/setup_compact.key"
      - "--output-dir"
      - "/prover"
      - "--prover-name"
      - "snark-prover-1"
      - "--prometheus-port"
      - "3126"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["${GPU_DEVICE_SNARK}"]
              capabilities: [gpu]
```

### Public RPC tuning

The default watcher settings target local development against a local reth node. They are too aggressive for most public RPC endpoints.

Set these values on the sequencer and any external nodes that talk to a public settlement RPC:

```yaml
l1_watcher_poll_interval: "2s"
l1_watcher_max_blocks_to_process: 10
```

Defaults are `100ms` and `1000`. Keep those only for local development.

### Multi-VM deployments

This sample is single-host only. It uses `depends_on` and `127.0.0.1` addresses.

For a multi-VM layout, start the sequencer first. Wait until this succeeds:

```bash
curl -s -X POST http://<seq-ip>:3050 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

Then start the RPC nodes. External nodes need both of these sequencer endpoints before they can sync:

```yaml
sequencer_block_replay_download_address: "http://<seq-ip>:3053"
general_main_node_rpc_url: "http://<seq-ip>:3050"
```

Open ports `3050` and `3053` between hosts.

{% hint style="warning" %}
`restart: unless-stopped` is the right default for transient RPC outages such as upstream `502` responses. If startup is failing with `429` rate limits, set `restart: "no"` until your outbound IP is allowlisted. Restore `restart: unless-stopped` after allowlisting.
{% endhint %}

***

## Start

```bash
docker compose up -d
```

## Verify

```bash
# Check all containers are running
docker compose ps

# Follow sequencer logs
docker compose logs -f server

# Test the RPC endpoint
curl -s -X POST http://localhost:3050 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

> **Tip:** A successful response returns your chain ID in hex (e.g., `0x1bc` for chain ID 444). The External Node RPC is available at `http://localhost:3051`.


# Infrastructure Stack

Deploy the Block Explorer and Bridge for your ADI Rollup.

The infrastructure stack provides the **Block Explorer** and the **Bridge**. The Bridge depends on the Block Explorer API to display transaction history and block data, so both must run together.

```mermaid
%%{init: {'theme': 'dark'}}%%
graph LR
    RPC["Sequencer RPC :3050"] --> DF["Data Fetcher :3040"]
    RPC --> WRK["Worker (indexer)"]
    DF --> WRK
    WRK --> PG["PostgreSQL"]
    PG --> API["Explorer API :3020"]
    API --> APP["Explorer App :3010"]
    API --> BR["Bridge :3000"]

    style RPC fill:#4a90d9,stroke:#3a7bc8,color:#fff
    style APP fill:#5bb974,stroke:#4a9f6a,color:#000
    style BR fill:#5bb974,stroke:#4a9f6a,color:#000
    style PG fill:#f5a623,stroke:#d4911e,color:#000
```

***

## Docker Compose

Create `docker-compose.infra.yml`:

```yaml
services:
  # ─────────────────────────────────────────────
  # PostgreSQL — explorer database
  # ─────────────────────────────────────────────
  postgres:
    image: postgres:14
    container_name: ${CHAIN_SHORT_NAME}-explorer-db
    restart: unless-stopped
    volumes:
      - explorer_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: block-explorer

  # ─────────────────────────────────────────────
  # Explorer — Data Fetcher
  # ─────────────────────────────────────────────
  explorer-data-fetcher:
    image: ${EXPLORER_DATA_FETCHER_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-explorer-data-fetcher
    restart: unless-stopped
    ports:
      - "3040:3040"
    environment:
      PORT: 3040
      LOG_LEVEL: verbose
      BLOCKCHAIN_RPC_URL: http://host.docker.internal:3050
      RPC_BATCH_MAX_COUNT: 50
      RPC_CALLS_CONNECTION_QUICK_TIMEOUT: 30000
    extra_hosts:
      - "host.docker.internal:host-gateway"

  # ─────────────────────────────────────────────
  # Explorer — Worker (blockchain indexer)
  # ─────────────────────────────────────────────
  explorer-worker:
    image: ${EXPLORER_WORKER_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-explorer-worker
    restart: unless-stopped
    ports:
      - "3001:3001"
    environment:
      PORT: 3001
      LOG_LEVEL: verbose
      DATABASE_HOST: postgres
      DATABASE_USER: postgres
      DATABASE_PASSWORD: postgres
      DATABASE_NAME: block-explorer
      BLOCKCHAIN_RPC_URL: http://host.docker.internal:3050
      DATA_FETCHER_URL: http://explorer-data-fetcher:3040
      BASE_TOKEN_SYMBOL: ${CHAIN_CURRENCY_SYMBOL:-ADI}
      BASE_TOKEN_NAME: ${CHAIN_CURRENCY_NAME:-ADI Token}
      BASE_TOKEN_ICON_URL: /images/icons/adi-logo.svg
      SETTLEMENT_RPC_URL: ${L1_RPC_URL:-https://rpc.ab.testnet.adifoundation.ai}
      DIAMOND_PROXY_ADDRESS: ${DIAMOND_PROXY_ADDRESS}
      SETTLEMENT_BATCH_INDEXER_POLLING_INTERVAL: 10000
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      postgres:
        condition: service_healthy

  # ─────────────────────────────────────────────
  # Explorer — API
  # ─────────────────────────────────────────────
  explorer-api:
    image: ${EXPLORER_API_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-explorer-api
    restart: unless-stopped
    ports:
      - "3020:3020"
    environment:
      PORT: 3020
      LOG_LEVEL: verbose
      DATABASE_HOST: postgres
      DATABASE_USER: postgres
      DATABASE_PASSWORD: postgres
      DATABASE_NAME: block-explorer
      BASE_TOKEN_SYMBOL: ${CHAIN_CURRENCY_SYMBOL:-ADI}
      BASE_TOKEN_NAME: ${CHAIN_CURRENCY_NAME:-ADI Token}
      BASE_TOKEN_L1_ADDRESS: "0x000000000000000000000000000000000000800A"
      BASE_TOKEN_ICON_URL: /images/icons/adi-logo.svg
    depends_on:
      - explorer-worker

  # ─────────────────────────────────────────────
  # Explorer — App (frontend)
  # ─────────────────────────────────────────────
  explorer-app:
    image: ${EXPLORER_APP_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-explorer-app
    restart: unless-stopped
    ports:
      - "3010:3010"
    environment:
      VITE_BRAND_NAME: ADI
      APP_NETWORK_NAME: "${CHAIN_NAME:-ADI Chain}"
      APP_L2_CHAIN_ID: ${CHAIN_ID:-444}
      APP_RPC_URL: http://localhost:3050
      APP_API_URL: http://localhost:3020
      APP_BASE_TOKEN_ADDRESS: "0x000000000000000000000000000000000000800A"
      APP_L1_EXPLORER_URL: https://explorer.ab.testnet.adifoundation.ai
      APP_BRIDGE_URL: http://localhost:3000
      APP_SETTLEMENT_CHAIN_NAME: "ADI Testnet"
      APP_SETTLEMENT_CHAIN_ID: 99999
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      - explorer-api

  # ─────────────────────────────────────────────
  # Bridge (dApp Portal)
  # ─────────────────────────────────────────────
  bridge:
    image: ${BRIDGE_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-bridge
    restart: unless-stopped
    ports:
      - "3000:3000"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      RUNTIME_CONFIG: >
        {
          "nodeType": "hyperchain",
          "hyperchainsConfig": [
            {
              "network": {
                "id": ${CHAIN_ID:-444},
                "name": "${CHAIN_NAME:-ADI Chain}",
                "key": "${CHAIN_SHORT_NAME:-myrollup}",
                "rpcUrl": "http://localhost:3050/",
                "blockExplorerUrl": "http://localhost:3010/",
                "blockExplorerApi": "http://localhost:3020",
                "l1BlockExplorerApi": "https://explorer-api.ab.testnet.adifoundation.ai",
                "nativeTokenBridgingOnly": false,
                "nativeCurrency": {
                  "name": "${CHAIN_CURRENCY_NAME:-ADI Token}",
                  "symbol": "${CHAIN_CURRENCY_SYMBOL:-ADI}",
                  "decimals": 18
                },
                "l1Network": {
                  "id": 99999,
                  "name": "ADI OS Testnet",
                  "network": "adi_testnet",
                  "nativeCurrency": {
                    "name": "ADI Token",
                    "symbol": "ADI",
                    "decimals": 18
                  },
                  "rpcUrls": {
                    "default": {
                      "http": ["${L1_RPC_URL:-https://rpc.ab.testnet.adifoundation.ai}"]
                    },
                    "public": {
                      "http": ["${L1_RPC_URL:-https://rpc.ab.testnet.adifoundation.ai}"]
                    }
                  }
                }
              },
              "tokens": [
                {
                  "address": "0x000000000000000000000000000000000000800A",
                  "l1Address": "0x0000000000000000000000000000000000000000",
                  "symbol": "${CHAIN_CURRENCY_SYMBOL:-ADI}",
                  "name": "${CHAIN_CURRENCY_NAME:-ADI Token}",
                  "decimals": 18,
                  "iconUrl": "/img/adi.svg"
                }
              ]
            }
          ]
        }

volumes:
  explorer_db_data:
    name: ${CHAIN_SHORT_NAME:-myrollup}_explorer_db_data
```

{% hint style="info" %}
The bridge UI defaults to showing the settlement layer's base token. In this example, that is ADI. To bridge custom ERC20 tokens, add them to the `tokens` array in `RUNTIME_CONFIG`, or use `requestL2TransactionTwoBridges` directly.
{% endhint %}

***

## Start

```bash
docker compose -f docker-compose.infra.yml up -d
```

## Verify

| Service        | URL                     | What to Expect                                     |
| -------------- | ----------------------- | -------------------------------------------------- |
| Block Explorer | `http://localhost:3010` | Block explorer UI — blocks, transactions, accounts |
| Explorer API   | `http://localhost:3020` | REST API for blockchain data                       |
| Bridge         | `http://localhost:3000` | Deposit and withdrawal interface                   |

> **Warning:** The Bridge will not display any network data until the Block Explorer worker has indexed at least a few blocks. Allow a minute for the initial sync to complete.


# Blockscout Explorer

Deploy Blockscout as an optional block explorer with smart contract verification for your ADI Rollup.

[Blockscout](https://www.blockscout.com/) provides an alternative block explorer with smart contract verification (Solidity + Vyper), Solidity-to-UML visualization, function signature decoding, and chain statistics.

> **Warning:** Blockscout is entirely optional and runs independently from the Block Explorer. The Bridge requires the **Block Explorer** ([Infrastructure Stack](/adi-network-components/overview-1/run-a-rollup/infrastructure-stack)) — not Blockscout. You can run both explorers side by side.

For verifying contracts on the public ADI explorers instead of your self-hosted one, see [Verify smart contracts](/how-to-start/verify-smart-contracts).

***

## Docker Compose

Create `docker-compose.blockscout.yml`:

```yaml
services:
  # ─────────────────────────────────────────────
  # Blockscout — PostgreSQL
  # ─────────────────────────────────────────────
  blockscout-db:
    image: postgres:15-alpine
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-db
    restart: unless-stopped
    command: postgres -c max_connections=300
    environment:
      POSTGRES_PASSWORD: ${BLOCKSCOUT_DB_PASSWORD:-postgres}
      POSTGRES_USER: blockscout
      POSTGRES_DB: blockscout
    volumes:
      - blockscout_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U blockscout -d blockscout"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

  # ─────────────────────────────────────────────
  # Blockscout — Redis
  # ─────────────────────────────────────────────
  blockscout-redis:
    image: redis:7-alpine
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-redis
    restart: unless-stopped
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  # ─────────────────────────────────────────────
  # Blockscout — Backend
  # ─────────────────────────────────────────────
  blockscout-backend:
    image: ghcr.io/blockscout/blockscout:${BLOCKSCOUT_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-backend
    restart: unless-stopped
    command: >
      sh -c "bin/blockscout eval \"Elixir.Explorer.ReleaseTasks.create_and_migrate()\"
      && bin/blockscout start"
    environment:
      DATABASE_URL: postgresql://blockscout:${BLOCKSCOUT_DB_PASSWORD:-postgres}@blockscout-db:5432/blockscout
      ECTO_USE_SSL: "false"
      POOL_SIZE: "80"
      POOL_SIZE_API: "10"
      ETHEREUM_JSONRPC_HTTP_URL: http://${RPC_HOST:-host.docker.internal}:${RPC_PORT:-3050}
      ETHEREUM_JSONRPC_TRACE_URL: http://${RPC_HOST:-host.docker.internal}:${RPC_PORT:-3050}
      ETHEREUM_JSONRPC_TRANSPORT: http
      ETHEREUM_JSONRPC_VARIANT: geth
      CHAIN_ID: ${CHAIN_ID:-444}
      COIN: ${CHAIN_CURRENCY_SYMBOL:-ADI}
      COIN_NAME: ${CHAIN_CURRENCY_NAME:-ADI Token}
      INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER: "true"
      INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER: "true"
      INDEXER_BEACON_BLOB_FETCHER_DISABLED: "true"
      PORT: 4000
      ACCOUNT_ENABLED: "false"
      DISABLE_MARKET: "true"
      SECRET_KEY_BASE: ${SECRET_KEY_BASE:?Run openssl rand -hex 32 and set SECRET_KEY_BASE in .env}
      ACCOUNT_REDIS_URL: redis://blockscout-redis:6379/0
      MICROSERVICE_SC_VERIFIER_ENABLED: "true"
      MICROSERVICE_SC_VERIFIER_URL: http://blockscout-verifier:8050
      MICROSERVICE_SC_VERIFIER_TYPE: sc_verifier
      MICROSERVICE_VISUALIZE_SOL2UML_ENABLED: "true"
      MICROSERVICE_VISUALIZE_SOL2UML_URL: http://blockscout-visualizer:8050
      MICROSERVICE_SIG_PROVIDER_ENABLED: "true"
      MICROSERVICE_SIG_PROVIDER_URL: http://blockscout-sig-provider:8050
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      blockscout-db:
        condition: service_healthy
      blockscout-redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4000/api/v2/stats"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 120s

  # ─────────────────────────────────────────────
  # Blockscout — Frontend
  # ─────────────────────────────────────────────
  blockscout-frontend:
    image: ghcr.io/blockscout/frontend:${BLOCKSCOUT_FRONTEND_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-frontend
    restart: unless-stopped
    ports:
      - "4000:3000"
    environment:
      NEXT_PUBLIC_APP_HOST: localhost
      NEXT_PUBLIC_APP_PORT: 4000
      NEXT_PUBLIC_APP_PROTOCOL: http
      NEXT_PUBLIC_API_HOST: blockscout-backend
      NEXT_PUBLIC_API_PORT: 4000
      NEXT_PUBLIC_API_PROTOCOL: http
      NEXT_PUBLIC_API_BASE_PATH: /
      NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL: ws
      NEXT_PUBLIC_NETWORK_NAME: "${CHAIN_NAME:-ADI Chain}"
      NEXT_PUBLIC_NETWORK_SHORT_NAME: "${CHAIN_SHORT_NAME:-adi}"
      NEXT_PUBLIC_NETWORK_ID: ${CHAIN_ID:-444}
      NEXT_PUBLIC_NETWORK_CURRENCY_NAME: "${CHAIN_CURRENCY_NAME:-ADI Token}"
      NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: ${CHAIN_CURRENCY_SYMBOL:-ADI}
      NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: 18
      NEXT_PUBLIC_IS_TESTNET: "true"
      NEXT_PUBLIC_VISUALIZE_API_HOST: http://blockscout-visualizer:8050
      NEXT_PUBLIC_STATS_API_HOST: http://blockscout-stats:8050
      NEXT_PUBLIC_AD_BANNER_PROVIDER: none
    depends_on:
      blockscout-backend:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # ─────────────────────────────────────────────
  # Blockscout — Smart Contract Verifier
  # ─────────────────────────────────────────────
  blockscout-verifier:
    image: ghcr.io/blockscout/smart-contract-verifier:${BLOCKSCOUT_VERIFIER_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-verifier
    restart: unless-stopped
    environment:
      SMART_CONTRACT_VERIFIER__SERVER__HTTP__ADDR: 0.0.0.0:8050
      SMART_CONTRACT_VERIFIER__SERVER__HTTP__ENABLED: "true"
      SMART_CONTRACT_VERIFIER__SOLIDITY__ENABLED: "true"
      SMART_CONTRACT_VERIFIER__SOLIDITY__COMPILERS_DIR: /tmp/solidity-compilers
      SMART_CONTRACT_VERIFIER__VYPER__ENABLED: "true"
      SMART_CONTRACT_VERIFIER__VYPER__COMPILERS_DIR: /tmp/vyper-compilers
      SMART_CONTRACT_VERIFIER__SOURCIFY__ENABLED: "true"
      SMART_CONTRACT_VERIFIER__SOURCIFY__API_URL: https://sourcify.dev/server/
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8050/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # ─────────────────────────────────────────────
  # Blockscout — Visualizer (Solidity UML)
  # ─────────────────────────────────────────────
  blockscout-visualizer:
    image: ghcr.io/blockscout/visualizer:${BLOCKSCOUT_VISUALIZER_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-visualizer
    restart: unless-stopped
    environment:
      VISUALIZER__SERVER__HTTP__ADDR: 0.0.0.0:8050
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8050/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # ─────────────────────────────────────────────
  # Blockscout — Signature Provider
  # ─────────────────────────────────────────────
  blockscout-sig-provider:
    image: ghcr.io/blockscout/sig-provider:${BLOCKSCOUT_SIG_PROVIDER_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-sig-provider
    restart: unless-stopped
    environment:
      SIG_PROVIDER__SERVER__HTTP__ADDR: 0.0.0.0:8050
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8050/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # ─────────────────────────────────────────────
  # Blockscout — Stats Database
  # ─────────────────────────────────────────────
  blockscout-stats-db:
    image: postgres:15-alpine
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-stats-db
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: ${BLOCKSCOUT_DB_PASSWORD:-postgres}
      POSTGRES_USER: stats
      POSTGRES_DB: stats
    volumes:
      - blockscout_stats_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U stats -d stats"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s

  # ─────────────────────────────────────────────
  # Blockscout — Stats Service
  # ─────────────────────────────────────────────
  blockscout-stats:
    image: ghcr.io/blockscout/stats:${BLOCKSCOUT_STATS_VERSION:-latest}
    container_name: ${CHAIN_SHORT_NAME:-adi}-blockscout-stats
    restart: unless-stopped
    environment:
      STATS__DB_URL: postgresql://stats:${BLOCKSCOUT_DB_PASSWORD:-postgres}@blockscout-stats-db:5432/stats
      STATS__BLOCKSCOUT_DB_URL: postgresql://blockscout:${BLOCKSCOUT_DB_PASSWORD:-postgres}@blockscout-db:5432/blockscout
      STATS__CREATE_DATABASE: "false"
      STATS__RUN_MIGRATIONS: "true"
      STATS__BLOCKSCOUT_API_URL: http://blockscout-backend:4000
      STATS__SERVER__HTTP__ENABLED: "true"
      STATS__SERVER__HTTP__ADDR: 0.0.0.0:8050
      STATS__SERVER__HTTP__CORS__ENABLED: "true"
      STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN: http://localhost:4000
      STATS__FORCE_UPDATE_ON_START: "false"
    depends_on:
      blockscout-stats-db:
        condition: service_healthy
      blockscout-backend:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8050/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

volumes:
  blockscout_db_data:
    name: ${CHAIN_SHORT_NAME:-adi}_blockscout_db_data
  blockscout_stats_db_data:
    name: ${CHAIN_SHORT_NAME:-adi}_blockscout_stats_db_data
```

***

## Start

> **Note:** The default RPC host is `host.docker.internal`, which works out of the box with the `extra_hosts` entry on Linux and on Docker Desktop for macOS/Windows. If your node runs on a different machine, set `RPC_HOST` and `RPC_PORT` in `.env` and consider an SSH tunnel.

```bash
docker compose -f docker-compose.blockscout.yml up -d
```

> **Tip:** Blockscout will be available at `http://localhost:4000`. The initial sync may take a few minutes depending on your chain's block count.


# Operations

Running, monitoring, GPU configuration, and troubleshooting your ADI Rollup deployment.

## Running Everything Together

To bring up the full stack in one command:

```bash
# Core + Infrastructure
docker compose \
  -f docker-compose.yml \
  -f docker-compose.infra.yml \
  up -d

# Core + Infrastructure + Blockscout
docker compose \
  -f docker-compose.yml \
  -f docker-compose.infra.yml \
  -f docker-compose.blockscout.yml \
  up -d
```

### Rate limits and allowlisting

The mainnet RPC at `rpc.adifoundation.ai` enforces per-IP rate limits for L3 traffic.

If sequencer startup hits HTTP `429` with Cloudflare `1015`, the issue is infrastructure configuration, not chain logic. Contact ADI Ops to allowlist your outbound IP.

Testnet at `rpc.ab.testnet.adifoundation.ai` does not enforce these limits.

{% hint style="warning" %}
If you are hitting `429` during startup, set `restart: "no"` on the affected services until your IP is allowlisted. Keep `restart: unless-stopped` for transient upstream failures such as `502`.
{% endhint %}

***

## Port Reference

| Port   | Service       | Description                                               |
| ------ | ------------- | --------------------------------------------------------- |
| `3050` | Sequencer     | L2 JSON-RPC endpoint                                      |
| `3051` | External Node | L2 JSON-RPC (read replica)                                |
| `3053` | Sequencer     | Block replay server used by the External Node             |
| `3054` | External Node | Replay server override to avoid conflict on the same host |
| `3320` | Sequencer     | Prover API (internal)                                     |
| `3000` | Bridge        | dApp Portal / Bridge UI                                   |
| `3010` | Explorer App  | Block Explorer frontend                                   |
| `3020` | Explorer API  | Block Explorer REST API                                   |
| `3040` | Data Fetcher  | Explorer data fetcher                                     |
| `4000` | Blockscout    | Blockscout frontend (optional)                            |

***

## GPU Configuration

### Single GPU

If you have a dedicated GPU per prover, use the full GPU UUID:

```bash
nvidia-smi -L
# GPU 0: NVIDIA A100 80GB (UUID: GPU-12345678-abcd-...)
```

Set in `.env`:

```bash
GPU_DEVICE_FRI=GPU-12345678-abcd-...
GPU_DEVICE_SNARK=GPU-87654321-dcba-...
```

### Scaling Provers

For higher proving throughput, add more prover instances. Each needs a unique name and Prometheus port:

```yaml
  fri-prover-2:
    image: ${PROVER_FRI_IMAGE}
    container_name: ${CHAIN_SHORT_NAME}-fri-prover-2
    restart: unless-stopped
    network_mode: host
    depends_on: [server]
    environment:
      RUST_LOG: "info"
    volumes:
      - ./volumes/prover:/prover
    command:
      - "--base-url"
      - "http://127.0.0.1:3320"
      - "--app-bin-path"
      - "/multiblock_batch.bin"
      - "--enabled-logging"
      - "--prover-name"
      - "fri-prover-2"
      - "--prometheus-port"
      - "3125"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              device_ids: ["${GPU_DEVICE_FRI_2}"]
              capabilities: [gpu]
```

***

## Viewing Logs

```bash
# All services
docker compose logs -f

# Specific service
docker compose logs -f server
docker compose logs -f fri-prover
docker compose logs -f snark-prover

# Infrastructure stack
docker compose -f docker-compose.infra.yml logs -f explorer-worker
```

***

## Monitoring

| Endpoint                        | What It Shows                    |
| ------------------------------- | -------------------------------- |
| `http://localhost:3050`         | Sequencer JSON-RPC               |
| `http://localhost:3051`         | External Node JSON-RPC           |
| `http://localhost:3312/metrics` | Sequencer Prometheus metrics     |
| `http://localhost:3316/metrics` | External Node Prometheus metrics |
| `http://localhost:3124/metrics` | FRI Prover Prometheus metrics    |
| `http://localhost:3126/metrics` | SNARK Prover Prometheus metrics  |

Check sync status:

```bash
# Sequencer block number
curl -s -X POST http://localhost:3050 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# GPU utilization
watch -n 1 nvidia-smi
```

***

## Restarting Services

```bash
# Restart a single service
docker compose restart server

# Restart the entire core stack
docker compose down && docker compose up -d
```

***

## Troubleshooting

| Error                                           | Likely cause                                                                                     | Resolution                                                                                                                                                                                                                                                                                                        |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `failed to fetch finalized L1 state` with `429` | Your settlement RPC is rate-limiting L3 traffic                                                  | Check the upstream response. On mainnet, contact ADI Ops to allowlist your outbound IP. Set `restart: "no"` until allowlisting is complete.                                                                                                                                                                       |
| `failed to fetch finalized L1 state` with `502` | Upstream settlement RPC is unavailable or returning a transient gateway error                    | Keep `restart: unless-stopped`. Retry after the upstream recovers, or switch to a healthier RPC endpoint.                                                                                                                                                                                                         |
| `sender does not have enough funds`             | An operator wallet is missing settlement-layer gas funds                                         | Fund the commit, prove, and execute operator wallets with the settlement-layer gas token. If `base_token_address` was omitted at deploy time, that token is ADI.                                                                                                                                                  |
| `Existing batch first block does not match`     | Local node state does not match the chain you are trying to run                                  | Stop the node. Remove the affected node's RocksDB and replay data. Keep reusable wallet files only. Do not reuse `genesis.yaml` or `contracts.yaml` across chains.                                                                                                                                                |
| `L1 commit watcher component failed`            | Settlement RPC is wrong, unavailable, rate-limited, or paired with the wrong settlement chain ID | Verify `L1_RPC_URL` reaches your settlement layer. Confirm the generated `genesis.json` has the correct `l1_chain_id`. For ADI, use `36900` on mainnet or `99999` on testnet. If you use a public RPC, relax the watcher settings to `l1_watcher_poll_interval: "2s"` and `l1_watcher_max_blocks_to_process: 10`. |


# Configuration Reference

Complete environment variable and CLI argument reference for all ADI Rollup components.

This page covers the most important environment variables and CLI arguments for each component. For the full list of available options, see the linked source repositories.

***

## Sequencer Configuration

The sequencer uses environment variables in `component_setting_name` format (lowercase, underscore-separated). All settings are defined via the [`smart_config`](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/tree/main/node/bin/src/config) framework.

### General

| Variable                              | Default                 | Description                                     |
| ------------------------------------- | ----------------------- | ----------------------------------------------- |
| `general_l1_rpc_url`                  | `http://localhost:8545` | Settlement-layer JSON-RPC endpoint for the node |
| `general_rocks_db_path`               | `./db`                  | Path to RocksDB state storage                   |
| `general_main_node_rpc_url`           | —                       | Main node RPC URL (required for External Nodes) |
| `general_min_blocks_to_replay`        | `10`                    | Minimum blocks to replay on restart             |
| `general_force_starting_block_number` | —                       | Force replay from a specific block number       |

{% hint style="info" %}
For L3 chains, `general_l1_rpc_url` points to the settlement layer. On ADI, that means ADI Chain. For L2 external nodes, it points to Ethereum. The variable name is inherited from zkSync OS upstream.
{% endhint %}

### L1 Watchers

| Variable                           | Default | Description                                                                               |
| ---------------------------------- | ------- | ----------------------------------------------------------------------------------------- |
| `l1_watcher_poll_interval`         | `100ms` | Poll interval for settlement-layer watchers. Good for local dev. Use `2s` for public RPCs |
| `l1_watcher_max_blocks_to_process` | `1000`  | Maximum blocks processed per poll. Good for local dev. Use `10` for public endpoints      |

### Sequencer

| Variable                                  | Default            | Description                                                                                           |
| ----------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------- |
| `sequencer_block_time`                    | `250ms`            | Block production interval                                                                             |
| `sequencer_max_transactions_in_block`     | `1000`             | Maximum transactions per block                                                                        |
| `sequencer_block_gas_limit`               | `100000000`        | Gas limit per block                                                                                   |
| `sequencer_block_pubdata_limit_bytes`     | `110000`           | Pubdata size limit per block                                                                          |
| `sequencer_block_dump_path`               | `./db/block_dumps` | Path to dump block data for replay                                                                    |
| `sequencer_block_replay_server_address`   | `0.0.0.0:3053`     | Replay server address used by External Nodes                                                          |
| `sequencer_block_replay_download_address` | —                  | Replay source address; setting this enables External Node mode (must include `http://` or `https://`) |
| `sequencer_fee_collector_address`         | `0x3661...c049`    | Address that collects transaction fees                                                                |

### Sequencer Fee Overrides

| Variable                           | Default | Description                           |
| ---------------------------------- | ------- | ------------------------------------- |
| `sequencer_base_fee_override`      | —       | Override base fee (hex, e.g. `0x3e8`) |
| `sequencer_pubdata_price_override` | —       | Override pubdata price (hex)          |
| `sequencer_native_price_override`  | —       | Override native token price (hex)     |

### RPC

| Variable                    | Default          | Description                                                                           |
| --------------------------- | ---------------- | ------------------------------------------------------------------------------------- |
| `rpc_address`               | `0.0.0.0:3050`   | JSON-RPC listen address                                                               |
| `rpc_max_connections`       | `1000`           | Maximum concurrent connections                                                        |
| `rpc_eth_call_gas`          | `10000000`       | Gas limit for `eth_call`                                                              |
| `rpc_max_logs_per_response` | `20000`          | Maximum log entries returned per request                                              |
| `rpc_max_request_size`      | `15`             | Maximum request payload size (MB)                                                     |
| `rpc_max_response_size`     | `24`             | Maximum response payload size (MB)                                                    |
| `private_api_address`       | `127.0.0.1:8546` | Private API listen address (use a different port for External Nodes on the same host) |

### L1 Sender

| Variable                                  | Default                | Description                                                           |
| ----------------------------------------- | ---------------------- | --------------------------------------------------------------------- |
| `l1_sender_operator_commit_pk`            | —                      | Private key for committing batches to L1                              |
| `l1_sender_operator_prove_pk`             | —                      | Private key for submitting proofs to L1                               |
| `l1_sender_operator_execute_pk`           | —                      | Private key for executing batches on L1                               |
| `l1_sender_max_fee_per_gas`               | `100 Gwei`             | Maximum gas price for L1 transactions                                 |
| `l1_sender_max_fee_per_gas_gwei`          | —                      | Same setting expressed with a Gwei suffix for easier Docker env usage |
| `l1_sender_max_priority_fee_per_gas`      | `1 Gwei`               | Maximum priority fee for L1 transactions                              |
| `l1_sender_max_priority_fee_per_gas_gwei` | —                      | Same setting expressed with a Gwei suffix                             |
| `l1_sender_max_fee_per_blob_gas`          | `1 Gwei`               | Maximum blob gas price                                                |
| `l1_sender_max_fee_per_blob_gas_gwei`     | —                      | Same setting expressed with a Gwei suffix                             |
| `l1_sender_pubdata_mode`                  | —                      | Pubdata availability mode — `Blobs` or `Calldata`                     |
| `l1_sender_enabled`                       | `true`                 | Enable L1 settlement transactions                                     |
| `l1_sender_fusaka_upgrade_timestamp`      | `18446744073709551615` | Switch to Fusaka blob format after this Unix timestamp                |

### Prover API (on Sequencer)

| Variable                                        | Default        | Description                                         |
| ----------------------------------------------- | -------------- | --------------------------------------------------- |
| `prover_api_address`                            | `0.0.0.0:3320` | Prover API listen address                           |
| `prover_api_fake_fri_provers_enabled`           | `true`         | Use fake FRI provers (set `false` for production)   |
| `prover_api_fake_snark_provers_enabled`         | `true`         | Use fake SNARK provers (set `false` for production) |
| `prover_api_fri_job_timeout`                    | `300s`         | Timeout before reassigning a FRI job                |
| `prover_api_snark_job_timeout`                  | `300s`         | Timeout before reassigning a SNARK job              |
| `prover_api_max_fris_per_snark`                 | `10`           | Maximum FRI proofs aggregated per SNARK proof       |
| `prover_api_object_store_file_backed_base_path` | `./db/shared`  | Base path for file-backed prover proof storage      |

### Prover Input Generator

| Variable                                          | Default | Description                                                          |
| ------------------------------------------------- | ------- | -------------------------------------------------------------------- |
| `prover_input_generator_maximum_in_flight_blocks` | `16`    | Number of blocks the prover input generator can process concurrently |

### Batching

| Variable                         | Default | Description                                 |
| -------------------------------- | ------- | ------------------------------------------- |
| `batcher_batch_timeout`          | `1s`    | Maximum time to wait before sealing a batch |
| `batcher_blocks_per_batch_limit` | `10`    | Maximum blocks per batch                    |

### Genesis

| Variable                            | Default | Description                           |
| ----------------------------------- | ------- | ------------------------------------- |
| `genesis_chain_id`                  | —       | L2 chain ID                           |
| `genesis_bridgehub_address`         | —       | L1 Bridgehub contract address         |
| `genesis_bytecode_supplier_address` | —       | L1 bytecode supplier contract address |
| `genesis_genesis_input_path`        | —       | Path to `genesis.json` file           |

{% hint style="info" %}
`l1_chain_id` in `genesis.json` is the chain ID of your settlement layer. For L3s on ADI, use `36900` on mainnet or `99999` on testnet. `adi init` sets this automatically.
{% endhint %}

### Observability

| Variable                        | Default        | Description                                                                                |
| ------------------------------- | -------------- | ------------------------------------------------------------------------------------------ |
| `observability_prometheus_port` | `3312`         | Prometheus metrics port                                                                    |
| `status_server_address`         | `0.0.0.0:3071` | Status server address; set a different port on the External Node when both run on one host |
| `RUST_LOG`                      | —              | Log level filter (e.g. `info`, `debug`, `warn`)                                            |

> Full server configuration reference: [ADI-Stack-Server/node/bin/src/config](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/tree/main/node/bin/src/config)

***

## FRI Prover Configuration

The FRI prover is configured via CLI arguments. Environment variables control logging only.

| Argument                          | Default                      | Description                                                                     |
| --------------------------------- | ---------------------------- | ------------------------------------------------------------------------------- |
| `--base-url` / `--sequencer-urls` | `http://localhost:3124`      | Sequencer prover API URL(s), comma-separated for round-robin                    |
| `--sequencer-urls-file`           | —                            | Path to file with URLs (one per line), takes precedence over `--sequencer-urls` |
| `--app-bin-path`                  | `../../multiblock_batch.bin` | Path to the RISC-V binary                                                       |
| `--circuit-limit`                 | `10000`                      | Maximum MainVM circuits to instantiate                                          |
| `--iterations`                    | —                            | Number of proving iterations before exiting (unlimited if omitted)              |
| `--prover-name`                   | `unknown_prover`             | Prover identifier for monitoring                                                |
| `--prometheus-port`               | `3124`                       | Prometheus metrics port                                                         |
| `--enabled-logging`               | `false`                      | Enable structured logging                                                       |
| `--request-timeout-secs`          | `2`                          | HTTP request timeout to sequencer                                               |

| Env Variable | Description                                         |
| ------------ | --------------------------------------------------- |
| `RUST_LOG`   | Log level filter (`debug`, `info`, `warn`, `error`) |

> Full FRI prover source: [ADI-Stack-Airbender-Prover/crates/zksync\_os\_fri\_prover](https://github.com/ADI-Foundation-Labs/ADI-Stack-Airbender-Prover/tree/main/crates/zksync_os_fri_prover)

***

## SNARK Prover Configuration

The SNARK prover uses the `run-prover` subcommand with CLI arguments.

| Argument                               | Default | Description                                         |
| -------------------------------------- | ------- | --------------------------------------------------- |
| `--sequencer-url` / `--sequencer-urls` | —       | Sequencer prover API URL(s)                         |
| `--sequencer-urls-file`                | —       | Path to file with URLs                              |
| `--binary-path`                        | —       | Path to the RISC-V binary                           |
| `--trusted-setup-file`                 | —       | Path to the trusted setup key (`setup_compact.key`) |
| `--output-dir`                         | —       | Output directory for SNARK proofs                   |
| `--iterations`                         | —       | Number of proving iterations before exiting         |
| `--prover-name`                        | —       | Prover identifier for monitoring                    |
| `--prometheus-port`                    | `3124`  | Prometheus metrics port                             |
| `--disable-zk`                         | `false` | Disable zero-knowledge property (testing only)      |

| Env Variable     | Default | Description                                                    |
| ---------------- | ------- | -------------------------------------------------------------- |
| `RUST_MIN_STACK` | —       | Thread stack size in bytes (recommended: `267108864` = 256 MB) |
| `RUST_LOG`       | —       | Log level filter                                               |

> Full SNARK prover source: [ADI-Stack-Airbender-Prover/crates/zksync\_os\_snark\_prover](https://github.com/ADI-Foundation-Labs/ADI-Stack-Airbender-Prover/tree/main/crates/zksync_os_snark_prover)

***

## Block Explorer Configuration

The Block Explorer consists of four services. Key environment variables for each:

### Worker (Indexer)

| Variable                | Default          | Description                                 |
| ----------------------- | ---------------- | ------------------------------------------- |
| `BLOCKCHAIN_RPC_URL`    | —                | L2 JSON-RPC endpoint to index               |
| `DATA_FETCHER_URL`      | —                | URL of the data fetcher service             |
| `DATABASE_HOST`         | `localhost`      | PostgreSQL host                             |
| `DATABASE_USER`         | `postgres`       | PostgreSQL user                             |
| `DATABASE_PASSWORD`     | —                | PostgreSQL password                         |
| `DATABASE_NAME`         | `block-explorer` | PostgreSQL database name                    |
| `SETTLEMENT_RPC_URL`    | —                | Settlement-layer RPC URL for batch tracking |
| `DIAMOND_PROXY_ADDRESS` | —                | L1 Diamond Proxy contract address           |
| `BASE_TOKEN_SYMBOL`     | `ETH`            | Base token ticker symbol                    |
| `BASE_TOKEN_NAME`       | `Ether`          | Base token display name                     |

### API

| Variable                | Default          | Description                  |
| ----------------------- | ---------------- | ---------------------------- |
| `PORT`                  | `3020`           | HTTP listen port             |
| `DATABASE_HOST`         | `localhost`      | PostgreSQL host              |
| `DATABASE_USER`         | `postgres`       | PostgreSQL user              |
| `DATABASE_PASSWORD`     | —                | PostgreSQL password          |
| `DATABASE_NAME`         | `block-explorer` | PostgreSQL database name     |
| `BASE_TOKEN_SYMBOL`     | `ETH`            | Base token ticker            |
| `BASE_TOKEN_L1_ADDRESS` | —                | L1 address of the base token |

### App (Frontend)

| Variable                 | Default | Description                        |
| ------------------------ | ------- | ---------------------------------- |
| `APP_NETWORK_NAME`       | —       | Network display name               |
| `APP_L2_CHAIN_ID`        | —       | L2 chain ID                        |
| `APP_RPC_URL`            | —       | L2 JSON-RPC URL (used by frontend) |
| `APP_API_URL`            | —       | Block Explorer API URL             |
| `APP_BRIDGE_URL`         | —       | Bridge UI URL                      |
| `APP_L1_EXPLORER_URL`    | —       | L1 block explorer URL              |
| `APP_BASE_TOKEN_ADDRESS` | —       | Base token contract address on L2  |

### Data Fetcher

| Variable              | Default | Description                      |
| --------------------- | ------- | -------------------------------- |
| `PORT`                | `3040`  | HTTP listen port                 |
| `BLOCKCHAIN_RPC_URL`  | —       | L2 JSON-RPC endpoint             |
| `RPC_BATCH_MAX_COUNT` | `50`    | Maximum batch size for RPC calls |

> Full Block Explorer source: [matter-labs/block-explorer](https://github.com/matter-labs/block-explorer)

***

## Bridge (dApp Portal) Configuration

The Bridge is configured via a `RUNTIME_CONFIG` JSON environment variable. Key fields:

| Field                                           | Description                                                                                                                             |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `nodeType`                                      | Must be `"hyperchain"` for custom rollups                                                                                               |
| `hyperchainsConfig[].network.id`                | L2 chain ID                                                                                                                             |
| `hyperchainsConfig[].network.name`              | Network display name                                                                                                                    |
| `hyperchainsConfig[].network.rpcUrl`            | L2 JSON-RPC endpoint                                                                                                                    |
| `hyperchainsConfig[].network.blockExplorerUrl`  | Block Explorer frontend URL                                                                                                             |
| `hyperchainsConfig[].network.blockExplorerApi`  | Block Explorer API URL                                                                                                                  |
| `hyperchainsConfig[].network.l1Network.rpcUrls` | L1 RPC URLs                                                                                                                             |
| `hyperchainsConfig[].tokens`                    | List of bridgeable tokens with addresses and metadata. The UI defaults to the settlement layer's base token unless you add more entries |

> Full dApp Portal source: [matter-labs/dapp-portal](https://github.com/matter-labs/dapp-portal)


# L3 Chains on Mainnet

Production L3 chains live on mainnet

## L3 chains on ADI Mainnet

ADI Mainnet hosts two production L3 chains. Both settle on ADI Mainnet, chain ID `36900`. Both use ADI as their native gas token.

<table><thead><tr><th>Chain</th><th width="115.8671875">Chain ID</th><th>Use case</th><th>Runtime</th></tr></thead><tbody><tr><td>Good Energy (Esyasoft)</td><td><code>999</code></td><td>Energy data anchoring</td><td><code>zksync-os/v0.13.0</code></td></tr><tr><td>Apeiro</td><td><code>37001</code></td><td>Healthcare claims verification</td><td><code>zksync-os/v0.13.0</code></td></tr></tbody></table>

### Good Energy (Esyasoft)

Good Energy is the first zkSync OS L3 to reach mainnet. It anchors energy data onchain. The sequencer is producing blocks.

| Property         | Value                         |
| ---------------- | ----------------------------- |
| Chain ID         | `999`                         |
| Settlement layer | ADI Mainnet, chain ID `36900` |
| Native gas token | ADI                           |
| Runtime          | `zksync-os/v0.13.0`           |

#### Network services

* **Explorer:** [Good Energy Explorer](https://goodenergyexplorer.com)
* **Explorer API:** [Good Energy Explorer API](https://api.goodenergyexplorer.com)
* **Bridge:** [Good Energy Bridge](https://bridge.goodenergyexplorer.com)

### Apeiro

Apeiro verifies healthcare claims on a dedicated L3. The application has active production traffic.

As of July 21, 2026, the chain had processed more than 2,700 transactions across more than 45 addresses.

| Property         | Value                         |
| ---------------- | ----------------------------- |
| Chain ID         | `37001`                       |
| Settlement layer | ADI Mainnet, chain ID `36900` |
| Native gas token | ADI                           |
| Runtime          | `zksync-os/v0.13.0`           |

#### Network services

* **Explorer:** [Apeiro Explorer](https://explorer.apeiro.adifoundation.ai)
* **Blockscout Explorer:** [Apeiro Blockscout](https://explorer-bls.apeiro.adifoundation.ai)
* **Bridge:** [Apeiro Bridge](https://bridge.apeiro.adifoundation.ai)

### Related documentation

See [L3 Chains](/adi-network-components/overview-1) for L3 architecture and settlement flow. See [Configuration Reference](/adi-network-components/overview-1/run-a-rollup/configuration-reference) for the settlement-RPC configuration.


# JSON-RPC API

The JSON-RPC methods served by the ADI Sequencer - the Ethereum-compatible namespaces plus the extensions.

The node serves everything over one JSON-RPC endpoint: `https://rpc.ab.testnet.adifoundation.ai/` for Testnet and `https://rpc.adifoundation.ai/` for Mainnet.

Most of it is plain Ethereum JSON-RPC, so ethers, viem, web3.js and Foundry all work without any special handling. On top of that there are two extra namespaces: `zks` for the rollup-specific bits (bridge addresses, L2->L1 proofs, finality) and `ots` for the Otterscan explorer.

Every call is a normal JSON-RPC 2.0 POST:

```bash
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
  https://rpc.ab.testnet.adifoundation.ai
```

## What's available

| Namespace | What it covers                                                                                                                             |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `eth`     | The usual chain / state / transaction methods, log filters, and WebSocket subscriptions.                                                   |
| `zks`     | Rollup extras - contract addresses, L2->L1 log proofs, genesis, block metadata, and the [finality API](/references/json-rpc-api/finality). |
| `debug`   | Raw block/header/receipt bytes and transaction/block tracing.                                                                              |
| `ots`     | Otterscan support: block details, transaction search, contract creator, internal ops.                                                      |
| `net`     | Network id.                                                                                                                                |
| `web3`    | Client version and `keccak256`.                                                                                                            |

## eth

Standard Ethereum JSON-RPC, grouped by what you'd use it for.

| Group                     | Methods                                                                                                                                                                                                          |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Chain & state             | `eth_chainId`, `eth_blockNumber`, `eth_syncing`, `eth_gasPrice`, `eth_maxPriorityFeePerGas`, `eth_feeHistory`, `eth_getBalance`, `eth_getCode`, `eth_getStorageAt`, `eth_getTransactionCount`                    |
| Blocks & headers          | `eth_getBlockByHash`, `eth_getBlockByNumber`, `eth_getBlockReceipts`, `eth_getBlockTransactionCountByHash` / `ByNumber`, `eth_getHeaderByHash` / `ByNumber`                                                      |
| Transactions              | `eth_getTransactionByHash`, `eth_getTransactionReceipt`, `eth_getTransactionByBlockHashAndIndex`, `eth_getTransactionByBlockNumberAndIndex`, `eth_getTransactionBySenderAndNonce`, `eth_getRawTransactionByHash` |
| Execution                 | `eth_call`, `eth_estimateGas`                                                                                                                                                                                    |
| Submitting                | `eth_sendRawTransaction`, `eth_sendRawTransactionSync`                                                                                                                                                           |
| Filters                   | `eth_newFilter`, `eth_newBlockFilter`, `eth_newPendingTransactionFilter`, `eth_getFilterChanges`, `eth_getFilterLogs`, `eth_getLogs`, `eth_uninstallFilter`                                                      |
| Subscriptions (WebSocket) | `eth_subscribe` / `eth_unsubscribe` for `newHeads`, `logs`, `newPendingTransactions`, `syncing`                                                                                                                  |

The `latest`, `safe`, and `finalized` block tags map onto the rollup's path to L1 - see [Finality](/references/json-rpc-api/finality).

## zks

The rollup-specific methods.

| Method                            | What it returns                                                                                     |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| `zks_getBridgehubContract`        | Address of the L1 Bridgehub contract.                                                               |
| `zks_getBytecodeSupplierContract` | Address of the bytecode supplier contract.                                                          |
| `zks_getL2ToL1LogProof`           | Merkle proof for an L2->L1 log, by tx hash and log index. `null` until the batch is executed on L1. |
| `zks_getGenesis`                  | The chain's genesis input.                                                                          |
| `zks_getBlockMetadataByNumber`    | Extended metadata for an L2 block.                                                                  |
| `zks_getBlockFinality`            | Finality of a block.                                                                                |
| `zks_getTransactionFinality`      | Finality of a transaction.                                                                          |
| `zks_getBatchFinality`            | Finality of an L1 batch.                                                                            |
| `zks_getFinalityStatus`           | Every finality frontier the node tracks, in one call.                                               |

The four finality methods have their own page, with request/response examples - see [Finality](/references/json-rpc-api/finality).

## debug

Transaction and block tracing: `debug_traceBlockByHash`, `debug_traceBlockByNumber`, `debug_traceTransaction`, `debug_traceCall`.

## ots

Otterscan explorer support: `ots_getApiLevel`, `ots_hasCode`, `ots_getHeaderByNumber`, `ots_getBlockDetails`, `ots_getBlockDetailsByHash`, `ots_getBlockTransactions`, `ots_searchTransactionsBefore`, `ots_searchTransactionsAfter`, `ots_getTransactionBySenderAndNonce`.

## net and web3

| Method               | Returns                                  |
| -------------------- | ---------------------------------------- |
| `net_version`        | Network (chain) id, as a decimal string. |
| `web3_clientVersion` | Client version string.                   |
| `web3_sha3`          | Keccak-256 of the input data.            |


# Finality

The zks finality methods - how far a block, transaction, or batch has progressed through the L1 finality pipeline.

Blocks don't become final the moment they're produced - they're sealed by the sequencer, later committed to L1, and finally executed on L1. The `zks` finality methods let you ask exactly where something sits in that journey, whether you're pointing at a block, a transaction, or a whole batch.

The three stages, least to most final:

| Stage     | JSON          | Meaning                                | Matches tag |
| --------- | ------------- | -------------------------------------- | ----------- |
| Pending   | `"pending"`   | sealed by the sequencer, not yet on L1 | `latest`    |
| Committed | `"committed"` | committed to L1                        | `safe`      |
| Executed  | `"executed"`  | executed on L1 - fully final           | `finalized` |

These stages are exactly the `latest`, `safe`, and `finalized` block tags you pass to `eth` methods like `eth_getBlockByNumber`.

{% hint style="info" %}
A batch and its blocks can only be linked once the batch is **executed** on L1 (the same rule `zks_getL2ToL1LogProof` follows). So `batchNumber` on a block/transaction lookup, and `blockNumber` on a batch lookup, stay `null` until execution - then they fill in.
{% endhint %}

## The response shape

`zks_getBlockFinality`, `zks_getTransactionFinality` and `zks_getBatchFinality` all return the same object:

| Field         | Type           | Notes                                                                                                                                                  |
| ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `stage`       | string         | `"pending"`, `"committed"` or `"executed"`.                                                                                                            |
| `blockNumber` | number \| null | The L2 block - the one you asked for, the transaction's block, or (for a batch) the batch's last block. Only filled in for a batch once it's executed. |
| `batchNumber` | number \| null | The L1 batch the block/transaction landed in, or the batch you asked about. Only filled in for a block/transaction once it's executed.                 |

## zks\_getBlockFinality

Finality of a block, by number or tag. `null` if the node doesn't have that block.

```bash
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"zks_getBlockFinality","params":["0x10"],"id":1}' \
  https://rpc.ab.testnet.adifoundation.ai
```

```json
{"jsonrpc":"2.0","id":1,"result":{"stage":"executed","blockNumber":16,"batchNumber":1}}
```

## zks\_getTransactionFinality

Finality of a transaction, by hash. `null` if the node hasn't seen it.

```bash
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"zks_getTransactionFinality","params":["0xabc...def"],"id":1}' \
  https://rpc.ab.testnet.adifoundation.ai
```

```json
{"jsonrpc":"2.0","id":1,"result":{"stage":"committed","blockNumber":42,"batchNumber":null}}
```

The block is committed but not executed yet, which is why `batchNumber` is still `null`.

## zks\_getBatchFinality

Finality of an L1 batch, by number. Anything past the committed frontier comes back as `pending`. This one always returns an object - never `null`.

```bash
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"zks_getBatchFinality","params":[3],"id":1}' \
  https://rpc.ab.testnet.adifoundation.ai
```

```json
{"jsonrpc":"2.0","id":1,"result":{"stage":"executed","blockNumber":55,"batchNumber":3}}
```

Once the batch is executed, `blockNumber` is its last L2 block.

## zks\_getFinalityStatus

Every frontier the node is tracking, in a single call. Handy for a dashboard or a sync check - no params needed.

```bash
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"zks_getFinalityStatus","params":[],"id":1}' \
  https://rpc.ab.testnet.adifoundation.ai
```

```json
{"jsonrpc":"2.0","id":1,"result":{"lastSealedBlock":368727,"lastCommittedBlock":368691,"lastCommittedBatch":11881,"lastExecutedBlock":368691,"lastExecutedBatch":11881}}
```

| Field                | Meaning                                 | Tag         |
| -------------------- | --------------------------------------- | ----------- |
| `lastSealedBlock`    | newest block the sequencer sealed       | `latest`    |
| `lastCommittedBlock` | newest block committed to L1            | `safe`      |
| `lastCommittedBatch` | newest batch committed to L1            | -           |
| `lastExecutedBlock`  | newest block whose batch executed on L1 | `finalized` |
| `lastExecutedBatch`  | newest batch executed on L1             | -           |


# CLI Configuration

Full configuration reference for the ADI CLI

Complete reference for the ADI CLI config file at `~/.adi_cli/.adi.yml`. For a quick start, see the [minimal config](/adi-network-components/overview-1/cli#minimal-config) in the CLI guide.

## Full Annotated Config

```yaml
# Where to store ecosystem state (wallets, contracts, chain configs)
# Default: ~/.adi_cli/state
state_dir: ~/.adi_cli/state

# Enable verbose logging for troubleshooting
# Default: false (can also use -d flag)
debug: false

# Default protocol version for toolkit Docker image
# Used by init, add, and deploy when --protocol-version is not provided
protocol_version: v0.30.1

# State storage backend (currently only "filesystem" is supported)
state_backend: filesystem

ecosystem:
  # Name used for the ecosystem directory and identification
  name: adi_ecosystem

  # Settlement layer network: localhost (Anvil) | sepolia | mainnet
  l1_network: sepolia

  # Settlement layer RPC endpoint
  # For L3s on ADI, this is ADI Chain.
  # Anvil: http://host.docker.internal:8545
  # ADI Testnet: https://rpc.ab.testnet.adifoundation.ai
  rpc_url: https://rpc.ab.testnet.adifoundation.ai

  # Settlement layer the chains settle on
  #   l1: settles on Ethereum L1 (the chain is an L2)
  #   l2: settles on an L2 such as ADI Chain (the chain is an L3)
  # For L3s on ADI, keep the default l2.
  # Default: l2
  settlement: l2

  # Ecosystem-level ownership transfer after deploy
  # ownership:
  #   new_owner: "0x..."

  chains:
    - name: my_chain
      chain_id: 222

      # no-proofs: development/testing (fast, no real proofs)
      # gpu: production (requires GPU prover infrastructure)
      # Default: no-proofs
      prover_mode: no-proofs

      # EVM bytecode emulator for unmodified Ethereum contracts
      evm_emulator: false

      # Data-availability pubdata mode
      #   blobs:     EIP-4844 blobs — requires settlement: l1
      #   calldata:  pubdata posted as calldata on the settlement layer
      #   custom_da: external DA, e.g. Avail (validium)
      # blobs is incompatible with settlement: l2 — an L3 cannot post EIP-4844
      # blobs, so init and deploy reject that combination.
      # Default: calldata (the only mode valid on either settlement layer)
      pubdata_mode: calldata

      # Custom ERC20 token for gas payments on the settlement layer
      # Omit this to use ADI as the gas token
      # base_token_address: "0x..."
      # base_token_price_nominator: 1
      # base_token_price_denominator: 1

      # Override generated operator addresses
      # operators:
      #   operator: "0x..."         # PRECOMMITTER, COMMITTER, REVERTER
      #   prove_operator: "0x..."   # PROVER
      #   execute_operator: "0x..." # EXECUTOR

      # Per-chain funding (ETH in ether)
      # funding:
      #   operator_eth: 30.0
      #   prove_operator_eth: 30.0
      #   execute_operator_eth: 30.0

      # Per-chain ownership
      # ownership:
      #   new_owner: "0x..."

# Ecosystem-level wallet funding during deployment
# A full deployment requires ~270 ADI tokens across all wallets.
# Testnet faucet: https://faucet.ab.testnet.adifoundation.ai/
funding:
  # SECURITY: use ADI_FUNDER_KEY env var instead
  # funder_key: "0x..."

  # Testnet (minimal):
  #   deployer_eth: 1.0
  #   governor_eth: 1.0
  #
  # Production:
  deployer_eth: 100.0
  governor_eth: 40.0
  governor_cgt_units: 5.0   # only if using custom base token

# Gas price multiplier percentage
# CLI fetches current gas price and multiplies by this value
# Recommended: 200 for Anvil, 300 for Sepolia/mainnet
gas_multiplier: 200

# S3 state backup
# s3:
#   enabled: true
#   tenant_id: my-tenant       # S3 key prefix
#   bucket: adi-state
#   region: us-east-1
#   endpoint_url: http://localhost:9000  # MinIO/LocalStack

# Override Docker toolkit image
# toolkit:
#   image_tag: "latest"
```

{% hint style="info" %}
For L3 chains, `ecosystem.rpc_url` points to your settlement layer. For deployments on ADI, that means ADI Chain. If `base_token_address` is omitted, ADI is the gas token. To use a custom token, set `base_token_address` before `adi init`.
{% endhint %}

## Config Resolution Priority

Config file sources are mutually exclusive — only one file is loaded:

1. `--config` flag
2. `ADI_CONFIG` environment variable
3. `~/.adi_cli/.adi.yml` (default)
4. `~/.adi.yml` (legacy fallback, deprecated — the CLI warns and suggests moving it to `~/.adi_cli/`)

Override sources are always applied on top:

5. `ADI__*` environment variables
6. CLI flags (highest priority)

## Environment Variables

| Variable                  | Purpose                                                      |
| ------------------------- | ------------------------------------------------------------ |
| `ADI_FUNDER_KEY`          | Private key (hex) of the wallet that funds ecosystem wallets |
| `ADI_PRIVATE_KEY`         | Private key (hex) for accepting ownership as new owner       |
| `ADI_RPC_URL`             | Settlement layer RPC endpoint                                |
| `ADI_EXPLORER_URL`        | Block explorer API URL for contract verification             |
| `ADI_EXPLORER_API_KEY`    | Block explorer API key                                       |
| `ADI_CONFIG`              | Path to an alternative config file                           |
| `ADI__PROTOCOL_VERSION`   | Default protocol version                                     |
| `ADI__TOOLKIT__IMAGE_TAG` | Override Docker image tag                                    |
| `ADI_OPERATOR`            | Operator address (PRECOMMITTER, COMMITTER, REVERTER)         |
| `ADI_PROVE_OPERATOR`      | Prove operator address (PROVER)                              |
| `ADI_EXECUTE_OPERATOR`    | Execute operator address (EXECUTOR)                          |
| `AWS_ACCESS_KEY_ID`       | AWS access key for S3 state sync                             |
| `AWS_SECRET_ACCESS_KEY`   | AWS secret key for S3 state sync                             |
| `ADI__S3__ENABLED`        | Enable S3 sync (`true`/`false`)                              |
| `ADI__S3__TENANT_ID`      | Tenant identifier for S3 key prefix                          |
| `ADI__S3__BUCKET`         | S3 bucket name                                               |
| `RUST_LOG`                | Logging verbosity: `error`, `warn`, `info`, `debug`, `trace` |

Override any config value using the `ADI__` prefix with double underscores as path separators:

```bash
export ADI__ECOSYSTEM__NAME=production
export ADI__ECOSYSTEM__RPC_URL=http://localhost:8545
```

## S3 State Synchronization

When enabled, the CLI archives and uploads ecosystem state to S3 after write operations (init, deploy). Archives are stored at `s3://{bucket}/{tenant_id}/{ecosystem-name}.tar.gz`.

```bash
# Sync current state to S3
adi state sync --ecosystem-name my-ecosystem

# Restore state from S3
adi state restore --ecosystem-name my-ecosystem

# Force restore (overwrite local state)
adi state restore --ecosystem-name my-ecosystem --force
```

{% tabs %}
{% tab title="S3 Config" %}

```yaml
s3:
  enabled: true
  tenant_id: my-tenant
  bucket: adi-state
  region: us-east-1
```

{% endtab %}

{% tab title="MinIO (Local Dev)" %}

```bash
docker run -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"
```

```yaml
s3:
  enabled: true
  tenant_id: dev
  bucket: adi-state
  endpoint_url: http://localhost:9000
```

Set credentials via `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
{% endtab %}
{% endtabs %}

## Deprecated Fields

These fields still work but will be removed in a future release:

| Field                 | Replacement                                             |
| --------------------- | ------------------------------------------------------- |
| Top-level `ownership` | `ecosystem.ownership` or `ecosystem.chains[].ownership` |
| Top-level `operators` | `ecosystem.chains[].operators`                          |

{% hint style="warning" %}
The former `chains[].blobs` boolean has been **removed** and replaced by `chains[].pubdata_mode`. A leftover `blobs:` key in an old config is silently ignored — the chain falls back to the default `pubdata_mode: calldata`. See the [CLI changelog (v0.4.0)](https://github.com/ADI-Foundation-Labs/ADI-CLI/blob/main/CHANGELOG.md#040---2026-07-23) for details.
{% endhint %}


# Changelog

Release history for the ADI Server - the node behind ADI's sequencer and external nodes.

### Deployed versions

| Component      | Mainnet version | Testnet version |
| -------------- | --------------- | --------------- |
| Protocol       | `v0.30.1`       | `v0.30.1`       |
| Sequencer      | `v0.21.1-b4`    | `v0.21.1-b4`    |
| External nodes | `v0.21.1-b4`    | `v0.21.1-b4`    |

### Release notes

## [v0.21.1-b4](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.21.1-b4)

*2026-09-07 - latest*

Current sequencer and external node version on testnet and mainnet, superseding `v0.13.0-b4`. This covers everything ADI changed across the `v0.13` -> `v0.21` line, plus the one upstream change that every node operator has to act on: block replay moved off HTTP onto devp2p.

**Breaking changes**

* **network** - block replay no longer runs over HTTP. External nodes now sync over [devp2p](https://github.com/ethereum/devp2p) on port `3060` (TCP for RLPx, UDP for peer discovery) instead of pulling replay records from the sequencer's HTTP replay server on port `3053`. An external node left on the HTTP-replay build stops syncing and cannot peer with the network.
  * Removed config: `sequencer_block_replay_download_address`, `sequencer_block_replay_server_enabled`, `sequencer_block_replay_server_address`.
  * New required config on external nodes: `general_node_role: external`.
  * The cluster needs at least one node reachable at a stable address for ENs to peer against, and networking must be enabled on main and external nodes alike.
  * See the [v0.13.0 -> v0.20.12 migration guide](https://github.com/ADI-Foundation-Labs/ADI-Stack-EN-Setup-script/blob/main/upgrades/v0.13.0_to_v0.20.12.md) for the full rollout.

**Features**

* **network** - resolve hostnames in `enode` URLs, so boot nodes can be configured by DNS name instead of a hardcoded IP (#16).
* **network** - raise the P2P active-connection cap from 25 to 100, apply it to inbound connections, and disable peer rotation, so a growing external-node fleet is not churned off the sequencer.
* **l1** - retry L1 requests in the L1 sender and in the commit and execute watchers, instead of failing the operation on the first transient RPC error (#17).
* **rocksdb** - cap open files at 4096 and move SST index and bloom filters into a sized block cache (2 GiB repository, 2 GiB state, 1 GiB preimages) on read-heavy databases. The previous defaults pinned every SST file's index and filter in RAM permanently, so memory grew without bound as the database grew.
* **memory** - use jemalloc as the global allocator. On mainnet, app plus allocator overhead measured \~22 GiB on the glibc build versus \~3 GiB on the jemalloc build under the same database and workload, because jemalloc returns freed memory to the OS.
* **prover** - support circuit-soundness proving versions V8 (protocol v30.2) and V9 (protocol v31.1).

**Fixes**

* **network** - an external node whose replay connection goes quiet now disconnects and re-requests instead of stalling indefinitely. The tolerated silence is configurable via `en_replay_inactivity_timeout` (default 300s); it must comfortably exceed the chain's block cadence, so raise it on chains that can legitimately idle for longer (#1513).
* **sequencer** - apply runtime fee overrides before the mempool reads the base fee, so an override takes effect on the next block instead of one block late.
* **rpc(fees)** - propagate a config-override base fee to every fee-exposing RPC endpoint, derive `native_price` from the base fee when it is not set explicitly, and default `pubdata_price` to `0` unless overridden, so the base fee covers all additional cost.
* **rpc(estimation)** - `eth_estimateGas` caps the gas limit by what the sender can afford, but divided the balance by the block base fee while validation charges the effective gas price. Any request carrying a priority fee therefore produced an unaffordable cap and failed with `LackOfFundForMaxFee` for every account below `fee_cap * block_gas_limit`. The allowance is now divided by the request's fee cap, which is affordable by construction.
* **merkle\_tree** - handle empty batch proofs instead of failing on them (#1535).
* **rpc** - log config-override messages at debug instead of info.

**Images**

* `harbor.sde.adifoundation.ai/adi-public/chain/server:v0.21.1-b4`

## [v0.13.0-b4](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.13.0-b4)

*2026-06-30*

L1 watcher hardening.

**Fixes**

* **l1-watcher** - leave 2 confirmation blocks unprocessed, matching the upgrade watcher's behavior, to avoid `block range extends beyond current head block` errors.
* **tests** - add the missing private API server config and bind it to a dynamically allocated port.

**Images**

* `harbor.sde.adifoundation.ai/adi-public/chain/external-node@v0.13.0-b4`
* `harbor.sde.adifoundation.ai/adi-public/chain/server@v0.13.0-b4`

## [v0.13.0-b3](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.13.0-b3)

*2026-06-11*

The `zks` finality RPC and ADI-native CI/CD.

**Features**

* **rpc(finality)** - add `zks_getBlockFinality`, `zks_getTransactionFinality`, `zks_getBatchFinality`, and `zks_getFinalityStatus`, reporting how far a block, transaction, or batch has moved through L1 finality (`pending` -> `committed` -> `executed`), plus a single-call snapshot of every finality frontier. See the [JSON-RPC API](https://github.com/ADI-Foundation-Labs/ADI-Network-Documentation/blob/main/references/json-rpc-api.md#finality).

**CI/CD**

* Switch to ADI-native CI/CD workflows (multi-arch container image build and push to GHCR).

## [v0.13.0-b2](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.13.0-b2)

*2026-06-03*

L1 block-finality hardening.

**Fixes**

* **l1** - add 2 confirmation blocks to mitigate reorg issues in L1 block finality.

## [v0.13.0-b1](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.13.0-b1)

*2026-02-04*

Replay-connection resilience.

**Fixes**

* **replay** - retry on a dropped replay connection and raise the initial delay to 500s (#12).

## [v0.13.0-b](https://github.com/ADI-Foundation-Labs/ADI-Stack-Server/releases/tag/v0.13.0-b)

*First ADI beta*

First ADI beta over upstream `v0.13.0` - a private runtime-config RPC plus RPC and mempool fixes.

**Features**

* **rpc(private)** - private RPC API for runtime configuration overrides, with on-disk persistence, `config_removeOverrides`, and `pubdata_price = 0` support.

**Fixes**

* **rpc** - fix gas estimation for accounts with small balances.
* **mempool** - use the pending base fee on canonical-state change (#9); add a transaction fee cap (#8).
* **fees** - fee estimation now honours config overrides (#7).

**Chores**

* Integrate ADI repositories and improvements; refresh dependencies, README, licenses, and authors.


# Whitepaper

Access the ADI whitepaper and audit whitepaper.

#### Whitepaper

{% embed url="<https://drive.google.com/file/d/11Q1mZA-TRlTKA2tXLsKSyCyemI2mIgMG/preview>" %}

#### MiCA Whitepaper

{% embed url="<https://drive.google.com/file/d/1LQCJA-3mhLGrU5jdB3poOyHGc7uaSeM6/view?usp=sharing>" %}


# ADI DLT Framework

View documentation outlining the DLT Framework of the ADI Foundation, describing technical architecture, security model, governance structure, and operational control framework supporting ADI

{% embed url="<https://drive.google.com/file/d/1AN5WgRgrn6v0nEcnXB0uNyKEUr3PkU5N/preview>" %}


# Audits

This page contains published reports on audits for ADI Chain

Find below completed public audit reports:

<table><thead><tr><th width="178.75">Auditor</th><th>Report</th></tr></thead><tbody><tr><td>OpenZeppelin</td><td><a href="https://www.openzeppelin.com/news/adi-token-audit">https://www.openzeppelin.com/news/adi-token-audit</a></td></tr><tr><td>Hacken</td><td><a href="https://hacken.io/audits/adi-chain/">https://hacken.io/audits/adi-chain/</a></td></tr></tbody></table>


# Financial Statements

Reports & Financial Statements of ADI DLT Foundation

{% embed url="<https://drive.google.com/file/d/1PLEFGob149TL2A3NyRil_ugIMyvyGoi7/preview>" %}


