Only this pageAll pages
Powered by GitBook
1 of 80

Pharos Docs

Introduction

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Node & Validator Guide

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Tooling & Infrastructure

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

API & SDK

Loading...

Loading...

Loading...

Developer Guide

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Resources

Loading...

Loading...

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

All the options mentioned can be accessed at .

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.

A card/bank on-ramp. Use the Alchemy Pay button on the . 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.

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.

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 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)

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

  • 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 , the , and in the main docs.

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.
  • Use + Add wallet if you want to receive to a different address than the one currently connected.

  • Confirm the destination address is your own before finalizing anything.

  • 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.
  • 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.

  • Crypto already, on an exchange

    Withdraw directly, network = Pharos

    Fiat (card/bank) only

    Halliday or Alchemy Pay

    No wallet yet

    TopNod

    Option 1 — Halliday

    Option 2 — Alchemy Pay

    Option 3 — TopNod

    Verify you received it

    Security

    port.pharos.xyz/get-pros
    Port page
    pharosscan.xyz
    Pacific Mainnet
    Token Registry
    Wallets
    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
      }'

    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.

    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

    WSS Public Endpoint

    For the current node binary and Docker image versions, see .

    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)

    • Interoperability & Cross-Chain Design How Pharos enables seamless interaction across ecosystems.

    • Research & Design Papers Detailed technical explorations and protocol-level innovations.

    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

    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.

    Access the full Encyclopedia here:

    Rollback Blocks

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

    Docker:

    Binary:

    Revert to latest stable block:

    Revert to a specific block number:

    You will see an interactive confirmation prompt:

    Enter y to confirm.

    Docker:

    Binary:

    Validator Requirements

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

    Component
    Specification

    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.


    • Snapshots currently contain the full block history. In the future, we will support pruned snapshots that include only the most recent blocks and state.

    Native restaking and shared security

  • High-performance storage systems

  • Evaluate Pharos for research or system design purposes

    When Should You Use the Encyclopedia?

    For Builders

    Explore the Pharos Encyclopedia

    Explorer

    https://atlantic.pharosscan.xyz/

    ChainID

    688689

    Environment

    Atlantic Testnet

    Ratelimit

    500 times/5m

    Max Pending TXs (Addr)

    64

    Current Node Versions
    https://atlantic.dplabs-internal.com
    wss://atlantic.dplabs-internal.com

    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.

  • Pharos Network

    💡 Notes & Best Practices

    Atlantic

    Storage

    5 TB SSD with at least 350MiB/s bandwidth and 30000 IOPS

    Network Bandwidth

    0.5 Gbps

    Open Files Limit

    ulimit -n ≥ 10000000

    CPU

    32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum

    Memory

    256 GB

    Step 1: Stop the Node

    Step 2: Backup local_storage

    Step 3: Revert Blocks

    Step 4: Start the Node

    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


    2️⃣ Start a Fresh Node Container


    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:


    4️⃣ Stop the Node and Restore Node Identity

    Replace new config with the backup node info:


    5️⃣ Restart and Finalize

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


    6️⃣ Clean Up Backup

    cd /data/$WORKSPACE
    docker compose down
    cd /data/$WORKSPACE
    ./ops stop
    mv /data/$WORKSPACE/data/local_storage /data/$WORKSPACE/data/local_storage.bak
    cd /data/$WORKSPACE/bin
    ./pharos_cli revert -c ../pharos.conf -t stable
    cd /data/$WORKSPACE/bin
    ./pharos_cli revert -c ../pharos.conf -t stable -n 1234567
    You are trying to revert stable block to 1234567, are you sure? [y/n]
    cd /data/$WORKSPACE
    docker compose up -d
    ./ops start --config ./pharos.conf
    docker stop pharos-testnet
    mv /data/testnet /data/testnet_bak
    docker rm pharos-testnet
    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
    curl 127.0.0.1:18100/ \\
      -X POST \\
      -H "Content-Type: application/json" \\
      --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
    docker stop pharos-testnet
    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/
    docker start pharos-testnet
    curl 127.0.0.1:18100/ \\
      -X POST \\
      -H "Content-Type: application/json" \\
      --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
    rm -rf /data/testnet_bak

    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.

    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 , , and , proposing their own database and merklization solutions.

    • 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:

      Blockchain
      CPU (cores)
      Memory (GB)
      Network (Gbps)
      Storage (TB SSD)
    • Challenge 3: State Bloat Problem Hinders Blockchain Development

      In , 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.

    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.

    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.

    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

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

    Canonical Contracts

    Atlantic Testnet Canonical Contracts

    Contract Name
    Description
    Address

    Create2Deployer

    Helper for CREATE2 opcode usage

    0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2

    Contract Name
    Description
    Address

    Using Docker

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

    📌 For the latest Docker image tag, see .

    Component
    Specification

    Alchemy RPC

    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.

    To use Alchemy with Pharos, you’ll need to 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

    Cross Chain Infrastructure

    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.

    • Transfer assets between networks

    • Send messages across chains

    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

    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

    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 before continuing.

    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

    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

    Create2Deployer

    Helper for CREATE2 opcode usage

    0x13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2

    Foundry Deterministic Deploy

    Pacific Mainnet Canonical Contracts

    Integrated with Foundry for deterministic deployments

    Build omnichain applications
  • Access liquidity and users from other ecosystems

  • Coordinate logic across multiple chains

  • 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.

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

    Refer to the pages below for setup and usage:

    • Chainlink CCIP

    • Circle CCTP

    • LayerZero

    Overview

    What You Can Do with Cross-Chain Infrastructure

    Why It Matters

    Available Solutions

    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+

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

    For Atlantic Testnet:

    For Mainnet:

    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:

    Note: Pruning is disabled by default. To enable pruning, see Enable Pruning in Pharos Node.

    For TraceDB Node:

    Create the Docker Compose configuration file:

    Note: The image uses a specific version tag (pharos_community_v0.16.0_3cf35988_0914).

    Edit docker-compose.yml and set your password:

    Change this line:

    To your actual password:

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

    You should see:

    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.

    Start the Pharos node with Docker Compose:

    View logs:

    Check if the node is running:

    You should see:

    Check node health:

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

    After deployment, your directory structure will look like:

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

    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

    • Node Monitoring Setup - Configure Prometheus metrics for your node

    CPU

    32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum

    Prerequisites

    Current Node Versions

    1. Create working directory

    2. Download configuration files

    3. Create docker-compose.yml

    4. Set password in docker-compose.yml

    5. Verify required files

    6. Start the node

    7. Check node status

    Managing the node

    Stop the node

    Restart the node

    Stop and remove the container

    View real-time logs

    Execute commands in the container

    Directory Structure

    Upgrading the node

    Security Recommendations

    Next Steps

    Select the Pharos network

  • 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.

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

    # Begin with npm by following the npm documentation
    # https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
    # For yarn, refer to yarn's installation guide
    # https://classic.yarnpkg.com/lang/en/docs/install

    Run the following commands to create and initialize your project:

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

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

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

    Replace your-api-key with your actual Alchemy API key from the Alchemy Dashboard

    Run your script to make a request to the network:

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

    You've made your first request to the Pharos using Alchemy API. You can now explore the various JSON-RPC methods available on Pharos and start building your dApps.

    Overview

    Getting Started with Alchemy

    create an Alchemy account

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

    Step 2. Set up your project

    Step 3. Make your first request

    Step 4. Run your script

    Next Steps

    export WORKSPACE=pharos
    mkdir -p /data/$WORKSPACE && cd /data/$WORKSPACE
    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
    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
    wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/full.conf
    wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/traceDB.conf
    cat > docker-compose.yml <<EOF
    services:
      pharos:
        image: public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.16.0_3cf35988_0914
        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
    vim docker-compose.yml
    - CONSENSUS_KEY_PWD=YOUR_PASSWORD_HERE
    - CONSENSUS_KEY_PWD=your_secure_password
    tree .
    .
    ├── bin
    │   └── VERSION
    ├── docker-compose.yml
    ├── genesis.conf
    └── pharos.conf
    docker compose up -d
    docker compose logs -f pharos
    docker compose ps
    NAME          IMAGE                                                                         STATUS         PORTS
    pharos-node   public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.16.0_3cf35988_0914         Up 2 minutes   0.0.0.0:18100->18100/tcp, ...
    curl http://localhost:18100
    docker compose stop
    docker compose restart
    docker compose down
    docker compose logs -f
    docker compose exec pharos bash
    /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
    # Pull the new image
    docker compose pull
    
    # Restart with new image
    docker compose up -d
    $ mkdir pharos-api-quickstart
    $ cd pharos-api-quickstart
    $ npm init --yes
    $ mkdir pharos-api-quickstart
    $ cd pharos-api-quickstart
    $ yarn init --yes
    $ npm install axios
    $ yarn add axios
    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: []
    $ node index.js
    $ Latest Block: 0x...
    TPS
    Validator

    Ethereum

    4

    16

    0.025

    2
    30

    1.5 Million

    Solana

    12

    256

    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.

  • 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 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.

  • Shortcomings of Existing Solutions

    The Right Path to Improving Blockchain Scalability

    Avalanche
    Monad
    Sei Network
    Why We Need a Blockchain-Native Store

    Atlantic Testnet

    17

    facd080f

    public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.17.0_facd080f_1015

    pharos-v0.17.0-facd080f.tar.gz

    Pacific Mainnet

    16

    3cf35988

    public.ecr.aws/k2g7b7g1/pharos:pharos_community_v0.16.0_3cf35988_0914

    pharos-v0.16.0-3cf35988.tar.gz

    Deployment Methods

    🐳 Using Docker

    🔧 Using Source Code

    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

    Feature

    Details

    Opcode pricing

    Fully aligned with Ethereum EVM opcode

    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.

    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.

    • 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:

    • 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:

    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.

    About Pharos

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


    Pharos Network Overview

    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 , 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.


    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.


    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.


    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.

    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.

    • Snapshots do not contain full block history, only the latest validated state.

    • Archival nodes should still sync from genesis.

    Rapid Node Initialization(Testnet)

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

    We periodically publish . Use the following command to download and extract:

    Only use snapshots that match your target network.

    1~10

    2
    65000

    ~1,500

    Aptos

    32

    64

    1

    2
    30000

    ~150

    Monad

    16

    32

    0.1

    2
    10000

    -

    Reth

    (Single Node)

    24

    256

    10

    2
    20000

    1

    Mega ETH

    (Single Sequencer)

    100

    1024~

    4096

    10

    2
    100000

    1

    Why We Need Pipelining
    When sending transactions via Web3 libraries (ethers.js, web3.js), always use estimateGas() and add a buffer.
  • Example (ethers.js):

  • If a transaction fails unexpectedly despite having enough gas, check whether gas refunds are affecting execution.

  • Debug with debug_traceTransaction or EVM logs.

  • EIP-1559 support

    ✅ Compatible (base fee + priority fee)

    Base fee

    Dynamic (recalculated per epoch)

    Transaction fee model

    Charged by gas_limit at inclusion time

    Fee distribution

    Base fee is burned; priority fee is credited to the validator

    Refund logic

    Full EVM-style refund tracking supported, but refund does not affect charge

    Transaction Fee Distribution

    Difference from Ethereum

    Best Practices for Developers

    Conclusion

    gas table

    🧰 Initialization Using Snapshots

    ✅ Step1: Download the Snapshot Dataset

    🧯 Step2: Stop the Container

    🧹 Step3: Remove Local Data

    📦 Step4: Replace with the Latest Snapshot

    🚀 Step5: Start the Container

    new snapshots
    const estimatedGas = await contract.estimateGas.someFunction();
    const gasLimit = estimatedGas.mul(12).div(10); // Adding a 20% buffer
    await contract.someFunction({ gasLimit });
    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
    docker-compose down
    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
    mv public /data/$WORKSPACE/pharos-node/domain/light/data/public
    docker-compose up -d

    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

    Pharos Modular Stack

    Restaking Integration

    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 for detailed instructions.


    Latest Snapshot
    atlantic-snapshot-2026-10-16-04.tar.gz

    Type

    FullHistory

    Last Updated

    Format


    Latest Snapshot
    mainnet-snapshot-2026-10-15-07.tar.gz

    Type

    Archival

    Last Updated

    Format


    💡 Notes & Best Practices

    Atlantic Node Snapshot

    Mainnet Node Snapshot

    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 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.

    Install your preferred contract framework from the below list:

    • ethers.js

    • web3.js

    • web3.py

    Clone the example repo and add dependencies.

    Run the script using Node.js:

    • 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.

    • 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.

    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! 🚀

    Hemera SocialScan

    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.

    Parameter
    Value

    Fordefi

    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.

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

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

    • MPC-based key management

    • Policy-based transaction approvals

    Supra

    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.

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

    Contract
    Address

    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.

    Compressed TAR archive

    Notes

    Only includes the public database

    sha256 checksum

    a7d4f00822ba78f792bc086c66bb714e02051dfe24be62b1e08cfad00d53bf91

    Compressed TAR archive

    Notes

    Only includes the public database

    sha256 checksum

    7b67d3f804904b4dacd6778acb9ee8f4cd383110f02ea9a2e18b370446bdb4dd

    atlantic-snapshot-2026-10-16-04.tar.gz
    mainnet-snapshot-2026-10-15-07.tar.gz

    Institutional custody workflows

  • Secure signing infrastructure

  • Integration with DeFi protocols

  • 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

    • 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

    • Enable Pharos in your Fordefi account

    • Set up policies and access controls

    • Start managing assets and signing transactions securely

    Overview

    Enable Pharos Network

    Key Features

    When to Use Fordefi

    Integration Notes

    Next Steps

    Pull

    0x16f70cAD28dd621b0072B5A8a8c392970E87C3dD

    Contract
    Address

    Storage

    0xaa2f56843Cec7840F0C106F0202313d8d8CB13d6

    Pull

    0x443A0f4Da5d2fdC47de3eeD45Af41d399F0E5702

    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

    • 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

    For detailed integration guides and supported features:

    https://docs.supra.com/oracles


    • 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

    Storage

    Overview

    Network Deployment

    Pacific Mainnet Contracts

    0x58e158c74DF7Ad6396C0dcbadc4878faC9e93d57

    Atlantic Testnet Contracts

    When to Use Supra

    Integration Notes

    Documentation

    Next Steps

    Setup 1: Pick Your Favorite Contract Develop Framework and IDE

    Setup 2: Set Up the Project

    Step 3: Run the Script

    Key Points

    Troubleshooting

    Conclusion

    git clone https://github.com/PharosNetwork/examples
    cd examples/transaction/ethersjs
    npm install
    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);
        });
    node index.js

    Developer APIs (Contract Verification API)

    Explorer Developer API Key

    102dfc05-661d-4efd-a81e-24afb3918c7f

    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

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

    • 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

    • Verify your deployed contracts

    • Use the explorer to debug and monitor transactions

    • Combine with RPC and indexing tools for deeper insights

    Explorer Link

    https://pharos.socialscan.io/

    Overview

    Explorer Access

    What You Can Do

    Contract Verification

    Notes

    Next Steps

    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.

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    Clone the example repo:

    Install OpenZeppelin Contracts:

    Create a New Solidity File:

    • Create a new file for your uniswap contract:

    Write the uniswap Contract:

    • Open contracts/Uniswap.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    Test the Smart Contract

    Set the private key:

    Deploy the Contract:

    • 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.

    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! 🚀

    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:

    kubectl create namespace prometheus
    1. Add the prometheus-community chart repository:

    helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
    1. Deploy Prometheus:

    1. Verify that all pods in the prometheus namespace are in the READY state:

    1. Get the Pushgateway Service IP/Domain:

    You should see the prometheus-pushgateway Service. Save the IP address of the Pushgateway Service.

    For detailed instructions on configuring the metrics push in your pharos.conf, see .

    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

    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)

    For more details on Nirvana Labs infrastructure and capabilities:

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

    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 before continuing.

    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.

    Before you begin, ensure you have the following:

    • Git: Used for code management and obtain examples.

    Wallet

    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.

    • Create and manage accounts

    • Sign and send transactions

    • Interact with smart contracts

    Access

    Documentation

    https://api.socialscan.io/pharos-mainnet/v1/explorer/command_api/contract

    Connect to decentralized applications

  • Manage assets across networks

  • 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

    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

    Pharos supports wallet and custody solutions for different use cases.

    Refer to the pages below for setup and usage:

    • Safe MultiSig

    • Fordefi

    Overview

    What You Can Do with Wallets

    Custody for Applications

    Why It Matters

    Available Solutions

    Setup 1: Install Hardhat

    Setup 2: Set Up the Project

    Step 3: Write the Uniswap Contract

    Step 4: Deploy the Uniswap Contract

    Troubleshooting

    Conclusion

    Hardhat

    Modify the metrics push configuration file

    Node Monitoring Setup

    Node Debugging & Configuration

    Pharos Block Validation

    You can refer to the Pharos Block Validation 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.

    Check if the Node has joined as a Validator

    Query the local NodeID

    Send a transaction to join the Validator

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

    Check NodeID in Pharos system contract

    Log Inspection

    All logs are written to the directory:

    To monitor logs in real-time, use:

    Port Configuration

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

    Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    Install your preferred contract framework from the below list:

    • ethers.js

    • web3.js

    • web3.py

    Clone the example repo and add dependencies.

    Run the script using Node.js:

    • 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.

    • 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.

    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 Transaction

    Prerequisites

    Ethereum documentation

    Setup 1: Pick Your Favorite Contract Develop Framework and IDE

    Setup 2: Set Up the Project

    Step 3: Run the Script

    Key Points

    Troubleshooting

    Conclusion

    git clone https://github.com/PharosNetwork/examples
    cd examples/uniswap/hardhat/contract
    npm install
    touch contracts/Uniswap.sol
    // 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;
        }
    }
    npx hardhat compile
    npx hardhat test
    npx hardhat vars set PRIVATE_KEY
    npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
    helm upgrade -i prometheus prometheus-community/prometheus \
    --namespace prometheus \
    --set alertmanager.persistence.storageClass="gp2" \
    --set server.persistentVolume.storageClass="gp2"
    kubectl get pods -n prometheus
    kubectl -n prometheus-agent-namespace get svc
    curl 127.0.0.1:18100/ \
      -X POST \
      -H "Content-Type: application/json" \
      --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
    cat /data/domain.json  | grep "NODE_ID"
    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
    cd /data/pharos-node/domain/light/log/
    tail -f pharos.log
    18100 — RPC
    18200 — P2P
    19000 — Internal services
    git clone https://github.com/PharosNetwork/examples
    cd examples/transaction/ethersjs
    npm install
    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);
        });
    node index.js

    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.

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    Clone the example repo:

    Set the private key:

    Then

    Connect the Frontend to the Smart Contract:

    • Open src/app.jsx and replace its content with the following code:

    Replace Placeholder Values

    • Replace YOUR_CONTRACT_ADDRESS with the address of your deployed contract.

    Start the React App

    • Navigate to the frontend directory and start the app:

    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.

    • 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.

    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! 🚀

    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.

    Below is an example of a Uniswap V3 transaction deployed on Pharos Devnet, where the actual gas used was 4,618,680:

    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:

    To ensure the transaction executes successfully, we need to set the gas limit slightly higher than the expected gas usage.

    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

    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.

    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

    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.

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

    • ZAN RPC

    • Nirvana RPC

    • Alchemy RPC

    Node Management

    This guide covers and .

    Atlantic testnet
    Pacific mainnet

    Write Your First NFT

    Before you begin, ensure you have the following:

    • Git: Used for code management and obtain examples.

    • Node.js: Install it from .

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    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.

    Before you begin, ensure you have the following:

    • Git: Used for code management and obtain examples.

    • Node.js: Install it from .

    Indexer

    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.

    • Query smart contract data efficiently

    • Track events and historical activity

    • Build dashboards and analytics

    Why It Matters

    Why It Matters

    Available RPC Providers

    Power frontend applications with structured data

  • Monitor protocol and application-level metrics

  • 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.

    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

    Overview

    What You Can Do with Indexers

    Why It Matters

    Available Indexing Providers

    Setup 1: Install Hardhat

    Setup 2: Set Up the Project

    Setup 3: Deploy the Smart Contract

    Setup 4: Build the Frontend

    Step 5: Run the dApp

    Troubleshooting

    Conclusion

    Hardhat

    Foundry

    Clone the example repo:

    Install OpenZeppelin Contracts:

    • Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

    Create a New Solidity File:

    • Create a new file for your NFT contract:

    Write the Token Contract:

    • Open src/Token.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    • Use forge to compile the contract:

    Test the Smart Contract

    • Use forge to test the contract:

    Set the private key:

    Create a Deployment Script:

    • Create a new file for the deployment script:

    Write the Deployment Script:

    • Open script/DeployToken.s.sol and add the following code:

    Deploy the Contract:

    • Use forge to deploy the contract to the Pharos Testnet:

    • For Atlantic Testnet

    • For Testnet

    Note: Replace with your token contract address

    Open script/InteractToken.s.sol and add the following code:

    Execute the script using forge:

    • 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.

    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! 🚀

    Prerequisites

    Setup 1: Install Foundry

    nodejs.org

    Setup 2: Set Up the Project

    Setup 3: Write the NFT Contract

    Step 4: Deploy the Token Contract

    Step 5: Verify the Token Contract

    Step 6: Interact with the NFT Contract

    Troubleshooting

    Conclusion

    Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.
    • Foundry

    Clone the example repo:

    Set the private key:

    Then

    Connect the Frontend to the Smart Contract:

    • Open src/app.jsx and replace its content with the following code:

    Replace Placeholder Values

    • Replace YOUR_CONTRACT_ADDRESS with the address of your deployed contract.

    Start the React App

    • Navigate to the frontend directory and start the app:

    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.

    • 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.

    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! 🚀

    Prerequisites

    nodejs.org

    Setup 1: Install Foundry

    Setup 2: Set Up the Project

    Setup 3: Deploy the Smart Contract

    Setup 4: Build the Frontend

    Step 5: Run the dApp

    Troubleshooting

    Conclusion

    git clone https://github.com/PharosNetwork/examples
    cd examples/dapp/hardhat
    npm install
    npx hardhat vars set PRIVATE_KEY
    cd contract && npm install
    npx hardhat ignition deploy ./ignition/modules/Counter.js --network pharos
    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;
    npm start
    git clone https://github.com/PharosNetwork/examples
    cd examples/nft/foundry/contract
    forge install OpenZeppelin/openzeppelin-contracts --no-git
    touch src/Token.sol
    // 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") {}
    }
    forge build
    forge test
    export PRIVATE_KEY=<your private key>
    touch script/DeployToken.s.sol
    // 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();
        }
    }
    forge script script/DeployToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
    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
    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
    // 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();
        }
    }
    forge script script/InteractToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
    git clone https://github.com/PharosNetwork/examples
    cd examples/dapp/foundry
    npm install
    export PRIVATE_KEY=<your private key>
    cd contract
    forge install OpenZeppelin/openzeppelin-contracts --no-git
    forge script script/Counter.s.sol:CounterScript --rpc-url <PHAROS_RPC_URL> --broadcast
    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;
    npm start

    Ops activation example

    --stake 100000 (override required)

    --stake 1000000 (ops default)

    Complete the Docker Compose setup for your network using the deployment guide linked below. To create and start the container for the first time, run this from the workspace containing docker-compose.yml:

    Use docker-compose up -d if your installation uses the standalone Compose command.

    If the container already exists but is stopped, use the matching docker start command below.

    Follow , which uses container_name: atlantic-testnet.

    Follow Using Docker and select the For Mainnet downloads (mainnet.genesis and mainnet.version). Its Compose example uses container_name: pharos-node.

    If you customized container_name in docker-compose.yml, use your container name in the docker start and docker exec commands.

    On first start, the Docker image automatically generates the node's cryptographic keys in keys/ under the mounted data directory, including domain.pub, stabilizing.pub, and their private-key files. The image also copies ops to the data directory on each start; see the Docker directory structure. No manual ./ops generate-keys step is needed for Docker deployment.

    ⚠️ Protect the private keys in keys/ and keep secure backups.

    With the container running, open a shell for your network:

    Run node and ./ops commands from the mounted data directory (/data inside the container), where ops and keys/ are available. Commands in the container can access the node's local keys and local RPC. Continue with Join validator sets to generate an unsigned registration transaction using ./ops.

    ⚠️ The registration sender becomes the permanent validator owner (the Safe address when using a Safe). Changing the owner is not supported. A hardware wallet or Safe multisig is mandatory.

    Use the latest ops tool and your initialized node's existing public-key files.

    ⚠️ The domain public key must be the real node key from keys/domain.pub, never empty or a placeholder. An empty _publicKey produces poolId = sha256("") and collides with other empty-key registrations.

    The required flags are --domain-label (validator name) and --domain-endpoint (public endpoint). The endpoint must be reachable by other nodes and use TCP with the consensus port (default: 19000): tcp://YOUR_PUBLIC_IP:19000 or tcp://yourdomain:19000. Do not use 127.0.0.1 or localhost as the validator endpoint.

    Replace the validator name and public endpoint below with your own. The examples use the node's existing ./keys/domain.pub and ./keys/stabilizing.pub files; these are also the defaults when the key-path flags are omitted. If your files are elsewhere, supply their actual paths.

    Use the Atlantic RPC. Explicitly set --stake 100000 to meet Atlantic's 100,000 PROS activation threshold and override the ops default of 1,000,000 PROS.

    ./ops add-validator \
      --domain-label "my-validator" \
      --domain-endpoint "tcp://YOUR_PUBLIC_IP:19000" \
      --rpc-endpoint "https://atlantic.dplabs-internal.com" \
      --domain-pubkey "./keys/domain.pub" \
      --stabilizing-pubkey "./keys/stabilizing.pub" \
      --stake 100000 \
      --unsigned

    Use the Pacific mainnet RPC. --stake 1000000 meets mainnet's 1,000,000 PROS activation threshold and matches the ops default.

    ./ops add-validator \
      --domain-label "my-validator" \
      --domain-endpoint "tcp://YOUR_PUBLIC_IP:19000" \
      --rpc-endpoint "https://rpc.pharos.xyz" \
      --domain-pubkey "./keys/domain.pub" \
      --stabilizing-pubkey "./keys/stabilizing.pub" \
      --stake 1000000 \
      --unsigned

    --unsigned prints the staking contract address, value in wei, method, calldata, and function ABI without signing or broadcasting. VALIDATOR_PRIVATE_KEY is not required in this mode.

    • Contract / To: staking contract 0x4100000000000000000000000000000000000000 on both networks.

    • Method: registerValidator, a payable function that registers the node and locks the supplied stake.

    • Calldata / Data: the encoded function call, including the description, public keys, proofs of possession, and endpoint. Copy the complete hex string without line breaks.

    • Value: the stake sent with the transaction as msg.value, expressed in wei. The stake is not a separate function parameter.

    • Function ABI: the function definition used to decode calldata or enter parameters individually in a wallet.

    --stake is in PROS tokens. Ops converts it to wei: 1 PROS = 10^18 wei. Use the amounts for your network's example:

    Input or output
    Amount

    Ops --stake

    100000 PROS

    Safe value / browser wallet amount

    100000 PROS

    Input or output
    Amount

    ⚠️ Safe's value field uses PROS, not wei. If you change --stake, regenerate the unsigned output and match the wallet amount or Cast value to it. Keep enough PROS available for transaction fees in addition to the stake.

    Choose one submission method below. Submit from your intended owner wallet on the same network as your node and RPC.

    Atlantic testnet

    Use an Atlantic testnet Safe UI and a Safe wallet on Atlantic (chain ID 688689). Set Value to 100000 PROS for the Atlantic example. Verify the network against the Atlantic network information.

    Pacific mainnet

    Use Pharos Safe for Pacific mainnet (chain ID 1672). Set Value to 1000000 PROS for the mainnet example. Verify the network against the Pacific network information.

    Build and execute the transaction

    1. Open the intended owner Safe's transaction builder and set To to 0x4100000000000000000000000000000000000000.

    2. Set Value in PROS to match the unsigned registration for your network, using the amounts above.

    3. Enable custom data (recommended) and paste the generated calldata into Data, with no newlines or line breaks.

    4. Review the network, Safe address, destination, value, and calldata. Collect the required Safe signer approvals and execute the transaction.

    Alternatively, disable custom data, load the function ABI printed by ops, and select registerValidator. Enter its six string parameters individually: description, public key, public-key proof of possession, BLS public key, BLS proof of possession, and endpoint, in ABI order. Use the exact values decoded from the generated calldata, and keep the same PROS value.

    To obtain those values with Cast (included in Foundry):

    Replace the data placeholder with the complete generated calldata. Enter each decoded string's contents without the surrounding display quotes.

    Using MetaMask as an example:

    1. Connect your hardware wallet, select the intended owner account, and add or switch to your node's network using the Atlantic testnet or Pacific mainnet network information.

    2. In Settings → Transactions, enable Show Hex Data.

    3. Click Send, choose PROS, and set To to 0x4100000000000000000000000000000000000000.

    4. Set Amount in PROS to match your generated stake (100000 for the Atlantic example or 1000000 for the Pacific mainnet example). Paste the generated calldata into Hex data, with no newlines or line breaks.

    5. Click Continue, review the transaction details, then confirm and sign with your hardware wallet to send it.

    Install Foundry to use cast, then connect and unlock your Ledger. Use your network's example and replace the data placeholder with the complete calldata printed by ops, on one line:

    This uses the Atlantic RPC and the value for --stake 100000:

    cast send 0x4100000000000000000000000000000000000000 \
      --ledger \
      --rpc-url "https://atlantic.dplabs-internal.com" \
      --value 100000000000000000000000 \
      --data "YOUR_CALLDATA_PRINTED_BY_OPS"

    This uses the Pacific mainnet RPC and the value for --stake 1000000:

    cast send 0x4100000000000000000000000000000000000000 \
      --ledger \
      --rpc-url "https://rpc.pharos.xyz" \
      --value 1000000000000000000000000 \
      --data "YOUR_CALLDATA_PRINTED_BY_OPS"

    The unsuffixed Cast --value is in wei. If you generated a different stake, use its printed wei value. Check that the Ledger account is your intended permanent owner before approving the transaction on the device.

    • Registration: minimum 100 PROS; accepted registration stake is 100–50,000,000 PROS.

    • Atlantic testnet staking/activation: total stake (self-stake + delegators) must reach 100,000 PROS.

    • Pacific mainnet activation: total stake (self-stake + delegators) must reach 1,000,000 PROS. Registering with at least this amount activates the validator at the next epoch.

    Registration below the network's activation threshold leaves the validator in the bootstrapping state until sufficient total stake is reached. Registering with 100 PROS alone does not activate a validator on either network.

    After registration, check the node's health with the ops command:

    Then test network connectivity to other validators:

    Minimum stake required for activation

    100,000 PROS

    1,000,000 PROS

    Atlantic testnet
    Pacific mainnet
    docker compose up -d
    docker start atlantic-testnet
    docker start pharos-node
    docker exec -it atlantic-testnet /bin/bash
    docker exec -it pharos-node /bin/bash
    cast decode-calldata \
      "registerValidator(string,string,string,string,string,string)" \
      "YOUR_CALLDATA_PRINTED_BY_OPS"
    ./ops health-check
    ./ops network-test

    Use the same network throughout: node configuration, container, RPC, and owner wallet. Select the matching network in each example below. Ops defaults to 1,000,000 PROS; explicitly set --stake 100000 for the Atlantic activation example.

    Node Initialization

    Atlantic testnet

    Pacific mainnet

    Keys and ops

    Entering the container

    Atlantic testnet

    Pacific mainnet

    Working directory

    Join validator sets

    Prerequisites

    Generate unsigned registration

    Understand the unsigned output

    Stake amount and units

    Submit via Safe

    Submit via hardware wallet and browser extension

    Optional: submit via Cast and Ledger

    Stake requirements

    Check node health

    Test network connectivity

    Example Scenario

    https://pharosscan.xyz/tx/0x205a123820ad75de162ba3cabe06183d34377e239c3a8f6f18636d4c87b3a524
    https://sepolia.etherscan.io/tx/0x55af4ffe714ad075c6f979e7f89670e1498647abedd6ea7112e1c6339cef64aa
    https://pharosscan.xyz/tx/0x6ccf7f593f5783414b3726e147319e74046526d800b325110bdf81ac3012aa5e

    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.

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    Clone the example repo:

    Install OpenZeppelin Contracts:

    • Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

    Create a New Solidity File:

    • Create a new file for your uniswap contract:

    Write the uniswap Contract:

    • Open src/Uniswap.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    • Use forge to compile the contract:

    Test the Smart Contract

    • Use forge to test the contract:

    Create a Deployment Script:

    • Create a new file for the deployment script:

    Write the Deployment Script:

    • Open script/DeployUniswap.s.sol and add the following code:

    Set the private key:

    Deploy the Contract:

    • Use forge to deploy the contract to the Pharos Testnet:

    Open script/InteractUniswap.s.sol and add the following code:

    Execute the script using forge:

    • 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.

    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! 🚀

    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

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

    Layer
    What It Does
    Resource
    URL

    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.

    • 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:

    Add the wasm32-unknown-unknown build target to your Rust compiler:

    You should now have it available as a Cargo subcommand:

    Note: Pharos built the chain excution client from scratch using C++, and we design, developed and open-sourced the 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.

    Clone the example repo:

    • Create a new file for your interoperability contract:

    Write the interoperability Contract:

    • Open src/lib.rs in your favorite text editor and add the following code:

    Compile the Smart Contract:

    • Use cargo to compile the contract:

    • 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.

    Setup 1: Install Foundry

    Setup 2: Set Up the Project

    Step 3: Write the Uniswap Contract

    Step 4: Deploy the Uniswap Contract

    Step 5: Interact with the Uniswap Contract

    Troubleshooting

    Conclusion

    Foundry

    Setup 2: Set Up the Project

    Setup 3: Write the Interoperability Contract

    Step 4: Deploy the Interoperability Contract

    Troubleshooting

    Dora
    git clone https://github.com/PharosNetwork/examples
    cd examples/uniswap/foundry/contract
    forge install OpenZeppelin/openzeppelin-contracts --no-git
    touch src/Uniswap.sol
    // 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;
        }
    }
    forge build
    forge test
    touch script/DeployUniswap.s.sol
    // 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();
        }
    }
    export PRIVATE_KEY=<your private key>
    forge script script/DeployUniswap.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
    // 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();
        }
    }
    forge script script/InteractUniswap.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
    cargo install --git https://github.com/PharosNetwork/pharos-cargo-stylus
    rustup target add wasm32-unknown-unknown
    cargo stylus --help
    
    Cargo subcommand for developing Pharos Stylus projects
    git clone https://github.com/PharosNetwork/examples
    cd examples/interoperability/call-evm-from-wasm
    touch src/lib.rs
    #![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()))
        }
    }
    cargo stylus check --endpoint=<PHAROS_RPC_URL>
    cargo stylus deploy --private-key=<YOUR_PRIVATE_KEY> --endpoint=<PHAROS_RPC_URL>

    Ops value / Cast --value

    100000000000000000000000 wei

    Ops --stake

    1000000 PROS

    Safe value / browser wallet amount

    1000000 PROS

    Ops value / Cast --value

    1000000000000000000000000 wei

    Routing

    Connects incoming requests to the right Service Agent

    Settlement

    Handles per-call billing automatically via the x402 micropayment protocol

    Wallet & Earnings

    Publishing Guide

    See: Publishing a Service Agent on Anvita Flow

    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

    Agent hosting

    Runs your Service Agent in a managed runtime

    Agent identity

    Generates a unique identity and service profile for each published agent

    Discovery

    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
    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

    Anvita Flow Home

    https://flow.anvita.xyz/home

    Anvita On (User Chat)

    https://flow.anvita.xyz/agent/chat

    Developer Console

    What Anvita Flow Provides

    The Call Flow

    The Developer Flow

    Quick Links

    Lists your Service Agent in the Marketplace so Steward Agents can find it

    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.

    Prerequisites

    Component
    Specification

    CPU

    32 cores, 2.8GHz or faster, AMD Milan EPYC or Intel Xeon Platinum

    Download the latest binary release from GitHub:

    The extracted package contains:

    • bin/pharos_light - Pharos node binary

    • bin/pharos_cli - Pharos CLI tool

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

    For Atlantic Testnet:

    For Mainnet:

    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:

    Note: Pruning is disabled by default. To enable pruning, see .

    For TraceDB Node:

    Download the latest ops tool for Linux:

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

    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

    Generate the domain and stabilizing keys for your node:

    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

    Initialize the node with genesis state:

    This command will:

    • Create the storage database

    • Initialize the genesis block

    • Prepare the node for first startup

    Start the Pharos node service:

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

    Check the logs to verify the node is running:

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

    Create a systemd service file:

    Enable and start the service:

    Check service status:

    View logs:

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

    After deployment, your directory structure should look like:

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

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

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

    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

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

    To voluntarily exit your validator from the network:

    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

    • - Configure Prometheus metrics for your node

    • To register as a validator, see Step 9 above

    Write Your First NFT

    Before you begin, ensure you have the following:

    • Git: Used for code management and obtain examples.

    • Node.js: Install it from .

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    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.

    Before you begin, ensure you have the following:

    • Git: Used for code management and obtain examples.

    • Node.js: Install it from .

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    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.

    The following cross-chain lanes are currently available:

    • Pharos ↔ Ethereum

    • Pharos ↔ Jovay

    Oracle

    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.

    • Access price feeds and market data

    • Build applications that rely on real-world inputs

    • Trigger contract logic based on external conditions

    Pacific Mainnet

    Parameter
    Value
    https://flow.anvita.xyz/service-agents
    https://flow.anvita.xyz/dashboard
    Pharos ↔ Polygon
  • Pharos ↔ Base

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

    Parameter
    Value

    Chain Selector

    7801139999541420232

    Router Contract

    0x4e52dD94e9BCfeFE3C78153bDfB0AB1d30687297

    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

    • 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

    For detailed integration guides and supported configurations:

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

    • 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

    Supported Lanes

    Network Configuration

    Core Parameters

    When to Use CCIP

    Integration Notes

    Documentation

    Next Steps

  • Power lending, trading, and RWA protocols

  • Integrate off-chain data into on-chain workflows

  • 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.

    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

    Overview

    What You Can Do with Oracles

    Why It Matters

    Available Oracle Providers

    stabilizing.pub - BLS12-381 public key

    --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)

  • , and
    pharos.conf
  • Firewall configuration: Ensure required ports are open (18100, 19000, 20000)

  • Regular updates: Keep your node software up to date with latest releases

  • 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

    2. Download configuration files

    3. Download and setup ops tool

    4. Set password for key encryption

    5. Generate cryptographic keys

    6. Bootstrap the node

    7. Start the node

    8. (Optional) Setup systemd service

    Directory Structure

    9. (Optional) Register as Validator

    Set up your validator private key

    Register the validator

    Exit the validator (if needed)

    Security Recommendations

    Next Steps

    Enable Pruning in Pharos Node
    Node Monitoring Setup
    • Foundry

    Clone the example repo:

    Install OpenZeppelin Contracts:

    • Foundry uses forge to manage dependencies. Install OpenZeppelin contracts:

    Create a New Solidity File:

    • Create a new file for your token contract:

    Write the Token Contract:

    • Open src/Token.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    • Use forge to compile the contract:

    Test the Smart Contract

    • Use forge to test the contract:

    Create a Deployment Script:

    • Create a new file for the deployment script:

    Write the Deployment Script:

    • Open script/DeployToken.s.sol and add the following code:

    Set the private key:

    Deploy the Contract:

    • Use forge to deploy the contract to the Pharos Testnet:

    • For Atlantic Testnet

    • For Testnet

    Note: Replace with your token contract address

    Open script/InteractToken.s.sol and add the following code:

    Execute the script using forge:

    • 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.

    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! 🚀

    Prerequisites

    nodejs.org

    Setup 1: Install Foundry

    Setup 2: Set Up the Project

    Setup 3: Write the Token Contract

    Step 4: Deploy the Token Contract

    Step 5: Verify the Token Contract

    Step 6: Interact with the Token Contract

    Troubleshooting

    Next Steps

    export WORKSPACE=pharos
    mkdir -p /data/$WORKSPACE && cd /data/$WORKSPACE
    wget https://github.com/PharosNetwork/resources/releases/download/v0.16.0/pharos-v0.16.0-3cf35988.tar.gz
    tar -zxvf pharos-v0.16.0-3cf35988.tar.gz
    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
    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
    wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/full.conf
    wget -O pharos.conf https://raw.githubusercontent.com/PharosNetwork/resources/refs/heads/main/conf/traceDB.conf
    wget https://github.com/PharosNetwork/ops/releases/latest/download/ops-linux-amd64 -O ops
    chmod +x ops
    ./ops set-password YOUR_SECURE_PASSWORD
    ./ops generate-keys
    ./ops bootstrap --config ./pharos.conf
    ./ops start --config ./pharos.conf
    tail -f log/aldaba.log
    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
    sudo systemctl daemon-reload
    sudo systemctl enable pharos
    sudo systemctl start pharos
    sudo systemctl status pharos
    sudo journalctl -u pharos -f
    /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
    export VALIDATOR_PRIVATE_KEY=your_private_key_hex
    ./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
    export VALIDATOR_PRIVATE_KEY=your_private_key_hex
    
    ./ops exit-validator \
      --rpc-endpoint http://127.0.0.1:18100 \
      --domain-pubkey ./keys/domain.pub
    git clone https://github.com/PharosNetwork/examples
    cd examples/token/foundry/contract
    forge install OpenZeppelin/openzeppelin-contracts --no-git
    touch src/Token.sol
    // 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);
        }
    }
    forge build
    forge test
    touch script/DeployToken.s.sol
    // 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();
        }
    }
    export PRIVATE_KEY=<your private key>
    forge script script/DeployToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast
    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
    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
    // 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();
        }
    }
    forge script script/InteractToken.s.sol --rpc-url <PHAROS_RPC_URL> --broadcast

    Hardhat

    Clone the example repo:

    Install OpenZeppelin Contracts:

    Create a New Solidity File:

    • Create a new file for your NFT contract:

    Write the Token Contract:

    • Open contracts/Token.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    Test the Smart Contract

    Set the private key:

    Deploy the Contract:

    Add the following content to your hardhat.config.js file

    • For Atlantic Testnet

    Field
    Value

    chain_name

    pharos

    chain_id

    688689

    rpc_endpoint

    • For Testnet

    Field
    Value

    chain_name

    pharos

    chain_id

    688688

    rpc_endpoint

    Run the hardhat verify command

    • 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.

    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! 🚀

    Prerequisites

    Setup 1: Install Hardhat

    nodejs.org
    git clone https://github.com/PharosNetwork/examples
    cd examples/nft/hardhat/contract
    npm install
    touch contracts/Token.sol
    // 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;
        }
    }
    npx hardhat compile
    npx hardhat test
    npx hardhat vars set PRIVATE_KEY
    npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
    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
        },
      }
    };
    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
        },
      }
    };
    npx hardhat verify --network pharos <contract_address> <constructor_args_params>

    Setup 2: Set Up the Project

    Setup 3: Write the NFT Contract

    Step 4: Deploy the Token Contract

    Step 5: Verify the Token Contract

    Troubleshooting

    Conclusion

    PROS

    RPC URL

    Block Explorer

    For the current node binary and Docker image versions, see Current Node Versions.

    Network

    Pharos Pacific Mainnet

    ChainID

    1672

    Network Information

    Coin Symbol/Ticker

    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().

    Common Settings

    Parameter
    Value

    Decimals

    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.

    Pair
    Feed ID
    Contract Address
    Pair
    Feed ID
    Contract Address

    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.

    • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

    Clone the example repo:

    Install OpenZeppelin Contracts:

    Create a New Solidity File:

    • Create a new file for your token contract:

    Write the Token Contract:

    • Open contracts/Token.sol in your favorite text editor and add the following code:

    Compile the Smart Contract:

    Test the Smart Contract

    Set the private key:

    Deploy the Contract:

    Add the following content to your hardhat.config.js file

    • For Atlantic Testnet

    Field
    Value
    • For Testnet

    Field
    Value

    Run the hardhat verify command

    • 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.

    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! 🚀

    Pharos MCP

    The Pharos MCP server brings Pharos chain data and onchain operations into any client that speaks the — 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.

    x402

    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.

    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

    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:

    • CryptoZombies: An interactive online course that teaches Solidity through building a zombie game.

    Token Registry

    Symbol
    Name
    initsupply
    Decimals
    Address

    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/

    https://testnet.dplabs-internal.com

    api_host

    https://api.socialscan.io/pharos-testnet/v1/explorer/command_api/contract

    explorer_url

    https://testnet.pharosscan.xyz/

    https://rpc.pharos.xyz
    https://www.pharosscan.xyz
    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.

    • 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 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.

    • 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).

    • 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.

    x402 enables micro-payment and pay-per-use scenarios that were previously impossible due to economic unfeasibility. Below are some possible application scenarios:

    • 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.

    • 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.

    • 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.

    • 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.

    • 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.

    Overview

    Working

    Initial Request and Payment Requirements

    x402 Flowchart
    x402 Flowchart

    Payment Credential Construction and Signing

    Verification

    On-chain Settlement and Confirmation

    Delivery and Receipt

    Use Cases

    Agent Commerce

    Medical and Health Tech

    Education and Knowledge Services

    E-commerce and Consumer Services

    Other Scenarios

    0x371Ed36aEe5bFFbED1A2aa9b7deD7F4EB30F5466

    BTC/USD

    0x00039d9e45394f473ab1f050a1b963e6b05351e52d71e507509ada0c95ed75b8

    0x8b5DB228Fbccf588f08B073C235254aCb6791D36

    ETH/USD

    0x000362205e10b3a147d02792eccee483dca6c7b44ecce7012cb8c6e0b68b3ae9

    0x1Ed3e8CdC173363593BfAD9bF77aa2A0e7351DB8

    WETH/USD

    Same as ETH/USD

    Same as ETH/USD

    WBTC/USD

    0x00034a95e798013fd8bc6ac56707488e1c4622e5dffa9f3c2fe99f8105b4fa5b

    0x518B75ab24251Db9c34c7173f62FBa6E773598CC

    USDT/USD

    0x0003a910a43485e0685ff5d6d366541f5c21150f0634c5b14254392d1a1c06db

    0x525D92D6Caa35060B4D5e3deDa6F52BdA3804618

    USDC/USD

    0x00038f83323b6b08116d1614cf33a9bd71ab5e0abf0c9f1b783a74a43e7bd992

    0x3f882C4F2836b1e5C5278ff2B9FccE2502A35C5d

    LINK/USD

    0x00036d7a1251e3f67d6658466b5e9e7fe8418af7feac9567ff322bff95cc2401

    0x0644220E991a78Bf900648ce59211695cA47c480

    BNB/USD

    0x000335fd3f3ffa06cfd9297b97367f77145d7a5f132e84c736cc471dd98621fe

    0x481Ecd48683ed2dD249d888Ab71B29D7b951B999

    SOL/USD

    0x0003b778d3f6b2ac4991302b89cb313f99a42467d6c9c5f96f57c29c0d2bc24f

    0xdABF88CF4d4d2963631C7d69c394A83af2930983

    XRP/USD

    0x0003c16c6aed42294f5cb4741f6e59ba2d728f0eae2eb9e6d3f555808c59fc45

    0x22AeEADe5afFbd25F9F2b40f186aCf003E478FB0

    WSRUSD/RUSD

    0x0007c129e624689953a2636c014531b2f0bd0c15f9a22211f53023354f309fa6

    0xb5779919e2F183e59A34140331F7779cbFF1d1BC

    SYZUSD/YZUSD

    0x0007690a1667f3978924aa34b16a46ab12fcaae1b012c2f348905cb8299f58b7

    0x36Aff5A272c80B3222c0C9138f2D43c7FAE2F372

    STRUSD/TRUSD

    0x00074de84889ebc1ae6505e72281365430ba0ccf73dfaea09c6036bb1199739f

    0xbeD4692E83F7a2b51bDF8357085769dC87cA8E7d

    0x14d427B112aD61B18E4b1ACdB2893987EB483760

    BTC/USD

    0x00037da06d56d083fe599397a4769a042d63aa73dc4ef57709d31e9971a5b439

    0x5bcff8e4f4C02b5a3192A96DF4Eb5BB0Afdc36f0

    ETH/USD

    0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782

    0x72A7c93b90fEbAA9fD53ded20411Fbb09eA72D03

    WETH/USD

    Same as ETH/USD

    Same as ETH/USD

    WBTC/USD

    0x0003986bae710e410e6a6ec824db9ac91f97f6dd47fc5b28d028c14e825c5891

    0xA5b7Fa94b22588f7E6df28f3F242b81e84DBE9Ef

    USDT/USD

    0x00032874077216155926e26c159c1c20a572921371d9de605fe9633e48d136f9

    0xc5e5b55352c5a6b50Bc9F8e192948D9F51523649

    USDC/USD

    0x0003dc85e8b01946bf9dfd8b0db860129181eb6105a8c8981d9f28e00b6f60d9

    0x70dcf7caD4D5C54e9b4f7286Cd5a2794b93B038a

    LINK/USD

    0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265

    0x58DDd6596605f550db5ce1892867ecEA7Db5CDDC

    BNB/USD

    0x000387d7c042a9d5c97c15354b531bd01bf6d3a351e190f2394403cf2f79bde9

    0x0D030427DFFe9644121429bD0CE6260EbeE5CEC6

    SOL/USD

    0x0003d338ea2ac3be9e026033b1aa601673c37bab5e13851c59966f9f820754d6

    0xbb4b916035F28757e1fFC303CC81a1A29536F8F9

    XRP/USD

    0x00035e3ddda6345c3c8ce45639d4449451f1d5828d7a70845e446f04905937cd

    0x731bF4Fbb91A47cf795f87b8E12148Cdd1a3FEBa

    WSRUSD/RUSD

    0x000706ad7092fdc2310335a82828efe1d4c0220e5bf0fc46100da431f3433743

    0x045fE9F80ac65c2702985a889A6a500D9fA20897

    SYZUSD/YZUSD

    0x00070d19efa92f43763e29a8c444be98d19bf9c7487fb6e62ab5be7adb8ba896

    0x3aca4c56afc1BA3CfF9C8f8e9911798ad82F538A

    STRUSD/TRUSD

    0x00074185003a58896a9e79b81bcfb4ab3212924f4dbb75629d795143ab967e06

    0x9A648AC8B1bB657C64A73d9912e816973a998552

    18

    PriceFeed DeviationThreshold

    0.5

    PriceFeed Heartbeat

    3600 seconds

    SelfManagedFeedsCache

    All feeds

    0xd9F00c86ced2De92F6B7152b0492cb28edc4B5a8

    PROS/USD

    SelfManagedFeedsCache

    All feeds

    0x388c44D3B59d7e89582Caa6Fc475f282a0725480

    PROS/USD

    PriceFeed 2.0

    Pacific Mainnet

    Atlantic Testnet

    Contract Interface

    0x0003f73a1abb2eec1ecdde915c659cbb8f0cbb030156b75dd401fbfd5281e246

    0x00037de4af3cb4fd0d82eed58a43ec144809f7fa7b4c6508eb628ad6d4d8b363

    api_host

    https://api.socialscan.io/pharos-atlantic-testnet/v1/explorer/command_api/contract

    explorer_url

    https://atlantic.pharosscan.xyz/

    api_host

    https://api.socialscan.io/pharos-testnet/v1/explorer/command_api/contract

    explorer_url

    https://testnet.pharosscan.xyz/

    chain_name

    pharos

    chain_id

    688689

    rpc_endpoint

    chain_name

    pharos

    chain_id

    688688

    rpc_endpoint

    Setup 1: Install Hardhat

    Setup 2: Set Up the Project

    Setup 3: Write the Token Contract

    Step 4: Deploy the Token Contract

    Step 5: Verify the Token Contract

    Troubleshooting

    Conclusion

    Hardhat

    https://atlantic.dplabs-internal.com"

    https://testnet.dplabs-internal.com

    /// @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);
    git clone https://github.com/PharosNetwork/examples
    cd examples/token/hardhat/contract
    npm install
    touch contracts/Token.sol
    // 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);
        }
    }
    npx hardhat compile
    npx hardhat test
    npx hardhat vars set PRIVATE_KEY
    npx hardhat ignition deploy ./ignition/modules/Token.js --network pharos
    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
        },
      }
    };
    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
        },
      }
    };
    npx hardhat verify --network pharos <contract_address> <constructor_args_params>

    Authentication

    None — public

    Rate limit

    120 requests per minute per IP

    Chain ID

    1672 (0x688)

    Native token

    PROS

    Source

    Add the server to your MCP client configuration:

    No API key is required.

    Some clients cannot connect to remote MCP servers directly. Bridge to the hosted endpoint with mcp-remote:

    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

    Tool
    JSON-RPC method
    Description

    get_balance

    eth_getBalance

    Native PROS balance of an address

    get_transaction_count

    eth_getTransactionCount

    Tool
    JSON-RPC method
    Description

    eth_call

    eth_call

    Read-only contract call

    estimate_gas

    eth_estimateGas

    Tool
    JSON-RPC method
    Description

    send_raw_transaction

    eth_sendRawTransaction

    Broadcast an already-signed transaction

    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.

    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.

    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.

    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.

    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.

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

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

    Variable
    Default
    Description

    PHAROS_RPC_URL

    https://rpc.pharos.xyz

    Upstream JSON-RPC endpoint

    PHAROS_CHAIN_ID

    1672

    • 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.

    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

    Hosted endpoint

    https://mcp.pharos.xyz/mcp

    Transport

    Overview

    Model Context Protocol

    Streamable HTTP

    {
      "mcpServers": {
        "pharos": {
          "type": "http",
          "url": "https://mcp.pharos.xyz/mcp"
        }
      }
    }
    {
      "mcpServers": {
        "pharos": {
          "command": "npx",
          "args": ["-y", "mcp-remote", "https://mcp.pharos.xyz/mcp"]
        }
      }
    }
    curl https://mcp.pharos.xyz/health
    {"status":"ok","chain":"Pharos","chainId":1672,"rpcUrl":"https://rpc.pharos.xyz"}
    Your private key ──sign (locally)──▶ 0x02f86b…──▶ MCP server ──▶ Pharos
    git clone https://github.com/PharosNetwork/pharos-mcp.git
    cd pharos-mcp
    npm install
    npm run build
    npm start
    {
      "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"
          }
        }
      }
    }
    "env": {
      "PHAROS_RPC_URL": "https://your-provider.example/v2/YOUR_API_KEY"
    }
    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

    Quick Start

    Clients that only support stdio

    Verify the connection

    Available Tools

    Chain and block data

    Accounts and transactions

    Contracts

    Broadcasting

    Block Parameters

    Querying Event Logs

    Sending Transactions

    Self-Hosting

    Connecting a local instance over stdio

    Using your own RPC endpoint

    Docker

    Configuration

    Security Notes

    Troubleshooting

    : Executes code in a sandboxed environment.

    References:

    • Ethereum Yellow Paper: The authoritative technical document that defines the EVM and its operational mechanisms.

    • 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

      • Function Parameters from Calldata: Understand how to access function parameters using calldata. Link

      • Testing Macros in EVM.codes: Build and test functions using raw EVM bytecode.

      • General EVM Playground:

    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:

    • 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

      • Ethersplay: Link

      • Porosity:

    • EVM Execution Model: Explore how EVM executes opcodes, manages memory and storage, and handles transactions.

      • Link:

    • 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

    • EVM Puzzles: A collection of interactive puzzles designed to help you practice and understand EVM operations.

      • Link:

    • EVM Learning Manual: A curated list of resources for learning EVM, maintained by the blockchain community.

      • Link: EVM Learning Manual

    • Stack Overflow: A platform where you can find answers to common EVM-related questions and participate in discussions.

      • Link:

    • Contract Address Generation: Learn how contract addresses are generated using sender address and nonce.

      • Link: Contract Address Generation

    • Bytecode Decompilation: Understand the process of converting bytecode into opcodes and reconstructing high-level code.

      • Link:

    • Storage Management: Explore how EVM handles storage variables, including writing to and reading from storage slots.

      • Link:

    • 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:

    • Stack Overflow - EVM Tag:

    • Contract Address Generation:

    • Mythril Documentation:

    • Solidity Documentation - Storage:

    Learning Resources

    Tutorials and Guides

    Academic Research

    Tools for EVM Development

    Community and Discussion

    Practical Examples and Use Cases

    References

    Link: CryptoZombies
  • Dapp University: A YouTube channel with over 10 hours of hands-on tutorials for both beginners and experienced Solidity developers.

    • Link: Dapp University

  • Udemy Ethereum Blockchain Developer Bootcamp With Solidity: An extensive course providing up-to-date blockchain development tools and resources.

    • Link: Udemy Course

    • Ethereum StackExchange: A platform for asking and answering questions related to Solidity and Ethereum development.

      • Link: Ethereum StackExchange

    • Solidity Language Users Chat: A chat room for Solidity developers to discuss and share knowledge.

      • Link:

    • Remix: A browser-based IDE with an integrated compiler and Solidity runtime environment.

      • Link: Remix IDE

    • Hardhat: An Ethereum development environment with local Ethereum network, debugging features, and a plugin ecosystem.

      • Link:

    • Foundry: A fast, portable, and modular toolkit for Ethereum application development written in Rust.

      • Link:

    • Visual Studio Code (VS Code): Solidity extensions for syntax highlighting, compilation, and debugging.

      • Link: Solidity VS Code Extension

    • IntelliJ IDEA: Solidity plugin for IntelliJ IDEA and other JetBrains IDEs.

      • Link:

    • Solhint: A Solidity linter that provides security, style guide, and best practice rules for smart contract validation.

      • Link: Solhint

    • Ethlint: A linter to identify and fix style and security issues in Solidity.

      • Link:

    • Solidity by Example: A collection of example contracts to help you understand Solidity in practice.

      • Link: Solidity by Example

    • Security Considerations: A section in the Solidity documentation that provides guidance on security best practices.

      • Link: Security Considerations

    The Solidity documentation is available in multiple languages, thanks to community volunteers.

    • Chinese (Simplified): Solidity Documentation (Chinese)

    • Spanish: Solidity Documentation (Spanish)

    • Russian: Solidity Documentation (Russian)

    • Solidity Parser for JavaScript: A Solidity parser built on top of ANTLR4 grammar for JavaScript.

      • Link: Solidity Parser for JavaScript

    • Sūrya: A utility tool for smart contract systems, offering visual outputs and information about contract structure.

      • Link:

    Learning Resources

    Online Tutorials and Courses

    Solidity Documentation

    Community and Discussion

    Development Environments and Tools

    Integrated Development Environments (IDEs)

    Editor Integrations

    Linters and Tools

    Additional Resources

    Example Projects

    Security and Best Practices

    Community Translations

    Third-Party Tools and Parsers

    0xcfC8330f4BCAB529c625D12781b1C19466A9Fc8B

    USDT

    Tether USD

    0

    6

    0xE7E84B8B4f39C507499c40B4ac199B050e2882d5

    WBTC

    Wrapped BTC

    0

    18

    0x0c64F03EEa5c30946D5c55B4b532D08ad74638a4

    WETH

    Wrapped ETH

    0

    18

    0x7d211F77525ea39A0592794f793cC1036eEaccD5

    WPHRS

    Wrapped PHRS

    0

    18

    0x838800b758277CC111B2d48Ab01e5E164f8E9471

    Symbol
    Name
    Pharos Address
    Ethereum Address
    Base Address

    WPROS

    Wrapped PROS

    0x52c48d4213107b20bc583832b0d951fb9ca8f0b0

    0xB197E02499e6502733C6bCE2eb39013C39A03147

    USDC

    USD Coin

    0

    Atlantic Testnet Token Registry

    6

    Pacific Mainnet Token Registry

    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.

    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 .

    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 . After the API key is created, you can get the URLs for the Pharos Network.

    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.

    Network
    HTTPS URL

    0x8B7DdE054BE9D180c1Be7FaE0874697374A49832

    USDC (Circle Deployed)

    USDC

    0xc879c018db60520f4355c26ed1a6d572cdac1815

    LINK

    Chainlink Token

    0x51e2A24742Db77604B881d6781Ee16B5b8fcBE29

    0x514910771AF9Ca656af840dff83E8264EcF986CA

    WETH

    Wrapped ETH

    0x1f4b7011Ee3d53969bb67F59428a9ec0477856E9

    Link
    Link
    EVM Cheatsheet
    Link
    Ethereum Yellow Paper
    EVM Puzzles
    Stack Overflow - EVM Tag
    Mythril Documentation
    Solidity Documentation - Storage
    https://learnblockchain.cn/article/4800
    https://stackoverflow.com/questions/tagged/evm
    https://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
    https://mythril.readthedocs.io/en/latest/
    https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html
    Solidity Chat
    Hardhat
    Foundry
    IntelliJ IDEA Solidity Plugin
    Ethlint
    Sūrya

    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

    Address nonce

    get_transaction

    eth_getTransactionByHash

    Transaction by hash

    get_transaction_receipt

    eth_getTransactionReceipt

    Transaction receipt

    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)

    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

    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.

    github.com/PharosNetwork/pharos-mcp
    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}

    How to Access Pharos Testnet RPC from ZAN?

    Step 1: Create a ZAN account

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

    Step 3: Copy your network URL

    For Example:

    Preparing a ZAN account
    can create the API key on the ZAN platform

    Video Reference:

    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

    Note: These configuration keys require v0.16.0 or later. In earlier versions they were named enable_pamir_cetina and pamir_cetina_*. Using the wrong key names for your node version will silently disable metrics pushing without any error, so make sure the names match your node version.

    {
      "chain": {
        "monitor_config": {
          "storage_enable_cetina": true,
          "storage_cetina_job_name": "your_job_name",
          "storage_cetina_push_address": "your_pushgateway_address",
          "storage_cetina_push_interval": 5,
          "storage_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

    Version note: In v0.15.0 and earlier, these keys were named enable_pamir_cetina, pamir_cetina_job_name, pamir_cetina_push_address, pamir_cetina_push_interval, and pamir_cetina_push_port respectively.

    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:

    1. 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

    For Docker:

    For Binary:

    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 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.

    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.

    Restart your node to apply the changes:

    storage_enable_cetina

    Enable/disable metrics pushing

    true

    storage_cetina_job_name

    Job name for Prometheus metrics

    "atlantic_testnet"

    vim pharos.conf
    docker compose restart
    ./ops stop --force
    ./ops start --config ./pharos.conf

    Setup Steps

    Rust: Install it from .

  • Pharos Devnet/Testnet Access: Access to a Pharos node (local or remote) for interacting with the blockchain.

  • Install the pharos wasm toolchain plugin using the Cargo tool:

    Add the wasm32-unknown-unknown build target to your Rust compiler:

    You should now have it available as a Cargo subcommand:

    Note: Pharos built the chain execution client from scratch using C++, and we design, developed and open-sourced the 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.

    • Create a new file for your token contract:

    Write the Token Contract:

    • Open src/lib.rs in your favorite text editor and add the following code:

    Compile the Smart Contract:

    • Use cargo to compile the contract:

    Open scripts/main.py and replace your deployed contract address to the variable CONTRACT_ADDRESS:

    Execute the script using python:

    • 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.

    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! 🚀

    Prerequisites

    Ethereum documentation
    Pharos RPC.mp4

    storage_cetina_push_address

    Pushgateway address (without protocol)

    "your_pushgateway_address"

    storage_cetina_push_interval

    Push interval in seconds

    5

    storage_cetina_push_port

    Pushgateway port

    9091

    Setup 1: Install WASM Toolchains With Cargo

    Setup 3: Write the Token Contract

    Step 4: Deploy the Token Contract

    Step 5: Interact with the Token Contract

    Troubleshooting

    Next Steps

    rust-lang.org
    Dora
    cargo install --git https://github.com/PharosNetwork/pharos-cargo-stylus
    rustup target add wasm32-unknown-unknown
    cargo stylus --help
    
    Cargo subcommand for developing Pharos Stylus projects
    touch src/lib.rs
    // 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(())
        }
    }
    cargo stylus check --endpoint=<PHAROS_RPC_URL>
    cargo stylus deploy --private-key=<YOUR_PRIVATE_KEY> --endpoint=<PHAROS_RPC_URL>
    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()}")
    python3 -m pip install web3 && python3 scripts/main.py

    x402 Atlantic Testnet

    A developer guide on how to get started with x402 on Pharos Atlantic Testnet

    Server-side: Using Express + x402 Middleware

    For Express applications, you can use the middleware pattern:

    
    // 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:688689") {
        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 Atlantic Testnet (Chain ID: 688689)
    resourceServer.register("eip155:688689", 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:688689",
              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:688689",
              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 Atlantic Testnet",
        chainId: 688689,
        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 Atlantic Testnet (Chain ID: 688689)`);
      console.log(`💰 Payment receiver address: ${payToAddress}`);
      console.log(`🪙 USDC contract: ${usdcAddress}`);
    });
    

    Client-side: Using Fetch + x402 SDK

    
    // 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 Atlantic Testnet (Chain ID: 688689)
    client.register("eip155:688689", 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

    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 Atlantic Testnet (ID: 688689)
    const pharos = defineChain({
      id: 688_689,
      name: "Pharos",
      nativeCurrency: { name: "PHRS", symbol: "PHRS", decimals: 18 },
      rpcUrls: { default: { http: ["https://atlantic.dplabs-internal.com/"] } }, // Pharos RPC endpoint
      testnet: true,
    });
    
    // 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:688689", 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:688689 (Pharos Atlantic)`);
    });
    

    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. For Atlantic Testnet, configure chain ID 688689 and the test USDC token address (0xE0BE08c77f415F577A1B3A9aD7a1Df1479564ec8), which is not an official address. Ensure the generated server and client use these Atlantic Testnet settings.

    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.

    During the process, you can ask it to generate simple example code through conversation, including /data and /premium endpoints.

    The service can then be started.

    Next, you can ask it to write a test client.

    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.

    Features
    Pharos
    Other EVM Chains
    • 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.

    • 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.

    • Pharos Official Website: https://www.pharos.xyz/

    • Pharos Documentation: https://docs.pharos.xyz/

    • x402 Specification: https://x402.org/

    x402 Pacific Mainnet

    A developer guide on how to get started with x402 on Pharos Pacific Mainnet

    Server-side: Using Express + x402 Middleware

    For Express applications, you can use the middleware pattern:

    
    // 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

    
    // 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

    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.

    During the process, you can ask it to generate simple example code through conversation, including /data and /premium endpoints.

    The service can then be started.

    Next, you can ask it to write a test client.

    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.

    Features
    Pharos
    Other EVM Chains
    • 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.

    • 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.

    • Pharos Official Website: https://www.pharos.xyz/

    • Pharos Documentation: https://docs.pharos.xyz/

    • x402 Specification: https://x402.org/

    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.


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

    • Fullnode / Validator:

    • Full Cache Node:

    • Tracedb Node:

    The base configuration for fullnode/validator:

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

    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.

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

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

    Both features can be enabled simultaneously for maximum query capability:

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


    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.

    Configuration Item
    Default Value
    Purpose
    Impact on RPC Node Performance
    Configuration Item
    Default Value
    Purpose
    Impact
    Configuration Item
    Default Value
    Purpose
    Impact

    Recommendations for node pool eth JSON-RPC API scheduling:

    Node Type
    Scheduling Rule

    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.

    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

    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.

  • 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.

  • TPS

    30,000

    Usually < 5,000

    Block Time

    < 1 Second

    Advantages of x402 on Pharos

    Why Choose Pharos for x402?

    Development Recommendations

    Reference Resources

    x402-2
    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.

  • 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.

  • TPS

    30,000

    Usually < 5,000

    Block Time

    < 1 Second

    Advantages of x402 on Pharos

    Why Choose Pharos for x402?

    Development Recommendations

    Reference Resources

    x402-2
    ,
    eth_newPendingTransactionFilter
    for monitoring and event retrieval
    eth_getFilterChanges
    . (Pharos currently uses
    eth_subscribe
    instead of filter interfaces.)

    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.

    {
      "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"
        }
      }
    }
    {
      "storage": {
        "mygrid_conf": {
          "config": {
            "mygrid": {
              "expert_flags": {
                "mygrid_client_enable_full_state_kv_db": true
              }
            }
          }
        }
      }
    }
    {
      "chain": {
        "startup_config": {
          "parameters": {
            "/GlobalFlag/enable_persist_trace": "true"
          }
        }
      }
    }
    {
      "chain": {
        "startup_config": {
          "parameters": {
            "/GlobalFlag/enable_persist_trace": "true"
          }
        }
      },
      "storage": {
        "mygrid_conf": {
          "config": {
            "mygrid": {
              "expert_flags": {
                "mygrid_client_enable_full_state_kv_db": true
              }
            }
          }
        }
      }
    }

    cache_query_worker_count

    16

    Handles getBlock*/getTx*/getReceipt* and other cache queries

    High: Insufficient workers cause queuing and increased latency

    max_session

    102400

    Maximum session count

    Needs to be large enough for multi-client scenarios

    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

    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

    Node Configuration (pharos.conf)

    Base Configuration

    Enabling Full Cache Node (Flat State KV DB)

    Enabling Tracedb Node

    Combining Full Cache + Tracedb

    RPC Node Tuning

    Thread Counts and Queue Sizes

    Session Management

    Block Cache

    Node Pool JSON-RPC API Scheduling Strategy

    By Node Type

    By Data Availability

    By Interface Affinity

    full.conf
    full-cache-node.conf
    traceDB.conf

    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

    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

    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.

    Usually 2 - 15 Seconds

    Finality Time

    Sub-second

    Usually several minutes

    Micro-payment Viability

    Extremely High

    Low - High

    x402-3
    x402-4
    x402-5
    x402-6

    Usually 2 - 15 Seconds

    Finality Time

    Sub-second

    Usually several minutes

    Micro-payment Viability

    Extremely High

    Low - High

    x402-3
    x402-4
    x402-5
    x402-6

    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.

    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:

    Like other Merkle tree implementations, Letus's SPV is the reverse process of computing the root hash. Specifically, the process is as follows:

    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.

    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:

    Verifier input: root_hash (committed root), key, returned proofs[] (and optional sibling sub-proofs, see below).

    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.

    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.

    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:

    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:

    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").

    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.

    In summary: "key_hash verification fails" && "the entire proof can be verified using the target key's offset" yields a valid non-existence proof.

    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.

    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).

    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.

    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.

    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.)

    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).

    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".

    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.

    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.

    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.

    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.

    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.

    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".

    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.

  • 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).

  • 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.
    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 )
    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]);
    }
    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)
    # 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
    # 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
    # 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
    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!

    State Tree Root Hash Calculation Method

    SPV Verification Method

    Trie Path

    Proof Chain tree_proofs

    Proof Chain Verification Logic

    SPV Instance Explanation

    Existence Proof

    Non-Existence Proof - Case 1

    Non-Existence Proof - Case 2

    Correctness Discussion

    Prerequisites and Overall Conclusions (Including SkipEmpty)

    Existence Proof Case Discussion

    Non-Existence Proof Case Discussion - Case 1

    Non-Existence Proof Case Discussion - Case 2

    Why "Only Main Chain, No Sibling Information" Is Not Sufficient

    What Role Do sibling_leftmost_leaf_proofs Play

    Why It Is Difficult for an Attacker to Forge Sibling Chains

    SPV Existence Proof
    SPV Non-Existence Proof - Case 1
    SPV Non-Existence Proof - Case 2
    Atlantic
    Atlantic
    Special Processing Networks (SPNs)
    Using Docker (Atlantic Testnet)

    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

    This guide is about publishing a Service Agent.

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

    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".

    Before publishing, make sure you have:

    • A completed Pharos Skill package with SKILL.md at its root

    • An Anvita Flow account — register at

    • 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.

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

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

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

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

    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.

    1. Register an account

    Go to and register. One account covers both user and developer roles.

    2. Open the Developer Console

    Two ways to get there:

    • Go directly to

    - 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.

    Fill in the three sections of the creation form:

    Upload your zipped package. You can only proceed after it passes the parsing check. If it fails, revisit and confirm the folder structure, zip method, and frontmatter.

    Describe how the Service Agent should:

    • Understand incoming requests

    • Decide when to ask follow-up questions

    • Confirm inputs and delivery scope before starting work

    Setting
    What It Controls

    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.

    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

    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.

    Once published, your Service Agent enters the Anvita Flow Marketplace. When a user makes a request to their Steward Agent in Anvita On (), 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.

    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

    Go to 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.

    What You Did
    Where
    Issue
    Cause
    Fix
    Each paid call is settled automatically Enabling your wallet:

    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

    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
    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
    # ✓ 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
    └── ...
    ---
    name: your-skill
    description: One sentence describing what this Skill does and when it should be called.
    ---

    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

    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

    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

    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)

    2. How Anvita Flow Works

    3. Prerequisites

    4. Step 1 — Prepare Your Skill Package

    Rule 1 — Correct Folder Structure

    Rule 2 — Correct Zip Structure

    Rule 3 — Required Frontmatter in SKILL.md

    One Agent Per Skill

    5. Step 2 — Register and Enter the Console

    6. Step 3 — Create the Agent

    Upload Skill Package

    Customer Service Strategy

    Runtime Configuration

    7. Step 4 — Debug

    8. Step 5 — Publish

    How It Gets Called After Publishing

    9. Earnings

    10. Summary

    Common Issues

    https://flow.anvita.xyz/home
    https://flow.anvita.xyz/home
    https://flow.anvita.xyz/service-agents
    Step 1
    https://flow.anvita.xyz/agent/chat
    https://flow.anvita.xyz/dashboard

    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 below

    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)

    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

    Earnings
    https://flow.anvita.xyz/dashboard

    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

    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.

    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

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

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

    Foundry — compiles and deploys Solidity contracts

    Verify installation:

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

    Claude Code — the AI CLI that runs your skill

    Verify:

    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

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

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

    After completing this guide it will look like this:

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

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

    Part
    What It Does

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

    Create a new file at:

    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.

    Paste this entire block into references/vault.md:

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

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

    Change it to:

    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.

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

    Add these 5 rows directly after it:

    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.

    1. Set your private key in the terminal:

    1. Open Claude Code inside the skill folder:

    Run these prompts in order inside Claude Code:


    1. 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....


    1. Deposit

    Deposit 0.1 PHRS into vault at 0xYourVaultAddress

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


    1. 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."


    1. 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.


    1. 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.


    1. 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.

    Error
    Cause
    Fix

    Circle CCTP

    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.

    CCTP smart contract addresses for Pharos. For contract interfaces and method signatures, see .

    Full contract source code is .

    What errors can happen and what to tell the user
    RPC URL:
    https://atlantic.dplabs-internal.com
  • Chain ID: 688689

  • Symbol: PHRS

  • Get testnet PHRS from the Pharos faucet or directly from the team.

  • Export your private key — you will need it when deploying

  • 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.

    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

    --value flag

    Sending native PHRS with a function call

    block.timestamp

    Setting and enforcing a time lock

    Event querying

    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)

    Still locked

    Too early to withdraw

    Run timeLeft() and wait

    Not owner

    Wrong wallet

    How the Skill Engine Works

    What is a Pharos Skill?

    The Flow from User Prompt to On-Chain Action

    Folder Structure Overview

    What is a Reference File?

    Prerequisites

    Tools to Install

    Wallet Setup

    Project Structure

    Step 1 — Write the Smart Contract

    Create the File

    The Contract

    Contract Explained

    Verify the Contract Compiles

    Step 2 — Create the Reference File

    Create the File

    Why This File Exists

    The Complete File Contents

    Step 3 — Edit SKILL.md

    Edit 1 — Add Vault Keywords to the Description

    Edit 2 — Add Rows to the Capability Index Table

    Step 4 — Run and Test the Skill

    Setup (One Time Only)

    Full End-to-End Test Sequence

    Common Errors and Fixes

    Reading on-chain history with cast logs

    Runs once at deploy. Sets unlockTime = now + duration.

    Check $PRIVATE_KEY matches the deployer address

    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.
    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
    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
    curl -L https://foundry.paradigm.xyz | bash
    source ~/.zshenv && foundryup
    cast --version
    forge --version
    npm install -g @anthropic-ai/claude-code
    claude --version
    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
    pharos-skill-engine-0.1.0/
    ├── SKILL.md                          ← edited
    ├── assets/
    │   └── vault/
    │       └── SimpleVault.sol           ← new
    └── references/
        └── vault.md                      ← new
    assets/vault/SimpleVault.sol
    // 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;
        }
    }
    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
    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.
    or generate Web3 scripts targeting Pharos Chain / Pharos Network. Do not attempt...
    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...
    | Generate contract interaction scripts ... | → `references/script-gen.md` |
    | 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` |
    export PRIVATE_KEY=0xYourPrivateKeyHere
    cd /path/to/pharos-skill-engine-0.1.0
    claude

    Address

    Pharos

    31

    Blockchain

    Address

    Pharos

    31

    Blockchain

    Address

    Pharos

    31

    Blockchain

    Address

    Pharos

    31

    Blockchain

    Address

    Pharos Testnet

    31

    Blockchain

    Address

    Pharos Testnet

    31

    Blockchain

    Address

    Pharos Testnet

    31

    Blockchain

    Address

    Pharos Testnet

    31

    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. 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.

    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:

    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 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 function. After sufficient 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 function.

    To send an arbitrary message, directly call MessageTransmitterV2#sendMessage. The message recipient must implement the following methods to handle messages based on their finality threshold:

    • Implement IMessageHandlerV2#handleReceiveFinalizedMessage to receive messages with finalityThresholdExecuted ≥ 2000.

    • Implement IMessageHandlerV2#handleReceiveUnfinalizedMessage to receive messages with finalityThresholdExecuted < 2000.

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

    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

    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 endpoint, using the transaction hash as a query parameter.

    The message format includes a dynamically sized messageBody field, used for application-specific messages. For example, TokenMessengerV2 defines a BurnMessageV2 with data related to crosschain transfers.

    Field
    Offset
    Solidity Type
    Length (bytes)
    Description

    version

    0

    uint32

    4

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

    Environment
    URL

    Testnet

    https://iris-api-sandbox.circle.com

    Mainnet

    https://iris-api.circle.com

    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

    Returns public keys for validating attestations across all supported CCTP versions.

    Retrieve public keys to verify attestation authenticity for crosschain transactions.

    Retrieves messages and attestations for a given transaction or nonce, supporting messages for all CCTP versions.

    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.

    CCTP V2 defines the following finality thresholds:

    Finality Threshold
    Value

    Confirmed

    1000

    Finalized

    2000

    • 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.

    For information about CCTP transfer fees, including fee tables by blockchain, the maxFee parameter, and Standard Transfer fee switch support, see CCTP Fees.

    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.

    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.

    • 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.

    Blockchain

    Overview

    Contract Addresses

    Pacific Mainnet Contracts

    TokenMessengerV2

    Contract Interfaces
    available on GitHub

    MessageTransmitterV2

    TokenMinterV2

    MessageV2

    Atlantic Testnet Contracts

    TokenMessengerV2

    MessageTransmitterV2

    TokenMinterV2

    MessageV2

    Message Passing

    For Pharos

    Message Format

    Message Header

    Nonces

    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 as a reference implementation for converting between address and bytes32 in Solidity.

    Message body

    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.

    API hosts and endpoints

    API service hosts

    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.

    API endpoints

    Deprecated endpoint

    The endpoint /v2/fastBurn/USDC/fees is deprecated. Use instead to retrieve both Fast and Standard Transfer fees.

    Note: This deprecation does not affect (see preceding table), which remains active and valid.

    Finality thresholds

    Defined finality thresholds

    Messages and finality

    Only two finality thresholds are supported. Any minFinalityThreshold value below 1000 is treated as 1000, and any value above 1000 is treated as 2000.

    Fees

    Hooks

    Design overview

    Key benefits

    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 )

    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 )

    finalityThresholdExecuted

    144

    uint32

    4

    Actual finality threshold executed from source chain (see )

    messageBody

    148

    bytes

    dynamic

    App-specific message to be handled by recipient

    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

    Fetch attestation status and transaction details.

    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.

    Retrieves the current USDC Fast Transfer allowance remaining.

    Monitor available allowance for Fast Transfer burns in real-time.

    Returns the fees for USDC transfers between specified source and destination domains.

    Calculate transaction costs before initiating a Fast or Standard Transfer.

    Domain
    0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d
    Domain
    0x81D40F21F12A8F0E3252Bccb954D722d4c464B64
    Domain
    0xfd78EE919681417d192449715b2594ab58f5D002
    Domain
    0xec546b6B005471ECf012e5aF77FBeC07e0FD8f78
    Domain
    0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA
    Domain
    0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275
    Domain
    0xb43db544E2c27092c107639Ad201b3dEfAbcF192
    Domain
    0xbaC0179bB358A8936169a63408C8481D582390C4
    Message.sol library
    GET /v2/publicKeys
    GET /v2/messages
    /v2/burn/USDC/fees
    /v2/fastBurn/USDC/allowance
    CCTP V2 Nonces
    CCTP V2 Finality Thresholds
    CCTP V2 Finality Thresholds
    POST /v2/reattest
    GET /v2/fastBurn/USDC/allowance
    GET /v2/burn/USDC/fees

    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:

    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:

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

    For Docker Deployment:

    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:

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

    For Docker Deployment:

    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.


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

    Enter the container and enable auto-pruning:

    Expected output:

    If enable_auto_prune is 1, auto pruning is enabled.

    For Legacy Deployment (Pre-v0.11)

    Step 1: Verify Node Status

    Step 2: Enable Pruning

    Step 3: Query Pruning Settings (Optional)

    curl 127.0.0.1:18100/ \
      -X POST \
      -H "Content-Type: application/json" \
      --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}'
    docker exec -it pharos-testnet /bin/bash
    cd /data/pharos-node/domain/client/bin/
    ./pharos_cli prune --enable_auto_prune
    docker exec -it pharos-testnet /bin/bash
    cd /data/pharos-node/domain/client/bin/
    ./pharos_cli prune --get
    PruneInfo:
    enable_auto_prune: 1
    auto_prune_reserved_window: 100000
    prune_reserved_version: xxxxxx
    flushed_version: xxxxxx
    real_prune_reserved_version: xxxxxxx

    JSON-RPC API Methods

    Pharos Network is a fully EVM-equivalent Layer 1 blockchain, supporting a JSON-RPC interface for seamless blockchain interactions. This RPC API is fully compatible with existing Ethereum JSON-RPC API 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:

    // 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:

    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

    Example:

    Returns the number of the most recent block.

    • Input: None

    • Output: String - Block number in hexadecimal

    Example:

    Returns the chain ID of the current network.

    • Input: None

    • Output: String - Chain ID as a hexadecimal string

    Example:

    Returns the sync status of the node.

    • Input: None

    • Output: Object|Boolean - Sync status object, or false if not syncing

    Example:

    Returns the balance of an address at a given block.

    • Input:

      • String - Address to query

      • String - Block number or tag (latest, earliest, pending

    Example:

    Returns the number of transactions sent from an address (nonce).

    • Input:

      • String - Address to query

      • String - Block number or tag

    Example:

    Returns the value from a storage position at a given address.

    • Input:

      • String - Address of the contract

      • String - Position in storage

    Example:

    Returns the contract code at a given address.

    • Input:

      • String - Address to query

      • String - Block number or tag

    Example:

    Returns the account and storage values with Merkle proof (EIP-1186). Pharos supports using these proofs.

    • Input:

      • String - Address

      • Array - Array of storage keys

    Example:

    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:

    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

    Example:

    Estimates the gas needed to execute a transaction.

    • Input:

      • Object - Transaction call object

      • String - Block number or tag (optional)

    Example:

    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

    Example:

    Returns information about a block by its hash.

    • Input:

      • String - Block hash

      • Boolean - If true, returns full transaction objects; if false, returns transaction hashes

    Example:

    Returns information about a block by its number.

    • Input:

      • String - Block number or tag

      • Boolean - If true, returns full transaction objects

    Example:

    Returns all transaction receipts for a given block.

    • Input:

      • String - Block number or tag

    • Output: Array - List of transaction receipts

    Example:

    Returns the number of transactions in a block by block hash.

    • Input:

      • String - Block hash

    • Output: String - Transaction count as hexadecimal

    Example:

    Returns the number of transactions in a block by block number.

    • Input:

      • String - Block number or tag

    • Output: String - Transaction count as hexadecimal

    Example:

    Returns information about a transaction by its hash.

    • Input:

      • String - Transaction hash

    • Output: Object - Transaction details

    Example:

    Returns a transaction by block hash and index position.

    • Input:

      • String - Block hash

      • String - Transaction index (hexadecimal)

    Example:

    Returns a transaction by block number and index position.

    • Input:

      • String - Block number or tag

      • String - Transaction index (hexadecimal)

    Example:

    Returns the receipt of a transaction by transaction hash.

    • Input:

      • String - Transaction hash

    • Output: Object - Transaction receipt

    Example:

    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:

    Creates a subscription for real-time events (WebSocket only).

    • Input:

      • String - Subscription type: newHeads or logs

      • Object - Filter parameters (for logs

    Note: Only available over WebSocket connections. HTTP calls will fail.

    Example:

    Cancels an existing subscription (WebSocket only).

    • Input:

      • String - Subscription ID

    • Output: Boolean - true if successfully unsubscribed

    Example:


    Returns the current network ID.

    • Input: None

    • Output: String - Network ID

    Example:

    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.

    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.


    Returns the current client version.

    • Input: None

    • Output: String - Client version string

    Example:

    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.


    Returns the execution trace of a transaction.

    • Input:

      • String - Transaction hash

      • Object - Tracer options (optional). Currently only "callTracer" is supported.

    Example:

    Executes a call and returns the execution trace without creating a transaction.

    • Input:

      • Object - Transaction call object

      • String - Block number or tag

    Example:

    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.

    Example:

    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.

    Example:

    Returns the RLP-encoded block data.

    • Input:

      • String - Block number, block hash, or tag

    • Output: String - RLP-encoded block data

    Example:

    Returns the RLP-encoded block header.

    • Input:

      • String - Block number, block hash, or tag

    • Output: String - RLP-encoded header data

    Example:

    Returns the raw receipts for a block.

    • Input:

      • String - Block number, block hash, or tag

    • Output: Array - Raw receipt data

    Example:

    Returns the raw transaction data by hash.

    • Input:

      • String - Transaction hash

    • Output: String - Raw transaction data

    Example:

    Returns detailed protocol and version information.

    • Input: None

    • Output: Object - Version information including:

      • binaryVersion - Node binary version

    Note: This method is not available on the public RPC endpoint.


    Returns traces matching a given filter.

    • Input:

      • Object - Filter parameters:

        • fromBlock - Start block (hexadecimal)

    Note: Block range is limited to 500 blocks.

    Example:


    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.


    Returns account information at a given address (EIP-7587).

    • Input:

      • String - Address to query

      • String - Block number or tag

    Example:

    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.

    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.


    These are Pharos-specific debug methods that extend the standard debug namespace.

    Returns validator information for the consensus layer.

    • Input:

      • String - Block number or tag (optional)

    • Output: Object - Validator details

    Example:

    Returns the block proof data for BLS signature verification. See the for usage examples.

    • Input:

      • String - Block number or tag

    • Output: Object - Block proof including BLS signatures

    Example:

    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.

    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.

    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.


    These methods exist for Ethereum compatibility but return placeholder values since Pharos uses PoS consensus.

    Method
    Returns
    Note

    The following methods are declared but not implemented. Calling them returns METHOD_NOT_FOUND:

    • eth_batchGetBlockByNumber

    • eth_batchGetTransactionByHash

    • eth_batchGetTransactionReceipt


    • WebSocket: eth_subscribe and eth_unsubscribe require a WebSocket connection. Supported subscription types: newHeads, logs.

    • Block Tags: Supported values: latest, earliest, pending

    Array - Reward percentiles to sample from each block
  • Output: Object - Fee history data including baseFeePerGas, gasUsedRatio, and reward

  • ,
    safe
    ,
    finalized
    )
  • Output: String - Balance in Wei as hexadecimal

  • Output: String - Transaction count as hexadecimal
    String - Block number or tag
  • Output: String - Storage value as hexadecimal

  • Output: String - Contract bytecode as hexadecimal
    String - Block number or tag
  • Output: Object - Account proof including balance, codeHash, nonce, storageHash, and proofs

  • Output: String - Return data as hexadecimal
    Output: String - Estimated gas as hexadecimal
    Output: Object - Access list and estimated gas

    Output: Object - Block information

    Output: Object - Block information

    Output: Object - Transaction details
    Output: Object - Transaction details
    type only)
  • Output: String - Subscription ID

  • Output: Object - Execution trace

    Object - Tracer options (optional). Currently only "callTracer" is supported.
  • Output: Object - Execution trace

  • Output: Array - Array of execution traces

    Output: Array - Array of execution traces

    currentSpecVersion - Current spec version
  • currentStableBlock - Current stable block number

  • higherSpecVersions - Available higher spec versions

  • protocolVersion - Protocol version string

  • 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

  • Output: Object - Account details including balance, nonce, codeHash, and storageRoot
    eth_batchGetBlockReceipts
    ,
    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.

  • // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_maxPriorityFeePerGas",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": "0x0"
    }
    // 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"
          ],
          "..."
        ]
      }
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_blockNumber",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "result": "0x54aceb",
      "id": 1,
      "jsonrpc": "2.0"
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_chainId",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": "0x688"
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_syncing",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": false
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getBalance",
      "params": [
        "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
        "latest"
      ],
      "id": 1
    }
    
    // Response
    {"jsonrpc":"2.0","id":1,"result":"0x0234c8a3397aab58"}
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getTransactionCount",
      "params": [
        "0xfaC0718D882E5f07e1972D617bd430CD72A55D84",
        "latest"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0x2"
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getStorageAt",
      "params": [
        "0x4100000000000000000000000000000000000000",
        "0x0",
        "latest"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0x0000000000000000..."
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getCode",
      "params": [
        "0x4100000000000000000000000000000000000000",
        "latest"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0x6080604052600a6012565b6022565b5f90565b6018600e565b50601f60b5565b..."
    }
    // 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"
          }
        ]
      }
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_sendRawTransaction",
      "params": [
        "0xf86c0a8502540be400825208944bbeeb066ed09b7aed07bf39eee0460dfa261520880de0b6b3a7640000801ca0f3ae..."
      ],
      "id": 1
    }
    
    // Response
    {"jsonrpc":"2.0","id":1,"result":"0xe670ec6434177160..."}
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_call",
      "params": [
        {
          "to": "0x4100000000000000000000000000000000000000",
          "data": "0x76671808"
        },
        "latest"
      ],
      "id": 1
    }
    
    // Response
    {"jsonrpc":"2.0","id":1,"result":"0x0000000000000000..."}
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_estimateGas",
      "params": [
        {
          "to": "0x4100000000000000000000000000000000000000",
          "data": "0x76671808"
        }
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0x9d4b"
    }
    // 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"
      }
    }
    // 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": []
      }
    }
    // 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": []
      }
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getBlockReceipts",
      "params": [
        "0x2710"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": []
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getBlockTransactionCountByHash",
      "params": [
        "0xfa15419fc3de49e27f62eff7be1f344a3fd156e332ea9a78337c73a0b2b6573a"
      ],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": "0x0"
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "eth_getBlockTransactionCountByNumber",
      "params": [
        "0x2710"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0x0"
    }
    // 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"
      }
    }
    // 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"
      }
    }
    // 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"
      }
    }
    // 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"
      }
    }
    // 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"
        }
      ]
    }
    // 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"
        }
      }
    }
    // Request (WebSocket only)
    {
      "jsonrpc": "2.0",
      "method": "eth_unsubscribe",
      "params": ["0x1"],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": true
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "net_version",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "1672"
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "web3_clientVersion",
      "params": [],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "portal/1.0.0"
    }
    // 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..."
      }
    }
    // 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": []
        }
      }
    }
    // 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..."
        }
      ]
    }
    // 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..."
        }
      ]
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "debug_getRawBlock",
      "params": [
        "0x2710"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0xf90252f9024da01ff0db61e7c714a91d7e3528c7f659e43b1ddfddb8b20848c8..."
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "debug_getRawHeader",
      "params": [
        "0x2710"
      ],
      "id": 1
    }
    
    // Response
    {
      "id": 1,
      "jsonrpc": "2.0",
      "result": "0xf9024fa01ff0db61e7c714a91d7e3528c7f659e43b1ddfddb8b20848c87775af..."
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "debug_getRawReceipts",
      "params": [
        "0x2710"
      ],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": []
    }
    // Request
    {
      "jsonrpc": "2.0",
      "method": "debug_getRawTransaction",
      "params": [
        "0xee093bb802e09fe6119e83d7fe35004464a98e69000bcd5672ac73b32172c997"
      ],
      "id": 1
    }
    
    // Response
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": "0xf9036d668502540b..."
    }
    // 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"
        }
      ]
    }
    // 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..."
      }
    }
    // 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"
          },
          "..."
        ]
      }
    }
    // 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"
        ]
      }
    }

    eth_coinbase

    "0x0"

    No mining reward address in PoS

    eth_mining

    false

    eth_feeHistory

    eth_blockNumber

    eth_chainId

    eth_syncing

    eth_getBalance

    eth_getTransactionCount

    eth_getStorageAt

    eth_getCode

    eth_getProof

    eth_sendRawTransaction

    eth_call

    eth_estimateGas

    eth_createAccessList

    eth_getBlockByHash

    eth_getBlockByNumber

    eth_getBlockReceipts

    eth_getBlockTransactionCountByHash

    eth_getBlockTransactionCountByNumber

    eth_getTransactionByHash

    eth_getTransactionByBlockHashAndIndex

    eth_getTransactionByBlockNumberAndIndex

    eth_getTransactionReceipt

    eth_getLogs

    eth_subscribe

    eth_unsubscribe

    Network Methods

    net_version

    net_listening

    net_peerCount

    Web3 Methods

    web3_clientVersion

    web3_sha3

    Debug Methods

    debug_traceTransaction

    debug_traceCall

    debug_traceBlockByHash

    debug_traceBlockByNumber

    debug_getRawBlock

    debug_getRawHeader

    debug_getRawReceipts

    debug_getRawTransaction

    debug_protocolVersion

    Trace Methods

    trace_filter

    Transaction Pool Methods

    txpool_nonceFrom

    Pharos Extension Methods

    eth_getAccount

    eth_getProposerByTxHash

    eth_getProposersByBlockNumber

    Pharos Debug Extensions

    debug_getValidatorInfo

    debug_getBlockProof

    debug_getBlockReadStates

    debug_getBlockWriteStates

    debug_getBlockWriteSetKeys

    PoW Compatibility Methods (Deprecated)

    Not Implemented Methods

    Notes

    BLS Verification Demo
    Simplified Payment Verification (SPV)

    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

    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

    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.

    For more details of how Pharos implements SPV, you can check


    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

    curl Example — account proof only:

    curl Example — account proof + storage proof:


    Field
    Type
    Description

    Unlike Ethereum (which returns RLP-encoded hex strings), each Pharos proof node is a JSON object:

    • 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 .

    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:

    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:

    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.

    Each entry in storageProof has the following structure:


    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.

    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 ) — do not read offsets blindly from nextBeginOffset. Confirm:


    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.

    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.

    When isExist is false and the proof terminates at an Internal node with an empty slot, the response includes:

    Each entry represents a non-empty sibling slot of the terminating Internal node:

    Field
    Type
    Description
    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 . 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:

    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.


    A Python verification script 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 ), enforced as a soundness invariant.

    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.

    The full script source is available at the .


    For teams familiar with Ethereum's eth_getProof (), the key differences are:

    Aspect
    Ethereum
    Pharos

    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

    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).

  • Root verification: The hash of the MSU Root node must equal the trusted stateRoot from the block header.

  • 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.

  • Root verification: Confirm the MSU Root node hashes to the trusted stateRoot.

  • Optional cross-validation of
    stateRoot
    against an RPC endpoint.

    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.

    1

    string

    Account address (20-byte hex, 0x-prefixed)

    2

    string[]

    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 -X POST http://<node-rpc-endpoint> \\
      -H "Content-Type: application/json" \\
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "eth_getProof",
        "params": [
          "0x4100000000000000000000000000000000000000",
          ["0x0000000000000000000000000000000000000000000000000000000000000001"],
          "latest"
        ]
      }'

    balance

    string

    Account balance in hex. "0x0" if account does not exist.

    nonce

    string

    {
      "proofNode": "0x<hex-encoded binary node data>",
      "nextBeginOffset": <int>,
      "nextEndOffset": <int>
    }
    msu_slot_index  = key[len(key) - 1]                 // 0..255
    expected_offset = msu_slot_index * 32
    key_hash        = SHA-256(key)
    slot_idx        = nibble_at_depth(key_hash, depth)  // 0..15
    expected_offset = 3 + slot_idx * 32
    {
      "key": "0x<storage-key>",
      "value": "0x<storage-value>",
      "isExist": true,
      "proof": [
        { "proofNode": "0x...", "nextBeginOffset": N, "nextEndOffset": M },
        ...
      ]
    }
    {
      "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 },
              ...
            ]
          }
        ]
      }
    }
    {
      "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 }
          ]
        }
      ]
    }

    slotIndex

    int

    The slot index (0–15) of the sibling in the parent Internal node.

    leftmostLeafKey

    string

    {
      "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": []
      }
    }
    # 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

    Hash algorithm

    Keccak-256

    SHA-256

    Trie structure

    Merkle Patricia Trie (MPT)

    2. API Reference

    eth_getProof

    3. Response Format

    Common Fields

    Proof Node Format

    Slot Offset Derivation

    Storage Proof Format

    4. Existence Proof

    Example Response (Existence)

    Verification Logic (Existence)

    5. Non-existence Proof

    Why Additional Sibling Proofs Are Needed

    siblingLeftmostLeafProofs Format

    Example Response (Non-existence)

    Verification Logic (Non-existence)

    6. Verification Script

    Usage

    7. Comparison with Ethereum

    SPV Proof Theory Explanation
    § Slot Offset Derivation
    § Slot Offset Derivation
    § Slot Offset Derivation
    spv_verify.py
    § Slot Offset Derivation
    examples repository
    EIP-1186
    SPV Proof Theory Explanation
    Pharos storage state trie

    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")

    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 .

    storageProof

    object[]

    Array of storage proof entries (one per requested storage key). See .

    rawValue

    string

    Raw RLP-encoded account value (hex). Empty "0x" if account does not exist.

    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.

    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

    Proof Node Format
    Storage Proof Format

    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

    Contract
    Address

    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

    • 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

    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

    • Configure your contracts to interact with LayerZero endpoints

    • Select appropriate DVNs for your use case

    • Test cross-chain messaging flows across supported networks

    pharos

    Stage

    mainnet

    Endpoint ID (EID)

    30407

    endpointV2

    0x6F475642a6e85809B1c36Fa62763669b1b48DD5B

    sendUln302

    0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7

    receiveUln302

    Endpoint V2 Contracts

    When to Use LayerZero

    Integration Notes

    Metadata API

    Next Steps

    0xe1844c5D63a9543023008D332Bd3d2e6f1FE1043

    blockedMessageLib

    0xc1ce56b2099ca68720592583c7984cab4b6d7e7a

    executor

    0x4208D6E27538189bB48E603D6123A94b8Abe0A0b

    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.

    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 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.

    For more details, check out the . Start building smarter, faster, and focus on what really matters: delivering a great experience to your users.

    Goldsky Products

    Subgraphs

    Mirror

    Getting Started

    Goldsky Documentation

    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

    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

    • 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

    For detailed integration guides and implementation details:

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

    • 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

    0xa094978891512268f4a4a4641B8da1A2a3E3BEB7

    When to Use Chainlink Data Streams

    Integration Notes

    Documentation

    Next Steps

    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

    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.

    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.

    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.

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

    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.

    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

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

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

    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.

    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

    Copy the contract into your project:

    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.

    Every section in the reference follows this template:

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

    Operation Type
    Include?
    Notes

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

    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.

    • 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

    Check
    What to Verify
    Error / CLI Signature
    Cause
    Fix
    Agent reads the relevant reference file section for the exact command template
  • Agent reads assets/networks.json to get the correct RPC URL and chain ID

  • For write operations: agent runs all four pre-checks (private key, address, network, balance)

  • Agent executes the cast or forge command

  • Agent parses the output per the reference file's output parsing rules

  • Agent shows result with block explorer link

  • ,
    owner
    , etc.)
  • Revert messages that are human-readable ("Still locked", "Not owner", "Must send PHRS")

  • Include common natural language phrasings as synonyms in the User Need column

  • Point each row to the exact anchor in your reference file

  • 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

    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
    unzip pharos-skill-engine-0.1.0.zip
    cd pharos-skill-engine-0.1.0
    
    # Explore the structure
    ls
    # → SKILL.md  assets/  references/
    # 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
    # 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)
    # 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 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 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
    # 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
    # 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
    
    # <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>

    Deploy contract

    Always

    Include script generation step

    Verify contract

    Always

    
    | 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 |
    

    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

    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

    # ── 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

    File Structure Reference

    Part 2 — How It Works

    Layer 1 — The Agent Reads SKILL.md First

    Layer 2 — The Reference Files Contain the Commands

    Layer 3 — The Assets Are the Raw Materials

    The Write Operation Pre-Check Sequence

    How the Agent Resolves a Request — Step by Step

    Part 3 — How to Use It

    Step 1 — Install the Skill Engine

    Step 2 — Install Foundry

    Step 3 — Configure Your Wallet

    Step 4 — Start Using Capabilities

    Query Operations (free — no gas)

    Send Transactions

    Estimate Gas Before Sending

    Deploy and Verify a Contract

    Part 4 — How to Publish New Skills

    Stage 1 — Write the Contract

    Stage 2 — Write the Reference File

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

    What to Cover in Your Reference File

    Stage 3 — Register in SKILL.md

    Capability Index Format

    Complete SKILL.md Registration Checklist

    Publishing Checklist

    Part 5 — Global Error Reference

    Part 6 — Quick Reference

    All Commands at a Glance

    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

    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

    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

    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