# About Pharos

*“We believe in: rough consensus and running code” - David Clark, 1992*

***

## Pharos Network Overview

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

Pharos has designed a **Modular & Full-stack Parallel L1 Blockchain Network**, structured into three primary layers: **L1-Base**, **L1-Core**, and **L1-Extension**.

* **L1-Base**: Delivers industry-leading data availability and hardware acceleration.
* **L1-Core**: A high-performance, globally distributed blockchain network powered by decentralized nodes. These nodes provide high-throughput and sub-second finality, enabling real-time Web3 user experiences.
* **L1-Extension**: Built on top of L1-Core, this layer supports network expansion across three dimensions:
  1. Leverages heterogeneous computation to create custom [Special Processing Networks (SPNs)](https://github.com/PharosNetwork/docs/blob/main/introduction/core-technologies/pharos-spns.md), capable of running blockchain networks, sidechains, or non-blockchain applications like **HFT**, **ZKML**, and **AI models**.
  2. Enables **Native Restaking** to support rapid SPN creation, shared security, staking rewards, and slashing mechanisms. SPNs utilize the excess computing power of validators, providing flexibility in resource use while incentivizing upgrades.
  3. Facilitates **Cross-SPNs Interoperation**, allowing infrastructure, middleware, and application-focused SPNs to communicate and collaborate effortlessly, fostering a highly modular and composable ecosystem.

***

## Ecosystem Architecture

The Pharos ecosystem is structured across three key layers:

1. **Transaction Layer**: Facilitates secure, confidential, and efficient cross-chain asset and account interactions via the secure and confidential cross-chain interoperability protocol.
2. **Consensus Layer**: Integrates with external restaking protocols through the **Adaptive Restaking Interaction Protocol**, enabling shared security and liquidity. This includes circulating assets like stBTC and stETH within the Pharos DeFi ecosystem.
3. **Data Layer**: Uses the **Decentralized Data Exchange Protocol** to synchronize and collaborate with external data centers, promoting innovative on-chain use cases like AI, FHE.

***

### Pharos Modular Stack <a href="#pharos-modular-stack" id="pharos-modular-stack"></a>

A **modular stack** is provided to support SPNs, Pharos L2/L3 systems. Each modular component is open-sourced and available to partners and developers for scaling and customization.Performance benchmarks have been demonstrated on **Geth**, and future collaborations with ecosystem partners will help drive the development of Web3 infrastructure.

<figure><img src="/files/7DkXHa0Ls4Oqf7w3JrGb" alt=""><figcaption></figcaption></figure>

***

## Restaking Integration

Pharos supports a **multi-asset protocols** to enhance security and liquidity, allowing seamless integration with restaking protocols like **Babylon** and **Eigenlayer**. Future updates will include more protocols, expanding interoperability and security across the network.


# Vision & Mission

## Vision

Pharos Network is committed to driving trustless innovations that serve financially underserved communities and asset markets, supporting the future global economy and enabling true mass adoption of Web3 technologies.

***

## Mission

The network aims to establish a Web3 infrastructure with the following characteristics:

* **An Inclusive Payment System**: Providing a Web2-equivalent user experience at the scale of billions of user accounts globally.
* **An Inclusive Asset Exchange System**: Facilitating extreme liquidity for both crypto assets and real-world assets, bridging traditional finance with decentralized markets.
* **A dApp Innovation Platform**: Empowering the development of rich Web3 SuperApps through a revolutionary world computing model, leveraging a generic Coprocessor architecture.


# Why Pharos

Elevating Blockchain Efficiency: A Deep Dive into Scalability and Optimization

Blockchain technology has made significant strides in recent years, yet performance remains a primary bottleneck hindering the Web3 user experience. Although many protocols achieve high TPS in ideal or test environments, their performance in real-world falls far short of expectations. This performance gap makes it difficult for Web3 applications to compete with Web2 applications, limiting their mass adoption and enhancement of user experience.

For instance, with bitcoin inscription frenzy hits EVM blockchains in early 2024, we observed that most public chains could not exceed a thousand TPS(Transaction Per Second), typically ranging from dozens to a few hundred. Some public chains, under high-stress conditions, opted to raise Gas Prices to reduce transaction frequency, but this is neither a healthy nor sustainable solution. In fact, the execution cost of smart contracts is usually far higher than that of inscription transactions. Given the current infrastructure, most public chains are still unable to support the operations of Web3 Super DApps, which is one of the crucial factors constraining the current development of Web3. Paradigm also provided some insightful [data](https://www.paradigm.xyz/2024/04/reth-perf).

## **Three Major Challenges Towards High-Performance Blockchains**

After extensive research and testing, Pharos has identified three significant challenges currently facing high-performance Web3 blockchains:

* **Challenge 1: The "Bottleneck Effect" in Blockchain**

  Throughout the development of programmable blockchains, many developer and researchers have continuously optimized various modules, such as consensus, execution, and storage. However, due to the complexity of blockchain systems, isolated optimizations often fail to address overall performance issues, resulting in a limited improvement in end to end transactions per second (TPS). This phenomenon is what we refer to as the “Bottleneck Effect” in blockchain. This issue is particularly pronounced in traditional sequential execution blockchain networks. For instance, in a simulation of Ethereum’s transaction on a Geth client running on a single node (32GB RAM, 16 cores, 2TB SSD), we observed the following time distribution across different stages under continuous load pressure.\
  \
  \
  \
  Although many Layer 1 and Layer 2 solutions focus on optimizing performance from the perspective of executing transactions (e.g., Parallel EVM), they still face bottlenecks in merklization and database operations, which limit overall blockchain network performance. Consequently, in the past 1-2 years, we have seen blockchains, such as [Avalanche](https://www.avax.network/blog/introducing-firewood-a-next-generation-database-built-for-high-throughput-blockchains), [Monad](https://docs.monad.xyz/monad-arch/execution/monaddb), and [Sei Network](https://blog.sei.io/developers/sei-db-the-numbers/), proposing their own database and merklization solutions.

  <figure><img src="/files/kjA9dpnwmolWUotUpF6H" alt="" width="375"><figcaption></figcaption></figure>
* **Challenge 2: The Need for Decentralized & Large-Scale Networking**

  Blockchain is a decentralized distributed ledger system that must support efficient transaction execution and RPC services while maintaining decentralization and large-scale networking at a low cost to enhance network reliability and asset security. However, high hardware requirements can limit the network scale, reducing its security and degree of decentralization. The chart below shows the current hardware requirements for Validators/Sequencers on some L1 and L2 networks:

  <table data-full-width="true"><thead><tr><th width="219">Blockchain</th><th data-type="number">CPU (cores)</th><th>Memory (GB)</th><th>Network (Gbps)</th><th data-type="number">Storage (TB SSD)</th><th data-type="number">TPS</th><th>Validator</th></tr></thead><tbody><tr><td>Ethereum</td><td>4</td><td>16</td><td>0.025</td><td>2</td><td>30</td><td>1.5 Million</td></tr><tr><td>Solana</td><td>12</td><td>256</td><td>1~10</td><td>2</td><td>65000</td><td>~1,500</td></tr><tr><td>Aptos</td><td>32</td><td>64</td><td>1</td><td>2</td><td>30000</td><td>~150</td></tr><tr><td>Monad</td><td>16</td><td>32</td><td>0.1</td><td>2</td><td>10000</td><td>-</td></tr><tr><td><p>Reth</p><p>(Single Node)</p></td><td>24</td><td>256</td><td>10</td><td>2</td><td>20000</td><td>1</td></tr><tr><td><p>Mega ETH</p><p>(Single Sequencer)</p></td><td>100</td><td><p>1024~</p><p>4096</p></td><td>10</td><td>2</td><td>100000</td><td>1</td></tr></tbody></table>

  In reality, most personal computers and mobile devices already have decent configurations, and some L1 and L2 networks are considering incorporating them into their ecosystems.
* **Challenge 3: State Bloat Problem Hinders Blockchain Development**

  In [***Why We Need a Blockchain-Native Store***](broken://pages/e2SXAx8NuFPnSW3NP1SN), we explore the main bottlenecks facing current blockchain storage systems. With the increase in blockchain users, state bloat has led to challenges such as performance degradation, network scalability limitations, and resource inefficiencies. These issues create significant barriers to the large-scale, production-ready deployment of most L1 and L2 networks.

## **Shortcomings of Existing Solutions**

Pharos, considering the current state of monolithic chains and modular ecosystems, has identified four key shortcomings in the performance optimization of existing blockchains:

* **Insufficient "Parallelism"**

  The "parallelism" in existing blockchains is limited, particularly between the execution and storage layers.

  1. **Execution Layer**: While Parallel EVM technology enhances efficiency, actual parallelism remains below ideal levels due to limitations in optimistic execution algorithms and disk I/O speed. The current Parallel EVM framework also struggles to accommodate the diverse scenarios and execution logic of various DApps, especially AMM-based DEXs.
  2. **Storage Layer**: Most blockchains use traditional key-value stores and Merkle Tree structures, limiting concurrency in both I/O access and merklization. Teams like Reth and Monad have recognized this issue and are exploring async I/O and parallel merklization to overcome these bottlenecks.
* **Verifiable Storage Remains a Bottleneck in Large-Scale Scenarios**

  Most current L1 and L2 solutions still rely on verifiable storage architectures, but their inefficient querying and merklization performance severely limit overall blockchain throughput. The combination of MPT (Merkle Patricia Tree) and LSM Store, for instance, faces three performance issues: **Long I/O Paths, Hash-Based Addressing,** and **State Bloat**. As State Bloat worsens, the efficiency of starting new nodes continues to decline, further impacting network scale and decentralization.
* **Insufficient Utilization of Node Resources**

  The resources of existing blockchain nodes are not being fully utilized. One reason is that, in traditional blockchain models, CPU and I/O resources remain idle for much of the time. Furthermore, on-chain data and index maintenance also waste a significant amount of CPU and I/O resources. Taking the most typical LSM Database as an example, compaction consumes considerable CPU and I/O resources, competing with existing modular components and greatly reducing throughput.\\

  In the [*Why We Need Pipelining* ](broken://pages/H5TMhGlTWyHrvKvMntyG)section, we provide a detailed overview of the resource bottlenecks at each stage of blockchain processing.
* **Limitations of Layer 2 Scaling Solutions**

  While many Layer 2 (L2) solutions (such as ZK/OP Rollup, and Sidechains) have proven effective in improving Layer 1 (L1) scalability, several issues still persist:

  * **Increasing State Bloat in L2:**

    Most L2 sequencers continue to use traditional blockchain node architectures, which do not effectively address the state bloat problem, resulting in significant throughput limitations. Many L2 networks only achieve between tens to a few hundred TPS, as shown in Paradigm's recent performance data for various L2 solutions. Moreover, while decentralized sequencer networks are an aspirational goal for many L2 projects, they encounter technical challenges as complex as those faced by L1 networks. In ZK rollup designs, this problem is often compounded by the use of binary Merkle trees, which further intensify state bloat.
  * **Data and Liquidity Fragmentation:**

    While L2 solutions enhance scalability, they introduce significant delays in inter-network communication. For example, rollups typically require several hours to days for messages to transfer from L2 to L1 during the challenge period, slowing down communication and leading to severe data and liquidity fragmentation. Additionally, L2 networks cannot share assets and account states directly with each other and must rely on third-party bridges for transfers, which introduces both centralization and security risks.

## The Right Path to Improving Blockchain Scalability

In the current blockchain ecosystem, isolated optimizations often fall short of expectations. A holistic approach is necessary, considering consensus, execution, storage, and parallel processing.

Pharos introduces a suite of efficient modular components and parallel solutions, with a sustained focus on research and practical advancements in this field. We aims to deliver high-performance, low-latency, and cost-effective blockchain services, bringing a Web2-like user experience to Web3.


# Encyclopedia

## Deep Dive into Pharos Architecture, Concepts & Core Technologies

The Pharos Encyclopedia is a dedicated knowledge hub designed for developers, researchers, and contributors who want to explore the technical foundations, core concepts, and architecture of the Pharos Network.

While this Developer Documentation focuses on helping you build, integrate, and deploy applications, the Encyclopedia provides a deeper understanding of how Pharos works under the hood — from foundational concepts to protocol-level design.

## What You’ll Find in the Encyclopedia

The Encyclopedia covers in-depth topics across the Pharos stack, including:

* **Core Concepts**\
  Foundational concepts that help you understand how the Pharos ecosystem operates, including key primitives, system design patterns, and terminology.
* **Architecture Overview**\
  A breakdown of Pharos’ layered architecture, including L1 Base, Core, and Extension layers.
* **Consensus & Performance Design**\
  Insights into Pharos’ scalable BFT mechanisms and high-throughput execution model.
* **Core Technologies**\
  Deep dives into components such as:
  * Pharos DTVM
  * Dual VM architecture (EVM + WASM)
  * Special Processing Networks (SPNs)
  * Native restaking and shared security
  * High-performance storage systems
* **Interoperability & Cross-Chain Design**\
  How Pharos enables seamless interaction across ecosystems.
* **Research & Design Papers**\
  Detailed technical explorations and protocol-level innovations.

## When Should You Use the Encyclopedia?

You should explore the Encyclopedia if you want to:

* Understand core concepts and how Pharos works internally
* Dive into protocol-level architecture and design decisions
* Build advanced infrastructure or protocol integrations
* Contribute to the core technology or ecosystem tooling
* Evaluate Pharos for research or system design purposes

## For Builders

If your goal is to build and ship applications, we recommend starting with:

* Getting Started
* Developer Guide & Tutorials
* Tooling & Infrastructure

These sections are designed to help you go from idea to deployment as quickly as possible.

## Explore the Pharos Encyclopedia

Access the full Encyclopedia here:


# Network

## Overview

Pharos provides two primary network environments to help you move from development to production smoothly.

Whether you're experimenting with smart contracts or deploying a live application, each network is designed to support a specific stage of your workflow.

## Available Networks

* **Atlantic Testnet**\
  This is where you build and test. Use it to deploy contracts, try out integrations, and iterate on your application without worrying about real assets.
* **Pacific Mainnet**\
  This is the production network. Once your application is ready, you can deploy to Mainnet and interact with real users, assets, and infrastructure.

## Which One Should You Use?

In most cases:

* Start on **Atlantic Testnet** while developing and testing
* Move to **Pacific Mainnet** when you're ready to go live


# Atlantic Testnet

## Network Information

| Parameter              | Value                                  |
| ---------------------- | -------------------------------------- |
| RPC Public Endpoint    | <https://atlantic.dplabs-internal.com> |
| WSS Public Endpoint    | <wss://atlantic.dplabs-internal.com>   |
| Explorer               | <https://atlantic.pharosscan.xyz/>     |
| ChainID                | 688689                                 |
| Environment            | Atlantic Testnet                       |
| Ratelimit              | 500 times/5m                           |
| Max Pending TXs (Addr) | 64                                     |

> For the current node binary and Docker image versions, see [Current Node Versions](/node-and-validator-guide/validator-node-deployment#current-node-versions).


# Pacific Mainnet

## Network Information

| Parameter          | Value                        |
| ------------------ | ---------------------------- |
| Network            | Pharos Pacific Mainnet       |
| ChainID            | 1672                         |
| Coin Symbol/Ticker | PROS                         |
| RPC URL            | <https://rpc.pharos.xyz>     |
| Block Explorer     | <https://www.pharosscan.xyz> |

> For the current node binary and Docker image versions, see [Current Node Versions](/node-and-validator-guide/validator-node-deployment#current-node-versions).


# Get PROS Gas

PROS is the gas token on Pharos mainnet, you need it in your wallet before you can send transactions or deploy contracts. This page covers the three supported acquisition paths and how to confirm the funds arrived.

## Prerequisites

An EVM wallet such as MetaMask, OKX Wallet or any other with Pharos Mainnet added, **or** the TopNod app if you don't have a wallet yet.

## Choose a path

| You have...                       | Use                                 |
| --------------------------------- | ----------------------------------- |
| Crypto already, in another wallet | Halliday → wallet transfer          |
| Crypto already, on an exchange    | Withdraw directly, network = Pharos |
| Fiat (card/bank) only             | Halliday or Alchemy Pay             |
| No wallet yet                     | TopNod                              |

All the options mentioned can be accessed at [**port.pharos.xyz/get-pros**](https://port.pharos.xyz/get-pros).

### Option 1 — Halliday

The featured option on the page.

1. Click **Connect Wallet**.
2. Toggle **Use crypto** vs. **Use cash** at the top of the widget.
   * **Use crypto** shows your connected address under "Receiving Wallet Address" (confirm it's yours) plus a "Send crypto" panel — pick your source wallet or exchange from the shown icons (MetaMask, Rainbow, Coinbase, WalletConnect, etc.) or scan the QR code, and send PROS from there.
   * **Use cash** lists payment methods, priced in the currency set by the region selector in the top-left of the widget: **Debit card** (marked "Recommended"), then under "More options" — **Apple Pay**, **Google Pay**, **Credit card**, and **Coinbase**. Picking one hands off to that provider's own checkout.
3. Use **+ Add wallet** if you want to receive to a different address than the one currently connected.
4. Confirm the destination address is your own before finalizing anything.

### Option 2 — Alchemy Pay

A card/bank on-ramp. Use the **Alchemy Pay** button on the [Port page](https://port.pharos.xyz/get-pros). It will open a "Buy Crypto" checkout:

1. **You pay** — enter an amount and pick your fiat currency (e.g. USD).
2. **You receive (estimate)** — auto-fills the equivalent in **PROS** on the Pharos network; this is a live quote that refreshes on a short timer (shown as a countdown, e.g. "8 sec") until you proceed.
3. Review **Your order** (e.g. "749.46 PROS for 300 USD"), then click **Proceed · Buy PROS**.
4. Pay via **Visa, Mastercard, Apple Pay, Google Pay, or bank transfer** — the widget is "Powered by Alchemy Pay," so this is the same checkout as going to `ramp.alchemypay.org` directly.
5. Complete KYC if it's your first purchase, then confirm. The destination wallet address comes from whatever address you were connected with on Pharos Port.

### Option 3 — TopNod

Use the **TopNod** button on the Port page.

1. Create a wallet with Apple/Google sign-in, or open your existing one.
2. Use the app's Buy/Deposit function to fund it with PROS.

## Verify you received it

Once a transfer or purchase completes, confirm the balance on-chain rather than trusting a confirmation screen alone. Three ways, in order of convenience:

**1. Block explorer (fastest, no setup)**

Go to [pharosscan.xyz](https://www.pharosscan.xyz) and paste your address into the search bar. Your PROS balance shows on the address page, along with the incoming transaction (from the sender's address, the on-ramp's settlement address, or an exchange withdrawal). This also lets you check the transaction actually confirmed rather than just being pending.

**2. Your wallet**

Open MetaMask / OKX Wallet / TopNod with the Pharos network selected — the balance shown there is read live from the chain. If it shows 0 but the explorer shows a balance, the wallet is likely still on the wrong network or hasn't refreshed; switch networks or reload.

**3. RPC call (for scripting or double-checking a wallet UI)**

```bash
curl https://rpc.pharos.xyz \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": ["<your-address>", "latest"],
    "id": 1
  }'
```

The result is a hex-encoded wei value; divide by `1e18` for PROS.

## Security

* Official domains only: `port.pharos.xyz`, `alchemypay.org`, `topnod.com`.
* No flow here ever needs your seed phrase or private key.
* Always double check the network is Pharos before withdrawing from an exchange.

For network parameters, token contract addresses, and wallet setup, see [Pacific Mainnet](https://docs.pharos.xyz/getting-started/network/pacific-mainnet), the [Token Registry](https://docs.pharos.xyz/getting-started/token-registry), and [Wallets](https://docs.pharos.xyz/tooling-and-infrastructure/wallets) in the main docs.


# Canonical Contracts

## Atlantic Testnet Canonical Contracts

| Contract Name                | Description                                           | Address                                    |
| ---------------------------- | ----------------------------------------------------- | ------------------------------------------ |
| Create2Deployer              | Helper for CREATE2 opcode usage                       | 0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2 |
| DeterministicDeploymentProxy | Integrated with Foundry for deterministic deployments | 0x4e59b44847b379578588920ca78fbf26c0b4956c |
| MultiCall3                   | Allows bundling multiple transactions                 | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| GnosisSafe (v1.3.0)          | Multisignature wallet                                 | 0x69f4D1788e39c87893C980c06EdF4b7f686e2938 |
| GnosisSafeL2 (v1.3.0)        | Events-based implementation of GnosisSafe             | 0xfb1bffC9d739B8D520DaF37dF666da4C687191EA |
| MultiSendCallOnly (v1.3.0)   | Batches multiple transactions (calls only)            | 0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B |
| MultiSend (v1.3.0)           | Batches multiple transactions                         | 0x998739BFdAAdde7C933B942a68053933098f9EDa |
| Permit2                      | Next-generation token approval system                 | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| EntryPoint (v0.7.0)          | ERC-4337 entry point for account abstraction          | 0x0000000071727De22E5E9d8BAf0edAc6f37da032 |
| SenderCreator (v0.7.0)       | Helper for EntryPoint                                 | 0xEFC2c1444eBCC4Db75e7613d20C6a62fF67A167C |

## Pacific Mainnet Canonical Contracts

| Contract Name                 | Description                                                                     | Address                                    |
| ----------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------ |
| Create2Deployer               | Helper for CREATE2 opcode usage                                                 | 0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2 |
| Foundry Deterministic Deploy  | Integrated with Foundry for deterministic deployments                           | 0x4e59b44847b379578588920ca78fbf26c0b4956c |
| MultiCall3                    | Allows bundling multiple transactions                                           | 0xcA11bde05977b3631167028862bE2a173976CA11 |
| GnosisSafe (v1.3.0)           | Multisignature wallet                                                           | 0x69f4D1788e39c87893C980c06EdF4b7f686e2938 |
| GnosisSafeL2 (v1.3.0)         | Events-based implementation of GnosisSafe                                       | 0xfb1bffC9d739B8D520DaF37dF666da4C687191EA |
| SafeSingletonFactory          | Safe's deterministic deployment proxy                                           | 0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7 |
| CreateX                       | Advanced cross-chain deployment factory supporting CREATE, CREATE2, and CREATE3 | 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed |
| MultiSendCallOnly (v1.3.0)    | Batches multiple transactions (calls only)                                      | 0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B |
| MultiSend (v1.3.0)            | Batches multiple transactions                                                   | 0x998739BFdAAdde7C933B942a68053933098f9EDa |
| Permit2                       | Next-generation token approval system                                           | 0x000000000022D473030F116dDEE9F6B43aC78BA3 |
| ERC-4337 EntryPoint (v0.7)    | ERC-4337 entry point for account abstraction                                    | 0x0000000071727De22E5E9d8BAf0edAc6f37da032 |
| ERC-4337 SenderCreator (v0.7) | Helper for EntryPoint                                                           | 0xEFC2c1444eBCC4Db75e7613d20C6a62fF67A167C |
| ERC-4337 EntryPoint (v0.6)    | Account abstraction entry point (v0.6); validates and executes UserOperations   | 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 |
| ERC-4337 SenderCreator (v0.6) | Helper contract used by EntryPoint v0.6 to deploy smart accounts via initCode   | 0x7fc98430eAEdbb6070B35B39D798725049088348 |


# Token Registry

## Atlantic Testnet Token Registry

<table><thead><tr><th width="119.734375">Symbol</th><th width="150.43359375">Name</th><th width="120.29296875">initsupply</th><th width="119.98828125">Decimals</th><th>Address</th></tr></thead><tbody><tr><td>USDC</td><td>USD Coin</td><td>0</td><td>6</td><td>0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B</td></tr><tr><td>USDT</td><td>Tether USD</td><td>0</td><td>6</td><td>0xE7E84B8B4f39C507499c40B4ac199B050e2882d5</td></tr><tr><td>WBTC</td><td>Wrapped BTC</td><td>0</td><td>18</td><td>0x0c64F03EEa5c30946D5c55B4b532D08ad74638a4</td></tr><tr><td>WETH</td><td>Wrapped ETH</td><td>0</td><td>18</td><td>0x7d211F77525ea39A0592794f793cC1036eEaccD5</td></tr><tr><td>WPHRS</td><td>Wrapped PHRS</td><td>0</td><td>18</td><td>0x838800b758277CC111B2d48Ab01e5E164f8E9471</td></tr></tbody></table>

## Pacific Mainnet Token Registry

| Symbol                 | Name            | Pharos Address                             | Ethereum Address                           | Base Address                               |
| ---------------------- | --------------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ |
| WPROS                  | Wrapped PROS    | 0x52c48d4213107b20bc583832b0d951fb9ca8f0b0 | 0xB197E02499e6502733C6bCE2eb39013C39A03147 | 0x8B7DdE054BE9D180c1Be7FaE0874697374A49832 |
| USDC (Circle Deployed) | USDC            | 0xc879c018db60520f4355c26ed1a6d572cdac1815 |                                            |                                            |
| LINK                   | Chainlink Token | 0x51e2A24742Db77604B881d6781Ee16B5b8fcBE29 | 0x514910771AF9Ca656af840dff83E8264EcF986CA |                                            |
| WETH                   | Wrapped ETH     | 0x1f4b7011Ee3d53969bb67F59428a9ec0477856E9 |                                            |                                            |


# Gas Model

Understanding Gas Refund and Gas Limit in Pharos Transactions

## Overview

When sending a transaction on Ethereum or an EVM-compatible blockchain, it is essential to set an appropriate gas limit to ensure successful execution. One common issue arises when the gas limit is set exactly equal to the gas used, particularly when the transaction involves a gas refund mechanism.

## Summary Table

<table data-header-hidden><thead><tr><th width="219.41015625"></th><th></th></tr></thead><tbody><tr><td><strong>Feature</strong></td><td><strong>Details</strong></td></tr><tr><td>Opcode pricing</td><td>Fully aligned with Ethereum EVM opcode <a href="https://www.evm.codes/">gas table</a></td></tr><tr><td>EIP-1559 support</td><td>✅ Compatible (base fee + priority fee)</td></tr><tr><td>Base fee</td><td>Dynamic (recalculated per epoch)</td></tr><tr><td>Transaction fee model</td><td>Charged by gas_limit at inclusion time</td></tr><tr><td>Fee distribution</td><td>Base fee is burned; priority fee is credited to the validator</td></tr><tr><td>Refund logic</td><td>Full EVM-style refund tracking supported, but refund does not affect charge</td></tr></tbody></table>

## Transaction Fee Distribution

Following the EIP-1559 model, every transaction fee on Pharos is split into two components:

* **Base Fee** — the base fee is burned directly, permanently removing the corresponding tokens from circulation.
* **Priority Fee** — the priority fee (tip) is credited to the validator that produces the block, as a reward for including the transaction.

This fee-distribution mechanism is already implemented on Pharos.

### Difference from Ethereum

On Ethereum, the priority fee is credited to the block producer's address immediately after each transaction is executed. Pharos differs because of two core design choices:

* **Parallel execution & pipelined architecture** — to maximize performance, transactions are executed in parallel rather than sequentially.
* **Multi Concurrent Proposer (MCP) model** — multiple proposers can contribute to a single block.

Because transactions execute in parallel, priority fees cannot be settled at the granularity of individual transactions in real time. Instead, they are accumulated and credited to the corresponding validators in a batch at epoch boundaries.

## Best Practices for Developers

* **Set a slightly higher gas limit:** Allow extra gas beyond the expected execution cost.
  * Example: If you expect 100,000 gas used before refund, set the gas limit to 120,000 to avoid failures.
* **Use Estimation APIs:**
  * When sending transactions via Web3 libraries (`ethers.js`, `web3.js`), always use `estimateGas()` and add a buffer.
  * Example (`ethers.js`):

{% code fullWidth="false" %}

```javascript
const estimatedGas = await contract.estimateGas.someFunction();
const gasLimit = estimatedGas.mul(12).div(10); // Adding a 20% buffer
await contract.someFunction({ gasLimit });
```

{% endcode %}

* **Monitor Gas Refund Logic:**
  * If your contract relies heavily on gas refunds (e.g., clearing storage or `SELFDESTRUCT`), test transactions with different gas limits to find an optimal value.
* **Check for Out-of-Gas Failures:**
  * If a transaction fails unexpectedly despite having enough gas, check whether gas refunds are affecting execution.
  * Debug with `debug_traceTransaction` or EVM logs.

## Conclusion

To prevent transactions from failing due to gas refund issues, always set a gas limit slightly higher than the expected gas usage. This ensures smooth execution and prevents out-of-gas errors caused by refund mechanisms.

By following these best practices, developers can avoid failed transactions and improve the reliability of smart contract interactions.


# FAQs

## Problem Explanation

Ethereum's execution model deducts the full gas limit at the start of execution and refunds a portion of the gas at the end if applicable. However, if the gas limit is set too tightly (equal to the actual gas used before refunds), the refund cannot be processed, and the transaction fails with an out-of-gas error.

### Key reasons why this happens:

* Gas is initially reserved: The EVM deducts the full gas limit at the beginning of execution.
* Gas refund happens at the end: If a contract includes operations that delete storage variables (e.g., `SELFDESTRUCT`, `SSTORE` clearing a slot), the gas refund is only applied after execution completes.
* If gas runs out before refunding, the transaction fails because the EVM does not get a chance to apply the refund.

### Example Scenario

Below is an example of a Uniswap V3 transaction deployed on Pharos Devnet, where the actual gas used was 4,618,680:\
<https://pharosscan.xyz/tx/0x205a123820ad75de162ba3cabe06183d34377e239c3a8f6f18636d4c87b3a524>

<figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdlS2EDw_PPNgBSFYdTOVA5JzeQhlRYrIW2Lu16Gpqyi0SfU7Ezqg9sqm0VFjp29kWSxMPBBBVUfZSq_nARMBzcdzoRg4Bm8t8XCLik9g064UrpKap8jvmCLj2r4LgZCdIkozyD?key=77gsDOQDxHb7PI4vhmETXLk-" alt=""><figcaption></figcaption></figure>

However, since the gas limit was set exactly equal to the gas used, the transaction failed.

To verify this behavior, we deployed the same transaction on Ethereum Sepolia Testnet, and observed the same failure due to the gas limit issue:\
<https://sepolia.etherscan.io/tx/0x55af4ffe714ad075c6f979e7f89670e1498647abedd6ea7112e1c6339cef64aa>

<figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcaYqtP6uujRgownbFTRVNX8cVBy-vSrNYSPlKcnHIFPEvWwdcMf66bG4wEZC5z4nFfNQBc1j2GWJaYO0PQIqw0ZMixxRLPNfxxbmyw_Fkhy7KBX4_9aHoSUw8k8VHqhMwP7c8IGw?key=77gsDOQDxHb7PI4vhmETXLk-" alt=""><figcaption></figcaption></figure>

To ensure the transaction executes successfully, we need to set the gas limit slightly higher than the expected gas usage.\
<https://pharosscan.xyz/tx/0x6ccf7f593f5783414b3726e147319e74046526d800b325110bdf81ac3012aa5e>

<figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc-e8YrFFvEnvTLIr6JaqRBpA_grGLy4Uj4tgLKeFdpop40cqv0QFVT-vOsgIp2RqNQXH-zByGNlipf5iO_z24G6hQhr4WfrAMdcfsfFtjtvbS58xlQrLtebaeyGkz1bhRqCKuZ5g?key=77gsDOQDxHb7PI4vhmETXLk-" alt=""><figcaption></figcaption></figure>


# Validator Accountability

Validators are expected to actively and honestly participate in the consensus process. There is currently no automated slashing mechanism. However, if malicious behavior is detected, governance procedures may be initiated to penalize the offending validator.

The following behaviors are considered violations:

* Prolonged offline: The validator node is unreachable or unresponsive for an extended period.
* Consensus inactivity: Failing to propose blocks or submit consensus votes when expected.
* Equivocation: Casting conflicting or duplicate votes for the same consensus round (double voting). This is treated as intentional misbehavior and may result in governance action.

Validators who engage in any of the above will see their consensus participation degraded. As a result:

* Their block proposals may be rejected by the network.
* They will lose eligibility for transaction fee rewards associated with proposed blocks.

Validator rewards are tied to consensus participation. Validators that maintain consistent uptime and submit timely, correct votes are eligible to propose blocks and receive the associated transaction fees.


# Validator Requirements

To ensure a validator operates effectively and efficiently, the minimum recommended hardware specifications are:

<table><thead><tr><th width="199.58203125">Component</th><th>Specification</th></tr></thead><tbody><tr><td><strong>CPU</strong></td><td>32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum</td></tr><tr><td><strong>Memory</strong></td><td>256 GB</td></tr><tr><td><strong>Storage</strong></td><td>5 TB SSD with at least 350MiB/s bandwidth and 30000 IOPS</td></tr><tr><td><strong>Network Bandwidth</strong></td><td>0.5 Gbps</td></tr><tr><td><strong>Open Files Limit</strong></td><td>ulimit -n ≥ 10000000</td></tr></tbody></table>


# Validator Node Deployment

## Deploying Pharos Nodes

Before running a Validator Node or Validator Full Node (VFN) on the Pharos Network, you must choose a deployment method and ensure your machine meets the required specifications.

This guide provides clear instructions for deploying nodes using different methods. Once deployed, you can connect your node to the Pharos network and begin participating in consensus or network operations.

> 📘 Note: Please review the Node Requirements before choosing a deployment method.

## Current Node Versions

| Network          | Spec Version | Binary Version | Docker Image                                                            | Binary Package                                                                                                                        |
| ---------------- | ------------ | -------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Atlantic Testnet | 15           | 05cffaf0       | `public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.14.4_05cffaf0_0729` | [pharos-v0.14.4-05cffaf0.tar.gz](https://github.com/PharosNetwork/resources/releases/download/v0.14.4/pharos-v0.14.4-05cffaf0.tar.gz) |
| Pacific Mainnet  | 15           | 0f7751e5       | `public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.15.0_0f7751e5_0806` | [pharos-v0.15.0-0f7751e5.tar.gz](https://github.com/PharosNetwork/resources/releases/download/v0.15.0/pharos-v0.15.0-0f7751e5.tar.gz) |

## Deployment Methods

Choose one of the following deployment methods to set up your node:

### 🐳 Using Docker

* [Atlantic](https://github.com/PharosNetwork/docs/tree/main/node-and-validator-guide/validator-node-deployment/using-docker-atlantic.md)

### 🔧 Using Source Code

* [Atlantic](https://github.com/PharosNetwork/docs/tree/main/node-and-validator-guide/validator-node-deployment/using-binary-atlantic.md)


# Using Docker

This guide describes how to deploy a Pharos node using Docker and Docker Compose.

> 📌 For the latest Docker image tag, see [Current Node Versions](/node-and-validator-guide/validator-node-deployment#current-node-versions).

## **Prerequisites**

| Component         | Specification                                                     |
| ----------------- | ----------------------------------------------------------------- |
| CPU               | 32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum |
| Memory            | 256 GB                                                            |
| Storage           | 5 TB SSD with at least 350MiB/s bandwidth and 30000 IOPS          |
| Network Bandwidth | 0.5 Gbps                                                          |
| Open Files Limit  | ulimit -n ≥ 10000000                                              |

Additionally, ensure Docker is installed:

* Docker Engine 20.10+
* Docker Compose 2.0+

## **1. Create working directory**

```bash
export WORKSPACE=pharos
mkdir -p /data/$WORKSPACE && cd /data/$WORKSPACE
```

## **2. Download configuration files**

Download the genesis configuration and VERSION file based on your network:

**For Atlantic Testnet:**

```bash
mkdir -p bin
wget -O genesis.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/atlantic.genesis
wget -O bin/VERSION https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/atlantic.version
```

**For Mainnet:**

```bash
mkdir -p bin
wget -O genesis.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/mainnet.genesis
wget -O bin/VERSION https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/mainnet.version
```

> **Note:** When upgrading to a new version, you must also re-download the VERSION file to keep it in sync.

Download the node configuration file (choose one based on your node type):

**For Archive/Full/Validator Node:**

```bash
wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/full.conf
```

> **Note:** Pruning is disabled by default. To enable pruning, see [Enable Pruning in Pharos Node](/enable-pruning-in-pharos-node).

**For TraceDB Node:**

```bash
wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/traceDB.conf
```

## **3. Create docker-compose.yml**

Create the Docker Compose configuration file:

```bash
cat > docker-compose.yml <<EOF
services:
  pharos:
    image: public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.15.0_0f7751e5_0806
    container_name: pharos-node
    environment:
      - CONSENSUS_KEY_PWD=YOUR_PASSWORD_HERE  # Change this to your password
      - PHAROS_CONF=/data/pharos.conf
      - GENESIS_CONF=/data/genesis.conf
      - KEYS_DIR=/data/keys
    volumes:
      # Mount data directory
      - /data/$WORKSPACE:/data
    ports:
      - "18100:18100"  # HTTP RPC
      - "18200:18200"  # WebSocket
      - "19000:19000"  # P2P TCP
      - "20000:20000"  # RPC
    restart: unless-stopped
    ulimits:
      nofile:
        soft: 10000000
        hard: 10000000
    healthcheck:
      test: ["CMD", "curl", "-sf", "-X", "POST", "-H", "Content-Type: application/json", "-d", "{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}", "http://localhost:18100"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
EOF
```

**Note:** The image uses a specific version tag (`pharos_community_v0.15.0_0f7751e5_0806`).

## **4. Set password in docker-compose.yml**

Edit `docker-compose.yml` and set your password:

```bash
vim docker-compose.yml
```

Change this line:

```yaml
- CONSENSUS_KEY_PWD=YOUR_PASSWORD_HERE
```

To your actual password:

```yaml
- CONSENSUS_KEY_PWD=your_secure_password
```

## **5. Verify required files**

Before starting the container, verify all required files are present:

```bash
tree .
```

You should see:

```
.
├── bin
│   └── VERSION
├── docker-compose.yml
├── genesis.conf
└── pharos.conf
```

**Required files:**

* `genesis.conf` - Genesis configuration
* `pharos.conf` - Node configuration
* `docker-compose.yml` - Docker Compose configuration with your password
* `bin/VERSION` - Network version information

**Missing files?** Go back to steps 2-4 to download and configure them.

## **6. Start the node**

Start the Pharos node with Docker Compose:

```bash
docker compose up -d
```

## **7. Check node status**

View logs:

```bash
docker compose logs -f pharos
```

Check if the node is running:

```bash
docker compose ps
```

You should see:

```
NAME          IMAGE                                                                         STATUS         PORTS
pharos-node   public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.15.0_0f7751e5_0806         Up 2 minutes   0.0.0.0:18100->18100/tcp, ...
```

Check node health:

```bash
curl http://localhost:18100
```

## **Managing the node**

### Stop the node

```bash
docker compose stop
```

### Restart the node

```bash
docker compose restart
```

### Stop and remove the container

```bash
docker compose down
```

**Note:** This does not delete your data. Data is persisted in `/data/pharos/`.

### View real-time logs

```bash
docker compose logs -f
```

### Execute commands in the container

```bash
docker compose exec pharos bash
```

## **Directory Structure**

After deployment, your directory structure will look like:

```
/data/pharos/
├── bin/                   # Auto-copied from image on each start
│   ├── pharos_light
│   ├── pharos_cli
│   └── VERSION
├── ops                    # Auto-copied from image on each start
├── keys/                  # Auto-generated on first start
│   ├── domain.key
│   ├── domain.pub
│   ├── stabilizing.key
│   └── stabilizing.pub
├── data/                  # Created after bootstrap
│   └── meta_store/
├── log/
│   ├── aldaba.log
│   ├── aldaba_cli.log
│   └── pamir.log
├── genesis.conf           # Downloaded in step 2
├── pharos.conf            # Downloaded in step 2
└── docker-compose.yml     # Created in step 3, edited in step 4
```

## **Upgrading the node**

To upgrade to a new version, update the image tag in `docker-compose.yml` to the new version, then:

```bash
# Pull the new image
docker compose pull

# Restart with new image
docker compose up -d
```

## **Security Recommendations**

1. **Use strong passwords**: Set a secure password for `CONSENSUS_KEY_PWD`
2. **Protect your keys**: Ensure `/data/pharos/keys/` has proper permissions
3. **Firewall configuration**: Only expose necessary ports
4. **Regular backups**: Backup `/data/pharos/keys/` and `pharos.conf`
5. **Keep updated**: Regularly pull and update to the latest image
6. **Monitor logs**: Regularly check logs for errors or warnings

## **Next Steps**

* [Node Monitoring Setup](/node-and-validator-guide/node-monitoring-setup) - Configure Prometheus metrics for your node


# Using Binary

This guide describes how to deploy a Pharos node using pre-compiled binaries and the ops tool.

> 📌 For the latest binary package, see [Current Node Versions](/node-and-validator-guide/validator-node-deployment#current-node-versions).

## **Prerequisites**

| Component         | Specification                                                     |
| ----------------- | ----------------------------------------------------------------- |
| CPU               | 32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum |
| Memory            | 256 GB                                                            |
| Storage           | 5 TB SSD with at least 350MiB/s bandwidth and 30000 IOPS          |
| Network Bandwidth | 0.5 Gbps                                                          |
| Open Files Limit  | ulimit -n ≥ 10000000                                              |

## **1. Create working directory and download binary package**

```bash
export WORKSPACE=pharos
mkdir -p /data/$WORKSPACE && cd /data/$WORKSPACE
```

Download the latest binary release from GitHub:

```bash
wget https://github.com/PharosNetwork/resources/releases/download/v0.15.0/pharos-v0.15.0-0f7751e5.tar.gz
tar -zxvf pharos-v0.15.0-0f7751e5.tar.gz
```

The extracted package contains:

* `bin/pharos_light` - Pharos node binary
* `bin/pharos_cli` - Pharos CLI tool

## **2. Download configuration files**

Download the genesis configuration and VERSION file based on your network:

**For Atlantic Testnet:**

```bash
wget -O genesis.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/atlantic.genesis
wget -O bin/VERSION https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/atlantic.version
```

**For Mainnet:**

```bash
wget -O genesis.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/mainnet.genesis
wget -O bin/VERSION https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/mainnet.version
```

> **Note:** When upgrading to a new version, you must also re-download the VERSION file to keep it in sync.

Download the node configuration file (choose one based on your node type):

**For Archive/Full/Validator Node:**

```bash
wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/full.conf
```

> **Note:** Pruning is disabled by default. To enable pruning, see [Enable Pruning in Pharos Node](/enable-pruning-in-pharos-node).

**For TraceDB Node:**

```bash
wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/traceDB.conf
```

## **3. Download and setup ops tool**

Download the latest ops tool for Linux:

```bash
wget https://github.com/PharosNetwork/ops/releases/latest/download/ops-linux-amd64 -O ops
chmod +x ops
```

## **4. Set password for key encryption**

Set a password that will be used to encrypt your node's cryptographic keys:

```bash
./ops set-password YOUR_SECURE_PASSWORD
```

**Important:**

* Remember this password - you'll need it to start your node
* The password is stored in `./.password` file
* Keep this password secure and backed up

## **5. Generate cryptographic keys**

Generate the domain and stabilizing keys for your node:

```bash
./ops generate-keys
```

This will create the following files in the `./keys/` directory:

* `domain.key` - ECDSA private key (prime256v1)
* `domain.pub` - ECDSA public key
* `stabilizing.key` - BLS12-381 private key
* `stabilizing.pub` - BLS12-381 public key

## **6. Bootstrap the node**

Initialize the node with genesis state:

```bash
./ops bootstrap --config ./pharos.conf
```

This command will:

* Create the storage database
* Initialize the genesis block
* Prepare the node for first startup

## **7. Start the node**

Start the Pharos node service:

```bash
./ops start --config ./pharos.conf
```

The node will start in daemon mode and begin syncing with the network.

Check the logs to verify the node is running:

```bash
tail -f log/aldaba.log
```

## **8. (Optional) Setup systemd service**

For production deployments, it's recommended to manage the Pharos node with systemd.

Create a systemd service file:

```bash
sudo tee /etc/systemd/system/pharos.service > /dev/null <<EOF
[Unit]
Description=Pharos Node
After=network.target

[Service]
Type=simple
User=$USER
WorkingDirectory=/data/$WORKSPACE/bin
Environment="CONSENSUS_KEY_PWD=$(cat /data/$WORKSPACE/.password)"
Environment="PORTAL_SSL_PWD=$(cat /data/$WORKSPACE/.password)"
ExecStart=/data/$WORKSPACE/bin/pharos_light -c /data/$WORKSPACE/pharos.conf
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

# Resource limits
LimitNOFILE=10000000

[Install]
WantedBy=multi-user.target
EOF
```

Enable and start the service:

```bash
sudo systemctl daemon-reload
sudo systemctl enable pharos
sudo systemctl start pharos
```

Check service status:

```bash
sudo systemctl status pharos
```

View logs:

```bash
sudo journalctl -u pharos -f
```

**Note:** If not using systemd, you can manually stop the node with `pkill pharos_light` and restart with `./ops start --config ./pharos.conf`

## **Directory Structure**

After deployment, your directory structure should look like:

```
/data/pharos/
├── bin/
│   ├── pharos_light
│   ├── pharos_cli
│   └── VERSION
├── keys/
│   ├── domain.key
│   ├── domain.pub
│   ├── stabilizing.key
│   └── stabilizing.pub
├── data/
│   └── (blockchain data)
├── log/
│   ├── aldaba.log
│   ├── pamir.log
│   └── (other logs)
├── genesis.conf
├── pharos.conf
├── ops
└── .password
```

## **9. (Optional) Register as Validator**

If you want to run a validator node, you need to register your node on-chain after it has synced with the network.

### Set up your validator private key

The validator registration requires an Ethereum-compatible private key that holds sufficient tokens for staking. Set it as an environment variable:

```bash
export VALIDATOR_PRIVATE_KEY=your_private_key_hex
```

> **Security Note:** Do not pass the private key via command-line arguments. Always use the environment variable.

### Register the validator

```bash
./ops add-validator \
  --rpc-endpoint http://127.0.0.1:18100 \
  --domain-label "My Validator Node" \
  --domain-endpoint "your-node-public-endpoint:20000" \
  --domain-pubkey ./keys/domain.pub \
  --stabilizing-pubkey ./keys/stabilizing.pub \
  --stake 1000000
```

Parameters:

* `--rpc-endpoint` - RPC endpoint of a synced Pharos node (default: `http://127.0.0.1:18100`)
* `--domain-label` - A description for your validator
* `--domain-endpoint` - The public-facing endpoint of your node for P2P communication
* `--domain-pubkey` - Path to the domain public key file generated in Step 5
* `--stabilizing-pubkey` - Path to the stabilizing public key file generated in Step 5
* `--stake` - Stake amount in tokens (default: 1,000,000 tokens)

On success, the command will output the transaction hash and your Node ID.

### Exit the validator (if needed)

To voluntarily exit your validator from the network:

```bash
export VALIDATOR_PRIVATE_KEY=your_private_key_hex

./ops exit-validator \
  --rpc-endpoint http://127.0.0.1:18100 \
  --domain-pubkey ./keys/domain.pub
```

## **Security Recommendations**

1. **Protect your keys**: The files in `keys/` directory contain your node's private keys
2. **Secure your password**: Keep the `.password` file secure with proper permissions
3. **Backup important files**: Backup `keys/`, `.password`, and `pharos.conf`
4. **Firewall configuration**: Ensure required ports are open (18100, 19000, 20000)
5. **Regular updates**: Keep your node software up to date with latest releases

## **Next Steps**

* [Node Monitoring Setup](/node-and-validator-guide/node-monitoring-setup) - Configure Prometheus metrics for your node
* To register as a validator, see Step 9 above


# Node Configuration

This document provides detailed configuration guidance for Pharos nodes, including node types, configuration files, RPC tuning, and node pool scheduling strategies.

## Node Types

Pharos supports the following node types:

| Node Type           | Description                                                                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| **Validator Node**  | Joins the consensus network and participates in block production through consensus.                                                         |
| **Fullnode**        | Similar to validator node, but not included in the consensus node set. Serves as a regular RPC node or as a failover backup for validators. |
| **Full Cache Node** | A fullnode with an additional flat state database to accelerate state data queries.                                                         |
| **Tracedb Node**    | A fullnode with an additional trace database to accelerate trace-related request responses.                                                 |

***

## Node Configuration (pharos.conf)

### Base Configuration

All node types share the same base configuration. Pre-built configuration files are available:

* **Fullnode / Validator**: [full.conf](https://raw.githubusercontent.com/PharosNetwork/resources/main/conf/full.conf)
* **Full Cache Node**: [full-cache-node.conf](https://raw.githubusercontent.com/PharosNetwork/resources/main/conf/full-cache-node.conf)
* **Tracedb Node**: [traceDB.conf](https://raw.githubusercontent.com/PharosNetwork/resources/main/conf/traceDB.conf)

The base configuration for fullnode/validator:

```json
{
  "chain": {
    "secret_config": {
      "domain_key_path": "../keys/domain.key",
      "stabilizing_key_path": "../keys/stabilizing.key"
    },
    "startup_config": {
      "init_config": {
        "meta_path": "../data",
        "host_ip": "127.0.0.1",
        "http_port": "18100",
        "rpc_port": "20000",
        "tcp_port": "19000",
        "ws_port": "18200"
      }
    }
  },
  "storage": {
    "mygrid_conf": {},
    "mygrid_env": {
      "storage_data_path": "../data"
    }
  }
}
```

### Enabling Full Cache Node (Flat State KV DB)

To accelerate state data queries (e.g., `getBalance`, `getCode`, `getStorageAt`), enable the flat state KV database on top of the base configuration:

```json
{
  "storage": {
    "mygrid_conf": {
      "config": {
        "mygrid": {
          "expert_flags": {
            "mygrid_client_enable_full_state_kv_db": true
          }
        }
      }
    }
  }
}
```

This adds a secondary key-value index alongside the Merkle state database, providing O(1) lookups for account and storage data across all historical blocks. Recommended for RPC nodes serving high-volume state queries.

### Enabling Tracedb Node

To support `debug_traceTransaction`, `debug_traceBlockByHash`, `debug_traceBlockByNumber`, and `trace_filter`, enable trace persistence on top of the base configuration:

```json
{
  "chain": {
    "startup_config": {
      "parameters": {
        "/GlobalFlag/enable_persist_trace": "true"
      }
    }
  }
}
```

This persists transaction execution traces to a dedicated database, enabling trace replay without re-executing transactions. Recommended for nodes serving debug/trace API requests.

### Combining Full Cache + Tracedb

Both features can be enabled simultaneously for maximum query capability:

```json
{
  "chain": {
    "startup_config": {
      "parameters": {
        "/GlobalFlag/enable_persist_trace": "true"
      }
    }
  },
  "storage": {
    "mygrid_conf": {
      "config": {
        "mygrid": {
          "expert_flags": {
            "mygrid_client_enable_full_state_kv_db": true
          }
        }
      }
    }
  }
}
```

> **Note:** Enabling both features increases disk usage significantly. Plan for additional storage capacity beyond the base 5 TB requirement.

***

## RPC Node Tuning

Since RPC nodes primarily handle state queries, block queries, and simulated transaction execution, they require strong JSON-RPC layer processing capabilities. The following configurations can be used to tune the JSON-RPC layer processing logic.

### Thread Counts and Queue Sizes

| Configuration Item              | Default Value | Purpose                                                               | Impact on RPC Node Performance                                                      |
| ------------------------------- | ------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `cache_query_worker_count`      | 16            | Handles `getBlock*`/`getTx*`/`getReceipt*` and other cache queries    | **High**: Insufficient workers cause queuing and increased latency                  |
| `cache_query_worker_queue_size` | 1000          | Pending queue for above workers                                       | Queue full at high QPS will reject or block                                         |
| `db_query_worker_count`         | 4             | Handles `getBalance`/`getCode`/`getStorageAt` and other state queries | **High**: Easy to become bottleneck when state queries are frequent                 |
| `db_query_worker_queue_size`    | 1000          | Queue for above                                                       | Can increase appropriately                                                          |
| `heavy_task_worker_count`       | 4             | Handles `getLogs`, `feeHistory`                                       | **Medium**: `getLogs` is time-consuming, need more workers when concurrent          |
| `heavy_task_worker_queue_size`  | 1000          | Queue for above                                                       | Can increase when heavy tasks are frequent                                          |
| `eth_default_worker_count`      | 4             | `blockNumber`, `syncing`, `chainId`, `gasPrice`, `subscribe`, etc.    | **Medium**: Can slightly increase when connection count is high                     |
| `sim_tx_worker_count`           | 16            | `eth_call`, `estimateGas`, `createAccessList`                         | **Medium-High**: Easy to become bottleneck when dApps have many `eth_call` requests |
| `raw_tx_worker_count`           | 10            | `sendRawTransaction`                                                  | **Low**: Can reduce if node doesn't primarily receive transactions                  |

### Session Management

| Configuration Item              | Default Value | Purpose                             | Impact                                                           |
| ------------------------------- | ------------- | ----------------------------------- | ---------------------------------------------------------------- |
| `max_session`                   | 102400        | Maximum session count               | Needs to be large enough for multi-client scenarios              |
| `max_batch_request_size`        | 20            | Single batch JSON-RPC request count | Increasing can reduce connections and round-trips                |
| `enable_http_long_connection`   | true          | HTTP long connection                | Recommend keeping `true` to reduce connection overhead           |
| `session_idle_timeout_for_http` | 400000        | HTTP session idle timeout (ms)      | Reduces invalid connections                                      |
| `session_idle_timeout_for_ws`   | 360000        | WebSocket session idle timeout (ms) | Reduces invalid connections                                      |
| `socket_thread_count`           | 16            | Accept connections, read socket     | Insufficient when connection count is high will limit throughput |

### Block Cache

| Configuration Item | Default Value | Purpose                                                 | Impact                                                                                                                          |
| ------------------ | ------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `cache_depth`      | 2048          | Number of recent blocks cached (header/body/tx/receipt) | `getBlock*`/`getTx*`/`getReceipt*` cache hit avoids disk read, increasing can significantly improve hit rate and reduce latency |

***

## Node Pool JSON-RPC API Scheduling Strategy

Recommendations for node pool `eth` JSON-RPC API scheduling:

### By Node Type

| Node Type          | Scheduling Rule                                                                                                                                                                               |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Tracedb Node**   | `trace_filter` can **only** be served by tracedb node. `debug_traceBlockByHash`, `debug_traceBlockByNumber`, `debug_traceTransaction` should be **preferentially** scheduled to tracedb node. |
| **RPC Node**       | Interfaces other than trace/debug can be scheduled to regular RPC nodes.                                                                                                                      |
| **Fullnode**       | Can be scheduled as a regular RPC node.                                                                                                                                                       |
| **Validator Node** | **Do not** schedule any JSON-RPC requests, only handle transactions forwarded from other RPC nodes.                                                                                           |

### By Data Availability

When fullnode enables state data pruning and only retains `(latest - pruning_window, latest]`, please note the state data availability range and request range in scheduling strategy.

### By Interface Affinity

Affinity-related requests need to be scheduled to the **same node**:

* **Transaction submission group**: `eth_sendRawTransaction` and related queries `eth_getTransactionCount`, `eth_getTransactionByHash`, `eth_estimateGas`/`eth_call`, especially pending state-based queries.
* **Filter group**: `eth_newFilter`, `eth_newBlockFilter`, `eth_newPendingTransactionFilter` for monitoring and event retrieval `eth_getFilterChanges`. (Pharos currently uses `eth_subscribe` instead of filter interfaces.)


# Node Management

### Node Initialization

Pharos Full Nodes automatically generate cryptographic keys on startup — no manual configuration is needed. To start a validator node, run the following command in your working directory:

```sh
docker start atlantic-testnet
```

### Entering the container

```sh
docker exec -it atlantic-testnet /bin/bash
```

### Join validator sets

If your full node has synced up with the latest block, you can convert it to a validator.

If you need to join Pharos Atlantic as a Validator, please contact and send domain file and pubkeys to <janesh@dplabs.xyz> or TG ([@janesh\_dani](https://t.me/janesh_dani/))

⚠️ Note: If more than one-third of validators go offline, the Atlantic may halt due to consensus safety constraints.

⚠️ To join the network, the corresponding account needs to stake a certain amount of tokens. Please contact us to obtain the staking tokens.

* Pack needed files into `node_info.tar.gz`
  * <mark style="color:green;">`tar -zcvf node_info.tar.gz /data/atlantic/domain.json`</mark> \ <mark style="color:green;">`/data/atlantic/resources/domain_keys/prime256v1/domain/new.pub`</mark> \ <mark style="color:green;">`/data/atlantic/resources/domain_keys/bls12381/domain/new.pub`</mark>
* Domain config:
  * <mark style="color:green;">`/data/atlantic/domain.json`</mark>
* Node pubkeys:
  * <mark style="color:green;">`/data/atlantic/resources/domain_keys/prime256v1/domain/new.pub`</mark>
  * <mark style="color:green;">`/data/atlantic/resources/domain_keys/bls12381/domain/new.pub`</mark>


# Rapid Node Initialization

To help developers and validators quickly join the Pharos Network without syncing from genesis, we provide regularly updated snapshot datasets for each network. These snapshots contain pre-synced state data and allow rapid setup of Devnet, Testnet, or Mainnet nodes.

> ⚠️ Snapshots should only be used from trusted sources. Always verify integrity before replacing production data.

***

## Pharos Network

* [Atlantic](/node-and-validator-guide/rapid-node-initialization/rapid-node-initialization-testnet)

### 💡 Notes & Best Practices

* Snapshots currently contain the full block history. In the future, we will support pruned snapshots that include only the most recent blocks and state.
* Full Cache Nodes should still sync from genesis.
* After applying a snapshot, the node will continue syncing to the latest height.
* Always ensure your snapshot corresponds to the correct network and version.


# Rapid Node Initialization(Testnet)

## 🧰 Initialization Using Snapshots

Follow these steps to bootstrap a Pharos node using the latest snapshot:

### ✅ **Step1: Download the Snapshot Dataset**

We periodically publish [new snapshots](/node-and-validator-guide/pharos-network-snapshots). Use the following command to download and extract:

```bash
wget https://snapshot.dplabs-internal.com/atlantic/atlantic-snapshot-2026-03-05-11.tar.gz
tar -zxvf atlantic-snapshot-2026-03-05-11.tar.gz
```

> Only use snapshots that match your target network.

### 🧯 **Step2: Stop the Container**

```bash
docker-compose down
```

### 🧹 **Step3: Remove Local Data**

```bash
rm -rf /data/$WORKSPACE/pharos-node/domain/light/data/public
mv /data/$WORKSPACE/pharos-node/domain/light/data/local_storage /data/$WORKSPACE/pharos-node/domain/light/data/local_storage.bak
```

### 📦 **Step4: Replace with the Latest Snapshot**

```bash
mv public /data/$WORKSPACE/pharos-node/domain/light/data/public
```

### 🚀 **Step5: Start the Container**

```bash
docker-compose up -d
```


# Rapid Node Initialization(Devnet)

## 🧰 Initialization Using Snapshots

Follow these steps to bootstrap a Pharos node using the latest snapshot:

### ✅ Step 1: Download the Latest Snapshot

We periodically publish [new snapshots](/node-and-validator-guide/pharos-network-snapshots). Use the following command to download and extract:

```sh
wget https://snapshot.dplabs-internal.com/devnet/snapshot-latest.tar.gz
tar -zxvf snapshot-latest.tar.gz
```

> Check this page regularly for updates. Only use snapshots that match your target network.

### 🧯 Step 2: Stop the Running Node Container

```sh
docker stop pharos-devnet
```

Make sure the node is fully stopped before continuing to avoid data corruption.

### 🧹 Step 3: Replace Old Data with the Snapshot

Remove the outdated data directory and move in the extracted snapshot:

```sh
rm -rf /data/pharos-devnet/pharos-node/domain/light/data/public
mv public /data/pharos-devnet/pharos-node/domain/light/data/public
```

> Adjust the file path to match your actual mount or installation directory.

### 🚀 Step 4: Restart the Node Container

```sh
docker start pharos-devnet
```

Your node should now resume from the latest synced snapshot.


# Pharos Network Snapshots

Welcome to the official snapshot page for Pharos Network. Here you can find regularly updated data snapshots for each network environment — Atlantic Testnet and Mainnet — to help you quickly synchronize and bootstrap your nodes without performing a full sync from genesis.

Snapshots include the latest database state and can be used for fast setup, disaster recovery, or performance testing.

### 💡 Notes & Best Practices

* Snapshots do not contain full block history, only the latest validated state.
* Archival nodes should still sync from genesis.
* After applying a snapshot, the node will continue syncing from the latest height.
* Always ensure your snapshot corresponds to the correct network and version.

> ⚠️ Always verify the snapshot source and integrity before applying it to your node.\\

See [Rapid Node Initialization](/node-and-validator-guide/rapid-node-initialization) for detailed instructions.

***

### Atlantic Full Cache Node Snapshot

| Latest Snapshot | atlantic-snapshot-2026-08-16-04.tar.gz                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Last Updated    | [atlantic-snapshot-2026-08-16-04.tar.gz](https://snapshot.dplabs-internal.com/atlantic/atlantic-snapshot-2026-08-16-04.tar.gz) |
| Format          | Compressed TAR archive                                                                                                         |
| Notes           | Only includes the ***public*** database                                                                                        |
| sha256 checksum | `87d0412ad7fb0694c5426c31fd275ee0aac92cf41aefb03bd7f136b36af1b097`                                                             |

***

### Mainnet Full Cache Node Snapshot

| Latest Snapshot | mainnet-snapshot-2026-07-01-03.tar.gz                                                                                       |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Last Updated    | [mainnet-snapshot-2026-07-01-03.tar.gz](https://snapshot.dplabs-internal.com/mainnet/mainnet-snapshot-2026-07-01-03.tar.gz) |
| Format          | Compressed TAR archive                                                                                                      |
| Notes           | Only includes the ***public*** database                                                                                     |
| sha256 checksum | `55cc3b1536f4bd7911d6fc703e4c1c15d15c7ea96306c38a75c3fb9e5d90542c`                                                          |

***


# Node Debugging & Configuration

## Pharos Block Validation

You can refer to the [Pharos Block Validation](https://silken-muskox-24e.notion.site/Pharos-Block-Validation-2ce8ec314f7580228d47cea4119df371) document for details on the Pharos consensus mechanism and block validation process.

## Block Sync Verification

You can use the RPC API to check whether the node is actively syncing blocks. If the block number is increasing, the node is catching up to the latest chain height.

```sh
curl 127.0.0.1:18100/ \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
```

## Check if the Node has joined as a Validator

### Query the local NodeID

```sh
cat /data/domain.json  | grep "NODE_ID"
```

### Send a transaction to join the Validator

<https://pharosscan.xyz/tx/0xf0045f3551f3d70df2ce5a1b2abe51839c3c2cf4a3e487c89ef6e5a006e16b92?tab=state>

### Check NodeID in Pharos system contract

```sh
curl -X POST --data '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [{
        "to": "0x4100000000000000000000000000000000000000",
        "data": "0x9de70258"
    }, "latest"],
    "id": 1
}' -H "Content-Type: application/json" https://atlantic.dplabs-internal.com
```

## Log Inspection

All logs are written to the directory:

```sh
cd /data/pharos-node/domain/light/log/
```

To monitor logs in real-time, use:

```sh
tail -f pharos.log
```

## Port Configuration

Ensure that the following ports are open and accessible for proper node operation:

```sh
18100 — RPC
18200 — P2P
19000 — Internal services
```


# Node Monitoring Setup

This guide describes how to configure monitoring for your Pharos node using Prometheus Pushgateway.

## Overview

Pharos nodes can push metrics to a Prometheus Pushgateway, which allows you to monitor node performance, block production, and other key metrics.

## Configuration

To enable monitoring, add the `monitor_config` section to your `pharos.conf` file.

### Example Configuration

```json
{
  "chain": {
    "monitor_config": {
      "enable_pamir_cetina": true,
      "pamir_cetina_job_name": "your_job_name",
      "pamir_cetina_push_address": "your_pushgateway_address",
      "pamir_cetina_push_interval": 5,
      "pamir_cetina_push_port": 9091
    },
    "secret_config": {
      "domain_key_path": "../keys/domain.key",
      "stabilizing_key_path": "../keys/stabilizing.key"
    },
    "startup_config": {
      "init_config": {
        "meta_path": "../data",
        "host_ip": "127.0.0.1",
        "http_port": "18100",
        "rpc_port": "20000",
        "tcp_port": "19000",
        "ws_port": "18200"
      }
    }
  },
  "storage": {
    "mygrid_conf": {},
    "mygrid_env": {
      "storage_data_path": "../data"
    }
  }
}
```

### Configuration Parameters

| Parameter                    | Description                            | Example                      |
| ---------------------------- | -------------------------------------- | ---------------------------- |
| `enable_pamir_cetina`        | Enable/disable metrics pushing         | `true`                       |
| `pamir_cetina_job_name`      | Job name for Prometheus metrics        | `"atlantic_testnet"`         |
| `pamir_cetina_push_address`  | Pushgateway address (without protocol) | `"your_pushgateway_address"` |
| `pamir_cetina_push_interval` | Push interval in seconds               | `5`                          |
| `pamir_cetina_push_port`     | Pushgateway port                       | `9091`                       |

## Setup Steps

1. Ensure you have a Prometheus Pushgateway running and accessible from your node
2. Edit your `pharos.conf` file and add the `monitor_config` section under `chain`:

```bash
vim pharos.conf
```

3. Replace the placeholder values:
   * `your_job_name`: A unique identifier for your node (e.g., `my_validator_node`)
   * `your_pushgateway_address`: Your Pushgateway hostname or IP
4. Restart your node to apply the changes:

**For Docker:**

```bash
docker compose restart
```

**For Binary:**

```bash
./ops stop --force
./ops start --config ./pharos.conf
```


# Rollback Blocks

This guide describes how to revert the Pharos node to a previous block height.

## Step 1: Stop the Node

**Docker:**

```bash
cd /data/$WORKSPACE
docker compose down
```

**Binary:**

```bash
cd /data/$WORKSPACE
./ops stop
```

## Step 2: Backup local\_storage

```bash
mv /data/$WORKSPACE/data/local_storage /data/$WORKSPACE/data/local_storage.bak
```

## Step 3: Revert Blocks

**Revert to latest stable block:**

```bash
cd /data/$WORKSPACE/bin
./pharos_cli revert -c ../pharos.conf -t stable
```

**Revert to a specific block number:**

```bash
cd /data/$WORKSPACE/bin
./pharos_cli revert -c ../pharos.conf -t stable -n 1234567
```

You will see an interactive confirmation prompt:

```
You are trying to revert stable block to 1234567, are you sure? [y/n]
```

Enter `y` to confirm.

## Step 4: Start the Node

**Docker:**

```bash
cd /data/$WORKSPACE
docker compose up -d
```

**Binary:**

```bash
./ops start --config ./pharos.conf
```


# Pharos Testnet: Rebuild Node with Preserved Node Info

This guide helps you **rebuild your Pharos Testnet node** while preserving your **existing validator identity** (node info). This is useful when resetting the database but retaining validator keys and configuration.

***

### **1️⃣ Stop and Backup the Existing Node**

```
docker stop pharos-testnet
mv /data/testnet /data/testnet_bak
docker rm pharos-testnet
```

***

### **2️⃣ Start a Fresh Node Container**

```
docker run -d --name pharos-testnet \\
  -v /data/testnet:/data \\
  -p 18100:18100 -p 18200:18200 -p 19000:19000 \\
  public.ecr.aws/k2g7b7g1/pharos/testnet:63b85b6b
```

***

### **3️⃣ ⏳ Wait for Initial Sync (\~3 minutes)**

**Do not run docker stop or docker-compose down during this step.**

You can check sync progress using:

```
curl 127.0.0.1:18100/ \\
  -X POST \\
  -H "Content-Type: application/json" \\
  --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
```

***

### **4️⃣ Stop the Node and Restore Node Identity**

```
docker stop pharos-testnet
```

Replace new config with the backup node info:

```
rm -rf /data/testnet/pharos-node/domain/light/data/
rm -rf /data/testnet/pharos-node/domain/light/certs/
rm -rf /data/testnet/resources/

mv /data/testnet_bak/pharos-node/domain/light/data/ /data/testnet/pharos-node/domain/light/data/
mv /data/testnet_bak/pharos-node/domain/light/certs/ /data/testnet/pharos-node/domain/light/certs/
mv /data/testnet_bak/resources/ /data/testnet/resources/
```

***

### **5️⃣ Restart and Finalize**

```
docker start pharos-testnet
```

Wait \~3 minutes again for sync to resume. You can check status using:

```
curl 127.0.0.1:18100/ \\
  -X POST \\
  -H "Content-Type: application/json" \\
  --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
```

***

### **6️⃣ Clean Up Backup**

```
rm -rf /data/testnet_bak
```


# Pharos Node Monitoring

We recommend deploying Prometheus, Pushgateway, and Grafana to monitor Pharos Node metrics.

## Deploy Prometheus and pushgateway

If you have already deployed a Prometheus monitoring and alerting system, you can skip this step:

1. **Create the Prometheus namespace:**

<pre class="language-sh"><code class="lang-sh"><strong>kubectl create namespace prometheus
</strong></code></pre>

2. **Add the** <mark style="color:green;">`prometheus-community`</mark> **chart repository:**

```sh
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
```

3. **Deploy Prometheus:**

```sh
helm upgrade -i prometheus prometheus-community/prometheus \
--namespace prometheus \
--set alertmanager.persistence.storageClass="gp2" \
--set server.persistentVolume.storageClass="gp2"
```

4. **Verify that all pods in the** prometheus **namespace are in the** <mark style="color:green;">`READY`</mark> **state:**

```sh
kubectl get pods -n prometheus
```

5. **Get the Pushgateway Service IP/Domain:**

```sh
kubectl -n prometheus-agent-namespace get svc
```

You should see the <mark style="color:green;">`prometheus-pushgateway`</mark> Service. Save the <mark style="color:green;">`IP address`</mark> of the Pushgateway Service.

## Modify the metrics push configuration file

For detailed instructions on configuring the metrics push in your `pharos.conf`, see [Node Monitoring Setup](/node-and-validator-guide/node-monitoring-setup).


# Enable AutoPruning in Pharos Node

This guide describes how to enable automatic data pruning on your Pharos node to manage disk space.

***

## For v0.11+ Deployment (New Structure)

If you deployed your node using the v0.11 deployment guide (Binary or Docker with the new structure), follow these steps.

### Step 1: Verify Node Status

Check if the node is running:

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

### Step 2: Enable Pruning

**For Binary Deployment:**

```bash
cd /data/$WORKSPACE/bin
./pharos_cli prune --c ../pharos.conf --enable_auto_prune
```

**For Docker Deployment:**

```bash
docker exec -it pharos-node /bin/bash
cd /data/bin
./pharos_cli prune --c ../pharos.conf --enable_auto_prune
```

### Step 3: Query Pruning Settings (Optional)

**For Binary Deployment:**

```bash
cd /data/$WORKSPACE/bin
./pharos_cli prune --c ../pharos.conf --get
```

**For Docker Deployment:**

```bash
docker exec -it pharos-node /bin/bash
cd /data/bin
./pharos_cli prune --c ../pharos.conf --get
```

Expected output:

```
PruneInfo:
enable_auto_prune: 1
auto_prune_reserved_window: 100000
prune_reserved_version: xxxxxx
flushed_version: xxxxxx
real_prune_reserved_version: xxxxxxx
```

If `enable_auto_prune` is `1`, auto pruning is enabled.

***

## For Legacy Deployment (Pre-v0.11)

If you deployed your node using the old Python ops tool, follow these steps.

### Step 1: Verify Node Status

```bash
curl 127.0.0.1:18100/ \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
```

### Step 2: Enable Pruning

Enter the container and enable auto-pruning:

```bash
docker exec -it pharos-testnet /bin/bash
cd /data/pharos-node/domain/client/bin/
./pharos_cli prune --enable_auto_prune
```

### Step 3: Query Pruning Settings (Optional)

```bash
docker exec -it pharos-testnet /bin/bash
cd /data/pharos-node/domain/client/bin/
./pharos_cli prune --get
```

Expected output:

```
PruneInfo:
enable_auto_prune: 1
auto_prune_reserved_window: 100000
prune_reserved_version: xxxxxx
flushed_version: xxxxxx
real_prune_reserved_version: xxxxxxx
```

If `enable_auto_prune` is `1`, auto pruning is enabled.


# Block Explorer

## Overview

A block explorer is one of the most essential tools when building on Pharos. It gives you visibility into everything happening on-chain — from transactions and blocks to smart contracts and account activity.

As a developer, you’ll frequently use a block explorer to verify deployments, debug issues, and track interactions with your contracts.

## What You Can Do with a Block Explorer

* View transactions and check their status
* Inspect blocks and network activity
* Explore wallet addresses and balances
* Interact with and inspect smart contracts
* Verify deployed contracts and view source code

## Why It Matters

The block explorer acts as your primary interface for understanding on-chain behavior. Whether you're testing on Testnet or deploying to Mainnet, it helps you quickly validate that everything is working as expected.


# Hemera SocialScan

## Overview

Hemera SocialScan is the official block explorer for the Pharos Network. It provides real-time visibility into on-chain activity, allowing developers and users to inspect transactions, blocks, accounts, tokens, and smart contracts.

For developers, the explorer provides a familiar interface of EVM that includes tools for contract verification, token analytics, and transaction debugging.

## Explorer Access

| Parameter                                  | Value                                                                                                                                                    |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Explorer Link                              | <https://pharos.socialscan.io/>                                                                                                                          |
| Developer APIs (Contract Verification API) | [`https://api.socialscan.io/pharos-mainnet/v1/explorer/command_api/contract`](https://api.socialscan.io/pharos-mainnet/v1/explorer/command_api/contract) |
| Explorer Developer API Key                 | `102dfc05-661d-4efd-a81e-24afb3918c7f`                                                                                                                   |

## What You Can Do

Hemera SocialScan supports common developer workflows:

* View and track transactions in real time
* Inspect blocks and network activity
* Explore wallet addresses, balances, and interactions
* View deployed smart contracts and their state
* Verify smart contracts and access source code
* Analyze token activity and transfers

## Contract Verification

You can verify deployed smart contracts using the SocialScan Developer API.

## Notes

* Ensure your contract is compiled with the correct settings before verification
* Verification may fail if bytecode or compiler settings do not match
* Use the explorer alongside your preferred framework (Foundry, Hardhat) for a smoother workflow

## Next Steps

* Verify your deployed contracts
* Use the explorer to debug and monitor transactions
* Combine with RPC and indexing tools for deeper insights


# RPC Infrastructure

## Overview

RPC endpoints are the primary way your application interacts with the Pharos Network. They allow you to send transactions, query on-chain data, and communicate with the network programmatically.

As a developer, you’ll use RPCs to connect your frontend, backend, or scripts to the blockchain during development and in production.

## What You Can Do with RPC

* Send transactions to the network
* Read on-chain data (balances, contract state, logs)
* Interact with smart contracts
* Listen to events and track activity
* Power applications, scripts, and integrations

## Why It Matters

RPC endpoints act as the bridge between your application and the blockchain. Choosing the right provider and configuration is important for performance, reliability, and scalability.

## Available RPC Providers

Pharos supports multiple RPC providers for both Mainnet and Testnet environments.

* ZAN RPC
* Nirvana RPC
* Alchemy RPC


# ZAN RPC

ZAN Node offers stable and fast RPC connections to Pharos Network, while providing a range of enhanced capabilities to data retrieval API and dynamic monitoring of smart contracts and assets.

## How to Access Pharos Testnet RPC from ZAN?

### Step 1: Create a ZAN account

A ZAN account is required if you want to create an API key on the ZAN platform for configuring an RPC node service on Pharos. For more information, see [Preparing a ZAN account](https://docs.zan.top/docs/quick-start-guide#-preparing-a-zan-account).

### Step 2: Create the API key required for configuring RPC Service on Pharos Network

If you want to configure the RPC node service network, you need to create an API key on the ZAN platform. This key is used for verification during node connection. You [can create the API key on the ZAN platform](https://docs.zan.top/docs/quick-start-guide#-creating-your-api-key-on-the-zan-platform). After the API key is created, you can get the URLs for the Pharos Network.

### Step 3: Copy your network URL

After the API key is created, you can get the URLs that can be used for accessing Pharos Network. Search for Pharos in the Chain RPC section. Click Copy to the right of the HTTPS URL to quickly copy the URL.

<figure><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeS_l7R2_PVVOMygh4Hy0Z8jNmeG1ZIsubVgkttcuOgfSbSTCrrFG60fX2SlbWSjI5EPGPGzlsE0HYoZ1trmuhtprjUkM6rvU_xNQfhYAQzH8sB42NWgE6T2OpP-8uwTT-57dWRzw?key=gOwVsY2HEJ_jkvNt_voFYQ" alt=""><figcaption></figcaption></figure>

### For Example:

| Network          | HTTPS URL                                                | WSS URL                                                 |
| ---------------- | -------------------------------------------------------- | ------------------------------------------------------- |
| Atlantic Testnet | <https://api.zan.top/node/v1/pharos/testnet/{apikey}>    | wss\://api.zan.top/node/ws/v1/pharos/testnet/{apikey}   |
| Pacific Mainnet  | <https://api.zan.top/node/v1/pharos/mainnet/{api\\_key}> | wss\://api.zan.top/node/ws/v1/pharos/mainnet/{api\_key} |

### Video Reference: [Pharos RPC.mp4](https://drive.google.com/file/d/1fMDF24yQu3xtnqQCOyQc9141Regfe1Ak/view?usp=sharing)


# Alchemy RPC

## Overview

Alchemy provides scalable RPC infrastructure and developer tooling for interacting with blockchain networks.

On Pharos, Alchemy can be used to access reliable RPC endpoints for both Pacific Mainnet and Atlantic Testnet, making it suitable for application development and production use.

## Getting Started with Alchemy

To use Alchemy with Pharos, you’ll need to [create an Alchemy account](https://dashboard.alchemy.com/signup?utm_source=docs\&utm_medium=referral\&utm_content=reference_pharos-api-quickstart) and generate an API key.

Here are some prerequisites before you start interacting:

1. Sign up or log in to Alchemy
2. Create a new app
3. Select the Pharos network
4. Generate your API key

Once done, the API key will let you interact with the Pharos network through a set of JSON-RPC methods. Follow the guide below to start interacting with Pharos.

### Step 1. Choose a package manager (npm or yarn)

Choose between `npm` and `yarn` based on your preference or project requirements.

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

```
# Begin with npm by following the npm documentation
# https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
```

{% endtab %}

{% tab title="yarn" %}

```
# For yarn, refer to yarn's installation guide
# https://classic.yarnpkg.com/lang/en/docs/install
```

{% endtab %}
{% endtabs %}

### Step 2. Set up your project

Run the following commands to create and initialize your project:

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

```
$ mkdir pharos-api-quickstart
$ cd pharos-api-quickstart
$ npm init --yes
```

{% endtab %}

{% tab title="yarn" %}

```
$ mkdir pharos-api-quickstart
$ cd pharos-api-quickstart
$ yarn init --yes
```

{% endtab %}
{% endtabs %}

This creates a new directory named `pharos-api-quickstart` and initializes a Node.js project within it.

### Step 3. Make your first request

Install Axios, a popular HTTP client, to make API requests:

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

```
$ npm install axios
```

{% endtab %}

{% tab title="yarn" %}

```
$ yarn add axios
```

{% endtab %}
{% endtabs %}

Create an `index.js` file in your project directory and paste the following code:

```javascript
const axios = require('axios');
 
const url = 'https://pharos-mainnet.g.alchemy.com/v2/your-api-key';
 
const payload = {
  jsonrpc: '2.0',
  id: 1,
  method: 'eth_blockNumber',
  params: []
```

Replace `your-api-key` with your actual Alchemy API key from the [Alchemy Dashboard](https://dashboard.alchemy.com/signup?utm_source=docs\&utm_medium=referral\&utm_content=reference_pharos-api-quickstart)

### Step 4. Run your script

Run your script to make a request to the network:

```shell
$ node index.js
```

You should see the latest block information from Pharos's network outputted to your console:

```shell
$ Latest Block: 0x...
```

## Next Steps

You've made your first request to the Pharos using Alchemy API. You can now explore the various [JSON-RPC methods](/api-and-sdk/json-rpc-methods) available on Pharos and start building your dApps.


# Nirvana RPC

## Overview

Nirvana Labs provides enterprise-grade blockchain infrastructure, including high-performance RPC nodes and dedicated node services.

On Pharos, Nirvana RPC can be used to power applications that require reliable network access, low latency, and scalable infrastructure.

## When to Use Nirvana RPC

Nirvana RPC is well-suited for:

* Production applications requiring stable and consistent RPC access
* Backend services and APIs handling high request volumes
* Use cases where performance and reliability are critical
* Teams that may need dedicated or scalable node infrastructure

## Access

Developers building on Pharos can access Nirvana RPC endpoints by requesting API keys through the Pharos team.

* **Access Type:** Dev API Key (unrestricted RPC access)
* **Request:** Contact the Pharos team to obtain credentials (Reach out on mail: <janesh@pharos.xyz> or tg: @janesh\_dani)

## Documentation

For more details on Nirvana Labs infrastructure and capabilities:

<https://nirvanalabs.io/product#rpc-nodes>


# Pharos MCP

## Overview

The Pharos MCP server brings Pharos chain data and onchain operations into any client that speaks the [Model Context Protocol](https://modelcontextprotocol.io) — Claude Desktop, Cursor, Windsurf, Codex, and custom agent runners.

Instead of writing RPC plumbing, you ask your AI assistant in plain language — *"what's the latest Pharos block?"*, *"check the PROS balance of 0x…"* — and it calls the right chain method for you.

|                     |                                                                                    |
| ------------------- | ---------------------------------------------------------------------------------- |
| **Hosted endpoint** | `https://mcp.pharos.xyz/mcp`                                                       |
| **Transport**       | Streamable HTTP                                                                    |
| **Authentication**  | None — public                                                                      |
| **Rate limit**      | 120 requests per minute per IP                                                     |
| **Chain ID**        | `1672` (`0x688`)                                                                   |
| **Native token**    | PROS                                                                               |
| **Source**          | [github.com/PharosNetwork/pharos-mcp](https://github.com/PharosNetwork/pharos-mcp) |

## Quick Start

Add the server to your MCP client configuration:

```json
{
  "mcpServers": {
    "pharos": {
      "type": "http",
      "url": "https://mcp.pharos.xyz/mcp"
    }
  }
}
```

No API key is required.

### Clients that only support stdio

Some clients cannot connect to remote MCP servers directly. Bridge to the hosted endpoint with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

```json
{
  "mcpServers": {
    "pharos": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.pharos.xyz/mcp"]
    }
  }
}
```

### Verify the connection

```bash
curl https://mcp.pharos.xyz/health
```

```json
{"status":"ok","chain":"Pharos","chainId":1672,"rpcUrl":"https://rpc.pharos.xyz"}
```

## Available Tools

### Chain and block data

| Tool               | JSON-RPC method                                        | Description                               |
| ------------------ | ------------------------------------------------------ | ----------------------------------------- |
| `get_chain_info`   | `eth_chainId`, `eth_blockNumber`, `web3_clientVersion` | Chain ID, client version and latest block |
| `get_block_number` | `eth_blockNumber`                                      | Latest block height                       |
| `get_block`        | `eth_getBlockByNumber`                                 | Block by tag or number                    |
| `get_gas_price`    | `eth_gasPrice`, `eth_maxPriorityFeePerGas`             | Current gas price and priority fee        |

### Accounts and transactions

| Tool                      | JSON-RPC method             | Description                       |
| ------------------------- | --------------------------- | --------------------------------- |
| `get_balance`             | `eth_getBalance`            | Native PROS balance of an address |
| `get_transaction_count`   | `eth_getTransactionCount`   | Address nonce                     |
| `get_transaction`         | `eth_getTransactionByHash`  | Transaction by hash               |
| `get_transaction_receipt` | `eth_getTransactionReceipt` | Transaction receipt               |

### Contracts

| Tool             | JSON-RPC method    | Description                           |
| ---------------- | ------------------ | ------------------------------------- |
| `eth_call`       | `eth_call`         | Read-only contract call               |
| `estimate_gas`   | `eth_estimateGas`  | Estimate gas without sending          |
| `get_code`       | `eth_getCode`      | Contract bytecode                     |
| `get_storage_at` | `eth_getStorageAt` | Read a contract storage slot          |
| `get_logs`       | `eth_getLogs`      | Event logs (max 1000 blocks per call) |

### Broadcasting

| Tool                   | JSON-RPC method          | Description                             |
| ---------------------- | ------------------------ | --------------------------------------- |
| `send_raw_transaction` | `eth_sendRawTransaction` | Broadcast an already-signed transaction |

## Block Parameters

Tools that accept a `block` parameter take any of:

* A named tag: `latest`, `earliest`, `pending`, `safe`, `finalized`
* A decimal number: `13005874`
* A hex quantity: `0xc67432`

Omitting it defaults to `latest`.

## Querying Event Logs

`get_logs` is capped at **1000 blocks per call**, matching the upstream RPC limit. Requests spanning a wider range are rejected with a message telling you the requested size and the maximum, so page through larger ranges in 1000-block windows.

Narrow results with `address` and `topics` wherever possible — an unfiltered query over a wide range is slow for everyone.

## Sending Transactions

The server is **non-custodial**: it never receives, stores, or asks for a private key. Signing happens entirely in your own wallet or script; the server only relays the resulting signed bytes to the network.

```
Your private key ──sign (locally)──▶ 0x02f86b…──▶ MCP server ──▶ Pharos
```

The flow is:

1. Build and sign a transaction locally with your own tooling (viem, ethers, a wallet).
2. Pass the resulting `0x`-prefixed string to `send_raw_transaction`.
3. Use the returned transaction hash with `get_transaction_receipt` to confirm inclusion.

Because signing is local, an AI agent connected to this server can prepare and broadcast a transaction for you without ever having access to your key.

## Self-Hosting

Run your own instance when you need a private RPC provider, a higher rate limit, or your own operational control.

Requires Node.js 20 or newer.

```bash
git clone https://github.com/PharosNetwork/pharos-mcp.git
cd pharos-mcp
npm install
npm run build
npm start
```

### Connecting a local instance over stdio

```json
{
  "mcpServers": {
    "pharos": {
      "command": "node",
      "args": ["/absolute/path/to/pharos-mcp/dist/src/index.js"],
      "env": {
        "PHAROS_RPC_URL": "https://rpc.pharos.xyz",
        "PHAROS_CHAIN_ID": "1672",
        "PHAROS_NATIVE_SYMBOL": "PROS"
      }
    }
  }
}
```

### Using your own RPC endpoint

Point `PHAROS_RPC_URL` at any Pharos RPC — including a provider endpoint that carries an API key, or your own node:

```json
"env": {
  "PHAROS_RPC_URL": "https://your-provider.example/v2/YOUR_API_KEY"
}
```

Keep such a URL out of version control; it holds a credential.

### Docker

```bash
docker build -t pharos-mcp-server .
docker run -p 3001:3001 \
  -e MCP_TRANSPORT=http \
  -e MCP_HOST=0.0.0.0 \
  pharos-mcp-server
```

### Configuration

| Variable                      | Default                  | Description                                                                          |
| ----------------------------- | ------------------------ | ------------------------------------------------------------------------------------ |
| `PHAROS_RPC_URL`              | `https://rpc.pharos.xyz` | Upstream JSON-RPC endpoint                                                           |
| `PHAROS_CHAIN_ID`             | `1672`                   | Expected chain ID                                                                    |
| `PHAROS_NATIVE_SYMBOL`        | `PROS`                   | Native token symbol                                                                  |
| `MCP_TRANSPORT`               | `stdio`                  | `stdio` or `http`                                                                    |
| `MCP_HOST`                    | `127.0.0.1`              | Bind address; use `0.0.0.0` in containers                                            |
| `MCP_PORT`                    | `3001`                   | HTTP port                                                                            |
| `MCP_AUTH_TOKEN`              | —                        | Bearer token; authentication is off when unset                                       |
| `ENABLE_SEND_RAW_TRANSACTION` | `false`                  | Enable transaction broadcasting                                                      |
| `RATE_LIMIT_PER_MINUTE`       | `120`                    | Per-IP request cap; `0` disables                                                     |
| `TRUST_PROXY`                 | `false`                  | Set `true` behind exactly one trusted proxy so rate limiting sees the real client IP |
| `MAX_SESSIONS`                | `500`                    | Maximum concurrent sessions; further sessions get `503`                              |
| `SESSION_TIMEOUT_MINUTES`     | `30`                     | Idle session eviction time                                                           |

## Security Notes

* The server has no use for a private key or mnemonic — never pass one to it.
* `send_raw_transaction` is disabled by default in self-hosted deployments. If you enable it on a publicly reachable instance, put authentication in front of it first; an open broadcast endpoint lets anyone push transactions through your node.
* Set `MCP_AUTH_TOKEN` and terminate TLS in front of any instance you expose beyond localhost.
* `admin_*`, `debug_*`, `personal_*` and `txpool_*` methods are not exposed.

## Troubleshooting

| Symptom                 | Cause and fix                                                                                                                                      |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Unknown MCP session`   | The session expired or the server restarted. Reconnect the client — a full client restart clears cached session state.                             |
| `429 Too Many Requests` | Rate limit reached. Back off and retry after the interval in the `Retry-After` header, or self-host for a higher limit.                            |
| `Block range too large` | `get_logs` was called with more than 1000 blocks. Page through the range in smaller windows.                                                       |
| `Connection failed`     | The client may not support remote MCP servers. If it connects but lists no tools, this is the same cause. Bridge with `mcp-remote` as shown above. |


# Indexer

## Overview

Indexing allows developers to efficiently query and access on-chain data without directly relying on raw RPC calls.

Instead of scanning the blockchain for every request, indexing services process and organize data in a way that makes it easier to retrieve, filter, and use within applications.

## What You Can Do with Indexers

* Query smart contract data efficiently
* Track events and historical activity
* Build dashboards and analytics
* Power frontend applications with structured data
* Monitor protocol and application-level metrics

## Why It Matters

While RPC endpoints are useful for direct interaction with the network, they are not optimized for complex queries or large-scale data retrieval.

Indexing services solve this by providing faster and more flexible access to blockchain data, making them essential for building scalable applications.

## Available Indexing Providers

Pharos supports indexing solutions that help developers access and structure on-chain data efficiently.

Refer to the provider below for setup and integration details:

* Goldsky


# Goldsky

Goldsky is a high-performance data indexing provider that makes it easy to extract, transform, and load on-chain data to power both application and analytics use cases.

## Indexing Pharos with Goldsky

Goldsky is Web3’s realtime data platform, giving developers access to world-class data infrastructure to power their onchain applications.

Seamlessly access the data you need with lightning-fast indexing, resilient subgraphs, and flexible data streaming pipelines. Spend less time on the complexities of infra maintenance and more time building incredible user experiences.

### Why Goldsky?

* **Scalable Infrastructure**: Scalable and resilient infrastructure, handling data challenges as your application grows.
* **Developer-Friendly Tools**: Easily integrate and iterate without being slowed down by complex data engineering.
* Enterprise Support: The Goldsky team is available 24/7 to help if things go wrong.

### Goldsky Products

#### Subgraphs

**Subgraphs** let you efficiently access blockchain data relevant to your application.

* **Fast Queries**: Optimized infrastructure means faster query responses and better performance.
* **Flexible Integration**: Customize your queries and adapt indexing to suit your application's unique needs.
* **Tagging & Organization**: Use tagging to organize your data, making it easier to manage and access.

**Use Cases**: dApps, NFT marketplaces, gaming, DAOs – any scenario where you need reliable, real-time blockchain data.

#### Mirror

**Mirror** gives you full control over your blockchain data. Instead of just querying, you can stream onchain data directly into your own database.

* **Realtime Streaming**: Stream onchain data directly to your existing systems, enabling fast and interactive user experiences.
* **Custom Data Handling**: Sync data into your own internal databases and combine it with other onchain or offchain data.
* **Automatic Data Updates**: Goldsky ensures that your data stays accurate with continuous synchronization and updates.

**Use Cases**: Complex data queries, integration into existing databases, real-time analytics, points systems, leaderboards, user progress tracking, loyalty programs, and custom metrics dashboards.

### Getting Started

For more details, check out the [Goldsky Documentation](https://goldsky.com/chains/pharos/?utm_source=pharos\&utm_medium=docs). Start building smarter, faster, and focus on what really matters: delivering a great experience to your users.


# Oracle

## Overview

Oracles provide access to off-chain data within smart contracts. They enable applications on Pharos to interact with real-world information such as asset prices, market data, and external events.

For many use cases, especially in DeFi and real-world asset applications, oracles are a critical component for bringing reliable data on-chain.

## What You Can Do with Oracles

* Access price feeds and market data
* Build applications that rely on real-world inputs
* Trigger contract logic based on external conditions
* Power lending, trading, and RWA protocols
* Integrate off-chain data into on-chain workflows

## Why It Matters

Smart contracts operate in a deterministic on-chain environment and cannot directly access external data.

Oracles bridge this gap by securely delivering off-chain data to the blockchain, enabling more advanced and dynamic applications.

## Available Oracle Providers

Pharos supports oracle solutions for different data and integration needs.

Refer to the pages below for setup and usage:

* Supra DORA Pull Oracle
* Chainlink Data Streams


# Chainlink Data Feeds

## Overview

Chainlink provides secure and decentralized oracle services used across the blockchain ecosystem.

On Pharos, Chainlink Data Streams enables developers to access high-frequency off-chain data with low latency, making it suitable for advanced financial and data-intensive applications.

## Network Configuration

Chainlink Data Streams is available on Pacific Mainnet with the following configuration:

### Verifier Proxy

| Network         | Address                                    |
| --------------- | ------------------------------------------ |
| Pacific Mainnet | 0xa094978891512268f4a4a4641B8da1A2a3E3BEB7 |

## When to Use Chainlink Data Streams

Chainlink Data Streams is typically used for:

* High-frequency price data and market feeds
* Low-latency applications such as trading and derivatives
* Advanced DeFi protocols requiring real-time data updates
* Use cases where data freshness and speed are critical

## Integration Notes

* Designed for applications that require faster and more frequent data updates compared to traditional oracle models
* Ensure your contracts are configured to use the correct verifier proxy
* Suitable for performance-sensitive and data-intensive use cases

## Documentation

For detailed integration guides and implementation details:

<https://docs.chain.link/data-streams>

## Next Steps

* Integrate Data Streams into your smart contracts
* Validate data flows and performance for your use case
* Combine with other infrastructure such as RPC and indexing for full application workflows


# Chainlink Push Engine Feeds

## Overview

Pharos has deployed Chainlink self-managed price feeds using the Chainlink Runtime Environment (CRE). A CRE workflow periodically fetches price data from the Chainlink Data Streams API, checks deviation and heartbeat thresholds, and pushes updates on-chain to a central cache contract.

Each price pair has a dedicated proxy contract that exposes the standard Chainlink Data Feeds interface, allowing any on-chain consumer to read the latest price via `latestAnswer()`.

## Version Update Notes

| Version | Time       | Note                                                                                                              |
| ------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
| V4      | 2026-07-30 | - Add `SYZUSD/YZUSD` pair (Only under PriceFeed 2.0)                                                              |
| V3      | 2026-06-24 | - Add `WSRUSD/RUSD` pair (Only under PriceFeed 2.0)                                                               |
| V2      | 2026-05-28 | <p>- <code>latestRoundData()</code> interface support<br>- PriceFeed contracts' addresses are updated</p>         |
| V1      | -          | - Supports 10 feed contracts and provides the `latestAnswer()`, `latestTimestamp()`, and `getFeedId()` interfaces |

## Common Settings

| Parameter                    | Value        |
| ---------------------------- | ------------ |
| Decimals                     | 18           |
| PriceFeed DeviationThreshold | 0.5          |
| PriceFeed Heartbeat          | 3600 seconds |

## PriceFeed 2.0

> **Note:** Self-Managed Feeds are pull-based and do not use a round-based aggregation model. As a result:
>
> * `roundId` and `answeredInRound` are always `0`. There are no OCR aggregation rounds — prices are written directly by CRE workflows.
> * `startedAt` and `updatedAt` both return the observation timestamp of the latest report. They are identical and do not represent distinct lifecycle events of a round.

> **Note:** When consuming price data from the cache, always verify that the returned data is sufficiently fresh for your use case.
>
> Use `latestAnswerTimestampForFeed` (or `latestReportTimestamp` on the proxy) to retrieve the timestamp alongside the answer.

> **Note:** The stored timestamp reflects the **observations timestamp** of the report — the time at which the price was observed by the Chainlink DON — not the time the transaction was included on-chain. Compare this value against the current block timestamp and enforce a maximum acceptable staleness threshold appropriate for your application.

## Pacific Mainnet

| Pair                  | Feed ID                                                            | Contract Address                           |
| --------------------- | ------------------------------------------------------------------ | ------------------------------------------ |
| SelfManagedFeedsCache | All feeds                                                          | 0xc71f7d98d3d9a000Fdfe307fBdb9d94AbD56424B |
| PROS/USD              | 0x0003f73a1abb2eec1ecdde915c659cbb8f0cbb030156b75dd401fbfd5281e246 | 0x9356C87a48F913d11C87a0d4b8cD16CD04624BF3 |
| BTC/USD               | 0x00039d9e45394f473ab1f050a1b963e6b05351e52d71e507509ada0c95ed75b8 | 0x6BFcd14b164de6c8C4dA2d065d511055A589EB20 |
| ETH/USD               | 0x000362205e10b3a147d02792eccee483dca6c7b44ecce7012cb8c6e0b68b3ae9 | 0x092ff0175Be8B2e83Ca5740d3EB13C6225901fa7 |
| WETH/USD              | Same as ETH/USD                                                    | Same as ETH/USD                            |
| WBTC/USD              | 0x00034a95e798013fd8bc6ac56707488e1c4622e5dffa9f3c2fe99f8105b4fa5b | 0x22E1db75084B7f0393896bc7046E64eFdC34b729 |
| USDT/USD              | 0x0003a910a43485e0685ff5d6d366541f5c21150f0634c5b14254392d1a1c06db | 0x84B06e38C70DD1f0039bA25E017CAe7cFcDE53b0 |
| USDC/USD              | 0x00038f83323b6b08116d1614cf33a9bd71ab5e0abf0c9f1b783a74a43e7bd992 | 0x8d08eA83A55ad1e805b5660F5eC76C99C6aF5eaf |
| LINK/USD              | 0x00036d7a1251e3f67d6658466b5e9e7fe8418af7feac9567ff322bff95cc2401 | 0xCb87D7B02AC34B0aC5C3472467AB67E1de655C0A |
| BNB/USD               | 0x000335fd3f3ffa06cfd9297b97367f77145d7a5f132e84c736cc471dd98621fe | 0x2eaB341Db05503c73A1274f1EFbD5d4560767229 |
| SOL/USD               | 0x0003b778d3f6b2ac4991302b89cb313f99a42467d6c9c5f96f57c29c0d2bc24f | 0x9c9FccaEf0851298321B813Ce2530c67e20F9C10 |
| XRP/USD               | 0x0003c16c6aed42294f5cb4741f6e59ba2d728f0eae2eb9e6d3f555808c59fc45 | 0x43CeBa953FF0165840f35342F6a6Bc1B923bc5FF |
| WSRUSD/USD            | 0x0007c129e624689953a2636c014531b2f0bd0c15f9a22211f53023354f309fa6 | 0xb5779919e2F183e59A34140331F7779cbFF1d1BC |
| SYZUSD/YZUSD          | 0x0007690a1667f3978924aa34b16a46ab12fcaae1b012c2f348905cb8299f58b7 | 0x36Aff5A272c80B3222c0C9138f2D43c7FAE2F372 |

## Atlantic Testnet

| Pair                  | Feed ID                                                            | Contract Address                           |
| --------------------- | ------------------------------------------------------------------ | ------------------------------------------ |
| SelfManagedFeedsCache | All feeds                                                          | 0x5456fD07A1622d33969f833d52aA5AD2c68C3Fa2 |
| PROS/USD              | 0x00037de4af3cb4fd0d82eed58a43ec144809f7fa7b4c6508eb628ad6d4d8b363 | 0x67488Fac9Bc4174a53a485b11F2066498Cd34b3A |
| BTC/USD               | 0x00037da06d56d083fe599397a4769a042d63aa73dc4ef57709d31e9971a5b439 | 0x82d0e03ea6d94120B92EA4Ea236DcFA273D42994 |
| ETH/USD               | 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 | 0xCd47D1843f3D6313836303fE1434BA26D257d500 |
| WETH/USD              | Same as ETH/USD                                                    | Same as ETH/USD                            |
| WBTC/USD              | 0x0003986bae710e410e6a6ec824db9ac91f97f6dd47fc5b28d028c14e825c5891 | 0x6F24f8bDeF2870aCa886fb3Fbc04919B0B46F993 |
| USDT/USD              | 0x00032874077216155926e26c159c1c20a572921371d9de605fe9633e48d136f9 | 0x2f7796B346d01a3f2264Ff0D93dDdFF8680b8B66 |
| USDC/USD              | 0x0003dc85e8b01946bf9dfd8b0db860129181eb6105a8c8981d9f28e00b6f60d9 | 0xDF6afcf662345Ea29ceACa6DA06141d828c516EA |
| LINK/USD              | 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265 | 0xc88944EEF085BCb1214a05C1f591CC05E94B904D |
| BNB/USD               | 0x000387d7c042a9d5c97c15354b531bd01bf6d3a351e190f2394403cf2f79bde9 | 0xaf5C639f95C98414E168E5D2F486409Fc35c1743 |
| SOL/USD               | 0x0003d338ea2ac3be9e026033b1aa601673c37bab5e13851c59966f9f820754d6 | 0x87766CFbf906722307F9CF1B3d6E329C87D37189 |
| XRP/USD               | 0x00035e3ddda6345c3c8ce45639d4449451f1d5828d7a70845e446f04905937cd | 0xbA64753e82cFC24310BBAF16d33A56C9D8DD6ff1 |
| WSRUSD/USD            | 0x000706ad7092fdc2310335a82828efe1d4c0220e5bf0fc46100da431f3433743 | 0x045fE9F80ac65c2702985a889A6a500D9fA20897 |
| SYZUSD/YZUSD          | 0x00070d19efa92f43763e29a8c444be98d19bf9c7487fb6e62ab5be7adb8ba896 | 0x3aca4c56afc1BA3CfF9C8f8e9911798ad82F538A |

## Contract Interface

```solidity
/// @notice Get the latest answer
function latestAnswer() external view returns (int256);

/// @notice Get the latest answer
function latestTimestamp() external view returns (uint256);

/// @return feedId The Feed ID associated with this proxy
function getFeedId() external view returns (bytes32 feedId);
```


# Supra

## Overview

Supra provides high-performance oracle services that deliver real-time price feeds and external data to decentralized applications.

On Pharos, Supra’s DORA Pull Oracle is deployed on Pacific Mainnet and Atlantic Testnet, enabling smart contracts to securely fetch off-chain data directly from the oracle network.

## Network Deployment

The DORA Pull Oracle is available on Pacific Mainnet and Atlantic Testnet with the following contracts:

### Pacific Mainnet Contracts

| Contract | Address                                    |
| -------- | ------------------------------------------ |
| Storage  | 0x58e158c74DF7Ad6396C0dcbadc4878faC9e93d57 |
| Pull     | 0x16f70cAD28dd621b0072B5A8a8c392970E87C3dD |

### Atlantic Testnet Contracts

| Contract | Address                                    |
| -------- | ------------------------------------------ |
| Storage  | 0xaa2f56843Cec7840F0C106F0202313d8d8CB13d6 |
| Pull     | 0x443A0f4Da5d2fdC47de3eeD45Af41d399F0E5702 |

## When to Use Supra

Supra is typically used for:

* Fetching real-time price feeds
* Building DeFi applications (lending, trading, derivatives)
* Powering RWA protocols that depend on external data
* Triggering smart contract logic based on off-chain inputs

## Integration Notes

* The DORA Pull model allows contracts to request data directly from the oracle
* Ensure your contract is configured to interact with the correct oracle contract addresses
* Suitable for applications requiring reliable and up-to-date data feeds

## Documentation

For detailed integration guides and supported features:

<https://docs.supra.com/oracles>

***

## Next Steps

* Integrate the DORA Pull Oracle into your smart contracts
* Test oracle data flows on Mainnet and Testnet
* Combine with other infrastructure such as RPC and indexing for full application workflows


# Cross Chain Infrastructure

## Overview

Cross-chain infrastructure enables applications on Pharos to interact with other blockchain networks. It allows developers to move assets, send messages, and build applications that operate across multiple ecosystems.

For developers building multi-chain applications or integrating external liquidity and users, cross-chain infrastructure is a key component.

## What You Can Do with Cross-Chain Infrastructure

* Transfer assets between networks
* Send messages across chains
* Build omnichain applications
* Access liquidity and users from other ecosystems
* Coordinate logic across multiple chains

## Why It Matters

Modern applications often extend beyond a single blockchain. Cross-chain infrastructure allows Pharos-based applications to integrate with other networks, improving composability and expanding reach.

## Available Solutions

Pharos supports cross-chain solutions for asset transfers and messaging.

Refer to the pages below for setup and usage:

* Chainlink CCIP
* Circle CCTP
* LayerZero


# Chainlink CCIP

Chainlink CCIP enables secure cross-chain messaging and token transfers between supported blockchains.

On Pharos, CCIP allows developers to build applications that interact with multiple networks, including cross-chain DeFi protocols, token bridges, and omnichain applications.

## Supported Lanes

The following cross-chain lanes are currently available:

* Pharos ↔ Ethereum
* Pharos ↔ Jovay
* Pharos ↔ Polygon
* Pharos ↔ Base

## Network Configuration

Chainlink CCIP is available on Pacific Mainnet with the following configuration:

### Core Parameters

| Parameter       | Value                                      |
| --------------- | ------------------------------------------ |
| Chain Selector  | 7801139999541420232                        |
| Router Contract | 0x4e52dD94e9BCfeFE3C78153bDfB0AB1d30687297 |

## When to Use CCIP

Chainlink CCIP is typically used for:

* Cross-chain token transfers
* Sending messages between smart contracts on different chains
* Building omnichain applications
* Integrating liquidity and users from other ecosystems

## Integration Notes

* Use the Router contract to initiate cross-chain messages and token transfers
* Ensure correct configuration of chain selectors and destination chains
* Suitable for applications requiring secure and standardized cross-chain communication

## Documentation

For detailed integration guides and supported configurations:

<https://docs.chain.link/ccip/directory/mainnet/chain/pharos-mainnet>

## Next Steps

* Configure your contracts to interact with the CCIP Router
* Test cross-chain messaging and transfers
* Combine with other infrastructure such as RPC and oracles for full application workflows


# Circle CCTP

## Overview

Circle’s Cross-Chain Transfer Protocol (CCTP) enables secure transfer of USDC across blockchains using a native burn-and-mint mechanism.

On Pharos, CCTP allows developers to move USDC between supported networks without relying on traditional bridges or wrapped tokens.

Unlike lock-and-mint bridges, CCTP burns USDC on the source chain and mints native USDC on the destination chain, ensuring a 1:1 transfer with no liquidity fragmentation.

## Contract Addresses

CCTP smart contract addresses for Pharos. For contract interfaces and method signatures, see [Contract Interfaces](https://developers.circle.com/cctp/references/contract-interfaces).

Full contract source code is [available on GitHub](https://github.com/circlefin/evm-cctp-contracts).

### Pacific Mainnet Contracts

#### TokenMessengerV2

| Blockchain | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos** | 31                                                                                                    | [`0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d`](https://pharos.socialscan.io/address/0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d) |

#### MessageTransmitterV2

| Blockchain | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos** | 31                                                                                                    | [`0x81D40F21F12A8F0E3252Bccb954D722d4c464B64`](https://pharos.socialscan.io/address/0x81D40F21F12A8F0E3252Bccb954D722d4c464B64) |

#### TokenMinterV2

| Blockchain | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos** | 31                                                                                                    | [`0xfd78EE919681417d192449715b2594ab58f5D002`](https://pharos.socialscan.io/address/0xfd78EE919681417d192449715b2594ab58f5D002) |

#### MessageV2

| Blockchain | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                         |
| ---------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos** | 31                                                                                                    | [`0xec546b6B005471ECf012e5aF77FBeC07e0FD8f78`](https://pharos.socialscan.io/address/0xec546b6B005471ECf012e5aF77FBeC07e0FD8f78) |

### Atlantic Testnet Contracts

#### TokenMessengerV2

| Blockchain         | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos Testnet** | 31                                                                                                    | [`0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA`](https://pharos-testnet.socialscan.io/address/0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA) |

#### MessageTransmitterV2

| Blockchain         | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos Testnet** | 31                                                                                                    | [`0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275`](https://pharos-testnet.socialscan.io/address/0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275) |

#### TokenMinterV2

| Blockchain         | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos Testnet** | 31                                                                                                    | [`0xb43db544E2c27092c107639Ad201b3dEfAbcF192`](https://pharos-testnet.socialscan.io/address/0xb43db544E2c27092c107639Ad201b3dEfAbcF192) |

#### MessageV2

| Blockchain         | [Domain](https://developers.circle.com/cctp/concepts/supported-chains-and-domains#domain-identifiers) | Address                                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Pharos Testnet** | 31                                                                                                    | [`0xbaC0179bB358A8936169a63408C8481D582390C4`](https://pharos-testnet.socialscan.io/address/0xbaC0179bB358A8936169a63408C8481D582390C4) |

## Message Passing

Cross-Chain Transfer Protocol (CCTP) uses generalized message passing to facilitate the native burning and minting of USDC across supported blockchains, also known as [domains](https://developers.circle.com/cctp/cctp-supported-blockchains#cctp-supported-domains). Message passing is a three-step process:

1. An onchain component on the source domain emits a message.
2. Circle’s offchain attestation service signs the message.
3. The onchain component at the destination domain receives the message, and forwards the message body to the specified recipient.

## For Pharos

The relationship between CCTP’s onchain components and Circle’s offchain Attestation Service is illustrated below for a burn-and-mint of USDC between EVM-compatible domains:

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

On Pharos, the onchain component for crosschain burning and minting is called `TokenMessengerV2`, which is built on top of `MessageTransmitterV2`, an onchain component for generalized message passing.

In the diagram, a token depositor calls the [TokenMessengerV2#depositForBurn](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L158) function to deposit a native token (such as USDC), which delegates to the TokenMinterV2 contract to burn the token. The `TokenMessengerV2` contract then sends a message via the [MessageTransmitterV2#sendMessage](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L143) function. After [sufficient block confirmations](https://developers.circle.com/cctp/required-block-confirmations), Circle’s offchain attestation service, Iris, signs the message. An API consumer must query this attestation and submits it onchain to the destination domain’s [MessageTransmitterV2#receiveMessage](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L206) function.

To send an arbitrary message, directly call [MessageTransmitterV2#sendMessage](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L143). The message recipient must implement the following methods to handle messages based on their finality threshold:

* Implement [IMessageHandlerV2#handleReceiveFinalizedMessage](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/interfaces/v2/IMessageHandlerV2.sol#L35) to receive messages with `finalityThresholdExecuted` ≥ 2000.
* Implement [IMessageHandlerV2#handleReceiveUnfinalizedMessage](https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/interfaces/v2/IMessageHandlerV2.sol#L51) to receive messages with `finalityThresholdExecuted` < 2000.

This distinction allows the recipient to control the level of finality it requires before accepting a message.

## Message Format

### Message Header

The top-level message header format is standard for all messages passing through CCTP.

| Field                       | Offset | Solidity Type | Length (bytes) | Description                                                                                                                   |
| --------------------------- | ------ | ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `version`                   | 0      | `uint32`      | 4              | Version identifier - use 1 for CCTP                                                                                           |
| `sourceDomain`              | 4      | `uint32`      | 4              | Source domain ID                                                                                                              |
| `destinationDomain`         | 8      | `uint32`      | 4              | Destination domain ID                                                                                                         |
| `nonce`                     | 12     | `bytes32`     | 32             | Unique message nonce (see [CCTP V2 Nonces](#cctp-v2-nonces))                                                                  |
| `sender`                    | 44     | `bytes32`     | 32             | Address of MessageTransmitterV2 caller on source domain                                                                       |
| `recipient`                 | 76     | `bytes32`     | 32             | Address to handle message body on destination domain                                                                          |
| `destinationCaller`         | 108    | `bytes32`     | 32             | Address permitted to call MessageTransmitterV2 on destination domain, or bytes32(0) if message can be received by any address |
| `minFinalityThreshold`      | 140    | `uint32`      | 4              | Minimum finality threshold before allowed to attest (see [CCTP V2 Finality Thresholds](#cctp-v2-finality-thresholds))         |
| `finalityThresholdExecuted` | 144    | `uint32`      | 4              | Actual finality threshold executed from source chain (see [CCTP V2 Finality Thresholds](#cctp-v2-finality-thresholds))        |
| `messageBody`               | 148    | `bytes`       | dynamic        | App-specific message to be handled by recipient                                                                               |

#### Nonces

A CCTP nonce is a unique identifier for a message that can only be used once on the destination domain. Circle assigns CCTP nonces offchain. The nonce for each message in a transaction can be queried through the [`GET /v2/messages`](https://developers.circle.com/api-reference/cctp/all/get-messages-v2) endpoint, using the transaction hash as a query parameter.

{% hint style="info" %}
**Why `bytes32` type for addresses**

CCTP is built to support EVM chains, which use 20 byte addresses, and non-EVM chains, many of which use 32 byte addresses. Circle provides a [`Message.sol` library](https://github.com/circlefin/evm-cctp-contracts/blob/40111601620071988e94e39274c8f48d6f406d6d/src/messages/Message.sol#L145-L157) as a reference implementation for converting between address and `bytes32` in Solidity.
{% endhint %}

### Message body

The message format includes a dynamically sized `messageBody` field, used for application-specific messages. For example, `TokenMessengerV2` defines a [BurnMessageV2](https://github.com/circlefin/evm-cctp-contracts/blob/master/src/messages/v2/BurnMessageV2.sol) with data related to crosschain transfers.

| Field             | Offset | Solidity Type | Length (bytes) | Description                                                                                                                                                                                                                                                                                            |
| ----------------- | ------ | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `version`         | 0      | `uint32`      | 4              | Version identifier - use 1 for CCTP                                                                                                                                                                                                                                                                    |
| `burnToken`       | 4      | `bytes32`     | 32             | Address of burned token on source domain                                                                                                                                                                                                                                                               |
| `mintRecipient`   | 36     | `bytes32`     | 32             | Address to receive minted tokens on destination domain                                                                                                                                                                                                                                                 |
| `amount`          | 68     | `uint256`     | 32             | Amount of burned tokens                                                                                                                                                                                                                                                                                |
| `messageSender`   | 100    | `bytes32`     | 32             | Address of caller of `depositForBurn` (or `depositForBurnWithCaller`) on source domain                                                                                                                                                                                                                 |
| `maxFee`          | 132    | `uint256`     | 32             | Maximum fee to pay on the destination domain, specified in units of `burnToken`                                                                                                                                                                                                                        |
| `feeExecuted`     | 164    | `uint256`     | 32             | Actual fee charged on the destination domain, specified in units of `burnToken` (capped by `maxFee`)                                                                                                                                                                                                   |
| `expirationBlock` | 196    | `uint256`     | 32             | An expiration block 24 hours in the future is encoded in the message before signing by attestation service, and is respected on the destination chain. If the burn expires, it must be re-signed. Expiration acts as a safety mechanism against problems with finalization, such as a stuck sequencer. |
| `hookData`        | 228    | `bytes`       | dynamic        | Arbitrary data to be included in the `depositForBurn` on source domain and to be executed on destination domain                                                                                                                                                                                        |

{% hint style="info" %}
**`expirationBlock` on ARB-stack blockchains**

For ARB-stack destination blockchains (Arbitrum, EDGE, and Plume), the `expirationBlock` is an Ethereum (L1) block number, not the L2 block number. ARB-stack blockchains track blocks internally using the parent blockchain (Ethereum). When validating expiration for these blockchains, compare the `expirationBlock` value against the current Ethereum block number, not the L2 block number.
{% endhint %}

## API hosts and endpoints

CCTP provides a set of API hosts and endpoints to manage messages, attestations, and transaction details for your crosschain USDC transfers.

### API service hosts

| Environment | URL                                   |
| ----------- | ------------------------------------- |
| **Testnet** | `https://iris-api-sandbox.circle.com` |
| **Mainnet** | `https://iris-api.circle.com`         |

{% hint style="info" %}
**API Service Rate Limit**

The CCTP API service rate limit is 35 requests per second. If you exceed 35 requests per second, the service blocks all API requests for the next 5 minutes and returns an HTTP 429 response.
{% endhint %}

### API endpoints

CCTP endpoints enable advanced capabilities such as fetching attestations for **Standard Transfer** or **Fast Transfer** burn events, verifying public keys across versions, accessing transaction details, querying fast transfer allowances and fees, and initiating re-attestation processes. Below is an overview of the CCTP public endpoints. Click on any endpoint for its API reference.

| Endpoint                                                                                                                                           | Description                                                                                                      | Use Case                                                                                       |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| [`GET /v2/publicKeys`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-public-keys-v2/README.md)                        | Returns public keys for validating attestations across all supported CCTP versions.                              | Retrieve public keys to verify attestation authenticity for crosschain transactions.           |
| [`GET /v2/messages`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-messages-v2/README.md)                             | Retrieves messages and attestations for a given transaction or nonce, supporting messages for all CCTP versions. | Fetch attestation status and transaction details.                                              |
| [`POST /v2/reattest`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/reattest-message/README.md)                           | Re-attests a soft finality V2 message to achieve finality or revive expired Fast Transfer burns.                 | Handle edge cases requiring updated attestations or finalize transactions with stricter rules. |
| [`GET /v2/fastBurn/USDC/allowance`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-fast-burn-usdc-allowance/README.md) | Retrieves the current USDC Fast Transfer allowance remaining.                                                    | Monitor available allowance for Fast Transfer burns in real-time.                              |
| [`GET /v2/burn/USDC/fees`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-burn-usdc-fees/README.md)                    | Returns the fees for USDC transfers between specified source and destination domains.                            | Calculate transaction costs before initiating a Fast or Standard Transfer.                     |

{% hint style="info" %}
**Deprecated endpoint**

The endpoint `/v2/fastBurn/USDC/fees` is deprecated. Use [`/v2/burn/USDC/fees`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-burn-usdc-fees/README.md) instead to retrieve both Fast and Standard Transfer fees.

**Note:** This deprecation does **not** affect [`/v2/fastBurn/USDC/allowance`](https://github.com/PharosNetwork/docs/blob/main/api-reference/cctp/all/get-fast-burn-usdc-allowance/README.md) (see preceding table), which remains active and valid.
{% endhint %}

## Finality thresholds

CCTP has the concept of a finality threshold, which is a chain-agnostic representation of the confirmation level required before an attestation is issued. This allows integrators to specify how many confirmations are needed based on their risk tolerance or use case.

In CCTP, each message specifies a `minFinalityThreshold`. This threshold indicates the minimum level of confirmation required for Circle's attestation service (Iris) to attest to the message. Iris will not attest to a message at a confirmation level below the specified minimum threshold. This allows applications to enforce a desired level of finality before acting on an attestation on the destination chain.

### Defined finality thresholds

CCTP V2 defines the following finality thresholds:

| Finality Threshold | Value |
| ------------------ | ----- |
| **Confirmed**      | 1000  |
| **Finalized**      | 2000  |

### Messages and finality

* Messages with a `minFinalityThreshold` of **1000** or lower are considered **Fast** messages. These messages are eligible for fast attestation at the *confirmed* level by Iris.
* Messages with a `minFinalityThreshold` of **2000** are considered **Standard** messages. These messages are attested to at the *finalized* level by Iris.

{% hint style="info" %}
Only two finality thresholds are supported. Any `minFinalityThreshold` value below **1000** is treated as **1000**, and any value above **1000** is treated as **2000**.
{% endhint %}

## Fees

For information about CCTP transfer fees, including fee tables by blockchain, the `maxFee` parameter, and Standard Transfer fee switch support, see [CCTP Fees](https://developers.circle.com/cctp/concepts/fees).

## Hooks

Hooks in CCTP V2 are metadata that can be attached to a burn message, allowing integrators to execute custom logic at the destination chain. Hook execution is left entirely to the integrator, offering maximum flexibility and enabling broader crosschain compatibility without altering the core CCTP protocol.

### Design overview

CCTP does not implement hook execution in the core protocol. Instead, hooks are treated as opaque metadata passed along with the burn message. This design allows integrators to define and control how hooks are processed on the destination chain, based on their own infrastructure and trust model.

### Key benefits

* **Maximum flexibility for integrators**
  * Determine execution timing: pre-mint or post-mint
  * Implement custom recovery or error-handling strategies if hook execution fails
  * Choose any execution environment (EVM or non-EVM); even non-EVM chains can support Hooks as data passed into a function call.
* **Improved Compliance and Security Separation**
  * **Compliance**: By delegating hook execution to the integrator, the protocol maintains a clear boundary between CCTP's core message-passing capabilities and application-specific logic. This modular approach helps integrators meet their own compliance requirements with greater flexibility.
  * **Security**: By keeping hook execution outside the core protocol, CCTP maintains a smaller and more focused security surface, while allowing integrators to manage their own execution environments independently.


# LayerZero

## Overview

LayerZero provides omnichain messaging infrastructure that allows smart contracts to communicate across different blockchain networks.

On Pharos, LayerZero enables developers to build applications that send messages and transfer assets across multiple chains, with configurable security through Decentralized Verifier Networks (DVNs).

## Network Configuration

LayerZero is available on Pacific Mainnet with the following configuration:

### Core Parameters

| Parameter         | Value   |
| ----------------- | ------- |
| Chain Key         | pharos  |
| Stage             | mainnet |
| Endpoint ID (EID) | 30407   |

### Endpoint V2 Contracts

| Contract          | Address                                    |
| ----------------- | ------------------------------------------ |
| endpointV2        | 0x6F475642a6e85809B1c36Fa62763669b1b48DD5B |
| sendUln302        | 0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7 |
| receiveUln302     | 0xe1844c5D63a9543023008D332Bd3d2e6f1FE1043 |
| blockedMessageLib | 0xc1ce56b2099ca68720592583c7984cab4b6d7e7a |
| executor          | 0x4208D6E27538189bB48E603D6123A94b8Abe0A0b |

## When to Use LayerZero

LayerZero is typically used for:

* Sending messages across chains
* Building omnichain applications
* Coordinating contract logic across multiple networks
* Use cases requiring flexible security configuration via DVNs

## Integration Notes

* Use Endpoint V2 contracts to send and receive cross-chain messages
* Configure DVNs based on your application's security requirements
* Suitable for applications that require customizable cross-chain messaging

## Metadata API

LayerZero provides metadata APIs to query deployments and configuration details.

* **Deployments:**\
  <https://metadata.layerzero-api.com/v1/metadata/deployments>
* **DVN Providers:**\
  <https://metadata.layerzero-api.com/v1/metadata/dvns>
* **Example (Ethereum DVNs):**\
  <https://metadata.layerzero-api.com/v1/metadata/dvns?chainNames=ethereum>

## Next Steps

* Configure your contracts to interact with LayerZero endpoints
* Select appropriate DVNs for your use case
* Test cross-chain messaging flows across supported networks


# Wallet

## Overview

Wallets and custody solutions are used to manage accounts, sign transactions, and control assets on the Pharos Network.

As a developer, you’ll use wallets for interacting with applications, while custody solutions are important for managing funds securely, especially in production environments.

## What You Can Do with Wallets

* Create and manage accounts
* Sign and send transactions
* Interact with smart contracts
* Connect to decentralized applications
* Manage assets across networks

## Custody for Applications

For applications and teams, custody solutions provide more advanced capabilities such as:

* Multi-signature (multisig) transaction approvals
* Secure key management
* Role-based access control
* Operational security for managing funds

## Why It Matters

Choosing the right wallet or custody solution is important for both development and production.

* Individual developers typically use wallets for testing and interaction
* Teams and protocols rely on custody solutions for secure fund management and operational workflows

## Available Solutions

Pharos supports wallet and custody solutions for different use cases.

Refer to the pages below for setup and usage:

* Safe MultiSig
* Fordefi


# Safe MultiSig

Safe (Gnosis Safe) is one of the most widely used multi-signature wallets in the blockchain ecosystem. It enables teams and DAOs to securely manage digital assets and smart contract permissions through configurable multi-signature approvals. Developers and organizations can create Safe wallets to manage treasury funds, protocol upgrades, and governance-controlled contracts on the Pharos network.

## Safe UI

Production interface for interacting with Safe wallets on Pharos: [https://app.safe.global](https://app.safe.global/welcome/accounts)

## Transaction Service API

Developers can interact programmatically with the Safe Transaction Service for automation and integration.

**Swagger API Endpoint**

<https://transaction.safe.pharosnetwork.xyz>

### This service enables:

* Transaction creation and submission
* Signature collection
* Transaction status monitoring
* Integration with automation workflows


# Fordefi

## Overview

Fordefi provides institutional-grade wallet infrastructure for securely managing digital assets.

It uses MPC (Multi-Party Computation) technology to eliminate single private keys, enabling secure transaction signing along with policy-based access control for teams and organizations.

## Enable Pharos Network

Pharos can be enabled directly within the Fordefi platform from the Chains settings:

<https://app.fordefi.com/settings/chains?search=PHAROS\\&page=1>

## Key Features

* MPC-based key management
* Policy-based transaction approvals
* Institutional custody workflows
* Secure signing infrastructure
* Integration with DeFi protocols

## When to Use Fordefi

Fordefi is typically used for:

* Managing treasury and protocol funds
* Secure transaction signing without single key exposure
* Teams requiring policy-driven approval workflows
* Institutional-grade custody and operations

## Integration Notes

* Enable Pharos from the Chains settings before initiating transactions
* Configure policies and approval flows based on your team’s requirements
* Suitable for both operational workflows and production environments

## Next Steps

* Enable Pharos in your Fordefi account
* Set up policies and access controls
* Start managing assets and signing transactions securely


# Pharos Skill Engine

> v0.1.0 · Atlantic Testnet & Pacific Mainnet

## Part 1 — What Is the Pharos Skill Engine?

The Pharos Skill Engine is a packaged developer toolkit that lets an AI agent perform any on-chain operation on the Pharos — check balances, send transactions, deploy and verify Solidity contracts, run batch airdrops, and generate Web3 interaction scripts — entirely through CLI commands.

It is not an SDK, not an API, and not a smart contract framework. It is a self-contained knowledge and tooling package that a developer installs once and then hands to an AI agent (like Claude). The agent reads the package, understands what Pharos operations are available, and executes them by running the correct `cast` and `forge` commands.

### The Core Idea

**Natural language in → on-chain action out.** A developer says "deploy an ERC20 token called Pharos Gold with symbol PGD" and the agent reads the Skill Engine, finds the one-click ERC20 template, generates the deploy script, runs it, and returns the contract address — all without the developer writing a single line of Solidity or a single CLI command by hand.

### What It Is Made Of

| File / Folder                    | What It Is                                   | Role                                                                                           |
| -------------------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `SKILL.md`                       | The entry point — read by the AI agent first | Defines what capabilities exist and where to find detailed instructions for each               |
| `assets/networks.json`           | Network configuration                        | RPC URLs, chain IDs, explorer URLs, native token symbols for both Atlantic testnet and mainnet |
| `assets/tokens.json`             | Known token registry                         | Pre-populated ERC20 addresses and decimals for both networks                                   |
| `assets/erc20/StandardERC20.sol` | Built-in ERC20 contract template             | Ready-to-deploy OpenZeppelin-based ERC20 with configurable name, symbol, decimals, supply      |
| `assets/airdrop/`                | Built-in airdrop infrastructure              | Distributor contracts and batch scripts; supports up to 6000+ addresses via CSV                |
| `assets/templates/`              | Script generation templates                  | JS (ethers.js v6), TypeScript (viem), Python (web3.py) templates for contract interaction      |
| `references/query.md`            | Query command reference                      | Full `cast call` / `cast balance` / `cast tx` command specs                                    |
| `references/transaction.md`      | Transaction command reference                | Full `cast send` / `cast estimate` / airdrop specs                                             |
| `references/contract.md`         | Contract command reference                   | Full `forge script` / `forge verify-contract` / ERC20 deploy specs                             |
| `references/script-gen.md`       | Script generation reference                  | Rules for generating JS/TS/Python interaction scripts from ABI or source                       |

### File Structure Reference

```
pharos-skill-engine-0.1.0/
│
├── SKILL.md                         ← AI agent entry point
│    ├── Prerequisites
│    ├── Network Configuration
│    ├── Capability Index             ← maps intents to reference sections
│    ├── General Error Handling
│    ├── Security Reminders
│    └── Write Operation Pre-checks
│
├── assets/
│   ├── networks.json                ← RPC URLs, chain IDs, explorer URLs
│   ├── tokens.json                  ← known ERC20 addresses + decimals
│   ├── erc20/StandardERC20.sol      ← built-in ERC20 template
│   ├── airdrop/                     ← batch airdrop infrastructure
│   │   ├── AirdropHelper.sol        ← mode selection + CSV parsing
│   │   ├── BatchAirdrop.s.sol       ← native token multi-batch script
│   │   ├── BatchAirdropERC20.s.sol  ← ERC20 multi-batch script
│   │   ├── NativeDistributor.sol    ← native token distributor contract
│   │   └── ERC20Distributor.sol     ← ERC20 distributor contract
│   └── templates/                   ← script generation templates
│       ├── template_read.js.tpl
│       ├── template_write.js.tpl
│       ├── template_read.ts.tpl
│       ├── template_write.ts.tpl
│       ├── template_read.py.tpl
│       └── template_write.py.tpl
│
└── references/
    ├── query.md                     ← balance, tx, contract read specs
    ├── transaction.md               ← send, gas, airdrop specs
    ├── contract.md                  ← deploy, verify, ERC20 specs
    └── script-gen.md                ← JS/TS/Python script generation specs
```

## Part 2 — How It Works

Understanding how the Skill Engine works helps both users who want to use it and developers who want to build new skills. The system has three layers.

### Layer 1 — The Agent Reads SKILL.md First

When a developer asks the AI agent to do something on Pharos, the agent's first action is to read `SKILL.md`. This file does two things:

* Declares the prerequisites the agent must satisfy (Foundry installed, private key configured)
* Provides a **Capability Index** — a table that maps every possible user intent to the correct capability and points to detailed instructions in a reference file

For example: if a developer says "check my wallet balance", the agent reads the Capability Index, finds the row for balance queries, and then reads `references/query.md` for the exact `cast balance` command template, parameter spec, and output parsing rules.

### Layer 2 — The Reference Files Contain the Commands

The four reference files are detailed machine-readable specs. Each one covers a domain of operations:

Every section in every reference file has the same structure: command template, parameter table, output parsing guide, error handling table, and agent guidelines. This consistency means the agent always knows where to find what it needs.

### Layer 3 — The Assets Are the Raw Materials

The `assets/` folder contains everything the agent copies into the user's project or reads directly:

### The Write Operation Pre-Check Sequence

Every operation that sends a transaction (transfer, deploy, airdrop, contract write) must pass four checks before the command runs. This is enforced by `SKILL.md` and cannot be skipped.

> **⚠️ Foundry does NOT read env vars automatically.** `cast` and `forge` do not pick up `$PRIVATE_KEY` from the environment on their own. The agent must always pass it explicitly as `--private-key $PRIVATE_KEY` in every command. This is a common source of confusion.

### How the Agent Resolves a Request — Step by Step

1. Developer makes a request in natural language
2. Agent checks if Foundry is installed (`which cast`). If not, install it before proceeding
3. Agent reads `SKILL.md` → scans the Capability Index for the matching intent
4. Agent reads the relevant reference file section for the exact command template
5. Agent reads `assets/networks.json` to get the correct RPC URL and chain ID
6. For write operations: agent runs all four pre-checks (private key, address, network, balance)
7. Agent executes the `cast` or `forge` command
8. Agent parses the output per the reference file's output parsing rules
9. Agent shows result with block explorer link

## Part 3 — How to Use It

### Step 1 — Install the Skill Engine

```bash
unzip pharos-skill-engine-0.1.0.zip
cd pharos-skill-engine-0.1.0

# Explore the structure
ls
# → SKILL.md  assets/  references/
```

### Step 2 — Install Foundry

Foundry is mandatory. The Skill Engine has no fallback — do not try curl/JSON-RPC workarounds.

```bash
# Check if already installed
which cast

# If not found, install Foundry
curl -L https://foundry.paradigm.xyz | bash
source ~/.zshenv && foundryup

# Verify both tools are available
cast --version
forge --version
```

### Step 3 — Configure Your Wallet

```bash
# Export your private key as an environment variable
export PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE

# Confirm the address it derives
cast wallet address --private-key $PRIVATE_KEY

# Set convenience aliases
export RPC=https://atlantic.dplabs-internal.com
export DEPLOYER=$(cast wallet address --private-key $PRIVATE_KEY)
```

> **⚠️ Private Key Safety.** Never hardcode your private key in a script file or commit it to git. Always use the `$PRIVATE_KEY`env var pattern. The Skill Engine will never auto-read it — you must pass it explicitly.

### Step 4 — Start Using Capabilities

#### Query Operations (free — no gas)

```bash
# Native PHRS balance
cast balance $DEPLOYER --rpc-url $RPC --ether

# ERC20 token balance (e.g. USDC on testnet)
cast call 0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8 "balanceOf(address)(uint256)" \
  $DEPLOYER --rpc-url $RPC

# Transaction status
cast tx 0xYOUR_TX_HASH --rpc-url $RPC
cast receipt 0xYOUR_TX_HASH --rpc-url $RPC

# Read any contract method
cast call <contract> "<method()(<returnType>)>" --rpc-url $RPC
```

#### Send Transactions

```bash
# Send native PHRS to an address
cast send <recipient> --value 0.1ether --private-key $PRIVATE_KEY --rpc-url $RPC

# Call a contract write method
cast send <contract> "<method(paramType)>" <arg> --private-key $PRIVATE_KEY --rpc-url $RPC

# Call a payable method (sending PHRS with the call)
cast send <contract> "<method()>" --value 0.01ether --private-key $PRIVATE_KEY --rpc-url $RPC
```

#### Estimate Gas Before Sending

```bash
# Estimate gas for a native transfer
cast estimate <recipient> --value 0.1ether --rpc-url $RPC

# Estimate gas for a contract call
cast estimate <contract> "<method(paramType)>" <arg> --rpc-url $RPC

# Get current gas price
cast gas-price --rpc-url $RPC
```

#### Deploy and Verify a Contract

```bash
# 1. Pre-checks
cast wallet address --private-key $PRIVATE_KEY
cast balance $DEPLOYER --rpc-url $RPC --ether

# 2. Run the deploy script
forge script script/DeployMyContract.s.sol:DeployMyContract \
  --rpc-url $RPC \
  --private-key $PRIVATE_KEY \
  --broadcast

# 3. Wait 10 seconds, then verify
sleep 10
forge verify-contract <contract_address> src/MyContract.sol:MyContract \
  --chain-id 688689 \
  --verifier-url https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract \
  --verifier blockscout
```

## Part 4 — How to Publish New Skills

Publishing a new skill means extending the Skill Engine, so an AI agent can perform a new on-chain operation — for example, interacting with a vault contract, a governance contract, a staking protocol, etc. The process has three stages.

### Stage 1 — Write the Contract

The contract is the source of truth. Every public function and event you write becomes a command in the reference. Write the contract first — the reference is derived from it.

Good contracts for skills have:

* Clear `public` or `external` functions with descriptive names
* Events emitted on every state change (so the agent can show `cast logs` output)
* View functions for reading state (`getBalance`, `timeLeft`, `owner`, etc.)
* Revert messages that are human-readable (`"Still locked"`, `"Not owner"`, `"Must send PHRS"`)

Copy the contract into your project:

```bash
# Convention used by the Skill Engine
cp MySkillContract.sol assets/<skill>/MySkillContract.sol
cp MySkillContract.sol src/<skill>/MySkillContract.sol

# Compile to confirm no errors
forge build
```

### Stage 2 — Write the Reference File

The reference file is what the agent reads when a user invokes your skill. It must be precise and complete — the agent runs commands exactly as written.

### Reference File Structure (`references/<skill>.md`)

Every section in the reference follows this template:

```markdown

# <Skill Name> Operation Instructions
 
> Network Configuration: <rpc> is read from assets/networks.json.
> Private Key: Pass explicitly via --private-key $PRIVATE_KEY.
 
---
 
## <Operation Name>
 
### Overview
<One paragraph explaining what this operation does.>

### Command Template

<exact cast or forge command>
 
### Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| <param>   | <type> | Yes    | <description> |
 
### Output Parsing
| Field | Description |
|-------|-------------|
| <field> | <how to interpret it> |
 
### Error Handling
| Error Signature | Cause | Suggested Action |
|----------------|-------|----|
| <error text> | <why> | <fix> |
 
> **Agent Guidelines**:
> 1. Complete Write Operation Pre-checks (see SKILL.md)
> 2. <next step>
> 3. <next step>
```

> **Note:** When writing your reference file, replace `<exact cast or forge command>` with the actual command formatted as a `bash` code block.

#### What to Cover in Your Reference File

| Operation Type  | Include?           | Notes                                  |
| --------------- | ------------------ | -------------------------------------- |
| Deploy contract | Always             | Include script generation step         |
| Verify contract | Always             | Include `--constructor-args` if needed |
| Write functions | All public ones    | One section per function               |
| Read functions  | Useful ones        | Skip trivial getters                   |
| Events          | All emitted events | Include `cast logs` query              |
| Error cases     | All revert strings | Map each to a suggested fix            |

### Stage 3 — Register in SKILL.md

Open `SKILL.md` and add rows to the Capability Index table for every user intent your skill handles.

#### Capability Index Format

```markdown

| User Need | Capability | Detailed Instructions |
|-----------|------------|----------------------|
| Deploy MySkill / [synonyms] | forge script + built-in MySkill template | → references/<skill>.md |
| Call myAction on MySkill    | cast send myAction()                    | → references/<skill>.md#section |
| Check MySkill state         | cast call getState()                    | → references/<skill>.md#section |
| Query MySkill events        | cast logs                               | → references/<skill>.md#section |

```

> **Write for how users actually speak.** The "User Need" column should capture how a non-technical user would phrase the request — not just the function name. Include synonyms. The agent matches intents, not exact phrases.

#### Complete SKILL.md Registration Checklist

* Add a row in the Capability Index for every public write function in your contract
* Add a row for every public read function (if useful to expose)
* Add a row for event querying if your contract emits events
* Add a row for contract verification
* Include common natural language phrasings as synonyms in the User Need column
* Point each row to the exact anchor in your reference file

#### Publishing Checklist

| Check                        | What to Verify                                                                                            |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- |
| Contract compiles            | `forge build` succeeds with no errors or warnings                                                         |
| Contract deployed on testnet | Confirmed transaction hash and contract address on `atlantic.pharosscan.xyz`                              |
| Contract verified            | Source code visible on Pharos Scan with green verified badge                                              |
| Reference file complete      | Every public function has a command template, parameter table, output parsing, and error handling section |
| Agent Guidelines written     | Each section has numbered steps the agent follows in order                                                |
| Capability Index updated     | `SKILL.md` has rows for every operation, with natural language phrasings                                  |
| 10-second delay documented   | Verification section in reference file mentions the `sleep 10` indexer delay                              |
| Assets copied                | Contract template in both `assets/<skill>/` and `src/<skill>/` if applicable                              |
| Error messages match         | Revert messages in error handling table match exact strings in the Solidity contract                      |

## Part 5 — Global Error Reference

| Error / CLI Signature               | Cause                                                      | Fix                                                                              |
| ----------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `invalid address`                   | Address format is wrong                                    | Confirm address is `0x` + 40 hex characters (42 chars total)                     |
| `transaction not found`             | TX hash doesn't exist or node hasn't synced                | Double-check the hash; wait and retry if the node is still syncing               |
| Empty return value from `cast call` | No contract code at the target address                     | Confirm the contract address and that you're on the right network                |
| `execution reverted`                | Contract call reverted                                     | Extract the revert reason from the error message; check business logic           |
| `insufficient funds`                | Wallet balance too low for amount + gas                    | Run `cast balance --ether` and compare against required amount                   |
| `nonce too low`                     | Nonce conflict — previous transaction still pending        | Wait for prior transaction to confirm, or manually set `--nonce`                 |
| `connection refused`                | Missing `--rpc-url`; Foundry defaulted to `localhost:8545` | Always pass `--rpc-url $RPC` explicitly                                          |
| `assets/networks.json` unreadable   | Config file missing or malformed JSON                      | Confirm you are running commands from inside the `pharos-skill-engine` directory |
| `psycopg2` / SQL error on verify    | `forge verify-contract` run too soon after deployment      | Add `sleep 10` between deployment and verification                               |
| `PRIVATE_KEY` not set               | `export PRIVATE_KEY` not run in current shell              | Run: `export PRIVATE_KEY=0x...`                                                  |
| `forge/cast: command not found`     | Foundry not installed                                      | Run: \`curl -L <https://foundry.paradigm.xyz>                                    |

## Part 6 — Quick Reference

### All Commands at a Glance

```bash
# ── SETUP ──────────────────────────────────────────────────────────
curl -L https://foundry.paradigm.xyz | bash && source ~/.zshenv && foundryup
export PRIVATE_KEY=0x...
export RPC=https://atlantic.dplabs-internal.com
export DEPLOYER=$(cast wallet address --private-key $PRIVATE_KEY)

# ── QUERY (free — no gas) ──────────────────────────────────────────
cast balance $DEPLOYER --rpc-url $RPC --ether
cast call <token> "balanceOf(address)(uint256)" $DEPLOYER --rpc-url $RPC
cast tx <tx_hash> --rpc-url $RPC
cast receipt <tx_hash> --rpc-url $RPC
cast call <contract> "<method()(<returns>)>" --rpc-url $RPC

# ── TRANSACTIONS ───────────────────────────────────────────────────
cast send <to> --value 0.1ether --private-key $PRIVATE_KEY --rpc-url $RPC
cast send <contract> "<method(type)>" <arg> --private-key $PRIVATE_KEY --rpc-url $RPC
cast send <contract> "<payable()>" --value 0.01ether --private-key $PRIVATE_KEY --rpc-url $RPC

# ── GAS ────────────────────────────────────────────────────────────
cast estimate <to> --value 0.1ether --rpc-url $RPC
cast gas-price --rpc-url $RPC

# ── DEPLOY ─────────────────────────────────────────────────────────
forge script script/Deploy<Name>.s.sol:<Name> \
  --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast

# ── VERIFY ─────────────────────────────────────────────────────────
sleep 10
forge verify-contract <addr> src/<path>:<Name> \
  --chain-id 688689 \
  --verifier-url https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract \
  --verifier blockscout

# ── EVENTS ─────────────────────────────────────────────────────────
cast logs --from-block 0 --address <contract> "<Event(types)>" --rpc-url $RPC

# ── AIRDROP (large scale) ──────────────────────────────────────────
CSV_PATH=airdrop.csv BATCH_SIZE=200 \
  forge script assets/airdrop/BatchAirdrop.s.sol:BatchAirdrop \
  --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast
```


# Piggy Bank Tutorial

This guide walks through building a time-locked native token vault (SimpleVault/PiggyBank) as a Pharos Skill — from setting up the Skill Engine in VS Code, writing the contract and reference file inside the Skill Engine folder, updating `SKILL.md`, to testing everything through Claude in your VS Code terminal.

## What You Will Build

By the end of this guide you will have a published Pharos skill that lets anyone ask an AI to:

* Deploy a **Simple Vault** (piggy bank) contract on Pharos
* Deposit PHRS into the vault
* Check how much time remains before withdrawal is allowed
* Withdraw funds after the time lock expires
* Query the full on-chain history of deposits and withdrawals

Along the way, you will learn three core Solidity/EVM concepts:

| Concept           | Where It Appears                          |
| ----------------- | ----------------------------------------- |
| `--value` flag    | Sending native PHRS with a function call  |
| `block.timestamp` | Setting and enforcing a time lock         |
| Event querying    | Reading on-chain history with `cast logs` |

## How the Skill Engine Works

### What is a Pharos Skill?

A Pharos Skill is a folder of files that teaches an AI (Claude) how to interact with the Pharos blockchain. When you ask the AI something like *"Deploy a vault on Pharos"*, it reads your skill files to know exactly which commands to run.

The AI is smart but it does not automatically know your contract. You teach it by writing instruction files in plain Markdown. The AI reads those files and follows them precisely.

### The Flow from User Prompt to On-Chain Action

```
User says "Deploy a Simple Vault with a 1 hour lock"
        ↓
AI reads SKILL.md → finds "Deploy Simple Vault" row in Capability Index
        ↓
AI reads references/vault.md → finds the Deploy section
        ↓
AI follows the Agent Guidelines step by step
        ↓
AI copies SimpleVault.sol to your project, generates deploy script, runs it
        ↓
Contract is live on Pharos. AI shows you the address and explorer link.
```

### Folder Structure Overview

```
pharos-skill-engine/
│
├── SKILL.md                  ← Entry point. Tells the AI what this skill does
│                               and maps user needs to reference files.
│
├── assets/                   ← Files the AI uses directly
│   ├── networks.json         ← RPC URLs, chain IDs, explorer URLs for Pharos
│   ├── erc20/                ← Built-in ERC20 contract template
│   ├── airdrop/              ← Built-in airdrop contracts
│   └── vault/                ← Your vault contract lives here
│
└── references/               ← Instruction manuals written for the AI
    ├── query.md              ← Teaches AI how to check balances
    ├── transaction.md        ← Teaches AI how to send transactions
    ├── contract.md           ← Teaches AI how to deploy contracts
    └── vault.md              ← Your new file — teaches AI about the vault
```

### What is a Reference File?

A reference file is a Markdown file the AI reads at runtime. It is not code and not meant for humans to execute directly. It tells the AI:

* Which `cast` or `forge` command to run
* What each parameter in the command means
* What the terminal output means
* What errors can happen and what to tell the user

Every section in a reference file follows the same 4-part structure:

```
1. Command Template   — the actual command with <placeholders>
2. Parameters table   — what each <placeholder> means
3. Output Parsing     — what fields to look for in the output
4. Error Handling     — what can go wrong and how to respond
```

Plus an `> Agent Guidelines` block that gives the AI an ordered checklist to follow.

## Prerequisites

### Tools to Install

**Foundry** — compiles and deploys Solidity contracts

```bash
curl -L https://foundry.paradigm.xyz | bash
source ~/.zshenv && foundryup
```

Verify installation:

```bash
cast --version
forge --version
```

Both should print a version number. If either fails, re-run `foundryup`.

**Claude Code** — the AI CLI that runs your skill

```bash
npm install -g @anthropic-ai/claude-code
```

Verify:

```bash
claude --version
```

### Wallet Setup

1. Create an EVM-compatible wallet (MetaMask or any EVM wallet)
2. Add the Pharos Atlantic Testnet to your wallet:
   * **Network Name:** `Pharos Atlantic Testnet`
   * **RPC URL:** `https://atlantic.dplabs-internal.com`
   * **Chain ID:** `688689`
   * **Symbol:** `PHRS`
3. Get testnet PHRS from the Pharos faucet or directly from the team.
4. Export your private key — you will need it when deploying

> ⚠️ **Security Warning:** Never share your private key. Never commit it to git. Always use the `$PRIVATE_KEY`environment variable — never paste it directly into commands you share with others.

## Project Structure

Before you start, your skill folder should look like this:

```
pharos-skill-engine-0.1.0/
├── SKILL.md
├── assets/
│   ├── networks.json
│   ├── tokens.json
│   ├── erc20/
│   │   └── StandardERC20.sol
│   ├── airdrop/
│   │   └── (airdrop contracts)
│   └── templates/
│       └── (script templates)
└── references/
    ├── contract.md
    ├── query.md
    ├── script-gen.md
    └── transaction.md
```

After completing this guide it will look like this:

```
pharos-skill-engine-0.1.0/
├── SKILL.md                          ← edited
├── assets/
│   └── vault/
│       └── SimpleVault.sol           ← new
└── references/
    └── vault.md                      ← new
```

You are adding 2 new files and making 2 small edits to `SKILL.md`.

## Step 1 — Write the Smart Contract

### Create the File

Create a new folder and file at this exact path inside the Skill Engine folder:

```
assets/vault/SimpleVault.sol
```

### The Contract

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

contract SimpleVault {
    address public owner;
    uint256 public unlockTime;

    event Deposited(address indexed sender, uint256 amount, uint256 unlockTime);
    event Withdrawn(address indexed owner, uint256 amount);

    constructor(uint256 _lockDurationSeconds) {
        owner = msg.sender;
        unlockTime = block.timestamp + _lockDurationSeconds;
    }

    function deposit() external payable {
        require(msg.value > 0, "Must send PHRS");
        emit Deposited(msg.sender, msg.value, unlockTime);
    }

    function withdraw() external {
        require(msg.sender == owner, "Not owner");
        require(block.timestamp >= unlockTime, "Still locked");
        uint256 balance = address(this).balance;
        require(balance > 0, "Nothing to withdraw");
        payable(owner).transfer(balance);
        emit Withdrawn(owner, balance);
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }

    function timeLeft() external view returns (uint256) {
        if (block.timestamp >= unlockTime) return 0;
        return unlockTime - block.timestamp;
    }
}
```

#### Contract Explained

| Part                                | What It Does                                                                       |
| ----------------------------------- | ---------------------------------------------------------------------------------- |
| `owner`                             | Stores the deployer's address. Only this address can withdraw.                     |
| `unlockTime`                        | The Unix timestamp after which withdrawal is allowed. Set once at deploy.          |
| `constructor(_lockDurationSeconds)` | Runs once at deploy. Sets `unlockTime = now + duration`.                           |
| `deposit()`                         | Marked `payable` — means it can receive PHRS. Requires `msg.value > 0`.            |
| `withdraw()`                        | Checks caller is owner AND current time is past `unlockTime`. Sends all PHRS back. |
| `timeLeft()`                        | Read-only. Returns seconds remaining. Returns `0` if unlocked.                     |
| `getBalance()`                      | Read-only. Returns how much PHRS is currently in the vault.                        |
| `Deposited` event                   | Logged on-chain every time someone deposits. Queryable with `cast logs`.           |
| `Withdrawn` event                   | Logged on-chain when owner withdraws. Queryable with `cast logs`.                  |

### Verify the Contract Compiles

```bash
cd ~/Desktop
forge init vault-test
cd vault-test
cp /path/to/pharos-skill-engine-0.1.0/assets/vault/SimpleVault.sol src/SimpleVault.sol
forge build
```

You should see `Compiler run successful`. If you see errors, check that the Solidity version in `foundry.toml` is `^0.8.20` or higher.

## Step 2 — Create the Reference File

### Create the File

Create a new file at:

```
references/vault.md
```

### Why This File Exists

This is the instruction manual the AI reads when a user asks about the vault. Without this file the AI has no idea your vault contract exists or how to use it.

### The Complete File Contents

Paste this entire block into `references/vault.md`:

````
**# Vault Operation Instructions**

This file contains detailed instructions for deploying and interacting with the
SimpleVault (Piggy Bank) contract on Pharos. It teaches three key concepts:
sending native tokens with `--value`, using `block.timestamp` for time locks,
and querying on-chain events.

> **Network Configuration**: The `<rpc>` parameter in all commands is read from
> the corresponding network's `rpcUrl` field in `assets/networks.json`.
> Defaults to the Atlantic testnet.
>
> **Private Key Configuration**: All write operations must explicitly pass the
> private key via the `--private-key` parameter.
> Recommended: `--private-key $PRIVATE_KEY`.

---

**## Deploy SimpleVault**

**### Overview**

SimpleVault is a piggy bank contract. The deployer sets a time lock duration in
seconds at deployment. PHRS deposited into the vault cannot be withdrawn until
the lock expires.

**### Step 1: Generate Deployment Script**

The Agent generates `script/DeploySimpleVault.s.sol` in the user's project:

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

import "forge-std/Script.sol";
import "forge-std/console.sol";
import "";

contract DeploySimpleVault is Script {
    function run() external {
        uint256 lockDurationSeconds = ;
        vm.startBroadcast();
        SimpleVault vault = new SimpleVault(lockDurationSeconds);
        console.log("=== Deploy Result ===");
        console.log("Vault address:", address(vault));
        console.log("Owner:", vault.owner());
        console.log("Unlock time (unix):", vault.unlockTime());
        console.log("Lock duration (seconds):", lockDurationSeconds);
        console.log("Deployer:", msg.sender);
        vm.stopBroadcast();
    }
}
```

**### Step 2: Execute Deployment**

**Command Template**

```bash
forge script script/DeploySimpleVault.s.sol:DeploySimpleVault \
  --rpc-url  \
  --private-key $PRIVATE_KEY \
  --broadcast
```

**Parameters**

|** Parameter **|** Type **|** Required **|** Description **|
|---|---|---|---|
| `lockDurationSeconds` | uint256 | Yes | Lock duration in seconds. `3600` = 1 hour, `86400` = 1 day |
| `--rpc-url` | string | Yes | RPC endpoint URL, read from `assets/networks.json` |
| `--private-key` | string | Yes | Deployer private key via `$PRIVATE_KEY` |

**Output Parsing**

|** Field **|** Description **|
|---|---|
| `Vault address:` | Deployed contract address — save this for all future interactions |
| `Owner:` | Address that can withdraw funds |
| `Unlock time (unix):` | Unix timestamp when withdrawal becomes allowed |

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| `compiler error` | Contract compilation failed | Check source file path and Foundry setup |
| `insufficient funds` | Not enough balance for gas | Check balance with `cast balance` |
| `connection refused` | Missing `--rpc-url` or RPC unreachable | Confirm `--rpc-url` is passed |

> **Agent Guidelines:**
> 1. Complete "Write Operation Pre-checks" (see SKILL.md)
> 2. Ask user for `lockDurationSeconds` (suggest `3600` for 1 hour as default)
> 3. Copy `assets/vault/SimpleVault.sol` to the user's project at `src/vault/SimpleVault.sol`
> 4. Check deployer balance: `cast balance <deployer> --rpc-url <rpc> --ether`
> 5. Generate `script/DeploySimpleVault.s.sol` with the lock duration filled in
> 6. Read `rpcUrl` from `assets/networks.json`
> 7. Execute `forge script` with `--rpc-url`, `--private-key $PRIVATE_KEY`, `--broadcast`
> 8. Extract vault address from output, show block explorer link: `<explorerUrl>/address/<vaultAddress>`
> 9. Ask if user wants to verify the contract. If yes, wait ~10 seconds then verify.

---

**## Verify SimpleVault**

**Command Template**

```bash
forge verify-contract  src/vault/SimpleVault.sol:SimpleVault \
  --chain-id  \
  --verifier-url /v1/explorer/command_api/contract \
  --verifier blockscout \
  --constructor-args $(cast abi-encode "constructor(uint256)" )
```

**Parameters**

|** Parameter **|** Type **|** Required **|** Description **|
|---|---|---|---|
| `<vault_address>` | string | Yes | Deployed vault contract address |
| `<chain_id>` | number | Yes | Read from `chainId` in `assets/networks.json` |
| `<explorer_api_url>` | string | Yes | Read from `explorerApiUrl` in `assets/networks.json` |
| `<lockDurationSeconds>` | uint256 | Yes | Same value used during deployment |

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| `contract not found` | Contract not indexed yet | Wait 10–15 seconds and retry |
| `verification failed` | Source mismatch | Check compiler version matches |
| `constructor arguments mismatch` | Wrong args encoding | Confirm `lockDurationSeconds` value |

---

**## Deposit PHRS into the Vault**

**Command Template**

```bash
cast send  "deposit()" \
  --value ether \
  --private-key $PRIVATE_KEY \
  --rpc-url 
```

**Parameters**

|** Parameter **|** Type **|** Required **|** Description **|
|---|---|---|---|
| `<vault_address>` | string | Yes | Vault contract address from deployment |
| `<amount>` | number | Yes | Amount of PHRS to deposit. Example: `0.1ether`, `1ether` |
| `--value` | flag | Yes | Sends native PHRS with the call. Without it the call sends 0 PHRS and reverts |
| `--private-key` | string | Yes | Sender private key via `$PRIVATE_KEY` |
| `--rpc-url` | string | Yes | RPC endpoint URL from `assets/networks.json` |

**Output Parsing**

|** Field **|** Description **|
|---|---|
| `status` | `1` = success, `0` = failed |
| `transactionHash` | Use to look up the Deposited event on the explorer |

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| `execution reverted: Must send PHRS` | `--value` flag missing or 0 | Add `--value <amount>ether` to the command |
| `insufficient funds` | Wallet balance too low | Check with `cast balance <address> --rpc-url <rpc> --ether` |
| `invalid address` | Wrong vault address format | Confirm address is `0x` + 40 hex chars |

> **Agent Guidelines:** Complete "Write Operation Pre-checks" (see SKILL.md).
> Check wallet balance before sending. After successful deposit, show transaction
> link `<explorerUrl>/tx/<txHash>` and remind user of the unlock time.

---

**## Check Vault Balance**

**Command Template**

```bash
cast call  "getBalance()(uint256)" --rpc-url 
```

**Output Parsing**

- Returns raw balance in wei
- Convert to PHRS: `cast --from-wei <value>`

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| Empty return value | No contract at that address | Confirm vault address is correct |
| `invalid address` | Wrong address format | Check address is `0x` + 40 hex characters |

---

**## Check Time Remaining Before Withdrawal**

**Command Template**

```bash
cast call  "timeLeft()(uint256)" --rpc-url 
```

**Output Parsing**

- Returns seconds remaining as a number
- `0` means the vault is unlocked — withdrawal is allowed
- Any positive number means that many seconds remain

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| Empty return value | No contract at that address | Confirm vault address is correct |

> **Agent Guidelines:** Convert seconds to human-readable format
> (e.g. "59 minutes 2 seconds remaining" or "Vault is unlocked — you can withdraw now").
> Also show `unlockTime` with:
> `cast call <vault_address> "unlockTime()(uint256)" --rpc-url <rpc>`

---

**## Withdraw PHRS from the Vault**

**Command Template**

```bash
cast send  "withdraw()" \
  --private-key $PRIVATE_KEY \
  --rpc-url 
```

**Parameters**

|** Parameter **|** Type **|** Required **|** Description **|
|---|---|---|---|
| `<vault_address>` | string | Yes | Vault contract address |
| `--private-key` | string | Yes | Must be the owner's key — only the deployer can withdraw |
| `--rpc-url` | string | Yes | RPC endpoint URL from `assets/networks.json` |

**Output Parsing**

|** Field **|** Description **|
|---|---|
| `status` | `1` = success, `0` = failed |
| `transactionHash` | Use to look up the Withdrawn event on the explorer |

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| `execution reverted: Still locked` | Time lock not expired yet | Run `timeLeft()` to see remaining time, wait and retry |
| `execution reverted: Not owner` | Wrong private key | Confirm `$PRIVATE_KEY` belongs to the deployer wallet |
| `execution reverted: Nothing to withdraw` | Vault balance is 0 | Deposit PHRS first |
| `insufficient funds` | Not enough gas | Check wallet balance |

> **Agent Guidelines:** Always run `timeLeft()` before attempting withdrawal.
> If result > 0, tell the user how long to wait and do NOT execute `withdraw()`.
> Only proceed when `timeLeft()` returns `0`.
> After success, show `<explorerUrl>/tx/<txHash>`.

---

**## Query Events (Deposit and Withdrawal History)**

**### Query Deposit History**

**Command Template**

```bash
cast logs \
  --rpc-url  \
  --address  \
  "Deposited(address,uint256,uint256)"
```

**Output Parsing**

|** Field **|** Description **|
|---|---|
| `topics[1]` | Depositor address (indexed) |
| `data[0]` | Amount deposited in wei — divide by 10^18 for PHRS |
| `data[1]` | Unlock time as Unix timestamp |
| `blockNumber` | Block where the deposit happened |
| `transactionHash` | Deposit transaction hash |

**### Query Withdrawal History**

**Command Template**

```bash
cast logs \
  --rpc-url  \
  --address  \
  "Withdrawn(address,uint256)"
```

**Output Parsing**

|** Field **|** Description **|
|---|---|
| `topics[1]` | Owner address (indexed) |
| `data[0]` | Amount withdrawn in wei — divide by 10^18 for PHRS |

**Error Handling**

|** Error **|** Cause **|** Fix **|
|---|---|---|
| Empty result | No events emitted yet | Inform user no activity has occurred on this vault |
| `invalid address` | Wrong vault address | Confirm address format |
| Connection timeout | RPC unreachable | Check network connection |

> **Agent Guidelines:** Convert wei to PHRS (divide by 10^18). Convert unix
> timestamps to readable dates. Include transaction links
> `<explorerUrl>/tx/<txHash>` for each event. If no logs returned, clearly
> state no activity has occurred yet.
````

## Step 3 — Edit SKILL.md

You need to make two small edits to `SKILL.md`.

### Edit 1 — Add Vault Keywords to the Description

Find the end of the `description` field. It currently ends with:

```
or generate Web3 scripts targeting Pharos Chain / Pharos Network. Do not attempt...
```

Change it to:

```
or generate Web3 scripts targeting Pharos Chain / Pharos Network, or deploy a
vault, piggy bank, time lock, lock PHRS, or deposit and withdraw with a
time-locked contract. Do not attempt...
```

**Why:** This controls when the AI triggers your skill. Adding these keywords means the AI will load your skill whenever someone asks about vaults or time locks.

### Edit 2 — Add Rows to the Capability Index Table

Find the last row of the Capability Index table. It currently ends with:

```
| Generate contract interaction scripts ... | → `references/script-gen.md` |
```

Add these 5 rows directly after it:

```markdown
| Deploy Simple Vault / Piggy Bank (time-locked PHRS deposit) | `forge script` + built-in Vault template | → `references/vault.md` |
| Deposit PHRS into vault | `cast send` with `--value` | → `references/vault.md#deposit-phrs-into-the-vault` |
| Check vault time lock / time remaining | `cast call timeLeft()` | → `references/vault.md#check-time-remaining-before-withdrawal` |
| Withdraw from vault after time lock expires | `cast send withdraw()` | → `references/vault.md#withdraw-phrs-from-the-vault` |
| Query vault deposit and withdrawal events | `cast logs` | → `references/vault.md#query-events-deposit-and-withdrawal-history` |
```

**Why:** This table is how the AI navigates the skill. When a user asks to deposit PHRS, the AI scans this table, finds the matching row, and opens the reference file linked there.

## Step 4 — Run and Test the Skill

### Setup (One Time Only)

1. **Set your private key in the terminal:**

```bash
export PRIVATE_KEY=0xYourPrivateKeyHere
```

2. **Open Claude Code inside the skill folder:**

```bash
cd /path/to/pharos-skill-engine-0.1.0
```

```bash
claude
```

### Full End-to-End Test Sequence

Run these prompts in order inside Claude Code:

***

3. **Deploy**

> *Deploy a Simple Vault on Pharos with a 5 minute lock*

The AI will ask you to confirm your wallet address and network, check your balance, generate the deploy script, and run it. You will get back a vault address like `0xAbc123...`.

***

4. **Deposit**

> *Deposit 0.1 PHRS into vault at 0xYourVaultAddress*

The AI runs `cast send` with `--value 0.1ether`. You will see a transaction hash.

***

5. **Check Time Remaining**

> *How much time is left on my vault at 0xYourVaultAddress*

The AI runs `timeLeft()` and tells you something like "4 minutes 32 seconds remaining."

***

6. **Try Early Withdrawal — Should Fail Gracefully**

> *Withdraw from my vault at 0xYourVaultAddress*

The AI checks `timeLeft()` first, sees the lock has not expired, and tells you to wait. It will **not** send the transaction.

***

7. **Withdraw After Lock Expires**

Wait 5 minutes, then repeat the withdraw prompt. This time the AI will execute `withdraw()` and your PHRS comes back to your wallet.

***

8. **Query Events**

> *Show me the deposit history for vault 0xYourVaultAddress*

The AI runs `cast logs` and shows you the `Deposited` event with the amount, timestamp, and transaction link.

## Common Errors and Fixes

| Error                              | Cause                       | Fix                                                 |
| ---------------------------------- | --------------------------- | --------------------------------------------------- |
| `Still locked`                     | Too early to withdraw       | Run `timeLeft()` and wait                           |
| `Not owner`                        | Wrong wallet                | Check `$PRIVATE_KEY` matches the deployer address   |
| `Must send PHRS`                   | Missing `--value` flag      | Add `--value 0.1ether` to the deposit command       |
| `Nothing to withdraw`              | Vault is empty              | Deposit PHRS first                                  |
| `insufficient funds`               | Not enough PHRS for gas     | Get testnet PHRS from the faucet                    |
| `connection refused`               | Missing `--rpc-url`         | Always pass `--rpc-url` explicitly in every command |
| `compiler error`                   | Solidity compilation failed | Check Foundry is up to date with `foundryup`        |
| `contract not found` during verify | Explorer not indexed yet    | Wait 10–15 seconds and retry verification           |


# Anvita Flow

> AI agent infrastructure

## What is Anvita Flow?

Anvita Flow is an AI agent infrastructure. It provides the underlying layer that lets developers convert their Pharos Skills into hosted, discoverable, and callable Service Agents, without building their own runtime, routing, or settlement infrastructure.

## Core Concepts

| Concept               | What It Is                                                                                                     |
| --------------------- | -------------------------------------------------------------------------------------------------------------- |
| **Skill**             | A packaged set of on-chain capabilities built using the Pharos Skill Engine                                    |
| **Service Agent**     | A hosted runtime that wraps a Skill, gives it an identity, and exposes it as a callable service on the network |
| **Steward Agent**     | A user's personal AI assistant that finds and calls Service Agents on their behalf                             |
| **Anvita On**         | The user-facing chat interface where users interact with their Steward Agent                                   |
| **Agent Card**        | The public profile of a Service Agent — name, capabilities, example tasks, pricing                             |
| **Marketplace**       | The registry where all published Service Agents are listed and discoverable                                    |
| **x402 Protocol**     | The micropayment protocol powering per-call billing and value settlement                                       |
| **Developer Console** | The interface where developers upload, debug, and publish Service Agents                                       |

## What Anvita Flow Provides

As infrastructure, Anvita Flow handles everything between your Skill and the end user:

| Layer              | What It Does                                                              |
| ------------------ | ------------------------------------------------------------------------- |
| **Agent hosting**  | Runs your Service Agent in a managed runtime                              |
| **Agent identity** | Generates a unique identity and service profile for each published agent  |
| **Discovery**      | Lists your Service Agent in the Marketplace so Steward Agents can find it |
| **Routing**        | Connects incoming requests to the right Service Agent                     |
| **Settlement**     | Handles per-call billing automatically via the x402 micropayment protocol |

## The Call Flow

```
User makes a request in Anvita On
        ↓
Steward Agent searches the Marketplace for a matching Service Agent
        ↓
Anvita Flow routes the request to the Service Agent
        ↓
Service Agent runs the Skill and returns the result
        ↓
Steward Agent delivers the result to the user
        ↓
Settlement handled automatically via x402
```

## The Developer Flow

```
Write a Pharos Skill
        ↓
Package it as a zip with SKILL.md at the root
        ↓
Upload to the Anvita Flow Developer Console
        ↓
Configure runtime and complete the Agent Card
        ↓
Debug and publish
        ↓
Service Agent is live — discoverable and callable on the network
```

## Quick Links

| Resource              | URL                                            |
| --------------------- | ---------------------------------------------- |
| Anvita Flow Home      | <https://flow.anvita.xyz/home>                 |
| Anvita On (User Chat) | <https://flow.anvita.xyz/agent/chat>           |
| Developer Console     | <https://flow.anvita.xyz/service-agents>       |
| Wallet & Earnings     | <https://flow.anvita.xyz/dashboard>            |
| Publishing Guide      | See: Publishing a Service Agent on Anvita Flow |


# Publish Service Agent

This guide walks you through publishing your Skill as a hosted Service Agent on Anvita Flow, so other Agents on the network can discover and call it.

## 1. What You Will Build

By the end of this guide your Skill will be live as a published Service Agent on Anvita Flow — discoverable and callable by any other Agent on the network.

**Overall flow:**

```
Submit Skill
        ↓
Hosted runtime runs it
        ↓
Service Agent identity & service profile generated
        ↓
Published to Marketplace
        ↓
Discovered and called by other Agents
```

### Two Types of Agent on Anvita Flow

Before starting, understand the distinction:

| Agent Type                    | What It Is                                                                                                                | Who Creates It          |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
| **Service Agent**             | Wraps your Skill. Provides a service that other Agents can discover and call.                                             | You, using this guide   |
| **Steward Agent (Anvita On)** | A user's personal assistant. Receives user requests and finds suitable Service Agents in the Marketplace to fulfill them. | End users via Anvita On |

This guide is about publishing a **Service Agent**.

## 2. How Anvita Flow Works

Once your Service Agent is published, this is the call flow:

```
User makes a request in Anvita On (https://flow.anvita.xyz/agent/chat)
        ↓
Steward Agent searches the Marketplace for a suitable Service Agent
        ↓
Steward Agent calls your Service Agent
        ↓
Your Service Agent runs the Skill and returns the result
        ↓
Steward Agent delivers the result back to the user
```

You can also call your own Service Agent directly to verify it: in Anvita On, tell your Steward Agent *"go find \[your service agent name] to do \[task] for me"*.

## 3. Prerequisites

Before publishing, make sure you have:

* A completed Pharos Skill package with `SKILL.md` at its root
* An Anvita Flow account — register at <https://flow.anvita.xyz/home>
* Access to the Developer Console — if you see a no-permission message, contact the team to be added to the allowlist

> **One account covers both roles.** The same Anvita Flow account works for both ordinary users (Steward Agent) and developers (Service Agent publishing). You don't need a separate account.

## 4. Step 1 — Prepare Your Skill Package

A Skill package is a folder packaged as a `.zip` for upload. It must follow three rules to pass the platform's structure check.

### Rule 1 — Correct Folder Structure

```
your-skill/             ← Package name: replace with your own skill name
├── SKILL.md            ← Required — must be uppercase
├── scripts/            ← Optional
├── references/         ← Optional
├── assets/             ← Optional
└── ...                 ← Optional: any other files your skill needs
```

> **`SKILL.md` must be uppercase.** Non-standard names like `skill.md` will not pass the structure check.

### Rule 2 — Correct Zip Structure

Zip the **entire `your-skill/` folder**, not the files inside it. The top level of the zip must be the folder itself:

```
# ✓ Correct — zip the folder itself
 
your-skill.zip
└── your-skill/
    ├── SKILL.md
    └── ...
```

```
# ✗ Wrong — SKILL.md is at the zip root, folder is missing
 
your-skill.zip
├── SKILL.md
└── ...
```

### Rule 3 — Required Frontmatter in SKILL.md

`SKILL.md` must begin with a frontmatter block containing `name` and `description`. The `name` must match the package folder name **exactly, including case**:

```yaml
---
name: your-skill
description: One sentence describing what this Skill does and when it should be called.
---
```

### One Agent Per Skill

A Service Agent currently hosts a single Skill package. If your repo contains multiple Skills, split them into separate packages and submit each one individually.

## 5. Step 2 — Register and Enter the Console

**1. Register an account**

Go to <https://flow.anvita.xyz/home> and register. One account covers both user and developer roles.

**2. Open the Developer Console**

Two ways to get there:

* Go directly to <https://flow.anvita.xyz/service-agents>

\- Or open \[<https://flow.anvita.xyz/agent/chat]\\(https://flow.anvita.xyz/agent/chat)> and click \*\*Developer Portal\*\* in the lower-left profile area

> The **Add Agent** button on the chat page creates a personal Steward Agent — it is unrelated to publishing a Service Agent. Ignore it.

**3. Start the creation flow**

On the **Service Agents** page, click **Create A Managed Service Agent** to begin the three-step flow below.

## 6. Step 3 — Create the Agent

Fill in the three sections of the creation form:

### Upload Skill Package

Upload your zipped package. You can only proceed after it passes the parsing check. If it fails, revisit [Step 1](#4-step-1--prepare-your-skill-package) and confirm the folder structure, zip method, and frontmatter.

### Customer Service Strategy

Describe how the Service Agent should:

* Understand incoming requests
* Decide when to ask follow-up questions
* Confirm inputs and delivery scope before starting work

### Runtime Configuration

| Setting                   | What It Controls                                                  |
| ------------------------- | ----------------------------------------------------------------- |
| Max concurrent sessions   | How many client Agents the Service Agent can serve simultaneously |
| Max single execution time | The task is automatically interrupted if it exceeds this duration |

## 7. Step 4 — Debug

Before submitting for publishing, run at least one complete debug session:

1. From a client Agent's perspective, send a request to your Service Agent
2. Confirm it returns a correct response
3. One successful end-to-end run is sufficient to proceed

> 💡 **Use your own Steward Agent to test.** In Anvita On, tell it *"go find \[your service agent name] to do \[task] for me"* this simulates exactly how other users will call your Agent after publishing.

## 8. Step 5 — Publish

Complete the public-facing **Agent Card**. This is what the Marketplace shows to Steward Agents when they are searching for a Service Agent to fulfill a user request.

| Field                                      | What to Write                                                              |
| ------------------------------------------ | -------------------------------------------------------------------------- |
| **Agent name**                             | Shown in search results and the Marketplace listing                        |
| **One-sentence introduction**              | Helps client Agents quickly judge if this Service Agent fits their request |
| **Capability description**                 | Which tasks it can complete using public information                       |
| **Example tasks** (min. 2)                 | One example request per line that a Steward Agent might send               |
| **Information required from the customer** | Key inputs needed before the task can start                                |
| **Deliverables**                           | The type and composition of the final result                               |
| **Range not supported**                    | Data or request types it explicitly refuses                                |
| **Estimated execution duration**           | A rough estimate of how long a single task takes                           |
| **Unit price for charges**                 | See [Earnings](#9-earnings) below                                          |

Once complete, submit for review. After approval it is published.

> **Success indicator:** when the Service Agents list shows **Published / Running**, your Service Agent has joined Anvita Flow and is live in the Marketplace.

### How It Gets Called After Publishing

Once published, your Service Agent enters the Anvita Flow Marketplace. When a user makes a request to their Steward Agent in Anvita On (<https://flow.anvita.xyz/agent/chat>), the Steward Agent discovers and calls a suitable Service Agent from the Marketplace, which performs the task and returns the result.

You can also call your own Service Agent: in the Anvita On chat, just tell your Steward Agent "go find xxx service agent to do … for me" to call it directly and verify the result.

## 9. Earnings

Your Service Agent can earn for you by setting a unit price per call.

**How pricing works:**

* Pricing is per-call and fixed
* Set the unit price at publishing time to either `Free` or `X.XX USDC/call`
* The price is shown publicly in the Marketplace
* Each paid call is settled automatically **Enabling your wallet:**

Go to <https://flow.anvita.xyz/dashboard> and enable your wallet. Once enabled, an Agent wallet is generated automatically for every Service Agent you publish.

> **The earnings feature is still in beta.** To avoid call failures, set the public price to **Free** for now.

## 10. Summary

| What You Did                                                       | Where                               |
| ------------------------------------------------------------------ | ----------------------------------- |
| Prepared the Skill package with correct structure and frontmatter  | `your-skill/SKILL.md`               |
| Zipped the folder correctly for upload                             | `your-skill.zip`                    |
| Created the Service Agent with runtime config and service strategy | Anvita Flow Developer Console       |
| Ran a debug session to confirm end-to-end function                 | Anvita Flow Debug tab               |
| Completed the Agent Card and submitted for review                  | Anvita Flow Publish tab             |
| Enabled wallet for earnings (optional)                             | <https://flow.anvita.xyz/dashboard> |

## Common Issues

| Issue                                      | Cause                                       | Fix                                                       |
| ------------------------------------------ | ------------------------------------------- | --------------------------------------------------------- |
| Upload fails structure check               | Wrong zip method or missing `SKILL.md`      | Zip the folder itself, not the files inside it            |
| `SKILL.md` not recognized                  | Filename is lowercase (`skill.md`)          | Rename to uppercase `SKILL.md`                            |
| Frontmatter name mismatch                  | `name` field doesn't match folder name      | Make them match exactly, including case                   |
| No-permission message in console           | Account not on the allowlist                | Contact the Pharos team to be added                       |
| Service Agent not appearing in Marketplace | Still under review or not yet approved      | Wait for approval email and check the Service Agents list |
| Call failures from other Agents            | Unit price set to a paid amount during beta | Set price to `Free` until beta ends                       |


# JSON-RPC API Methods

Pharos Network is a fully EVM-equivalent Layer 1 blockchain, supporting a [JSON-RPC](https://www.jsonrpc.org/specification) interface for seamless blockchain interactions. This RPC API is fully compatible with existing [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) while also providing richer semantics and enhanced services.

## Standard Ethereum Methods

### eth\_gasPrice

Returns the current gas price in Wei.

* Input: None
* Output: `String` - Gas price in Wei

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_gasPrice",
  "params": [],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x2540be400"
}
```

### eth\_maxPriorityFeePerGas

Returns the priority fee needed to be included in a block (EIP-1559).

* Input: None
* Output: `String` - Priority fee in Wei

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_maxPriorityFeePerGas",
  "params": [],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

### eth\_feeHistory

Returns historical gas information for fee estimation (EIP-1559).

* Input:
  * `String` - Number of blocks in the requested range
  * `String` - Highest block of the requested range
  * `Array` - Reward percentiles to sample from each block
* Output: `Object` - Fee history data including baseFeePerGas, gasUsedRatio, and reward

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_feeHistory",
  "params": [
    "0x5",
    "latest",
    [
      25,
      75
    ]
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "baseFeePerGas": [
      "0x2540be400",
      "0x2540be400",
      "..."
    ],
    "gasUsedRatio": [
      0.0,
      0.0,
      "..."
    ],
    "oldestBlock": "0x54ace7",
    "reward": [
      [
        "0x0",
        "0x0"
      ],
      [
        "0x0",
        "0x0"
      ],
      "..."
    ]
  }
}
```

### eth\_blockNumber

Returns the number of the most recent block.

* Input: None
* Output: `String` - Block number in hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_blockNumber",
  "params": [],
  "id": 1
}

// Response
{
  "result": "0x54aceb",
  "id": 1,
  "jsonrpc": "2.0"
}
```

### eth\_chainId

Returns the chain ID of the current network.

* Input: None
* Output: `String` - Chain ID as a hexadecimal string

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_chainId",
  "params": [],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x688"
}
```

### eth\_syncing

Returns the sync status of the node.

* Input: None
* Output: `Object|Boolean` - Sync status object, or `false` if not syncing

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_syncing",
  "params": [],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}
```

### eth\_getBalance

Returns the balance of an address at a given block.

* Input:
  * `String` - Address to query
  * `String` - Block number or tag (`latest`, `earliest`, `pending`, `safe`, `finalized`)
* Output: `String` - Balance in Wei as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
    "latest"
  ],
  "id": 1
}

// Response
{"jsonrpc":"2.0","id":1,"result":"0x0234c8a3397aab58"}
```

### eth\_getTransactionCount

Returns the number of transactions sent from an address (nonce).

* Input:
  * `String` - Address to query
  * `String` - Block number or tag
* Output: `String` - Transaction count as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0xfaC0718D882E5f07e1972D617bd430CD72A55D84",
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0x2"
}
```

### eth\_getStorageAt

Returns the value from a storage position at a given address.

* Input:
  * `String` - Address of the contract
  * `String` - Position in storage
  * `String` - Block number or tag
* Output: `String` - Storage value as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getStorageAt",
  "params": [
    "0x4100000000000000000000000000000000000000",
    "0x0",
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0x0000000000000000..."
}
```

### eth\_getCode

Returns the contract code at a given address.

* Input:
  * `String` - Address to query
  * `String` - Block number or tag
* Output: `String` - Contract bytecode as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getCode",
  "params": [
    "0x4100000000000000000000000000000000000000",
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0x6080604052600a6012565b6022565b5f90565b6018600e565b50601f60b5565b..."
}
```

### eth\_getProof

Returns the account and storage values with Merkle proof (EIP-1186). Pharos supports [Simplified Payment Verification (SPV)](https://github.com/PharosNetwork/docs/blob/main/api-and-sdk/spv-proof-theory-explanation.md) using these proofs.

* Input:
  * `String` - Address
  * `Array` - Array of storage keys
  * `String` - Block number or tag
* Output: `Object` - Account proof including balance, codeHash, nonce, storageHash, and proofs

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getProof",
  "params": [
    "0x4100000000000000000000000000000000000000",
    [
      "0x0"
    ],
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "accountProof": [
      {
        "nextBeginOffset": 0,
        "nextEndOffset": 32,
        "proofNode": "0xe02388eb3621238f..."
      },
      {
        "nextBeginOffset": 387,
        "nextEndOffset": 419,
        "proofNode": "0x0000005846b1739f..."
      },
      "..."
    ],
    "address": "0x4100000000000000000000000000000000000000",
    "balance": "0x34F086F3B33B684000000",
    "codeHash": "0xa556a9adc4732609...",
    "isExist": true,
    "nonce": "0x0",
    "rawValue": "0xef808b034f086f3b...",
    "storageHash": "0xd96ef1a22d793063...",
    "storageProof": [
      {
        "isExist": false,
        "key": "0x0000000000000000...",
        "proof": [
          {
            "nextBeginOffset": 0,
            "nextEndOffset": 32,
            "proofNode": "0xe02388eb3621238f..."
          },
          {
            "nextBeginOffset": 323,
            "nextEndOffset": 355,
            "proofNode": "0x0000005846b1739f..."
          },
          {
            "nextBeginOffset": 163,
            "nextEndOffset": 195,
            "proofNode": "0x000000186be0bf31..."
          },
          {
            "nextBeginOffset": 259,
            "nextEndOffset": 291,
            "proofNode": "0x00000036e93a5eef..."
          },
          {
            "nextBeginOffset": 259,
            "nextEndOffset": 291,
            "proofNode": "0x0000000000000000..."
          }
        ],
        "siblingLeftmostLeafProofs": [
          {
            "leftmostLeafKey": "0x32ec8cb089301315...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015aa0b752210382..."
              }
            ],
            "slotIndex": 0
          },
          {
            "leftmostLeafKey": "0x319d86c4583accd6...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015a63a0b7cfd891..."
              }
            ],
            "slotIndex": 3
          },
          {
            "leftmostLeafKey": "0x76c9cf548b4179f8...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015aa46483878fad..."
              }
            ],
            "slotIndex": 4
          },
          {
            "leftmostLeafKey": "0xf1dcf78b36c5848c...",
            "proofPath": [
              {
                "nextBeginOffset": 227,
                "nextEndOffset": 259,
                "proofNode": "0x0000000000000000..."
              },
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015a758f88ba6af0..."
              }
            ],
            "slotIndex": 5
          },
          {
            "leftmostLeafKey": "0x76c9cf548b4179f8...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015a690bf928e548..."
              }
            ],
            "slotIndex": 9
          },
          {
            "leftmostLeafKey": "0xc7f7e0e747d90acb...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015a8b0d031d5cab..."
              }
            ],
            "slotIndex": 11
          },
          {
            "leftmostLeafKey": "0xf428e3a2624c4aac...",
            "proofPath": [
              {
                "nextBeginOffset": 0,
                "nextEndOffset": 0,
                "proofNode": "0x015a0ff2163c136f..."
              }
            ],
            "slotIndex": 15
          }
        ],
        "value": "0x"
      }
    ]
  }
}
```

### eth\_sendRawTransaction

Submits a signed transaction to the network. Supports legacy, EIP-2930 (access list), and EIP-1559 transaction types.

* Input:
  * `String` - Signed transaction data
* Output: `String` - Transaction hash

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_sendRawTransaction",
  "params": [
    "0xf86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae..."
  ],
  "id": 1
}

// Response
{"jsonrpc":"2.0","id":1,"result":"0xe670ec6434177160..."}
```

### eth\_call

Executes a message call without creating a transaction. Useful for reading contract state.

* Input:
  * `Object` - Transaction call object (from, to, gas, gasPrice, value, data)
  * `String` - Block number or tag
* Output: `String` - Return data as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_call",
  "params": [
    {
      "to": "0x4100000000000000000000000000000000000000",
      "data": "0x76671808"
    },
    "latest"
  ],
  "id": 1
}

// Response
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000..."}
```

### eth\_estimateGas

Estimates the gas needed to execute a transaction.

* Input:
  * `Object` - Transaction call object
  * `String` - Block number or tag (optional)
* Output: `String` - Estimated gas as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_estimateGas",
  "params": [
    {
      "to": "0x4100000000000000000000000000000000000000",
      "data": "0x76671808"
    }
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0x9d4b"
}
```

### eth\_createAccessList

Creates an EIP-2930 access list for a transaction.

> **Note:** This method is still under debugging and may return null values.

* Input:
  * `Object` - Transaction call object
  * `String` - Block number or tag
* Output: `Object` - Access list and estimated gas

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_createAccessList",
  "params": [
    {
      "to": "0x4100000000000000000000000000000000000000",
      "data": "0x76671808"
    },
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "accessList": [
      {
        "address": "0x0000000000000000000000000000000000000000",
        "storageKeys": []
      },
      {
        "address": "0x0000000000000000000000000000000000000001",
        "storageKeys": []
      },
      "..."
    ],
    "gasUsed": "0x9d4b"
  }
}
```

### eth\_getBlockByHash

Returns information about a block by its hash.

* Input:
  * `String` - Block hash
  * `Boolean` - If `true`, returns full transaction objects; if `false`, returns transaction hashes
* Output: `Object` - Block information

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByHash",
  "params": [
    "0xfa15419fc3de49e27f62eff7be1f344a3fd156e332ea9a78337c73a0b2b6573a",
    false
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "baseFeePerGas": "0x2540be400",
    "difficulty": "0x0",
    "extraData": "0x0a0000010000003c...",
    "gasLimit": "0x3b9aca00",
    "gasUsed": "0x0",
    "hash": "0xfa15419fc3de49e2...",
    "logsBloom": "0x0000000000000000...",
    "miner": "0x0000000000000000000000000000000000000000",
    "mixHash": "0x0000000000000000...",
    "nonce": "0x0000000000000000",
    "number": "0x2710",
    "parentHash": "0x1ff0db61e7c714a9...",
    "receiptsRoot": "0x56e81f171bcc55a6...",
    "sha3Uncles": "0x1dcc4de8dec75d7a...",
    "size": "0x257",
    "stateRoot": "0xc5914a0db9663398...",
    "timestamp": "0x693c2fd1",
    "totalDifficulty": "0x0",
    "transactions": [],
    "transactionsRoot": "0x56e81f171bcc55a6...",
    "uncles": []
  }
}
```

### eth\_getBlockByNumber

Returns information about a block by its number.

* Input:
  * `String` - Block number or tag
  * `Boolean` - If `true`, returns full transaction objects
* Output: `Object` - Block information

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockByNumber",
  "params": [
    "0x2710",
    false
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "baseFeePerGas": "0x2540be400",
    "difficulty": "0x0",
    "extraData": "0x0a0000010000003cc91a139b010000e3b0c44298fc1c149afbf4c8996fb92427...",
    "gasLimit": "0x3b9aca00",
    "gasUsed": "0x0",
    "hash": "0xfa15419fc3de49e2...",
    "logsBloom": "0x0000000000000000000000000000000000000000000000000000000000000000...",
    "miner": "0x0000000000000000000000000000000000000000",
    "mixHash": "0x0000000000000000...",
    "nonce": "0x0000000000000000",
    "number": "0x2710",
    "parentHash": "0x1ff0db61e7c714a9...",
    "receiptsRoot": "0x56e81f171bcc55a6...",
    "sha3Uncles": "0x1dcc4de8dec75d7a...",
    "size": "0x257",
    "stateRoot": "0xc5914a0db9663398...",
    "timestamp": "0x693c2fd1",
    "totalDifficulty": "0x0",
    "transactions": [],
    "transactionsRoot": "0x56e81f171bcc55a6...",
    "uncles": []
  }
}
```

### eth\_getBlockReceipts

Returns all transaction receipts for a given block.

* Input:
  * `String` - Block number or tag
* Output: `Array` - List of transaction receipts

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockReceipts",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": []
}
```

### eth\_getBlockTransactionCountByHash

Returns the number of transactions in a block by block hash.

* Input:
  * `String` - Block hash
* Output: `String` - Transaction count as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockTransactionCountByHash",
  "params": [
    "0xfa15419fc3de49e27f62eff7be1f344a3fd156e332ea9a78337c73a0b2b6573a"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x0"
}
```

### eth\_getBlockTransactionCountByNumber

Returns the number of transactions in a block by block number.

* Input:
  * `String` - Block number or tag
* Output: `String` - Transaction count as hexadecimal

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getBlockTransactionCountByNumber",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0x0"
}
```

### eth\_getTransactionByHash

Returns information about a transaction by its hash.

* Input:
  * `String` - Transaction hash
* Output: `Object` - Transaction details

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByHash",
  "params": [
    "0xee093bb802e09fe6119e83d7fe35004464a98e69000bcd5672ac73b32172c997"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "blockHash": "0x1d13424e6a78ab6d...",
    "blockNumber": "0x4ff5de",
    "chainId": "0x688",
    "from": "0x7565f60988b83e577cb1a670ce577667311b11ae",
    "gas": "0x1380a5",
    "gasPrice": "0x2540be400",
    "hash": "0xee093bb802e09fe6...",
    "input": "0x6a76120200000000...",
    "nonce": "0x66",
    "r": "0xd7f06b78d1c679b4...",
    "s": "0x74b4c81773e6d4f0...",
    "to": "0xcce192445aa8118117e73554773e8be8617f31d0",
    "transactionIndex": "0x0",
    "type": "0x0",
    "v": "0xd34",
    "value": "0x0"
  }
}
```

### eth\_getTransactionByBlockHashAndIndex

Returns a transaction by block hash and index position.

* Input:
  * `String` - Block hash
  * `String` - Transaction index (hexadecimal)
* Output: `Object` - Transaction details

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByBlockHashAndIndex",
  "params": [
    "0x1d13424e6a78ab6d9263e6e5f8c9026588d8ba6d2709127a241fbe53a57fd9d9",
    "0x0"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "blockHash": "0x1d13424e6a78ab6d...",
    "blockNumber": "0x4ff5de",
    "chainId": "0x688",
    "from": "0x7565f60988b83e577cb1a670ce577667311b11ae",
    "gas": "0x1380a5",
    "gasPrice": "0x2540be400",
    "hash": "0xee093bb802e09fe6...",
    "input": "0x6a76120200000000...",
    "nonce": "0x66",
    "r": "0xd7f06b78d1c679b4...",
    "s": "0x74b4c81773e6d4f0...",
    "to": "0xcce192445aa8118117e73554773e8be8617f31d0",
    "transactionIndex": "0x0",
    "type": "0x0",
    "v": "0xd34",
    "value": "0x0"
  }
}
```

### eth\_getTransactionByBlockNumberAndIndex

Returns a transaction by block number and index position.

* Input:
  * `String` - Block number or tag
  * `String` - Transaction index (hexadecimal)
* Output: `Object` - Transaction details

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionByBlockNumberAndIndex",
  "params": [
    "0x4ff5de",
    "0x0"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "blockHash": "0x1d13424e6a78ab6d...",
    "blockNumber": "0x4ff5de",
    "chainId": "0x688",
    "from": "0x7565f60988b83e577cb1a670ce577667311b11ae",
    "gas": "0x1380a5",
    "gasPrice": "0x2540be400",
    "hash": "0xee093bb802e09fe6...",
    "input": "0x6a76120200000000...",
    "nonce": "0x66",
    "r": "0xd7f06b78d1c679b4...",
    "s": "0x74b4c81773e6d4f0...",
    "to": "0xcce192445aa8118117e73554773e8be8617f31d0",
    "transactionIndex": "0x0",
    "type": "0x0",
    "v": "0xd34",
    "value": "0x0"
  }
}
```

### eth\_getTransactionReceipt

Returns the receipt of a transaction by transaction hash.

* Input:
  * `String` - Transaction hash
* Output: `Object` - Transaction receipt

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionReceipt",
  "params": [
    "0xee093bb802e09fe6119e83d7fe35004464a98e69000bcd5672ac73b32172c997"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "blockHash": "0x1d13424e6a78ab6d...",
    "blockNumber": "0x4ff5de",
    "contractAddress": null,
    "cumulativeGasUsed": "0xea376",
    "effectiveGasPrice": "0x2540be400",
    "from": "0x7565f60988b83e577cb1a670ce577667311b11ae",
    "gasUsed": "0xea376",
    "logs": [
      {
        "address": "...",
        "blockHash": "...",
        "blockNumber": "...",
        "blockTimestamp": "...",
        "data": "...",
        "logIndex": "...",
        "removed": "...",
        "topics": "...",
        "transactionHash": "...",
        "transactionIndex": "..."
      },
      {
        "address": "...",
        "blockHash": "...",
        "blockNumber": "...",
        "blockTimestamp": "...",
        "data": "...",
        "logIndex": "...",
        "removed": "...",
        "topics": "...",
        "transactionHash": "...",
        "transactionIndex": "..."
      },
      {
        "address": "...",
        "blockHash": "...",
        "blockNumber": "...",
        "blockTimestamp": "...",
        "data": "...",
        "logIndex": "...",
        "removed": "...",
        "topics": "...",
        "transactionHash": "...",
        "transactionIndex": "..."
      }
    ],
    "logsBloom": "0x0000000040000000...",
    "status": "0x1",
    "to": "0xcce192445aa8118117e73554773e8be8617f31d0",
    "transactionHash": "0xee093bb802e09fe6...",
    "transactionIndex": "0x0",
    "type": "0x0"
  }
}
```

### eth\_getLogs

Returns logs matching a given filter object.

* Input:
  * `Object` - Filter parameters (fromBlock, toBlock, address, topics)
* Output: `Array` - List of log entries

> **Note:** Block range is limited to 100 blocks when rate limiting is enabled, or 10,000 blocks as a fallback cap.

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getLogs",
  "params": [
    {
      "fromBlock": "0x1",
      "toBlock": "0x64",
      "address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
      "topics": [
        "0xddf252ad1be2c89b..."
      ]
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0x742d...f44e",
      "topics": [
        "0xddf252...b3ef",
        "0x000000...sender",
        "0x000000...receiver"
      ],
      "data": "0x0000000000000000...",
      "blockNumber": "0x50",
      "transactionHash": "0xabc123...",
      "logIndex": "0x0"
    }
  ]
}
```

### eth\_subscribe

Creates a subscription for real-time events (WebSocket only).

* Input:
  * `String` - Subscription type: `newHeads` or `logs`
  * `Object` - Filter parameters (for `logs` type only)
* Output: `String` - Subscription ID

> **Note:** Only available over WebSocket connections. HTTP calls will fail.

**Example:**

```json
// Request (newHeads subscription)
{
  "jsonrpc": "2.0",
  "method": "eth_subscribe",
  "params": ["newHeads"],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x1"
}

// Subsequent notification
{
  "jsonrpc": "2.0",
  "method": "eth_subscription",
  "params": {
    "subscription": "0x1",
    "result": {
      "number": "0x2710",
      "hash": "0xfa15419fc3de49e2...",
      "parentHash": "0x1ff0db61e7c714a9...",
      "timestamp": "0x693c2fd1"
    }
  }
}
```

### eth\_unsubscribe

Cancels an existing subscription (WebSocket only).

* Input:
  * `String` - Subscription ID
* Output: `Boolean` - `true` if successfully unsubscribed

**Example:**

```json
// Request (WebSocket only)
{
  "jsonrpc": "2.0",
  "method": "eth_unsubscribe",
  "params": ["0x1"],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
```

***

## Network Methods

### net\_version

Returns the current network ID.

* Input: None
* Output: `String` - Network ID

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "net_version",
  "params": [],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "1672"
}
```

### net\_listening

Returns whether the node is actively listening for network connections.

* Input: None
* Output: `Boolean` - Returns `true` if the node is listening for connections (hardcoded to `true` in current implementation)

> **Note:** This method is not available on the public RPC endpoint.

### net\_peerCount

Returns the number of connected peers.

* Input: None
* Output: `String` - Number of peers as hexadecimal

> **Note:** This method is not available on the public RPC endpoint.

***

## Web3 Methods

### web3\_clientVersion

Returns the current client version.

* Input: None
* Output: `String` - Client version string

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "web3_clientVersion",
  "params": [],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "portal/1.0.0"
}
```

### web3\_sha3

Returns the Keccak-256 hash of the given data.

* Input:
  * `String` - Data to hash (hexadecimal)
* Output: `String` - Keccak-256 hash

> **Note:** This method is not available on the public RPC endpoint.

***

## Debug Methods

### debug\_traceTransaction

Returns the execution trace of a transaction.

* Input:
  * `String` - Transaction hash
  * `Object` - Tracer options (optional). Currently only `"callTracer"` is supported.
* Output: `Object` - Execution trace

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_traceTransaction",
  "params": [
    "0xabc123...",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "type": "CALL",
    "from": "0x...",
    "to": "0x...",
    "value": "0x0",
    "gas": "0x...",
    "gasUsed": "0x...",
    "input": "0x...",
    "output": "0x..."
  }
}
```

### debug\_traceCall

Executes a call and returns the execution trace without creating a transaction.

* Input:
  * `Object` - Transaction call object
  * `String` - Block number or tag
  * `Object` - Tracer options (optional). Currently only `"callTracer"` is supported.
* Output: `Object` - Execution trace

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_traceCall",
  "params": [
    {
      "from": "0xfaC0718D882E5f07e1972D617bd430CD72A55D84",
      "to": "0x4100000000000000000000000000000000000000",
      "data": "0x76671808"
    },
    "latest",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "failed": false,
    "gas": "0x9d4b",
    "returnValue": {
      "failed": false,
      "gas": "0x9d4b",
      "returnValue": null
    },
    "structLogs": {
      "depth": 1,
      "gas": "0x1c97138",
      "gasCost": "0x3",
      "memSize": 0,
      "op": 96,
      "opName": "PUSH1",
      "pc": 0,
      "refund": 0,
      "stack": []
    }
  }
}
```

### debug\_traceBlockByHash

Returns traces for all transactions in a block by block hash.

* Input:
  * `String` - Block hash
  * `Object` - Tracer options (optional). Currently only `"callTracer"` is supported as the tracer type.
* Output: `Array` - Array of execution traces

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByHash",
  "params": [
    "0x1d13424e6a78ab6d9263e6e5f8c9026588d8ba6d2709127a241fbe53a57fd9d9",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "result": {
        "calls": "...",
        "from": "...",
        "gas": "...",
        "gas_used": "...",
        "input": "...",
        "output": "...",
        "to": "...",
        "type": "...",
        "value": "..."
      },
      "txHash": "0xee093bb802e09fe6..."
    }
  ]
}
```

### debug\_traceBlockByNumber

Returns traces for all transactions in a block by block number.

* Input:
  * `String` - Block number or tag
  * `Object` - Tracer options (optional). Currently only `"callTracer"` is supported as the tracer type.
* Output: `Array` - Array of execution traces

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_traceBlockByNumber",
  "params": [
    "0x4ff5de",
    {
      "tracer": "callTracer"
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "result": {
        "calls": "...",
        "from": "...",
        "gas": "...",
        "gas_used": "...",
        "input": "...",
        "output": "...",
        "to": "...",
        "type": "...",
        "value": "..."
      },
      "txHash": "0xee093bb802e09fe6..."
    }
  ]
}
```

### debug\_getRawBlock

Returns the RLP-encoded block data.

* Input:
  * `String` - Block number, block hash, or tag
* Output: `String` - RLP-encoded block data

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getRawBlock",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0xf90252f9024da01ff0db61e7c714a91d7e3528c7f659e43b1ddfddb8b20848c8..."
}
```

### debug\_getRawHeader

Returns the RLP-encoded block header.

* Input:
  * `String` - Block number, block hash, or tag
* Output: `String` - RLP-encoded header data

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getRawHeader",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": "0xf9024fa01ff0db61e7c714a91d7e3528c7f659e43b1ddfddb8b20848c87775af..."
}
```

### debug\_getRawReceipts

Returns the raw receipts for a block.

* Input:
  * `String` - Block number, block hash, or tag
* Output: `Array` - Raw receipt data

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getRawReceipts",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": []
}
```

### debug\_getRawTransaction

Returns the raw transaction data by hash.

* Input:
  * `String` - Transaction hash
* Output: `String` - Raw transaction data

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getRawTransaction",
  "params": [
    "0xee093bb802e09fe6119e83d7fe35004464a98e69000bcd5672ac73b32172c997"
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0xf9036d668502540b..."
}
```

### debug\_protocolVersion

Returns detailed protocol and version information.

* Input: None
* Output: `Object` - Version information including:
  * `binaryVersion` - Node binary version
  * `currentSpecVersion` - Current spec version
  * `currentStableBlock` - Current stable block number
  * `higherSpecVersions` - Available higher spec versions
  * `protocolVersion` - Protocol version string

> **Note:** This method is not available on the public RPC endpoint.

***

## Trace Methods

### trace\_filter

Returns traces matching a given filter.

* Input:
  * `Object` - Filter parameters:
    * `fromBlock` - Start block (hexadecimal)
    * `toBlock` - End block (hexadecimal)
    * `fromAddress` - Array of sender addresses (optional)
    * `toAddress` - Array of receiver addresses (optional)
    * `count` - Maximum number of traces to return (optional)
    * `after` - Offset for pagination (optional)
* Output: `Array` - Matching traces

> **Note:** Block range is limited to 500 blocks.

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "trace_filter",
  "params": [
    {
      "fromBlock": "0x4ff5de",
      "toBlock": "0x4ff5de"
    }
  ],
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "action": {
        "callType": "...",
        "from": "...",
        "gas": "...",
        "input": "...",
        "to": "...",
        "value": "..."
      },
      "blockHash": "0x1d13424e6a78ab6d...",
      "blockNumber": "0x4ff5de",
      "result": {
        "gas_used": "...",
        "output": "..."
      },
      "subtraces": "0x1",
      "traceAddress": [],
      "transactionHash": "0xee093bb802e09fe6...",
      "transactionPosition": "0x0",
      "type": "CALL"
    }
  ]
}
```

***

## Transaction Pool Methods

### txpool\_nonceFrom

Returns the next nonce for an address from the transaction pool.

* Input:
  * `String` - Address
* Output: `String` - Nonce as hexadecimal

> **Note:** This method is not available on the public RPC endpoint.

***

## Pharos Extension Methods

### eth\_getAccount

Returns account information at a given address (EIP-7587).

* Input:
  * `String` - Address to query
  * `String` - Block number or tag
* Output: `Object` - Account details including balance, nonce, codeHash, and storageRoot

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "eth_getAccount",
  "params": [
    "0xfaC0718D882E5f07e1972D617bd430CD72A55D84",
    "latest"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "balance": "0x425F2B5D87BCB400",
    "codeHash": "0xc5d2460186f7233c...",
    "nonce": "0x2",
    "storageRoot": "0x0000000000000000..."
  }
}
```

### eth\_getProposerByTxHash

Returns the block proposer for a given transaction.

* Input:
  * `String` - Transaction hash
* Output: `Object` - Proposer information

> **Note:** This method is not available on the public RPC endpoint.

### eth\_getProposersByBlockNumber

Returns the list of proposers for a given block.

* Input:
  * `String` - Block number or tag
* Output: `Array` - List of proposer addresses

> **Note:** This method is not available on the public RPC endpoint.

***

## Pharos Debug Extensions

These are Pharos-specific debug methods that extend the standard debug namespace.

### debug\_getValidatorInfo

Returns validator information for the consensus layer.

* Input:
  * `String` - Block number or tag (optional)
* Output: `Object` - Validator details

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getValidatorInfo",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "blockNumber": "0x2710",
    "validatorSet": [
      {
        "blsKey": "943fd788d6e2d5d7a7520fa92594cd7349169672a4074ea6ad5be564ebd261186c4da4c70a44283f8afc74c1443882a8",
        "identityKey": "04837f833999de7d9ede38c540180e2e7c193cc7a91e40c36355ae348c627687ee8f8274a3462728f46deceaa5f9d76c9224577d87dd975e610749a5d9f367bfab",
        "staking": "0xD3C21BCECCEDA1000000",
        "validatorID": "aa6ddf1a61d8e4dce4b63737de9c188413c489a4b3e080dface9eca672650d62"
      },
      {
        "blsKey": "aa7793ce126340dde2ccaa9c90320e9639006de133629ebf36c21a71a935789def7890446b7181df3e63ba25c8fdffbc",
        "identityKey": "042348d764c8679511b696c15e5f65c3e6e96720a80915def4895c92b7a626e1c9e22aad9e01d4204257feaae538c52ebbd74f0b05897c9d36cf5659f5f91a9b6c",
        "staking": "0xD3C21BCECCEDA1000000",
        "validatorID": "6bdae2537d2ef6508a36b437a28ef7ce7cdfd8581ae81d11f193579896159e62"
      },
      "..."
    ]
  }
}
```

### debug\_getBlockProof

Returns the block proof data for BLS signature verification. See the [BLS Verification Demo](https://github.com/PharosNetwork/examples/blob/main/block-bls-verification/main.go) for usage examples.

* Input:
  * `String` - Block number or tag
* Output: `Object` - Block proof including BLS signatures

**Example:**

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "debug_getBlockProof",
  "params": [
    "0x2710"
  ],
  "id": 1
}

// Response
{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "blockNumber": "0x2710",
    "blockProofHash": "0xfa15419fc3de49e2...",
    "blsAggregatedSignature": "ac312817ac4b5a58695d54da42deedfc581063348a9f2e239e5233684fb43b47d5...",
    "signedBlsKeys": [
      "8281cd60dbf3e67d84a1dcd88f6f95ecf0309ed2c8226eb61d498e3b4453fe309d818f6f02db16b6c610db7b828ef1b5",
      "aa7793ce126340dde2ccaa9c90320e9639006de133629ebf36c21a71a935789def7890446b7181df3e63ba25c8fdffbc",
      "943fd788d6e2d5d7a7520fa92594cd7349169672a4074ea6ad5be564ebd261186c4da4c70a44283f8afc74c1443882a8"
    ]
  }
}
```

### debug\_getBlockReadStates

Returns the read state set for a block.

* Input:
  * `String` - Block number or tag
* Output: `Object` - Read state data

> **Note:** This method is only available on internal debug endpoints.

### debug\_getBlockWriteStates

Returns the write state set for a block.

* Input:
  * `String` - Block number or tag
* Output: `Object` - Write state data

> **Note:** This method is only available on internal debug endpoints.

### debug\_getBlockWriteSetKeys

Returns the write set keys for a block.

* Input:
  * `String` - Block number or tag
* Output: `Array` - Write set keys

> **Note:** This method is only available on internal debug endpoints.

***

## PoW Compatibility Methods (Deprecated)

These methods exist for Ethereum compatibility but return placeholder values since Pharos uses PoS consensus.

| Method                             | Returns | Note                                  |
| ---------------------------------- | ------- | ------------------------------------- |
| eth\_coinbase                      | `"0x0"` | No mining reward address in PoS       |
| eth\_mining                        | `false` | Validators produce blocks, not miners |
| eth\_hashrate                      | `"0x0"` | No hashrate in PoS                    |
| eth\_accounts                      | `[]`    | Node does not manage accounts         |
| eth\_pendingTransactions           | `[]`    | Returns empty array                   |
| eth\_getCompilers                  | `[]`    | Deprecated                            |
| eth\_getUncleCountByBlockHash      | `null`  | Uncles are a PoW concept              |
| eth\_getUncleCountByBlockNumber    | `null`  | Uncles are a PoW concept              |
| eth\_getUncleByBlockHashAndIndex   | `null`  | Uncles are a PoW concept              |
| eth\_getUncleByBlockNumberAndIndex | `null`  | Uncles are a PoW concept              |

***

## Not Implemented Methods

The following methods are declared but not implemented. Calling them returns `METHOD_NOT_FOUND`:

* `eth_batchGetBlockByNumber`
* `eth_batchGetTransactionByHash`
* `eth_batchGetTransactionReceipt`
* `eth_batchGetBlockReceipts`

***

## Notes

* **WebSocket:** `eth_subscribe` and `eth_unsubscribe` require a WebSocket connection. Supported subscription types: `newHeads`, `logs`.
* **Block Tags:** Supported values: `latest`, `earliest`, `pending`, `safe`, `finalized`, or a specific block number in hexadecimal.
* **EIP Support:** EIP-1559 (fee market), EIP-2930 (access lists), EIP-1186 (eth\_getProof) are fully supported.
* **Rate Limiting:** `eth_getLogs` range is limited to 100 blocks (with rate limiting) or 10,000 blocks (fallback). `trace_filter` is limited to 500 blocks.


# eth\_getProof — Storage State Verification (SPV)

## 1. Introduction

Like Ethereum Merkle Proof verification, Pharos provides Simplified Payment Verification (SPV) to allow light clients to verify the existence — or non-existence — of accounts and storage values at a given block height, without possessing the full state.

Pharos implements the storage state trie using a **hexary hash tree** with the **SHA-256** hash algorithm. The trie consists of three node types:

| Node Type    | Size (bytes) | Structure                                                                                          |
| ------------ | ------------ | -------------------------------------------------------------------------------------------------- |
| **MSU Root** | 8192         | 256 × 32-byte SHA-256 hashes. The SHA-256 of this node equals the `stateRoot` in the block header. |
| **Internal** | 515          | 3-byte header + 16 × 32-byte child hashes.                                                         |
| **Leaf**     | 65           | 1-byte type + 32-byte key hash + 32-byte value hash.                                               |

The first-level subtree (MSU Root) contains 256 slots, with each slot representing a shard (partition) of the storage. The actual Merkle proof begins from the second-level subtree. Starting from the second-level subtree, each Internal node value starts with 3 bytes of metadata followed by 16 child node hashes. The hashes of child nodes at the corresponding offset positions in the parent node can be verified recursively, allowing us to determine whether the given root node hash can ultimately be computed, thus justifying the storage data at the specified block height.

<figure><img src="/files/b8rN0ZroWOOY7dfNIFVL" alt=""><figcaption><p>Pharos storage state trie</p></figcaption></figure>

<figure><img src="/files/8cBtPJ8hBc5mvfbI3jJp" alt=""><figcaption></figcaption></figure>

For more details of how Pharos implements SPV, you can check [SPV Proof Theory Explanation](/api-and-sdk/eth-getproof/spv-proof-theory)

***

## 2. API Reference

### `eth_getProof`

Returns the account information and Merkle proof for a given account address (and optionally specific storage keys) at a specified block height.

**Parameters:**

| # | Type       | Description                                                                                                      |
| - | ---------- | ---------------------------------------------------------------------------------------------------------------- |
| 1 | `string`   | Account address (20-byte hex, `0x`-prefixed)                                                                     |
| 2 | `string[]` | Array of storage keys to prove (each 32-byte hex, `0x`-prefixed). Pass `[]` if only the account proof is needed. |
| 3 | `string`   | Block number (hex `0x`-prefixed) or block tag (`"latest"`, `"earliest"`)                                         |

**curl Example — account proof only:**

```bash
curl -X POST http://<node-rpc-endpoint> \\
  -H "Content-Type: application/json" \\
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getProof",
    "params": [
      "0x4100000000000000000000000000000000000000",
      [],
      "latest"
    ]
  }'
```

**curl Example — account proof + storage proof:**

```bash
curl -X POST http://<node-rpc-endpoint> \\
  -H "Content-Type: application/json" \\
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "eth_getProof",
    "params": [
      "0x4100000000000000000000000000000000000000",
      ["0x0000000000000000000000000000000000000000000000000000000000000001"],
      "latest"
    ]
  }'
```

***

## 3. Response Format

### Common Fields

| Field          | Type       | Description                                                                                                        |
| -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
| `balance`      | `string`   | Account balance in hex. `"0x0"` if account does not exist.                                                         |
| `nonce`        | `string`   | Account nonce in hex. `"0x0"` if account does not exist.                                                           |
| `codeHash`     | `string`   | SHA-256 hash of the account's contract code. All-zero if none.                                                     |
| `storageHash`  | `string`   | Root hash of the account's storage trie.                                                                           |
| `isExist`      | `bool`     | `true` if the account exists at the given block, `false` otherwise.                                                |
| `accountProof` | `object[]` | Array of proof nodes from MSU Root to the target account. See [Proof Node Format](#proof-node-format).             |
| `storageProof` | `object[]` | Array of storage proof entries (one per requested storage key). See [Storage Proof Format](#storage-proof-format). |
| `rawValue`     | `string`   | Raw RLP-encoded account value (hex). Empty `"0x"` if account does not exist.                                       |

### Proof Node Format

Unlike Ethereum (which returns RLP-encoded hex strings), each Pharos proof node is a JSON object:

```json
{
  "proofNode": "0x<hex-encoded binary node data>",
  "nextBeginOffset": <int>,
  "nextEndOffset": <int>
}
```

* `proofNode`: The hex-encoded binary data of the trie node (MSU Root, Internal, or Leaf).
* `nextBeginOffset` / `nextEndOffset`: The byte offset range within this node's binary data that points to the **next** node in the proof chain. **These offsets are advisory only — a sound verifier MUST independently compute the expected offset from the key and reject the proof on mismatch. See** [**§ Slot Offset Derivation**](#slot-offset-derivation)**.**

### Slot Offset Derivation

For each non-leaf node in the proof chain, the verifier must independently derive the offset of the child hash slot from the queried key. **Trusting the prover-supplied `nextBeginOffset` without this independent derivation breaks non-existence soundness** — an attacker could redirect the chain to traverse a different subtree that genuinely does not contain the key.

There are two distinct cases:

**(1) MSU Root layer (the first node in `accountProof` / `storageProof.proof`)**

The MSU Root is a flat array of 256 × 32-byte slots. The slot index for a given key is the **last byte of the key**, and the byte offset is:

```
msu_slot_index  = key[len(key) - 1]                 // 0..255
expected_offset = msu_slot_index * 32
```

Where `key` is:

* For `accountProof`: the 20-byte account address.
* For `storageProof[i].proof`: `address (20 bytes) || storageKey (32 bytes)` = 52 bytes total.

The verifier MUST assert `proofNode[0].nextBeginOffset == expected_offset` before reading the slot, otherwise reject the proof.

**(2) Internal node layer (every subsequent non-leaf node)**

Internal nodes inside an MSU subtree have a 3-byte header followed by 16 × 32-byte child slots. The slot index is the nibble of `SHA-256(key)` at the corresponding trie depth:

```
key_hash        = SHA-256(key)
slot_idx        = nibble_at_depth(key_hash, depth)  // 0..15
expected_offset = 3 + slot_idx * 32
```

Where `depth` is the layer's position in the proof chain (the first internal node has depth = 0).

The verifier should likewise cross-check `nextBeginOffset == expected_offset` at each internal layer.

### Storage Proof Format

Each entry in `storageProof` has the following structure:

```json
{
  "key": "0x<storage-key>",
  "value": "0x<storage-value>",
  "isExist": true,
  "proof": [
    { "proofNode": "0x...", "nextBeginOffset": N, "nextEndOffset": M },
    ...
  ]
}
```

***

## 4. Existence Proof

When the queried account (or storage key) **exists**, the response contains a complete proof chain from the MSU Root node down to the Leaf node. The `isExist` field is `true`.

### Example Response (Existence)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "balance": "0xD3C21BCECCEDA1000000",
    "nonce": "0x0",
    "codeHash": "0x415b1764c41b466152a1b0830e26f2e60c391b0dfe1e06eacf28522115f9041d",
    "storageHash": "0xccc11c21e5f76a72d68abcc8a72d3c50446342e12fd7b8cfe611955ed11baa24",
    "isExist": true,
    "rawValue": "0xee808ad3c21bcecceda100000080a0415b...",
    "accountProof": [
      { "proofNode": "0x<MSU Root: 8192 bytes>",    "nextBeginOffset": 1024, "nextEndOffset": 1056 },
      { "proofNode": "0x<Internal node: 515 bytes>", "nextBeginOffset": 35,   "nextEndOffset": 67   },
      { "proofNode": "0x<Internal node: 515 bytes>", "nextBeginOffset": 259,  "nextEndOffset": 291  },
      { "proofNode": "0x<Leaf node: 65 bytes>",      "nextBeginOffset": 0,    "nextEndOffset": 0    }
    ],
    "storageProof": [
      {
        "key": "0x0000000000000000000000000000000000000000000000000000000000000001",
        "value": "0x<storage-value>",
        "isExist": true,
        "proof": [
          { "proofNode": "0x...", "nextBeginOffset": 35, "nextEndOffset": 67 },
          ...
        ]
      }
    ]
  }
}
```

### Verification Logic (Existence)

1. **Leaf verification**: The last proof node is a Leaf. Verify that `SHA256(key) == leaf.key_hash` and `SHA256(value) == leaf.value_hash`.
2. **Hash chain**: Compute `current_hash = SHA256(leaf_node)`. Walk upward through the proof chain. At each parent, **independently derive the expected slot offset from the key** (see [§ Slot Offset Derivation](#slot-offset-derivation)) — do **not** read offsets blindly from `nextBeginOffset`. Confirm:
   * The slot at the key-derived offset in the parent equals `current_hash`.
   * The parent's `nextBeginOffset` matches the key-derived offset (cross-check). Then recompute `current_hash = SHA256(parent_node)`.
3. **Root verification**: The hash of the MSU Root node must equal the trusted `stateRoot` from the block header.

***

## 5. Non-existence Proof

When the queried account (or storage key) **does not exist**, the proof chain terminates at an Internal node where the target slot is all-zero (empty). The `isExist` field is `false`.

### Why Additional Sibling Proofs Are Needed

In an existence proof, the Leaf node's key hash and value hash provide a self-contained anchor: the verifier can compute hashes upward through the chain and confirm the root. However, for a non-existence proof, the chain terminates at an Internal node with an empty slot — and an Internal node filled with only zeros would itself hash to zero, which could be trivially forged.

To ensure the Internal node's integrity, the response includes **sibling leftmost leaf proofs** (`siblingLeftmostLeafProofs`). These provide proof paths from the non-empty sibling slots to their leftmost leaf nodes, enabling the verifier to:

1. Independently recompute each sibling slot's hash.
2. Reconstruct the full Internal node (including the empty target slot).
3. Verify the hash chain upward to the `stateRoot`.

This is a deliberate **trade-off**: the proof structure is more complex, but it enables compact storage optimization in the underlying trie engine. The verification semantics are complete — a non-existence proof fully demonstrates that the queried key is absent from the state at the given block height, and is sufficient for all practical use cases such as cross-chain bridge verification.

### `siblingLeftmostLeafProofs` Format

When `isExist` is `false` and the proof terminates at an Internal node with an empty slot, the response includes:

```json
{
  "siblingLeftmostLeafProofs": [
    {
      "slotIndex": 0,
      "leftmostLeafKey": "0x<key of the leftmost leaf in slot 0's subtree>",
      "proofPath": [
        { "proofNode": "0x<Internal node in slot 0's subtree>", "nextBeginOffset": N, "nextEndOffset": M },
        { "proofNode": "0x<Leaf node>", "nextBeginOffset": 0, "nextEndOffset": 0 }
      ]
    },
    {
      "slotIndex": 2,
      "leftmostLeafKey": "0x<key of the leftmost leaf in slot 2's subtree>",
      "proofPath": [
        { "proofNode": "0x<Leaf node>", "nextBeginOffset": 0, "nextEndOffset": 0 }
      ]
    }
  ]
}
```

Each entry represents a non-empty sibling slot of the terminating Internal node:

| Field             | Type       | Description                                                                                        |
| ----------------- | ---------- | -------------------------------------------------------------------------------------------------- |
| `slotIndex`       | `int`      | The slot index (0–15) of the sibling in the parent Internal node.                                  |
| `leftmostLeafKey` | `string`   | The key of the leftmost leaf node reachable from this sibling slot.                                |
| `proofPath`       | `object[]` | Proof path from the sibling slot down to the leftmost leaf. Same format as `accountProof` entries. |

### Example Response (Non-existence)

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "balance": "0x0",
    "nonce": "0x0",
    "codeHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "storageHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "isExist": false,
    "rawValue": "0x",
    "accountProof": [
      { "proofNode": "0x<MSU Root: 8192 bytes>",    "nextBeginOffset": 1024, "nextEndOffset": 1056 },
      { "proofNode": "0x<Internal node: 515 bytes>", "nextBeginOffset": 259,  "nextEndOffset": 291  },
      { "proofNode": "0x<Internal: target slot is all-zero>", "nextBeginOffset": 35, "nextEndOffset": 67 }
    ],
    "siblingLeftmostLeafProofs": [
      {
        "slotIndex": 0,
        "leftmostLeafKey": "0x<sibling leaf key>",
        "proofPath": [
          { "proofNode": "0x<Internal>", "nextBeginOffset": 35, "nextEndOffset": 67 },
          { "proofNode": "0x<Leaf>",     "nextBeginOffset": 0,  "nextEndOffset": 0  }
        ]
      },
      {
        "slotIndex": 7,
        "leftmostLeafKey": "0x<sibling leaf key>",
        "proofPath": [
          { "proofNode": "0x<Leaf>", "nextBeginOffset": 0, "nextEndOffset": 0 }
        ]
      }
    ],
    "storageProof": []
  }
}
```

### Verification Logic (Non-existence)

1. **Main chain**: Walk the proof chain as usual. **Slot offsets at every layer (including the MSU Root) MUST be derived from the key, not read from `nextBeginOffset`** — see [§ Slot Offset Derivation](#slot-offset-derivation). The last node is an Internal node — verify that the target slot (determined by the key hash nibble at the corresponding depth) is all-zero.
2. **Sibling verification**: For each entry in `siblingLeftmostLeafProofs`:
   * Concatenate the main proof chain (excluding the last node) with the sibling's `proofPath`.
   * Verify that this combined chain is a valid existence proof for `leftmostLeafKey`. Slot offsets within the combined chain are derived from `leftmostLeafKey`, including the MSU Root slot — which, for honest data, must resolve to the same MSU as the main key.
3. **Root verification**: Confirm the MSU Root node hashes to the trusted `stateRoot`.

> **Why MSU Root slot derivation is load-bearing for non-existence.** The 16-slot semantics of internal nodes are bound to the key via SHA-256 nibbles, so an attacker cannot redirect those hops. The MSU Root layer is the **only** layer not bound to the key by the trie structure itself — if a verifier accepts the prover-supplied `nextBeginOffset` there, an attacker can prove non-existence for any key by pointing the chain at any of the 255 MSU subtrees that genuinely don't contain it (an honest path within the wrong subtree will validate against the unchanged `stateRoot`). Deriving the MSU Root slot from the key closes this gap.

***

## 6. Verification Script

A Python verification script [`spv_verify.py`](https://github.com/PharosNetwork/examples/blob/main/spv-verification/spv_verify.py) is provided to verify both existence and non-existence proofs. It handles:

* Automatic `stateRoot` computation from the MSU Root node (SHA-256) if not present in the JSON.
* Both existence proofs (leaf-terminated) and non-existence proofs (empty-slot-terminated with sibling verification).
* Key-derived MSU Root slot offset (see [§ Slot Offset Derivation](#slot-offset-derivation)), enforced as a soundness invariant.
* Optional cross-validation of `stateRoot` against an RPC endpoint.

> Earlier versions of this script trusted `nextBeginOffset` for the MSU Root layer. Integrators using a downstream fork of this script should ensure their copy includes the key-derived slot check; without it, non-existence proofs are forgeable.

### Usage

```bash
# Basic verification (no RPC cross-check):
python3 spv_verify.py proof.json --address 0x<account-address> --no-rpc

# With stateRoot cross-check against RPC:
python3 spv_verify.py proof.json --address 0x<account-address> \
    --block 0x<block-number> --rpc-url http://127.0.0.1:18100

# If the JSON already contains 'address' and 'stateRoot':
python3 spv_verify.py proof.json --no-rpc
```

The full script source is available at the [`examples` repository](https://github.com/PharosNetwork/examples/blob/main/spv-verification/spv_verify.py).

***

## 7. Comparison with Ethereum

For teams familiar with Ethereum's `eth_getProof` ([EIP-1186](https://eips.ethereum.org/EIPS/eip-1186)), the key differences are:

| Aspect                  | Ethereum                             | Pharos                                                          |
| ----------------------- | ------------------------------------ | --------------------------------------------------------------- |
| **Hash algorithm**      | Keccak-256                           | SHA-256                                                         |
| **Trie structure**      | Merkle Patricia Trie (MPT)           | Hexary hash tree with 256-slot MSU Root                         |
| **Node encoding**       | RLP-encoded hex strings              | Fixed-size binary objects                                       |
| **Proof format**        | `accountProof: string[]`             | `accountProof: object[]` (with `nextBeginOffset/nextEndOffset`) |
| **Non-existence proof** | Implicit (path terminates naturally) | Explicit `siblingLeftmostLeafProofs`                            |

**Why are non-existence proofs different?**

In Ethereum's MPT, nodes are RLP-encoded and self-describing — an empty branch is simply an empty string in the RLP list, and the verifier can decode and confirm it directly. In Pharos's hexary trie, Internal nodes are fixed-size binary structures where an empty slot is 32 zero-bytes. To prevent trivial forgery of all-zero Internal nodes, the non-existence proof includes sibling subtree proofs that allow the verifier to independently reconstruct and validate the Internal node's hash. This is a trade-off: proof verification is more complex, but the underlying storage engine benefits from significant optimizations. The verification semantics are complete and fully meet practical use case requirements.

For users who're interested in the implementation details of such proof and how it works, you can refer to [SPV Proof Theory Explanation](/api-and-sdk/eth-getproof/spv-proof-theory)


# SPV Proof Theory Explanation

## SPV Proof Theory Explanation

SPV (in the context of this repository) refers to: On a Merkle state tree of a certain data version, for a key provided by the user, the server constructs proof data, allowing the verifier to independently verify "whether the key exists, and if so, what the value is" or "that the key does not exist", given that the verifier only knows the root hash (or equivalent commitment). In this implementation, the state is organized as a **nibble Trie**, with node hashes cryptographically bound to keys/values. The Letus-side entry point is `MerkleIndex::GetSPV`, and the key-based downward traversal follows `TrieNode::NodeRead` (TreeOpType::kGetSPV).

### Merkle Tree Structure

#### Two Layers: Grid (MSU) + Subtrees

In upper layers such as MyGrid, the state is sharded by MSU (Multi-Shard Unit): it can be understood as first routing keys to one of 256 slots based on the key, with each slot corresponding to a Letus Merkle subtree. The global root hash is obtained by concatenating "the root hashes of each MSU subtree" and then hashing them once more; on the client side, the 256×32-byte subtree roots are concatenated into a "large internal node" as the first hop of the proof chain, see `GetMerkleProofGridReq::handleAllPartIOSuccess`.

```
RootHash = Hash( MSU[0] || MSU[1] || ... || MSU[255] ) (each MSU[i] is a 32-byte subtree root hash)
MSU[0]        MSU[1]        MSU[255]
  |             |             |
  +--- Letus    +--- Letus    +--- ... (nibble Trie)
      subtree       subtree
      (nibble Trie) (nibble Trie)
```

Within a single MSU is Letus's 16-ary nibble Trie (each layer takes one nibble 0\~F of the key\_hash). Below is a minimal schematic diagram using ASCII art (showing only some slots and two layers):

```
           [ Root_internal ]
            /      |      \\   ... (up to 16 child slots)
           /       |       \\
          v        v        v
      slot_3    slot_7    slot_A
        |         |         |
      [Inter]   (Leaf K1) [Inter]
                   |         \\
                   v          v
                (Leaf K2)  (Leaf K3)
```

* **Internal nodes**: Up to 16 child slots, each slot corresponding to a 32-byte subtree hash (empty slots are often all zeros).
* **Leaf nodes**: Store key\_hash and value\_hash, corresponding one-to-one with the existence of the queried key.

#### State Tree Root Hash Calculation Method

```
Explanation:
1. The + in the formulas below indicates string concatenation
2. RootHash represents the root hash of the state DB
3. MSU[0] represents the root hash of MSU slot 0
4. Inter_0 represents the hash of the intermediate node corresponding to slot 0 on the Merkle tree
5. Leaf_0 represents the hash of the leaf node corresponding to slot 0 on the Merkle tree

RootHash = Hash( MSU[0] + MSU[1] + ... + MSU[255] )
MSU[0] = Hash( Inter_0 + Inter_1 + ... + Inter_e )
Inter_x = Hash( Leaf_0 + Leaf_1 + ... + Leaf_e )
Leaf_x = Hash( key + value )
```

Additionally, note that to more efficiently handle the case where there are many empty slots in intermediate nodes in the Trie, Letus chooses to skip empty slots when computing hashes. The relevant logic is similar to the following code:

```c++
std::string hash_val = "";
for (int i = 0; i < kInternalSlotCount; i++) {
    if (node_impl->IsSlotCommittedEmpty(i)) {
        continue;
    }
    hash_val = hash_update(hash_val, node_impl->GetSlotHash[i]);
}
```

### SPV Verification Method

Like other Merkle tree implementations, Letus's SPV is the reverse process of computing the root hash. Specifically, the process is as follows:

#### Trie Path

1. Perform `ComputeKeyHash` on the user key to obtain a fixed-length NibbleHash.
2. Starting from the MSU root (and the Grid layer above), sequentially take key\_hash\[d] as the slot index 0\~15 for depth d = 0,1,2…, enter the corresponding subtree on the internal node, until reaching a **leaf** or terminating at some layer where the **slot is empty**.
3. These obtained slots (slot\_idx) will be used later to verify the SPV return results layer by layer.

#### Proof Chain tree\_proofs

1. Each element in the chain is a `TreeProofNode`, which may correspond to an intermediate node or a leaf node.
2. For an **intermediate node TreeProofNode**: It contains the `proof_node` with hex-encoded node content, and `next_begin_offset` and `next_end_offset` that indicate "where the current subtree root hash falls within the parent node's byte string". **A sound verifier MUST independently derive these offsets from the key and treat the prover-supplied values as advisory cross-check inputs only.** The derivation differs by layer:

   * **MSU Root layer** (the first proof node, 256 × 32-byte flat slot array, no header): `next_begin_offset = slot_idx * 32`, where `slot_idx` is derived from the queried key via the network's MSU routing function — `slot_idx = key[len(key) - 1]` for the Pharos mainnet/testnet — not from any prover-supplied data.
   * **Internal node layer** (every subsequent non-leaf node): `next_begin_offset = 3 + 32 * slot_idx`, where `slot_idx` is the nibble of `ComputeKeyHash(key)` at the corresponding trie depth. The leading 3 bytes are node metadata.
   * `next_end_offset = next_begin_offset + 32` in both cases.

   If the prover-supplied `next_begin_offset` disagrees with the key-derived value at any layer — especially at the MSU Root — the proof must be rejected. See the Correctness Discussion below for why this is load-bearing for non-existence soundness.
3. For a **leaf node TreeProofNode**: It contains the `proof_node` with hex-encoded node content, representing the concatenation of the key's hash and the value's hash (specific encoding depends on the implementation).

#### Proof Chain Verification Logic

Verifier input: `root_hash` (committed root), `key`, returned `proofs[]` (and optional sibling sub-proofs, see below).

```
cur = Starting from the last node (deepest) in proofs, compute "the subtree root hash of that node" according to the node type
for i = len(proofs)-2 .. 0:
    parent = proofs[i]
    # Independently derive the expected slot offset from the queried key — do not
    # trust the prover-supplied next_begin_offset for slicing.
    if parent is the MSU Root layer (i == 0):
        expected_offset = key[len(key) - 1] * 32                   # 256-slot flat array
    else:
        slot_idx = nibble_at_depth(ComputeKeyHash(key), trie_depth_of(parent))
        expected_offset = 3 + slot_idx * 32                         # internal node layout
    Assert parent.next_begin_offset == expected_offset             # cross-check
    H_slot = parent.proof_node[expected_offset : expected_offset + 32]
    Assert H_slot == cur
    cur = Hash(parent.proof_node) // Or the parent node hash rule agreed upon by the implementation
Assert cur == root_hash (or consistent with the root after concatenation with the MSU/Grid chain)
```

**Existence**: The last hop is a leaf, and the key\_hash in the leaf is consistent with `ComputeKeyHash(key)`, and the value can be verified.

**Non-existence** has two typical forms (consistent with the "instance" numbering below):

* **Case 1**: The last hop is a leaf, but the key\_hash in the leaf is inconsistent with the queried key (the path leads to "someone else's leaf") — typically without sibling sub-proofs.
* **Case 2**: At an internal node, the next nibble corresponding to the query is an empty slot, while other slots at the same layer have data — often accompanied by `sibling_leftmost_leaf_proofs` (sub-chains to the leftmost leaf of each sibling slot); the information of these sibling slots is also used to assist in non-existence proofs.

## SPV Instance Explanation

Consistent with the implementation: The `sha256(key)` in the pseudocode below and the hexadecimal strings in the comments (such as "12e2aef") are only for illustrative purposes to facilitate comparison with the diagrams; actual verification should be based on the NibbleHash obtained from `ComputeKeyHash` and the node encoding rules.

### Existence Proof

**Scenario**: key = K, (K, V) exists in the tree, the query path goes from the MSU root along the nibble of hash(K) all the way to a leaf, and that leaf is the storage for K.

**Instance**:

<figure><img src="/files/vZJKAE9wHazU0ER353wy" alt=""><figcaption><p>SPV Existence Proof</p></figcaption></figure>

As shown in the figure above, a total of three diagrams illustrate an actual example of SPV verification.

* The **leftmost diagram** shows the structure of the Trie, and the hash corresponding to the key we expect to verify, i.e., "12e2aef"
* The **middle diagram** shows the response given by SPV, and the correspondence between the response content and each layer of the Trie
* The **rightmost diagram** shows the SPV verification flow, verifying from the leaf node to the state root hash. We focus on explaining the SPV verification flow shown on the rightmost side:

```
# Step 1: Get slot_idx at each layer
key_hash = sha256(key)  # key_hash = "12e2aef"
slot_idx[0] = 1
slot_idx[1] = 2
...

# Step 2: Verify leaf node
check: proof[-1][1] == key_hash  # proof[-1] consists of three elements, proof[-1][1] represents key_hash
tmp_hash = sha256(proof[-1])

# Step 3: Verify the intermediate node one layer above the leaf node
check: proof[-2][slot_idx[-1]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-2])

# Step 4: Verify the intermediate node one layer above that
check: proof[-3][slot_idx[-2]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-3])
...

# Final step: Verify the root hash matches the expected state DB root hash
check: tmp_hash == root_hash
```

### Non-Existence Proof - Case 1

**Meaning**: The proof chain ends with a leaf node, but the key\_hash stored in that leaf is inconsistent with the key\_hash obtained by applying the same hash algorithm to the queried key — that is, following the query path leads to the leaf of another existing key (the path prefix overlaps with another, and the last hop lands on "someone else's leaf").

<figure><img src="/files/h5qUDLUrZPdBSc4F1ZHK" alt=""><figcaption><p>SPV Non-Existence Proof - Case 1</p></figcaption></figure>

As shown in the figure above, this is an example of a non-existence proof: the hash corresponding to the key we expect to verify is "12e2111".

Obviously, from the leftmost diagram, we can see that according to our expected key\_hash, a leaf node of the Merkle tree is still found. However, the key corresponding to this leaf node is not the same as the key we expect, which indicates that the key we expect for SPV does not exist. Next, let's look at how to verify this through SPV.

```
# Step 1: Get slot_idx at each layer
key_hash = sha256(key)  # key_hash = "12e2111"
slot_idx[0] = 1
slot_idx[1] = 2
...

# Step 2: Verify leaf node
check: proof[-1][1] != key_hash  # This step indicates that this verification is a non-existence proof!
tmp_hash = sha256(proof[-1])

# Step 3: Verify the intermediate node one layer above the leaf node
check: proof[-2][slot_idx[-1]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-2])

# Step 4: Verify the intermediate node one layer above that
check: proof[-3][slot_idx[-2]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-3])
...

# Final step: Verify the root hash matches the expected state DB root hash
check: tmp_hash == root_hash
```

**In summary**: "key\_hash verification fails" && "the entire proof can be verified using the target key's offset" yields a valid non-existence proof.

### Non-Existence Proof - Case 2

**Meaning**: At a certain internal node, the next nibble of the query path corresponds to an empty slot (the subtree is not created, or an all-zero empty slot proof is provided), and the path ends at an intermediate node rather than a leaf node.

<figure><img src="/files/EpFky564roxyGYSRerYO" alt=""><figcaption><p>SPV Non-Existence Proof - Case 2</p></figcaption></figure>

As shown in the figure above, this is an example of a non-existence proof: the hash corresponding to the key we expect to verify is "12221".

In this case, the verification process is more complex. Returning only the main path of the target key (tree\_proofs) is not sufficient; we also need `sibling_leftmost_leaf_proofs` corresponding to each non-empty sibling slot: each sibling item contains `slot_index`, `leftmost_leaf_key` (the original key of the leftmost leaf in that slot's subtree), and `proof_path` (the sequence of nodes from a certain layer's internal node down to that leaf along the leftmost path). The reason is explained in the "Non-Existence Proof Case Discussion - Case 2" section below. During verification, it can be understood in a manner consistent with the implementation: the main chain first verifies to the "empty slot internal"; then for each sibling sub-proof, concatenate "main chain prefix + sibling path" to form a complete chain, and verify to the root using the existence proof method (using the sibling's leftmost leaf key to walk the nibble path).

```
# The main chain is denoted as proof (i.e., tree_proofs); siblings are sibling_leftmost_leaf_proofs.
# The last section is an internal node, not a leaf.

# Step 1: Get slot_idx at each layer for the queried key
key_hash = sha256(key)  # key_hash = "12221"
slot_idx[0] = 1
slot_idx[1] = 2
...

# Step 2: Verify the last section internal node: the query path falls on an empty slot at the last layer
check: proof[-1][slot_idx[-1]] == 0

# Step 3: Compute hash upward from the last section internal node
tmp_hash = sha256(proof[-1])

# Step 4: Verify the intermediate node one layer above
check: proof[-2][slot_idx[-2]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-2])

# Step 5: One layer above that
check: proof[-3][slot_idx[-3]] == tmp_hash
# Additionally, the correspondence between next_begin_offset and slot_idx can be checked
tmp_hash = sha256(proof[-3])
...

# Final step: Verify the root hash matches the expected state DB root hash
check: tmp_hash == root_hash

# For each sibling sub-proof in siblings (where proof_path is non-empty):
# Remove the last section from the main chain, append that item's proof_path,
# then roll to the root using the same steps as "existence proof"
for sib in siblings:
    if not sib.proof_path:
        continue
    combined = proof[0 : len(proof)-1] + sib.proof_path
    key_hash = sha256(sib.leftmost_leaf_key)
    slot_idx[0] = 1
    slot_idx[1] = 2
    ...
    check: combined[-1][1] == key_hash
    tmp_hash = sha256(combined[-1])
    check: combined[-2][slot_idx[-1]] == tmp_hash
    tmp_hash = sha256(combined[-2])
    check: combined[-3][slot_idx[-2]] == tmp_hash
    tmp_hash = sha256(combined[-3])
    ...
    check: tmp_hash == root_hash
```

**Summary**: Case 2 = Main chain (terminating at empty slot internal) + Several sibling chains (each being an "existence-style" sub-proof of the leftmost leaf of a certain slot); sibling chains share the same root\_hash with the main chain, ensuring branch completeness under the parent node's 16-slot semantics.

## Correctness Discussion

This section explains: Given that the root\_hash root\_hash is trustworthy (or has been anchored to a trusted state), why the verification steps above are semantically sufficient to support the conclusion of "existence / non-existence". The text first explains SkipEmpty and the overall security goals, then discusses existence, non-existence (leaf key mismatch), and non-existence (internal empty slot + sibling sub-proofs) in three sections.

### Prerequisites and Overall Conclusions (Including SkipEmpty)

**Prerequisite — Key-Derived Slot Offsets at Every Layer (including MSU Root).** All correctness arguments below presume that the verifier derives the slot offset at each layer of the proof chain from the queried key itself, **not** from prover-supplied fields. For internal nodes inside an MSU subtree the slot index is the corresponding nibble of `ComputeKeyHash(key)`; for the MSU Root layer the slot index is fixed by the network's MSU routing function applied to the key (the last byte of the key for the Pharos network). If a verifier accepts the prover-supplied `next_begin_offset` for the MSU Root layer, the Case 2 non-existence argument below collapses: a malicious prover can redirect the chain to a sibling MSU that genuinely does not contain the key, present an honest non-existence path inside that wrong MSU, and the sibling-leftmost-leaf subchains will still close to the unchanged `root_hash`. The 16-slot pinning argument constrains internal layers within an MSU but does not by itself constrain which MSU subtree was descended into. The key→MSU-slot derivation is therefore load-bearing and must be enforced before any other check.

Letus skips committed empty slots when computing hashes for internal nodes (see the loop illustration earlier), therefore:

* The same root\_hash may correspond to multiple "logical tree shapes" (which slots are empty, how nodes are laid out), and the proof\_node byte layout seen by the verifier from the SPV may not necessarily correspond one-to-one with the "complete 16-ary expanded diagram".
* However, this does not weaken the SPV's goal: The verifier only cares about "whether the path of this key is consistent with the root commitment under the current hash rules". Skipping empty slots changes "how to compress and encode child hashes", but does not change the commitment relationship that "non-empty subtree hashes are rolled into the root by the parent node and ancestors all the way".

Under this premise, when the hash function satisfies the expected cryptographic properties (collision resistance, preimage resistance; corresponding to the algorithms used in ComputeKeyHash, node hashes, etc. in the implementation), it can be considered that:

* An attacker cannot tamper with a valid existence proof for a certain key and turn it into a valid non-existence proof for the same key;
* Nor can they tamper with a valid non-existence proof and turn it into a valid existence proof for the same key.

(If hashes could be arbitrarily collided or preimages could be found, the above conclusions would no longer hold; the arguments below all assume "ideal" hash behavior.)

### Existence Proof Case Discussion

An existence proof must simultaneously satisfy: rolling hashes from bottom to top are consistent with root\_hash, and the key\_hash in the leaf is consistent with the hash computed from the queried key using the same rules.

The key points of reliability can be broken down into three layers:

1. **Leaf Layer Binding** The leaf node encoding contains key\_hash (and value-side commitments). The verifier explicitly checks that the key\_hash in the leaf is consistent with `ComputeKeyHash(query key)`, which is equivalent to requiring that "this existence claim is bound to this key", not some other preimage.
2. **Path and Slot Consistency** In each layer's internal node, the 32-byte subtree root hash of the child slot must be consistent with the "current subtree root" being verified; and the slot\_idx used by the verifier to select slots is derived entirely from the nibble path of the **same key\_hash**. Therefore, an attacker cannot disguise a "subtree hash pointing elsewhere" as "the one that should be seen when going down along the query key", unless they can break the binding between the parent node hash and the child slot content (which boils down to hash collision or forging node encoding).
3. **Root Anchoring** The final `tmp_hash == root_hash` pins the entire path to that version of the state. If the root comes from a trusted source, then the meaning of "exists (K, V)" under that version is fixed jointly by the tree and encoding rules.

**In summary**: The three-layer checks of leaf—path—root together ensure that the "claimed existing key" aligns with the "actual leaf content on the Merkle path" and the "global state commitment".

### Non-Existence Proof Case Discussion - Case 1

**Case 1**: Following the nibble path of the query key leads to a leaf, but the key\_hash in the leaf ≠ `ComputeKeyHash(query key)` (reaching "someone else's leaf" — the path prefix overlaps with another, and the last hop lands on an already existing different key).

**Argument**:

1. **Path Authenticity** If the proof chain can close from bottom to top to root\_hash under the slot\_idx (and next\_begin\_offset consistency) at each layer derived from the query key, it indicates: On the slot sequence that the key claims to follow, the Merkle structure is self-consistent, not randomly assembled isolated nodes.
2. **Uniqueness Semantics of Leaf Content** The key\_hash in the leaf is obtained from the key at storage time using the same hash rules. In the sense of preimage resistance, it is impossible to forge a value for the "query key" that is the same as the key\_hash already written in the leaf, unless by chance of collision or it is the original key. What is written in the leaf is the hash of "another key", so the query key is not the key bound by that leaf.
3. **Conclusion** Chain verification passes ⇒ There is indeed a leaf hanging at the end of the query path on the tree; the key bound in the leaf is determined and is not the query key ⇒ In that version of the state, one cannot claim that the query key exists using that leaf as evidence. This is the semantics of "non-existence" in Case 1.

### Non-Existence Proof Case Discussion - Case 2

**Case 2**: At a certain internal node in the query path, the next nibble corresponds to an empty slot, the main chain tree\_proofs ends at the internal node without passing through a leaf.

#### Why "Only Main Chain, No Sibling Information" Is Not Sufficient

When only providing node material for "walking to a certain layer's internal node along the query key, with the next slot being empty", the verifier can only confirm: On the chain provided by the attacker, the sub-slot in the query direction appears to be empty. However, the SkipEmpty and sparse Trie encoding methods make the "appearance of the intermediate" less transparent in the proof than the "entire subtree commitment": A malicious Prover could theoretically attempt to construct a kind of local view — pretending the slot is empty on the query nibble, while concealing or forging the subtrees that actually exist on other slots at the same layer, as long as those slots' real paths and leaf bindings below have not been required to be shown.

In other words: Relying solely on the main chain of "empty slot termination" is not sufficient to prove that "the subtree hashes on other non-empty slots at the same layer are also consistent with the current parent node, ancestors, and root"; without constraints on the "side branches", the "emptiness" could be fabricated.

```plaintext
Suppose a malicious full node wants to forge a proof of "key K does not exist" to a light client, but K actually exists in the Trie:

1. Layout of the Real Internal Node (depth=d):
slot[2] = H_child_a
slot[7] = H_child_k - Key K's path key_hash[d]=7 leads here (non-empty, indicating K exists)
slot[11] = H_child_c
All other slots are empty (all zeros)

2. Forged Internal Node Constructed by the Attacker:
slot[2] = H_child_a
slot[7] = all zeros // "Vacate" this slot
slot[9] = H_child_k // Move H_child_k to another empty slot
slot[11] = H_child_c
All other slots are empty (all zeros)

3. The Hashes of Both Nodes Are Identical, Because:
Real: SHA256(type || padding || H_child_a || H_child_k || H_child_c)
Forged: SHA256(type || padding || H_child_a || H_child_k || H_child_c)
The concatenation order of non-empty hashes remains unchanged (it's the order encountered when scanning slots 0-15); only the positions have changed.
                                                               
4. Verifier checks slot[7] and finds all zeros, concluding K does not exist

5. Verifier executes HashInternalNodeSkipEmpty and obtains the same hash as the real node

6. Tracing up the path to the root, the child hash recorded in the parent node matches

7. Result: The light client is deceived - it believes K does not exist, but K actually exists!
```

The figure above illustrates this kind of tampering/concealment: For example, the attacker lets the query path stop at an empty slot in the narrative, but does not synchronously provide sibling branch information consistent with the real state.

#### What Role Do sibling\_leftmost\_leaf\_proofs Play

For each non-empty sibling slot that needs to be declared as "indeed having data at the same layer", the Prover additionally provides `sibling_leftmost_leaf_proofs`: for the key of the leftmost leaf in that slot's subtree, and a sub-chain from a certain layer's internal node (sharing a prefix with the main chain) down to that leaf along the "leftmost path" (see the combined in the instance pseudocode earlier).

During verification:

1. The main chain still verifies according to the query key's slot\_idx to the internal node where "empty slot termination" occurs, and closes with root\_hash.
2. Each sibling sub-proof, after concatenating "the main chain minus the last section" with proof\_path, rolls to the **same** root\_hash in the manner of an existence proof, recalculating the slot indices at each layer using leftmost\_leaf\_key.

In this way, under the same parent node (and ancestors), the "empty slot" in the query direction and the "non-empty subtree down to a certain leaf" in the sibling direction are simultaneously constrained by the same root commitment: The subtree hashes of each non-empty slot in the parent internal must be able to support both the empty slot narrative on the main chain and the sibling chains going all the way down to real leaf nodes; both must be consistent with the parent hash computed under the node encoding and SkipEmpty rules.

#### Why It Is Difficult for an Attacker to Forge Sibling Chains

The verification of sibling chains uses the **own nibble path of leftmost\_leaf\_key**, not the path of the query key. Therefore:

* One cannot arbitrarily take "any other leaf on the tree" to impersonate the "leftmost leaf" of that slot, unless going down from the leftmost path along leftmost\_leaf\_key, the next\_begin\_offset / slot\_idx at each layer are consistent with the encoding and close to the root — this is equivalent to performing a complete existence-style verification for that key.
* One cannot simply reuse "another key" as leftmost\_leaf\_key without regard to the path: If that key is not the leftmost leaf in that slot's subtree, or if its slot positions at deeper layers are inconsistent with the structure implied by proof\_path, the verification will fail at some layer.
* Under the assumptions of preimage resistance and collision resistance, forging a sibling chain that can pass verification means forging node content and leaf bindings compatible with the real root\_hash, which is computationally infeasible.

**Summary (Correctness Discussion)**: Existence relies on leaf key binding + path slots + root; Case 1 relies on path authenticity + leaf bound to non-query key; Case 2 uses sibling leftmost leaf existence sub-chains in addition to the empty slot main chain to pin non-empty branches at the same layer to the same root\_hash, thereby plugging the gap of "only fabricating a segment of internal". All three are compatible with Letus's SkipEmpty encoding: not pursuing "one-to-one correspondence between proof and unique tree shape", but pursuing "semantic consistency under root commitment, and types cannot be mutually tampered".


# Foundry

This guide is suitable for developers who want to start building dApps using Pharos and Foundry toolchains. If you are a new user of Ethereum, please consider researching the [Ethereum documentation](https://ethereum.org/en/developers/docs/) before continuing.

## Write Your First Transaction

This guide will walk you through the process of sending your first transaction on the Pharos blockchain using three popular libraries: `ethers.js`, `web3.js`, and `web3.py`. By the end of this guide, you will understand how to send a transaction programmatically using these libraries.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Pick Your Favorite Contract Develop Framework and IDE

Install your preferred contract framework from the below list:

* ethers.js
* web3.js
* web3.py

### Setup 2: Set Up the Project

Clone the example repo and add dependencies.

```sh
git clone https://github.com/PharosNetwork/examples
cd examples/transaction/ethersjs
npm install
```

```javascript
const { ethers } = require("ethers");

// Connect to Pharos Testnet
const provider = new ethers.providers.JsonRpcProvider("<PHAROS_RPC_URL>");

// Note: Your wallet private key (for testnet only, never expose this in production)
const privateKey = "YOUR_PRIVATE_KEY";
const wallet = new ethers.Wallet(privateKey, provider);

// Transaction details
const tx = {
    to: "RECIPIENT_ADDRESS", // Note: Replace with the recipient's address
    value: ethers.utils.parseEther("0.1"), // Amount to send (0.1 PHAR)
};

// Send the transaction
wallet.sendTransaction(tx)
    .then((transaction) => {
        console.log("Transaction sent:", transaction.hash);
    })
    .catch((error) => {
        console.error("Error sending transaction:", error);
    });
```

### Step 3: Run the Script

Run the script using Node.js:

<pre class="language-sh"><code class="lang-sh"><strong>node index.js
</strong></code></pre>

### Key Points

* **Private Key**: Never expose your private key in production. Use environment variables or secure storage.
* **Recipient Address**: Replace RECIPIENT\_ADDRESS with the actual recipient's wallet address.
* **Gas Limit**: Ensure you include a sufficient gas limit for the transaction.

### Troubleshooting

* **Transaction Fails**: Ensure you have enough testnet tokens to cover the transaction fee.
* **Incorrect Network**: Double-check that you are connected to the Pharos Testnet.
* **Invalid Address**: Verify that the recipient's address is correct.

### Conclusion

Now that you’ve sent your first transaction using ethers.js, web3.js, and web3.py.

This guide provides a comprehensive introduction to sending transactions on the Pharos blockchain using three popular libraries. If you encounter any issues, refer to the Troubleshooting section or consult the respective library's documentation. Happy building! 🚀


# Write Your First dApp

This guide will walk you through the process of building and deploying your first end-to-end decentralized application (dApp) on the Pharos blockchain. By the end of this guide, you will have a fully functional dApp that includes a smart contract, a backend, and a frontend.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Foundry

* [Foundry](https://book.getfoundry.sh/getting-started/installation)

### Setup 2: Set Up the Project

Clone the example repo:

```sh
git clone https://github.com/PharosNetwork/examples
cd examples/dapp/foundry
npm install
```

### Setup 3: Deploy the Smart Contract

Set the private key:

```sh
export PRIVATE_KEY=<your private key>
```

Then

```shell
cd contract
forge install OpenZeppelin/openzeppelin-contracts --no-git
forge script script/Counter.s.sol:CounterScript --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Setup 4: Build the Frontend

Connect the Frontend to the Smart Contract:

* Open `src/app.jsx` and replace its content with the following code:

```jsx
import React, { useState } from "react";
import { ethers } from "ethers";

const contractAddress = "YOUR_CONTRACT_ADDRESS"; // Replace with your contract address
const abi = [
    {
        "inputs": [],
        "name": "get",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "x",
                "type": "uint256"
            }
        ],
        "name": "set",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];

function App() {
    const [value, setValue] = useState("");
    const [storedValue, setStoredValue] = useState("");

    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(contractAddress, abi, signer);

    const handleSet = async () => {
        await contract.set(value);
        alert("Value set!");
    };

    const handleGet = async () => {
        const result = await contract.get();
        setStoredValue(result.toString());
    };

    return (
        <div>
            <h1>Simple Storage dApp</h1>
            <input
                type="text"
                value={value}
                onChange={(e) => setValue(e.target.value)}
                placeholder="Enter a value"
            />
            <button onClick={handleSet}>Set Value</button>
            <button onClick={handleGet}>Get Value</button>
            <p>Stored Value: {storedValue}</p>
        </div>
    );
}

export default App;
```

Replace Placeholder Values

* Replace `YOUR_CONTRACT_ADDRESS` with the address of your deployed contract.

### Step 5: Run the dApp

Start the React App

* Navigate to the frontend directory and start the app:

```shell
npm start
```

Interact with the dApp:

* Open your browser and navigate to `http://localhost:3000`.
* Use the input field and buttons to interact with your smart contract.

### Troubleshooting

* Contract Deployment Fails: Ensure you have enough testnet tokens to cover the deployment cost.
* Frontend Connection Issues: Verify that the contract address and ABI are correct.

### Conclusion

Now that you’ve built your first end-to-end dApp.

This guide provides a comprehensive introduction to building and deploying an end-to-end dApp on the Pharos blockchain. If you encounter any issues, refer to the Troubleshooting section or consult the Pharos documentation. Happy building! 🚀


# Write Your First Token

This guide will walk you through the process of creating and deploying your first token on the Pharos blockchain. By the end of this guide, you will have a fully functional token contract and understand how to interact with it.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Foundry

* [Foundry](https://book.getfoundry.sh/getting-started/installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/token/foundry/contract
```

Install OpenZeppelin Contracts:

* Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

```shell
forge install OpenZeppelin/openzeppelin-contracts --no-git
```

### Setup 3: Write the Token Contract

Create a New Solidity File:

* Create a new file for your token contract:

```shell
touch src/Token.sol
```

Write the Token Contract:

* Open `src/Token.sol` in your favorite text editor and add the following code:

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

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

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

Compile the Smart Contract:

* Use forge to compile the contract:

```sh
forge build
```

Test the Smart Contract

* Use forge to test the contract:

```shell
forge test
```

### Step 4: Deploy the Token Contract

Create a Deployment Script:

* Create a new file for the deployment script:

```shell
touch script/DeployToken.s.sol
```

Write the Deployment Script:

* Open script/DeployToken.s.sol and add the following code:

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

import "forge-std/Script.sol";
import "../src/Token.sol";

contract DeployToken is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        Token token = new Token(1000000); // Initial supply of 1,000,000 tokens

        vm.stopBroadcast();
    }
}
```

Set the private key:

```shell
export PRIVATE_KEY=<your private key>
```

Deploy the Contract:

* Use forge to deploy the contract to the Pharos Testnet:

```shell
forge script script/DeployToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Step 5: Verify the Token Contract

* For Atlantic Testnet

```shell
forge verify-contract \
<contract_address> \
--constructor-args <abi_encoded_args> \
src/Token.sol:Token \
--chain-id 688688 \
--verifier blockscout \
--verifier-url https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract
```

* For Testnet

```shell
forge verify-contract \
<contract_address> \
--constructor-args <abi_encoded_args> \
src/Token.sol:Token \
--chain-id 688689 \
--verifier blockscout \
--verifier-url https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract
```

> Note: Replace with your token contract address

### Step 6: Interact with the Token Contract

Open `script/InteractToken.s.sol` and add the following code:

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

import {Script, console} from "forge-std/Script.sol";
import {Token} from "../src/Token.sol";

contract InteractToken is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        Token token = Token(address(0x00)); // Replace with your token contract address

        // Check balance
        uint256 balance = token.balanceOf(msg.sender);
        console.log("Balance:", balance);

        // Transfer tokens
        token.transfer(address(0x00), 100); // Replace with recipient address and amount
        console.log("Tokens transferred");

        vm.stopBroadcast();
    }
}
```

Execute the script using forge:

```shell
forge script script/InteractToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.

### Next Steps

Now that you’ve created and deployed your first token using Foundry.

This guide provides a comprehensive introduction to creating and deploying a token on the Pharos blockchain using Foundry. If you encounter any issues, refer to the Troubleshooting section or consult the Foundry documentation. Happy building! 🚀


# Write Your First NFT

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Foundry

* [Foundry](https://book.getfoundry.sh/getting-started/installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/nft/foundry/contract
```

Install OpenZeppelin Contracts:

* Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

```shell
forge install OpenZeppelin/openzeppelin-contracts --no-git
```

### Setup 3: Write the NFT Contract

Create a New Solidity File:

* Create a new file for your NFT contract:

```shell
touch src/Token.sol
```

Write the Token Contract:

* Open `src/Token.sol` in your favorite text editor and add the following code:

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

import {ERC721} from "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";

contract Token is ERC721 {
    constructor() ERC721("Token", "MTK") {}
}
```

Compile the Smart Contract:

* Use forge to compile the contract:

```shell
forge build
```

Test the Smart Contract

* Use forge to test the contract:

```shell
forge test
```

### Step 4: Deploy the Token Contract

Set the private key:

```shell
export PRIVATE_KEY=<your private key>
```

Create a Deployment Script:

* Create a new file for the deployment script:

```shell
touch script/DeployToken.s.sol
```

Write the Deployment Script:

* Open script/DeployToken.s.sol and add the following code:

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

import "forge-std/Script.sol";
import "../src/Token.sol";

contract DeployToken is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        Token token = new Token(1000000); // Initial supply of 1,000,000 tokens

        vm.stopBroadcast();
    }
}
```

Deploy the Contract:

* Use forge to deploy the contract to the Pharos Testnet:

```shell
forge script script/DeployToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Step 5: Verify the Token Contract

* For Atlantic Testnet

```shell
forge verify-contract \
<contract_address> \
--constructor-args <abi_encoded_args> \
src/Token.sol:Token \
--chain-id 688688 \
--verifier blockscout \
--verifier-url https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract
```

* For Testnet

```shell
forge verify-contract \
<contract_address> \
--constructor-args <abi_encoded_args> \
src/Token.sol:Token \
--chain-id 688688 \
--verifier blockscout \
--verifier-url https://api.socialscan.io/pharos-testnet/v1/explorer/command_api/contract
```

> Note: Replace with your token contract address

### Step 6: Interact with the NFT Contract

Open `script/InteractToken.s.sol` and add the following code:

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

import {Script, console} from "forge-std/Script.sol";
import {Token} from "../src/Token.sol";

contract InteractToken is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        Token token = Token(address(0x00)); // Replace with your token contract address

        // Check balance
        uint256 balance = token.balanceOf(msg.sender);
        console.log("Balance:", balance);

        // Transfer tokens
        token.transfer(address(0x00), 100); // Replace with recipient address and amount
        console.log("Tokens transferred");

        vm.stopBroadcast();
    }
}
```

Execute the script using forge:

```shell
forge script script/InteractToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.

### Conclusion

Now that you’ve created and deployed your first NFT using Foundry.

This guide provides a comprehensive introduction to to creating and deploying an NFT on the Pharos blockchain. If you encounter any issues, refer to the Troubleshooting section or consult the Foundry documentation. Happy building! 🚀


# Write Your First Uniswap Contract

This guide will walk you through the process of creating and deploying a simple Uniswap-like decentralized exchange (DEX) contract on the Pharos blockchain. By the end of this guide, you will have a basic understanding of how automated market makers (AMMs) work and how to implement a simple DEX.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Foundry

* [Foundry](https://book.getfoundry.sh/getting-started/installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/uniswap/foundry/contract
```

Install OpenZeppelin Contracts:

* Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

```shell
forge install OpenZeppelin/openzeppelin-contracts --no-git
```

### Step 3: Write the Uniswap Contract

Create a New Solidity File:

* Create a new file for your uniswap contract:

```shell
touch src/Uniswap.sol
```

Write the uniswap Contract:

* Open `src/Uniswap.sol` in your favorite text editor and add the following code:

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

import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";

contract Uniswap {
    address public tokenA;
    address public tokenB;
    uint256 public reserveA;
    uint256 public reserveB;

    constructor(address _tokenA, address _tokenB) {
        tokenA = _tokenA;
        tokenB = _tokenB;
    }

    function addLiquidity(uint256 amountA, uint256 amountB) external {
        require(IERC20(tokenA).transferFrom(msg.sender, address(this), amountA), "Transfer failed");
        require(IERC20(tokenB).transferFrom(msg.sender, address(this), amountB), "Transfer failed");

        reserveA += amountA;
        reserveB += amountB;
    }

    function removeLiquidity(uint256 amountA, uint256 amountB) external {
        require(reserveA >= amountA && reserveB >= amountB, "Insufficient reserves");

        reserveA -= amountA;
        reserveB -= amountB;

        require(IERC20(tokenA).transfer(msg.sender, amountA), "Transfer failed");
        require(IERC20(tokenB).transfer(msg.sender, amountB), "Transfer failed");
    }

    function swapAToB(uint256 amountA) external {
        uint256 amountB = (amountA * reserveB) / reserveA;
        require(IERC20(tokenA).transferFrom(msg.sender, address(this), amountA), "Transfer failed");
        require(IERC20(tokenB).transfer(msg.sender, amountB), "Transfer failed");

        reserveA += amountA;
        reserveB -= amountB;
    }

    function swapBToA(uint256 amountB) external {
        uint256 amountA = (amountB * reserveA) / reserveB;
        require(IERC20(tokenB).transferFrom(msg.sender, address(this), amountB), "Transfer failed");
        require(IERC20(tokenA).transfer(msg.sender, amountA), "Transfer failed");

        reserveB += amountB;
        reserveA -= amountA;
    }
}
```

Compile the Smart Contract:

* Use forge to compile the contract:

```shell
forge build
```

Test the Smart Contract

* Use forge to test the contract:

```shell
forge test
```

### Step 4: Deploy the Uniswap Contract

Create a Deployment Script:

* Create a new file for the deployment script:

```shell
touch script/DeployUniswap.s.sol
```

Write the Deployment Script:

* Open script/DeployUniswap.s.sol and add the following code:

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

import "forge-std/Script.sol";
import "../src/Uniswap.sol";

contract DeployUniswap is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        address tokenA = address(0xAA); // Replace with the address of token A
        address tokenB = address(0xBB); // Replace with the address of token B

        Uniswap uniswap = new Uniswap(tokenA, tokenB);

        vm.stopBroadcast();
    }
}
```

Set the private key:

```shell
export PRIVATE_KEY=<your private key>
```

Deploy the Contract:

* Use forge to deploy the contract to the Pharos Testnet:

```shell
forge script script/DeployUniswap.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Step 5: Interact with the Uniswap Contract

Open `script/InteractUniswap.s.sol` and add the following code:

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

import "forge-std/Script.sol";
import "../src/Uniswap.sol";
import "../test/Uniswap.t.sol";

contract DeployUniswap is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);

        MockERC20 tokenA = new MockERC20("TokenA", "TKA");
        MockERC20 tokenB = new MockERC20("TokenB", "TKB");

        Uniswap uniswap = new Uniswap(address(tokenA), address(tokenB));

        console.log("TokenA deployed at:", address(tokenA));
        console.log("TokenB deployed at:", address(tokenB));
        console.log("Uniswap deployed at:", address(uniswap));

        vm.stopBroadcast();
    }
}
```

Execute the script using forge:

```shell
forge script script/InteractUniswap.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Test Failures**: Check the test output for detailed error messages and adjust the tests accordingly.

### Conclusion

This guide provides a comprehensive introduction to creating and deploying a Uniswap-like contract on the Pharos blockchain using Foundry. If you encounter any issues, refer to the Foundry documentation or the troubleshooting section. Happy building! 🚀


# Hardhat

This guide is suitable for developers who want to start building dApps using Pharos and hardhat toolchains. If you are a new user of Ethereum, please consider researching the [Ethereum documentation](https://ethereum.org/en/developers/docs/) before continuing.

## Write Your First Transaction

This guide will walk you through the process of sending your first transaction on the Pharos blockchain using three popular libraries: `ethers.js`, `web3.js`, and `web3.py`. By the end of this guide, you will understand how to send a transaction programmatically using these libraries.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Pick Your Favorite Contract Develop Framework and IDE

Install your preferred contract framework from the below list:

* ethers.js
* web3.js
* web3.py

### Setup 2: Set Up the Project

Clone the example repo and add dependencies.

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/transaction/ethersjs
npm install
```

```javascript
const { ethers } = require("ethers");

// Connect to Pharos Testnet
const provider = new ethers.providers.JsonRpcProvider("<PHAROS_RPC_URL>");

// Note: Your wallet private key (for testnet only, never expose this in production)
const privateKey = "YOUR_PRIVATE_KEY";
const wallet = new ethers.Wallet(privateKey, provider);

// Transaction details
const tx = {
    to: "RECIPIENT_ADDRESS", // Note: Replace with the recipient's address
    value: ethers.utils.parseEther("0.1"), // Amount to send (0.1 PHAR)
};

// Send the transaction
wallet.sendTransaction(tx)
    .then((transaction) => {
        console.log("Transaction sent:", transaction.hash);
    })
    .catch((error) => {
        console.error("Error sending transaction:", error);
    });
```

### Step 3: Run the Script

Run the script using Node.js:

```shell
node index.js
```

### Key Points

* **Private Key**: Never expose your private key in production. Use environment variables or secure storage.
* **Recipient Address**: Replace RECIPIENT\_ADDRESS with the actual recipient's wallet address.
* **Gas Limit**: Ensure you include a sufficient gas limit for the transaction.

### Troubleshooting

* **Transaction Fails**: Ensure you have enough testnet tokens to cover the transaction fee.
* **Incorrect Network**: Double-check that you are connected to the Pharos Testnet.
* **Invalid Address**: Verify that the recipient's address is correct.

### Conclusion

Now that you’ve sent your first transaction using ethers.js, web3.js, and web3.py.

This guide provides a comprehensive introduction to sending transactions on the Pharos blockchain using three popular libraries. If you encounter any issues, refer to the Troubleshooting section or consult the respective library's documentation. Happy building! 🚀


# Write Your First dApp

This guide will walk you through the process of building and deploying your first end-to-end decentralized application (dApp) on the Pharos blockchain. By the end of this guide, you will have a fully functional dApp that includes a smart contract, a backend, and a frontend.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Hardhat

* [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/dapp/hardhat
npm install
```

### Setup 3: Deploy the Smart Contract

Set the private key:

```shell
npx hardhat vars set PRIVATE_KEY
```

Then

```shell
cd contract && npm install
npx hardhat ignition deploy ./ignition/modules/Counter.js --network pharos
```

### Setup 4: Build the Frontend

Connect the Frontend to the Smart Contract:

* Open `src/app.jsx` and replace its content with the following code:

```jsx
import React, { useState } from "react";
import { ethers } from "ethers";

const contractAddress = "YOUR_CONTRACT_ADDRESS"; // Replace with your contract address
const abi = [
    {
        "inputs": [],
        "name": "get",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "x",
                "type": "uint256"
            }
        ],
        "name": "set",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];

function App() {
    const [value, setValue] = useState("");
    const [storedValue, setStoredValue] = useState("");

    const provider = new ethers.providers.Web3Provider(window.ethereum);
    const signer = provider.getSigner();
    const contract = new ethers.Contract(contractAddress, abi, signer);

    const handleSet = async () => {
        await contract.set(value);
        alert("Value set!");
    };

    const handleGet = async () => {
        const result = await contract.get();
        setStoredValue(result.toString());
    };

    return (
        <div>
            <h1>Simple Storage dApp</h1>
            <input
                type="text"
                value={value}
                onChange={(e) => setValue(e.target.value)}
                placeholder="Enter a value"
            />
            <button onClick={handleSet}>Set Value</button>
            <button onClick={handleGet}>Get Value</button>
            <p>Stored Value: {storedValue}</p>
        </div>
    );
}

export default App;
```

Replace Placeholder Values

* Replace `YOUR_CONTRACT_ADDRESS` with the address of your deployed contract.

### Step 5: Run the dApp

Start the React App

* Navigate to the frontend directory and start the app:

```shell
npm start
```

Interact with the dApp:

* Open your browser and navigate to `http://localhost:3000`.
* Use the input field and buttons to interact with your smart contract.

### Troubleshooting

* Contract Deployment Fails: Ensure you have enough testnet tokens to cover the deployment cost.
* Frontend Connection Issues: Verify that the contract address and ABI are correct.

### Conclusion

Now that you’ve built your first end-to-end dApp.

This guide provides a comprehensive introduction to building and deploying an end-to-end dApp on the Pharos blockchain. If you encounter any issues, refer to the Troubleshooting section or consult the Pharos documentation. Happy building! 🚀


# Write Your First Token

This guide will walk you through the process of creating and deploying your first token on the Pharos blockchain. By the end of this guide, you will have a fully functional token contract and understand how to interact with it.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Hardhat

* [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/token/hardhat/contract
```

Install OpenZeppelin Contracts:

```shell
npm install
```

### Setup 3: Write the Token Contract

Create a New Solidity File:

* Create a new file for your token contract:

```shell
touch contracts/Token.sol
```

Write the Token Contract:

* Open `contracts/Token.sol` in your favorite text editor and add the following code:

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

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

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

Compile the Smart Contract:

```shell
npx hardhat compile
```

Test the Smart Contract

```shell
npx hardhat test
```

### Step 4: Deploy the Token Contract

Set the private key:

```shell
npx hardhat vars set PRIVATE_KEY
```

Deploy the Contract:

```shell
npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
```

### Step 5: Verify the Token Contract

Add the following content to your `hardhat.config.js` file

* For Atlantic Testnet

| Field         | Value                                                                                  |
| ------------- | -------------------------------------------------------------------------------------- |
| chain\_name   | pharos                                                                                 |
| chain\_id     | 688689                                                                                 |
| rpc\_endpoint | <https://atlantic.dplabs-internal.com>"                                                |
| api\_host     | <https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command\\_api/contract> |
| explorer\_url | <https://atlantic.pharosscan.xyz/>                                                     |

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

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.28",
  networks: {
    pharos: {
      url: "https://atlantic.dplabs-internal.com",
      accounts: [vars.get("PRIVATE_KEY")],
    },
  },
  etherscan: {
    customChains: [
      {
        network: "pharos",
        chainId: 688689,
        urls: {
          apiURL: "https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract",
          browserURL: "https://atlantic.pharosscan.xyz/",
        },
      },
    ],
    apiKey: {
      pharos: "Put a random string", // Note we don't need a apiKey here, just leave a random string
    },
  }
};
```

* For Testnet

| Field         | Value                                                                         |
| ------------- | ----------------------------------------------------------------------------- |
| chain\_name   | pharos                                                                        |
| chain\_id     | 688688                                                                        |
| rpc\_endpoint | <https://testnet.dplabs-internal.com>                                         |
| api\_host     | <https://api.socialscan.io/pharos-testnet/v1/explorer/command\\_api/contract> |
| explorer\_url | <https://testnet.pharosscan.xyz/>                                             |

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

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.28",
  networks: {
    pharos: {
      url: "https://testnet.dplabs-internal.com",
      accounts: [vars.get("PRIVATE_KEY")],
    },
  },
  etherscan: {
    customChains: [
      {
        network: "pharos",
        chainId: 688688, // Or use Pharos Atlantic Testnet chain id 688689
        urls: {
          apiURL: "https://api.socialscan.io/pharos-testnet/v1/explorer/command_api/contract",
          browserURL: "https://testnet.pharosscan.xyz/",
        },
      },
    ],
    apiKey: {
      pharos: "Put a random string", // Note we don't need a apiKey here, just leave a random string
    },
  }
};
```

Run the hardhat verify command

```shell
npx hardhat verify --network pharos <contract_address> <constructor_args_params>
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.

### Conclusion

Now that you’ve created and deployed your first token using Hardhat.

This guide provides a comprehensive introduction to creating and deploying a token on the Pharos blockchain using Hardhat. If you encounter any issues, refer to the Troubleshooting section or consult the Hardhat documentation. Happy building! 🚀


# Write Your First NFT

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Hardhat

* [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/nft/hardhat/contract
```

Install OpenZeppelin Contracts:

```shell
npm install
```

### Setup 3: Write the NFT Contract

Create a New Solidity File:

* Create a new file for your NFT contract:

```shell
touch contracts/Token.sol
```

Write the Token Contract:

* Open `contracts/Token.sol` in your favorite text editor and add the following code:

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

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

contract Token is ERC721 {
    uint256 id;
    constructor() ERC721("Token", "MTK") {}

    function mint(address to) public returns (uint256) {
        id += 1;
        _mint(to, id);
        return id;
    }
}
```

Compile the Smart Contract:

```shell
npx hardhat compile
```

Test the Smart Contract

```shell
npx hardhat test
```

### Step 4: Deploy the Token Contract

Set the private key:

```shell
npx hardhat vars set PRIVATE_KEY
```

Deploy the Contract:

```shell
npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
```

### Step 5: Verify the Token Contract

Add the following content to your `hardhat.config.js` file

* For Atlantic Testnet

| Field         | Value                                                                                  |
| ------------- | -------------------------------------------------------------------------------------- |
| chain\_name   | pharos                                                                                 |
| chain\_id     | 688689                                                                                 |
| rpc\_endpoint | <https://atlantic.dplabs-internal.com>"                                                |
| api\_host     | <https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command\\_api/contract> |
| explorer\_url | <https://atlantic.pharosscan.xyz/>                                                     |

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

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.28",
  networks: {
    pharos: {
      url: "https://atlantic.dplabs-internal.com",
      accounts: [vars.get("PRIVATE_KEY")],
    },
  },
  etherscan: {
    customChains: [
      {
        network: "pharos",
        chainId: 688689,
        urls: {
          apiURL: "https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract",
          browserURL: "https://atlantic.pharosscan.xyz/",
        },
      },
    ],
    apiKey: {
      pharos: "Put a random string", // Note we don't need a apiKey here, just leave a random string
    },
  }
};
```

* For Testnet

| Field         | Value                                                                         |
| ------------- | ----------------------------------------------------------------------------- |
| chain\_name   | pharos                                                                        |
| chain\_id     | 688688                                                                        |
| rpc\_endpoint | <https://testnet.dplabs-internal.com>                                         |
| api\_host     | <https://api.socialscan.io/pharos-testnet/v1/explorer/command\\_api/contract> |
| explorer\_url | <https://testnet.pharosscan.xyz/>                                             |

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

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.28",
  networks: {
    pharos: {
      url: "https://testnet.dplabs-internal.com",
      accounts: [vars.get("PRIVATE_KEY")],
    },
  },
  etherscan: {
    customChains: [
      {
        network: "pharos",
        chainId: 688688, // Or use Pharos Atlantic Testnet chain id 688689
        urls: {
          apiURL: "https://api.socialscan.io/pharos-testnet/v1/explorer/command_api/contract",
          browserURL: "https://testnet.pharosscan.xyz/",
        },
      },
    ],
    apiKey: {
      pharos: "Put a random string", // Note we don't need a apiKey here, just leave a random string
    },
  }
};
```

Run the hardhat verify command

```shell
npx hardhat verify --network pharos <contract_address> <constructor_args_params>
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.

### Conclusion

Now that you’ve created and deployed your first NFT using Hardhat.

This guide provides a comprehensive introduction to to creating and deploying an NFT on the Pharos blockchain. If you encounter any issues, refer to the Troubleshooting section or consult the Hardhat documentation. Happy building! 🚀


# Write Your First Uniswap Contract

This guide will walk you through the process of creating and deploying a simple Uniswap-like decentralized exchange (DEX) contract on the Pharos blockchain. By the end of this guide, you will have a basic understanding of how automated market makers (AMMs) work and how to implement a simple DEX.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Node.js: Install it from [nodejs.org](https://nodejs.org/).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install Hardhat

* [Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started#installation)

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/uniswap/hardhat/contract
```

Install OpenZeppelin Contracts:

```shell
npm install
```

### Step 3: Write the Uniswap Contract

Create a New Solidity File:

* Create a new file for your uniswap contract:

```shell
touch contracts/Uniswap.sol
```

Write the uniswap Contract:

* Open `contracts/Uniswap.sol` in your favorite text editor and add the following code:

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

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

contract Uniswap {
    address public tokenA;
    address public tokenB;
    uint256 public reserveA;
    uint256 public reserveB;

    constructor(address _tokenA, address _tokenB) {
        tokenA = _tokenA;
        tokenB = _tokenB;
    }

    function addLiquidity(uint256 amountA, uint256 amountB) external {
        require(IERC20(tokenA).transferFrom(msg.sender, address(this), amountA), "Transfer failed");
        require(IERC20(tokenB).transferFrom(msg.sender, address(this), amountB), "Transfer failed");

        reserveA += amountA;
        reserveB += amountB;
    }

    function removeLiquidity(uint256 amountA, uint256 amountB) external {
        require(reserveA >= amountA && reserveB >= amountB, "Insufficient reserves");

        reserveA -= amountA;
        reserveB -= amountB;

        require(IERC20(tokenA).transfer(msg.sender, amountA), "Transfer failed");
        require(IERC20(tokenB).transfer(msg.sender, amountB), "Transfer failed");
    }

    function swapAToB(uint256 amountA) external {
        uint256 amountB = (amountA * reserveB) / reserveA;
        require(IERC20(tokenA).transferFrom(msg.sender, address(this), amountA), "Transfer failed");
        require(IERC20(tokenB).transfer(msg.sender, amountB), "Transfer failed");

        reserveA += amountA;
        reserveB -= amountB;
    }

    function swapBToA(uint256 amountB) external {
        uint256 amountA = (amountB * reserveA) / reserveB;
        require(IERC20(tokenB).transferFrom(msg.sender, address(this), amountB), "Transfer failed");
        require(IERC20(tokenA).transfer(msg.sender, amountA), "Transfer failed");

        reserveB += amountB;
        reserveA -= amountA;
    }
}
```

Compile the Smart Contract:

```shell
npx hardhat compile
```

Test the Smart Contract

```shell
npx hardhat test
```

### Step 4: Deploy the Uniswap Contract

Set the private key:

```shell
npx hardhat vars set PRIVATE_KEY
```

Deploy the Contract:

```shell
npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Test Failures**: Check the test output for detailed error messages and adjust the tests accordingly.

### Conclusion

This guide provides a comprehensive introduction to creating and deploying a Uniswap-like contract on the Pharos blockchain using Hardhat. If you encounter any issues, refer to the Hardhat documentation or the troubleshooting section. Happy building! 🚀


# Rust

This guide is suitable for developers who want to start building dApps using Pharos and Rust WASM toolchains. If you are a new user of Ethereum, please consider researching the [Ethereum documentation](https://ethereum.org/en/developers/docs/) before continuing.

This guide will walk you through the process of creating and deploying your first token on the Pharos blockchain using Rust and WASM toolchains. By the end of this guide, you will have a fully functional token contract and understand how to interact with it.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Python: Used to interact with the Pharos network and execute contract testing scripts.
* Rust: Install it from [rust-lang.org](https://www.rust-lang.org/tools/install).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install WASM Toolchains With Cargo

Install the pharos wasm toolchain plugin using the Cargo tool:

```shell
cargo install --git https://github.com/PharosNetwork/pharos-cargo-stylus
```

Add the `wasm32-unknown-unknown` build target to your Rust compiler:

```shell
rustup target add wasm32-unknown-unknown
```

You should now have it available as a Cargo subcommand:

```shell
cargo stylus --help

Cargo subcommand for developing Pharos Stylus projects
```

> Note: Pharos built the chain execution client from scratch using C++, and we design, developed and open-sourced the [Dora](https://github.com/dp-labs/dora) VM, which combines EVM and WASM in a deeply compiled VM arch with native interoperability. The Rust toolchain and Rust SDK are forked from Stylus toolchain and SDK to facilitate easier adoption, the change here is the removal of Arbitrum ink billing and the simplification of the activation and compilation process on Pharos.

### Setup 3: Write the Token Contract

* Create a new file for your token contract:

```shell
touch src/lib.rs
```

Write the Token Contract:

* Open `src/lib.rs` in your favorite text editor and add the following code:

```rust
// Only run this as a WASM if the export-abi feature is not set.
#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
extern crate alloc;

// Modules and imports
mod erc20;

use crate::erc20::{Erc20, Erc20Error, Erc20Params};
use alloy_primitives::{Address, U256};
use stylus_sdk::{msg, prelude::*};

/// Immutable definitions
struct StylusTestTokenParams;
impl Erc20Params for StylusTestTokenParams {
    const NAME: &'static str = "StylusTestToken";
    const SYMBOL: &'static str = "STTK";
    const DECIMALS: u8 = 18;
}

// Define the entrypoint as a Solidity storage object. The sol_storage! macro
// will generate Rust-equivalent structs with all fields mapped to Solidity-equivalent
// storage slots and types.
sol_storage! {
    #[entrypoint]
    struct StylusTestToken {
        // Allows erc20 to access StylusTestToken's storage and make calls
        #[borrow]
        Erc20<StylusTestTokenParams> erc20;
    }
}

#[public]
#[inherit(Erc20<StylusTestTokenParams>)]
impl StylusTestToken {
    /// Mints tokens
    pub fn mint(&mut self, value: U256) -> Result<(), Erc20Error> {
        self.erc20.mint(msg::sender(), value)?;
        Ok(())
    }

    /// Mints tokens to another address
    pub fn mint_to(&mut self, to: Address, value: U256) -> Result<(), Erc20Error> {
        self.erc20.mint(to, value)?;
        Ok(())
    }

    /// Burns tokens
    pub fn burn(&mut self, value: U256) -> Result<(), Erc20Error> {
        self.erc20.burn(msg::sender(), value)?;
        Ok(())
    }
}
```

Compile the Smart Contract:

* Use cargo to compile the contract:

```shell
cargo stylus check --endpoint=<PHAROS_RPC_URL>
```

### Step 4: Deploy the Token Contract

```shell
cargo stylus deploy --private-key=<YOUR_PRIVATE_KEY> --endpoint=<PHAROS_RPC_URL>
```

### Step 5: Interact with the Token Contract

Open `scripts/main.py` and replace your deployed contract address to the variable `CONTRACT_ADDRESS`:

```python
from web3 import Web3

# Replace Pharos RPC URL
RPC_URL = "<Pharos RPC URL>"
# Note: Replace this address to your contract address
CONTRACT_ADDRESS = Web3.to_checksum_address("0x8ca06d75562be39f645b3b1ead98b15ddfd8c06f")
PRIVATE_KEY = "Replace your private key"

contract_abi = [
    {
        "inputs": [],
        "name": "name",
        "outputs": [{"internalType": "string", "name": "", "type": "string"}],
        "stateMutability": "pure",
        "type": "function",
    },
    {
        "inputs": [],
        "name": "symbol",
        "outputs": [{"internalType": "string", "name": "", "type": "string"}],
        "stateMutability": "pure",
        "type": "function",
    },
    {
        "inputs": [],
        "name": "decimals",
        "outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
        "stateMutability": "pure",
        "type": "function",
    },
    {
        "inputs": [],
        "name": "totalSupply",
        "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
        "stateMutability": "view",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "owner", "type": "address"}],
        "name": "balanceOf",
        "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
        "stateMutability": "view",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "to", "type": "address"}, {"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "transfer",
        "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "from", "type": "address"}, {"internalType": "address", "name": "to", "type": "address"}, {"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "transferFrom",
        "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "spender", "type": "address"}, {"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "approve",
        "outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "owner", "type": "address"}, {"internalType": "address", "name": "spender", "type": "address"}],
        "name": "allowance",
        "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
        "stateMutability": "view",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "mint",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "address", "name": "to", "type": "address"}, {"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "mintTo",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function",
    },
    {
        "inputs": [{"internalType": "uint256", "name": "value", "type": "uint256"}],
        "name": "burn",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function",
    },
]

w3 = Web3(Web3.HTTPProvider(RPC_URL))
print(f"Connected to Ethereum: {w3.is_connected()}")
contract = w3.eth.contract(address=CONTRACT_ADDRESS, abi=contract_abi)
your_address = w3.eth.account.from_key(PRIVATE_KEY).address
print(f"Your Address: {your_address}")
print(f"Your Balance: {contract.functions.balanceOf(your_address).call()}")
```

Execute the script using python:

```shell
python3 -m pip install web3 && python3 scripts/main.py
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.

### Next Steps

Now that you’ve created and deployed your first token using Rust. This guide provides a comprehensive introduction to creating and deploying a token on the Pharos blockchain using Rust. If you encounter any issues, refer to the Troubleshooting section or consult the Rust documentation. Happy building! 🚀


# Interoperability

This guide is suitable for developers who want to realize interoperability between EVM and WASM. If you are a new user of Ethereum, please consider researching the [Ethereum documentation](https://ethereum.org/en/developers/docs/) before continuing.


# Call EVM From WASM

This guide will walk you through the process of calling EVM contracts from the WASM code e.g., Rust.

### Prerequisites

Before you begin, ensure you have the following:

* Git: Used for code management and obtain examples.
* Rust: Install it from [rust-lang.org](https://www.rust-lang.org/tools/install).
* Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

### Setup 1: Install WASM Toolchains With Cargo

Install the pharos wasm toolchain plugin using the Cargo tool:

```shell
cargo install --git https://github.com/PharosNetwork/pharos-cargo-stylus
```

Add the `wasm32-unknown-unknown` build target to your Rust compiler:

```shell
rustup target add wasm32-unknown-unknown
```

You should now have it available as a Cargo subcommand:

```shell
cargo stylus --help

Cargo subcommand for developing Pharos Stylus projects
```

> Note: Pharos built the chain excution client from scratch using C++, and we design, developed and open-sourced the [Dora](https://github.com/dp-labs/dora) VM, which combines EVM and WASM in a deeply compiled VM arch with native interoperability. The Rust toolchain and Rust SDK are forked from Stylus toolchain and SDK to facilitate easier adoption, the change here is the removal of Arbitrum ink billing and the simplification of the activation and compilation process on Pharos.

### Setup 2: Set Up the Project

Clone the example repo:

```shell
git clone https://github.com/PharosNetwork/examples
cd examples/interoperability/call-evm-from-wasm
```

### Setup 3: Write the Interoperability Contract

* Create a new file for your interoperability contract:

```shell
touch src/lib.rs
```

Write the interoperability Contract:

* Open `src/lib.rs` in your favorite text editor and add the following code:

```rust
#![cfg_attr(not(feature = "export-abi"), no_main)]
extern crate alloc;

use stylus_sdk::{
    abi::Bytes,
    alloy_primitives::{Address, U256},
    prelude::*,
    stylus_core::calls::context::Call,
};
use alloy_sol_types::{sol, SolCall};

sol! {
    interface IErc20  {
        function mint(uint256 value) external;
    }
}

#[storage]
#[entrypoint]
pub struct Interoperability;

#[public]
impl Interoperability {
    /// Here we can call an EVM contract with the given target address and calldata.
    pub fn execute(&self, target: Address, data: Bytes) -> Bytes {
        let result = self.vm().call(&Call::default(), target, &data);
        result.unwrap().into()
    }

    /// Here we can transfer ETH to the given target address.
    pub fn transfer_eth(&self, to: Address, amount: U256) -> Result<(), Vec<u8>> {
        self.vm().transfer_eth(to, amount)
    }

    /// Here we call an ERC20 token contract written in Solidity.
    pub fn mint_erc20(&self, erc20: Address, value: U256) -> Bytes {
        self.execute(erc20, Bytes(IErc20::mintCall {
            value,
        }.abi_encode()))
    }
}
```

Compile the Smart Contract:

* Use cargo to compile the contract:

```shell
cargo stylus check --endpoint=<PHAROS_RPC_URL>
```

### Step 4: Deploy the Interoperability Contract

```shell
cargo stylus deploy --private-key=<YOUR_PRIVATE_KEY> --endpoint=<PHAROS_RPC_URL>
```

### Troubleshooting

* **Contract Deployment Fails**: Ensure you have enough testnet tokens to cover the deployment cost.
* **Interaction Issues**: Verify that the contract address and ABI are correct.
* **Insufficient Balance**: Ensure your wallet has enough tokens to transfer.


# x402

A developer guide on how to get started with x402 on Pharos Mainnet

## Overview

x402 is an open, internet-native payment protocol. The HTTP 402 status code was defined in the early HTTP/1.1 specification as “Payment Required.” However, for a long time, it served only as a reserved status code with few practical applications. With the maturation of blockchain and crypto-payment infrastructure, this status code has been reactivated, evolving into a lightweight, trustless mechanism for content payment.

In this model, when a client requests protected resources (such as articles, API responses, or data streams), the server does not return the content directly. Instead, it returns an HTTP 402 response accompanied by payment instructions (e.g., recipient address, amount, supported payment networks, etc.). The client initiates a micro-payment—either on-chain or off-chain—via a wallet. Once the server verifies the validity of the transaction, it unlocks and delivers the requested content. In decentralized networks like Pharos, this process is typically implemented as follows: the user sends a micro-token transfer to a specified address → the network node or server-side listens for and verifies the transaction → the original requested resource is automatically returned upon successful verification. The entire process requires no registration, login, or traditional payment gateways, truly achieving “pay-as-you-go, instant access.”

This mechanism provides native, programmable payment capabilities for AI agents, automated services, or IoT devices, serving as one of the key infrastructures for building an Autonomous Economy.

## Working

<figure><img src="/files/HR9JX0A0Mxg4HIh8zx9b" alt="x402 Flowchart"><figcaption><p>x402 Flowchart</p></figcaption></figure>

### Initial Request and Payment Requirements

* **Resource Request Initiation:** The client initiates an HTTP request (e.g., `GET /api`) to the resource server for a specific protected resource.
* **Payment Trigger Response:** The server returns an `HTTP 402 Payment Required` status code. A Base64-encoded `PaymentRequired` object is carried in the response body via the `PAYMENT-REQUIRED` header. This object defines the payment scheme, target network, and specific payment parameters required for the current transaction.

### Payment Credential Construction and Signing

* **Scheme Selection and Payment Payload Generation:** The client parses the payment requirements returned by the server and selects a specific payment scheme based on its own supported payment capabilities. Subsequently, the client constructs a `PaymentPayload` containing the necessary metadata according to the protocol specifications of that scheme.
* **Secondary Request Submission:** The client re-initiates the resource request and attaches the signed `PaymentPayload` in the `PAYMENT-SIGNATURE` HTTP header.

### Verification

* **Verification Routing:** Upon receiving the signed request, the resource server executes validation logic. The server can choose to perform local verification or forward the `PaymentPayload` and the original `PaymentRequirements` to the Facilitator's `/verify` interface for remote verification.

**Compliance Check:** The Facilitator verifies the payload according to the specified payment scheme and blockchain network rules, then returns a `Verification Response` to the resource server.

* **Access Control Decisions:**
  * **Verification Failed:** If the verification does not pass, the resource server returns a 402 status code and error details again.
  * **Verification Succeeded:** If the verification passes, the resource server confirms the payment credential is valid and immediately initiates business logic processing (Do Work), preparing for on-chain settlement.

### On-chain Settlement and Confirmation

* **Transaction Broadcasting:** The resource server is responsible for initiating fund settlement. This can be completed by interacting directly with a blockchain node or by delegating it to a Facilitator. If delegated, the server sends the payment credential to the Facilitator's `/settle` interface.
* **Block Inclusion:** The Facilitator submits the transaction to the blockchain network (Submit tx).

### Delivery and Receipt

* **Settlement Feedback:** Once the on-chain transaction is confirmed, the Facilitator returns a settlement execution response (Settlement Response) containing the transaction hash (`tx_hash`) to the resource server.
* **Resource Delivery:** The resource server sends an `HTTP 200 OK` response to the client, placing the requested resource content in the response body. Simultaneously, to provide proof of payment, the server attaches Base64-encoded settlement response information in the `PAYMENT-RESPONSE` response header, marking the completion of the entire "pay-before-receive" closed loop.

**Pharos Support:** The x402 protocol itself is chain-agnostic. On Pharos, it supports all ERC-20 tokens. Pharos’s high-performance features (30,000 TPS, sub-second finality) make it particularly suitable for x402 micro-payment scenarios.

## Use Cases

x402 enables micro-payment and pay-per-use scenarios that were previously impossible due to economic unfeasibility. Below are some possible application scenarios:

### Agent Commerce

* **AI Agents subscribing to content services with auto-renewal:** AI agents can represent users in subscribing to content services such as industry updates, financial newsletters, or in-depth analysis reports, automatically completing periodic fee payments and renewal operations through integrated digital wallets.
* **Pay-per-use AI capability calls:** Users or systems pay based on the actual number of uses when calling Large Language Model (LLM) inference, image generation, or other AI APIs.
* **Payments between agents:** Supports peer-to-peer payments between multiple autonomous agents for data sharing, task collaboration, or service provision without human intervention.
* **Automated financial trading agents:** Based on real-time market data and external information sources (e.g., paid data streams), these agents autonomously execute high-frequency trading strategies and dynamically procure necessary data resources to optimize decision-making.

### Medical and Health Tech

* **AI-assisted diagnostic reports:** After users upload medical images, they pay to unlock structured diagnostic suggestions.
* **Personalized health advice APIs:** Wearable devices call health analysis models and pay-per-call to obtain deep insights.
* **Access to electronic medical record fragments:** Third-party applications request specific medical visit records, and patients authorize and pay a small fee to share data.

### Education and Knowledge Services

* **AI Tutor single-question answering:** Basic prompts are free, while in-depth analysis requires payment to unlock.
* **Professional course fragment unlocking:** After a trial preview, users pay by the minute or chapter to continue learning.
* **Single academic paper access:** No subscription required; pay per paper for instant PDF access.

### E-commerce and Consumer Services

* **Personalized recommendation engine calls:** E-commerce platforms pay AI service providers based on successful conversions.
* **AR try-on/make-up experience:** Payment is required to unlock real-time rendering before high-precision virtual trials.
* **Supply chain risk assessment queries:** Enterprises pay-per-query for supplier risks, which can be used for procurement decision-making.

### Other Scenarios

* **Computing power rental:** Billing by the second for using GPU/CPU resources for training or inference.
* **IoT micro-transactions:** Sensor networks pay automatically based on data points or events (e.g., environmental monitoring, smart meters).
* **Sustainability and carbon credits:** Companies or individuals purchase carbon offsets on demand, with the API returning a 402 to trigger instant settlement.

**Key Advantage:** x402 transaction costs on Pharos are extremely low, making true micro-payments possible. Combined with instant finality, this enables real-time access control.

## x402 SDK for Pharos

### Server-side: Using Express + x402 Middleware

For Express applications, you can use the middleware pattern:

```ts

// Load .env environment variables
import { config } from "dotenv";
config();
// Import required modules
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";

// === Environment Configuration ===
const payToAddress = process.env.PAY_TO_ADDRESS as `0x${string}`;
if (!payToAddress) {
  console.error("❌ Please set the PAY_TO_ADDRESS environment variable");
  console.error('Example: export PAY_TO_ADDRESS=0xYourAddressHere');
  process.exit(1);
  }
  const facilitatorUrl = process.env.FACILITATOR_URL; // Facilitator service URL
const port = parseInt(process.env.PORT || "4021", 10); // Listening port

// USDC Configuration
const usdcAddress = process.env.USDC_ADDRESS; // USDC contract address on Pharos
const usdcName = process.env.USDC_NAME || "USDC";

// === Initialize Client and Resource Server ===
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
const resourceServer = new x402ResourceServer(facilitatorClient);

// === Create EVM Payment Scheme and Register Custom Amount Parser ===
const evmScheme = new ExactEvmScheme();
// Convert USD amount to USDC (6 decimals precision)
evmScheme.registerMoneyParser(async (amount: number, network: string) => {
  if (network === "eip155:1672") {
    return {
      amount: Math.round(amount * 1e6).toString(), // Convert to integer string (e.g., 0.01 → "10000")
      asset: usdcAddress,                          // USDC contract address
      extra: {
        token: usdcName,
        name: usdcName,
        version: "2",
      },
    };
  }
  return null;
});
// Register Pharos Pacific Mainnet (Chain ID: 1672)
resourceServer.register("eip155:1672", evmScheme);

// === Create Express App ===
const app = express();

// Use payment middleware to protect routes
app.use(
  paymentMiddleware(
    {
      // Premium endpoint: $0.01
      "GET /data": {
        accepts: {
          scheme: "exact",
          price: "0.01",
          network: "eip155:1672",
          payTo: payToAddress,
        },
        description: "Paid data endpoint",
        mimeType: "application/json",
      },
// Low-cost endpoint: $0.005
      "GET /api/info": {
        accepts: {
          scheme: "exact",
          price: "0.005",
          network: "eip155:1672",
          payTo: payToAddress,
        },
        description: "Low-cost info endpoint",
        mimeType: "application/json",
      },
    },
    resourceServer
  )
);

// === Protected Business Endpoints ===
app.get("/data", (req, res) => {
  res.json({ message: "Hello, paid user!", timestamp: Date.now() });
});
app.get("/api/info", (req, res) => {
  res.json({
    name: "Pharos x402 Server",
    network: "Pharos Pacific Mainnet",
    chainId: 1672,
    timestamp: Date.now(),
  });
});

// === Health Check (No Payment Required) ===
app.get("/health", (req, res) => {
  res.json({ status: "ok" });
});
// === Start Server ===
app.listen(port, () => {
  console.log(`✅ Server started, listening on http://localhost:${port}`);
  console.log(`📡 Network: Pharos Pacific Mainnet (Chain ID: 1672)`);
  console.log(`💰 Payment receiver address: ${payToAddress}`);
  console.log(`🪙 USDC contract: ${usdcAddress}`);
});

```

### Client-side: Using Fetch + x402 SDK

```ts

// Load environment variables
import { config } from "dotenv";
config();
// Import required modules
import { wrapFetchWithPayment, x402Client, decodePaymentResponseHeader } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import fs from "fs";

// === Get EVM private key (prefer environment variable, fall back to .private_key file) ===
const privateKey = 
  process.env.EVM_PRIVATE_KEY || 
  (fs.existsSync(".private_key") ? fs.readFileSync(".private_key", "utf-8").trim() : null);
if (!privateKey) {
  console.error("❌ Please set the EVM_PRIVATE_KEY environment variable, or create a .private_key file");
  process.exit(1);
}

// === Initialize EVM account and x402 client ===
const signer = privateKeyToAccount(privateKey as `0x${string}`);
const client = new x402Client();

// Register payment scheme for Pharos Pacific Mainnet (Chain ID: 1672)
client.register("eip155:1672", new ExactEvmScheme(signer));

// Wrap fetch to enable automatic payments
const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// === Get target URL (from command-line argument, defaulting to local /data endpoint) ===
const url = process.argv[2] || "http://localhost:4021/data";
console.log(`📡 Request URL: ${url}`);
console.log(`💳 Payer account: ${signer.address}`);

// === Make a request with automatic payment ===
try {
  const response = await fetchWithPayment(url);
  const data = await response.json();
  console.log("✅ Request succeeded!");
  console.log("📦 Response data:", JSON.stringify(data, null, 2));

// If the response includes payment info, decode and print transaction details
  const paymentResponseHeader = response.headers.get("PAYMENT-RESPONSE");
  if (paymentResponseHeader) {
    const paymentResponse = decodePaymentResponseHeader(paymentResponseHeader);
    console.log("💰 Transaction hash:", paymentResponse.transaction);
    console.log("🌐 Network:", paymentResponse.network);
    console.log("📤 Payer address:", paymentResponse.payer);
  }
} catch (error) {
  console.error("❌ Request failed:", error);
  process.exit(1);
}

```

### Facilitator Implementation

```ts
import dotenv from "dotenv";
import express from "express";
import { privateKeyToAccount } from "viem/accounts";
import { createWalletClient, http, publicActions, defineChain } from "viem";
import { x402Facilitator } from "@x402/core/facilitator";
import { toFacilitatorEvmSigner } from "@x402/evm";
import { ExactEvmScheme } from "@x402/evm/exact/facilitator";

dotenv.config();

// Check for EVM private key
if (!process.env.EVM_PRIVATE_KEY) {
  console.error("❌ Please set EVM_PRIVATE_KEY");
  process.exit(1);
}

// Define Pharos mainnet (ID: 1672)
const pharos = defineChain({
  id: 1672,
  name: "Pharos",
  nativeCurrency: { name: "PROS", symbol: "PROS", decimals: 18 },
  rpcUrls: { default: { http: ["https://rpc.pharos.xyz"] } }, // Pharos RPC endpoint
  testnet: false,
});

// Create wallet client
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const client = createWalletClient({
  account,
  chain: pharos,
  transport: http(undefined, { timeout: 30_000 }),
}).extend(publicActions);

// Create EVM signer required by the Facilitator
const signer = toFacilitatorEvmSigner({
  address: account.address,
  getCode: (args) => client.getCode(args),
  readContract: (args) => client.readContract({ ...args, args: args.args || [] }),
  verifyTypedData: (args) => client.verifyTypedData(args as any),
  writeContract: (args) => client.writeContract({ ...args, args: args.args || [] }),
  sendTransaction: (args) => client.sendTransaction(args),
  waitForTransactionReceipt: (args) => client.waitForTransactionReceipt(args),
});

// Initialize Facilitator and register Pharos chain
const facilitator = new x402Facilitator();
facilitator.register("eip155:1672", new ExactEvmScheme(signer, { deployERC4337WithEIP6492: true }));

// Create Express app
const app = express();
app.use(express.json());

// Verify payment
app.post("/verify", async (req, res) => {
  try {
    const { paymentPayload, paymentRequirements } = req.body;
    const result = await facilitator.verify(paymentPayload, paymentRequirements);
    res.json(result);
  } catch (e) {
    res.status(500).json({ error: (e as Error).message });
  }
});

// Settle payment on-chain
app.post("/settle", async (req, res) => {
  try {
    const { paymentPayload, paymentRequirements } = req.body;
    const result = await facilitator.settle(paymentPayload, paymentRequirements);
    res.json(result);
  } catch (e) {
    res.status(500).json({ error: (e as Error).message });
  }
});

// Return supported payment schemes
app.get("/supported", (req, res) => {
  res.json(facilitator.getSupported());
});

// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`✅ Facilitator is running on port: ${PORT}`);
  console.log(`⛓️ Supported network: eip155:1672 (Pharos Pacific)`);
});

```

### Skill

**Skill Repository:** `https://github.com/PharosNetwork/examples/tree/main/skills/x402-pharos`

This skill facilitates using the x402 SDK to construct the Pharos x402 server and the corresponding test client. This skill targets the Pharos Pacific Mainnet with chain ID 1672. Configure the USDC token address (`0xc879c018db60520f4355c26ed1a6d572cdac1815`) with the official token contract address you intend to use for payments.

The following demonstrates building a server and client demo using this skill, taking opencode as an example.

**Create Server:** Enter the prompt: "Help me create a pharos-x402 server." It will ask for corresponding parameters (recipient address, Facilitator URL, etc.) and automatically generate the corresponding code files and configuration files.

<figure><img src="/files/7kongUBWMWzumzwz2n1w" alt="x402-2"><figcaption></figcaption></figure>

During the process, you can ask it to generate simple example code through conversation, including `/data` and `/premium` endpoints.

<figure><img src="/files/U1LXqlNLBwMPjHP1IY94" alt="x402-3"><figcaption></figcaption></figure>

The service can then be started.

<figure><img src="/files/lhrtnmLuPaDAdf6ZUQ4r" alt="x402-4"><figcaption></figcaption></figure>

Next, you can ask it to write a test client.

<figure><img src="/files/mtmlQDpsLmULaAXfox0m" alt="x402-5"><figcaption></figcaption></figure>

Once the private key is configured, you can test the payment process.

**Please note: never reveal your private key to large models. Configure it via environment variables or file writing.**

<figure><img src="/files/nm50rAuA6yEL511XUVzA" alt="x402-6"><figcaption></figcaption></figure>

## Advantages of x402 on Pharos

| Features                | Pharos         | Other EVM Chains        |
| ----------------------- | -------------- | ----------------------- |
| TPS                     | 30,000         | Usually < 5,000         |
| Block Time              | < 1 Second     | Usually 2 - 15 Seconds  |
| Finality Time           | Sub-second     | Usually several minutes |
| Micro-payment Viability | Extremely High | Low - High              |

### Why Choose Pharos for x402?

* **Ultra-low transaction costs:** Pharos's high throughput and efficient consensus minimize transaction costs, making micro-payments truly economically viable.
* **Instant Finality:** Sub-second finality means users can access content immediately after payment without waiting for multiple block confirmations.
* **EVM Compatibility:** Fully compatible with Ethereum tools (ethers.js, viem, Hardhat, etc.); existing developers do not need to learn new tools.
* **Compliance-Friendly:** Pharos's built-in compliance modules (ZK-KYC/AML) allow x402 payments to meet regulatory requirements, making it suitable for institutional adoption.
* **Modular Architecture:** Through SPN (Specialized Processing Network), the execution environment can be customized for specific payment scenarios.

## Development Recommendations

* **Temporary Access Authorization based on JWT:** After the user completes payment verification, the server can issue a short-term valid JSON Web Token (JWT) as a credential for subsequent resource access. This token should have a reasonable expiration time to avoid repeated payments while reducing the overhead of frequent on-chain verification by the server.
* **Security Management of Sensitive Information:** All confidential data such as private keys and API keys should be injected into the application through environment variables or dedicated secret management services. Hardcoding them in source code or configuration files is strictly prohibited to prevent accidental leakage.
* **Transaction Idempotency and Retry Support:** The client should be able to initiate a retry request based on the original transaction hash after a network interruption or request failure. The server must implement idempotency processing logic to ensure that the same on-chain transaction is not billed multiple times or granted permissions repeatedly.
* **Introducing Facilitator Service to Decouple Blockchain Interaction:** Design a middleware service (Facilitator) to encapsulate complex logic such as blockchain node communication, transaction listening, and verification, simplifying the main business code. However, note that this component could become a performance bottleneck or a single point of failure; it is recommended to combine redundant deployment, health checks, multi-account strategies, and downgrade policies to improve system resilience.

## Reference Resources

* **Pharos Official Website:** `https://www.pharos.xyz/`
* **Pharos Documentation:** `https://docs.pharos.xyz/`
* **x402 Specification:** `https://x402.org/`


# EVM

The Ethereum Virtual Machine (EVM) is the core component of the Ethereum blockchain, responsible for executing smart contracts and managing the state of the network. It acts as a decentralized computer, maintaining a global state and executing program instructions defined in smart contracts. Key features include:

* **State Machine**: Executes transactions and changes the blockchain state.
* **Deterministic Execution**: Given the same input, it will always produce the same output.
* **Isolated Execution**: Executes code in a sandboxed environment.

**References**:

* [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf): The authoritative technical document that defines the EVM and its operational mechanisms.

### Learning Resources

#### Tutorials and Guides

* **EVM Playground**: An interactive platform for experimenting with EVM opcodes and bytecode. It provides detailed explanations and visualizations of how EVM executes opcodes.
  * **Accessing Constant Variables**: Learn how to define and use constant variables in EVM. [Link](https://evm.codes/playground?lesson=1)
  * **Function Parameters from Calldata**: Understand how to access function parameters using calldata. [Link](https://evm.codes/playground?lesson=2)
  * **Testing Macros in EVM.codes**: Build and test functions using raw EVM bytecode. [Link](https://evm.codes/playground?lesson=3)
  * **General EVM Playground**: [Link](https://evm.codes)

**EVM Cheatsheet**: A comprehensive guide detailing EVM architecture, functionalities, and operational mechanisms. It covers:

* EVM Architecture: Stack, memory, storage, and instruction set.
* Bytecode and Opcodes: Detailed explanations of EVM bytecode and commonly used opcodes.
* Memory vs. Storage: Differences and use cases.
* Gas and Gas Management: Understanding gas costs and optimization techniques.
* **Link**: [EVM Cheatsheet](https://github.com/pylaugh/evm-cheatsheet)

#### Academic Research

* **Smart Contract Decompilation**: Learn how to decompile EVM bytecode back into high-level languages like Solidity. Tools like Mythril, Ethersplay, or Porosity are used for this purpose.
  * **Mythril**: [Link](https://github.com/ConsenSys/mythril)
  * **Ethersplay**: [Link](https://github.com/ConsenSys/ethersplay)
  * **Porosity**: [Link](https://github.com/gnidan/porosity)
* **EVM Execution Model**: Explore how EVM executes opcodes, manages memory and storage, and handles transactions.
  * **Link**: [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)

### Tools for EVM Development

* **EVM From Scratch**: A project that guides you through implementing your own EVM in various programming languages. It helps in understanding the core mechanics of EVM.
  * **Link**: [EVM From Scratch](https://github.com/w1nt3r-eth/evm-from-scratch)
* **EVM Puzzles**: A collection of interactive puzzles designed to help you practice and understand EVM operations.
  * **Link**: [EVM Puzzles](https://github.com/fvictorio/evm-puzzles)

### Community and Discussion

* **EVM Learning Manual**: A curated list of resources for learning EVM, maintained by the blockchain community.
  * **Link**: [EVM Learning Manual](https://learnblockchain.cn/article/4800)
* **Stack Overflow**: A platform where you can find answers to common EVM-related questions and participate in discussions.
  * **Link**: [Stack Overflow - EVM Tag](https://stackoverflow.com/questions/tagged/evm)

### Practical Examples and Use Cases

* **Contract Address Generation**: Learn how contract addresses are generated using sender address and nonce.
  * **Link**: [Contract Address Generation](https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed)
* **Bytecode Decompilation**: Understand the process of converting bytecode into opcodes and reconstructing high-level code.
  * **Link**: [Mythril Documentation](https://mythril.readthedocs.io/en/latest/)
* **Storage Management**: Explore how EVM handles storage variables, including writing to and reading from storage slots.
  * **Link**: [Solidity Documentation - Storage](https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html)

### References

* **EVM Playground**: <https://evm.codes>
* **EVM Cheatsheet**: <https://github.com/pylaugh/evm-cheatsheet>
* **EVM From Scratch**: <https://github.com/w1nt3r-eth/evm-from-scratch>
* **EVM Puzzles**: <https://github.com/fvictorio/evm-puzzles>
* **EVM Learning Manual**: <https://learnblockchain.cn/article/4800>
* **Stack Overflow - EVM Tag**: <https://stackoverflow.com/questions/tagged/evm>
* **Contract Address Generation**: <https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed>
* **Mythril Documentation**: <https://mythril.readthedocs.io/en/latest/>
* **Solidity Documentation - Storage**: <https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html>


# Solidity

The official Solidity documentation is the most comprehensive and up-to-date resource for learning about the Solidity language. It covers everything from basic syntax to advanced topics and provides detailed explanations, examples, and code snippets.

* **Link**: [Solidity Documentation](https://docs.soliditylang.org/en/latest/)

### Learning Resources

#### Online Tutorials and Courses

* **CryptoZombies**: An interactive online course that teaches Solidity through building a zombie game.
  * **Link**: [CryptoZombies](https://cryptozombies.io/)
* **Dapp University**: A YouTube channel with over 10 hours of hands-on tutorials for both beginners and experienced Solidity developers.
  * **Link**: [Dapp University](https://www.youtube.com/user/DappUniversity)
* **Udemy Ethereum Blockchain Developer Bootcamp With Solidity**: An extensive course providing up-to-date blockchain development tools and resources.
  * **Link**: [Udemy Course](https://www.udemy.com/course/ethereum-and-solidity-the-complete-developers-guide/)

#### Community and Discussion

* **Ethereum StackExchange**: A platform for asking and answering questions related to Solidity and Ethereum development.
  * **Link**: [Ethereum StackExchange](https://ethereum.stackexchange.com/)
* **Solidity Language Users Chat**: A chat room for Solidity developers to discuss and share knowledge.
  * **Link**: [Solidity Chat](https://gitter.im/ethereum/solidity)

### Development Environments and Tools

#### Integrated Development Environments (IDEs)

* **Remix**: A browser-based IDE with an integrated compiler and Solidity runtime environment.
  * **Link**: [Remix IDE](https://remix.ethereum.org/)
* **Hardhat**: An Ethereum development environment with local Ethereum network, debugging features, and a plugin ecosystem.
  * **Link**: [Hardhat](https://hardhat.org/)
* **Foundry**: A fast, portable, and modular toolkit for Ethereum application development written in Rust.
  * **Link**: [Foundry](https://github.com/foundry-rs/foundry)

#### Editor Integrations

* **Visual Studio Code (VS Code)**: Solidity extensions for syntax highlighting, compilation, and debugging.
  * **Link**: [Solidity VS Code Extension](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity)
* **IntelliJ IDEA**: Solidity plugin for IntelliJ IDEA and other JetBrains IDEs.
  * **Link**: [IntelliJ IDEA Solidity Plugin](https://plugins.jetbrains.com/plugin/10479-solidity)

#### Linters and Tools

* **Solhint**: A Solidity linter that provides security, style guide, and best practice rules for smart contract validation.
  * **Link**: [Solhint](https://github.com/protofire/solhint)
* **Ethlint**: A linter to identify and fix style and security issues in Solidity.
  * **Link**: [Ethlint](https://github.com/duaraghav8/Ethlint)

### Additional Resources

#### Example Projects

* **Solidity by Example**: A collection of example contracts to help you understand Solidity in practice.
  * **Link**: [Solidity by Example](https://docs.soliditylang.org/en/latest/solidity-by-example.html)

#### Security and Best Practices

* **Security Considerations**: A section in the Solidity documentation that provides guidance on security best practices.
  * **Link**: [Security Considerations](https://docs.soliditylang.org/en/latest/security-considerations.html)

### Community Translations

The Solidity documentation is available in multiple languages, thanks to community volunteers.

* **Chinese (Simplified)**: [Solidity Documentation (Chinese)](https://readthedocs.org/projects/solidity-zh/downloads/pdf/latest/)
* **Spanish**: [Solidity Documentation (Spanish)](https://solidity.readthedocs.io/es/latest/)
* **Russian**: [Solidity Documentation (Russian)](https://solidity.readthedocs.io/ru/latest/)

### Third-Party Tools and Parsers

* **Solidity Parser for JavaScript**: A Solidity parser built on top of ANTLR4 grammar for JavaScript.
  * **Link**: [Solidity Parser for JavaScript](https://github.com/federicobond/solidity-parser-antlr)
* **Sūrya**: A utility tool for smart contract systems, offering visual outputs and information about contract structure.
  * **Link**: [Sūrya](https://github.com/ConsenSys/surya)


