# Armchain Developer Docs

## Welcome to Armchain

**Post-Quantum Secure · EVM-Compatible · Instant Finality**

Armchain is a Layer 1 blockchain built for the post-quantum era. If you're familiar with Ethereum development, you'll feel right at home, deploy the same Solidity contracts you already know, but with quantum-resistant security and instant transaction finality.

Under the hood, Armchain replaces traditional ECDSA signatures with **ML-DSA44** (a NIST-standardized post-quantum algorithm) and uses **Lachesis aBFT** consensus so transactions are final the moment they're confirmed. No waiting, no rollbacks.

[Get Started](/get-started/overview) · [Quickstart](/get-started/quickstart) · [SDK Reference](/developers/overview-1) · [API Docs](/api-reference/json-rpc)

***

### Why Armchain?

* **Quantum-resistant from day one**: All signatures use ML-DSA44 (NIST FIPS 204), protecting against future quantum attacks
* **Native Account Abstraction**: Smart contract accounts are first-class citizens, with support for Passkey (biometric), SLH-DSA, MPC, and custom validation schemes
* **Instant finality**: Transactions are final the moment they're confirmed. No waiting for block confirmations
* **EVM compatible**: Write and deploy Solidity smart contracts just like on Ethereum (London fork)
* **High throughput**: DAG-based consensus lets all validators work in parallel, not one at a time
* **ARM token**: Native token for gas fees, staking, and governance

### Network at a Glance

| Parameter               | Value                             |
| ----------------------- | --------------------------------- |
| **Chain ID**            | `55`                              |
| **Native Token**        | ARM                               |
| **RPC URL**             | `https://www.armchain.org/devnet` |
| **Finality**            | Instant (aBFT)                    |
| **EVM Version**         | London                            |
| **Signature Algorithm** | ML-DSA44                          |

> **Quick test**: Verify connectivity with a single curl command:
>
> ```bash
> curl -s -X POST https://www.armchain.org/devnet \
>   -H "Content-Type: application/json" \
>   -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
> ```

***

### Documentation

#### Getting Started

| Guide                                                       | Description                           |
| ----------------------------------------------------------- | ------------------------------------- |
| [Overview](/get-started/overview)                           | What Armchain is and why it matters   |
| [Quickstart](/get-started/quickstart)                       | Deploy your first contract in minutes |
| [Network Configuration](/get-started/network-configuration) | Connect your tools to Armchain        |

#### Developers

| Guide                                         | Description                                    |
| --------------------------------------------- | ---------------------------------------------- |
| [Smart Contracts](/developers/overview)       | Writing and deploying Solidity contracts       |
| [Armchain Ethers SDK](/developers/overview-1) | Drop-in ethers.js replacement with PQC signing |
| [Wallet Integration](/developers/overview-2)  | Connect your dApp to a wallet                  |

#### Architecture

| Guide                                                        | Description                                     |
| ------------------------------------------------------------ | ----------------------------------------------- |
| [Consensus (Lachesis aBFT)](/architecture/consensus)         | How Armchain achieves instant finality          |
| [DAG Structure](/architecture/dag)                           | The directed acyclic graph powering the network |
| [EVM Integration](/architecture/evm)                         | How the EVM runs on Armchain                    |
| [Transaction Lifecycle](/architecture/transaction-lifecycle) | From submission to finality                     |

#### Post-Quantum Cryptography

| Guide                                       | Description                                 |
| ------------------------------------------- | ------------------------------------------- |
| [PQC Overview](/pqc/overview)               | Why Armchain uses post-quantum cryptography |
| [ML-DSA44 Deep Dive](/pqc/mldsa44)          | Algorithm parameters and performance        |
| [Transaction Types](/pqc/transaction-types) | Type 3 PQC transaction format               |

#### Account Abstraction

| Guide                                                         | Description                                               |
| ------------------------------------------------------------- | --------------------------------------------------------- |
| [Overview](/account-abstraction/overview)                     | How native AA works on Armchain                           |
| [Validation Schemes](/account-abstraction/validation-schemes) | MLDSA, Passkey, SLH-DSA, MPC, and custom schemes          |
| [Paymaster](/account-abstraction/paymaster)                   | Gas sponsorship, Foundation Paymaster, and staking policy |
| [System Contracts](/account-abstraction/system-contracts)     | Genesis contracts, Yul implementation, developer notes    |

#### Node Operators

| Guide                                                    | Description                         |
| -------------------------------------------------------- | ----------------------------------- |
| [Node Types](/node-operators/node-types)                 | Choose the right node configuration |
| [Fakenet](/node-operators/fakenet)                       | Local development network           |
| [Configuration Reference](/node-operators/configuration) | All node configuration options      |

#### Tools & Infrastructure

| Guide                             | Description                                |
| --------------------------------- | ------------------------------------------ |
| [Block Explorer](/tools/explorer) | Browse blocks, transactions, and contracts |
| [Armchain Wallet](/tools/wallet)  | Official browser extension wallet          |
| [Faucet](/tools/faucet)           | Get devnet ARM tokens                      |

#### API Reference

| Guide                                        | Description                                     |
| -------------------------------------------- | ----------------------------------------------- |
| [JSON-RPC API](/api-reference/json-rpc)      | Ethereum-compatible JSON-RPC endpoints          |
| [Armchain APIs](/api-reference/armchain-api) | DAG, validator, and Armchain-specific endpoints |

#### Resources

| Guide                                   | Description                 |
| --------------------------------------- | --------------------------- |
| [FAQ](/resources/faq)                   | Answers to common questions |
| [Glossary](/resources/glossary)         | Key terms and definitions   |
| [Contributing](/resources/contributing) | How to contribute           |

***

[**armchain.org**](https://www.armchain.org) · [**Explorer**](https://www.armchain.org/explorer)

*Maintained by the Armchain team · Last updated June 2026*


# Get Started

New to Armchain? Start here. These guides will get you connected to the network and deploying your first contract in minutes.

| Guide                                                       | Description                                                           |
| ----------------------------------------------------------- | --------------------------------------------------------------------- |
| [Overview](/get-started/overview)                           | What Armchain is, how it works, and why post-quantum security matters |
| [Quickstart](/get-started/quickstart)                       | Deploy your first contract step-by-step in under 10 minutes           |
| [Network Configuration](/get-started/network-configuration) | RPC endpoints, Chain ID, and how to connect your tools                |


# Overview

## What is Armchain?

Armchain is a **quantum-resistant** Layer 1 blockchain. Think of it as an Ethereum-compatible chain that swaps out the traditional signature scheme (ECDSA) for a quantum-safe one (**ML-DSA44**, standardized by NIST in FIPS 204). This means your smart contracts, tokens, and dApps get the same developer experience you're used to, but with cryptography designed to withstand future quantum computers.

Armchain uses the **Lachesis aBFT** consensus protocol for **instant finality**. Once a transaction is confirmed, it's permanent with no chance of rollbacks or reorganizations. The network maintains full **EVM compatibility** (London fork), so standard Solidity contracts deploy without any changes.

## Why Armchain?

### The Quantum Threat: Why It Matters

Current blockchain networks (Ethereum, Bitcoin, etc.) rely on ECDSA signatures based on the secp256k1 elliptic curve. A sufficiently powerful quantum computer running Shor's algorithm could break these signatures, which would mean:

* **Signature forgery**: Attackers could authorize transactions from any address
* **Private key recovery**: On-chain public keys could be used to derive private keys
* **Consensus takeover**: Validator authentication could be compromised

While large-scale quantum computers don't exist yet, the "harvest now, decrypt later" threat is real today. Adversaries can store signed blockchain data now and attempt to crack it later when quantum hardware matures.

### How Armchain Addresses This

Armchain uses post-quantum cryptography at every layer of the protocol:

| Layer                     | Classical (Ethereum)     | Armchain (Post-Quantum)                 |
| ------------------------- | ------------------------ | --------------------------------------- |
| **User Transactions**     | ECDSA (secp256k1)        | ML-DSA44 (FIPS 204)                     |
| **Validator Signatures**  | ECDSA                    | ML-DSA44                                |
| **Wallet Key Derivation** | BIP-32/44 with secp256k1 | BIP-32/44 with ML-DSA44 (hardened only) |
| **Address Format**        | keccak256(ECDSA pubkey)  | keccak256(ML-DSA44 pubkey)              |
| **Transaction Type**      | Type 0/1/2               | Type 3 (PQC)                            |

### Instant Finality

Unlike Ethereum where you might wait several minutes for full finality, Armchain's Lachesis aBFT consensus provides **deterministic, immediate finality**:

* **No rollbacks**: Confirmed transactions are irreversibly final
* **Asynchronous**: No synchronous rounds or timeouts
* **Byzantine fault tolerant**: Tolerates up to 1/3 malicious validators
* **DAG-based**: Parallel event processing via a directed acyclic graph

### EVM Compatibility

If you've built on Ethereum, you can build on Armchain. The network runs a full Ethereum Virtual Machine (London fork):

* Standard **Solidity** contracts deploy without modification
* **Remix IDE** for development and deployment
* **Hardhat** for compilation and project structure (signing requires the Armchain Ethers SDK)
* **Armchain Ethers SDK** (`@armchain-ethersv6/ethers`) for dApp development
* Standard **JSON-RPC** APIs (`eth_sendTransaction`, `eth_call`, etc.)
* **ERC-20**, **ERC-721**, **ERC-1155** token standards
* EIP-1559 gas pricing and EIP-2930 access lists

## Architecture at a Glance

Here's how the different layers of Armchain fit together:

| Layer                       | Components                                          |
| --------------------------- | --------------------------------------------------- |
| **Applications**            | dApps, Wallets, Smart Contracts                     |
| **JSON-RPC API**            | `eth`, `arm`, `dag`, `admin`, `personal` namespaces |
| **EVM**                     | Ethereum Virtual Machine (London fork rules)        |
| **Transaction Processing**  | Type 3 PQC transactions, EIP-1559 gas               |
| **Lachesis aBFT Consensus** | DAG ordering, event emission, instant finality      |
| **ML-DSA44 Cryptography**   | Signatures, key management, validation              |
| **P2P Network**             | devp2p, gossip protocol, peer discovery             |

## Armchain vs. Other Chains

| Feature        | Armchain      | Ethereum     | Polygon PoS  | Arbitrum          |
| -------------- | ------------- | ------------ | ------------ | ----------------- |
| Consensus      | Lachesis aBFT | Gasper (PoS) | pBFT + PoS   | Optimistic Rollup |
| Finality       | Instant       | \~15 min     | \~2 min      | \~7 days (L1)     |
| Quantum Safe   | ✅ ML-DSA44    | ❌ ECDSA      | ❌ ECDSA      | ❌ ECDSA           |
| EVM Compatible | ✅             | ✅ (native)   | ✅            | ✅                 |
| Layer          | L1            | L1           | L1 sidechain | L2                |
| Native Token   | ARM           | ETH          | POL          | ETH               |

## Who is This Documentation For?

Whether you're an experienced blockchain developer or just getting started, you'll find guides tailored to your role:

* **dApp Developers**: Smart contract deployment and interaction
* **SDK/Library Authors**: Integrating post-quantum signing into tooling
* **Node Operators**: Running full/archive nodes and validators
* **Wallet Developers**: Implementing quantum-safe key management
* **Researchers**: Post-quantum blockchain architecture

## Next Steps

* [Quickstart →](/get-started/quickstart) Deploy your first contract in 10 minutes
* [Network Configuration →](/get-started/network-configuration) Connect your tools and wallet to Armchain
* [Architecture Deep Dive →](/architecture/consensus) Learn how Armchain works under the hood


# Quickstart

Get a Solidity smart contract running on Armchain in just a few minutes. This guide uses Remix IDE and the Armchain Ethers SDK, no complex local setup required.

> **Coming from Ethereum?** The workflow is very similar. The main difference is that Armchain uses ML-DSA44 (post-quantum) signatures instead of ECDSA, so you'll use the Armchain Ethers SDK instead of standard ethers.js for any signing operations.

> **Tooling note**: Hardhat and Foundry do not natively support ML-DSA44 signing. Use **Remix IDE** for contract development and the [**Armchain Ethers SDK**](/developers/overview-1) for programmatic interaction.

## Prerequisites

* [Node.js](https://nodejs.org/) v18+
* **Armchain Wallet** browser extension connected to devnet
* Familiarity with Solidity and Ethereum development

## Step 1: Write Your Contract in Remix

1. Open [Remix IDE](https://remix.ethereum.org) in your browser
2. In the **File Explorer**, create a new file: `HelloArmchain.sol`
3. Paste the following contract:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract HelloArmchain {
    string public greeting;
    address public owner;

    event GreetingChanged(string oldGreeting, string newGreeting, address changedBy);

    constructor(string memory _greeting) {
        greeting = _greeting;
        owner = msg.sender;
    }

    function setGreeting(string memory _newGreeting) external {
        string memory oldGreeting = greeting;
        greeting = _newGreeting;
        emit GreetingChanged(oldGreeting, _newGreeting, msg.sender);
    }

    function getGreeting() external view returns (string memory) {
        return greeting;
    }
}
```

## Step 2: Compile

1. Open the **Solidity Compiler** tab in Remix (the `<S>` icon)
2. Select compiler version **0.8.24**
3. Set the EVM version to **london** under Advanced Settings
4. Click **Compile HelloArmchain.sol**

## Step 3: Deploy to Armchain

1. Open the **Deploy & Run Transactions** tab in Remix
2. Under **Environment**, select **Injected Provider**. Remix will connect to your Armchain Wallet
3. Make sure your wallet is connected to the **Armchain Devnet** (Chain ID: 55)
4. In the constructor field, enter `"Hello from Armchain!"`
5. Click **Deploy** and confirm the transaction in your wallet

Your contract address will appear in the **Deployed Contracts** panel.

> For local development without a wallet, see [Fakenet Setup](/node-operators/fakenet) to run a local Armchain network and use the SDK directly.

## Step 4: Interact with Your Contract

Use the **Armchain Ethers SDK** to interact with your deployed contract from JavaScript or TypeScript.

Install the SDK:

```bash
npm install @armchain-ethersv6/ethers
```

Then interact with your contract:

```typescript
import { JsonRpcProvider, Wallet, Contract } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

// Paste the ABI from Remix (Compiler tab → ABI button)
const abi = [
  "function getGreeting() view returns (string)",
  "function setGreeting(string) external",
  "event GreetingChanged(string, string, address)"
];

const contract = new Contract("YOUR_CONTRACT_ADDRESS", abi, wallet);

// Read
const greeting = await contract.getGreeting();
console.log("Current greeting:", greeting);

// Write
const tx = await contract.setGreeting("Quantum-safe greetings!");
await tx.wait();
console.log("Greeting updated!");
```

## Alternative: Hardhat + Armchain Ethers SDK

> **⚠ Partial Compatibility**: Hardhat does not natively support ML-DSA44 signing. `npx hardhat compile` works, but Hardhat's built-in accounts and `hre.ethers` signers use ECDSA and **cannot sign Armchain transactions**. All signing must go through `@armchain-ethersv6/ethers`. Deployment scripts, fork testing, and self-funding are **not supported** via standard Hardhat workflows.

Hardhat can be used for **compilation and project structure**:

```bash
mkdir my-armchain-dapp && cd my-armchain-dapp
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npm install @armchain-ethersv6/ethers
npx hardhat init
```

Configure `hardhat.config.js` for Armchain:

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

module.exports = {
  solidity: {
    version: "0.8.24",
    settings: { evmVersion: "london" },
  },
  networks: {
    armchain: {
      url: "https://www.armchain.org/devnet",
      chainId: 55,
    },
    "armchain-local": {
      url: "http://localhost:4000",
      chainId: 55,
    },
  },
};
```

Compile your contracts:

```bash
npx hardhat compile
```

For deployments, write scripts using `@armchain-ethersv6/ethers` directly instead of `hre.ethers`:

```javascript
// scripts/deploy.js - uses Armchain Ethers SDK for signing, not hre.ethers
import { JsonRpcProvider, Wallet, ContractFactory } from "@armchain-ethersv6/ethers";
import HelloArmchainArtifact from "../artifacts/contracts/HelloArmchain.sol/HelloArmchain.json" assert { type: "json" };

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

const factory = new ContractFactory(HelloArmchainArtifact.abi, HelloArmchainArtifact.bytecode, wallet);
const contract = await factory.deploy("Hello from Armchain!");
await contract.waitForDeployment();
console.log("Deployed to:", await contract.getAddress());
```

> **Note**: Do not use `hre.ethers.getSigners()`, `hre.ethers.getContractFactory()`, or `deployer.sendTransaction()`. These all use ECDSA and will fail on Armchain.

## Next Steps

* [Network Configuration](/get-started/network-configuration) for RPC endpoints, chain details, and wallet setup
* [Smart Contract Development](/developers/overview) for contract patterns and best practices
* [Armchain Ethers SDK](/developers/overview-1) for the full SDK reference
* [Understanding PQC](/pqc/overview) to learn about the cryptography powering Armchain


# Network Configuration

Everything you need to connect your development tools, wallets, and applications to Armchain.

## Armchain Devnet

| Parameter               | Value                             |
| ----------------------- | --------------------------------- |
| **Network Name**        | Armchain Devnet                   |
| **Chain ID**            | `55`                              |
| **Currency Symbol**     | ARM                               |
| **Currency Decimals**   | 18                                |
| **RPC URL (HTTP)**      | `https://www.armchain.org/devnet` |
| **Transaction Type**    | Type 3 (PQC)                      |
| **Signature Algorithm** | ML-DSA44                          |

## Armchain Local (Fakenet)

| Parameter             | Value                   |
| --------------------- | ----------------------- |
| **Network Name**      | Armchain Local          |
| **Chain ID**          | `55`                    |
| **Currency Symbol**   | ARM                     |
| **Currency Decimals** | 18                      |
| **RPC URL (HTTP)**    | `http://localhost:4000` |
| **WebSocket URL**     | `ws://localhost:4500`   |
| **Block Explorer**    | -                       |

> See [Fakenet Setup](/node-operators/fakenet) for instructions on running a local development network.

## Connecting Tools

### Hardhat

> **⚠ Partial Compatibility**: Hardhat's built-in signers use ECDSA and **cannot sign Armchain transactions**. Configure network connectivity as below, but use `@armchain-ethersv6/ethers` for all signing operations. Do **not** use `hre.ethers.getSigners()` or `hre.ethers.getContractFactory().deploy()` as they will fail.

```javascript
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: {
    version: "0.8.24",
    settings: { evmVersion: "london" },
  },
  networks: {
    armchain: {
      url: "https://www.armchain.org/devnet",
      chainId: 55,
      // Do not set 'accounts' here - use @armchain-ethersv6/ethers for signing
    },
    "armchain-local": {
      url: "http://localhost:4000",
      chainId: 55,
    },
  },
};
```

### Armchain Ethers SDK

```typescript
import { JsonRpcProvider, Wallet } from "@armchain-ethersv6/ethers";

// Connect to devnet
const provider = new JsonRpcProvider("https://www.armchain.org/devnet");

// Connect to local network
const localProvider = new JsonRpcProvider("http://localhost:4000");

// Create a wallet
const wallet = new Wallet(privateKey, provider);

// Check balance
const balance = await provider.getBalance(wallet.address);
console.log("Balance:", balance.toString(), "wei");
```

### Standard ethers.js (Read-Only)

For read-only operations (querying balances, reading contract state), standard ethers.js can connect to Armchain RPC:

```typescript
import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider("https://www.armchain.org/devnet");

// Read-only operations work with standard ethers
const balance = await provider.getBalance("0x...");
const blockNumber = await provider.getBlockNumber();
```

> **Important**: Signing transactions requires `@armchain-ethersv6/ethers` with ML-DSA44 support. Standard ethers.js cannot produce valid Armchain signatures.

### curl (Direct RPC)

```bash
# Get latest block number
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# Get balance
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xYOUR_ADDRESS","latest"],"id":1}'

# Get chain ID
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

## Default Ports

When running your own node, these are the default ports:

| Port    | Protocol | Description                        |
| ------- | -------- | ---------------------------------- |
| `5050`  | TCP/UDP  | P2P (devp2p) network communication |
| `18545` | TCP      | HTTP JSON-RPC                      |
| `18546` | TCP      | WebSocket JSON-RPC                 |

When running a Fakenet demo:

| Port Pattern | Description                      |
| ------------ | -------------------------------- |
| `3000 + i`   | P2P port for validator `i`       |
| `4000 + i`   | HTTP RPC port for validator `i`  |
| `4500 + i`   | WebSocket port for validator `i` |

## API Namespaces

Armchain supports the following JSON-RPC API namespaces:

| Namespace  | Description                                         |
| ---------- | --------------------------------------------------- |
| `eth`      | Standard Ethereum API (transactions, blocks, state) |
| `net`      | Network information                                 |
| `web3`     | Web3 utility methods                                |
| `personal` | Account management (lock/unlock)                    |
| `txpool`   | Transaction pool inspection                         |
| `debug`    | Debugging and tracing                               |
| `admin`    | Node administration (peers, etc.)                   |
| `arm`      | Armchain-specific methods                           |
| `dag`      | DAG-specific queries                                |

## Gas Configuration

| Parameter                        | Value                                      |
| -------------------------------- | ------------------------------------------ |
| **Minimum Gas Price**            | 1 Gwei (`1000000000` wei)                  |
| **Max Block Gas**                | 20,500,000                                 |
| **Default Gas Limit (transfer)** | 21,000                                     |
| **Gas Price Model**              | EIP-1559 (dynamic base fee + priority fee) |

## Next Steps

* [Quickstart](/get-started/quickstart) to deploy your first contract
* [JSON-RPC Reference](/api-reference/json-rpc) for full API documentation
* [Node Types](/node-operators/node-types) for GC modes, sync modes, and resource requirements


# Developers

Everything you need to build on Armchain. If you've built on Ethereum before, the smart contract side is virtually identical. The key difference is how transactions are signed.

| Guide                                   | Description                                                                                           |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| [Smart Contracts](/developers/overview) | Write, test, and deploy Solidity contracts (same as Ethereum)                                         |
| [Ethers SDK](/developers/overview-1)    | The Armchain JavaScript/TypeScript SDK, a drop-in replacement for ethers.js with post-quantum signing |
| [Wallets](/developers/overview-2)       | Connect your dApp to the Armchain Wallet, or build your own wallet with PQC support                   |


# Smart Contracts

Writing, testing, and deploying Solidity smart contracts on Armchain.

## Overview

If you've built smart contracts on Ethereum, you already know how to build on Armchain. The EVM is fully compatible (London fork), so **no Solidity changes are required**. The main differences are at the network level:

* Transactions use ML-DSA44 signatures (not ECDSA), handled by the SDK
* Transaction type is 3 (PQC), set automatically by the SDK
* Finality is instant, no need to wait for confirmations

## Development Setup

### Option 1: Remix IDE

No local setup required.

1. Open [Remix IDE](https://remix.ethereum.org)
2. Write your Solidity contract
3. In the **Solidity Compiler** tab, select version `0.8.24` and set EVM version to **london**
4. In the **Deploy & Run Transactions** tab, select **Injected Provider** (with Armchain Wallet connected)
5. For local development, select **External HTTP Provider** and enter `http://localhost:4000`

### Option 2: Hardhat (Partial Compatibility)

> **⚠ Compatibility Warning**: Hardhat's built-in signers and `hre.ethers` use ECDSA and **cannot sign Armchain transactions**. The following features are **not supported** with standard Hardhat:
>
> * `hre.ethers.getSigners()` / `hre.ethers.getContractFactory().deploy()` (ECDSA signing fails)
> * Self-funding test accounts
> * Fork testing against Armchain
>
> Hardhat **can** be used for: `npx hardhat compile`, artifact generation, and project structure. All signing must go through `@armchain-ethersv6/ethers`.

```bash
mkdir my-armchain-project && cd my-armchain-project
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npm install @armchain-ethersv6/ethers
npx hardhat init
```

Configure for Armchain:

```javascript
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: {
    version: "0.8.24",
    settings: {
      optimizer: { enabled: true, runs: 200 },
      evmVersion: "london",
    },
  },
  networks: {
    armchain: {
      url: "https://www.armchain.org/devnet",
      chainId: 55,
    },
    "armchain-local": {
      url: "http://localhost:4000",
      chainId: 55,
    },
  },
};
```

Compile with:

```bash
npx hardhat compile
```

## Writing Contracts

### Supported Solidity Versions

Use Solidity `0.8.x`. Recommended:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
```

### EVM Version

Set the EVM target to `london` in your compiler settings. Do **not** use:

* `shanghai` (`PUSH0` opcode not supported)
* `cancun` (transient storage not supported)

### Standard Patterns

All common Solidity patterns work on Armchain:

#### ERC-20 Token

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor(uint256 initialSupply) 
        ERC20("My Armchain Token", "MAT") 
        Ownable(msg.sender) 
    {
        _mint(msg.sender, initialSupply * 10 ** decimals());
    }

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}
```

#### NFT (ERC-721)

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFT is ERC721, ERC721URIStorage, Ownable {
    uint256 private _nextTokenId;

    constructor() ERC721("Armchain NFT", "ANFT") Ownable(msg.sender) {}

    function safeMint(address to, string memory uri) external onlyOwner {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // Required overrides
    function tokenURI(uint256 tokenId)
        public view override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public view override(ERC721, ERC721URIStorage)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
```

#### Upgradeable Contract (Proxy Pattern)

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract MyUpgradeable is Initializable, OwnableUpgradeable, UUPSUpgradeable {
    uint256 public value;

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    function initialize(uint256 _value) public initializer {
        __Ownable_init(msg.sender);
        __UUPSUpgradeable_init();
        value = _value;
    }

    function setValue(uint256 _value) external onlyOwner {
        value = _value;
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}
```

## Testing

> **⚠ Compatibility Warning**: Hardhat's default test environment (`hre.ethers.getSigners()`) provides ECDSA signers that cannot send Armchain transactions. Hardhat tests that deploy contracts or send transactions will fail. Unit tests that only test pure Solidity logic (no state changes) work fine.

For contract interaction tests, use the Armchain Ethers SDK with a [local Fakenet](/node-operators/fakenet):

```javascript
// test with Armchain Ethers SDK against a local fakenet
import { JsonRpcProvider, Wallet, ContractFactory } from "@armchain-ethersv6/ethers";
import MyTokenArtifact from "../artifacts/contracts/MyToken.sol/MyToken.json" assert { type: "json" };

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(process.env.FAKENET_KEY, provider);

// Deploy
const factory = new ContractFactory(MyTokenArtifact.abi, MyTokenArtifact.bytecode, wallet);
const token = await factory.deploy(1000000);
await token.waitForDeployment();

// Assert
const supply = await token.totalSupply();
console.assert(supply === 1000000n * 10n ** 18n, "Unexpected supply");
```

Remix IDE also has a built-in **Solidity Unit Testing** plugin for simpler logic checks.

## Deploying

### Remix IDE

Deploy using **Remix IDE** with the **Injected Provider** environment (Armchain Wallet). For a step-by-step walkthrough, see the [Quickstart](/get-started/quickstart).

### Hardhat + Armchain Ethers SDK

> **⚠ Do not use `hre.ethers` for deployments.** Use `@armchain-ethersv6/ethers` with artifacts generated by `npx hardhat compile`.

```javascript
// scripts/deploy.js
import { JsonRpcProvider, Wallet, ContractFactory } from "@armchain-ethersv6/ethers";
import MyTokenArtifact from "../artifacts/contracts/MyToken.sol/MyToken.json" assert { type: "json" };

const provider = new JsonRpcProvider(process.env.RPC_URL ?? "https://www.armchain.org/devnet");
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);

const factory = new ContractFactory(MyTokenArtifact.abi, MyTokenArtifact.bytecode, wallet);
const token = await factory.deploy(1000000);
await token.waitForDeployment();
console.log("MyToken deployed to:", await token.getAddress());
```

Run with Node.js directly (not via `npx hardhat run`, which injects an ECDSA signer):

```bash
node scripts/deploy.js
```

## Contract Verification

Submit your source code to the Armchain Explorer for public verification:

1. Deploy your contract
2. Navigate to the contract address on [explorer.armchain.org](https://www.armchain.org/explorer)
3. Click "Verify & Publish"
4. Submit your source code, compiler version, and constructor arguments

The explorer supports:

* **Single file** verification
* **Standard JSON input** verification
* **Multi-file** verification with imports

## Gas Optimization Tips

Since Armchain uses the same gas model as Ethereum (Berlin pricing):

1. **Use `calldata` instead of `memory`** for read-only function parameters
2. **Pack storage variables** so that `uint128` + `uint128` fits in one 32-byte slot
3. **Use `immutable` and `constant`** for values that don't change
4. **Minimize storage writes** since they cost 20,000-5,000 gas
5. **Use events for data that doesn't need on-chain reads**
6. **Enable the optimizer** with 200+ runs

```solidity
// Good: packed storage
struct User {
    uint128 balance;    // Slot 1 (first half)
    uint128 timestamp;  // Slot 1 (second half)
    address wallet;     // Slot 2
}

// Good: use immutable
address public immutable FACTORY;

// Good: use calldata
function process(bytes calldata data) external {
    // calldata is cheaper than memory for external functions
}
```

## Security Best Practices

1. **Use OpenZeppelin contracts** for standard implementations
2. **Follow checks-effects-interactions** pattern to prevent reentrancy
3. **Use `ReentrancyGuard`** for state-changing external calls
4. **Validate all inputs** by checking addresses, amounts, and array lengths
5. **Use access control** like `Ownable`, `AccessControl`, or custom modifiers
6. **Test thoroughly** with unit tests, integration tests, and fuzz tests
7. **Get audited** before production deployment
8. **Use timelocks** for admin functions in production

## Important Notes

* **No `PUSH0` opcode**: Set `evmVersion: "london"` in compiler settings.
* **Instant finality**: No confirmation waits. Once mined, the contract is live.
* **Same address derivation**: `CREATE` and `CREATE2` work identically to Ethereum.
* **Gas prices**: EIP-1559 fee market. Minimum gas price is 1 Gwei.

## Writing Custom AA Validation Contracts

If you are building a **custom AA account validation scheme** or a **paymaster contract** for Armchain's native Account Abstraction system, there is an important constraint to be aware of.

Armchain's AA system introduces new EVM opcodes that are not part of the standard Ethereum opcode set. The Solidity compiler (`solc`) does not recognize these opcodes and will reject contracts that use them. Any contract that needs to interact with the AA execution layer (validation hooks, paymaster hooks) must be written in **Yul**, the EVM intermediate representation language.

This applies to:

* Custom account validation scheme contracts
* Paymaster contracts that interact with AA-specific execution hooks
* Any extension of the system contract framework

Standard Solidity contracts that do not touch AA-specific opcodes are completely unaffected. ERC-20s, NFTs, DeFi protocols, and all other regular contracts compile and deploy normally.

> The Armchain team is evaluating a fork of the Solidity compiler that adds recognition of the new AA opcodes. Until that is available, Yul is the required path for AA system contract development.

For full context, see [Account Abstraction: System Contracts](/account-abstraction/system-contracts).

## Further Reading

* [Armchain Ethers SDK](/developers/overview-1) to interact with contracts from JavaScript
* [EVM Integration](/architecture/evm) for technical details of Armchain's EVM
* [Transaction Types](/pqc/transaction-types) for the Type 3 PQC transaction format
* [Quickstart](/get-started/quickstart) to deploy your first contract
* [Account Abstraction](/account-abstraction/overview) for building on top of native AA


# Ethers SDK

The `@armchain-ethersv6/ethers` library is Armchain's official JavaScript/TypeScript SDK. If you're familiar with **ethers.js v6**, this is essentially a drop-in replacement with the same API and same patterns, but with all cryptographic operations swapped to use **ML-DSA44 post-quantum signatures**.

## Why This SDK?

Standard ethers.js uses ECDSA for signing, which Armchain nodes reject. Any application that **signs transactions or messages** needs this SDK. Read-only operations (querying balances, reading contract state) work fine with standard ethers.js.

| Operation                | Standard ethers.js | Armchain Ethers SDK  |
| ------------------------ | ------------------ | -------------------- |
| Read blockchain state    | ✅ Works            | ✅ Works              |
| Query balances/blocks    | ✅ Works            | ✅ Works              |
| Call view functions      | ✅ Works            | ✅ Works              |
| **Sign transactions**    | ❌ Rejected         | ✅ ML-DSA44           |
| **Sign messages**        | ❌ Incompatible     | ✅ ML-DSA44           |
| **Create wallets**       | ❌ ECDSA keys       | ✅ ML-DSA44 keys      |
| **HD wallet derivation** | ❌ Wrong coin type  | ✅ Armchain coin type |

## Installation

```bash
npm install @armchain-ethersv6/ethers
```

## Quick Start

### Connect to Armchain

```typescript
import { JsonRpcProvider } from "@armchain-ethersv6/ethers";

// Devnet
const provider = new JsonRpcProvider("https://www.armchain.org/devnet");

// Local (Fakenet)
const localProvider = new JsonRpcProvider("http://localhost:4000");

// Query blockchain
const blockNumber = await provider.getBlockNumber();
const balance = await provider.getBalance("0xADDRESS");
console.log("Block:", blockNumber, "Balance:", balance.toString());
```

### Create a Wallet

```typescript
import { Wallet, JsonRpcProvider } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");

// From private key (ML-DSA44, 2,560 bytes)
const wallet = new Wallet(privateKey, provider);
console.log("Address:", wallet.address);

// Random wallet
const randomWallet = Wallet.createRandom();
console.log("New address:", randomWallet.address);
console.log("New private key:", randomWallet.privateKey);
```

### Send a Transaction

```typescript
import { Wallet, JsonRpcProvider, parseEther } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(privateKey, provider);

const tx = await wallet.sendTransaction({
  to: "0xRECIPIENT_ADDRESS",
  value: parseEther("1.0"),
  // type: 3 is automatically set by the SDK
});

console.log("TX Hash:", tx.hash);

console.log("Confirmed in block:", receipt.blockNumber);
```

### Interact with a Smart Contract

```typescript
import { Contract, Wallet, JsonRpcProvider, parseEther } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(privateKey, provider);

// ERC-20 token contract
const tokenAbi = [
  "function balanceOf(address) view returns (uint256)",
  "function transfer(address to, uint256 amount) returns (bool)",
  "function symbol() view returns (string)",
  "event Transfer(address indexed from, address indexed to, uint256 value)"
];

const token = new Contract("0xTOKEN_ADDRESS", tokenAbi, wallet);

// Read (no signature needed)
const symbol = await token.symbol();
const balance = await token.balanceOf(wallet.address);
console.log(`${symbol} balance:`, balance.toString());

// Write (auto-signed with ML-DSA44)
const tx = await token.transfer("0xRECIPIENT", parseEther("100"));
const receipt = await tx.wait();
console.log("Transfer confirmed:", receipt.hash);
```

### Deploy a Contract

```typescript
import { ContractFactory, Wallet, JsonRpcProvider } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(privateKey, provider);

const abi = [/* contract ABI */];
const bytecode = "0x...";  // Compiled contract bytecode

const factory = new ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(/* constructor args */);
await contract.waitForDeployment();

console.log("Deployed at:", await contract.getAddress());
```

## HD Wallet (Hierarchical Deterministic)

Armchain uses a custom HD wallet derivation path:

```
m/44'/65536'/account'/0/address'
```

| Component     | Value           | Description                     |
| ------------- | --------------- | ------------------------------- |
| Purpose       | `44'`           | BIP-44 standard                 |
| Coin Type     | `65536'`        | Armchain's registered coin type |
| Account       | `0'`, `1'`, ... | Account index (hardened)        |
| Change        | `0`             | External addresses              |
| Address Index | `0'`, `1'`, ... | Address index (hardened)        |

> **All derivation is hardened** (denoted by `'`). Non-hardened derivation is not supported with ML-DSA44.

### Create from Mnemonic

```typescript
import { HDNodeWallet, Mnemonic } from "@armchain-ethersv6/ethers";

// Create from seed phrase
const mnemonic = "word1 word2 word3 ...";  // 12 or 24 words
const wallet = HDNodeWallet.fromPhrase(mnemonic);
console.log("Address:", wallet.address);

// Derive multiple accounts
for (let i = 0; i < 5; i++) {
  const path = `m/44'/65536'/${i}'/0/0'`;
  const child = wallet.derivePath(path);
  console.log(`Account ${i}:`, child.address);
}
```

### Generate a New Mnemonic

```typescript
import { Wallet } from "@armchain-ethersv6/ethers";

const wallet = Wallet.createRandom();
console.log("Mnemonic:", wallet.mnemonic.phrase);
console.log("Address:", wallet.address);
console.log("Path:", wallet.path);
```

## Signing Messages

```typescript
import { Wallet, hashMessage } from "@armchain-ethersv6/ethers";

const wallet = new Wallet(privateKey);

// Sign a message
const message = "Hello, Armchain!";
const signature = await wallet.signMessage(message);
console.log("Signature:", signature);

// The signature contains the full PQC signature blob:
// - Raw ML-DSA44 signature (2,420 bytes) + public key (1,312 bytes) = 3,732 bytes total
```

## Transaction Types

The SDK automatically creates **Type 3 (PQC) transactions**:

```typescript
// All transactions are Type 3 by default
const tx = await wallet.sendTransaction({
  to: recipient,
  value: parseEther("1.0"),
  // type: 3 is auto-set
  // gasLimit, maxFeePerGas, maxPriorityFeePerGas are auto-estimated
});
```

### Manual Transaction Construction

```typescript
import { Transaction, parseEther, parseUnits } from "@armchain-ethersv6/ethers";

const tx = Transaction.from({
  type: 3,
  chainId: 55,
  nonce: 42,
  to: "0xRECIPIENT",
  value: parseEther("1.0"),
  data: "0x",
  gasLimit: 21000n,
  maxFeePerGas: parseUnits("20", "gwei"),
  maxPriorityFeePerGas: parseUnits("2", "gwei"),
});

// Sign
const signedTx = await wallet.signTransaction(tx);
console.log("Signed TX:", signedTx);

// Broadcast
const response = await provider.broadcastTransaction(signedTx);
```

## Key Sizes and Formats

```typescript
import { Wallet } from "@armchain-ethersv6/ethers";

// Generate a new wallet (handles ML-DSA44 key generation internally)
const wallet = Wallet.createRandom();
console.log("Address:", wallet.address);
console.log("Private key size:", wallet.privateKey.length);  // Large: ML-DSA44 keys are 2,560 bytes
```

### Size Reference

| Component      | Size        | Description                                                          |
| -------------- | ----------- | -------------------------------------------------------------------- |
| Private Key    | 2,560 bytes | ML-DSA44 secret key                                                  |
| Public Key     | 1,312 bytes | ML-DSA44 public key                                                  |
| Raw Signature  | 2,420 bytes | ML-DSA44 detached signature                                          |
| Signature Blob | 3,732 bytes | Raw signature (2,420) + public key (1,312), included in transactions |

## API Compatibility

The SDK maintains the same API as ethers.js v6 wherever possible:

```typescript
// These all work the same as standard ethers.js v6
import {
  JsonRpcProvider,
  WebSocketProvider,
  Contract,
  ContractFactory,
  Wallet,
  HDNodeWallet,
  parseEther,
  parseUnits,
  formatEther,
  formatUnits,
  isAddress,
  getAddress,
  keccak256,
  AbiCoder,
  Interface,
  EventLog,
  // ... and more
} from "@armchain-ethersv6/ethers";
```

### Removed/Changed Methods

| Method                             | Status  | Reason                              |
| ---------------------------------- | ------- | ----------------------------------- |
| `Signature.recoverPublicKey()`     | Removed | Public key is embedded in signature |
| `SigningKey.computeSharedSecret()` | Removed | ECDH not applicable to DSA          |
| `SigningKey.compressedPublicKey`   | Removed | No compression in ML-DSA44          |
| `Transaction.unsignedSerialized`   | Removed | All PQC txns must be signed         |

### Legacy Compatibility Properties

For migration convenience, these properties still exist but return compatibility values:

```typescript
const sig = Signature.from(signatureBlob);
sig.r;        // First 32 bytes of signature (for compatibility)
sig.s;        // Next 32 bytes (for compatibility)  
sig.v;        // Always 27 (not used in PQC)
sig.yParity;  // Always 0 (not applicable)
```

## Error Handling

```typescript
try {
  const tx = await wallet.sendTransaction({
    to: recipient,
    value: parseEther("1000000"),  // More than balance
  });
} catch (error) {
  if (error.code === "INSUFFICIENT_FUNDS") {
    console.log("Not enough ARM");
  } else if (error.code === "NONCE_EXPIRED") {
    console.log("Nonce already used");
  } else if (error.code === "UNPREDICTABLE_GAS_LIMIT") {
    console.log("Transaction would revert");
  } else {
    console.error("Unknown error:", error);
  }
}
```

## Migration from Standard ethers.js

If you're porting an Ethereum dApp to Armchain:

### Step 1: Replace the Package

```bash
npm uninstall ethers
npm install @armchain-ethersv6/ethers
```

### Step 2: Update Imports

```typescript
// Before
import { ethers } from "ethers";

// After
import { ethers } from "@armchain-ethersv6/ethers";
```

### Step 3: Update RPC Endpoint

```typescript
// Before
const provider = new JsonRpcProvider("https://eth-mainnet.g.alchemy.com/...");

// After
const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
```

### Step 4: Key Generation

```typescript
// Before (ECDSA)
const wallet = Wallet.createRandom();  // 32-byte private key

// After (ML-DSA44)
const wallet = Wallet.createRandom();  // 2,560-byte private key
// Same API, larger keys internally
```

### Step 5: No Other Changes

Contract interactions, event listeners, and transaction building work identically to standard ethers.js.

## Performance Notes

| Operation              | Time     |
| ---------------------- | -------- |
| Key Generation         | \~2.0 ms |
| Signature Creation     | \~3.5 ms |
| Signature Verification | \~5.0 ms |
| HD Child Derivation    | \~4.1 ms |
| Address Computation    | \~0.3 ms |

These are approximately 4x slower than ECDSA but negligible for typical dApp usage.

## Further Reading

* [PQC Transaction Types](/pqc/transaction-types) for the detailed Type 3 format
* [ML-DSA44 Deep Dive](/pqc/mldsa44) for the cryptographic details
* [Smart Contracts](/developers/overview) for writing contracts on Armchain


# Wallets

This guide covers two perspectives: connecting your **dApp** to the Armchain Wallet, and building **custom wallet** support for Armchain.

## Armchain Wallet

The **Armchain Wallet** is the official browser extension wallet for the Armchain network. It is a fork of Wigwam wallet, customized for ML-DSA44 post-quantum cryptography.

### Key Features

* **ML-DSA44 key management**: Generates and stores post-quantum keys
* **HD wallet derivation**: BIP-39 mnemonic with Armchain coin type (`65536`)
* **Hardened derivation only**: All child keys use hardened paths for security
* **Type 3 transactions**: Signs PQC transactions natively
* **EIP-1559 gas pricing**: Dynamic fee support
* **Multi-account support**: Derive multiple accounts from a single seed phrase
* **ARM token support**: Native token balance display and transfers

### Network Configuration

The wallet ships with built-in Armchain network configurations:

```typescript
// Devnet
{
  chainId: 55,
  name: "Armchain Devnet",
  rpcUrls: ["https://www.armchain.org/devnet"],
  nativeCurrency: { symbol: "ARM", name: "Armchain", decimals: 18 },
}

// Local Development
{
  chainId: 55,
  name: "Armchain Local",
  rpcUrls: ["http://localhost:4000"],
  nativeCurrency: { symbol: "ARM", name: "Armchain", decimals: 18 },
}
```

## For dApp Developers

### Connecting to the Wallet

The Armchain Wallet injects a provider into the browser, similar to MetaMask:

```typescript
// Check if wallet is available
if (typeof window.ethereum !== "undefined") {
  // Armchain Wallet detected
}

// Request account access
const accounts = await window.ethereum.request({
  method: "eth_requestAccounts"
});
console.log("Connected account:", accounts[0]);
```

### Using with Armchain Ethers SDK

```typescript
import { BrowserProvider, Contract } from "@armchain-ethersv6/ethers";

// Connect to injected wallet
const provider = new BrowserProvider(window.ethereum);
const signer = await provider.getSigner();

console.log("Connected:", await signer.getAddress());
console.log("Balance:", await provider.getBalance(signer.address));

// Interact with contracts
const contract = new Contract(contractAddress, abi, signer);
const tx = await contract.someFunction(arg1, arg2);
await tx.wait();
```

### Handling Chain Switching

```typescript
// Switch to Armchain devnet
try {
  await window.ethereum.request({
    method: "wallet_switchEthereumChain",
    params: [{ chainId: "0x37" }],  // 55 in hex
  });
} catch (switchError) {
  // Chain not added - request to add it
  if (switchError.code === 4902) {
    await window.ethereum.request({
      method: "wallet_addEthereumChain",
      params: [{
        chainId: "0x37",
        chainName: "Armchain Devnet",
        rpcUrls: ["https://www.armchain.org/devnet"],
        nativeCurrency: {
          name: "Armchain",
          symbol: "ARM",
          decimals: 18,
        },
        blockExplorerUrls: ["https://www.armchain.org/explorer"],
      }],
    });
  }
}
```

### Listening for Events

```typescript
// Account changed
window.ethereum.on("accountsChanged", (accounts) => {
  console.log("Account switched to:", accounts[0]);
  // Refresh account-dependent UI
});

// Chain changed
window.ethereum.on("chainChanged", (chainId) => {
  console.log("Chain switched to:", parseInt(chainId, 16));
  // Reload the page or refresh state
  window.location.reload();
});

// Disconnected
window.ethereum.on("disconnect", () => {
  console.log("Wallet disconnected");
});
```

### Signing Messages

```typescript
const signer = await provider.getSigner();

// Personal sign (EIP-191)
const message = "Sign this message to verify your identity";
const signature = await signer.signMessage(message);

// The signature is an ML-DSA44 signature (3,732 bytes)
console.log("Signature:", signature);
```

### EIP-712 Typed Data Signing

```typescript
const domain = {
  name: "My dApp",
  version: "1",
  chainId: 55,
  verifyingContract: "0xCONTRACT_ADDRESS",
};

const types = {
  Order: [
    { name: "maker", type: "address" },
    { name: "amount", type: "uint256" },
    { name: "nonce", type: "uint256" },
  ],
};

const value = {
  maker: await signer.getAddress(),
  amount: parseEther("100"),
  nonce: 1,
};

const signature = await signer.signTypedData(domain, types, value);
```

## For Wallet Developers

If you're building a wallet that supports Armchain, here are the key requirements. The recommended approach is to use the `@armchain-ethersv6/ethers` SDK, which handles all cryptographic operations internally.

### Key Generation

Armchain uses ML-DSA44 (NIST FIPS 204) for all key operations. Key pairs are generated from a 32-byte random seed.

| Component   | Size        |
| ----------- | ----------- |
| Private Key | 2,560 bytes |
| Public Key  | 1,312 bytes |

The SDK's `Wallet.createRandom()` and `HDNodeWallet.fromPhrase()` handle key generation automatically. If implementing from scratch, use a FIPS 204-compliant ML-DSA44 library.

### HD Wallet Derivation

Armchain uses a BIP-44 compatible derivation path with all hardened indices:

```
m/44'/65536'/account'/0/address'
```

> **Critical**: Non-hardened derivation is NOT supported with ML-DSA44. All path components must use hardened derivation.

Since ML-DSA44 key generation takes a 32-byte seed (unlike ECDSA's scalar), the HD derivation chain produces child seeds rather than child keys directly. See the `@armchain-ethersv6/ethers` SDK source for the reference implementation.

```typescript
// Derive accounts using the SDK
import { HDNodeWallet } from "@armchain-ethersv6/ethers";

const wallet = HDNodeWallet.fromPhrase(mnemonic);
const child = wallet.derivePath("m/44'/65536'/0'/0/0'");
console.log("Address:", child.address);
```

### Transaction Signing

All Armchain transactions must be Type 3 (PQC). The SDK handles transaction construction and signing:

```typescript
import { Wallet, JsonRpcProvider } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new Wallet(privateKey, provider);

// The SDK automatically creates Type 3 transactions and signs with ML-DSA44
const tx = await wallet.sendTransaction({
  to: recipient,
  value: amount,
  // type: 3 is auto-set
});
```

For custom implementations, the signing process involves computing a transaction hash and producing an ML-DSA44 signature blob that includes both the raw signature and the public key (since ML-DSA44 does not support public key recovery). Refer to the [Transaction Types](/pqc/transaction-types) page for the wire format specification.

### Address Computation

Armchain addresses are derived from ML-DSA44 public keys using `keccak256`, producing standard 20-byte Ethereum-format addresses. The SDK handles this automatically via `SigningKey.computeAddress()`.

### Key Storage Considerations

ML-DSA44 private keys are **2,560 bytes** (vs 32 bytes for ECDSA). This affects:

| Aspect              | ECDSA       | ML-DSA44        | Impact                             |
| ------------------- | ----------- | --------------- | ---------------------------------- |
| Private key storage | 32 bytes    | 2,560 bytes     | \~80× more storage                 |
| Key encryption time | Fast        | Slightly slower | Negligible                         |
| Key backup size     | Small       | Larger          | QR codes may not work for raw keys |
| Mnemonic backup     | 12/24 words | 12/24 words     | Same: mnemonic derives the key     |

> **Recommendation**: Always support mnemonic-based backup. The 12/24 word seed phrase is the same size regardless of the underlying key type.

## Common Integration Patterns

### Connect Wallet Button

```typescript
async function connectWallet() {
  if (!window.ethereum) {
    alert("Please install Armchain Wallet");
    return;
  }

  try {
    const accounts = await window.ethereum.request({
      method: "eth_requestAccounts"
    });
    
    const chainId = await window.ethereum.request({
      method: "eth_chainId"
    });

    if (parseInt(chainId, 16) !== 55) {
      // Not on Armchain - request switch
      await switchToArmchain();
    }

    return accounts[0];
  } catch (error) {
    console.error("Connection failed:", error);
  }
}
```

### Transaction Helper

```typescript
import { BrowserProvider, parseEther } from "@armchain-ethersv6/ethers";

async function sendARM(to: string, amount: string) {
  const provider = new BrowserProvider(window.ethereum);
  const signer = await provider.getSigner();

  const tx = await signer.sendTransaction({
    to,
    value: parseEther(amount),
  });

  // Instant finality means one confirmation is enough
  const receipt = await tx.wait();
  return receipt;
}
```

## Further Reading

* [Armchain Ethers SDK](/developers/overview-1) for the full SDK reference
* [PQC Overview](/pqc/overview) to understand post-quantum cryptography
* [Transaction Types](/pqc/transaction-types) for the Type 3 transaction format
* [Network Configuration](/get-started/network-configuration) for RPC endpoints and chain details


# Architecture

Understand how Armchain works under the hood. These pages cover the core protocol design, from how transactions are ordered using a DAG to how the EVM executes them.

> **Tip**: If you just want to start building, head to the [Quickstart](/get-started/quickstart). Come back here when you're curious about the internals.

| Document                                                     | Description                                                           |
| ------------------------------------------------------------ | --------------------------------------------------------------------- |
| [DAG](/architecture/dag)                                     | How Armchain uses a Directed Acyclic Graph instead of a linear chain  |
| [Consensus](/architecture/consensus)                         | How Lachesis aBFT achieves instant finality without leaders or rounds |
| [EVM](/architecture/evm)                                     | Ethereum Virtual Machine compatibility and execution details          |
| [Transaction Lifecycle](/architecture/transaction-lifecycle) | The complete journey of a transaction from submission to finality     |


# DAG

Most blockchains process transactions in a single line, one block after another. Armchain takes a different approach using a **Directed Acyclic Graph (DAG)**, which lets multiple validators process transactions in parallel. This is one of the key reasons Armchain achieves higher throughput and faster finality.

This page explains how the DAG works and how it connects to block production.

## What is a DAG?

A Directed Acyclic Graph is a data structure where:

* **Directed**: Each edge (reference) has a direction, from a newer event to older events
* **Acyclic**: There are no cycles: you can never follow references and end up where you started
* **Graph**: Unlike a chain (linear), multiple events can exist at the same "level"

## Events

The fundamental unit of the DAG is an **event**. Each validator creates events that contain:

| Field          | Description                            |
| -------------- | -------------------------------------- |
| `Creator`      | Validator ID                           |
| `Sequence`     | Event number for this validator        |
| `Parents`      | References to up to 10 previous events |
| `Transactions` | 0 or more user transactions            |
| `Timestamp`    | Median timestamp                       |
| `Gas Used`     | Sum of gas for transactions            |
| `Signature`    | ML-DSA44 (3,732 bytes)                 |

### Event Creation

A validator creates a new event when:

1. It has transactions to include, **or**
2. A minimum time has passed since the last event (to maintain network liveness)

The validator selects parent events: its own latest event plus events from other validators it has learned about through gossip.

### Parent Selection

| Parameter        | Value |
| ---------------- | ----- |
| Max Parents      | 10    |
| Max Free Parents | 3     |

* A **self-parent** is always included (the validator's own previous event)
* **Other parents** are selected from the latest known events of other validators
* More parents improve DAG connectivity, which speeds up consensus convergence

## DAG Visualization

Consider a network with 4 validators (A, B, C, D):

```
Time ──────────────────────────────────────────►

A:  [A₁] ─────────────── [A₂] ────────────── [A₃]
      │                     │╲                   │
B:  [B₁] ──── [B₂] ───────┘ ╲── [B₃] ─────────┘
      │          │╲                  │╲
C:  [C₁] ──────┘  ╲── [C₂] ───────┘  ╲─ [C₃]
      │               ╱       │              │
D:  [D₁] ── [D₂] ──┘─────── [D₃] ─────────┘
```

Each arrow represents a parent reference. Events reference **multiple** validators, creating a mesh structure.

## From DAG to Blocks

While the DAG provides the raw ordering structure, Armchain still produces **blocks** for EVM compatibility. The process uses **frames** and **Atropos elections**:

### 1. Frame Processing

Events in the DAG are grouped into **frames** (consensus rounds). Within each frame, candidate events are evaluated for finalization.

### 2. Atropos Election

Root events in frame F+1 vote on candidate events in frame F through a weighted voting mechanism. When a candidate reaches quorum (≥ 2/3 of total validator stake weight), it is elected as the **Atropos**, the finalized event for that frame. There is a 1:1 mapping between Atropos events and blocks.

### 3. Block Construction

The Atropos event and all confirmed events from the frame are assembled into a block:

| Frame   | Events                 | Atropos      | Block     |
| ------- | ---------------------- | ------------ | --------- |
| Frame 0 | A₁, B₁, C₁, D₁         | B₁           | Block 1   |
| Frame 1 | A₂, B₂, C₂, D₂         | C₂           | Block 2   |
| Frame N | A₍ₙ₎, B₍ₙ₎, C₍ₙ₎, D₍ₙ₎ | Elected root | Block N+1 |

### 4. EVM Execution

Each block's transactions are sorted by Lamport time and executed sequentially through the EVM, producing state changes, receipts, and logs, exactly as in Ethereum. See [Consensus](/architecture/consensus) for the full three-phase block processing lifecycle.

## Vector Clock (VecMT)

Armchain uses a **Vector Clock / Median Timestamp (VecMT)** index to efficiently track event ordering across validators:

* Each validator maintains a vector of the latest sequence numbers it knows about from all other validators
* The **median timestamp** algorithm provides a fork-free ordering
* This enables the aBFT consensus to determine which events are agreed upon

## Advantages of the DAG

### Parallel Processing

Unlike a linear chain where only one block producer creates a block at a time, **all validators create events simultaneously**:

| Model        | Production Pattern                        | Throughput                 |
| ------------ | ----------------------------------------- | -------------------------- |
| Linear chain | A → B → C → D → A → B → ...               | One proposer at a time     |
| DAG          | All validators emit events simultaneously | All validators in parallel |

### Reduced Wasted Work

In PoW and some PoS chains, only one proposer's block is accepted; others are discarded. In Armchain's DAG, **every event contributes to consensus**: no work is wasted.

### Network Resilience

The DAG structure naturally handles:

* **Message reordering**: Events reference parents, so ordering is reconstructed
* **Temporary disconnections**: Validators can create events offline and sync later
* **Variable latency**: No synchronous rounds or timeouts required

## DAG Parameters

| Parameter             | Devnet   | Fakenet    |
| --------------------- | -------- | ---------- |
| Max Parents per Event | 10       | 10         |
| Max Free Parents      | 3        | 3          |
| Default Event Gas     | 28,000   | 28,000     |
| Max Epoch Duration    | 4 hours  | 10 minutes |
| Max Empty Block Skip  | 1 minute | 3 seconds  |

## Querying DAG Data

Armchain exposes DAG-specific APIs via the `dag` namespace:

```bash
# Get DAG event by hash
curl -X POST http://localhost:4000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getEvent","params":["0xEVENT_HASH"],"id":1}'

# Get current epoch
curl -X POST http://localhost:4000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getEpochStats","params":[],"id":1}'
```

See the [Armchain-Specific APIs](/api-reference/armchain-api) for the full DAG API reference.

## Further Reading

* [Consensus (Lachesis aBFT)](/architecture/consensus): The consensus algorithm built on the DAG
* [Transaction Lifecycle](/architecture/transaction-lifecycle): How transactions flow through the DAG
* [EVM Integration](/architecture/evm): How blocks from the DAG feed the EVM


# Consensus

Armchain uses the **Lachesis** consensus protocol, an **asynchronous Byzantine Fault Tolerant (aBFT)** consensus mechanism. In plain terms, this means transactions are finalized instantly with no waiting for multiple block confirmations like on Ethereum, and no risk of the chain reorganizing and reversing your transaction.

This page explains how Lachesis works and why it provides stronger guarantees than most other consensus mechanisms.

## What is aBFT?

Asynchronous Byzantine Fault Tolerance is the strongest class of consensus guarantees:

* **Asynchronous**: No timing assumptions. Consensus proceeds regardless of network delays, message reordering, or partitions.
* **Byzantine Fault Tolerant**: Tolerates up to **1/3 of validators** (by stake weight) being malicious or offline.
* **Deterministic Finality**: Confirmed transactions are **permanently final**: no rollbacks, no reorganizations.

### Async BFT vs. Synchronous BFT

At a high level, synchronous vs. asynchronous BFT differs in **trigger mechanism**.

#### Synchronous / Partially Synchronous BFT

**Examples**: Cosmos (Tendermint), Ethereum (Gasper/Casper).

These protocols operate on a fixed schedule. Each time slot has a designated leader. If the leader disconnects or responds too slowly, the protocol moves to the next leader.

| Aspect        | Details                                                                                                                                                                                                                                       |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Trigger**   | Time - "It is 12:00:00, time for the next block"                                                                                                                                                                                              |
| **Structure** | Linear chain - blocks are added one by one                                                                                                                                                                                                    |
| **Mechanism** | Rounds and leaders: time is divided into slots (e.g., Ethereum's 12-second slots). A leader proposes a block, validators vote.                                                                                                                |
| **Weakness**  | If the leader's block doesn't reach validators within the timeout (due to lag or DDoS), validators vote to skip that leader (View Change). An attacker can stall the network by delaying messages just enough to trigger timeouts repeatedly. |

#### Asynchronous BFT (Lachesis)

**Example**: Armchain / Fantom (Lachesis).

There is no clock, no leader, and no rounds. Nodes communicate with neighbors and process events as they arrive.

| Aspect        | Details                                                                                                                                                                                                                                                       |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Trigger**   | Events - "I just received new data, let's process it"                                                                                                                                                                                                         |
| **Structure** | DAG - events flow in parallel like a river, then are sorted into a total order                                                                                                                                                                                |
| **Mechanism** | Leaderless: no single node is in charge of a "round." Everyone produces events whenever they want. Gossip-about-gossip creates a web of causal connections, and virtual voting determines consensus by inspecting the DAG - no explicit vote messages needed. |
| **Strength**  | Because it doesn't wait for a clock, it can go as fast as the network allows. If the network is fast, consensus is instant. If the network lags, it just slows down - it doesn't "break" or require a complex view-change reset.                              |

#### Comparison Summary

| Feature             | Synchronous (Cosmos/ETH)                                                                                                                | Asynchronous (Lachesis)                                                |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| **Trigger**         | Time-based slots                                                                                                                        | Event-based                                                            |
| **Structure**       | Linear chain                                                                                                                            | DAG (parallel, sorted later)                                           |
| **Bottleneck**      | The timeout - the network can never be faster than the predefined slot time (e.g., Ethereum is always 12s, even if the network is idle) | Bandwidth - the network moves as fast as data can travel               |
| **DDoS Resistance** | Lower - attacker can target the leader or exploit timing                                                                                | Higher - no leader to DDoS, timing delays don't break the logic        |
| **Finality**        | Fast (Tendermint is instant, Ethereum \~15 min for full finality)                                                                       | Instant - usually < 1 second because no "round" coordination is needed |

> **Why isn't everyone using aBFT?** Asynchronous algorithms are mathematically harder to implement correctly. They require complex graph theory (finding roots and Atropos in the DAG) compared to "longest chain" or "majority vote" logic. aBFT nodes must also gossip more history (DAG structure) to prove event visibility, which is bandwidth-intensive.

### Comparison with Ethereum PoS

| Aspect             | Ethereum PoS                   | Lachesis aBFT                           |
| ------------------ | ------------------------------ | --------------------------------------- |
| Block Production   | Fixed 12-second slots          | Variable timing based on consensus      |
| Proposer Selection | Single validator per slot      | All validators participate continuously |
| Finality           | Multi-slot (2 epochs \~13 min) | Immediate upon Atropos selection        |
| Communication      | Broadcast blocks               | Gossip events asynchronously            |
| Consensus Type     | BFT (synchronous)              | aBFT (asynchronous)                     |
| Structure          | Linear chain                   | DAG with Atropos checkpoints            |
| Throughput         | Limited by single proposer     | Higher - parallel event creation        |
| Network Tolerance  | Sensitive to network delays    | Resilient to network delays             |

## How Lachesis Works

### High-Level Architecture

```
┌─────────────┐     ┌─────────────────┐     ┌──────────────────┐
│   Emitter   │────▶│ Lachesis Engine  │────▶│ Block Processing  │
│  (Mempool)  │     │  (Consensus)    │     │   (Callbacks)    │
└─────────────┘     └─────────────────┘     └──────────────────┘
       │                    │                        │
       ▼                    ▼                        ▼
┌─────────────┐     ┌─────────────────┐     ┌──────────────────┐
│   Events    │     │   DAG + Roots   │     │    EVM State     │
│ (with Txs)  │     │   + Atropos     │     │  + SFC Update    │
└─────────────┘     └─────────────────┘     └──────────────────┘
```

### Key Concepts

#### Events

Events are the fundamental units of the DAG. Each event is created by a validator and contains:

* **Creator**: Validator ID that created the event
* **Parents**: References to previous events (self-parent + other parents)
* **Transactions**: User transactions included in the event
* **Frame**: Consensus round the event belongs to
* **Lamport timestamp**: Logical clock for ordering
* **Epoch**: Time period the event belongs to
* **Gas power used**: Gas consumed by the event's transactions
* **Signature**: ML-DSA44 signature (3,732 bytes)

#### Frames

A **frame** is a consensus round that groups events for processing. Frames are the granularity at which Atropos events are elected.

```
┌───────────┐     ┌───────────┐     ┌───────────┐     ┌───────────┐
│  Frame 0  │────▶│  Frame 1  │────▶│  Frame 2  │────▶│  Frame 3  │
│(Candidates)│    │  (Voting) │     │  (Voting) │     │ (Decision)│
└───────────┘     └───────────┘     └───────────┘     └───────────┘
      │                 │                 │                 │
      ▼                 ▼                 ▼                 ▼
 Events created    Roots vote on    Votes aggregated  Atropos decided
 by validators     Frame 0 events   from Frame 1      for Frame 0
```

#### Roots

A **root** is a special event that qualifies to participate in the Atropos election:

* Must be **forkless-caused** by ≥ 2/3 of validator weight from the previous frame
* Each validator can have **at most one root per frame**
* Roots cast votes in the election process

> **Forkless cause**: An event E becomes a root if it is observed by roots from validators representing ≥ 2/3 of total stake weight from the previous frame. For example, with validators A(30), B(25), C(25), D(20) = 100 total weight, the quorum is `(100 × 2/3) + 1 = 67` weight.

#### Epochs

An **epoch** is a larger time period containing multiple frames:

* Each epoch has a **unique validator set**
* Epoch boundaries are determined by:
  * **Maximum epoch gas consumed** (`MaxEpochGas`: 1.5B gas)
  * **Maximum epoch duration** (`MaxEpochDuration`: 4 hours)
  * **Cheaters detected** (forces epoch seal)
  * **AdvanceEpochs > 0** (administrative trigger)

| Parameter          | Devnet        | Fakenet     |
| ------------------ | ------------- | ----------- |
| Max Epoch Duration | 4 hours       | 10 minutes  |
| Max Epoch Gas      | 1,500,000,000 | 300,000,000 |

At epoch boundaries:

1. Validator metrics (uptime, missed blocks, originated fees) are pushed on-chain to the SFC contract
2. Validator rewards become claimable
3. A new validator set may be activated
4. Network rules may be updated

### DAG-Based Event Ordering

Unlike traditional blockchains that produce a single linear chain of blocks, Lachesis builds a **Directed Acyclic Graph (DAG)** of events. Each validator independently creates events that reference previous events from other validators.

```
Time ──────────────────────────────────────────────────▶

Validator A: [E_a1]──────────▶[E_a2]──────────▶[E_a3]──────────▶[E_a4]
               │                 │╲                │
Validator B: [E_b1]────┬───▶[E_b2]──┬──▶[E_b3]──────────▶[E_b4]
               │        │╲         │       │╲
Validator C: [E_c1]────┘  ╲──[E_c2]───┘──▶[E_c3]──────────▶[E_c4]
               │             ╱         │
Validator D: [E_d1]──[E_d2]──────────[E_d3]──────────▶[E_d4]

             Frame 0       Frame 1       Frame 2       Frame 3
```

**Gossip about gossip**: When Node A communicates with Node B, Node B adds a reference to Node A's latest event, building a web of causal connections. **Virtual voting**: By inspecting the DAG structure, nodes determine which events have been observed by which validators. No explicit vote messages are sent.

### Atropos Election

The **Atropos** is a single finalized event selected through weighted voting that serves as a checkpoint and block boundary. There is exactly **one Atropos per frame**, and there is a **1:1 mapping between Atropos events and blocks**.

#### Election Algorithm

1. **Round 1** (Frame F+1 voting on Frame F): Each root R in Frame F+1 votes:
   * **YES** for candidate C if R observes C (has forkless causality)
   * **NO** for candidate C if R does NOT observe C
2. **Round 2+** (Frame F+2 and beyond): Each root R' aggregates votes from the previous round:
   * Count weighted YES votes and NO votes
   * Vote according to the majority
   * If `YES_weight ≥ Quorum` OR `NO_weight ≥ Quorum` → **DECIDED**
3. **Quorum**: `(TotalWeight × 2/3) + 1`
4. **Atropos selection**: Iterate through validators in deterministic order (by weight descending). The first validator whose candidate event has a decided "YES" vote becomes the Atropos.

#### Worked Example

Consider 4 validators with stakes: A(30), B(25), C(25), D(20) = 100 total weight, quorum = 67.

**Frame F: Candidates**: Each validator creates a candidate event:

* `a0_0` (5 txs), `b0_0` (3 txs), `c0_0` (7 txs), `d0_0` (2 txs)

**Frame F+1: Round 1 Voting**: Root events observe candidates:

| Root               | Observes                   |
| ------------------ | -------------------------- |
| `a1_1` (weight 30) | a0\_0, b0\_0, c0\_0        |
| `b1_1` (weight 25) | b0\_0, c0\_0, d0\_0        |
| `c1_1` (weight 25) | a0\_0, b0\_0, c0\_0, d0\_0 |
| `d1_1` (weight 20) | c0\_0, d0\_0               |

**Vote matrix** (YES = ✓, NO = ✗):

| Candidate | a1\_1 (30w) | b1\_1 (25w) | c1\_1 (25w) | d1\_1 (20w) | Total YES                    |
| --------- | ----------- | ----------- | ----------- | ----------- | ---------------------------- |
| `a0_0`    | ✓           | ✗           | ✓           | ✗           | 55 - NOT DECIDED             |
| `b0_0`    | ✓           | ✓           | ✓           | ✗           | 80 - **DECIDED YES** (≥ 67)  |
| `c0_0`    | ✓           | ✓           | ✓           | ✓           | 100 - **DECIDED YES** (≥ 67) |
| `d0_0`    | ✗           | ✓           | ✓           | ✓           | 70 - **DECIDED YES** (≥ 67)  |

**Atropos selection**: Iterate by weight descending, A(30), B(25), C(25), D(20):

1. A's candidate (`a0_0`): NOT decided → skip
2. B's candidate (`b0_0`): **DECIDED YES** → **`b0_0` is elected Atropos**

This event becomes the checkpoint for Frame F. A new block is created containing all confirmed events from this frame.

#### When No Atropos is Decided

If not enough events reach quorum in a frame (e.g., too many validators offline but still above 2/3 total weight), voting continues into subsequent rounds. If an Atropos cannot be elected for a frame, that frame's block is skipped.

## Block Processing Lifecycle

### Event Emission

The **Emitter** is responsible for creating events at regular intervals, packaging transactions from the mempool.

```
┌─────────────┐
│   Mempool   │
│  (Pending   │
│    Txs)     │
└──────┬──────┘
       │ getSortedTxs()
       ▼
┌─────────────┐
│   Emitter   │◄─── Tick every ~11ms
│             │
│  • Min interval: 110ms
│  • Max interval: 10min
│  • Max 32 txs/address
└──────┬──────┘
       │ createEvent()
       ▼
┌─────────────┐
│    Event    │
│  • Creator  │
│  • Parents  │
│  • Txs      │
│  • Lamport  │
│  • Signature│
└──────┬──────┘
       │
       ├─── world.Process(e) ──▶ Lachesis Engine
       │
       └─── world.Broadcast(e) ──▶ Network Peers
```

**Emitter configuration**:

| Parameter                    | Value  |
| ---------------------------- | ------ |
| Min Emit Interval            | 110 ms |
| Max Emit Interval            | 10 min |
| Confirming Interval          | 120 ms |
| Doublesign Protection        | 27 min |
| Parallel Instance Protection | 1 min  |

### Consensus Callbacks

When the Lachesis engine decides on an Atropos, it invokes application callbacks using a **three-phase model**:

```
Lachesis Engine
       │ onFrameDecided()
       ▼
┌──────────────┐
│  BeginBlock  │
│  • Load state│
│  • Merge     │
│    cheaters  │
│  • Open      │
│    StateDB   │
└──────┬───────┘
       │
  ┌────┼────┐
  ▼    ▼    ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ApplyEvent│ │ApplyEvent│ │ApplyEvent│  (for each confirmed
│• Collect │ │          │ │          │   event in order)
│  events  │ │          │ │          │
│• Process │ │          │ │          │
│  MBProofs│ │          │ │          │
└──────────┘ └──────────┘ └──────────┘
       │
       ▼
┌──────────────┐
│   EndBlock   │
│  • Execute   │
│    txs       │
│  • Seal epoch│
│    (if ready)│
│  • Persist   │
│    state     │
└──────────────┘
```

1. **BeginBlock**: Loads block and epoch state, triggers the events module to fetch the confirmed Atropos events.
2. **ApplyEvent** (called for each confirmed event): Collects events and processes misbehavior proofs (e.g., double signing). Cheaters are penalized after the current epoch ends.
3. **EndBlock**: Performs the three-phase transaction execution (see below), then persists state and updates the state root.

### Three-Phase Transaction Execution

The EndBlock callback executes transactions in three distinct phases:

#### Phase 1: Pre-Internal Transactions

* **Punish cheaters**: `DeactivateValidator(validatorID, DoublesignBit)` for any validators caught double-signing
* **If sealing epoch**: Push validator metrics to the SFC contract via `SealEpoch(metrics)`:
  * `offlineTimes[]`: duration each validator was offline
  * `offlineBlocks[]`: blocks missed by each validator
  * `uptimes[]`: active time for each validator
  * `originatedTxsFee[]`: total fees from transactions originated by each validator

#### Epoch Sealing (if triggered)

Epoch sealing is triggered when any of these conditions are met:

| Condition              | Threshold                      |
| ---------------------- | ------------------------------ |
| Epoch gas consumed     | ≥ `MaxEpochGas` (1.5B gas)     |
| Epoch duration elapsed | ≥ `MaxEpochDuration` (4 hours) |
| Cheaters detected      | Any double-signers found       |
| Administrative advance | `AdvanceEpochs > 0`            |

When an epoch is sealed:

* New validators are selected based on `NextValidatorProfiles`
* `ValidatorBlockStates` are reset
* Epoch number is incremented
* Network rules are updated if `DirtyRules` is present

#### Phase 2: Post-Internal Transactions

* **If sealing epoch**: `SealEpochValidators(nextValidatorIDs)`, which updates the active validator set on-chain

#### Phase 3: Event Transactions

* Sort confirmed events by Lamport time
* Apply `spillBlockEvents()` to exclude events exceeding `MaxBlockGas`
* Collect all transactions from events
* Execute through the EVM
* Track receipts and gas usage
* Update originated fees per validator

### State Persistence

After transaction execution, the block is persisted to storage:

```
EVM State Commit
  statedb.Commit(true) → New StateRoot
       │
       ▼
Block Construction
  block := {
    Time:       blockCtx.Time,
    Atropos:    cBlock.Atropos,
    Events:     confirmedEvents,
    Root:       stateRoot,
    GasUsed:    evmBlock.GasUsed,
  }
       │
       ▼
Storage Operations
  • SetTxPosition()       - map tx hash → event & block position
  • SetReceipts()         - store transaction receipts by block
  • IndexLogs()           - index logs for efficient filtering
  • SetBlock()            - store block data
  • SetBlockIndex()       - map Atropos hash → block number
  • SetBlockEpochState()  - update current block/epoch state
  • commitEVM()           - flush EVM state to disk
```

## Validators

Validators are the nodes that participate in consensus by creating and signing events.

### Validator Requirements

* **Staking**: Validators must stake ARM tokens (minimum self-stake varies by network)
* **Key Type**: Validators use **ML-DSA44** keys exclusively
* **Uptime**: Validators earn rewards proportional to their participation
* **Max Parents**: Each event can reference up to **10 parent events**

### Validator Key Generation

Validators generate ML-DSA44 key pairs:

```bash
# Generate a new validator key
armnode validator new
```

This creates a key file in the validator keystore with:

* Private key: 2,560 bytes (ML-DSA44)
* Public key: 1,312 bytes (ML-DSA44)

## Validator Rewards System

### SFC Contract

The **SFC (Special Fee Contract)** is the core on-chain governance mechanism deployed at genesis. It manages:

* Validator registration and management
* Stake delegation
* Reward distribution
* Epoch sealing
* Slashing and penalties

The SFC is owned by a single **owner address** set at genesis (the root of trust). No validator can change this owner address: only the owner itself can transfer ownership.

```
SFC Contract (Fixed Address at Genesis)
├── STATE:
│   ├── validators[]         - validator info (status, stake, times)
│   ├── delegations[]        - delegation info per delegator+validator
│   ├── epochSnapshots[]     - epoch state snapshots
│   ├── withdrawalRequests[] - pending withdrawals
│   └── parameters           - baseRewardPerSecond, penalties, etc.
│
├── USER FUNCTIONS:
│   ├── createValidator(pubkey) payable
│   ├── delegate(toValidatorID) payable
│   ├── undelegate(toValidatorID, wrID, amount)
│   ├── withdraw(toValidatorID, wrID)
│   ├── claimRewards(toValidatorID)
│   └── lockStake(toValidatorID, duration, amount)
│
├── NODE INTERFACE (via NodeDriver):
│   ├── sealEpoch(offlineTimes, offlineBlocks, uptimes, fees)
│   ├── sealEpochValidators(nextValidatorIDs)
│   └── deactivateValidator(validatorID, status)
│
└── GOVERNANCE (Owner Only):
    ├── updateBaseRewardPerSecond(value)
    ├── updateOfflinePenaltyThreshold(blocksNum, time)
    └── transferOwnership(newOwner)
```

### Reward Calculation

Validator rewards consist of two components:

```
Total Reward = Transaction Fees + Base Rewards

Where:
  Transaction Fees = Σ(GasUsed × GasPrice) for transactions originated by the validator

  Base Rewards = baseRewardPerSecond × epochDuration
                 × (validatorStakeWeight / totalBaseRewardWeight)
```

**The greater the stake, the greater the rewards.** After each epoch, rewards are recalculated and become claimable by validators and their delegators.

### Epoch Sealing

When an epoch ends, the node pushes validator metrics on-chain through the **NodeDriver** intermediary contract:

**Validator metrics collected per epoch**:

| Metric             | Description                                         |
| ------------------ | --------------------------------------------------- |
| `missed.BlocksNum` | Blocks missed by the validator                      |
| `missed.Period`    | Duration the validator was offline                  |
| `uptime`           | Active time during the epoch                        |
| `originatedTxFee`  | Total fees from transactions the validator included |

**Forgiveness rule** (`BlockMissedSlack = 50`): If a validator missed ≤ 50 blocks, the miss counts are reset to zero and no penalty applies. This prevents penalizing validators for brief, incidental downtime.

**Epoch snapshot** (persisted on-chain):

| Field                   | Description                                           |
| ----------------------- | ----------------------------------------------------- |
| `endTime`               | Epoch end timestamp                                   |
| `epochFee`              | Total transaction fees collected                      |
| `totalBaseRewardWeight` | Sum of validator weights for base reward distribution |
| `totalTxRewardWeight`   | Weighted transaction fee distribution                 |
| `baseRewardPerSecond`   | Governance-set reward rate                            |
| `totalStake`            | Total staked in the epoch                             |
| `totalSupply`           | Total token supply                                    |

### Governance Model

The SFC owner address (set at genesis, root of trust) controls key economic parameters:

| Parameter                 | Type       | Description                                   |
| ------------------------- | ---------- | --------------------------------------------- |
| `baseRewardPerSecond`     | Governance | Validator reward rate - set by owner          |
| `offlinePenaltyThreshold` | Governance | (blocksNum, time) tuple for offline penalties |
| `BlockMissedSlack`        | Network    | 50 blocks (forgiveness threshold)             |
| `MinGasPrice`             | Network    | 1 Gwei                                        |
| `MaxEpochGas`             | Network    | 1.5B gas                                      |
| `MaxEpochDuration`        | Network    | 4 hours                                       |
| `MinSelfStake`            | Network    | Varies by network                             |
| `WithdrawalPeriodTime`    | Network    | 7 days                                        |

> **Key point**: Node operators cannot modify governance parameters. Only the designated owner address can update `baseRewardPerSecond`, penalty thresholds, and transfer ownership.

## Finality Guarantees

### What "Instant Finality" Means

When you submit a transaction to Armchain:

1. Transaction enters the mempool
2. Emitter includes it in an event (\~110ms tick)
3. Event is gossiped to peers and enters the DAG
4. aBFT consensus elects an Atropos for the frame
5. Three-phase block processing executes the transaction
6. **Transaction is final**: it will never be reversed

No confirmation waits are needed. A single confirmation on Armchain is equivalent to thousands of confirmations on a proof-of-work chain.

### Safety Under Network Partition

Even if the network splits into partitions:

* No **conflicting** blocks can be finalized
* When the partition heals, consensus resumes from where it left off
* No fork choice rules are needed: there are no forks

### Liveness

Lachesis guarantees liveness (the network continues making progress) as long as more than 2/3 of validators (by stake weight) are honest and online.

## Gas and Event Economics

### Gas Power

Validators have **gas power** that replenishes over time:

| Parameter         | Value                                  |
| ----------------- | -------------------------------------- |
| Max Block Gas     | 20,500,000                             |
| Default Event Gas | 28,000                                 |
| Min Gas Price     | 1 Gwei                                 |
| Empty Block Skip  | 1 minute (devnet), 3 seconds (fakenet) |

### Max Parents

Events in the DAG can reference multiple parent events:

| Parameter        | Value |
| ---------------- | ----- |
| Max Parents      | 10    |
| Max Free Parents | 3     |

More parent references improve the DAG's connectivity and consensus speed, but consume more bandwidth.

## Further Reading

* [DAG Structure](/architecture/dag): Deep dive into the DAG data structure
* [EVM Integration](/architecture/evm): How the EVM runs on top of Lachesis
* [Transaction Lifecycle](/architecture/transaction-lifecycle): End-to-end transaction flow
* [Running a Validator](https://github.com/khizarbakhtiar1/dev-docs/blob/main/node-operators/running-a-validator.md): Participate in consensus
* [Node Types](/node-operators/node-types): GC modes, sync modes, and database configuration


# EVM

Armchain runs a full **Ethereum Virtual Machine (EVM)** supporting the **London** hard fork. If you've written Solidity contracts for Ethereum, they'll work on Armchain without any changes. The differences are at the network and signing level, not the contract level.

This page covers EVM compatibility details, gas costs, precompiled contracts, and what's different from Ethereum.

## EVM Version and Hard Fork Support

| Hard Fork      | Status      | Key Features                                             |
| -------------- | ----------- | -------------------------------------------------------- |
| Homestead      | ✅ Supported | Basic EVM                                                |
| Byzantium      | ✅ Supported | `REVERT`, precompiles, `STATICCALL`                      |
| Constantinople | ✅ Supported | `CREATE2`, bitwise shifting                              |
| Istanbul       | ✅ Supported | `CHAINID` opcode, gas repricing                          |
| Berlin         | ✅ Supported | EIP-2929 (gas cost changes), EIP-2930 (access lists)     |
| London         | ✅ Supported | EIP-1559 (dynamic base fee), EIP-3529 (refund reduction) |
| Shanghai       | ❌ Not yet   | `PUSH0` opcode                                           |

### Solidity Compiler Compatibility

Use Solidity `0.8.x` for best results. Recommended pragma:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
```

> Avoid Shanghai/Cancun features (e.g., `PUSH0`, transient storage) as they are not yet supported.

## How the EVM Fits into Armchain

```
┌──────────────────────────────┐
│      Lachesis Consensus       │
│    (DAG event ordering)       │
└──────────┬───────────────────┘
           │ Ordered events
           ▼
┌──────────────────────────────┐
│       Block Assembly          │
│  (group events into blocks)   │
└──────────┬───────────────────┘
           │ Block with transactions
           ▼
┌──────────────────────────────┐
│   State Transition (EVM)      │
│  - Execute each transaction   │
│  - Update state trie          │
│  - Generate receipts & logs   │
│  - Compute state root         │
└──────────┬───────────────────┘
           │ Final state
           ▼
┌──────────────────────────────┐
│      Finalized Block          │
│  (state root, receipts,       │
│   logs - PERMANENT)           │
└──────────────────────────────┘
```

### State Transition

The state transition model is a fork of go-ethereum's `core/state_transition.go`, adapted for Armchain:

1. **Pre-validation**: Check nonce, balance, gas limits
2. **Gas purchase**: Deduct upfront gas cost from sender
3. **EVM execution**: Run contract bytecode
4. **Gas refund**: Refund unused gas (with EIP-3529 limits)
5. **Reward**: Validator receives transaction fees

### Key Differences from Ethereum

| Aspect              | Ethereum                 | Armchain                                      |
| ------------------- | ------------------------ | --------------------------------------------- |
| Block Production    | Single proposer per slot | All validators create events in parallel      |
| Finality            | \~15 minutes (2 epochs)  | Instant (aBFT)                                |
| Block Time          | 12 seconds fixed         | Variable (event-driven)                       |
| Transaction Signing | ECDSA (secp256k1)        | ML-DSA44 (Type 3)                             |
| Empty Block Policy  | Always produces blocks   | Skips empty blocks (1 min devnet, 3s fakenet) |

## Transaction Pool

Armchain maintains a standard Ethereum-compatible transaction pool (mempool):

```
┌─────────────────────────────────┐
│         Transaction Pool         │
├─────────────────────────────────┤
│  Pending:  Txns ready for        │
│            inclusion             │
│  Queued:   Txns with future      │
│            nonces                │
├─────────────────────────────────┤
│  Validation:                     │
│  - Signature valid (ML-DSA44)?   │
│  - Sufficient balance?           │
│  - Nonce correct?                │
│  - Gas price meets minimum?      │
│  - Gas limit within bounds?      │
└─────────────────────────────────┘
```

### Gas Price Model (EIP-1559)

Armchain uses EIP-1559 dynamic fee pricing:

```javascript
// Transaction fee structure
{
  maxFeePerGas: "20000000000",         // Maximum total fee (20 Gwei)
  maxPriorityFeePerGas: "2000000000",  // Tip to validator (2 Gwei)
  gasLimit: "21000",                    // Gas units
  type: 3                               // PQC transaction
}

// Actual fee = min(maxFeePerGas, baseFee + maxPriorityFeePerGas) × gasUsed
```

| Parameter           | Value              |
| ------------------- | ------------------ |
| Minimum Gas Price   | 1 Gwei             |
| Max Block Gas       | 20,500,000         |
| Base Fee Adjustment | EIP-1559 algorithm |

## Precompiled Contracts

Armchain supports all Ethereum precompiles through Berlin, plus a new PQC-specific precompile:

| Address    | Contract       | Purpose                                                                                                              |
| ---------- | -------------- | -------------------------------------------------------------------------------------------------------------------- |
| `0x01`     | ecRecover      | ECDSA public key recovery (retained for smart contract compatibility; **not used** for Armchain transaction signing) |
| `0x02`     | SHA-256        | SHA-256 hash                                                                                                         |
| `0x03`     | RIPEMD-160     | RIPEMD-160 hash                                                                                                      |
| `0x04`     | Identity       | Data copy                                                                                                            |
| `0x05`     | ModExp         | Modular exponentiation                                                                                               |
| `0x06`     | ecAdd          | BN256 point addition                                                                                                 |
| `0x07`     | ecMul          | BN256 scalar multiplication                                                                                          |
| `0x08`     | ecPairing      | BN256 pairing check                                                                                                  |
| `0x09`     | Blake2F        | BLAKE2b compression                                                                                                  |
| **`0x13`** | **pqcRecover** | **ML-DSA44 signature recovery (Armchain-specific)**                                                                  |

### pqcRecover Precompile (`0x13`)

The `pqcRecover` precompile recovers the signer's Ethereum address from an ML-DSA44 signature. This is the post-quantum equivalent of `ecRecover` and is the correct precompile to use for on-chain signature verification on Armchain.

**Input**: `signatureBlob` (3,732 bytes, containing the ML-DSA44 signature + public key)

**Output**: 32-byte padded address (last 20 bytes are the signer's address)

**Behavior**:

1. Validates the ML-DSA44 signature format
2. Extracts the public key from the signature blob
3. Derives the Ethereum address from the ML-DSA44 public key (`keccak256(publicKey)`, last 20 bytes)
4. Returns the 32-byte zero-padded address

**Solidity usage**:

```solidity
/// @notice Recover signer address from an ML-DSA44 signature
/// @param signature The 3,732-byte signature blob (signature + public key)
/// @return signer The recovered address
function pqcRecover(bytes memory signature) internal view returns (address signer) {
    (bool success, bytes memory result) = address(0x13).staticcall(signature);
    require(success, "pqcRecover failed");
    signer = address(uint160(uint256(bytes32(result))));
}
```

> **Note**: `ecRecover` (`0x01`) still exists for backward compatibility with contracts that use ECDSA internally (e.g., signature verification of off-chain ECDSA signatures). However, all Armchain transaction signatures use ML-DSA44, so `pqcRecover` (`0x13`) should be used for verifying Armchain transaction/message signatures on-chain.

## Smart Contract Considerations

### Contract Size Limit

The maximum smart contract bytecode size is **24,576 bytes** (same as Ethereum's EIP-170 limit).

### Gas Costs

Gas costs follow Berlin pricing:

| Operation            | Gas Cost                |
| -------------------- | ----------------------- |
| Simple transfer      | 21,000                  |
| Storage write (cold) | 20,000                  |
| Storage write (warm) | 5,000                   |
| Storage read (cold)  | 2,100                   |
| Storage read (warm)  | 100                     |
| Contract creation    | 32,000 + bytecode costs |
| `CALL` (cold)        | 2,600                   |
| `LOG` per topic      | 375                     |

### Token Standards

All standard ERC token interfaces work on Armchain:

* **ERC-20**: Fungible tokens
* **ERC-721**: Non-fungible tokens (NFTs)
* **ERC-1155**: Multi-token standard
* **ERC-4626**: Tokenized vaults

### Solidity Example

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ArmToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("Arm Token", "ARMT") {
        _mint(msg.sender, initialSupply);
    }
}
```

This deploys and works identically on Armchain as it would on Ethereum.

## Account Abstraction Opcodes

Armchain's native Account Abstraction introduces additional EVM opcodes beyond the standard London opcode set. These opcodes support the AA execution model: invoking account validation contracts, managing paymaster interactions, and enforcing gas accounting at the protocol level.

These opcodes are **not part of the standard Ethereum opcode set** and are not recognized by the Solidity compiler (`solc`). Any contract that uses them must be written in Yul, the EVM intermediate representation language. This affects:

* AA validation scheme contracts (MLDSA, Passkey, SLH-DSA, MPC wallets)
* Paymaster contracts that interact with AA-specific execution hooks
* Custom account validation logic written by third-party developers

Standard Solidity contracts that do not interact with these opcodes are unaffected and compile normally.

> For more on the AA system contracts and the Yul requirement, see [Account Abstraction: System Contracts](/account-abstraction/system-contracts).

## Further Reading

* [Consensus (Lachesis aBFT)](/architecture/consensus): How blocks are produced
* [Transaction Lifecycle](/architecture/transaction-lifecycle): From submission to EVM execution
* [Smart Contract Development](/developers/overview): Writing contracts for Armchain
* [Transaction Types](/pqc/transaction-types): Type 3 PQC transactions
* [Account Abstraction](/account-abstraction/overview): Native AA and how it uses the EVM


# Transaction Lifecycle

This page walks through what happens to a transaction from the moment you hit "send" to when it's permanently finalized on-chain. Understanding this flow is helpful for debugging, optimizing, and building reliable dApps.

## Overview

```
User signs transaction (ML-DSA44)
        │
        ▼
Transaction submitted via JSON-RPC
        │
        ▼
Node validates and adds to mempool
        │
        ▼
Validator includes in DAG event
        │
        ▼
Event gossiped to network
        │
        ▼
Lachesis determines event order
        │
        ▼
Event transactions assembled into block
        │
        ▼
EVM executes transactions
        │
        ▼
Block finalized (INSTANT - no rollbacks)
        │
        ▼
Transaction receipt available via RPC
```

## Phase 1: Transaction Creation

### Constructing the Transaction

A user constructs a transaction with these fields:

```javascript
{
  type: 3,                        // PQC transaction type (required)
  chainId: 55,                    // Armchain chain ID
  nonce: 42,                      // Sender's transaction count
  to: "0x742d35Cc...",           // Recipient address
  value: "1000000000000000000",   // Amount in wei (1 ARM)
  data: "0x",                     // Call data (empty for transfers)
  gasLimit: 21000,                // Maximum gas units
  maxFeePerGas: "20000000000",    // Max total fee per gas (20 Gwei)
  maxPriorityFeePerGas: "2000000000"  // Priority fee (2 Gwei)
}
```

### Signing with ML-DSA44

The transaction is signed using the sender's ML-DSA44 private key:

1. Serialize the transaction fields using RLP encoding
2. Compute the signing hash: `keccak256(0x03 || RLP(fields))`
3. Sign the hash with the ML-DSA44 private key
4. Construct the signature blob (raw signature + public key)
5. Append the signature blob to the transaction

The signed transaction uses a typed envelope format (EIP-2718):

```
0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList,
    signatureBlob    ← 3,732 bytes
])
```

> **Total transaction size**: \~3,930 bytes for a simple transfer (vs \~200 bytes with ECDSA). The additional size comes from the post-quantum signature.

## Phase 2: Submission and Validation

### RPC Submission

The signed transaction is submitted to a node via JSON-RPC:

```bash
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0x03f9...signed_tx_hex"],
    "id": 1
  }'
```

### Node Validation

The receiving node performs these checks:

| Check                    | Description                                               |
| ------------------------ | --------------------------------------------------------- |
| **Type validation**      | Transaction type must be `3` (PQC)                        |
| **Signature validation** | ML-DSA44 signature must be valid for the transaction hash |
| **Signature size**       | Signature blob must be exactly 3,732 bytes                |
| **Address derivation**   | Sender address derived from public key in signature       |
| **Nonce check**          | Nonce must match sender's current nonce                   |
| **Balance check**        | Sender must have sufficient ARM for value + gas           |
| **Gas price check**      | Gas price must meet the minimum (1 Gwei)                  |
| **Gas limit check**      | Gas limit must be within block gas limit                  |
| **Chain ID**             | Must match the network's chain ID                         |

### Mempool (Transaction Pool)

Valid transactions enter the mempool, organized as:

* **Pending**: Ready for inclusion (correct nonce, sufficient balance)
* **Queued**: Waiting for earlier nonces to be processed

> **Account Abstraction transactions** go through a separate AA mempool and gossip layer. They are routed to AA system contracts for validation before execution rather than following the standard Type 3 flow described on this page. See [Account Abstraction: Overview](/account-abstraction/overview) for the AA transaction lifecycle.

You can inspect the mempool:

```bash
# Get pending transaction count
curl -X POST https://www.armchain.org/devnet \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"txpool_status","params":[],"id":1}'
```

## Phase 3: Event Inclusion

### Validator Selection

Any active validator with available gas power can include the transaction:

1. Validator selects transactions from its local mempool (by gas price, highest first)
2. Validator creates a DAG **event** containing these transactions
3. The event includes references to parent events (up to 10 parents)
4. Validator signs the event with its ML-DSA44 validator key

### Event Structure

```
┌───────────────────────────────────┐
│            DAG Event               │
├───────────────────────────────────┤
│  Validator ID:    3                │
│  Sequence:        1057             │
│  Self-Parent:     [Event 1056]     │
│  Other Parents:   [V1:890, V5:442] │
│  Transactions:    [tx1, tx2, tx3]  │
│  Gas Used:        125,000          │
│  Signature:       ML-DSA44 (3,732B)│
└───────────────────────────────────┘
```

## Phase 4: Gossip and Consensus

### P2P Gossip

Events are propagated through the network using the devp2p gossip protocol:

```
Validator A creates event
    │
    ├── sends to Peer B
    │       ├── B sends to Peer D
    │       └── B sends to Peer E
    └── sends to Peer C
            └── C sends to Peer F
```

Default P2P port: `5050` (TCP/UDP).

### Lachesis Consensus

The Lachesis aBFT algorithm processes the growing DAG to determine a **total ordering** of events:

1. **Root detection**: Identify events that are "roots": events forkless-caused by ≥ 2/3 of validator weight from the previous frame
2. **Atropos election**: Root events in frame F+1 vote on candidate events in frame F. Votes are weighted by validator stake. When a candidate reaches quorum (`(TotalWeight × 2/3) + 1`), it is decided.
3. **Atropos selection**: Validators are iterated by weight (descending). The first validator whose candidate has a decided "YES" vote becomes the Atropos for that frame.
4. **Block assignment**: The Atropos event maps 1:1 to a block. All confirmed events from the frame are included.

This process is asynchronous: it does not require rounds, timeouts, or leader election. See [Consensus](/architecture/consensus) for a detailed worked example.

## Phase 5: EVM Execution

### Block Execution

Once events are ordered into a block:

1. Transactions are executed **sequentially** through the EVM
2. Each transaction updates the state trie
3. Events (logs) are emitted for contract interactions
4. Gas is consumed and fees are calculated
5. A state root is computed for the block

### Transaction Receipt

After execution, a receipt is generated:

```javascript
{
  transactionHash: "0xabc123...",
  blockNumber: 42000,
  blockHash: "0xdef456...",
  from: "0x742d35...",
  to: "0x5FbDB2...",
  status: 1,              // 1 = success, 0 = revert
  gasUsed: "21000",
  effectiveGasPrice: "5000000000",
  logs: [],
  cumulativeGasUsed: "21000",
  type: "0x3"             // PQC transaction
}
```

## Phase 6: Finality

### Instant and Irreversible

Once the block is sealed, the transaction is **final**:

* **No waiting for confirmations**: 1 confirmation = absolute finality
* **No rollbacks**: The aBFT guarantee means the block will never be reverted
* **No chain reorganizations**: The DAG structure prevents forks
* **Deterministic**: Every honest node will agree on the same final state

### Confirming Finality

```javascript
// Using @armchain-ethersv6/ethers
const tx = await wallet.sendTransaction({
  to: recipient,
  value: ethers.parseEther("1.0"),
  type: 3
});

// Wait for receipt - once received, it's FINAL
const receipt = await tx.wait();
console.log("Final in block:", receipt.blockNumber);
// That's it - no need to wait for more confirmations
```

## Transaction Size Comparison

| Transaction Type  | Typical Size      | Signature Size             | Notes                   |
| ----------------- | ----------------- | -------------------------- | ----------------------- |
| Type 0 (Legacy)   | \~200 bytes       | 65 bytes (ECDSA)           | No access list          |
| Type 2 (EIP-1559) | \~250 bytes       | 65 bytes (ECDSA)           | Dynamic fees            |
| **Type 3 (PQC)**  | **\~3,930 bytes** | **3,732 bytes (ML-DSA44)** | **Post-quantum secure** |

The \~57x increase in signature blob size (65 → 3,732 bytes) drives the overall \~36x increase in transaction size (\~110 → \~3,930 bytes). This is factored into Armchain's block gas limits and network parameters.

## Error Handling

Common transaction errors:

| Error                            | Cause                               | Solution                                        |
| -------------------------------- | ----------------------------------- | ----------------------------------------------- |
| `nonce too low`                  | Transaction nonce already used      | Get latest nonce from `eth_getTransactionCount` |
| `insufficient funds`             | Not enough ARM for value + gas      | Check balance with `eth_getBalance`             |
| `gas too low`                    | Gas limit below minimum             | Use `eth_estimateGas` to get estimate           |
| `invalid signature`              | Malformed ML-DSA44 signature        | Ensure signing with `@armchain-ethersv6/ethers` |
| `invalid sender`                 | Can't derive address from signature | Verify key generation and signing process       |
| `transaction type not supported` | Non-Type 3 transaction              | Set `type: 3` in transaction object             |

## Further Reading

* [PQC Transaction Types](/pqc/transaction-types): Detailed Type 3 format specification
* [ML-DSA44 Deep Dive](/pqc/mldsa44): How signatures are created and verified
* [JSON-RPC API](/api-reference/json-rpc): RPC methods for transaction management
* [Armchain Ethers SDK](/developers/overview-1): SDK for building transactions
* [Account Abstraction Overview](/account-abstraction/overview): The AA transaction lifecycle and separate mempool


# Post-Quantum Cryptography

Armchain is quantum-safe by default. Every transaction, every validator signature, and every wallet key uses ML-DSA44, a NIST-standardized algorithm designed to resist attacks from quantum computers.

| Document                                    | Description                                                     |
| ------------------------------------------- | --------------------------------------------------------------- |
| [Overview](/pqc/overview)                   | Why post-quantum cryptography matters and how Armchain uses it  |
| [ML-DSA-44](/pqc/mldsa44)                   | Algorithm details, key sizes, and performance characteristics   |
| [Transaction Types](/pqc/transaction-types) | How PQC transactions differ from standard Ethereum transactions |


# Overview

Armchain replaces the traditional cryptography used by most blockchains (ECDSA/secp256k1) with **ML-DSA44** (FIPS 204), a quantum-resistant signature algorithm standardized by NIST. This protection applies at every layer: user transactions, validator signatures, and wallet key derivation.

This page explains why post-quantum cryptography matters, how Armchain implements it, and what it means for developers.

## Why Post-Quantum?

### The Quantum Threat

Today's blockchain networks rely on **ECDSA (secp256k1)** for transaction signing. The security of this algorithm depends on mathematical problems that quantum computers can solve efficiently using **Shor's algorithm**.

| Threat                         | Impact                                                                        | Timeline               |
| ------------------------------ | ----------------------------------------------------------------------------- | ---------------------- |
| **Harvest Now, Decrypt Later** | Adversaries store encrypted data today, decrypt when quantum computers arrive | **Already happening**  |
| **Key Recovery**               | Derive private keys from public keys on-chain                                 | 5–10 years (estimated) |
| **Signature Forgery**          | Forge valid transaction signatures                                            | 5–10 years (estimated) |

Once a sufficiently powerful quantum computer exists, **every ECDSA-signed transaction becomes forgeable**. Funds in addresses with exposed public keys (any address that has sent a transaction) are immediately vulnerable.

### Armchain's Approach

Armchain addresses this **proactively** by replacing ECDSA with **ML-DSA44** (Module-Lattice Digital Signature Algorithm):

* **Transaction signing**: All user transactions use ML-DSA44 (Type 3 transactions)
* **Validator signatures**: All consensus messages use ML-DSA44
* **Wallet key generation**: HD wallets derive ML-DSA44 keys
* **Address derivation**: Addresses are derived from ML-DSA44 public keys
* **SDK support**: `@armchain-ethersv6/ethers` implements ML-DSA44 natively

## NIST Standardization

ML-DSA (formerly known as CRYSTALS-Dilithium) was standardized by NIST in **FIPS 204** (August 2024) as part of the Post-Quantum Cryptography Standardization Project.

| Standard     | Algorithm          | Status                             |
| ------------ | ------------------ | ---------------------------------- |
| **FIPS 204** | ML-DSA (Dilithium) | **Finalized** - Digital signatures |
| FIPS 203     | ML-KEM (Kyber)     | Finalized - Key encapsulation      |
| FIPS 205     | SLH-DSA (SPHINCS+) | Finalized - Hash-based signatures  |

Armchain chose **ML-DSA44** (security level 2, \~128-bit classical security) as the optimal balance of security, key/signature size, and performance.

## Architecture

### Where PQC Applies

```
┌───────────────────────────────────────────────────┐
│                    User Layer                      │
│  Wallet → ML-DSA44 key generation & storage       │
│  dApp   → Sign transactions with ML-DSA44         │
├───────────────────────────────────────────────────┤
│                    SDK Layer                       │
│  @armchain-ethersv6/ethers                        │
│    → MLDSASigningKey / MLDSAWallet                │
│    → Type 3 transaction encoding                  │
│    → HD derivation (coin type 65536)              │
├───────────────────────────────────────────────────┤
│                   Network Layer                    │
│  armchain-client (armnode)                        │
│    → Validate ML-DSA44 signatures on ingest       │
│    → Validator consensus with ML-DSA44            │
│    → Event signing in DAG                         │
├───────────────────────────────────────────────────┤
│                  Consensus Layer                   │
│  Lachesis aBFT                                    │
│    → All parent hash signatures: ML-DSA44         │
│    → Block attestation: ML-DSA44                  │
└───────────────────────────────────────────────────┘
```

### What Stays the Same

Armchain maintains full EVM compatibility. The following are **unchanged**:

* **Smart contracts**: Solidity/Vyper, same opcodes, same ABI encoding
* **Addresses**: 20-byte Ethereum-style addresses (derived from ML-DSA44 public key hash)
* **JSON-RPC**: All standard `eth_*` methods work identically
* **Contract interactions**: ABI encoding, function selectors, events, logs
* **Gas model**: EIP-1559 fee market, same gas costs for opcodes

## Key Sizes Comparison

| Property                 | ECDSA (secp256k1)            | ML-DSA44                                             |
| ------------------------ | ---------------------------- | ---------------------------------------------------- |
| Private Key              | 32 bytes                     | **2,560 bytes**                                      |
| Public Key               | 33 bytes (compressed)        | **1,312 bytes**                                      |
| Raw Signature            | 64 bytes (r, s) + 1 byte (v) | **2,420 bytes**                                      |
| Signature Blob (in txns) | 65 bytes                     | **3,732 bytes** (signature 2,420 + public key 1,312) |
| Security Level           | \~128-bit (classical only)   | **\~128-bit (classical + quantum)**                  |
| NIST Category            | -                            | **Category 2**                                       |

> **Note**: Larger keys and signatures result in larger transactions (\~3,930 bytes for a simple transfer vs \~110 bytes with ECDSA). The signature blob (3,732 bytes) contains the raw ML-DSA44 signature (2,420 bytes) concatenated with the public key (1,312 bytes), since ML-DSA44 does not support public key recovery. This is an inherent trade-off of lattice-based cryptography.

## Section Contents

| Document                                    | Description                                |
| ------------------------------------------- | ------------------------------------------ |
| [ML-DSA44 Deep Dive](/pqc/mldsa44)          | Algorithm details, parameters, performance |
| [Transaction Types](/pqc/transaction-types) | Type 3 PQC transaction format and encoding |

## Expanded Signing Options via Account Abstraction

ML-DSA44 is the signing algorithm for all standard Armchain transactions. Armchain's native Account Abstraction layer extends this further: AA accounts can be controlled using additional cryptographic schemes beyond ML-DSA44.

Supported AA validation schemes include:

* **SLH-DSA (SPHINCS+)**: Another NIST-standardized post-quantum scheme (FIPS 205), based on hash functions rather than lattices
* **Passkey (P-256 / WebAuthn)**: NIST P-256 signatures from biometric devices and hardware security keys
* **MPC for ECDSA**: Multi-party ECDSA for Guardian accounts and social recovery

These schemes apply to smart contract accounts within the AA framework. The standard transaction layer (Type 3) continues to use ML-DSA44 exclusively.

> For a full description of all available schemes, see [Account Abstraction: Validation Schemes](/account-abstraction/validation-schemes).

## Further Reading

* [FIPS 204 (ML-DSA)](https://csrc.nist.gov/pubs/fips/204/final): NIST standard
* [FIPS 205 (SLH-DSA)](https://csrc.nist.gov/pubs/fips/205/final): NIST SLH-DSA standard
* [CRYSTALS-Dilithium](https://pq-crystals.org/dilithium/): Algorithm specification
* [@noble/post-quantum](https://github.com/paulmillr/noble-post-quantum): Library used by Armchain
* [Ethers SDK](/developers/overview-1): Using PQC with the SDK
* [Account Abstraction](/account-abstraction/overview): Native AA and its validation scheme framework


# ML-DSA-44

**ML-DSA44** (Module-Lattice Digital Signature Algorithm, Security Level 2) is the post-quantum signature scheme used throughout Armchain. It's standardized by NIST in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final).

This page covers the algorithm's parameters, performance characteristics, and how it's used in Armchain. You don't need to understand lattice math to build on Armchain (the SDK handles all of this), but this background is useful if you're curious about the cryptographic foundations.

## Algorithm Overview

ML-DSA is a lattice-based signature scheme derived from **CRYSTALS-Dilithium**. Its security relies on the hardness of the **Module Learning With Errors (MLWE)** problem, a problem believed to be hard for both classical and quantum computers.

### How Lattice Signatures Work (Simplified)

1. **Key Generation**: Create a matrix **A** and vectors **s₁**, **s₂** with small coefficients. The public key includes **A** and **t** = **As₁** + **s₂**
2. **Signing**: Use rejection sampling to produce a signature **z** and a hint **h** that don't leak information about the secret key
3. **Verification**: Check that the signature is consistent with the public key and the message hash

The security guarantee: recovering **s₁** from **A** and **t** requires solving MLWE, which no known quantum algorithm can do efficiently.

## Parameters (ML-DSA44)

| Parameter                   | Value           | Description                                                       |
| --------------------------- | --------------- | ----------------------------------------------------------------- |
| **Security Level**          | NIST Category 2 | \~128-bit classical + quantum security                            |
| **Private Key Size**        | 2,560 bytes     | Full secret key material                                          |
| **Public Key Size**         | 1,312 bytes     | Verification key                                                  |
| **Raw Signature Size**      | 2,420 bytes     | ML-DSA44 detached signature                                       |
| **Signature Blob Size**     | 3,732 bytes     | Raw signature (2,420) + public key (1,312) - used in transactions |
| **Seed Size**               | 32 bytes        | Random seed for key generation                                    |
| **Matrix Dimension (k, l)** | (4, 4)          | Module rank                                                       |
| **Polynomial Degree (n)**   | 256             | Ring degree                                                       |
| **Modulus (q)**             | 8,380,417       | Prime modulus                                                     |
| **η (eta)**                 | 2               | Secret key coefficient bound                                      |
| **γ₁**                      | 2¹⁷             | Signature coefficient range                                       |
| **γ₂**                      | (q-1)/88        | Low-order rounding range                                          |

### Why ML-DSA44 (Not 65 or 87)?

ML-DSA comes in three security levels:

| Variant      | NIST Level | PK Size   | Sig Size  | Security      |
| ------------ | ---------- | --------- | --------- | ------------- |
| **ML-DSA44** | **2**      | **1,312** | **3,732** | **\~128-bit** |
| ML-DSA65     | 3          | 1,952     | 3,309     | \~192-bit     |
| ML-DSA87     | 5          | 2,592     | 4,627     | \~256-bit     |

Armchain chose ML-DSA44 because:

1. **128-bit security is sufficient**: NIST recommends Category 2+ for most applications
2. **Smaller signatures**: Reduces bandwidth and storage across all nodes
3. **Better performance**: Faster signing and verification
4. **Industry consensus**: Most PQC migration efforts target level 2

## Implementation in Armchain

### JavaScript / TypeScript (SDK)

The `@armchain-ethersv6/ethers` library uses [`@noble/post-quantum`](https://github.com/paulmillr/noble-post-quantum) v0.5.1 for ML-DSA44 operations (key generation, signing, and verification).

See the [Ethers SDK documentation](/developers/overview-1) for usage examples.

### Go (Node Client)

The `armchain-client` uses [`github.com/cloudflare/circl`](https://github.com/cloudflare/circl) for ML-DSA44 (key generation, signing, and verification).

## Address Derivation

Addresses are derived from ML-DSA44 public keys using the same process as Ethereum, but with the larger public key:

```
ML-DSA44 Public Key (1,312 bytes)
        │
        ▼
   Keccak-256 hash (32 bytes)
        │
        ▼
   Take last 20 bytes
        │
        ▼
   Address (20 bytes, 0x-prefixed)
```

Addresses look identical to Ethereum addresses (e.g., `0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18`) but are derived from ML-DSA44 public keys.

## HD Derivation

Armchain uses a modified BIP-44 HD derivation scheme:

```
m / 44' / 65536' / account' / 0 / address'
```

| Component      | Value      | Notes                                         |
| -------------- | ---------- | --------------------------------------------- |
| BIP-44 purpose | 44'        | Standard                                      |
| Coin type      | **65536'** | Armchain-specific                             |
| Account        | n'         | Per-account derivation (hardened)             |
| Change         | 0          | Always 0 (no change addresses)                |
| Address index  | m'         | **Hardened** (unlike Ethereum's non-hardened) |

> **All derivation indices are hardened** to prevent public key derivation, which is critical because ML-DSA44 does not support the same public key derivation properties as secp256k1.

### Derivation Process

Since ML-DSA44 key generation takes a 32-byte seed (not a scalar like ECDSA), the HD derivation uses HMAC-based key derivation to produce child seeds from a BIP-39 master seed. Each child seed is then passed to the ML-DSA44 key generation function to produce a key pair.

```
Master Seed (from BIP-39 mnemonic)
        │
        ▼
   Key derivation function
        │
        ▼
   Master key (32 bytes)
        │
   ┌────┼────┐
   ▼    ▼    ▼
  Derive child seeds via HMAC chain
        │
        ▼
   32-byte seed → ML-DSA44 keygen → (pubkey, privkey)
```

See the `@armchain-ethersv6/ethers` SDK for the reference implementation.

## Performance

Benchmarks on a modern x86\_64 CPU (`@noble/post-quantum` in JavaScript):

| Operation           | Time     | Notes                       |
| ------------------- | -------- | --------------------------- |
| Key Generation      | \~2.0 ms | From 32-byte seed           |
| Signing             | \~3.5 ms | Includes rejection sampling |
| Verification        | \~5.0 ms |                             |
| HD Child Derivation | \~4.1 ms | Full path derivation        |
| Address Computation | \~0.3 ms | Keccak-256 of public key    |

Benchmarks on the Go client (`circl` library):

| Operation      | Time     | Notes                           |
| -------------- | -------- | ------------------------------- |
| Key Generation | \~0.1 ms | Optimized Go implementation     |
| Signing        | \~0.3 ms |                                 |
| Verification   | \~0.1 ms | Node validates every inbound tx |

## Security Considerations

### Known Attack Vectors (Mitigated)

1. **Side-channel attacks**: The `@noble/post-quantum` library uses constant-time operations. Rejection sampling is implemented to prevent timing leaks.
2. **Seed reuse**: HD derivation ensures unique seeds per account/address.
3. **Hash collisions**: ML-DSA44 uses internal SHAKE-256 hashing, which is quantum-resistant.

### Assumptions

* ML-DSA44's security relies on the hardness of MLWE. If this problem is found to be easy, the scheme breaks.
* NIST Category 2 corresponds to \~128-bit security against quantum attacks (equivalent to AES-128 key recovery).
* The scheme is considered secure against all currently known quantum algorithms.

## Further Reading

* [FIPS 204 Standard](https://csrc.nist.gov/pubs/fips/204/final)
* [CRYSTALS-Dilithium Specification](https://pq-crystals.org/dilithium/)
* [@noble/post-quantum Library](https://github.com/paulmillr/noble-post-quantum)
* [Cloudflare CIRCL](https://github.com/cloudflare/circl)
* [PQC Overview](/pqc/overview): Why post-quantum cryptography matters
* [Transaction Types](/pqc/transaction-types): How signatures are encoded in transactions


# Transaction Types

Armchain introduces **Type 3** transactions for post-quantum cryptography. If you're using the Armchain Ethers SDK, this is handled automatically. The SDK creates and signs Type 3 transactions for you. This page is for developers who want to understand the format details or build custom tooling.

## Transaction Type Summary

| Type  | Name               | EIP          | Signature    | Supported |
| ----- | ------------------ | ------------ | ------------ | --------- |
| 0     | Legacy             | Pre-EIP-2718 | ECDSA        | **No**    |
| 1     | Access List        | EIP-2930     | ECDSA        | **No**    |
| 2     | EIP-1559           | EIP-1559     | ECDSA        | **No**    |
| **3** | **PQC (Armchain)** | -            | **ML-DSA44** | **Yes**   |

> **Armchain only supports Type 3 transactions.** ECDSA-signed transactions (Types 0, 1, 2) are rejected by the network.

## Type 3 Transaction Format

### Fields

| Field                  | Type      | Description                            |
| ---------------------- | --------- | -------------------------------------- |
| `type`                 | `uint8`   | Always `0x03`                          |
| `chainId`              | `uint64`  | Chain ID (55 devnet)                   |
| `nonce`                | `uint64`  | Sender account nonce                   |
| `maxPriorityFeePerGas` | `uint256` | Tip for the validator (EIP-1559)       |
| `maxFeePerGas`         | `uint256` | Maximum total gas price (EIP-1559)     |
| `gasLimit`             | `uint64`  | Maximum gas for execution              |
| `to`                   | `address` | Recipient (null for contract creation) |
| `value`                | `uint256` | ARM amount in wei                      |
| `data`                 | `bytes`   | Calldata or constructor bytecode       |
| `accessList`           | `[]tuple` | Storage access list (EIP-2930)         |
| `signatureBlob`        | `bytes`   | ML-DSA44 signature + public key        |

### Signature Blob

The `signatureBlob` field contains the **concatenation** of the ML-DSA44 signature and the public key:

```
signatureBlob (3,732 bytes)
```

ML-DSA44 does not support public key recovery, so both the signature and the public key are included in the blob.

### RLP Encoding

Type 3 transactions use **typed transaction envelopes** (EIP-2718):

```
Serialized = 0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList,
    signatureBlob
])
```

The **signing payload** (hash that is signed) is:

```
sigHash = Keccak256(0x03 || RLP([
    chainId,
    nonce,
    maxPriorityFeePerGas,
    maxFeePerGas,
    gasLimit,
    to,
    value,
    data,
    accessList
]))
```

The signature is computed over the `sigHash`, then the `signatureBlob` (signature + public key) is appended to the transaction fields.

## Creating Type 3 Transactions

### Using the Ethers SDK

```javascript
import { ethers } from "@armchain-ethersv6/ethers";

const provider = new ethers.JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// Simple transfer
const tx = await wallet.sendTransaction({
  to: "0xRecipientAddress",
  value: ethers.parseEther("1.0"),
  type: 3, // PQC transaction type
});

console.log("TX hash:", tx.hash);
const receipt = await tx.wait();
console.log("Confirmed in block:", receipt.blockNumber);
```

### Manual Transaction Construction

```javascript
import { ethers } from "@armchain-ethersv6/ethers";

const provider = new ethers.JsonRpcProvider("https://www.armchain.org/devnet");
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

// Build a transaction manually
const txRequest = {
  type: 3,
  chainId: 55,
  to: "0xRecipientAddress",
  value: ethers.parseEther("1.0"),
  nonce: await provider.getTransactionCount(wallet.address),
  maxFeePerGas: ethers.parseUnits("100", "gwei"),
  maxPriorityFeePerGas: ethers.parseUnits("1", "gwei"),
  gasLimit: 21000n,
};

// Sign (produces the signatureBlob internally)
const signedTx = await wallet.signTransaction(txRequest);

// Broadcast
const response = await provider.broadcastTransaction(signedTx);
console.log("TX hash:", response.hash);
```

### Contract Deployment

```javascript
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(/* constructor args */, {
  type: 3,
});
await contract.waitForDeployment();
console.log("Deployed at:", await contract.getAddress());
```

### Contract Interaction

```javascript
const contract = new ethers.Contract(contractAddress, abi, wallet);

// Read (no transaction needed)
const value = await contract.someViewFunction();

// Write (creates a Type 3 transaction)
const tx = await contract.someFunction(arg1, arg2, {
  type: 3,
});
await tx.wait();
```

## JSON-RPC Representation

When queried via JSON-RPC (`eth_getTransactionByHash`), Type 3 transactions include:

```json
{
  "type": "0x3",
  "chainId": "0x37",
  "nonce": "0x0",
  "maxPriorityFeePerGas": "0x3b9aca00",
  "maxFeePerGas": "0x174876e800",
  "gas": "0x5208",
  "to": "0x...",
  "value": "0xde0b6b3a7640000",
  "input": "0x",
  "accessList": [],
  "signatureBlob": "0x...",
  "hash": "0x...",
  "blockHash": "0x...",
  "blockNumber": "0x1",
  "transactionIndex": "0x0"
}
```

> **Note**: Type 3 transactions do NOT have `v`, `r`, `s` fields. They have a single `signatureBlob` field instead.

## Size Comparison

| Component                   | Type 2 (ECDSA)                     | Type 3 (ML-DSA44)                   |
| --------------------------- | ---------------------------------- | ----------------------------------- |
| Transaction fields          | \~45 bytes                         | \~45 bytes                          |
| Signature                   | 65 bytes (v, r, s)                 | 2,420 bytes (raw ML-DSA44)          |
| Public key                  | 0 bytes (recovered from signature) | 1,312 bytes (appended to signature) |
| **Signature blob total**    | **65 bytes**                       | **3,732 bytes**                     |
| **Total (simple transfer)** | **\~110 bytes**                    | **\~3,930 bytes**                   |

The \~36x increase in transaction size is the cost of quantum resistance. Impact:

* **Bandwidth**: More data to propagate between nodes
* **Storage**: Larger blocks on disk
* **Intrinsic gas**: Higher data-related gas costs

However, it does NOT impact:

* **Execution gas**: EVM computation costs are identical
* **Finality time**: Still instant (Lachesis aBFT)
* **Throughput**: Block gas limits accommodate the larger transactions

## Validation Rules

When a Type 3 transaction arrives at a node, the following checks are performed:

1. **Type check**: Transaction type must be `0x03`
2. **Chain ID check**: Must match the node's chain ID
3. **Signature blob size**: Must be exactly 3,732 bytes (2,420 + 1,312)
4. **ML-DSA44 verification**: Verify the signature over the signing hash using the public key from the signature blob
5. **Address derivation**: The sender address (`from`) is derived from the public key: `Keccak256(pubkey)[12:]`
6. **Nonce check**: Must match the sender's current nonce
7. **Balance check**: Sender must have sufficient ARM for `value + gas * maxFeePerGas`
8. **Gas limit check**: Must be at least the intrinsic gas

If any check fails, the transaction is rejected and not propagated.

## Further Reading

* [ML-DSA44 Deep Dive](/pqc/mldsa44): Algorithm details
* [PQC Overview](/pqc/overview): Why post-quantum cryptography
* [Ethers SDK](/developers/overview-1): Working with transactions in code


# Account Abstraction

Armchain implements **native Account Abstraction (AA)** at the protocol level, making smart contract accounts first-class citizens of the network. Unlike application-layer implementations (such as ERC-4337), Armchain's AA is built directly into the consensus client and EVM, with dedicated system contracts, a separate AA mempool, and its own gossip layer.

This gives developers and users flexibility in how accounts are controlled and how transactions are validated, from post-quantum signatures to biometric passkeys to MPC-managed keys, without sacrificing Armchain's instant finality or EVM compatibility.

| Document                                                      | Description                                                          |
| ------------------------------------------------------------- | -------------------------------------------------------------------- |
| [Overview](/account-abstraction/overview)                     | How native AA works on Armchain and what it enables                  |
| [Validation Schemes](/account-abstraction/validation-schemes) | Supported signing algorithms and account types                       |
| [Paymaster](/account-abstraction/paymaster)                   | Gas sponsorship, Foundation Paymaster, and economic policy           |
| [System Contracts](/account-abstraction/system-contracts)     | Protocol-level AA contracts, Yul implementation, and developer notes |


# Overview

Armchain ships with **native Account Abstraction (AA)** integrated directly into the protocol. This means smart contract accounts are fully supported at the consensus and client level, not bolted on as an application-layer standard. Users can control their accounts using a range of cryptographic schemes, and third parties can sponsor transaction fees through paymasters, all enforced at the network layer.

## What Native AA Means

Most blockchains use Externally Owned Accounts (EOAs): accounts controlled by a single private key. Account abstraction replaces that fixed signing requirement with arbitrary validation logic expressed in a smart contract. The account itself decides what constitutes a valid transaction.

On Armchain, this is native: the client nodes understand AA transactions directly, route them through a dedicated mempool, and apply validation via protocol-level system contracts before execution. There is no bundler, no separate entry point contract to fund, and no off-chain relay required.

| Feature           | Application-Layer AA (e.g. ERC-4337) | Armchain Native AA                            |
| ----------------- | ------------------------------------ | --------------------------------------------- |
| Validation layer  | Smart contract (EntryPoint)          | Protocol / system contract                    |
| Mempool           | Bundler-managed, off-chain           | Separate AA mempool in the client             |
| Gossip            | Standard P2P                         | Dedicated AA gossip layer                     |
| Gas sponsorship   | Paymaster contract                   | Paymaster contract (Foundation + third-party) |
| Custom validation | Solidity wallet contract             | System contract (Yul) or custom extension     |

## What It Enables

* **Multiple signing schemes**: Accounts can validate transactions using post-quantum signatures, WebAuthn passkeys, MPC-managed ECDSA keys, or any custom scheme.
* **Gasless onboarding**: The Foundation Paymaster sponsors contract deployments for new users, funded with ARM tokens. Users can get started without holding ARM first.
* **Third-party gas sponsorship**: Any entity can operate a paymaster to cover fees for their users.
* **Modular account logic**: Developers can implement custom account validation by extending the system contract framework.

## Architecture

```
┌─────────────────────────────────────────────────────────┐
│                       User / dApp                        │
│   Constructs AA transaction with chosen signing scheme   │
└──────────────────────────┬──────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                  AA Mempool + Gossip Layer               │
│   Separate from the standard transaction mempool         │
│   Validates: paymaster deposit, basic format checks      │
│   Propagates AA transactions between nodes               │
└──────────────────────────┬──────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│               AA System Contracts (Genesis)              │
│   Factory Registry   ← account deployment               │
│   Validation Schemes ← MLDSA, Passkey, SLH-DSA, MPC    │
│   Foundation Paymaster ← gas sponsorship                │
└──────────────────────────┬──────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────┐
│                   EVM Execution                          │
│   Account validation → paymaster validation → execution  │
└─────────────────────────────────────────────────────────┘
```

## AA Transaction Flow

1. **Construction**: The user (or dApp) builds an AA transaction specifying the account contract address, call data, chosen validation scheme, and an optional paymaster.
2. **AA Mempool ingress**: The node receives the transaction on the AA gossip layer. It checks the paymaster has a sufficient deposit and that the transaction is well-formed.
3. **Gossip**: The AA transaction is propagated to peers via a dedicated AA gossip protocol, separate from the standard event gossip used by regular transactions.
4. **Account validation**: At inclusion time, the account's system contract validation function is called. The signature is checked against the chosen scheme (MLDSA, Passkey, SLH-DSA, or MPC).
5. **Paymaster validation**: If a paymaster is specified, the paymaster contract confirms it is willing to cover the gas cost for this transaction.
6. **Execution**: The account's call is executed through the EVM. Gas is deducted from the paymaster deposit (if sponsored) or from the account balance.
7. **Finality**: Like all Armchain transactions, the result is final the moment the block is sealed. No confirmation waiting is needed.

## Separation of Concerns

The AA mempool and gossip layer operate independently from the standard PQC (Type 3) transaction pipeline. Regular Type 3 transactions and AA transactions each have dedicated:

* Ingest paths in the client
* Mempool data structures
* Gossip message types
* Validation rules

This separation ensures that AA-specific spam or load cannot degrade the standard transaction pipeline, and vice versa.

## Current Status

The baseline implementation is complete and tested:

* All validation scheme system contracts are deployed at genesis
* The Foundation Paymaster is operational
* AA mempool and gossip layer are integrated into both the geth and opera (Armchain) clients
* Unit, integration, and end-to-end tests cover the full AA pipeline

The following areas are in active development:

| Area                                      | Status                             |
| ----------------------------------------- | ---------------------------------- |
| Ethers SDK support for AA transactions    | Planned                            |
| Wallet UI for validation scheme selection | Planned                            |
| Block explorer AA transaction indexing    | Planned                            |
| Gas policy review for AA transactions     | Under review                       |
| Falcon validation scheme                  | Pending reliable Go implementation |
| Social login via MPC                      | Under consideration                |

## Further Reading

* [Validation Schemes](/account-abstraction/validation-schemes): Supported signing algorithms and how to choose between them
* [Paymaster](/account-abstraction/paymaster): How gas sponsorship works and the staking-based economic policy
* [System Contracts](/account-abstraction/system-contracts): Protocol-level contracts, Yul implementation, and developer guidance
* [EVM Integration](/architecture/evm): EVM opcodes and changes supporting native AA


# Validation Schemes

Armchain's native AA framework allows accounts to validate transactions using different cryptographic schemes. Each scheme is implemented as a system contract deployed at genesis. The architecture is modular: developers can add new validation logic by following the same contract interface.

## Supported Schemes

| Scheme      | Algorithm                     | Use Case                                                           | Status    |
| ----------- | ----------------------------- | ------------------------------------------------------------------ | --------- |
| **MLDSA**   | ML-DSA44 (FIPS 204)           | Post-quantum signing, same as standard Armchain transactions       | Available |
| **Passkey** | P-256 (ECDSA over secp256r1)  | WebAuthn, biometric devices (Touch ID, Face ID, hardware keys)     | Available |
| **SLH-DSA** | SLH-DSA / SPHINCS+ (FIPS 205) | Hash-based post-quantum, different security assumptions from MLDSA | Available |
| **MPC**     | ECDSA (secp256k1) via MPC     | Multi-party computation, Guardian accounts, social recovery        | Available |
| **Falcon**  | Falcon (NIST PQC)             | Lattice-based post-quantum, compact signatures                     | Pending   |
| **Custom**  | Developer-defined             | Any validation logic following the AA system contract interface    | Available |

***

## MLDSA

**ML-DSA44** (Module-Lattice Digital Signature Algorithm, FIPS 204) is Armchain's native signing algorithm for standard transactions. It is also available as a validation scheme for AA accounts.

An MLDSA AA account is controlled by an ML-DSA44 key pair. This is appropriate when the owner wants post-quantum security and is already using the Armchain tooling, or when migrating from a standard EOA to a smart account while keeping the same signing key type.

* **Security**: Post-quantum (\~128-bit security, NIST Category 2)
* **Key size**: 1,312-byte public key, 2,560-byte private key
* **Signature size**: 2,420 bytes

> For the full ML-DSA44 specification used by Armchain, see [ML-DSA44 Deep Dive](/pqc/mldsa44).

***

## Passkey (P-256 / WebAuthn)

The **Passkey** validation scheme uses **P-256** (ECDSA over secp256r1 / NIST P-256), the cryptographic algorithm underlying the WebAuthn standard. WebAuthn is supported natively by modern browsers (Chrome, Safari, Firefox) and operating systems.

This enables users to control a blockchain account using:

* **Biometric authentication**: Touch ID on MacBook, Face ID on iPhone, fingerprint readers on Android
* **Hardware security keys**: YubiKey and similar FIDO2 devices
* **Platform authenticators**: Windows Hello, iCloud Keychain

The passkey flow is familiar to users from web authentication: no seed phrase to manage, no separate private key to store. The signing key is generated and stored by the device's secure enclave or hardware authenticator.

> **Note**: P-256 is not quantum-resistant. The Passkey scheme trades off post-quantum security for maximum user-friendliness and hardware compatibility. For accounts where long-term quantum resistance matters, use MLDSA or SLH-DSA instead.

* **Standard**: WebAuthn / FIDO2 (W3C), NIST P-256 curve
* **Use case**: Consumer-facing dApps, biometric login, zero-seed-phrase onboarding
* **Browser support**: Chrome, Safari, Edge, Firefox (all modern versions)

***

## SLH-DSA (SPHINCS+)

**SLH-DSA** (Stateless Hash-Based Digital Signature Algorithm, FIPS 205), formerly known as SPHINCS+, is a post-quantum signature scheme standardized by NIST alongside ML-DSA.

Where MLDSA is based on the hardness of the Module Learning With Errors (MLWE) problem (a lattice assumption), SLH-DSA is based entirely on the security of hash functions. This gives it a different security profile: it does not depend on any lattice hardness assumption, making it a useful hedge for scenarios where the underlying assumption of MLDSA is ever questioned.

* **Security**: Post-quantum, hash-based (no lattice assumptions)
* **NIST Standard**: FIPS 205
* **Trade-off**: Larger signatures than MLDSA; slower signing; simpler security analysis

SLH-DSA is appropriate for high-assurance accounts where diverse cryptographic assumptions are a priority, such as long-lived treasury or governance accounts.

***

## MPC for ECDSA (Guardian Accounts)

The **MPC** scheme enables AA accounts controlled through **Multi-Party Computation over ECDSA (secp256k1)**. The on-chain validation logic verifies ECDSA signatures in the standard secp256k1 format, while the signing keys are managed off-chain through an MPC protocol that splits the private key across multiple parties.

No single party ever holds the complete private key. This enables:

* **Social recovery**: Account access can be recovered through a trusted set of guardians without any single guardian knowing the full key
* **Threshold signing**: M-of-N approval schemes for organizational wallets
* **Institutional custody**: Enterprise-grade key management with separation of duties

These accounts are referred to internally as **Guardian accounts**. The Guardian contract handles deployment, counterfactual address prediction, and execution of validated calls.

> **Note on social logins**: Social login (using OAuth providers like Google or Apple as account recovery) is architecturally possible via MPC but requires operating MPC infrastructure. This is under consideration as a future Armchain feature and is not currently available.

***

## Falcon (Pending)

**Falcon** is a lattice-based post-quantum signature scheme selected by NIST during the PQC standardization process. It produces smaller signatures than both MLDSA and SLH-DSA, which is advantageous for bandwidth-constrained environments.

Falcon support is not yet available on Armchain. A reliable, production-ready Go implementation has not been identified at this time. This scheme will be added once a suitable implementation is available.

***

## Custom Validation Schemes

The AA validation framework is intentionally modular. Developers can implement their own account validation logic by conforming to the system contract interface. Examples of what custom schemes can express:

* Multi-signature with an arbitrary threshold
* Time-locked transactions
* Allowlists of approved callers
* Hybrid schemes combining multiple signing algorithms

> **Important**: Custom validation contracts must currently be written in **Yul** (the EVM intermediate representation language). The AA system contracts introduce new EVM opcodes that the standard Solidity compiler (`solc`) does not yet recognize. Any contract that uses these opcodes for account validation or paymaster logic must be written at the Yul level until compiler support is added. See [System Contracts](/account-abstraction/system-contracts) for details.

***

## Choosing a Scheme

| Priority                                | Recommended Scheme                          |
| --------------------------------------- | ------------------------------------------- |
| Maximum quantum resistance              | MLDSA or SLH-DSA                            |
| Diverse post-quantum assumptions        | SLH-DSA (hash-based, no lattice dependency) |
| Consumer UX, biometric login            | Passkey (P-256 / WebAuthn)                  |
| Organizational custody, social recovery | MPC (Guardian account)                      |
| Compact signatures (future)             | Falcon (pending)                            |
| Custom logic                            | Custom (Yul contract)                       |

For most users, **MLDSA** offers the best combination of post-quantum security, tooling support, and performance. **Passkey** is the best choice for consumer products targeting mainstream users who should not need to manage seed phrases.

## Further Reading

* [Paymaster](/account-abstraction/paymaster): How gas sponsorship works alongside validation
* [System Contracts](/account-abstraction/system-contracts): How validation schemes are implemented at the protocol level
* [ML-DSA44 Deep Dive](/pqc/mldsa44): Full specification for the MLDSA algorithm
* [PQC Overview](/pqc/overview): Background on post-quantum cryptography on Armchain


# Paymaster

A paymaster is a contract that pays the gas cost for another account's AA transaction. Paymasters make it possible for users to interact with Armchain without holding ARM, and allow dApp developers and service providers to offer sponsored transactions to their users.

Armchain ships a **Foundation Paymaster** as a system contract deployed at genesis. Any other entity can also deploy and operate their own paymaster.

## Foundation Paymaster

The Foundation Paymaster is a protocol-level contract funded with ARM tokens. Its primary role is to sponsor contract deployments for new users who do not yet have ARM in their accounts.

This solves the bootstrapping problem: a new user who wants to deploy a smart account or interact with an AA contract for the first time needs gas. The Foundation Paymaster covers that initial gas cost, letting users get started without acquiring ARM first.

### What the Foundation Paymaster Covers

* Smart account (AA wallet) deployments for new users
* Initial contract interactions where the account balance is zero or insufficient
* Deployments routed through the Factory Registry system contract

The Foundation Paymaster is not intended to sponsor arbitrary transactions indefinitely. It covers the onboarding moment: once an account is deployed and operational, users are expected to hold ARM for ongoing transaction fees.

### Staking and Economic Policy

To prevent abuse and DDoS attacks against the paymaster system, all paymasters, including the Foundation Paymaster, operate under a **staking-based economic policy** embedded in the AA mempool rules.

The core principle is that paymasters must put economic value at risk to participate. A paymaster that sponsors invalid or malicious transactions loses stake. This creates a direct cost for bad actors trying to exploit sponsored transactions to flood the network.

The policy is enforced at the mempool layer, not the contract layer, so it applies to all incoming AA transactions before they consume node resources:

```
┌──────────────────────────────────────────────────────┐
│                   AA Mempool Rules                    │
├──────────────────────────────────────────────────────┤
│  1. Paymaster must have a stake deposit > minimum     │
│  2. Paymaster deposit must cover the gas limit        │
│  3. Transactions from penalized paymasters rejected   │
│  4. Stake slashed for verified malicious behavior     │
└──────────────────────────────────────────────────────┘
```

This design mirrors economic incentive structures from established paymaster ecosystems (such as those on Ethereum), adapted to Armchain's native AA architecture and ARM staking model.

## Third-Party Paymasters

Any entity can operate a paymaster on Armchain. Common paymaster models from the Ethereum ecosystem apply:

| Paymaster Model            | Description                                                       |
| -------------------------- | ----------------------------------------------------------------- |
| **Free sponsorship**       | Covers all fees for users of a specific dApp                      |
| **Token paymaster**        | Users pay in an ERC-20 token; paymaster converts to ARM           |
| **Subscription paymaster** | Users pay off-chain (monthly plan); paymaster covers on-chain gas |
| **Allowlist paymaster**    | Sponsors only transactions for whitelisted accounts or contracts  |

Infrastructure providers, wallet teams, and dApp developers can deploy their own paymasters to give users a seamless experience. To operate a paymaster, an entity must:

1. Deploy a paymaster contract conforming to the AA system contract interface
2. Register the paymaster in the AA mempool (by staking ARM as a deposit)
3. Maintain a sufficient ARM deposit to cover sponsored transactions

> **Note**: Paymaster contracts that interact with AA-specific opcodes must be written in Yul. See [System Contracts](/account-abstraction/system-contracts) for details on the compiler constraint.

## How Paymaster Gas Accounting Works

When an AA transaction specifies a paymaster:

1. **Pre-validation**: The AA mempool checks that the paymaster has a deposit covering the maximum possible gas cost (`gasLimit × maxFeePerGas`).
2. **Paymaster validation call**: Before execution, the paymaster contract's validation function is called. It confirms it accepts this specific transaction.
3. **Execution**: The user's AA transaction executes normally.
4. **Post-execution**: Actual gas used is deducted from the paymaster's deposit. Unused gas is returned.
5. **Deposit replenishment**: Paymaster operators must top up their deposits periodically to keep the paymaster active.

If a paymaster's deposit runs out mid-operation, transactions specifying that paymaster are rejected at the mempool level until the deposit is replenished.

## Deposit vs. Stake

The paymaster system distinguishes between two types of ARM holdings:

| Type        | Purpose                                     | Lock-up                    |
| ----------- | ------------------------------------------- | -------------------------- |
| **Deposit** | Covers gas costs for sponsored transactions | Withdrawable after a delay |
| **Stake**   | DDoS deterrence, slashable on bad behavior  | Longer lock-up, slashable  |

All paymasters must maintain both a deposit (sufficient to cover expected gas) and a stake (as a security bond). The stake is at risk if the paymaster is found to behave maliciously, such as approving transactions it later fails to cover, or participating in transaction spam.

## Further Reading

* [Overview](/account-abstraction/overview): How AA transactions flow through the network
* [Validation Schemes](/account-abstraction/validation-schemes): Account-side validation that works alongside paymasters
* [System Contracts](/account-abstraction/system-contracts): How the Foundation Paymaster is implemented as a genesis contract


# System Contracts

Armchain's AA functionality is built on a set of **system contracts** deployed at genesis. These contracts are embedded in the initial chain state and are available from block zero, similar to how the SFC (Special Fee Contract) governs validator staking and epoch management.

## Why System Contracts

Native AA requires new EVM-level semantics: the node must be able to call into a validation contract before executing a transaction, handle paymaster logic, and route transactions through the AA pipeline. These behaviors are encoded in the client (go-opera / armchain-client) and backed by on-chain contracts that hold the authoritative logic.

Deploying validation logic as protocol-level contracts (rather than user-deployed contracts) gives the following properties:

* **Trust**: The network trusts these contracts implicitly, similar to how go-ethereum trusts precompile addresses
* **Availability**: The contracts exist from genesis, no deployment transaction needed
* **Upgradeability**: Changes can be made through governance rather than ad-hoc deployment

## Why Yul

The AA system contracts are written in **Yul**, the EVM intermediate representation language (the same IR that `solc` compiles Solidity down to before generating bytecode).

Armchain's native AA introduces new EVM opcodes that are not part of the standard Ethereum opcode set. These opcodes are required for the AA execution model (for example, opcodes that allow the protocol to invoke account validation and paymaster logic with the appropriate permissions and gas accounting).

The Solidity compiler (`solc`) does not recognize these opcodes. Any contract that uses them must bypass the Solidity frontend and write directly in Yul.

```
┌──────────────────────────────────────────────────┐
│  Standard Solidity contracts                      │
│  solc → Yul IR → EVM bytecode                    │
│  Works fine for regular contracts                 │
├──────────────────────────────────────────────────┤
│  AA system contracts                              │
│  Yul (hand-written) → EVM bytecode               │
│  Required because new opcodes are unrecognized    │
│  by solc's opcode validation stage                │
└──────────────────────────────────────────────────┘
```

### Implications for Developers

If you are writing a **custom account validation contract** or a **custom paymaster**, and your contract uses the AA-specific opcodes, you must write that contract in Yul. There is no way to express these opcodes through the standard Solidity compiler today.

Contracts that do **not** interact with AA-specific opcodes (for example, a paymaster that only checks a signature but delegates all AA calls to a trusted helper) can still be written in Solidity with appropriate care.

> **Looking ahead**: The Armchain team is evaluating a fork of the Solidity compiler to add recognition of the new opcodes. Until that is available, Yul is the required path for any contract that touches AA-specific execution semantics. This is a known friction point and is being actively addressed.

## Deployed Contracts

The following contracts are deployed at genesis as part of the AA system:

### Factory Registry

The Factory Registry tracks approved account factory contracts and their trust levels. When a user deploys a new smart account (for example, a Guardian account or a Passkey account), the factory must be registered here for the deployment to be recognized by the AA mempool.

The trust level system allows the protocol to distinguish between system-level factories (highest trust, deployed at genesis) and community-deployed factories (lower trust, subject to additional checks).

### Validation Scheme Contracts

Each supported validation scheme has a corresponding system contract that implements the on-chain verification logic:

| Contract              | Scheme             | Description                                            |
| --------------------- | ------------------ | ------------------------------------------------------ |
| MLDSA Wallet          | ML-DSA44           | Verifies ML-DSA44 signatures for AA accounts           |
| Passkey Wallet        | P-256 / WebAuthn   | Verifies P-256 signatures from WebAuthn authenticators |
| SLH-DSA Wallet        | SLH-DSA / SPHINCS+ | Verifies SLH-DSA hash-based signatures                 |
| Guardian (MPC) Wallet | ECDSA via MPC      | Verifies ECDSA signatures for MPC-managed accounts     |
| Hybrid Wallet         | Combined           | Supports combining multiple validation schemes         |

Each contract exposes a standard validation interface that the AA pipeline calls before allowing a transaction to execute.

### Foundation Paymaster

The Foundation Paymaster system contract is pre-funded at genesis and handles gas sponsorship for new user onboarding. See [Paymaster](/account-abstraction/paymaster) for details on how it operates.

## Client Integration

The system contracts are integrated into both the `geth` base client and the `opera` (armchain-client) consensus layer:

* **geth layer**: State transition hooks call into the AA system contracts at the appropriate execution steps
* **opera layer**: The AA mempool, gossip, and event inclusion logic is implemented here
* **Tests**: Unit tests, integration tests, and end-to-end tests are in place covering the full AA transaction pipeline, from submission through validation, paymaster logic, and final execution

## Writing Custom Extensions

Developers who want to add a new validation scheme to Armchain's AA framework should:

1. **Implement the validation interface in Yul**: The contract must expose the standard AA validation function with the correct ABI and use AA-specific opcodes for execution hooks.
2. **Register with the Factory Registry**: A factory contract must be deployed and registered before users can create accounts using the new scheme.
3. **Follow the staking requirements**: Custom paymasters associated with the scheme must stake ARM and maintain a deposit.
4. **Test against a local fakenet**: Spin up a local Armchain fakenet and run end-to-end tests before deploying to devnet. See [Fakenet](/node-operators/fakenet).

The modular design means any validation scheme expressible in EVM bytecode can be added. The protocol does not constrain what the validation logic does, only that it conforms to the interface the AA pipeline calls.

## Further Reading

* [Overview](/account-abstraction/overview): High-level AA architecture and transaction flow
* [Validation Schemes](/account-abstraction/validation-schemes): Detailed descriptions of each supported scheme
* [Paymaster](/account-abstraction/paymaster): Gas sponsorship and staking policy
* [EVM Integration](/architecture/evm): EVM opcodes, precompiles, and Armchain extensions
* [Fakenet](/node-operators/fakenet): Local development network for testing


# Node Operators

Run your own Armchain node. Whether you want a local dev environment, a full archive node, or a validator, these guides walk you through setup and configuration.

| Document                                       | Description                                                       |
| ---------------------------------------------- | ----------------------------------------------------------------- |
| [Node Types](/node-operators/node-types)       | Understand the different node configurations and when to use each |
| [Configuration](/node-operators/configuration) | Complete reference for all node flags and settings                |
| [Fakenet](/node-operators/fakenet)             | Spin up a local development network in seconds                    |


# Node Types

Armchain supports several node configurations that you can mix and match depending on your needs, whether you're running a local dev node, a public RPC endpoint, or a validator.

## Configuration Dimensions

```
┌─────────────────────────────────────────────────────────────────────────┐
│  Node Configuration Dimensions                                          │
│                                                                         │
│  ┌─────────────────────────┐  ┌─────────────────────────┐              │
│  │  Garbage Collection     │  │  Synchronization        │              │
│  │  Mode (--gcmode)        │  │  Mode (--syncmode)      │              │
│  ├─────────────────────────┤  ├─────────────────────────┤              │
│  │  • archive (default)    │  │  • full (default)       │              │
│  │  • full                 │  │  • snap                 │              │
│  │  • light                │  │                         │              │
│  └─────────────────────────┘  └─────────────────────────┘              │
│                                                                         │
│  ┌─────────────────────────┐  ┌─────────────────────────┐              │
│  │  Validator Role         │  │  Database Preset        │              │
│  │                         │  │  (--db.preset)          │              │
│  ├─────────────────────────┤  ├─────────────────────────┤              │
│  │  • Validator            │  │  • pbl-1 (Pebble)      │              │
│  │    (--validator.id)     │  │  • ldb-1 (LevelDB)     │              │
│  │  • Non-validator        │  │  • legacy-ldb           │              │
│  │    (readonly)           │  │  • legacy-pbl           │              │
│  └─────────────────────────┘  └─────────────────────────┘              │
└─────────────────────────────────────────────────────────────────────────┘
```

> **Key difference from Geth**: Unlike go-ethereum where `full` is the default GC mode, Armchain defaults to `archive` mode.

***

## Garbage Collection Modes

The `--gcmode` flag controls how the node handles historical EVM state data. This is the primary differentiator for storage requirements and historical query capabilities.

### Archive Mode

**Flag**: `--gcmode archive` (**DEFAULT**)

Archive mode preserves all historical state by disabling trie write caching and garbage collection entirely.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Archive Node - State Trie DB                                        │
│                                                                      │
│   Block 1    Block 2    Block 3    ...    Block N                    │
│  ┌───────┐  ┌───────┐  ┌───────┐        ┌───────┐                  │
│  │ State │  │ State │  │ State │        │ State │                  │
│  │ Root  │  │ Root  │  │ Root  │        │ Root  │                  │
│  │   1   │  │   2   │  │   3   │        │   N   │                  │
│  └───┬───┘  └───┬───┘  └───┬───┘        └───┬───┘                  │
│      ▼          ▼          ▼              ▼                          │
│  ALL STATE   ALL STATE   ALL STATE   ALL STATE                      │
│    KEPT       KEPT        KEPT        KEPT                          │
│                                                                      │
│  ✓ Query any historical block state                                  │
│  ✓ eth_call at any block number                                      │
│  ✓ Debug and trace any historical transaction                        │
└─────────────────────────────────────────────────────────────────────┘
```

**Configuration details**:

| Setting             | Value                                       |
| ------------------- | ------------------------------------------- |
| `TrieDirtyDisabled` | `true` - disables trie write caching and GC |
| `GreedyGC`          | `false` - no greedy garbage collection      |

When `TrieDirtyDisabled` is set, every state commit is flushed directly to disk; no state trie is ever garbage-collected.

**Use cases**:

* Block explorers requiring historical state queries
* DeFi analytics platforms
* Debugging and forensic analysis
* RPC providers serving `eth_call` at arbitrary blocks

**Storage characteristics**:

* **Highest storage requirement**: all state preserved
* **Continuous growth**: no data is ever pruned

### Full Mode

**Flag**: `--gcmode full`

Full mode implements greedy garbage collection that prunes old state while keeping recent state accessible.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Full Node - State Trie DB                                           │
│                                                                      │
│  Block N-16  Block N-15  ...  Block N-1  Block N                    │
│  ┌───────┐   ┌───────┐      ┌───────┐   ┌───────┐                  │
│  │ State │   │ State │      │ State │   │ State │                  │
│  │ Root  │   │ Root  │      │ Root  │   │ Root  │                  │
│  └───┬───┘   └───┬───┘      └───┬───┘   └───┬───┘                  │
│      ▼           ▼              ▼           ▼                        │
│   GC QUEUE     KEPT           KEPT        KEPT                      │
│                                                                      │
│  TriesInMemory = 16 (recent tries kept in memory/disk)              │
│                                                                      │
│  ✓ Query recent block state                                          │
│  ✗ Limited historical state access                                   │
│  ✓ Reduced storage footprint                                         │
└─────────────────────────────────────────────────────────────────────┘
```

**Configuration details**:

| Setting             | Value                               |
| ------------------- | ----------------------------------- |
| `TrieDirtyDisabled` | `false` - enable dirty trie caching |
| `GreedyGC`          | `true` - enable greedy GC           |
| `TriesInMemory`     | `16` - keep 16 most recent tries    |

**Garbage collection process**:

The node keeps the 16 most recent state tries in memory. When the current block exceeds `TriesInMemory`, matured singleton nodes are flushed to disk, and older tries are dereferenced (marked for garbage collection). The GC queue processes roots in block-number order, removing all entries below the chosen retention threshold.

**Use cases**:

* Standard network participants
* dApp backends needing recent state
* Staking/validator operations
* General RPC endpoints

**Storage characteristics**:

* **Moderate storage**: old state is pruned
* **Bounded growth**: only recent state retained

### Light Mode

**Flag**: `--gcmode light`

Light mode provides minimal state retention for resource-constrained environments.

> **Important**: Unlike Geth's light client, Armchain's light mode does **not** implement a light client protocol (Ethereum LES). The node still downloads all blocks and events, validates all transactions, and participates in P2P gossip. The "light" designation only refers to more aggressive state pruning behavior, not protocol differences.

**Configuration details**:

| Setting             | Value                                       |
| ------------------- | ------------------------------------------- |
| `TrieDirtyDisabled` | `false`                                     |
| `GreedyGC`          | `false` (but with reduced `TrieDirtyLimit`) |

**Use cases**:

* Development and testing
* Resource-constrained environments
* Non-critical read operations

**Storage characteristics**:

* **Most aggressive pruning**
* **Minimal historical state**
* **Smallest storage footprint**

***

## Synchronization Modes

The `--syncmode` flag controls how the node initially synchronizes with the network.

### Full Sync

**Flag**: `--syncmode full` (**DEFAULT**)

Full synchronization downloads all events and replays all transactions from genesis.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Full Sync Process                                                   │
│                                                                      │
│  Genesis ──▶ Event 1 ──▶ Event 2 ──▶ ... ──▶ Latest Event          │
│     │           │           │                    │                    │
│     ▼           ▼           ▼                    ▼                    │
│  Execute     Execute     Execute              Execute                │
│    Txs         Txs         Txs                  Txs                  │
│     │           │           │                    │                    │
│     ▼           ▼           ▼                    ▼                    │
│  State 0 ──▶ State 1 ──▶ State 2 ──▶ ... ──▶ Current State         │
│                                                                      │
│  • Downloads all DAG events from genesis                             │
│  • Executes all transactions sequentially                            │
│  • Builds complete state history (if archive mode)                   │
│  • Longest sync time but most complete                               │
└─────────────────────────────────────────────────────────────────────┘
```

**Process**:

1. Download events from the DAG
2. Process each event through Lachesis consensus
3. Execute transactions and build state
4. Continue until caught up with the network

The sync state machine tracks progress through stages: snapshot sync (if applicable), EVM snapshot generation, and events processing. Events are only accepted once the node reaches the `ssEvents` stage.

### Snap Sync

**Flag**: `--syncmode snap`

Snapshot synchronization downloads a recent state snapshot and only replays recent history.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Snap Sync Process                                                   │
│                                                                      │
│  PHASE 1: Snapshot Download                                          │
│  ──────────────────────────                                          │
│  Remote peer's State Root @ Epoch N ───download───▶ Local copy      │
│  (accounts + storage)                                                │
│                                                                      │
│  PHASE 2: EVM Snapshot Generation                                    │
│  ────────────────────────────────                                    │
│  Generate local EVM snapshot from downloaded state                   │
│                                                                      │
│  PHASE 3: Catch-up Sync                                              │
│  ──────────────────────                                              │
│  Epoch N ──▶ Events ──▶ ... ──▶ Current                             │
│  (Switches to full sync for recent history)                          │
└─────────────────────────────────────────────────────────────────────┘
```

**Snap sync state machine**:

The handler tracks sync stages (`ssUnknown` → `ssSnaps` → `ssEvmSnapGen` → `ssEvents`) with the following rules:

* Full sync is never allowed while EVM snapshot generation is ongoing
* Snap sync is only possible if explicitly allowed (`AllowSnapsync`) and the node hasn't already transitioned to full sync
* Snap sync is needed when the node doesn't have a state DB for the finalized state root **or** when the epoch start time exceeds 14 days ago
* Once full sync (events stage) is reached, the node never reverts to snap sync

**Benefits**:

* **Faster initial sync**: skip historical transaction execution
* **Reduced bandwidth**: only download state, not full history
* **Quicker time-to-operational**: node becomes useful faster

**Limitations**:

* Cannot query state before the snapshot point
* Requires snap-capable peers
* May need fallback to full sync if snap is unavailable

***

## Validator vs Non-Validator Nodes

### Non-Validator (Readonly) Node

**Launch command**:

```bash
armnode --genesis file.g
```

Non-validator nodes synchronize with the network and validate all data but do not participate in block production.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Non-Validator Node                                                  │
│                                                                      │
│  Network ◄────────────────────────────────▶ Network                 │
│  (Receive Events)                    (Gossip Transactions)           │
│       │                                    ▲                         │
│       ▼                                    │                         │
│  ┌─────────────┐                 ┌─────────────┐                    │
│  │  Validate   │                 │ Transaction │                    │
│  │  Events     │                 │    Pool     │                    │
│  └──────┬──────┘                 └─────────────┘                    │
│         ▼                                                            │
│  ┌─────────────┐                                                    │
│  │  Execute    │                                                    │
│  │    Txs      │                                                    │
│  └──────┬──────┘                                                    │
│         ▼                                                            │
│  ┌─────────────┐                 ┌─────────────┐                    │
│  │ Update      │────────────────▶│  Serve RPC  │                    │
│  │ State DB    │                 │             │                    │
│  └─────────────┘                 └─────────────┘                    │
│                                                                      │
│  ✗ NO event creation                                                 │
│  ✗ NO block production                                               │
│  ✗ NO consensus participation                                        │
│  ✓ Full validation                                                   │
│  ✓ RPC services                                                      │
└─────────────────────────────────────────────────────────────────────┘
```

**Use cases**:

* RPC endpoint providers
* Block explorers
* dApp backends
* Network observers

### Validator Node

**Launch command**:

```bash
armnode --validator.id YOUR_ID --validator.pubkey 0xYOUR_PUBKEY
```

Validator nodes actively participate in consensus by creating events and producing blocks.

```
┌─────────────────────────────────────────────────────────────────────┐
│  Validator Node                                                      │
│                                                                      │
│  Network ◄────────────────────────────────▶ Network                 │
│  (Receive Events)                    (Broadcast Events)              │
│       │                                    ▲                         │
│       ▼                                    │                         │
│  ┌─────────────┐                 ┌─────────────┐                    │
│  │  Validate   │                 │   EMITTER   │◄── KEY UNLOCKED    │
│  │  Events     │                 │ Create Evts │                    │
│  └──────┬──────┘                 └──────┬──────┘                    │
│         ▼                               ▲                            │
│  ┌─────────────┐◄──────────────▶┌─────────────┐                    │
│  │  Lachesis   │                │ Transaction │                    │
│  │  Consensus  │                │    Pool     │                    │
│  └──────┬──────┘                └─────────────┘                    │
│         ▼                                                            │
│  ┌─────────────┐                                                    │
│  │  Execute    │                                                    │
│  │    Txs      │                                                    │
│  └──────┬──────┘                                                    │
│         ▼                                                            │
│  ┌─────────────┐                                                    │
│  │  Produce    │                                                    │
│  │  Blocks     │                                                    │
│  └─────────────┘                                                    │
│                                                                      │
│  ✓ Event creation (Emitter)                                          │
│  ✓ Block production                                                  │
│  ✓ Consensus participation                                           │
│  ✓ Earns rewards                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

**Required configuration**:

| Flag                   | Description                                  |
| ---------------------- | -------------------------------------------- |
| `--validator.id`       | ID of a validator to create events from      |
| `--validator.pubkey`   | Public key (ML-DSA44 hex) of the validator   |
| `--validator.password` | Password to unlock the validator private key |

**Validator key management**:

```bash
# Create new validator key
armnode validator new

# Convert existing account key to validator key
armnode validator convert <account address> <validator pubkey>
```

> **Important**: Validator functionality is independent of GC mode. A validator can run with any GC mode (`archive`, `full`, or `light`), though `full` or `archive` is recommended for reliability.

***

## Database Configuration

### Database Presets

The `--db.preset` flag controls the database layout and engine selection.

| Preset       | Engine  | Description                      |
| ------------ | ------- | -------------------------------- |
| `pbl-1`      | Pebble  | Modern layout, optimized storage |
| `ldb-1`      | LevelDB | Modern layout (**DEFAULT**)      |
| `legacy-ldb` | LevelDB | Legacy layout                    |
| `legacy-pbl` | Pebble  | Legacy layout with Pebble        |

### Storage Layout: ldb-1 (LevelDB)

```
datadir/
├── main/                     # Main database
│   ├── lachesis (C table)    # Consensus data
│   ├── gossip                # Gossip protocol data
│   ├── evm                   # EVM state
│   └── evm-logs (L table)    # Log indices
│
└── epoch-N/                  # Per-epoch databases
    ├── gossip (G table)      # Epoch gossip data
    └── lachesis (L table)    # Epoch consensus data
```

**Runtime cache distribution**:

| Database  | Cache Allocation           |
| --------- | -------------------------- |
| `main`    | 900 MiB (90% of allocated) |
| `epoch-N` | 100 MiB (10% of allocated) |

### Storage Layout: pbl-1 (Pebble)

```
datadir/
├── main/                     # Main consolidated DB
│   ├── lachesis (C table)
│   ├── gossip
│   └── evm
│
├── events/                   # Events database
│   └── gossip/e
│
├── evm-data/                 # EVM state (Pebble DRC)
│   └── evm/M
│
├── evm-logs/                 # Log indices
│
└── epoch-N/                  # Per-epoch (LevelDB)
    ├── gossip (G table)
    └── lachesis (L table)
```

**Runtime cache distribution**:

| Database   | Cache Allocation |
| ---------- | ---------------- |
| `evm-data` | 460 MiB          |
| `main`     | 320 MiB          |
| `evm-logs` | 260 MiB          |
| `events`   | 240 MiB          |
| `epoch-N`  | 100 MiB          |

***

## Resource Requirements

> **Note**: The storage estimates below are reference values from the upstream go-opera network and have **not** been updated for Armchain's \~57× larger ML-DSA44 signature sizes. Actual Armchain storage requirements will be significantly higher. Memory and bandwidth figures are indicative.

### Memory

| Setting             | Value                     |
| ------------------- | ------------------------- |
| Default cache size  | 3,600 MB (`--cache` flag) |
| Minimum cache size  | 3,600 MB                  |
| Constant cache size | 600 MB (always allocated) |

| Component           | Archive         | Full             | Light            |
| ------------------- | --------------- | ---------------- | ---------------- |
| Trie Dirty Limit    | Disabled        | 256 MiB (scaled) | 256 MiB (scaled) |
| EVM Database Cache  | 32 MiB (scaled) | 32 MiB (scaled)  | 32 MiB (scaled)  |
| Snapshot Cache      | 32 MiB (scaled) | 32 MiB (scaled)  | 32 MiB (scaled)  |
| **Recommended RAM** | **8+ GB**       | **4+ GB**        | **2+ GB**        |

### Storage (Reference Only)

> These figures are from the upstream network (Fantom Opera). Armchain's larger signature sizes will substantially increase storage requirements, particularly for events/DAG data and transaction receipts. Exact figures are pending evaluation.

| GC Mode | Reference Estimate | Growth Pattern               |
| ------- | ------------------ | ---------------------------- |
| Archive | 500+ GB            | Continuous (no pruning)      |
| Full    | 200–300 GB         | Bounded (pruned state)       |
| Light   | 150–200 GB         | Minimal (aggressive pruning) |

**Storage components** (reference breakdown):

| Component         | Approximate Size                    |
| ----------------- | ----------------------------------- |
| Events / DAG data | \~100 GB (same for all modes)       |
| EVM state         | Varies by GC mode (main difference) |
| Logs index        | \~50 GB                             |
| Receipts / Txs    | \~30 GB                             |
| Epoch data        | \~20 GB                             |

> **SSD is strongly recommended** for all modes.

### Bandwidth

**Initial sync**:

| Sync Mode | Data Size   | Typical Duration |
| --------- | ----------- | ---------------- |
| Full      | \~200+ GB   | Days to weeks    |
| Snap      | \~50–100 GB | Hours to days    |

**Steady-state**:

| Direction              | Bandwidth                     |
| ---------------------- | ----------------------------- |
| Inbound                | 1–5 Mbps (event/tx gossip)    |
| Outbound               | 1–5 Mbps (serving peers)      |
| Validator (additional) | \~1 Mbps (event broadcasting) |

**Port requirements**:

| Port  | Protocol | Usage                                  |
| ----- | -------- | -------------------------------------- |
| 5050  | TCP/UDP  | P2P (configurable via `--port`)        |
| 18545 | TCP      | HTTP JSON-RPC (if `--http` enabled)    |
| 18546 | TCP      | WebSocket JSON-RPC (if `--ws` enabled) |

***

## Node Selection Guide

### Decision Tree

```
START
  │
  ▼
┌─────────────────────────────────────────┐
│  Need historical state queries?          │
│  (eth_call at old blocks, tracing)       │
└───────────┬─────────────────┬───────────┘
          YES               NO
            │                 │
            ▼                 ▼
      ┌──────────┐   ┌──────────────────────────────┐
      │ ARCHIVE  │   │  Need to validate network?    │
      └──────────┘   │  (not just read)              │
                      └───────────┬──────────┬───────┘
                                YES        NO
                                  │          │
                                  ▼          ▼
                            ┌──────────┐ ┌──────────┐
                            │   FULL   │ │  LIGHT   │
                            └──────────┘ └──────────┘

┌─────────────────────────────────────────┐
│  Fast initial sync needed?               │
└───────────┬─────────────────┬───────────┘
          YES               NO
            │                 │
            ▼                 ▼
      ┌──────────┐     ┌──────────┐
      │ --sync   │     │ --sync   │
      │   snap   │     │   full   │
      └──────────┘     └──────────┘

┌─────────────────────────────────────────┐
│  Participating in consensus?             │
└───────────┬─────────────────┬───────────┘
          YES               NO
            │                 │
            ▼                 ▼
      ┌──────────────┐ ┌──────────────┐
      │ --validator  │ │  (no flags)  │
      │ .id + .pubkey│ │   readonly   │
      └──────────────┘ └──────────────┘
```

### Recommended Configurations

| Use Case        | GC Mode   | Sync Mode | Validator | Command Example                                                 |
| --------------- | --------- | --------- | --------- | --------------------------------------------------------------- |
| Block Explorer  | `archive` | `full`    | No        | `armnode --gcmode archive --genesis file.g`                     |
| RPC Provider    | `archive` | `snap`    | No        | `armnode --gcmode archive --syncmode snap --genesis file.g`     |
| Validator       | `full`    | `full`    | Yes       | `armnode --gcmode full --validator.id ID --validator.pubkey PK` |
| dApp Backend    | `full`    | `snap`    | No        | `armnode --gcmode full --syncmode snap --genesis file.g`        |
| Development     | `light`   | `snap`    | No        | `armnode --gcmode light --syncmode snap --genesis file.g`       |
| Fakenet Testing | `archive` | `full`    | Yes       | `armnode --fakenet 1/1`                                         |

***

## Summary

1. **Garbage Collection Modes** control state retention:
   * `archive` (**default**): All historical state preserved
   * `full`: Recent state with pruning (TriesInMemory = 16)
   * `light`: Minimal state retention
2. **Synchronization Modes** control initial sync behavior:
   * `full` (**default**): Complete history replay from genesis
   * `snap`: State snapshot-based fast sync
3. **Validator vs Non-Validator** determines consensus participation:
   * Validators require key configuration (`--validator.id`, `--validator.pubkey`)
   * Non-validators are read-only (synchronize and serve RPC)
4. **Key Differences from Geth**:
   * Archive mode is the default (not `full`)
   * Light mode is **not** a light client protocol; the node still downloads and validates everything
   * Validator functionality is separate from GC mode

## Further Reading

* [Configuration Reference](/node-operators/configuration): Full list of CLI flags
* [Fakenet](/node-operators/fakenet): Local development network
* [Consensus](/architecture/consensus): How Lachesis aBFT works
* [Transaction Lifecycle](/architecture/transaction-lifecycle): End-to-end transaction flow


# Configuration

Complete reference for all Armchain node (`armnode`) command-line flags. You can also export a config file and customize it. See [Configuration File (TOML)](#configuration-file-toml) at the bottom.

## General

| Flag          | Default      | Description                                                                                                                                                                                                                            |
| ------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--datadir`   | `~/.armnode` | Data directory for databases and keystore                                                                                                                                                                                              |
| `--genesis`   | -            | Path to genesis file (required for joining a network)                                                                                                                                                                                  |
| `--config`    | -            | Path to TOML configuration file                                                                                                                                                                                                        |
| `--fakenet`   | -            | Run as fakenet validator (`n/N` format)                                                                                                                                                                                                |
| `--cache`     | `3600`       | Megabytes of memory allocated for internal caching (minimum 3600)                                                                                                                                                                      |
| `--syncmode`  | `full`       | Blockchain sync mode (`full`, `snap`)                                                                                                                                                                                                  |
| `--gcmode`    | `archive`    | Garbage collection mode (`archive`, `full`, `light`). **Note**: Unlike Geth, Armchain defaults to `archive`. See [Node Types](/node-operators/node-types).                                                                             |
| `--db.preset` | `ldb-1`      | Database layout preset (`ldb-1`, `pbl-1`, `legacy-ldb`, `legacy-pbl`). See [Node Types - Database Configuration](/node-operators/node-types#database-configuration).                                                                   |
| `--nousb`     | `false`      | Disable USB hardware wallet monitoring. **Note**: Hardware wallets (USB/Ledger/Trezor) have not been upgraded to support ML-DSA44 PQC keys. This flag is retained for compatibility but HW wallet signing is not currently functional. |
| `--verbosity` | `3`          | Log verbosity (0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail)                                                                                                                                                                   |

## Network (P2P)

| Flag            | Default | Description                                          |
| --------------- | ------- | ---------------------------------------------------- |
| `--port`        | `5050`  | P2P listening port                                   |
| `--maxpeers`    | `50`    | Maximum number of network peers                      |
| `--bootnodes`   | -       | Comma-separated enode URLs for bootstrapping         |
| `--nat`         | `any`   | NAT port mapping (`any`, `none`, `upnp`, `extip:IP`) |
| `--nodiscover`  | `false` | Disable peer discovery                               |
| `--netrestrict` | -       | Restrict network to CIDR ranges                      |

## HTTP JSON-RPC

| Flag                | Default        | Description                                   |
| ------------------- | -------------- | --------------------------------------------- |
| `--http`            | `false`        | Enable HTTP RPC server                        |
| `--http.addr`       | `localhost`    | HTTP RPC listening address                    |
| `--http.port`       | `18545`        | HTTP RPC port                                 |
| `--http.api`        | `eth,net,web3` | Comma-separated API namespaces to expose      |
| `--http.corsdomain` | `""`           | Comma-separated list of allowed CORS domains  |
| `--http.vhosts`     | `localhost`    | Comma-separated list of allowed virtual hosts |

## WebSocket

| Flag           | Default        | Description                              |
| -------------- | -------------- | ---------------------------------------- |
| `--ws`         | `false`        | Enable WebSocket RPC server              |
| `--ws.addr`    | `localhost`    | WebSocket listening address              |
| `--ws.port`    | `18546`        | WebSocket port                           |
| `--ws.api`     | `eth,net,web3` | Comma-separated API namespaces to expose |
| `--ws.origins` | -              | Allowed WebSocket origins                |

## Validator

| Flag                   | Default | Description                                    |
| ---------------------- | ------- | ---------------------------------------------- |
| `--validator.id`       | -       | Validator ID (required for validators)         |
| `--validator.pubkey`   | -       | Validator public key (ML-DSA44 hex)            |
| `--validator.password` | -       | Path to file containing validator key password |

## RPC Limits

| Flag                          | Default    | Description                                  |
| ----------------------------- | ---------- | -------------------------------------------- |
| `--rpc.gascap`                | `50000000` | Gas cap for `eth_call` and `eth_estimateGas` |
| `--rpc.txfeecap`              | `1`        | Transaction fee cap in ARM                   |
| `--rpc.timeout`               | `5s`       | HTTP RPC timeout                             |
| `--rpc.allow-unprotected-txs` | `false`    | Allow non-EIP-155 transactions               |

## Account Management

| Flag                      | Default | Description                                 |
| ------------------------- | ------- | ------------------------------------------- |
| `--allow-insecure-unlock` | `false` | Allow account unlock over HTTP (dangerous!) |
| `--unlock`                | -       | Comma-separated account addresses to unlock |
| `--password`              | -       | Password file for account unlock            |

## Performance Tuning

| Flag               | Default | Description                                                        |
| ------------------ | ------- | ------------------------------------------------------------------ |
| `--cache`          | `3600`  | Memory cache in MB (minimum 3600; increase for better performance) |
| `--cache.database` | -       | Percentage of cache for database                                   |
| `--cache.gc`       | -       | Percentage of cache for trie pruning                               |
| `--maxpeers`       | `50`    | Peer count (more peers = more bandwidth but better connectivity)   |

## Tracing and Debugging

| Flag           | Default | Description              |
| -------------- | ------- | ------------------------ |
| `--tracing`    | `false` | Enable Jaeger tracing    |
| `--verbosity`  | `3`     | Log verbosity level      |
| `--pprof`      | `false` | Enable pprof HTTP server |
| `--pprof.port` | `6060`  | pprof port               |

## API Namespaces

Available namespaces for `--http.api` and `--ws.api`:

| Namespace  | Description                                               |
| ---------- | --------------------------------------------------------- |
| `eth`      | Standard Ethereum API (transactions, blocks, state, logs) |
| `net`      | Network information (version, peer count, listening)      |
| `web3`     | Web3 utilities (client version, SHA3)                     |
| `personal` | Account management (create, unlock, sign)                 |
| `txpool`   | Transaction pool inspection (pending, queued)             |
| `debug`    | Debugging and tracing (trace transaction, stack traces)   |
| `admin`    | Node administration (peers, node info, datadir)           |
| `arm`      | Armchain-specific methods                                 |
| `dag`      | DAG-specific queries (events, epochs)                     |

## Subcommands

```bash
# Account management
armnode account new           # Create a new account
armnode account list          # List all accounts
armnode account import <key>  # Import a private key
armnode account update <addr> # Update account password

# Validator management
armnode validator new         # Create a new ML-DSA44 validator key
armnode validator convert     # Convert account key to validator key

# Console
armnode console               # Start interactive JavaScript console
armnode attach                # Attach to a running node's IPC
armnode js <file>            # Execute a JavaScript file

# Configuration
armnode dumpconfig            # Dump current configuration
armnode checkconfig           # Validate configuration

# Chain data
armnode import <file>         # Import chain data
armnode export <file>         # Export chain data

# Database
armnode db stats              # Database statistics
armnode db compact            # Compact database

# Snapshot
armnode snapshot prune-state  # Prune state data

# Info
armnode version               # Print version information
armnode license               # Display license
```

## Configuration File (TOML)

Export a configuration file and customize it:

```bash
./build/armnode dumpconfig > config.toml
```

Example TOML structure:

```toml
[Node]
DataDir = "/data/armchain"

[Node.P2P]
MaxPeers = 50
ListenAddr = ":5050"
BootstrapNodes = ["enode://...@1.2.3.4:5050"]

[Node.HTTPHost]
Addr = "localhost"
Port = 18545
API = "eth,net,web3,arm"
Cors = "*"

[Emitter]
# Validator configuration
ValidatorID = 1
```

Then start with:

```bash
./build/armnode --config config.toml
```

## Environment Variables

| Variable  | Description                               |
| --------- | ----------------------------------------- |
| `GOPROXY` | Go module proxy (used during build)       |
| `HOME`    | Home directory (default datadir location) |

## Further Reading

* [Node Types and Configurations](/node-operators/node-types): GC modes, sync modes, database presets, and resource requirements
* [Fakenet](/node-operators/fakenet): Local development network options


# Fakenet

Fakenet is Armchain's built-in local development network. Think of it as Armchain's equivalent of Hardhat Network or Ganache. It runs a private chain with auto-generated validators, pre-funded accounts, and fast block times, all with a single command.

## Overview

Fakenet provides:

* **Instant setup**: No genesis file, single command to launch
* **Deterministic keys**: Auto-generated validator keys for reproducibility
* **Fast parameters**: 3-second empty block skip, 10-minute epochs
* **Full feature set**: All consensus, EVM, and PQC features active
* **Pre-funded accounts**: Validators start with 1 billion ARM each

## Quick Start

### Single Validator (PoA-like)

Single validator acting as a standalone block producer:

```bash
# Build the client
cd armchain-client
make armnode

# Run single-validator fakenet
./build/armnode --fakenet 1/1 \
  --http \
  --http.addr 0.0.0.0 \
  --http.port 4000 \
  --http.api eth,web3,net,personal,txpool,arm,dag,debug,admin \
  --http.corsdomain "*" \
  --ws \
  --ws.addr 0.0.0.0 \
  --ws.port 4500 \
  --ws.api eth,web3,net,arm \
  --allow-insecure-unlock
```

Your local RPC is now available at `http://localhost:4000`.

### Multi-Validator Network

More realistic test setup with multiple validators:

```bash
# Terminal 1 - Validator 1 of 3
./build/armnode --fakenet 1/3 --port 3001 --http --http.port 4001 --http.api eth,web3,net,personal,admin,arm,dag

# Terminal 2 - Validator 2 of 3
./build/armnode --fakenet 2/3 --port 3002 --http --http.port 4002 --http.api eth,web3,net,personal,admin,arm,dag

# Terminal 3 - Validator 3 of 3
./build/armnode --fakenet 3/3 --port 3003 --http --http.port 4003 --http.api eth,web3,net,personal,admin,arm,dag
```

Then connect the nodes to each other:

```bash
# Get enode URL from validator 1
curl -s http://localhost:4001 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":1}' | jq -r '.result.enode'

# Add peer to validator 2
curl -s http://localhost:4002 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"admin_addPeer","params":["ENODE_URL"],"id":1}'
```

### Non-Validator Node

To run a non-validator node (sync only) in a fakenet:

```bash
./build/armnode --fakenet 0/3 --port 3000 --http --http.port 4000
```

Use `0` as the validator ID to run as a non-validator.

## Using the Demo Scripts

The `demo/` directory contains convenience scripts for running a multi-node fakenet:

```bash
cd armchain-client/demo

# Start a 3-validator network
./start.sh

# Stop all nodes
./stop.sh

# Clean chain data (reset everything)
./clean.sh
```

### Demo Configuration

The demo scripts use these port assignments:

| Validator   | P2P Port | HTTP RPC Port | WebSocket Port |
| ----------- | -------- | ------------- | -------------- |
| Validator 1 | 3001     | 4001          | 4501           |
| Validator 2 | 3002     | 4002          | 4502           |
| Validator 3 | 3003     | 4003          | 4503           |

## Fakenet Parameters

Fakenet uses relaxed parameters for fast development:

| Parameter            | Fakenet          | Devnet        |
| -------------------- | ---------------- | ------------- |
| Empty Block Skip     | 3 seconds        | 1 minute      |
| Max Epoch Duration   | 10 minutes       | 4 hours       |
| Max Epoch Gas        | 300,000,000      | 1,500,000,000 |
| Gas Power Allocation | 1000× base       | 1× base       |
| Validator Password   | `"fakepassword"` | User-defined  |
| Hard Forks           | London + LLR     | London + LLR  |

## Working with Fakenet Accounts

### Pre-funded Validator Accounts

Each fakenet validator is pre-funded with **1 billion ARM** (1e27 wei). Access them via the console:

```bash
# Attach to the node
./build/armnode --datadir /path/to/datadir attach

# In the console:
> eth.accounts
["0xVALIDATOR_ADDRESS"]

> eth.getBalance(eth.accounts[0])
1e+27

# Unlock account for sending transactions
> personal.unlockAccount(eth.accounts[0], "fakepassword")
true
```

### Creating Test Accounts

```bash
# Via console
> personal.newAccount("testpassword")
"0xNEW_ACCOUNT_ADDRESS"

# Via RPC
curl -X POST http://localhost:4000 \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"personal_newAccount","params":["testpassword"],"id":1}'
```

### Sending Test Transactions

```bash
# Via console
> eth.sendTransaction({
    from: eth.accounts[0],
    to: "0xRECIPIENT",
    value: web3.toWei(100, "ether")
  })

# Via RPC
curl -X POST http://localhost:4000 \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"eth_sendTransaction",
    "params":[{
      "from": "0xVALIDATOR_ADDRESS",
      "to": "0xRECIPIENT",
      "value": "0x56BC75E2D63100000"
    }],
    "id":1
  }'
```

## Deploying Contracts to Fakenet

### With Hardhat

```javascript
// hardhat.config.js
module.exports = {
  solidity: "0.8.24",
  networks: {
    "armchain-local": {
      url: "http://localhost:4000",
      chainId: 55,
      accounts: [process.env.PRIVATE_KEY],
    },
  },
};
```

```bash
npx hardhat run scripts/deploy.js --network armchain-local
```

### With the Armchain Ethers SDK

```typescript
import { JsonRpcProvider, Wallet, parseEther } from "@armchain-ethersv6/ethers";

const provider = new JsonRpcProvider("http://localhost:4000");
const wallet = new Wallet(FAKENET_PRIVATE_KEY, provider);

// Deploy a contract
const factory = new ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy();
await contract.waitForDeployment();

console.log("Deployed at:", await contract.getAddress());
```

## Resetting Fakenet

To start fresh:

```bash
# Stop the node (Ctrl+C)

# Delete chain data
rm -rf ~/.armnode/  # or your custom --datadir

# Restart
./build/armnode --fakenet 1/1 --http --http.port 4000 ...
```

Or if using the demo scripts:

```bash
cd demo/
./stop.sh
./clean.sh
./start.sh
```

## Common Use Cases

| Use Case               | Configuration                                                                  |
| ---------------------- | ------------------------------------------------------------------------------ |
| Quick contract testing | Single validator: `--fakenet 1/1`                                              |
| Consensus testing      | Multi-validator: `--fakenet 1/3`, `2/3`, `3/3`                                 |
| Frontend development   | Single validator with CORS: `--http.corsdomain "*"`                            |
| API testing            | Enable all APIs: `--http.api eth,web3,net,personal,txpool,arm,dag,debug,admin` |
| Load testing           | Increase gas power, multiple validators                                        |

## Limitations

* **Not for production**: Keys are deterministic and publicly known
* **No persistence guarantees**: Chain data may be lost between restarts if not backed up
* **Network isolation**: Fakenet nodes can only connect to other fakenet nodes with the same validator count
* **No real staking**: Validators are auto-registered with equal stakes

## Further Reading

* [Quickstart](/get-started/quickstart): Deploy contracts to fakenet
* [Network Configuration](/get-started/network-configuration): All network details


# Tools

Official tools to help you build, test, and interact with the Armchain network.

| Tool                        | Description                                                             |
| --------------------------- | ----------------------------------------------------------------------- |
| [Wallet](/tools/wallet)     | Browser extension wallet for managing accounts and signing transactions |
| [Explorer](/tools/explorer) | Browse blocks, transactions, and verified contracts                     |
| [Faucet](/tools/faucet)     | Get free devnet ARM tokens for testing                                  |


# Wallet

The **Armchain Wallet** is the official browser extension wallet for the Armchain network, providing post-quantum secure key management and transaction signing.

## Download

The Armchain Wallet is available as a browser extension for Chrome-based browsers.

## Features

| Feature                 | Description                                               |
| ----------------------- | --------------------------------------------------------- |
| **ML-DSA44 Keys**       | Post-quantum secure key generation and storage            |
| **HD Wallet**           | BIP-39 mnemonic with Armchain-specific derivation paths   |
| **Multi-Account**       | Derive multiple accounts from a single seed phrase        |
| **Token Support**       | View and manage ARM, ERC-20, ERC-721, and ERC-1155 tokens |
| **dApp Connector**      | Connect to web3 dApps (EIP-1193 compatible)               |
| **Transaction Signing** | Sign Type 3 PQC transactions                              |
| **Message Signing**     | Sign personal messages and EIP-712 typed data             |
| **Network Management**  | Switch between devnet and custom networks                 |

## Getting Started

### Create a New Wallet

1. Install the extension
2. Click "Create New Wallet"
3. Set a strong password
4. **Write down your 12-word recovery phrase** and store it offline and securely
5. Confirm the recovery phrase
6. Wallet is ready

### Import an Existing Wallet

**From Seed Phrase:**

1. Click "Import Wallet"
2. Select "Seed Phrase"
3. Enter your 12 or 24-word recovery phrase
4. Set a password
5. Your accounts will be recovered

**From Private Key:**

1. Click "Import Wallet"
2. Select "Private Key"
3. Enter your ML-DSA44 private key
4. Set a password

> **Important**: Only import ML-DSA44 private keys (2,560 bytes). Standard ECDSA keys are not compatible.

## Account Management

### Creating Additional Accounts

The wallet supports deriving multiple accounts from a single seed phrase:

1. Click the account selector
2. Click "Create Account"
3. A new account is derived using the next hardened index

**Derivation path**: `m/44'/65536'/n'/0/0'` where `n` is the account index.

### Viewing Account Details

* **Address**: Your Armchain address (20 bytes, same format as Ethereum)
* **Balance**: ARM token balance
* **Tokens**: ERC-20 token balances
* **Activity**: Transaction history

## Sending Transactions

1. Click "Send"
2. Enter the recipient address
3. Enter the amount (in ARM)
4. Review gas fees (EIP-1559)
5. Click "Confirm"
6. The transaction is signed with ML-DSA44 and broadcast
7. Confirmation is immediate (instant finality)

## Connecting to dApps

The Armchain Wallet injects a standard EIP-1193 provider (`window.ethereum`), making it compatible with dApps that use:

* ethers.js / `@armchain-ethersv6/ethers`
* web3.js
* Any EIP-1193 compatible library

### Connect Flow

1. Visit a dApp
2. Click "Connect Wallet" on the dApp
3. Approve the connection in the wallet popup
4. Your address is now available to the dApp

## Network Configuration

### Pre-configured Networks

| Network         | Chain ID | RPC                               |
| --------------- | -------- | --------------------------------- |
| Armchain Devnet | 55       | <https://www.armchain.org/devnet> |
| Armchain Local  | 55       | <http://localhost:4000>           |

### Adding Custom Networks

1. Go to Settings → Networks
2. Click "Add Network"
3. Enter:
   * Network Name
   * RPC URL
   * Chain ID
   * Currency Symbol
   * Block Explorer URL (optional)

## Security

### Key Storage

* Private keys are encrypted with your wallet password using AES-256
* Keys never leave the extension. All signing happens locally
* No key material is sent to any server

### Recovery

* Your **seed phrase** is the only way to recover your wallet
* Store it offline, on paper or metal, in a secure location
* Never share your seed phrase
* Never enter it on a website; only enter it in the wallet extension

### Best Practices

1. Use a strong, unique password for the wallet
2. Back up your seed phrase immediately after creation
3. Test recovery on a separate device before funding the wallet
4. Keep the extension updated
5. Only connect to trusted dApps

## For Developers

See the [Wallet Integration Guide](/developers/overview-2) for details on:

* Connecting your dApp to the wallet
* Handling chain switching
* Signing messages and typed data
* Transaction management

## Troubleshooting

| Issue                       | Solution                                              |
| --------------------------- | ----------------------------------------------------- |
| Wallet not detected by dApp | Refresh the page, ensure extension is enabled         |
| Transaction pending forever | Check gas price, increase `maxFeePerGas`              |
| Wrong network               | Switch to the correct network in wallet settings      |
| Balance not updating        | Manually refresh or switch networks and back          |
| Import failed               | Ensure you're using an ML-DSA44 key or valid mnemonic |

## Further Reading

* [Wallet Integration (Developers)](/developers/overview-2) for building dApps with wallet support
* [PQC Overview](/pqc/overview) to understand the cryptography
* [Network Configuration](/get-started/network-configuration) for network details


# Explorer

The **Armchain Explorer** is a web-based tool for browsing blocks, transactions, addresses, and smart contracts. It works like Etherscan but is tailored for Armchain's post-quantum transactions.

## Public Explorer

**URL**: <https://www.armchain.org/explorer>

## Features

### Transaction Browsing

* View all transactions with decoded input data and event logs
* Filter by address, block, or contract
* See transaction status, gas usage, and fees
* **Type 3 PQC transactions**: Displays ML-DSA44 signature information

### Block Explorer

* Browse blocks with full transaction lists
* View block details: gas used, timestamp, validator
* Real-time block updates via WebSocket

### Address Pages

* View ARM balance and token balances
* Transaction history (sent and received)
* Internal transactions (from contract calls)
* Token transfers (ERC-20, ERC-721, ERC-1155)
* Analytics and charts

### Smart Contract Interaction

* **Read Contract**: Call view/pure functions directly from the browser
* **Write Contract**: Execute state-changing functions (requires wallet connection)
* **Contract Verification**: Submit source code for public verification
* **ABI Interface**: Auto-generated UI from contract ABI

### Contract Verification

To verify your smart contract on the explorer:

1. Navigate to your contract address
2. Click the **"Verify & Publish"** tab
3. Select your compiler version and settings
4. Submit your Solidity source code
5. The explorer will attempt to compile and match the bytecode

Supported verification methods:

* **Single file**: Flat Solidity file
* **Standard JSON input**: Hardhat/Foundry standard JSON output
* **Multi-file**: Multiple Solidity files with imports

### Event Log Decoding

The explorer automatically decodes event logs when the contract is verified:

* Shows decoded event names and parameters
* Indexed parameters are searchable
* Links to related addresses and tokens

### Analytics

* Network statistics (TPS, gas usage, block times)
* Address analytics (balance history, transaction volume)
* Token analytics (transfers, holders, supply)
* Charts and visualizations

## Explorer API

The explorer provides a REST API for programmatic access:

**Base URL**: `https://www.armchain.org/explorer/api`

### Get Account Balance

```bash
curl "https://www.armchain.org/explorer/api?module=account&action=balance&address=0xADDRESS"
```

### Get Transaction List

```bash
curl "https://www.armchain.org/explorer/api?module=account&action=txlist&address=0xADDRESS&page=1&offset=10"
```

### Get Contract ABI

```bash
curl "https://www.armchain.org/explorer/api?module=contract&action=getabi&address=0xCONTRACT"
```

### Get Contract Source Code

```bash
curl "https://www.armchain.org/explorer/api?module=contract&action=getsourcecode&address=0xCONTRACT"
```

## Real-Time Updates

The explorer uses WebSocket connections (Soketi/Pusher) for real-time updates:

* New blocks appear instantly
* Transaction confirmations update in real-time
* No page refresh needed

## Self-Hosting

The Armchain Explorer can be self-hosted for private networks. Contact the team for setup details.

### Requirements

* Node.js 18+
* PostgreSQL database
* Redis (for caching and queues)
* Soketi or Pusher (for real-time updates)

## Further Reading

* [Network Configuration](/get-started/network-configuration) for Explorer URLs and API endpoints
* [Smart Contracts](/developers/overview) for writing and verifying contracts
* [JSON-RPC API](/api-reference/json-rpc) for direct node API access


# Faucet

The Armchain faucet distributes free devnet ARM tokens for development and testing purposes.

## Devnet Faucet

The official devnet faucet is available at:

**<https://www.armchain.org/faucet>** *(coming soon)*

### How to Use

1. Connect to the Armchain devnet (Chain ID: 55)
2. Copy your wallet address
3. Visit the faucet URL
4. Paste your address
5. Click "Request Tokens"
6. Tokens arrive within seconds (instant finality)

### Limits

> **Note**: Drop amount and cooldown period for the devnet faucet are yet to be finalized.

| Parameter          | Value |
| ------------------ | ----- |
| Tokens per request | TBD   |
| Cooldown period    | TBD   |

## Local Development (Fakenet)

For local development, fakenet validators are pre-funded. No faucet needed.

### Pre-funded Accounts

When running a fakenet with a single validator, `--fakenet 1/1`, the validator account is funded with a large ARM balance. Use it as a "faucet" for your tests:

```javascript
import { ethers } from "@armchain-ethersv6/ethers";

const provider = new ethers.JsonRpcProvider("http://localhost:18545");

// The validator account private key (from fakenet genesis)
// Replace with the actual key from your fakenet setup
const faucet = new ethers.Wallet(VALIDATOR_PRIVATE_KEY, provider);

// Send ARM to a test address
async function fund(address) {
  const tx = await faucet.sendTransaction({
    to: address,
    value: ethers.parseEther("100"),
    type: 3,
  });
  await tx.wait();
  console.log(`Funded ${address} with 100 ARM`);
}
```

### Hardhat Local Faucet

> **⚠ Compatibility Warning**: Self-funding via `hre.ethers` or Hardhat's default signers does not work on Armchain. The `deployer.sendTransaction()` pattern uses ECDSA and will be rejected. Use the Armchain Ethers SDK example above instead.

## Programmatic Faucet (Self-Hosted)

For CI/CD pipelines or automated testing, set up a simple faucet script:

```javascript
import { ethers } from "@armchain-ethersv6/ethers";
import express from "express";

const app = express();
app.use(express.json());

const provider = new ethers.JsonRpcProvider("http://localhost:18545");
const faucet = new ethers.Wallet(process.env.FAUCET_KEY, provider);

const cooldowns = new Map();
const AMOUNT = ethers.parseEther("10");
const COOLDOWN_MS = 60 * 60 * 1000; // 1 hour

app.post("/fund", async (req, res) => {
  const { address } = req.body;

  if (!ethers.isAddress(address)) {
    return res.status(400).json({ error: "Invalid address" });
  }

  const lastRequest = cooldowns.get(address);
  if (lastRequest && Date.now() - lastRequest < COOLDOWN_MS) {
    return res.status(429).json({ error: "Rate limited" });
  }

  try {
    const tx = await faucet.sendTransaction({
      to: address,
      value: AMOUNT,
      type: 3,
    });
    await tx.wait();
    cooldowns.set(address, Date.now());
    res.json({ txHash: tx.hash });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(3000, () => console.log("Faucet running on :3000"));
```

## Further Reading

* [Fakenet Setup](/node-operators/fakenet) to run a local development network
* [Network Configuration](/get-started/network-configuration) to connect to devnet
* [Quickstart](/get-started/quickstart) to deploy your first contract


# API Reference

Complete API documentation for interacting with Armchain nodes. If you've used Ethereum's JSON-RPC before, most methods work identically.

| Reference                                   | Description                                                         |
| ------------------------------------------- | ------------------------------------------------------------------- |
| [JSON-RPC](/api-reference/json-rpc)         | Standard Ethereum-compatible RPC methods (eth\_*, net\_*, web3\_\*) |
| [Armchain API](/api-reference/armchain-api) | Armchain-specific endpoints for DAG queries, validators, and more   |


# JSON-RPC

Armchain exposes a standard Ethereum-compatible JSON-RPC API over HTTP and WebSocket. If you've used Ethereum's JSON-RPC before, everything works the same way: same method names, same parameters, same response format.

## Endpoint

```
HTTP:      http://localhost:18545   (or https://www.armchain.org/devnet for devnet)
WebSocket: ws://localhost:18546     (or wss://www.armchain.org/devnet for devnet)
```

## Request Format

```bash
curl -X POST <RPC_URL> \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "<METHOD>",
    "params": [<PARAMS>],
    "id": 1
  }'
```

***

## eth Namespace

### eth\_blockNumber

Returns the latest block number.

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

**Response:**

```json
{"jsonrpc":"2.0","id":1,"result":"0x1a4"}
```

***

### eth\_getBalance

Returns the balance of an address in wei.

**Parameters:**

1. `address`: 20-byte address
2. `blockTag`: Block number or `"latest"`, `"earliest"`, `"pending"`

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xADDRESS","latest"],"id":1}'
```

**Response:**

```json
{"jsonrpc":"2.0","id":1,"result":"0xde0b6b3a7640000"}
```

***

### eth\_getTransactionCount

Returns the number of transactions sent from an address (nonce).

**Parameters:**

1. `address`: 20-byte address
2. `blockTag`: Block number or tag

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0xADDRESS","latest"],"id":1}'
```

***

### eth\_sendRawTransaction

Submits a signed transaction for broadcast.

**Parameters:**

1. `data`: Signed transaction data (hex). Must be a **Type 3 PQC** transaction.

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0x03f9..."],"id":1}'
```

**Response:**

```json
{"jsonrpc":"2.0","id":1,"result":"0xTRANSACTION_HASH"}
```

***

### eth\_call

Executes a call without creating a transaction (read-only).

**Parameters:**

1. `transaction`: Transaction call object
2. `blockTag`: Block number or tag

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"eth_call",
    "params":[{
      "to": "0xCONTRACT_ADDRESS",
      "data": "0x70a08231000000000000000000000000ADDRESS"
    }, "latest"],
    "id":1
  }'
```

***

### eth\_estimateGas

Estimates gas for a transaction.

**Parameters:**

1. `transaction`: Transaction call object

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"eth_estimateGas",
    "params":[{
      "from": "0xFROM",
      "to": "0xTO",
      "value": "0xde0b6b3a7640000"
    }],
    "id":1
  }'
```

***

### eth\_getBlockByNumber

Returns block information by block number.

**Parameters:**

1. `blockNumber`: Block number (hex) or tag
2. `fullTransactions`: If `true`, returns full transaction objects

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1}'
```

***

### eth\_getTransactionByHash

Returns transaction details by hash.

**Parameters:**

1. `transactionHash`: 32-byte transaction hash

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0xHASH"],"id":1}'
```

**Response (Type 3 PQC Transaction):**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "0x3",
    "hash": "0x...",
    "from": "0x...",
    "to": "0x...",
    "value": "0xde0b6b3a7640000",
    "nonce": "0x1",
    "gasLimit": "0x5208",
    "maxFeePerGas": "0x4a817c800",
    "maxPriorityFeePerGas": "0x77359400",
    "chainId": "0x37",
    "blockNumber": "0x1a4",
    "blockHash": "0x...",
    "transactionIndex": "0x0"
  }
}
```

***

### eth\_getTransactionReceipt

Returns the receipt of a mined transaction.

**Parameters:**

1. `transactionHash`: 32-byte transaction hash

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0xHASH"],"id":1}'
```

***

### eth\_getLogs

Returns logs matching a filter.

**Parameters:**

1. `filter`: Filter object with `fromBlock`, `toBlock`, `address`, `topics`

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"eth_getLogs",
    "params":[{
      "fromBlock": "0x0",
      "toBlock": "latest",
      "address": "0xCONTRACT",
      "topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
    }],
    "id":1
  }'
```

***

### eth\_chainId

Returns the chain ID.

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
```

**Response:**

```json
{"jsonrpc":"2.0","id":1,"result":"0x37"}
```

***

### eth\_gasPrice

Returns the current gas price.

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":1}'
```

***

### eth\_getCode

Returns the code at a given address (for contracts).

**Parameters:**

1. `address`: Contract address
2. `blockTag`: Block number or tag

```bash
curl -X POST https://www.armchain.org/devnet -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_getCode","params":["0xCONTRACT","latest"],"id":1}'
```

***

## Other Standard Methods

| Method                                    | Description                               |
| ----------------------------------------- | ----------------------------------------- |
| `eth_accounts`                            | Returns list of owned addresses           |
| `eth_sign`                                | Signs data with account key               |
| `eth_signTransaction`                     | Signs a transaction without sending       |
| `eth_getBlockByHash`                      | Get block by hash                         |
| `eth_getTransactionByBlockHashAndIndex`   | Get tx by block hash and index            |
| `eth_getTransactionByBlockNumberAndIndex` | Get tx by block number and index          |
| `eth_getStorageAt`                        | Get storage value at position             |
| `eth_syncing`                             | Returns sync status                       |
| `eth_mining`                              | Returns whether node is mining/validating |
| `eth_coinbase`                            | Returns validator address                 |

***

## net Namespace

| Method          | Description                               |
| --------------- | ----------------------------------------- |
| `net_version`   | Returns network ID                        |
| `net_listening` | Whether node is listening for connections |
| `net_peerCount` | Number of connected peers                 |

***

## web3 Namespace

| Method               | Description                   |
| -------------------- | ----------------------------- |
| `web3_clientVersion` | Returns client version string |
| `web3_sha3`          | Returns Keccak-256 of input   |

***

## txpool Namespace

| Method           | Description                          |
| ---------------- | ------------------------------------ |
| `txpool_status`  | Returns pending and queued tx counts |
| `txpool_content` | Returns all pending and queued txs   |
| `txpool_inspect` | Returns textual summary of pool      |

***

## personal Namespace

> **Warning**: Only use on local/private nodes. Never expose on public endpoints.

| Method                     | Description                   |
| -------------------------- | ----------------------------- |
| `personal_newAccount`      | Create a new account          |
| `personal_unlockAccount`   | Unlock an account for signing |
| `personal_lockAccount`     | Lock an account               |
| `personal_listAccounts`    | List all accounts             |
| `personal_sendTransaction` | Sign and send a transaction   |

***

## admin Namespace

| Method             | Description                     |
| ------------------ | ------------------------------- |
| `admin_nodeInfo`   | Returns node information        |
| `admin_peers`      | Returns connected peers         |
| `admin_addPeer`    | Add a peer by enode URL         |
| `admin_removePeer` | Remove a peer                   |
| `admin_datadir`    | Returns the data directory path |

***

## WebSocket Subscriptions

### Subscribe to New Blocks

```javascript
// eth_subscribe: newHeads
ws.send(JSON.stringify({
  jsonrpc: "2.0",
  method: "eth_subscribe",
  params: ["newHeads"],
  id: 1
}));
```

### Subscribe to Logs

```javascript
// eth_subscribe: logs
ws.send(JSON.stringify({
  jsonrpc: "2.0",
  method: "eth_subscribe",
  params: ["logs", {
    address: "0xCONTRACT",
    topics: ["0xTOPIC_HASH"]
  }],
  id: 1
}));
```

### Subscribe to Pending Transactions

```javascript
ws.send(JSON.stringify({
  jsonrpc: "2.0",
  method: "eth_subscribe",
  params: ["newPendingTransactions"],
  id: 1
}));
```

### Unsubscribe

```javascript
ws.send(JSON.stringify({
  jsonrpc: "2.0",
  method: "eth_unsubscribe",
  params: ["SUBSCRIPTION_ID"],
  id: 2
}));
```

***

## Rate Limits

Public RPC endpoints may enforce rate limits.

## Further Reading

* [Armchain-Specific APIs](/api-reference/armchain-api): DAG and PQC-specific endpoints
* [Network Configuration](/get-started/network-configuration): Endpoints and chain details


# Armchain API

Beyond the standard Ethereum JSON-RPC methods, Armchain provides additional API namespaces for querying DAG data, epoch information, and validator state. These are useful when you need to interact with Armchain's consensus layer directly.

## arm Namespace

The `arm` namespace provides Armchain-specific methods. These are available when `arm` is enabled in `--http.api`.

### arm\_getBalance

Returns the balance at a given address (alias for `eth_getBalance`).

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"arm_getBalance","params":["0xADDRESS","latest"],"id":1}'
```

### arm\_getTransactionCount

Returns the transaction count (nonce) for an address.

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"arm_getTransactionCount","params":["0xADDRESS","latest"],"id":1}'
```

### arm\_sendTransaction

Send a transaction (account must be unlocked on the node).

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"arm_sendTransaction",
    "params":[{
      "from": "0xFROM",
      "to": "0xTO",
      "value": "0xDE0B6B3A7640000"
    }],
    "id":1
  }'
```

***

## dag Namespace

The `dag` namespace provides access to the DAG (Directed Acyclic Graph) data structures that underpin Lachesis consensus.

### dag\_getEvent

Returns a DAG event by its hash.

**Parameters:**

1. `eventHash`: 32-byte event hash

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getEvent","params":["0xEVENT_HASH"],"id":1}'
```

**Response:**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "0x...",
    "creator": 1,
    "epoch": 42,
    "seq": 1057,
    "frame": 100,
    "isRoot": false,
    "parents": ["0xPARENT_1", "0xPARENT_2"],
    "gasPowerLeft": { "shortTerm": 1000000, "longTerm": 5000000 },
    "gasPowerUsed": 28000,
    "txHash": "0x...",
    "lamportTime": 50000,
    "creationTime": 1708300000000000000
  }
}
```

### dag\_getEventsByEpoch

Returns events in a specific epoch.

**Parameters:**

1. `epoch`: Epoch number

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getEventsByEpoch","params":["0x2A"],"id":1}'
```

### dag\_getEpochStats

Returns statistics for the current or specified epoch.

**Parameters:**

1. `epoch`: (optional) Epoch number. Defaults to current epoch.

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getEpochStats","params":[],"id":1}'
```

### dag\_getHeads

Returns the current head events in the DAG (latest events from each validator).

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"dag_getHeads","params":[],"id":1}'
```

***

## aBFT Information

### abft\_getValidators

Returns the current validator set.

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"abft_getValidators","params":[],"id":1}'
```

**Response:**

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "epoch": 42,
    "validators": [
      {
        "id": 1,
        "pubkey": "0x01...",
        "stake": "1000000000000000000000000",
        "address": "0x..."
      }
    ]
  }
}
```

### abft\_getDowntime

Returns downtime information for a validator.

**Parameters:**

1. `validatorId`: Validator ID

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"abft_getDowntime","params":[1],"id":1}'
```

### abft\_getEpoch

Returns the current epoch number.

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"abft_getEpoch","params":[],"id":1}'
```

***

## debug Namespace

### debug\_traceTransaction

Replays a transaction and returns execution traces.

**Parameters:**

1. `txHash`: Transaction hash
2. `options`: (optional) Tracing options

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"debug_traceTransaction",
    "params":["0xTX_HASH", {"tracer": "callTracer"}],
    "id":1
  }'
```

### debug\_traceCall

Traces a call without executing it on-chain.

```bash
curl -X POST http://localhost:4000 -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0",
    "method":"debug_traceCall",
    "params":[{
      "from": "0xFROM",
      "to": "0xTO",
      "data": "0xCALLDATA"
    }, "latest", {"tracer": "callTracer"}],
    "id":1
  }'
```

***

## Transaction Object Fields (Type 3)

When querying Type 3 PQC transactions, the response includes these fields:

| Field                  | Type     | Description                                         |
| ---------------------- | -------- | --------------------------------------------------- |
| `type`                 | `string` | `"0x3"` - PQC transaction type                      |
| `hash`                 | `string` | Transaction hash                                    |
| `from`                 | `string` | Sender address (derived from ML-DSA44 public key)   |
| `to`                   | `string` | Recipient address (or `null` for contract creation) |
| `value`                | `string` | Transfer value in wei                               |
| `nonce`                | `string` | Sender nonce                                        |
| `gas` / `gasLimit`     | `string` | Gas limit                                           |
| `maxFeePerGas`         | `string` | Maximum total fee per gas                           |
| `maxPriorityFeePerGas` | `string` | Priority fee per gas                                |
| `input` / `data`       | `string` | Transaction input data                              |
| `chainId`              | `string` | Chain ID                                            |
| `blockNumber`          | `string` | Block number (when mined)                           |
| `blockHash`            | `string` | Block hash (when mined)                             |
| `transactionIndex`     | `string` | Index in block                                      |

> **Note**: Type 3 transactions do not have `v`, `r`, `s` fields. Signature data is in the `signatureBlob` field.

***

## Enabling API Namespaces

```bash
# Enable specific namespaces
./build/armnode \
  --http \
  --http.api eth,net,web3,arm,dag,txpool,debug,admin,personal \
  --ws \
  --ws.api eth,net,web3,arm,dag
```

> **Security**: Only enable `personal`, `admin`, and `debug` on trusted/local networks.

## Further Reading

* [JSON-RPC Standard Methods](/api-reference/json-rpc): Standard Ethereum RPC methods
* [DAG Structure](/architecture/dag): Understanding DAG events
* [Consensus](/architecture/consensus): Lachesis aBFT and epochs
* [Fakenet](/node-operators/fakenet): Local development network setup


# Resources

Additional reference material, common questions, and contribution guidelines.

| Document                                | Description                                         |
| --------------------------------------- | --------------------------------------------------- |
| [FAQ](/resources/faq)                   | Answers to the most common questions about Armchain |
| [Glossary](/resources/glossary)         | Key terms and concepts explained                    |
| [Contributing](/resources/contributing) | How to contribute to the Armchain ecosystem         |


# FAQ

Quick answers to the most common questions about Armchain. If your question isn't here, check the [Glossary](/resources/glossary) for terminology or visit the community Discord.

## General

### What is Armchain?

Armchain is a Layer 1 blockchain combining **EVM compatibility** with **post-quantum cryptography**. It uses Lachesis aBFT consensus for instant finality and ML-DSA44 (FIPS 204) for quantum-resistant signing.

### How is Armchain different from Ethereum?

| Feature          | Ethereum                | Armchain                    |
| ---------------- | ----------------------- | --------------------------- |
| Consensus        | Proof of Stake (Gasper) | Lachesis aBFT (DAG-based)   |
| Finality         | \~12 minutes            | **Instant**                 |
| Cryptography     | ECDSA (secp256k1)       | **ML-DSA44** (post-quantum) |
| Transaction Type | 0, 1, 2                 | **3** (PQC only)            |
| EVM              | Yes                     | Yes (London)                |

### Is Armchain EVM compatible?

Yes. Standard Solidity and Vyper contracts deploy on Armchain without modification. All EVM opcodes, precompiles, and ABI encoding work identically.

### What is the native token?

**ARM** is used for gas fees, validator staking, and governance.

***

## Post-Quantum Cryptography

### Why does Armchain use post-quantum cryptography?

Current blockchains use ECDSA, which can be broken by a sufficiently powerful quantum computer using Shor's algorithm. Armchain uses ML-DSA44 (a NIST-standardized post-quantum algorithm) to ensure that transactions and keys remain secure even in a post-quantum world.

### What is ML-DSA44?

ML-DSA44 (Module-Lattice Digital Signature Algorithm, Level 2) is a digital signature scheme standardized by NIST in FIPS 204. It provides \~128-bit security against both classical and quantum computers.

### Are quantum computers a real threat today?

Not yet for breaking cryptography. However, the **"harvest now, decrypt later"** attack is a current concern: adversaries store signed data today for later decryption when quantum computers become available. Armchain provides protection from day one.

### Why are transactions larger on Armchain?

ML-DSA44 signatures are inherently larger than ECDSA (3,732 bytes vs 65 bytes). The public key must also be included in every transaction (1,312 bytes) because ML-DSA44 does not support public key recovery. This results in \~3,930-byte simple transfers vs \~110 bytes with ECDSA.

### Can I use my Ethereum private key on Armchain?

No. ECDSA private keys are incompatible with Armchain. Generate ML-DSA44 keys using the Armchain Wallet or `@armchain-ethersv6/ethers`.

***

## Development

### Can I use Hardhat with Armchain?

Yes. Install `@armchain-ethersv6/ethers` and configure Hardhat with Armchain's RPC URL and chain ID. See [Quickstart](/get-started/quickstart).

### Can I use Foundry with Armchain?

Foundry can be used for compilation and read-only interactions (`cast call`). Deployment and write transactions require ML-DSA44 signing, which Foundry does not natively support. Use Hardhat or the Ethers SDK instead. See [Smart Contracts Overview](/developers/overview).

### What Solidity version should I use?

Armchain supports Solidity `^0.8.x`. Recommended: `0.8.24` or later.

### How do I verify contracts on the explorer?

Use the Armchain Explorer's contract verification tool. Submit source code, compiler version, and constructor arguments. See [Explorer](/tools/explorer).

### What is the gas limit for blocks?

The maximum block gas is **20,500,000**. Minimum gas price is **1 Gwei**.

***

## Wallet

### How do I create a wallet?

Install the Armchain Wallet browser extension, or create a wallet programmatically:

```javascript
import { ethers } from "@armchain-ethersv6/ethers";
const wallet = ethers.Wallet.createRandom();
console.log(wallet.address);
```

### What derivation path does Armchain use?

```
m/44'/65536'/account'/0/address'
```

All indices are hardened. Coin type is `65536`.

### How do I back up my wallet?

Write down your **12-word recovery phrase** and store it offline in a secure location. This phrase recovers all derived accounts.

***

## Node Operations

### How do I run a validator?

Build the `opera` binary, generate ML-DSA44 validator keys, stake ARM, and register. See [Running a Validator](https://github.com/khizarbakhtiar1/dev-docs/blob/main/node-operators/running-a-validator.md).

### What ports need to be open?

| Port  | Protocol | Purpose                                  |
| ----- | -------- | ---------------------------------------- |
| 5050  | TCP/UDP  | P2P networking                           |
| 18545 | TCP      | HTTP JSON-RPC (optional, for API access) |
| 18546 | TCP      | WebSocket JSON-RPC (optional)            |

### How do I set up a local development network?

Use fakenet mode:

```bash
armnode --fakenet 1/1
```

See [Fakenet Guide](/node-operators/fakenet).

***

## Account Abstraction

### Does Armchain support Account Abstraction?

Yes. Armchain implements **native Account Abstraction** at the protocol level. This is not an application-layer standard like ERC-4337. AA transactions go through a dedicated mempool and gossip layer in the consensus client, and are validated by system contracts deployed at genesis.

### What signing schemes can AA accounts use?

AA accounts support multiple validation schemes:

* **MLDSA**: ML-DSA44 (post-quantum, same as standard transactions)
* **Passkey (P-256)**: WebAuthn-compatible biometric devices and hardware keys (Touch ID, Face ID, YubiKey)
* **SLH-DSA**: SPHINCS+ hash-based post-quantum (FIPS 205)
* **MPC for ECDSA**: Guardian accounts with multi-party key management
* **Custom**: Any logic conforming to the system contract interface (written in Yul)

Falcon (lattice-based PQC) is planned but not yet available.

### What is the Foundation Paymaster?

The Foundation Paymaster is a protocol-level contract deployed at genesis. It sponsors contract deployments and initial transactions for new users who do not hold ARM, solving the gas bootstrapping problem for onboarding. It operates under a staking-based economic policy that deters DDoS attacks.

### Can I use a passkey (biometric) to control an Armchain account?

Yes. The Passkey validation scheme supports WebAuthn / FIDO2 credentials. Users can sign AA transactions using Touch ID, Face ID, fingerprint readers, or hardware security keys. No seed phrase management is required for Passkey accounts. Wallet UI support for this is in active development.

### Can third parties run their own paymaster on Armchain?

Yes. Any entity can deploy and operate a paymaster. The paymaster must stake ARM and maintain a deposit covering expected gas costs. Common models include free sponsorship for dApp users, token paymasters, and subscription-based paymasters.

### Does Armchain have social login?

Social login (using OAuth providers like Google or Apple as an account recovery mechanism) is not currently available. It would require operating MPC infrastructure on top of the existing Guardian account framework. This is an attractive feature under consideration for a future release.

### Do I need to write Yul to use Account Abstraction?

If you are a **user** of an AA account (Passkey, Guardian, MLDSA wallet), no Yul is required. You interact through a wallet UI or the SDK.

If you are a **developer building a custom validation scheme or paymaster** that uses AA-specific EVM opcodes, you must write that contract in Yul. The standard Solidity compiler does not recognize the new opcodes introduced for native AA. The Armchain team is evaluating a fork of the Solidity compiler to address this.

***

## Troubleshooting

### My transaction is failing with "transaction type not supported"

Ensure you're using `type: 3` in all transactions. Armchain only supports Type 3 (PQC) transactions.

### My transaction is failing with "invalid signature"

Ensure you're using ML-DSA44 keys (not ECDSA). Generate new keys with `ethers.Wallet.createRandom()` from `@armchain-ethersv6/ethers`.

### I can't connect to the node

* Check that the node is running and fully synced
* Verify the RPC URL and port (default: 18545 for HTTP)
* Ensure the RPC endpoint is enabled (`--http --http.addr 0.0.0.0 --http.port 18545`)
* Check firewall rules

### My dApp doesn't detect the Armchain Wallet

* Ensure the extension is installed and enabled
* Refresh the page
* Check that your code listens for `window.ethereum`

### The explorer shows "pending" for my transaction

Armchain has instant finality; transactions confirm in seconds. If a transaction stays pending:

* Verify the gas price is at least 1 Gwei
* Check that the nonce is correct
* Ensure the sender has sufficient ARM balance


# Glossary

Key terms and concepts in Armchain documentation.

***

### AA Mempool

A dedicated transaction pool for Account Abstraction transactions, separate from the standard Type 3 mempool. The AA mempool enforces paymaster deposit checks and AA-specific validation rules before transactions are gossiped to peers or included in events.

### Account Abstraction (AA)

A model where accounts are controlled by smart contracts rather than fixed private keys. Armchain implements native AA at the protocol level: AA transactions go through a dedicated mempool, are validated by system contracts, and can specify paymasters for gas sponsorship. See [Account Abstraction](/account-abstraction/overview).

### aBFT (Asynchronous Byzantine Fault Tolerance)

A consensus property where the network reaches agreement even if up to one-third of validators are faulty or malicious, without any timing assumptions. Armchain's Lachesis consensus is aBFT.

### Access List

A list of addresses and storage slots that a transaction will access (EIP-2930). Included in Type 3 transactions to optimize gas usage.

### Archive Mode

The default garbage collection mode (`--gcmode archive`) in Armchain. Preserves all historical EVM state, meaning no trie data is ever pruned. Required for `eth_call` at arbitrary block numbers and historical tracing. See [Node Types](/node-operators/node-types).

### ARM

The native token of the Armchain network. Used to pay gas fees, stake as a validator, and participate in governance.

### Atropos

The single finalized event elected per frame in the Lachesis consensus. There is a 1:1 mapping between Atropos events and blocks. Atropos is selected through weighted voting by root events across frames. See [Consensus](/architecture/consensus).

### Block

A batch of transactions ordered and finalized by the consensus layer. Blocks are created by processing events from the DAG.

### BIP-39

A standard for generating a mnemonic phrase (12 or 24 words) that encodes a cryptographic seed for HD wallet generation.

### BIP-44

A standard for HD wallet derivation paths: `m / purpose' / coin_type' / account' / change / address_index`. Armchain uses coin type `65536`.

### Chain ID

A unique identifier for a blockchain network. Armchain devnet is `55`.

### Coin Type

The BIP-44 coin type for Armchain: **65536**. Used in derivation paths to distinguish Armchain keys from keys on other networks.

### DAG (Directed Acyclic Graph)

A data structure where events (containing transactions) reference parent events, forming a graph without cycles. Armchain uses a DAG instead of a linear chain for event ordering.

### Database Preset

The `--db.preset` flag selects the database engine and storage layout. Options: `ldb-1` (LevelDB, default), `pbl-1` (Pebble), `legacy-ldb`, `legacy-pbl`. See [Node Types - Database Configuration](/node-operators/node-types#database-configuration).

### Factory Registry

A genesis system contract that tracks approved account factory contracts and their trust levels. New smart accounts must be deployed through a factory registered here for the AA mempool to recognize them. See [System Contracts](/account-abstraction/system-contracts).

### Foundation Paymaster

A protocol-level paymaster system contract deployed at genesis. Funded with ARM tokens, it sponsors contract deployments and initial interactions for new users who do not yet hold ARM. Subject to the same staking and deposit requirements as third-party paymasters. See [Paymaster](/account-abstraction/paymaster).

### Emitter

The component in the Armchain node that creates DAG events at regular intervals (\~110ms minimum), packaging transactions from the mempool and submitting them to the Lachesis consensus engine.

### Epoch

A time period in the Lachesis consensus during which a set of validators participates. Epochs end when enough blocks are sealed. Validator sets can change between epochs.

### EIP-1559

An Ethereum improvement proposal that introduces a base fee (burned) and a priority fee (tip to validators). Armchain uses the EIP-1559 fee model.

### EIP-2718

A standard for typed transaction envelopes. Type 3 transactions use this format with `0x03` as the type prefix.

### Event

A unit of data in the Lachesis DAG. Each event contains transactions, references to parent events, a Lamport timestamp, and a validator signature (ML-DSA44). Events are the fundamental units processed by the Lachesis consensus engine.

### EVM (Ethereum Virtual Machine)

The runtime environment for executing smart contracts. Armchain runs a London fork of the EVM, fully compatible with Solidity and Vyper contracts.

### Fakenet

A local development network for testing. Runs one or more validators with pre-funded accounts and instant finality. See [Fakenet Guide](/node-operators/fakenet).

### Full Mode

A garbage collection mode (`--gcmode full`) that keeps only the 16 most recent state tries (`TriesInMemory = 16`) and prunes older state via greedy GC. Reduces storage compared to archive mode at the cost of limited historical state access. See [Node Types](/node-operators/node-types).

### Finality

The guarantee that a confirmed transaction cannot be reversed. Armchain achieves **instant finality**, meaning once a transaction is included in a block, it is final.

### Frame

A consensus round in the Lachesis protocol that groups events for processing. Frames are the granularity at which Atropos events are elected. Each frame may produce one block (via its Atropos) or be skipped if no Atropos can be elected.

### Guardian Account

An AA smart account controlled through MPC-managed ECDSA keys (secp256k1). No single party holds the complete private key. Supports social recovery, threshold signing, and institutional custody. Deployed via the Guardian factory system contract. See [Validation Schemes](/account-abstraction/validation-schemes).

### Gas

A unit of computational work on the EVM. Every operation has a gas cost. Users pay gas fees in ARM.

### Hardened Derivation

A type of HD key derivation (indicated by `'` or `h` suffix) where child keys cannot be derived from the parent public key. Armchain uses hardened derivation for all path components.

### HD Wallet (Hierarchical Deterministic Wallet)

A wallet that derives multiple key pairs from a single master seed, enabling backup of all accounts with one recovery phrase.

### Intrinsic Gas

The minimum gas required for a transaction before any EVM execution. Includes base cost (21,000) plus data cost. Type 3 transactions have higher intrinsic gas due to larger signature data.

### Light Mode

A garbage collection mode (`--gcmode light`) with the most aggressive state pruning. **Not** a light client protocol: the node still downloads and validates all data. Only the on-disk state retention is minimized. See [Node Types](/node-operators/node-types).

### JSON-RPC

The API protocol for interacting with Armchain nodes. Compatible with Ethereum's JSON-RPC specification (`eth_*`, `net_*`, `web3_*` namespaces).

### Keccak-256

The hash function used for address derivation, transaction hashing, and various EVM operations. Also called SHA-3 in Ethereum contexts (though technically distinct from NIST SHA-3).

### Lachesis

Armchain's aBFT consensus protocol. Based on a DAG of events, it provides instant finality without relying on a single leader or synchronous communication. Key concepts include events, roots, frames, and Atropos elections.

### ML-DSA44 (Module-Lattice Digital Signature Algorithm)

The post-quantum signature algorithm used by Armchain. Standardized in FIPS 204 (NIST). Security level 2 (\~128-bit). Replaces ECDSA/secp256k1 throughout the stack.

### MLWE (Module Learning With Errors)

The mathematical problem underlying ML-DSA44's security. Believed to be hard for both classical and quantum computers.

### NodeDriver

An intermediary on-chain contract through which the Armchain node pushes system-level transactions (epoch sealing, validator deactivation) to the SFC contract. Deployed at genesis.

### Native Account Abstraction

Account Abstraction implemented at the protocol level in the consensus client, rather than as an application-layer standard. On Armchain, this means the node has a dedicated AA mempool, AA gossip protocol, and AA system contracts at genesis. Contrast with ERC-4337, which is an application-layer protocol on top of a standard EOA chain.

### Nonce

A sequential counter for each account that prevents transaction replay. Each transaction must use the next expected nonce.

### Passkey (WebAuthn)

A cryptographic credential stored in a device's secure enclave or hardware authenticator (Touch ID, Face ID, YubiKey). On Armchain, the Passkey AA validation scheme uses P-256 (secp256r1) signatures produced by WebAuthn-compatible authenticators to control a smart account. See [Validation Schemes](/account-abstraction/validation-schemes).

### Paymaster

A contract that pays the gas cost for another account's AA transaction. Paymasters must stake ARM and maintain a deposit. The Foundation Paymaster is the protocol-deployed paymaster for new user onboarding. Third-party paymasters can be deployed by any entity. See [Paymaster](/account-abstraction/paymaster).

### Opera

The name of the Armchain node binary, built from the `armchain-client` repository.

### pqcRecover

An EVM precompile at address `0x13` that recovers a signer's Ethereum address from an ML-DSA44 signature blob. The post-quantum equivalent of `ecRecover` (`0x01`). See [EVM Integration](/architecture/evm).

### Post-Quantum Cryptography (PQC)

Cryptographic algorithms designed to be secure against attacks by quantum computers. Armchain uses ML-DSA44, a lattice-based post-quantum signature scheme.

### RLP (Recursive Length Prefix)

The encoding format used for serializing transactions and other data structures in EVM-compatible blockchains.

### Root

A special event in the Lachesis DAG that qualifies to participate in the Atropos election. A root must be forkless-caused by ≥ 2/3 of validator weight from the previous frame. Each validator can have at most one root per frame.

### Seed

A 32-byte random value used to deterministically generate an ML-DSA44 key pair. In HD wallets, seeds are derived from the master seed via HMAC-SHA512 chains.

### SFC (Special Fee Contract)

The core on-chain governance contract deployed at genesis. Manages validator registration, stake delegation, reward distribution, epoch sealing, slashing, and network parameters. Owned by a single address set at genesis (root of trust). See [Consensus](/architecture/consensus).

### SLH-DSA (SPHINCS+)

Stateless Hash-Based Digital Signature Algorithm, standardized by NIST in FIPS 205. A post-quantum signature scheme based entirely on hash function security, with no lattice assumptions. Available as an AA validation scheme on Armchain for accounts that require diverse post-quantum security assumptions. See [Validation Schemes](/account-abstraction/validation-schemes).

### Shor's Algorithm

A quantum algorithm that can efficiently solve the discrete logarithm and integer factorization problems, breaking ECDSA and RSA. This is the primary quantum threat that ML-DSA44 mitigates.

### Signature Blob

The concatenation of a raw ML-DSA44 signature (2,420 bytes) and the signer's public key (1,312 bytes), totaling 3,732 bytes. Included in Type 3 transactions because ML-DSA44 does not support public key recovery.

### Snap Sync

A synchronization mode (`--syncmode snap`) that downloads a recent state snapshot and only replays recent history, instead of replaying all transactions from genesis. Faster initial sync but cannot query state before the snapshot point. See [Node Types](/node-operators/node-types).

### Validation Scheme

The cryptographic algorithm used by an AA account to authorize transactions. Each scheme is implemented as a system contract. Supported schemes include MLDSA, Passkey (P-256), SLH-DSA, and MPC for ECDSA. Custom schemes can be added by implementing the system contract interface in Yul. See [Validation Schemes](/account-abstraction/validation-schemes).

### Type 3 Transaction

Armchain's post-quantum transaction type. Uses ML-DSA44 for signing, EIP-1559 for fee market, and includes a `signatureBlob` instead of ECDSA's `v`, `r`, `s` fields.

### TriesInMemory

The number of recent state tries kept in memory/disk by a `full` mode node (default: 16). State tries older than this threshold are garbage-collected.

### Validator

A node that participates in the Lachesis consensus by creating events, voting on transaction ordering, and signing blocks with ML-DSA44 keys.

### VecMT (Vector Merkle Tree)

The data structure used to verify event membership in the DAG, enabling light client proofs and efficient consensus verification.

### WebAuthn

A W3C web standard for hardware-backed public key authentication. Armchain's Passkey AA validation scheme supports WebAuthn-compatible authenticators, enabling users to control blockchain accounts using biometric devices (Touch ID, Face ID) or hardware security keys without managing a seed phrase. See [Validation Schemes](/account-abstraction/validation-schemes).

### Yul

The EVM intermediate representation language, positioned between Solidity and raw EVM bytecode. Armchain's AA system contracts are written in Yul because the new AA-specific EVM opcodes are not recognized by the Solidity compiler. Custom AA validation contracts and paymasters that use these opcodes must also be written in Yul. See [System Contracts](/account-abstraction/system-contracts).

### Wei

The smallest denomination of ARM. 1 ARM = 10¹⁸ wei. Gas prices are typically expressed in Gwei (10⁹ wei).


# Contributing

Guidelines for contributing to the Armchain protocol, documentation, and ecosystem.

## Repositories

| Repository          | Description                     | Language                      |
| ------------------- | ------------------------------- | ----------------------------- |
| `armchain-client`   | Node client (armnode binary)    | Go                            |
| `armchain-ethersv6` | Ethers.js v6 fork with ML-DSA44 | TypeScript                    |
| `armchain-wallet`   | Browser extension wallet        | TypeScript                    |
| `armchain-explorer` | Block explorer                  | JavaScript (Vue.js + Node.js) |

## How to Contribute

### Reporting Issues

1. Search existing issues to avoid duplicates
2. Open a new issue with:
   * **Summary**: What happened
   * **Steps to reproduce**: How to trigger the issue
   * **Expected behavior**: What should happen
   * **Actual behavior**: What actually happens
   * **Environment**: OS, node version, SDK version, etc.

### Submitting Code

1. **Fork** the repository
2. **Create a branch** from `main`:

   ```bash
   git checkout -b feature/your-feature
   ```
3. **Make changes** following the coding standards below
4. **Write tests**, since all new features must include tests
5. **Run the test suite** and make sure all tests pass
6. **Commit** with clear, descriptive messages:

   ```
   feat: add support for batch transaction signing
   fix: correct nonce handling in HD derivation
   docs: update PQC transaction format documentation
   ```
7. **Push** and open a **Pull Request**

### Commit Message Format

We follow [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>(<scope>): <description>

[optional body]
[optional footer]
```

Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`

## Coding Standards

### Go (armchain-client)

* Follow standard Go conventions (`gofmt`, `go vet`)
* Write table-driven tests
* Document exported functions and types
* Keep functions focused and small
* Handle all errors explicitly

### TypeScript (ethersv6, wallet)

* Use TypeScript strict mode
* Follow the existing code style (ESLint configuration)
* write unit tests for new functions
* Document public APIs with JSDoc comments

### Solidity (contracts)

* Use Solidity `^0.8.x`
* Follow the [Solidity Style Guide](https://docs.soliditylang.org/en/latest/style-guide.html)
* Write comprehensive Hardhat tests
* Document with NatSpec comments

## Testing

### armchain-client

```bash
cd armchain-client
make test
```

### armchain-ethersv6

```bash
cd armchain-ethersv6
npm test
```

### Smart Contracts

```bash
npx hardhat test
```

## Documentation Contributions

This documentation lives in the `dev-docs/` directory. To contribute:

1. Fork the repository
2. Edit or create Markdown files
3. Follow the existing structure and formatting
4. Keep language clear and concise
5. Include code examples where helpful
6. Submit a PR

### Documentation Style

* Use **second person** ("you") when addressing the reader
* Use **present tense** ("The function returns..." not "The function will return...")
* Keep sentences short and direct
* Use code blocks with language hints (` ```javascript `, ` ```bash `, etc.)
* Link to related pages within the docs
* Include practical examples over theoretical explanations

## Code of Conduct

* Be respectful and constructive
* Welcome newcomers
* Focus on technical merit
* No harassment, discrimination, or personal attacks

## Getting Help

* **GitHub Issues** for bug reports and feature requests
* **Discord** for real-time discussion and support
* **Documentation**: You're reading it!

## License

Contributions are subject to the repository's license. By submitting a PR, you agree that your contributions will be licensed under the same terms.


