Introduction

Welcome to The Otterscan Book.

This is the official documentation for Otterscan, an open-source, fast, local, laptop-friendly Ethereum block explorer.

This book is open-source and available on GitHub: https://github.com/otterscan/otterscan-book.

It is also available online at: https://docs.otterscan.io/

This chapter starts with an overview of what is Otterscan.

Then we move into a high level explanation of its architecture.

Which chains are compatible with Otterscan?

How contract verification is handled.

What is it?

Otterscan is a block explorer for EVM chains designed to run locally with an archive node companion - more specifically, Erigon.

This approach brings many advantages, as follows.

Privacy

You are querying your own node, so you are not sending your IP address or queries to an external, third-party node.

Fast

Since you are querying your local archive node, everything is fast. No network roundtrips are necessary.

Actually, very fast

This software was designed to be a companion of Erigon, a blazingly fast archive node.

Really, it is even faster

The standard web3 JSON-RPC methods are quite verbose and generic requiring many calls to gather many pieces of information at client side.

We've implemented some custom methods at the rpcdaemon level, so less information is needed to be JSON-marshalled and transmitted over network.

Architecture

Otterscan itself can be split into three parts:

Otterscan UI

This is the main block explorer UI that end users will interact with.

It is a React single-page application, and the repository is available at https://github.com/otterscan/otterscan.

Otterscan API Specification

This is the set of API definitions the Otterscan UI depends on.

See more here.

Otterscan API Implementation

This is server software that implements the Otterscan API.

The reference implementation is Erigon, but any Ethereum client (in theory) could implement it.

Supported Networks

Otterscan runs on any network that has a client implementing the Otterscan API. Below is a non-exhaustive list.

Erigon

  • Ethereum
    • Mainnet
    • Sepolia and Holesky testnets
  • Gnosis Chain
    • Gnosis mainnet
    • Chiado testnet
  • Polygon
    • Polygon PoS
    • Amoy testnet
  • Ephemery testnet

OP-Erigon

Anvil

  • Local devnets

Anvil implements the Otterscan API, so you can point your Otterscan installation to an Anvil RPC endpoint and have an explorer for your local devnet.

Contract Verification

We display verified contracts using information from Sourcify.

For more details on how the integration works, please check the Contract verification chapter.

Installation

First of all, take a look at how we envision all Otterscan components should connect to each other. Understanding this architecture will make it easier to solve common network issues.

Make sure you have a working and synced Erigon instance. Setting up Erigon is out of scope of this book.

Otterscan RPC support is already embedded in every Erigon node >= v2.29.0. You just need to enable it.

This software is currently distributed as a docker image.

Otterscan is a merged explorer, which means it can optionally display beacon chain data for chains that support it.

Otterscan v2.x has some EXPERIMENTAL extra indexers.

Once you finish installing it, you can check if everything is working as expected.

Architecture overview

You can run all components that make Otterscan possible in your own machine, but it is likely you are going to run it in your local network.

For a better understanding, let's assume every component is in a different machine. First of all, you need to make sure all endpoints are accessible from the browser you are going to use.

network topology

The following sections will explain in more details the required configuration for each of these components.

Running Otterscan with Erigon

Enable the Otterscan RPC namespace in Erigon

When running Erigon, make sure to enable the eth, erigon, trace and ots namespaces in addition to whatever CLI options you are using to start Erigon. The trace namespace is used for transaction state diffs.

Enabling namespaces in Erigon is done through the --http.api argument.

Example

erigon --http.api "eth,erigon,trace,ots[,<other-namespaces>]" [<other CLI arguments...>]

ots stands for Otterscan, and it is the JSON-RPC namespace we use for our own custom APIs.

Enable CORS

CORS is required for the browser to call the Erigon node directly.

This is done using the --http.corsdomain argument. Make sure it is set correctly to the domain you are binding your Erigon node to.

💡 If you are going to enable CORS for all domains, make sure to quote it, i.e. --http.corsdomain='*' otherwise the wildcard (*) may be captured by the shell.

Example

erigon --http.corsdomain '*' [<other CLI arguments...>]

Optional flags

--ots.search.max.pagesize <N>

By default the ots_searchTransactions* JSON-RPC methods are capped at server level to max page size of 25, no matter what page size is specified in the method parameters.

That is intentional, in order to prevent DoS'ing the node if users were able to specify unbounded page sizes.

In you are using the Otterscan API directly, you may want to increase the maximum allowed page size by setting this parameter on Erigon startup.

Install and running Otterscan

Run Otterscan Docker image from Docker Hub

The official Otterscan repo on Docker Hub is here.

There is an image tag for each release tag on GitHub, e.g., v2.5.0, v2.6.0. In addition to that, there is a develop tag which is built automatically from the develop branch on GitHub in case you want to run the most recent develop build.

Example

docker run --rm -p 5100:80 --name otterscan -d otterscan/otterscan:<versiontag>

This will download the Otterscan image from Docker Hub, run it locally under the otterscan container name using the default parameters, and bind it to port 5100 (see the -p docker run parameter).

To stop the Otterscan service, run:

docker stop otterscan

By default, it assumes your Erigon node is at http://127.0.0.1:8545, and users' browsers will try to connect to this RPC URL. You can override the URL by setting the ERIGON_URL env variable in the container:

docker run --rm -p 5100:80 --name otterscan -d --env ERIGON_URL="<your-erigon-node-url>" otterscan/otterscan:<versiontag>

You can override the entire Otterscan configuration with the OTTERSCAN_CONFIG env variable:

docker run \
  --rm \
  -p 5100:80 \
  --name otterscan \
  -d \
  --env OTTERSCAN_CONFIG='{
    "erigonURL": "http://127.0.0.1:8545",
    "assetsURLPrefix": "http://127.0.0.1:5175",
    "branding": {
        "siteName": "My Otterscan",
        "networkTitle": "Dev Network"
    },
}' \
otterscan/otterscan:latest

These settings overwrite the Otterscan config file on container startup. To disable this behavior, pass --env DISABLE_CONFIG_OVERWRITE=1 to the Docker command.

This is the preferred way to run Otterscan. You can read about other ways here.

Run Otterscan development image from Docker Hub

The develop branch is automatically built and published on Docker Hub.

There is a helper script that always pulls the latest build and sets the required parameters.

From the repository root:

./scripts/run-ots-develop.sh <ERIGON-RPC-URL> <CL-REST-API-URL>

It'll start a container under the name otterscan.

Beacon chain support

Since The Merge, running a consensus layer node (CL) is required alongside the execution layer node (EL).

For merged chains, Otterscan can optionally display CL information. Most known merged chains are Ethereum and Gnosis (mainnet and testnets).

First you'll need to make sure your CL node is fully synced and then enable its REST API support.

You can then enable it in Otterscan.

Overview

Like the EL, which has blocks and transactions, CL has its own set of data structures. It is possible to read this information from CL through a standardized Beacon Chain REST API.

Gotchas

There are many CL implementations that you can combine with your EL, hence many possible issues or different behaviors, even in the same implementation depending on how it is configured.

We have been mainly using/testing both Lighthouse (in its "experimental tree states") and Erigon's own internal CL "Caplin" since they have checkpoint sync and backfilling.

It is out-of-scope of this document to review all existing CL implementations.

Let's now describe in more detail some issues we noticed.

Checkpoint vs. genesis sync

Checkpoint sync is a nice feature that allows you to "jump" directly to a finalized slot and have a functional CL node in a matter of minutes.

However, by doing that you won't have the details of historical slots, epochs, etc.

That may not be a problem if you are only concerned with your current validator balance, for example. Just be aware of that.

The alternative, if you want to browse all beacon chain history, is to do a genesis sync, but that is very slow on mainnet.

Backfilling

Some CL implementations (e.g. Lighthouse) have a nice feature called backfilling, where you can do a checkpoint sync and have a functional CL node in minutes, while historical information is downloaded in background.

That may be a better alternative to genesis sync if you want to have all historical info.

Some historical operations can be slow on default settings

Let's take as an example the /eth/v1/validator/duties/proposer/ call that we use to obtain the elected block proposers for a given epoch.

Its main use is to answer the question: for a missed slot, who was the elected validator who missed it?

Well, in Lighthouse, using the default settings, querying an old epoch is very slow: https://github.com/sigp/lighthouse/issues/3770

The alternative is to change data granularity, trading off disk space for speed.

There may be other cases with different trade-offs. Be aware of how your CL implementation works.

Enabling the CL REST API

Instructions for enabling the REST API are dependent on which CL implementation you are using.

It is out-of-scope of this document to explain how it is done since there are many CL implementations. Please check their docs.

We'll provide instructions for Lighthouse and Caplin as an example, since they are the implementations we use in our tests.

But the key points you need to observe are:

  • Default port number: that is not standardized, Lighthouse uses 5052, Prysm uses 3500, etc.
  • Bind the REST web server to the correct network interface: it is usually set to localhost for security reasons, be sure to explicitly bind it to your network interface in order for your browser to reach it. You'll most likely want to bind it to 0.0.0.0.
  • Enable CORS.

Example instructions:

Lighthouse

As of Lighthouse 3.3.0, the required parameters to make it work with Otterscan are:

lighthouse beacon \
  --http \
  --http-address "0.0.0.0" \
  [--http-port <REST-API-port-number> \]
  --http-allow-origin '*' \
  --genesis-backfill \
  [--disable-backfill-rate-limiting \]
  [<other CLI arguments>]
  • --http-port is optional, by default it binds to :5052.
  • --genesis-backfill is required in order to backfill slot data back to the genesis. If you don't specify that parameter and use checkpoint sync, only slot info after the checkpoint will be available.
  • --disable-backfill-rate-limiting is optional, but if your node is used exclusively for Otterscan you'll probably want to enable it in order to speed up backfilling during the first sync.

Caplin

💡 Caplin is being actively developed, and this document may contain outdated instructions. Please check the Erigon docs if you think that's the case.

Make sure to activate all of the following erigon arguments:

  • --internalcl: activate internal CL support (Erigon 2)

    💡 This flag is not necessary on Erigon 3 since internal CL is enabled by default

  • --caplin.archive: activate Caplin archive node support
  • --caplin.backfilling, --caplin.backfilling.blob, --caplin.backfilling.blob.no-pruning: enable backfilling of historical CL blocks
  • --beacon.api "beacon,builder,config,debug,events,node,validator,lighthouse": enable beacon chain REST API namespaces
  • --beacon.api.addr "0.0.0.0": expose the REST API endpoint as necessary (default is localhost)
  • --beacon.api.port <port-number>: change the port number for the REST API endpoint (optional; default is 5555)
  • --beacon.api.cors.allow-methods "GET,OPTIONS", --beacon.api.cors.allow-origins '*': enable CORS for the REST API endpoint

Enabling in Otterscan

Add the BEACON_API_URL environment parameter to your docker run command. The URL must point to an exposed CL REST endpoint which is reachable from your browser.

Example:

docker run \
  --rm \
  --name otterscan \
  -d \
  -p 5100:80 \
  --env ERIGON_URL="http://my-erigon-node:8545" \
  --env BEACON_API_URL="http://my-lighthouse-node:5052" \
  otterscan/otterscan:<tag>

Otterscan 2.x EXPERIMENTAL indexers

Otterscan v2.x supports some experimental, optional indexers.

They require some extra steps to be enabled.

⚠️ The following instructions are for Erigon 2.x but their support are deprecated. We are currently working on Erigon 3 support and both new APIs and features in the UI will likely change.

⚠️ They require MORE disk space and first sync time in order to generate the extra information.

Clone and build Erigon + OTS2 support

Checkout the ots2-alpha4 branch from Erigon repository: https://github.com/erigontech/erigon/tree/ots2-alpha4

Build it as usual with make command.

Enable OTS2 indexers inside Erigon

Change Erigon CLI args to:

  • Enable ots2 API namespace in addition to ots.
  • Add --experimental.ots2 CLI arg.

For example, if your Erigon start command is:

erigon \
        --http.api "eth,erigon,trace,ots" \
        [<other CLI arguments>]

change it to:

erigon \
        --http.api "eth,erigon,trace,ots,ots2" \
        --experimental.ots2 \
        [<other CLI arguments>]

Enable OTS2 mode in Otterscan

Add the OTS2=true env variable when starting the docker container.

For example, if your docker start command is:

docker run \
  --rm \
  --name otterscan \
  -d \
  -p 5100:80 \
  --env ERIGON_URL="<erigon-url>" \
  otterscan/otterscan:v2.3.0

change it to:

docker run \
  --rm \
  --name otterscan \
  -d \
  -p 5100:80 \
  --env ERIGON_URL="<erigon-url>" \
  --env OTS2=true \
  otterscan/otterscan:v2.3.0

Other ways to run Otterscan

(Alternative 1) Build Otterscan docker image locally and run it

If you don't want to download it from Docker Hub, you can build the docker image from the sources and run it.

If you just want to build the image locally, there is no need to install the development toolchain, just make sure you have a recent working Docker installation.

This method requires only git and docker.

The entire build process will take place inside the docker multi-stage build.

Clone Otterscan repo.

git clone https://github.com/otterscan/otterscan.git
cd otterscan
npm run docker-build

This will run the entire build process inside a build container, merge the production build of the React app with pre-made assets from otterscan-assets project into the same image format it is published in Docker Hub, but locally under the name otterscan.

Then you can start/stop it using the commands:

npm run docker-start
<browse locally on http://localhost:5273>
npm run docker-stop

(Alternative 2) Run a development build from the source

First, a brief explanation about the app:

  • The app itself is a simple React app which will be run locally and communicates with your Erigon node.
  • The app makes use of external data sources, like icons, signatures, etc. They require you to run a separate process to serve them. They are made available as a nginx Docker image.

These instructions are subjected to changes in future for the sake of simplification.

Make sure you have a working node 20/npm 10 installation.

By default, it assumes your erigon process is serving requests at http://localhost:8545, a beacon chain REST API serves requests at http://localhost:5052, and assets are hosted at http://localhost:5175.

You can customize these URLs by creating a .env.development.local file at the root of the repository (automatically git ignored) and adding the following entries:

VITE_ERIGON_URL=<your-erigon-JSON-RPC-endpoint>
VITE_BEACON_API_URL=<your-beacon-chain-REST-API-endpoint>
VITE_ASSETS_URL=<your-assets-endpoint>

Start serving the assets server by running the following Docker image:

npm run assets-start

To stop it, run:

npm run assets-stop

By default they run at http://localhost:5175

To run Otterscan development build:

npm ci
npm start

Otterscan should now be running at http://localhost:5173

Assets Server

Otterscan can make use of several external data sources to give context to on-chain data. The external data sources are served to Otterscan clients at the location given by the assetsURLPrefix config option.

Typically, these assets are served using the otterscan/otterscan-assets Docker image from the otterscan-assets repository. In a development environment, you can use npm run assets-start and npm run assets-stop to run the Docker commands.

Setups requiring custom or private data can serve assets manually following the same structure.

Structure Overview

The asset server serves files in the following folder structure:

  • assets: Contains token logos from https://github.com/trustwallet/assets.
    • [chain_id]: Each subdirectory corresponds to a specific blockchain network, such as Ethereum (1).
  • chains: Chain data from https://github.com/ethereum-lists/chains.
    • eip155-[chain_id].json
  • signatures: A directory mapping function selectors to function signatures, in the format from https://github.com/ethereum-lists/4bytes.
    • Each filename is a 4-byte selector (e.g., a9059cbb), and the file contents are the corresponding function signature (e.g., transfer(address,uint256)).
  • topic0: A directory mapping log topic hashes to event signatures, in the format from https://github.com/otterscan/topic0.
    • Each filename is a 32-byte hash (e.g., ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef), and the file contents are the corresponding event signature (e.g., Transfer(address indexed from,address indexed to,uint256 value)).

Manually recreating the assets folder structure

You can recreate the assets folder structure and serve it manually.

First, create a new directory and clone all the data repositories into it:

mkdir external-repos
cd external-repos

git clone https://github.com/trustwallet/assets.git trustwallet-assets
git clone https://github.com/ethereum-lists/chains.git chains
git clone https://github.com/ethereum-lists/4bytes.git 4bytes
git clone https://github.com/otterscan/topic0.git topic0
cd ..

Create the main assets-server directory:

mkdir assets-contents
cd assets-contents

Set up symbolic links pointing to external data from previously cloned repositories:

ln -s ../external-repos/chains/_data/chains chains
ln -s ../external-repos/4bytes/signatures signatures
ln -s ../external-repos/topic0/with_parameter_names topic0

Set up a symbolic link pointing to the token logos (replace ethereum with the chain name):

mkdir assets
ln -s ../external-repos/trustwallet-assets/blockchains/ethereum/assets ./assets/1

(Optional.) Populate a contracts folder with the Sourcify repository structure and reuse the assets server as a custom Sourcify source.

Spin up HTTP server

In a production environment, use a real HTTP server application, like how the otterscan-assets image uses nginx.

In a development environment, you can use the built-in, single-threaded http.server Python module to serve the current directory:

python3 -m http.server 5175

The assets server will be hosted at http://localhost:5175.

Validating the installation

You can make sure everything is working correctly if the homepage is able to show the latest block/timestamp your Erigon node is at just below the search button.

home

If the beacon chain support is properly configured, you should see the latest finalized slot as well.

home with beacon chain

Configuration options

In this chapter we first explain how the application configuration is read.

Then we explore all the available configuration options.

Specifying the configuration

There are multiple ways to configure Otterscan base settings, depending on how you want to run it.

Static hardcoded node/chain ID

Define a VITE_CONFIG_JSON environment variable containing a JSON string with the entire config.

Fetch config from server

If you don't specify a VITE_CONFIG_JSON variable, the dapp will fetch a <your-domain>/config.json file on page load.

That file can be overwritten server-side and changes will be reflected when users refresh the page.

You are free to define the best way to do that depending on how you package your Otterscan distribution.

For reference, our official Docker image accepts initialization parameters that overwrite that file when the container is initialized.

Development

During development, define a VITE_CONFIG_JSON variable inside a .env.development.local file, and vite will use those settings automatically.

Otterscan uses dotenv, and that file is automatically .gitignore'd and not pushed into version control.

Example .env.development.local file:

VITE_CONFIG_JSON='
{
  "erigonURL": "http://your-erigon-node-ip:8545",
  "beaconAPI": "http://your-beacon-node-ip:5052",
  "assetsURLPrefix": "http://localhost:5175",
  "experimentalFixedChainId": 11155111,
  "chainInfo": {
    "name": "Sepolia Testnet",
    "faucets": [],
    "nativeCurrency": {
      "name": "Sepolia Ether",
      "symbol": "SEPETH",
      "decimals": 18
    }
  },
  "sourcifySources": {
    "ipfs": "https://ipfs.io/ipns/repo.sourcify.dev",
    "central_server": "https://repo.sourcify.dev"
  }
}
'

Production

In a production environment, the VITE_CONFIG_JSON environment variable needs to be defined at build time, otherwise it has no effect; in that case, the node/chain config will be static.

That way the config won't need to be fetched from server—one less network call on page load. That's the recommended setting for controlled hosted environments where you control the node your users will connect to.

You can either:

  • Make your CI export a VITE_CONFIG_JSON env variable with the desired settings.
  • During build time, make sure there is a .env.production file with a VITE_CONFIG_JSON variable defined in it.

Options

Otterscan can be customized in many different ways.

If your chain is an Optimism L2 Superchain, you can wire that Otterscan instance with an L1 one.

Price resolvers can be customized as well.

If you are running Otterscan against a non-standard chain, but you are sure Erigon can run it, you can explicitly specify the configuration.

In case you are running your own Sourcify instance, that's configurable as well.

Branding

Integrators can customize some aspects of the Otterscan UI in order to match their brand.

Customization

Some components in the user interface can be customized in the config under the branding key:

{
  "branding": {
    "siteName": "Otterscan",
    "networkTitle": "Holesky Testnet",
    "hideAnnouncements": false
  }
}
  • siteName: Sets the name displayed on the home page, header, and page titles.
  • networkTitle: If set, adds an additional name to page titles.
  • hideAnnouncements: If set to true, hides new feature announcements from the home page.

To replace the default Otterscan logo with your own, simply replace src/otter.png with a different PNG image. Rebuild Otterscan for the change to take effect.

Optimism

For chains which follow the OP Stack, configure the opChainSettings key in the config:

  • l1ExplorerURL: The root URL of a block explorer for the layer-1 of this chain, without a trailing forward slash. This appears on block pages in the "L1 Epoch" row.

Example for Sepolia:

{
  "opChainSettings": {
    "l1ExplorerURL": "https://sepolia.otterscan.io"
  }
}

Price oracles

Otterscan makes use of on-chain Chainlink data feeds for fetching historical price data from Chainlink smart contracts compatible with the AggregatorV3 interface which act as price oracles. The native token price is fetched from a Chainlink data feed smart contract.

The Ethereum mainnet has a registry which Otterscan uses to look up token prices for supported tokens. On other chains, and for tokens not in the registry, Otterscan can estimate a token's price using information from on-chain decentralized exchanges.

NOTE: The prices estimated from on-chain decentralized exchanges can be manipulated, especially when tokens have low liquidity or are unable to be traded normally. This price estimation feature returns "best guess" prices; Otterscan certainly cannot guarantee that all tokens with an estimated price have a liquid market or that the prices shown are accurate.

To configure Otterscan's price oracle usage, configure the priceOracleInfo key in the config:

  • nativeTokenPrice: If configured, shows the native token price at the top of the page.
    • ethUSDOracleAddress: AggregatorV3-compatible smart contract that returns the price of the native token in USD. If Chainlink does not support your chain, you may consider deploying a smart contract which implements the AggregatorV3 interface yet derives price data from on-chain sources.
    • ethUSDOracleDecimals: Number of decimals used by the oracle smart contract.
  • wrappedEthAddress: The address of the wrapped native token contract, which should match the WETH variable in Uniswap router contracts. This is used to estimate the price of tokens which have at least one Uniswap pool paired with the wrapped native token.
  • uniswapV2: If configured, uses UniswapV2 pairs as sources of price information.
    • factoryAddress: The UniswapV2 factory address.
  • uniswapV3: If configured, uses UniswapV3 pools as sources of price information. Pools at the 0.01%, 0.05%, 0.3%, and 1% price tiers are queried.
    • factoryAddress: The UniswapV3 factory address.

Example for Ethereum mainnet:

{
  "priceOracleInfo": {
    "nativeTokenPrice": {
      "ethUSDOracleAddress": "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",
      "ethUSDOracleDecimals": 8
    },
    "wrappedEthAddress": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
    "uniswapV2": {
      "factoryAddress": "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"
    },
    "uniswapV3": {
      "factoryAddress": "0x1F98431c8aD98523631AE4a59f267346ea31F984"
    }
  }
}

Recognizing nonstandard chains

By default, Otterscan recognizes several chains, including the Ethereum mainnet and several Ethereum test networks. For other chains, specify either (1) a chainInfo key in the Otterscan config, or (2) create a JSON file accessible at {assetsURLPrefix}/chains/eip155-{chainId}.json. In both cases, use the ethereum-lists structure to describe the properties of the chain:

  • name: The full name of the network, such as "Ethereum Mainnet".
  • faucets: A list of faucet URLs which are accessible at the /faucets endpoint and navigable from address pages. The special string ${ADDRESS} can be included in the URL and will be replaced with the address the user navigated from.
  • nativeCurrency: Describes the native currency of the chain; this is analogous to ETH on the Ethereum mainnet.
    • name: Full name of the native currency, e.g. "Ether".
    • symbol: Few-character symbol used in trading, e.g. "ETH".
    • decimals: Number of decimals; usually 18.

Example:

{
  "name": "Sepolia Testnet",
  "faucets": [],
  "nativeCurrency": {
    "name": "Sepolia Ether",
    "symbol": "sepETH",
    "decimals": 18
  }
}

Alternative Sourcify sources

Rather than using the default ipfs.io and repo.sourcify.dev sites for accessing Sourcify, you may specify your own Sourcify root URLs in the configuration file by adding the sourcifySources key and changing the URLs accordingly:

"sourcifySources": {
  "ipfs": "https://ipfs.io/ipns/repo.sourcify.dev",
  "central_server": "https://repo.sourcify.dev"
}

This is useful if you have your own Sourcify repository hosted locally, in which case all of your Otterscan activity will be private.

API Spec

This chapter only makes sense if you intend to dive deep into Otterscan source code or want to integrate directly with the Otterscan APIs.

Specification

There is an ongoing effort to formalize the Otterscan API spec in the same format as the standard JSON-RPC APIs.

A more detailed explanation of the ots namespace:

There is an experimental ots2 namespace, which is likely to change, but here is an overview:

Design Goals

Otterscan's RPC namespaces aim to provide a minimalistic solution for Ethereum data access which overcomes the limitations of the standard JSON-RPC API. Implementing features directly in-node eliminates the need for additional indexer middleware, databases, or dependencies. Our design prioritizes simplicity and flexibility over high-performance scalability.

ots namespace spec

The standard Ethereum JSON-RPC APIs are very limited and in some cases non-performant for what you can do with an archive node.

There is plenty of useful data that can be extracted and we implemented some extra RPC methods for them.

They are all used by Otterscan, but we are documenting them here so others can try it, give feedback and eventually get it merged upstream if they are generalized enough.

We take an incremental approach when design the APIs, so there may be some methods very specific to Otterscan use cases, others that look more generic.

Those APIs are implemented at EL-client level under the ots JSON-RPC namespace.

How do I use it?

They are all JSON-RPC methods, so your favorite web3 library should have some way to custom call them.

For example, ethers.js wraps standard calls in nice, user-friendly classes and parses results into easy-to-use objects, but also allows you to do custom calls and get raw results while still taking advantage of their capabilities like automatic batching, network timeout handling, etc.

I'll use ethers.js as an example here because it is what I use in Otterscan. Please check your web3 library docs for custom call support.

Example

Let's call the ots_getTransactionError method to obtain the revert reason of a failed transaction. It accepts one string parameter containing the transaction hash and returns a byte blob that can be ABI-decoded:

const provider = ...; // Obtain a JsonRpcProvider object
const txHash = "..."; // Set the transaction hash
const result = (await provider.send("ots_getTransactionError", [txHash])) as string;

Method summary

All methods are prefixed with the ots_ namespace in order to make it clear it is vendor-specific and there is no name clash with other same-name implementations.

NameDescriptionReasoning
ots_getApiLevelTotally Otterscan internal API, absolutely no reason for anything outside Otterscan to use it.Used by Otterscan to check if it's connecting to a compatible patched Erigon node and display a friendly message if it is not.
ots_getInternalOperationsReturns the internal ETH transfers inside a transaction.For complex contract interactions, there may be internal calls that forward ETH between addresses. A very common example is someone swapping some token for ETH, in which case there is an ETH send to the sender address which is only unveiled by examining the internal calls.
ots_hasCodeCheck if a certain address contains deployed code.A common way to check if an address is a contract or an EOA is calling eth_getCode to see if it has some code deployed. However this call is expensive data-wise if the contract has a lot of deployed code. This call just returns a boolean.
ots_getTransactionErrorExtract the transaction's raw error output.In order to get the error message or custom error from a failed transaction, you need to get its error output and decode it. This info is not exposed through standard APIs.
ots_traceTransactionExtract all variations of calls, contract creations, and self-destructs and return a call tree.This is an optimized version of tracing; regular tracing returns lots of data, and custom tracing using a JS tracer could be slow.
ots_getBlockDetailsTailor-made and expanded version of eth_getBlock* for the block details page in Otterscan.The standard eth_getBlock* is quite verbose and it doesn't bring all info we need. We explicitly remove the transaction list (unnecessary for that page and also this call doesn't scale well), log blooms, and other unnecessary fields. We add issuance and block fees info to the response.
ots_getBlockDetailsByHashSame as ots_getBlockDetails, but it accepts a block hash as a parameter.
ots_getBlockTransactionsGet paginated transactions for a certain block. Also remove some verbose fields like logs.As the block size increases, getting all transactions from a block at once doesn't scale, so the first point here is to add pagination support. The second point is that receipts may have big, unnecessary information, like logs. So we cap all of them to save network bandwidth.
ots_searchTransactionsBefore and ots_searchTransactionsAfterGets paginated inbound/outbound transaction calls for a certain address.There is no native support for any kind of transaction search in the standard JSON-RPC API. We don't want to introduce additional indexer middleware in Otterscan, so we implemented an in-node search.
ots_getTransactionBySenderAndNonceGets the transaction hash for a certain sender address, given its nonce.There is no native support for this search in the standard JSON-RPC API. Otterscan needs it to enable navigation between nonces from the same sender address.
ots_getContractCreatorGets the transaction hash and the address which created a contract.No way to get this info from the standard JSON-RPC API.

Method details

Some methods include a sample call so you call try it from the CLI. The examples use curl and assume you are running rpcdaemon at http://127.0.0.1:8545.

ots_getApiLevel

Very simple API versioning scheme. Every time we add a new capability, the number is incremented. This allows for Otterscan to check if the Erigon node contains all API it needs.

Parameters:

<none>

Returns:

  • number containing the API version.

ots_getInternalOperations

Trace internal ETH transfers, contract creations (CREATE/CREATE2) and self-destructs for a certain transaction.

Parameters:

  1. txhash - The transaction hash.

Returns:

  • array of operations, sorted by their occurrence inside the transaction.

The operation is an object with the following fields:

  • type - transfer (0), self-destruct (1), create (2) or create2 (3).
  • from - the ETH sender, contract creator or contract address being self-destructed.
  • to - the ETH receiver, newly created contract address or the target ETH receiver of a self-destruct.
  • value - the amount of ETH transferred.

ots_hasCode

Check if an ETH address contains a deployed code.

Parameters:

  1. address - The ETH address to be checked.
  2. block - The block number at which the code presence will be checked or "latest" to check the latest state.

Returns:

  • boolean indicating if the address contains a bytecode or not.

Example 1: does Uniswap V1 Router address have a code deployed? (yes, it is a contract)

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0", 
    "id": 1, 
    "method":"ots_hasCode",
    "params":
      [
        "0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95",
        "latest"
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}

Example 2: Does Vitalik's public address have code deployed to it? (no, it is an EOA)

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0",
    "id": 1,
    "method":"ots_hasCode",
    "params":
      [
        "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        "latest"
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": false
}

ots_traceTransaction

Trace a transaction and generate a trace call tree.

Parameters:

  1. txhash - The transaction hash.

Returns:

  • object containing the trace tree.

ots_getTransactionError

Given a transaction hash, returns its raw revert reason.

The returned byte blob should be ABI decoded in order to be presented to the user.

For instance, the most common error format is a string revert message; in this case, it should be decoded using the Error(string) method selector, which will allow you to extract the string message.

If this is not the case, it is probably a Solidity custom error, so you must have the custom error ABI in order to decode it.

Parameters:

  1. txhash - The transaction hash.

Returns:

  • string containing the hexadecimal-formatted error blob or simply a "0x" if the transaction was successfully executed. It returns "0x" if it failed with no revert reason or out of gas, so make sure to analyze this return value together with the transaction success/fail result.

Example: get the revert reason of a random Uniswap v3 transaction spotted in the wild.

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0",
    "id": 1,
    "method":"ots_getTransactionError",
    "params":
      [
        "0xcdb0e53c4f1b5f37ea7f0d2a8428b13a5bff47fb457d11ef9bc85ccdc489635b"
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000135472616e73616374696f6e20746f6f206f6c6400000000000000000000000000"
}

ABI-decoding this byte string against Error(string) should result in the "Transaction too old" error message.

ots_getBlockDetails

Given a block number, return its data. Similar to the standard eth_getBlockByNumber/Hash method, but optimized.

Parameters:

  1. number representing the desired block number.

Returns:

  • object in a format similar to the one returned by eth_getBlockByNumber/Hash (please refer to their docs), with some small differences:
    • the block data comes nested inside a block attribute.
    • the transactions attribute is not returned. We remove the transaction list entirely to avoid unnecessary network traffic.
    • the transaction count is returned in a transactionCount attribute.
    • the logsBloom attribute comes with null. It is a byte blob that is rarely used, so we cap it to avoid unnecessary network traffic.
    • an extra issuance attribute returns an object with the fields:
      • blockReward - the miner reward.
      • uncleReward - the total reward issued to uncle blocks.
      • issuance - the total ETH issued in this block (miner + uncle rewards).
    • an extra totalFees attribute containing the sum of fees paid by senders in this block. Note that due to EIP-1559 this is NOT the same amount earned by the miner as block fees since it contains the amount paid as base fee.

ots_getBlockTransactions

Gets paginated transaction data for a certain block. Think of an optimized eth_getBlockBy* + eth_getTransactionReceipt.

The transactions field contains the transaction list with their bodies in a similar format of eth_getBlockBy* with transaction bodies, with a few differences:

  • the input field returns only the 4 bytes method selector instead of the entire calldata byte blob.

The receipts attribute contains the transactions receipt list, in the same sort order as the block transactions. Returning it here prevents the caller from making N+1 separate calls (eth_getBlockBy* and eth_getTransactionReceipt).

For receipts, it differs from the eth_getTransactionReceipt object format:

  • logs attribute returns null.
  • logsBloom attribute returns null.

ots_searchTransactionsBefore and ots_searchTransactionsAfter

These are address history navigation methods. They are similar, but ots_searchTransactionsBefore searches the history backward and ots_searchTransactionsAfter searches forward a certain point in time.

They are paginated, so you MUST include a page size. Some addresses like exchange addresses or very popular DeFi contracts like a Uniswap router will return millions of results.

They return inbound (to), outbound (from) and "internal" transactions. By internal it means that if a transaction calls a contract and somewhere in the call stack it sends ETH to the address you are searching for or the address is a contract and it calls a method on it, the transaction is matched and returned in the search results.

Parameters:

  1. address - The ETH address to be searched.
  2. blockNumber - It searches for occurrences of address before/after blockNumber. A value of 0 means you want to search from the most recent block (ots_searchTransactionsBefore) or from the genesis block (ots_searchTransactionsAfter).
  3. pageSize - How many transactions it may return. See the detailed explanation about this parameter below.

Returns:

  • object containing the following attributes:
    • txs - An array of objects representing the transaction results. The results are always returned sorted from the most recent to the oldest one (in descending chronological order).
    • receipts - An array of objects containing the transaction receipts for the transactions returned in the txs attribute.
    • firstPage - Boolean indicating this is the first page. It should be true when calling ots_searchTransactionsBefore with blockNumber == 0 (search from latest); because the results are in descending order, the search from the most recent block is the "first" one. It should also return true when calling ots_searchTransactionsAfter with a blockNumber which results in no more transactions after the returned ones because it searched forward up to the tip of the chain.
    • lastPage - Boolean indicating this is the last page. It should be true when calling ots_searchTransactionsAfter with blockNumber == 0 (search from genesis); because the results are in descending order, the genesis page is the "last" one. It should also return true when calling ots_searchTransactionsBefore with a blockNumber which results in no more transactions before the returned ones because it searched backwards up to the genesis block.

There is a small gotcha regarding pageSize. If there are fewer results than pageSize, they are just returned as is.

But if there are more than pageSize results, they are capped by the last found block. For example, let's say you are searching for Uniswap Router address with a pageSize of 25, and it already found 24 matches. It then looks at the next block containing this address's occurrences and there are 5 matches inside the block. They are all returned, so it returns 30 transaction results. The caller code should be aware of this.

Example: get the first 5 transactions that touched Uniswap V1 router (which includes the contract creation).

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0",
    "id": 1,
    "method":"ots_searchTransactionsAfter",
    "params":
      [
        "0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95",
        0,
        5
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "txs": [
      {
        "blockHash": "0x06a77abe52c486f58696665eaebd707f17fbe97eb54480c6533db725769ce3b7",
        "blockNumber": "0x652284",
        "from": "0xd1c24f50d05946b3fabefbae3cd0a7e9938c63f2",
        "gas": "0xf4240",
        "gasPrice": "0x2cb417800",
        "hash": "0x14455f1af43a52112d4ccf6043cb081fea4ea3a07d90dd57f2a9e1278114be94",
        "input": "0x1648f38e000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498",
        "nonce": "0x6",
        "to": "0xc0a47dfe034b400b47bdad5fecda2621de6c4d95",
        "transactionIndex": "0x71",
        ...
  }

ots_getTransactionBySenderAndNonce

Given a sender address and a nonce, returns the tx hash or null if not found. It returns only the tx hash on success, you can use the standard eth_getTransactionByHash after that to get the full transaction data.

Parameters:

  1. sender - The sender ETH address.
  2. nonce - The sender nonce.

Returns:

  • string containing the corresponding transaction hash or null if it doesn't exist.

Example: get the 4th transaction sent by Vitalik's public address (nonce == 3).

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0",
    "id": 1,
    "method":"ots_getTransactionBySenderAndNonce",
    "params":
      [
        "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
        3
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x021304206b2517c3f8f2df07014a55b79aac2ae097488fa807cc88eccd851a50"
}

ots_getContractCreator

Given an ETH contract address, returns the tx hash and the direct address who created the contract.

If the address is an EOA or a destroyed contract, it returns null.

Parameters:

  1. address - The ETH address that may contain a contract.

Returns:

  • object containing the following attributes, or null if the address does not contain a contract.
    • hash - The tx hash of the transaction who created the contract.
    • creator - The address which created the contract. Note that for simple transactions that directly deploy a contract this corresponds to the EOA in the from field of the transaction. For deployer contracts, i.e., the contract is created as a result of a method call, this corresponds to the address of the contract which created it.

Example: get the address which deployed the Uniswap V3 Router contract.

Request:

curl \
  -X POST \
  -H "Content-Type: application/json" \
  --data '
  {
    "jsonrpc":"2.0",
    "id": 1,
    "method":"ots_getContractCreator",
    "params":
      [
        "0xE592427A0AEce92De3Edee1F18E0157C05861564"
      ]
  }' \
  http://127.0.0.1:8545

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "hash": "0xe881c43cd88063e84a1d0283f41ee5348239b259c0d17a7e2e4552da3f4b2bc7",
    "creator": "0x6c9fc64a53c1b71fb3f9af64d1ae3a4931a5f4e9"
  }
}

ots2 namespace spec

These are the docs for the experimental Otterscan v2 APIs. For Otterscan 1 APIs docs, click here.

All those methods are subjected to be changed during Otterscan 2 alpha period, so we didn't put much effort into documenting them yet.

They are briefly described here so you can have a glimpse of what we aim to for future versions of Otterscan.

There may be more methods added, removed, merged, etc.

Method summary

All methods are prefixed with the ots2_ namespace in order to make it clear it is vendor-specific and there is no name clash with other same-name implementations.

NameDescription
ots2_getAllContractsListGets a paginated list of deployed contracts.
ots2_getAllContractsCountGets the total count of deployed contracts.
ots2_getERC20ListGets a paginated list of ERC20 contracts.
ots2_getERC20CountGets the total count of ERC20 contracts.
ots2_getERC721ListGets a paginated list of ERC721 contracts.
ots2_getERC721CountGets the total count of ERC721 contracts.
ots2_getERC1155ListGets a paginated list of ERC1155 contracts.
ots2_getERC1155CountGets the total count of ERC1155 contracts.
ots2_getERC1167ListGets a paginated list of ERC1167 minimal proxy contracts.
ots2_getERC1167CountGets the total count of ERC721 minimal proxy contracts.
ots2_getERC4626ListGets a paginated list of ERC4626 vault contracts.
ots2_getERC4626CountGets the total count of ERC4626 vault contracts.
ots2_getERC1167ImplGets the logical contract associated to an ERC1167 minimal proxy.
ots2_getAddressAttributesGiven an address, returns a summarize list of attributes associated to that address, e.g., is it an ERC20? a minimal proxy?
ots2_getERC20TransferListGets a paginated list of transactions that contain ERC20 transfers from/to a certain address.
ots2_getERC20TransferCountGets the total count of transactions that contain ERC20 transfers from/to a certain address.
ots2_getERC721TransferListGets a paginated list of transactions that contain ERC721 transfers from/to a certain address.
ots2_getERC721TransferCountGets the total count of transactions that contain ERC721 transfers from/to a certain address.
ots2_getERC20HoldingsGiven a certain address, returns all ERC20 that have interacted with it.
ots2_getWithdrawalsList (since alpha 2.1)Gets a paginated list of consensus layer withdrawals to a certain recipient address. Note that 1 block can have multiple withdrawals for the same address, each one is counted individually
ots2_getWithdrawalsCount (since alpha 2.1)Gets the total count of consensus layer withdrawals to a certain recipient address.

Contract verification

In this chapter we explain how contract verification works in Otterscan.

💡 If you are not interested in how contract verification works under the hood, you can skip this chapter.

We use Sourcify and the next section explains how the integration works.

In case you want to run Sourcify against a devnet or a custom chain not enabled in Sourcify's official hosted service, you can run your own instance of Sourcify and point Otterscan to it.

Sourcify

Otterscan consumes verified smart contract source code and metadata from Sourcify.

Sourcify is open source, their data is public and they host a public instance of the verifier.

Otterscan supports multiple ways to consume verified contract data.

To learn how to verify a smart contract on Sourcify, take a look at the Sourcify docs:

Integration modes

Otterscan supports multiple ways to consume Sourcify data, each one with pros and cons:

Direct HTTP connection to Sourcify's repository

Standard HTTP connection to the official Sourcify repository at https://repo.sourcify.dev/

Fast access to freshly verified data. On the other hand, it is centralized and leaks all addresses you visit in Otterscan to the Sourcify server.

💡 This is the default method Otterscan uses out of the box.

IPFS

We resolve the public Sourcify IPNS to get the latest known IPFS root hash of their repository.

The downside is that recently verified contracts may not have been added yet to the root hash and republished into IPNS.

It uses the public gateway at https://ipfs.io by default.

Please see our ipfs integration docs for more info about how we handle all IPFS integrations and privacy concerns.

Local snapshot (deprecated; soon to be removed)

☠️ This method is deprecated and will be removed in the future. The repository is outdated and kept only for historical reasons.

As a midterm solution, we are making available a snapshot docker image of their repository, containing only mainnet full verified contracts.

This would allow you to play with existing contracts up to the snapshot date/time locally, not depending on their service or IPFS connectivity availability.

The Sourcify snapshot is provided as a nginx image at: https://hub.docker.com/repository/docker/otterscan/sourcify-snapshot

You can run it with:

docker run \
  --rm \
  -d \
  -p 3006:80 \
  --name sourcify-snapshot \
  otterscan/sourcify-snapshot:2021-09

Stop it with:

docker stop sourcify-snapshot

Self-hosted Sourcify

This section presents a short guide to self-host a local Sourcify instance.

Set up a local Sourcify instance.

Verify a contract on a Sourcify instance.

Setup

Run a local Sourcify instance on your dev network

Launch a local chain

Launch an Otterscan-compatible local Ethereum RPC node hosting your development server.

  • Erigon:
    • ./erigon \
        --chain=dev \
        --datadir=dev \
        --http.api eth,erigon,trace,ots,ots2 \
        --http.corsdomain "*" \
        --http.vhosts "*" \
        --mine \
        --fakepow
      
    • Note: The Erigon devnet only supports a pre-Shanghai (pre-Merge) version of Ethereum. When compiling your contracts, you'll have to set the evmTarget to london for them to run at all on the devnet.
      • You'll have to create a full-fledged Ethereum test network in Erigon if you want to run a modern version of the EVM.
  • Foundry's Anvil:
    • anvil
    • Note: Anvil does not support ots2 RPC methods, so make sure experimental is set to false in your Otterscan config.

Sourcify

  1. Clone the Sourcify repository:

    git clone https://github.com/ethereum/sourcify.git
    
  2. To add support for your local blockchain, create a JSON file services/server/src/sourcify-chains.json:

    {
      "1337": {
        "sourcifyName": "Local chain",
        "supported": true,
        "rpc": [
          "http://localhost:8545"
        ]
      }
    }
    

    You can adjust the chain ID 1337 and the RPC host http://localhost:8545 as needed. The default chain ID for the Erigon devnet is 1337, and the default for Anvil is 31337.

  3. Adjust the repository URL in ui/.env.development since for simplicity we aren't using Sourcify's h5ai-nginx file viewer:

    REACT_APP_REPOSITORY_SERVER_URL=http://localhost:5555/repository
    
  4. Build all necessary Sourcify components, notably the server and the UI:

    npm run build:clean
    
  5. Start the Sourcify server:

    NODE_ENV=development npm run server:start
    
  6. Start the Sourcify UI:

    npm run ui:start
    

Otterscan configuration

To use the local Sourcify instance, you need to point to it in Otterscan's configuration file. In your Otterscan configuration JSON, specify your local Sourcify repository as the Sourcify source:

"sourcifySources": {
  "ipfs": "http://localhost:5555/repository",
  "central_server": "http://localhost:5555/repository"
}

Verification

Verifying through the Sourcify UI

You can verify contracts using the Sourcify UI by going to http://localhost:3000/#/verifier in your browser.

Verifying with Forge

  1. If you deploy using a Forge script, you should add the following contract verification arguments to the command:
forge script Deploy.s.sol .... \
  --verify \
  --verifier sourcify \
  --verifier-url http://localhost:5555
  1. If you want to deploy a contract without a script, you can use forge create to create the deployment transaction. Create a folder named contracts and put your contract files in there.

Here is how you would deploy a smart contract called MyContract with constructor arguments 0x67b1d87101671b127f5f8714789C7192f7ad340e and 123456:

./forge create \
  --verify \
  --verifier sourcify \
  --verifier-url http://localhost:5555/ \
  --interactive \
  --optimize \
  --rpc-url http://localhost:8545/ MyContract \
  --constructor-args 0x67b1d87101671b127f5f8714789C7192f7ad340e 123456
  1. To verify a contract that already has been deployed, you can use forge verify-contract. Here is an example of a verification of the above MyContract contract example to address 0xADC11306fcD68F47698D66047d923a52816Ee44F. Forge can usually infer constructor arguments automatically:
./forge verify-contract \
  --verifier sourcify \
  --verifier-url http://localhost:5555/ \
  --optimizer-runs 200 \
  --rpc-url http://localhost:8545/ 0xADC11306fcD68F47698D66047d923a52816Ee44F MyContract

Public instances

Otterscan is meant to be run in your own environment (see install instructions).

However, we host some testnet instances as a showcase of our features:

Third parties

Test in Prod

Test in Prod, the makers of OP-Erigon, also host instances for Optimism L2:

Ephemery

Ephemery has a public instance pointing to the latest iteration at https://otter.bordel.wtf/.

Commercial offerings

Some node providers offer their customers access to the Otterscan API. Please note that we are not affiliated with these providers, so you should review their terms of business:

Cookbook

This section presents a series of articles related to Otterscan which do not necessarily fit in the other chapters.

Running Otterscan on Pectra devnet-3

At the time of this writing (Sep 2024), the next expected Ethereum hardfork is Pectra and devnet-3 is up.

💡 This document may be updated for future devnets as Erigon becomes stable enough to sync them.

It is possible to run Otterscan against this devnet with some known limitations.

  • Pectra-specific features are still in development in Otterscan. Otterscan should work fine, but EIP-7702 transactions may not display correctly or may generate UI errors, for example.
  • It requires a dev build of Erigon 3 alpha.
  • Caplin (Erigon internal CL) still doesn't support Electra. For CL, it requires a dev build of Lighthouse.

Get the chainspec

  • Checkout the pectra-devnets repository.
    git clone https://github.com/ethpandaops/pectra-devnets
    
  • Set $PECTRA_DEVNETS to be this pectra-devnets directory.

Run Erigon

  • Checkout and build the docker_pectra branch of Erigon.
    git clone -b docker_pectra https://github.com/erigontech/erigon
    cd erigon
    make erigon
    
  • Initialize the chaindata with the devnet-3 spec. Consider $ERIGON_DATADIR to be the EL datadir.
    build/bin/erigon init --datadir=$ERIGON_DATADIR $PECTRA_DEVNETS/network-configs/devnet-3/metadata/genesis.json
    
  • Run erigon as usual.
    build/bin/erigon \
      --datadir $ERIGON_DATADIR \
      --networkid 7011893082 \
      --bootnodes="enode://ff18fe4e41b74b76faa14b8f6070627d2600055b7c2c07eab18c33b78aaa7a1e6cf251743da9fec363e679a237e7a380155be9d87e5e9b7ef1249f383fa82133@209.38.242.185:30303?discport=30303,enode://ee7d7f43463297d97fa5875bba43892db814be0b67c5192c100deb28312e4678d996d86c4fcb09b73d904dccc485c8e8ecf3bc3edc4cd02edb1155cdc6da255c@164.92.203.70:30303?discport=30303,enode://c936cb7124853fdfc5adbef42011382f28d099b4a72c4681ed9716a1070c7d91d0a41c52c1e971ee3f0848e26c67980010426837506d7e4219e3cedd1d976566@167.99.248.153:30303,enode://a3c9e658072ab8c4efb078738d97bcd22b296f3dd977a35b3b6aa48df670a8c3b8319324488b204c49a2b799a6ae42b2ab02809951bfac9acb0e2969e7521c5f@165.227.165.102:30303?discport=30303,enode://594963a9985d2ef5ee219c64c1017c407c0e665b775ee8a6b68595ae2f5f53f6875990949678ec9d54cee00bb98c9e79e0d03a31695c22a77454fc43d4af7d42@167.71.61.219:30303?discport=30303,enode://929afd46af9ec14e6baca8ab78ddc301501a321cbdd5716a4df3f779777fbbab52beeae2c175adde7e962036969cd83c7cbca4201782a9c7f61c85ecdfcf5ca4@159.65.113.238:30303?discport=30303,enode://31ee342e249b9accc098a171a034545136f899c46ac8c155581bc699f7fcdabc8372f534b4db259b7fbfed369a5ad2e0fc6a5baebc7d70be1808799c3a1bf111@207.154.253.142:30303?discport=30303,enode://97ccc7f93a52b22a0dbe94e5a8482f68eca5cbdec21bbe9061a72c4e1aab41cc8a38200828e5f4a1db509a0a1ec431e2c5ba208ceb8b7c5bb06af9f24e0ebb10@167.172.163.43:30303,enode://19dbac34a3a0d97fcffad075a222ad2eb2c95913759b48f540db305c2c2910a60393ab96e18835d8c2a994bc5c773d339848a8b11441066834bbf5328c08400d@134.209.245.90:30303?discport=30303,enode://a0ea666286918488d36ab9e1dd06c2780e566a48474894246e0d8434915b8d5b161275e300907174b4b93f408e2681aa58afae8f4202a0ee170f7d37a1b3fbba@159.223.221.12:30303?discport=30303,enode://4dc25297111d77f3aa8f3bddbef5606b35e57efd6c4d11fb8bd64b0b7259436b0404f013e939adbd732ebb894c3e640327bbfbee70b0779ba4005acd2df9f558@206.189.21.79:30303?discport=30303,enode://4116e37a44e1d7c15f2b66c1d6b262b73d996bc39bf67ad84fa2b31957242b76f54e8204e18fc046ccdf214a72064aee47aa7be4dd583a17990b379b5fa27314@142.93.209.238:30303?discport=30303,enode://3bc854243c4a17c67b55c0339d46e20cdbc121dead795b6a1cc3f2e99f5402078dbc848b73b1ef55c56ce15e2e23d59ca164b205b61059dbaba451085ae672fd@144.126.220.230:30303?discport=30303,enode://0ad64eb3d40c5339280916e9802134ad97d8b4834d8530cafc91595a3c8b3ace1e57e2bfcd4cccc3dbdc622a6bf529aec5e8d8a1ff09d404bb40ecadea061d46@170.64.206.111:30303?discport=30303,enode://423e0236cb3d0a985ff64cc6fff1ef0ac85be32cd7ed848030afa42dbef28c9c66c93849c300e77ec5bb85bc48d528955d1f368158879d316f81ada6152be915@165.227.153.225:30303?discport=30303,enode://508d211acdfd5f8546261cc9a3a57994d1315c1a978e686a14a6daa69ad9fa8c38c3009e05327005594b9d4027c1547c7ecab6443bdb2681fac4e8027234a755@207.154.214.116:30303?discport=30303,enode://353ab4e06d50adb15aefbc406292d1bd164548127ec7fec0ea6e50aba94fab951140b77d14ccf6f69f94281b224f3ae9c5b3d3ac26424c84c57be87370c8cac2@46.101.140.145:30303?discport=30303,enode://6ef4f8403989cbdb27dbddb3e446c65e3ed280656f8c6d91fd4f2ff7e7911c80c3f6544a7c1fcc461c0b43b7001b62e487e32e5eb991cc26823b2c0a8d6bff28@161.35.67.216:30303,enode://4b809fdf0a1fe50e7e14e5bb2eede1991a5be561637af94ab5e6aca861688112f9529d26c9ce1e8899d4452b481b67c926db04310f131dc685084a1bbc9b7818@164.92.185.229:30303?discport=30303,enode://5a94a260ff96bb3ef9b80a1745272e3f9fadd9fd6635109a6234f25eb8634b2ead1685286be0f9280e16d82e2b17e745462cb3664511628f780c48b2603030ab@165.227.169.145:30303?discport=30303,enode://9be7b72a6928df50a77ac7b9885a83b73c4a3e6510fddfc728db7ac38bbf0f76bfef1ee0ba32179cf71ee520b8e2c8959286e3a16689542019939c67c899b051@157.230.110.242:30303?discport=30303,enode://80a795999356f9eed541d214c083b1a1dded6f8d0c49f092745d5f28c94b184bdfcdeef97d7e7702080b070945256a0e1983b4aa9fd4e45615513814c76ddc8e@64.226.89.31:30303?discport=30303,enode://c29f55177e16e65445262f15259b62cacca6518a1f185e3ebe9659b7205a05af91d74a5a481296c76e45c8383bb1c4c29fe12f8c4c92f8e2b87e98b5673b2c25@164.92.141.167:30303,enode://b3530cf2e13fcfab20b1a3f72d099bdb970ffdc29a08308418c596ef291b306e1eeac6868a1f76cbcc6d01d85206391607c6d458c35c5aa7214b5695d38a819a@46.101.141.131:30303?discport=30303,enode://4030dc9c14b0cf8e2dee47f2015b014963062c7e6887039faeaf377ee7c09f0f19946973b971b44b672f972ac1eaa25bb1faedb7ac9b0e66fe4be5b0cd880ac7@46.101.96.239:30303?discport=30303,enode://6a1f5cd24c920324267b4b2f19811f6e01017e8018e77ee10d4cbffad6f02102ee14ff75ef10ebab1e51af88582dca33cfc1983a63f6c26072ad32a34be3ac63@209.38.228.221:30303?discport=30303,enode://80c64bb580051177fc0f849882939e66483b026d4da1b1ca307721cbfba7789b64429ca1f871fc2f22180c825aeece86a08fcbcfbba247304314053834541eae@165.227.153.218:30303?discport=30303,enode://ba8bca3076e508d80fb6fb1eda641a0dfaf40e7b36dbbfe39550c9238b1e098f29d3449d2ab29ed3af3650309a9d7132135a514b4c81042d6aaf34b51f3de26a@165.22.94.213:30303,enode://2aed8046b67279046109473dab173520882b4a8e78ab88173064bad8af7d2e36e122593ee54db79636099dcc6cec22f66970c37017a34953da5d32091cd9eb27@159.223.16.43:30303?discport=30303,enode://5de20a20ed93faa30dca8ea11c0e4c8c81414c20004a196733f506ffef9559b874e15c9fc6aa80cfdf2ef3d51b9922be94eafa0be08040d858aa88c64ae40b85@161.35.89.132:30303?discport=30303,enode://a9d816fabf48075cef072d14b8c55ac5e6a435f2126dfe77414c9fc7a2415f1f957cddb275683c887056449e40260ec38155619bfb5373d13746289d81a56d19@209.38.164.8:30303?discport=30303,enode://5f9ec2e85ebd646a9d40c60e0827b08a67add8be2f070a52cd79e469e85136d178387c9ba83810f45ea03d247fbe8eadfeef3b55ace60ddc3fef90f2d12323db@64.227.160.6:30303?discport=30303,enode://cf83258050e2de79d84c00048fd1921e2696eac931fca715c1b137c617f79a96db06ab86c6c5b9d5df7bc5cade9bdbe2f511ecb12ccf72d8c8572322d9b2ed7e@143.110.152.53:30303?discport=30303,enode://c746f68fd154d5452c79f373818d421c4845517ef81acbcdc7c1ba406dca7a9d11d01b5fd68115a62534cdd3b6bfb25e64ee31e4a6935197a491f30b4a638973@170.64.191.178:30303?discport=30303,enode://c28d15fd697493aadc201babed7b492cf0ea5486a011014a05048511816735bc1357330fb1b0c3a5df120d5e3c210894cdf18ee21af7b1da19d1f664884bc3e7@161.35.22.150:30303?discport=30303,enode://75196a118fc1cd9ed57be21f8e0e28560ebe338870d18d813c56aec82792216c2a5eb6e863d6fc45cb3235d9ccfdd66e452e983a929c8b272f22021b8c7d4d78@64.227.114.235:30303?discport=30303,enode://14fedd050d69d45a88bb42fcef3a9f579673326a69bce20856cd50097ff9228fde5e95c2c495c7bf712f812a6c3c5a71e4aa67ff58748cbd45eff719716e78fb@104.248.135.247:30303?discport=30303,enode://446941a7a1e3a884ddee74b72ca7e28524004b5740075e87dff565c949a8b0860cd86614faf7102ffbf7f2448506b90acb618d9e6417658a99e4e2364d3c0b21@138.68.73.9:30303,enode://d7493068bb71ae30561166c5964cebad13a9190b53d31edaaa82a78f938bc87d6b744efa0d493a2fbfc11ee5238a028e1f60fce7978631c3d992721440e0f8e2@207.154.214.127:30303?discport=30303,enode://c3423c7f56a199a0e31f78fa9f3e31db8e6c8a81e4ee6a40e96180a04d07e8dfb185caf7447ff1b842fe44a1f2cba3cd73a818ab12a31009fcea2cc9d597a17e@167.99.37.51:30303?discport=30303,enode://9fbbc9b3b6bc15d3615efbf076803ce92ae947f72ed3bb548efe62a0c602104eb1889c8fde40b100b32a16106fde7df8e8b4f5c1be3e5bcddd062e8563480532@138.68.166.32:30303?discport=30303,enode://546d77f608eda362b831e319f5f86af4b26793a93e9bd464881fa9911322ed8889099f1bfdb4694abd45689bd47716e26ecf8c5539824d4d6cbe55731344c975@128.199.21.177:30303?discport=30303,enode://12d5a619b426b73c332895b143d2e8f34000845daa121dbd22f97fd1ba72e0901493c5cddd9b6c59d36cf27d89c58d05a88c3e100634d841dc9a8e6d754309bb@24.199.118.171:30303?discport=30303,enode://22a4bb02de3c1baa0509d03f5fb122b123a8a225955b3201bb0492f73f76e66299b716194cee852ab0a347a717506327cd35d8003cad17440228a94506b01de8@209.38.24.26:30303?discport=30303,enode://5348e1cbc8f9962fbedaf08a1466e6890ca1501f6f7b4bd28304934948fcc8b04acaac776cc06460884559921f6f82d1cb2c5090a8c46d29ff32c82b8ec4e148@207.154.199.155:30303?discport=30303" \
      --externalcl \
      --http.api "eth,erigon,ots" \
      --http.corsdomain '*'
    

Note: the bootnodes are taken from here

Run Lighthouse

  • Check out and build the unstable branch of Lighthouse.
    git clone -b unstable https://github.com/sigp/lighthouse
    cd lighthouse
    make
    
  • Run lighthouse as usual. Consider $LH_DATADIR is the CL datadir.
    target/release/lighthouse bn \
      --testnet-dir $PECTRA_DEVNETS/network-configs/devnet-3/metadata/ \
      --datadir $LH_DATADIR \
      --checkpoint-sync-url https://checkpoint-sync.pectra-devnet-3.ethpandaops.io/ \
      --genesis-backfill \
      --disable-backfill-rate-limiting \
      --execution-endpoint "http://localhost:8551" \
      --execution-jwt $ERIGON_DATADIR/jwt.hex \
      --boot-nodes="enr:-Iq4QH9YP9c_ZBYZNAGJpeAp8bHo2at4Xxlv9346r6jcN2M1HOTJzyIcT55UivXY28xBRHpaur4-mmsx-ac9IozcxcGGAZHg-RR9gmlkgnY0gmlwhKEjS0eJc2VjcDI1NmsxoQJJ3h8aUO3GJHv-bdvHtsQZ2OEisutelYfGjXO4lSg8BYN1ZHCCIzI,enr:-LK4QHcwaTAj-IHTdgb2468cyWcOvwe1w5v8a70g6H-rwecXGqWMDaWJhaHk5CX6zabkWxZN534FjJGJLzqQwpREFnQFh2F0dG5ldHOIAAAAYAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKEjS0eJc2VjcDI1NmsxoQJGSG1iaWxtD7wAKUwwL6N1ZP-L5ZSoxOG0UFxu7ZO-sYN0Y3CCIyiDdWRwgiMo,enr:-Mm4QMK3bYqJNCGe3MQtnYQq3VKlQSyyyIf3YYElyxUyDC1PIctaPOwvhKL9BWFzo5j4-XQ6bEx6DBQF8cpQvzllq_kEh2F0dG5ldHOIAAAAGAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCE0SbyuYRxdWljgiMpiXNlY3AyNTZrMaEDj7zD5JDPNJkalSpukdLnZikjS9NoLBxEL_pCoGmchbCIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QBiYB_dqgCIuzQuntX1QJ0ompnQesuG76kzt41dz3K1iSx-xAvRq7Mr6_vVZGcj3RgrGMY7SGb-j0Ns-Kq-dAGUEh2F0dG5ldHOIAAAAAAAAAAaDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEpFzLRoRxdWljgiMpiXNlY3AyNTZrMaEDTnDF9m2CDL51ouYIc3wBPGYjCg_yqKP2EiaIn45LC6OIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QEesrCH4Kpe7uSNmAgwsTjDr1X796LFMdhYWmS6SLcujF-UknSDUEOJ-dymGBAYaWhsgesbRbE78ALic1bV37pwEh2F0dG5ldHOIBgAAAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEp2P4mYRxdWljgiMpiXNlY3AyNTZrMaEDCo_1OXqDsG9ETOi8cZ6iSylpMmxqYUXB8s4vfXAo0qGIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QIGJsArPpB7jrx797b-L3YtcjnkJXKDKMvwQ8F8M4DXURXeOCmUrofHu_XmiYAm5uKtfYnrqRxsuULXKwez3HuQEh2F0dG5ldHOIAMAAAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEpeOlZoRxdWljgiMpiXNlY3AyNTZrMaEDUwSln3m97ldBcHhH2bGkfwihfUWb2SPdyQV8XVLWnvSIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QLsPYu0a1FVXdjwmWxCUEV5CEjj9gwW47bi1NC6gOK3IaoNgrKrtvc7I_T5UuwHdlkaSYqfvIk1YLUi-iuGsi5sEh2F0dG5ldHOIBgAAAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEp0c924RxdWljgiMpiXNlY3AyNTZrMaECoAsFP5EJ-GSz9o6dtJbSB-ULdjphFi3brdyx1QfA7bWIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QLPeqXNPNg18MMmeuuqEYYWaS30KDrwqEL2gV8sUNXdKXd3VkwReQ-1SZ5TGkqTAOfoHMRwt0fZRBlxRnQOA8dIEh2F0dG5ldHOIAAAAAAAAYACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEhnpe84RxdWljgiMpiXNlY3AyNTZrMaECBIcRRir8-AAvet_y_6RJ9EPw16V_UUw_3yMANOh8XQSIc3luY25ldHMAg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QNd80QG-lLQMjY6wNDQNBKjLbRWvczDzvQx7VVGrMzpoeG27y59OjwhkWAIdYnbgyP4gITH9d3cg4crezUT3afgIh2F0dG5ldHOIAIABAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEn0Fx7oRxdWljgiMpiXNlY3AyNTZrMaEDm2s7GQJ7LJXgSCp0mkMWJwu-fKVPcI7umazrmh61dmuIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QD8SJ3WvRuG2i8effDaqvMhkbmiTWsLK2rCU5EaPMCW7P_l2IatxdsTLUrSxS5bSQmINohm07-mvo1TVWCymdikIh2F0dG5ldHOIAAAAAAAAADCDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEz5r9joRxdWljgiMpiXNlY3AyNTZrMaEDLU7_CmCPjnPoA-GODszqXyOEogLlnKIMUG_o29yCwfqIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QIwoESRrBq9hz9ODJGfypT9VWS5dcKjbY_ahlgsC0P9bdwpZfW4z0ZPCMNY7wHwSejHdUt-MgFzZ2coSBK5oMdUIh2F0dG5ldHOIAGAAAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEp6yjK4RxdWljgiMpiXNlY3AyNTZrMaEChgY4sODpLFJRwg80XXL9AHTHUYOzvsffRFIp9vhHilWIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QAbBEqLrqLbogLL3pYc9vOulTluk-ly0rhJOiyFPm_8WMu9t7A3-5CoY4XXyEl2jzvtWyAHBQjwtGgYde5S-N1IIh2F0dG5ldHOIAAAAAAAAGACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEhtH1WoRxdWljgiMpiXNlY3AyNTZrMaECJXj2OCPwk7RhEw8Cuxa78O6RO6KCLFNCbO2UDqxJC2iIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QM5fQpzJISCo7D4ZcsbxfAajJlWbqKrkhLcumq1w5tFLdal8JH7zbEuiENEUxuVYHULLaWa8NbhSM9qlrvw9ml8Ih2F0dG5ldHOIAAAAAAAADACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEn9_dDIRxdWljgiMpiXNlY3AyNTZrMaEClUjHxH0euOihEDlpGV77WNBAK2u-oAYWN56nhI-yqDSIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QD0qgam1Ol2K346338i8Fhzoiml4I8u2o6LZRoyuz0fZKFNt4xbBCudEU53nnfDll3agh02IzFPau5YZPauFpKkHh2F0dG5ldHOIAAAAAAAAgAGDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEzr0VT4RxdWljgiMpiXNlY3AyNTZrMaEC-Yf7GuxpT3ImxHaotc6EO0yj3s51ors2P0U3EjvTFzCIc3luY25ldHMOg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QL3jjIRRlWPqr9aDJnWD0Vec0CazOqNqjrhtTOCwK_GULjSjOQwfPmEIeWIWduiYuXvo4a3aL9btsUWvB48H0vsIh2F0dG5ldHOIAAAAAMAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEjl3R7oRxdWljgiMpiXNlY3AyNTZrMaEDYFwh_4j07AsiXv418uqGD5aW3z3Uvakuq8uws2v134qIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QIEd7ToKDICIOOcqw9pPDwdPyygxOYgtshLyVbh5CY4ANGldRGMQuu3bHfLdSD90Qtdm9wpTg9-DGVfdujMi9NMIh2F0dG5ldHOIAAAAAABgAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEkH7c5oRxdWljgiMpiXNlY3AyNTZrMaEDi4-6GEX-ad5RFTgiWzHhf3_HFMnS4IGB71AUXPX-C5-Ic3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QBn_4Y0CebUAX6KP2nEiyFWqukn1b2q6B7OGgWksAgZSU7buuzUqmwNlPfhfS7KqHY-ah9rlj4kL7w_XaWlZOLkHh2F0dG5ldHOIAAAAYAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEqkDOb4RxdWljgiMpiXNlY3AyNTZrMaEDJJzxbJwvoud2-qV0NO3cJ4H_Qb2v4HfT4D15BwEB0n-Ic3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QIsuAUU6TT5oY7Z4vs2H_0UmyF3V8HZrIPaiXDTEuqEPBk_4oC169-ZMYMHhtagOm9GuMcMCuCDoY8n18AfrsH0Hh2F0dG5ldHOIAAAAYAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEpeOZ4YRxdWljgiMpiXNlY3AyNTZrMaEDU_dPSt7zhPfa8yPCUcgOcDMRSyLxaekcEzohEwb45JOIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QCJ1VkqRvuu_2Muh1EoGrw1GVoBIIEKAZzrzYOoUlupBfc5G84G1m7Wic-Ijsy5UhzTypEElHw6HNDtjEyTiGTIIh2F0dG5ldHOIAABgAAAAAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEpFqnF4RxdWljgiMpiXNlY3AyNTZrMaEDJRSqbsdJkB9M_I6WXQ7ORGYDn7SkckHwmNGslx4nKuWIc3luY25ldHMPg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QA_VJo5-6JzU46NV_K7Wk5aB6-Pp1yt1c_4v1gg715-ha9DfvOG7eCcVvNl0D8L4ITZBir4qGspGiD4oUFHRF3sHh2F0dG5ldHOIAQAAAAAAAICDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEhtHHIoRxdWljgiMpiXNlY3AyNTZrMaECVmo4zAZk7GnXoc6vg4OLn8A1W0GgNI-OduRu3FBxb1WIc3luY25ldHMHg3RjcIIjKIN1ZHCCIyg,enr:-Mm4QAs7SBg3KgJI3UtRJSvSViPwz1UIbzmUKdwULme-se0iQsuFItQCxyl3wXjm5iLn3dd2_zj9oSKsMmHwWO30IPMHh2F0dG5ldHOIAAAAAIABAACDY3NjBIRldGgykP6frPVgIWIo__________-CaWSCdjSCaXCEhnpkk4RxdWljgiMpiXNlY3AyNTZrMaEDN3fR1A71Izi6b0ItqEZ6ewWPO-Ivm1PrDShC3Vbms5GIc3luY25ldHMLg3RjcIIjKIN1ZHCCIyg,enr:-Ly4QHCunWAHny5krOh_mkJtnfGOcLxig9qYT3M_jaWmv0GbcbMx-FhhI1sik3MCRCGh3kljEiLJyu6XdqW6Tk2Z9iAGh2F0dG5ldHOIGAAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhM-a1nSJc2VjcDI1NmsxoQJ2kaGOVz1ZrFe1AzhGyoQIwfF5V4J-xNBjVfO5QEcPEYhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QPxLB54OUtVB5YNUfDHPbMAxE8TFi5AJiRUcL_M4S3KvSZ-eSLb6rUpFTLE8PKUVDAfwGJv3uy4VCCDF1edgh8oGh2F0dG5ldHOIAAAAAAYAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhC5ljJGJc2VjcDI1NmsxoQJe6maziE4vCEI0WjXDVv6O1e6FMUn607D82sH2DhJiEYhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QB-4zoObs7jHXc8a1aed3JxRQNnL2dL16UWkd31VtY0VN2c9V_lnZdNJ4rWpq7Z-I3AZw0K9cC1j-Dg36m6PL_EGh2F0dG5ldHOIAAAAAAAAwACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKEjQ9iJc2VjcDI1NmsxoQONzoFh_aCVzVtVpBYV3rp6cUs9NtPw2KSbopbVvbM2bohzeW5jbmV0cweDdGNwgiMog3VkcIIjKA,enr:-Ly4QOukBAUfgTYutX2srxsnoBTS-nkAm79o2HMxn0ytvq0NO9z7if8swDTMHo7XD9Mo0cg8oW1_8lHY_5o-Q1nzNwAGh2F0dG5ldHOIAAwAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKRcueWJc2VjcDI1NmsxoQJv2WXVrYuNrAtuiVDUBbsb9VbYUz-YcbBdrIK1mfCAIYhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QN6APohnbZ8ePtZy0Vj1MGx-DGtVXfbXB7as4TJDFJi7N1OBsttbbNjALNFumcAI5W_n-rwNf7-0uzco5uocbzsGh2F0dG5ldHOIAAAAAwAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKXjqZGJc2VjcDI1NmsxoQPRqcRzfYcAwHOEPxPXUlp0Gk84NByOwdXNkiov1aYWZ4hzeW5jbmV0cw6DdGNwgiMog3VkcIIjKA,enr:-Ly4QCLn9JyYPnCXGfG4SgD5iak85ITYJLchYVd7sGuWro03eU8VI0WYI9e2-g2Wuo2RSfWCgUIio8DxL9pIWWtMFsMGh2F0dG5ldHOIAAAAAAAAAAyEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKRc6_WJc2VjcDI1NmsxoQM8-d1r2yhZXyovSCNbnnXyyyq9XcqgO9o87YDdbRsCQ4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QHPrLSKnxIFgWr9-T2stC5SRKovZHeN9Yc7Uy40-U8iKaH2S6YTDN7idj0mgdCVfrkuud49AzARO0B08iCZMveUkh2F0dG5ldHOIAAAAAIABAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhJ3mbvKJc2VjcDI1NmsxoQKR-IoUBcqPvwi8uRGigNQNshtYauc66gko_vsia-Xx44hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QLL5zzSAkvkZgT9pucH1V0qe_AUnFcV_z6GhTP5KBs2Hf37L0h15KUcrdGx2stYnqbwyJwYiXRVr2oFGcogPs4Akh2F0dG5ldHOIAAAAAAAAABiEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhEDiWR-Jc2VjcDI1NmsxoQMC3AEHcSnAq_6fCXk-69_7QRBe91D6Ak2LzPdDycsVI4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QMLXCEO2Whkh2g6Z9McmpWGHU3t6_J__dttNB1_1cbScUlGU85FXtxPgzfNG2vSexbfG2huewnyn_pxAwj5O5gEkh2F0dG5ldHOIAAMAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKRcjaeJc2VjcDI1NmsxoQN6ytBD7wl6RxboHaOIGBEYaWhHzZFtkJb9II20sfEHaYhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QImBbavEify9WpKR5ecPdISd9_VdAaCJzmFgQl5oTb3wexRePHyP7uIgpvh_eOGDXqqsIWQTEZcgn12onsVPZz8kh2F0dG5ldHOIAAAAAAAMAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhC5ljYOJc2VjcDI1NmsxoQJVqCFRgfbYCUhS8lMRGcihvKMLarznHIGGat1CRWVGD4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QN7De81bi8bry3gqJIBHzw1oJcAkmIwBBrBFVB8o94HiZKZDTZBCdCcsAb9bKwMd3GPF0I5zrkLxbzjWCgUQeyEkh2F0dG5ldHOIwAAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhC5lYO-Jc2VjcDI1NmsxoQO2k-9mrLGoBDQaHf09AHJsaNNQ-SimOcF_QvgvOdfObIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QM16sheh23CS2e0yANx-5pKEEpn86QDuCswnNEYAS3nVWrGRI88FAnNm2uIEzpUnuPcr6rR2Au61g6Ydd3tMUhskh2F0dG5ldHOIAAAAAACAAQCEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhJ3mc3yJc2VjcDI1NmsxoQM4wLPTJcXMDSbkmRkiUdYEGAg5JcoN4v7UfEs7FrNg1YhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QFK8DL1GO6X6wfc9TBxonNrSkTGSWdRJqt8zPA0r_rkkDAHi8SGZZp-NdaJnbStoDDcVLvf-w467vqRw22XIBZ2GAZHhBGmDh2F0dG5ldHOIAMAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhNEm5N2Jc2VjcDI1NmsxoQJVOWjZ3b6YWbWAKeNb6jIIuzP9014H_MexSg2v4RdwTohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QAoNGB5EcDtSSDPwIT0kRQSa7JmwLIavrm-be6kqrevsISkKXf8ITv2vApUXpkkYApabr3KSLDjqSF1R8KrZ5vuGAZHhBGxRh2F0dG5ldHOIAAwAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKXjmdqJc2VjcDI1NmsxoQP5wJZ1KfNQ-Cs1sc4z2Drp53FcvxKM2iw1cehs1JrVMohzeW5jbmV0cw2DdGNwgiMog3VkcIIjKA,enr:-MK4QDbEOKIvuf8kibpRk4pX-pwULgdgacGWev-JFj7eRHKGCitPzAFEV4KuIFdUaVuPRXuy-51mopt0hsaSG2eEA8iGAZHhBG31h2F0dG5ldHOIAAAAAAAAMACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKUWXtWJc2VjcDI1NmsxoQJ0-yIZwTM6a3nYNtwP09jRlv99ycteba0Cy4YWdHeS-IhzeW5jbmV0cweDdGNwgiMog3VkcIIjKA,enr:-MK4QL7WHb0FcSD9CvPxtOWh85zCGks3Ajk0vVXq-lGHQuEpXYm86QXLztTywUmLtv3gwNCdrQtJRqaZZaoUVS4k4GeGAZHhBHBKh2F0dG5ldHOIAAAAAAAAGACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhJ_fECuJc2VjcDI1NmsxoQI6nELvuF2U1ERTTMtnXbfmZQ50QwzBm2AdNjWYcHC24ohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QLV2R-5BfNy2Df1OqfEfiQ-D9p6yKFNDrUQtX_dkKoOeTsdG7QtcCv8LgLKnU5yjNf8mhuOoayedEp8ox4wCqpSGAZHhBG1Kh2F0dG5ldHOIAAAAwAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKEjWYSJc2VjcDI1NmsxoQMcke-W3i7ODVZnbq28QVnNVDbMmB5BhYlTDaDgaW3Hs4hzeW5jbmV0cwODdGNwgiMog3VkcIIjKA,enr:-MK4QLN8VNlJlbFqe9Ksm_TixvO46chZ4FRY2LhMPcdP9VepZA3bTDU4CIGiefkxZl5OMMg5PtHM_6Xh3Cmf3AbdTTqGAZHhBHD_h2F0dG5ldHOIAAADAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhNEmpAiJc2VjcDI1NmsxoQN4LkIjmL5Lny2E4FqsC9CEsvPxwdT-jJzk3OahCsy2XIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QK32AybiWTPF7-ckT509XAfOiYp_EsijsQEspb5zCtZwe5WUBCAFgttyKb1YS6waWxr0KwvqC2SqfFvr1xFsG4qGAZHhBIooh2F0dG5ldHOIAAAAAAAAGACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhEDjoAaJc2VjcDI1NmsxoQLsLkYc19PN0b_L10d0irdD5Iub1R2smGP8bT3FAvFFzIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QGHXDg-xkB8Kk_nutvItkA4hJH-CKlno0qLHgU9Vzdz1WI0KzdIQTHGdZYozHX4pAgIb9eZSfwC_SS-lUx-4brSGAZHhBHIqh2F0dG5ldHOIAAAGAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhI9umDWJc2VjcDI1NmsxoQI0k5salAEXHh_ohq0WLfBBG2Rk_VR3ZJBPqGopiljIoohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QFHEpQU7I7ObmjOE7gTQquk32Zte9YImMFlaogUYyptwHmMVhoTOzGUzWtzwkiFVE23J6BhqAfvaj_fvLVjyQMGGAZHhBHkSh2F0dG5ldHOIAAAwAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKpAv7KJc2VjcDI1NmsxoQMiGgGQkdf7ulLLO1_srqzvQDgswbBGvmnHQLBGCHxOG4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QCCbBzLP6meImuRrr8cgA10_gBFCOBV4MRUN2f-jUuNjJkcrpKQkhfEffEY4UBejQOifENEFbHytAKqTK-n7TgKGAZHhBG_Zh2F0dG5ldHOIAAAAAAAAAAOEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKEjFpaJc2VjcDI1NmsxoQK-w-nEbdGb1f1eEGkXmNDoR1JFKS8Tw41JlupzrP6ln4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QAnqsZguRBjctIorvNRLyLoJQvz79WyckEbShBrSU0NjQyRMaIlnLjinEDgxVjD1YUu8c3R2KJkehzh3QfeMiPWGAZHhBGn-h2F0dG5ldHOIAwAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKRa4zWJc2VjcDI1NmsxoQMFJ7mS6v1h4PUcwmDUHfUtEuSHM0seDsaKQ4CmM29fpohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-MK4QO1_z3egFfaP9mVfa9MNHqr4uiBItOAimxcJPNUzc0WvE2DOJzz35iWgDTvX7Lykd8KRMUdgsDObusFeOOQCsrCGAZHhBGq1h2F0dG5ldHOIAGAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKEjV0WJc2VjcDI1NmsxoQIJSQjZEVdAyhaEQN7yfvhBVg2oVGyHtq9XfSs4y_mnuohzeW5jbmV0cw6DdGNwgiMog3VkcIIjKA,enr:-MK4QM-gKq0sM0LUDQPgJfaLAZDLMQQpjRlUqVdL-HqTgdJhZfSjEj4_qNmqxOQc0HesgrX_PNMJqvkXJPR39tDsbzKGAZHhBHVCh2F0dG5ldHOIAAAAAAAAAwCEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKdjweCJc2VjcDI1NmsxoQPicbgqSK8hy4_kzSpcJ0cT6Q2Um1BIyqNlbkv0Bqoxw4hzeW5jbmV0cwqDdGNwgiMog3VkcIIjKA,enr:-Ly4QOQxOoVU510wme5T0VB6PEPiSXoLO2CwRmbG-q5TGOvxBVI7h7b_JHkLx-R3JFwrEnJAKWEVKUIc3lM8w3WCencJh2F0dG5ldHOIMAAAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhEDjcuuJc2VjcDI1NmsxoQNC4GPdsRwXf-6qbX4HjRjIW2SSJdpoxsfM9BdCW8JT2YhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QAZHOo5OSQKKDyyMhChlxV2PfaceYkgP-0nZuEdOVTN6RGK5jY1Zo08Gh7ulgaZniPdVl1rI0RbVTaF4ZR7y0ekIh2F0dG5ldHOIAAMAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhGj4h_eJc2VjcDI1NmsxoQOMgoYfjt6S4CpsIGDV0VlsXr804pTrfXITLMwFraYDE4hzeW5jbmV0cw2DdGNwgiMog3VkcIIjKA,enr:-Ly4QDUn5ntsdIeEM5FRJEpk1HKO5yUgP_S8UHM8uYyUcjt_P7M9D7Y0Gc3AYfvXnVs3_swbPJIDcBkwTOjSXZp0ac8Lh2F0dG5ldHOIAAAAAwAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhIpESQmJc2VjcDI1NmsxoQIj1hDqf6kfvbUEaJdmpaZp2ID1rIf7iDOVRnN0FHkbXohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QGy7qD0MndiJzoA3Amcoec5loLf29vUo6FNnF6U2uCDEHoCirAUgUaClFCxkDVt2t1XVtJe0pfns2vsiOVnR_iYJh2F0dG5ldHOIAABgAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhM-a1n-Jc2VjcDI1NmsxoQN4y2XWAt8YHTf4nBTDvFQpvnTRx8aXw_k-dfnUrbvkn4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QH0LJOPhjZ-FUxLcdpvDAif1wIvu5uLNxjVsrh3NdvrZGEXyOWscuG1CwgZX4lBCy77HGtMnuX9o_UKYLRHaWa4Ih2F0dG5ldHOIAAAAAAAwAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhKdjJTOJc2VjcDI1NmsxoQLSzwnWNOk1rLJNmBlQ7HtG64HerMOF8PZv1JpYnhgRt4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QCOsHEfic9_uIfwZ7Z-rIEvSMvHbUs2s46IfFOCtSA_ZBxnWX0BOH9L3iBBOtH7bqKAmtjTRw8t3oLDZJD7smCELh2F0dG5ldHOIAAAAAGAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhIpEpiCJc2VjcDI1NmsxoQL7B1-_c9PS33Hubb7f4iBpSSwNfa1Zj8qccbF-bNRKE4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QPTschBTWG6LbjvJvJow_kjFYlXV0hCpTuYmq3bsSTQLKc5XdSPWtnX9COt8IykceA1exUNhpVISGSMOESJPaIwJh2F0dG5ldHOIAAAAAAAAYACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhIDHFbGJc2VjcDI1NmsxoQNLTg0heNWKp2p8El496Iygr6-SWWIL3q9zu6mm888HMIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QMeh2EH9N12NzPGQpcgq3lSezorQ9yNm34q9kXQLUr5Rb9SnrO3nu3jwaheb0EN_cfssctvTgmgmxKED_0MeDIYJh2F0dG5ldHOIgAEAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhBjHdquJc2VjcDI1NmsxoQNSCaj6PKm81EaOpvyRncsGuglVFkJjGDOzq6qT5tBW-ohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QFZZrKBw5HYQz0VmpPyoj2zyxFKezXpL0JWQCEGxg9s8ZyD9zzC0nn8HB6XwAkpIBdBobZ8VPHzdWXVHZVy73Z8Ih2F0dG5ldHOIAAAAAAAAAAyEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhNEmGBqJc2VjcDI1NmsxoQItWpunYuwzPhlbKBw2S8TB89p8kpif3XaiM5NHXQeTuohzeW5jbmV0cw2DdGNwgiMog3VkcIIjKA,enr:-Ly4QBzBE5WqGd_ZSCv3p8UnxFWfewtKRSRsutl3sNb7MqUuJjM7ixvrnfkXVAq_xrBUN9eiVItyaVGQwUNPHlxQr94Jh2F0dG5ldHOIgAEAAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhM-ax5uJc2VjcDI1NmsxoQMkOV7Vs7V8IR4S258CqUh-EXt8PATnpGIt9yZb8k8nhIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QHZ5nVoMmeiEmTwpI9m00R5fiEuhUJskbfb8UrOsqtmJOqk95CB8L0-jNu-Lz8H26ygDO8cbmaUmoT7AZZIu8GMJh2F0dG5ldHOIAAAAADAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhGj4Eq2Jc2VjcDI1NmsxoQN5WN7sAfpbCDMJ7Xo943jKJUIXHvGAf4LyGhYYYa_7KohzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA,enr:-Ly4QHx0UWL0QpoV-_1NJ0H8UNAHmWqHx0Myya4d0gfSNwh3QAY7RHDkdWBHKeLWjjWyHuNhtb2sAyqbDm2C0i4lJFcIh2F0dG5ldHOIAAAGAAAAAACEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhLKA99uJc2VjcDI1NmsxoQNwXxpcra9CHQWa3iX3mqjeapdJff2DZfFEgqmytGlJhIhzeW5jbmV0cw2DdGNwgiMog3VkcIIjKA,enr:-Ly4QDWXNYWf1G72sFAuya70AbSUfqjlOlQfc8FeFQHrlBb0ENDkhZcAIQKpQc6OT6LVlsN_fdseJPBHYIcK3HP3_FcJh2F0dG5ldHOIAAAAAAAAAwCEZXRoMpD-n6z1YCFiKP__________gmlkgnY0gmlwhJB-4rqJc2VjcDI1NmsxoQN9JOOkjKxDzd-lgSeSR7Jy74b7_uwx00iO3eyvF-CySIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA" \
      --http \
      --http-allow-origin '*'
    

Note: the bootnodes are taken from here

Run Otterscan

  • Run the development build of Otterscan.
    docker run \
      --rm \
      --name otterscan-pectra-devnet-3 \
      --pull always \
      -p 3100:80 \
      --env ERIGON_URL="http://localhost:8545" \
      --env BEACON_API_URL="http://localhost:5052" \
      otterscan/otterscan:develop
    
  • Point your browser to http://localhost:3100

Ephemery testnet

Ephemery testnet is an ephemeral testnet which is reset every ~1 month.

Please see their docs if you want to run Erigon on it.

They also run a public Otterscan instance.

Getting in touch

Otterscan Discord server

Our Discord community server: https://discord.gg/5xM2q2eqDz

Erigon Discord server

Otterscan also has a community channel under the "ecosystem" section of Erigon's Discord.

💡 You must follow their instructions to get an invite

Twitter/X

Official Twitter account: (@otterscan).

Follow the creator on Twitter for more updates (@wmitsuda).

Acknowledgments

Kudos (in no particular order)

We make use of open-source software and integrate many public data sources, mainly:

To the Geth team whose code Erigon is based on.

To the Erigon team that made it possible for regular humans to run an archive node on a retail laptop. Also, they have been very helpful explaining Erigon's internals which made the Otterscan modifications possible.

To the Test in Prod team that made OP-Erigon. Their effort made it possible to run Otterscan against any Optimism Superchain.

To the mdbx team which created the blazingly fast database that empowers Erigon.

To Trust Wallet who sponsors and makes available their icons under a permissive license.

To the owners of the 4bytes repository that we import and use to translate method selectors to human-friendly strings.

To Sourcify, a public, decentralized source code and metadata verification service.

To Ethers, which is the client library we used to interact with the Erigon node. It is high-level enough to hide most JSON-RPC particularities but flexible enough to allow for easy interaction with custom methods.

License

Otterscan is distributed under the MIT License and redistributes MIT-compatible dependencies.

The Otterscan API is implemented inside Erigon and follows its own license (LPGL-3).

The Otterscan Book (this documentation) is distributed under CC BY 4.0.