This standard defines a universal format for specifying network configurations to decentralized applications (DApps). The configuration includes essential information about Ethereum networks, such as available RPC URLs, native currencies, contract addresses, and block explorers. Configurations are intended to be provided by:
Given that different EVM-compatible chains introduce unique features, this standard also supports extensibility, allowing networks to specify additional configuration parameters beyond the base requirements. This flexibility prevents networks from resorting to external storage for their unique configurations, preserving the goal of a unified and comprehensive network configuration standard.
Currently, decentralized applications (DApps) support numerous EVM-compatible networks and often define network configurations in inconsistent formats, which complicates interoperability and sharing of configurations. This standard introduces a unified network configuration format that can be adopted by EVM-based networks, making it easier for developers to integrate with multiple networks and providing consistent configuration management.
The proposed configuration format includes essential details required by DApps, including:
The goal of this standard is to simplify network configuration for DApp developers and enhance interoperability across Ethereum-compatible networks.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.
The configuration MUST use a JSON object, which DApp developers can use to define multiple Ethereum networks.
The specification interface is provided in the TypeScript type definition format, which is widely used to define JSON file formats for Ethereum DApps. Refer to Example Configuration for an illustrative implementation of this standard.
/**
* Represents the configuration for a smart contract.
*
* @typedef ContractConfig
* @property address - The address of the contract on the Ethereum network. **MUST** use a checksum.
* @property abiUrl - URL to contract ABI file, can be relative to `abiRoot` if specified or absolute. The absence of the key or `null` value indicates that the ABI of the contract is unknown. URL content **MUST** use Contract ABI specification JSON format.
* @property blockCreated - The block number in which the contract was deployed.
* @see https://docs.soliditylang.org/en/latest/abi-spec.html#json
* @see /eips/eip-55.html
* @see https://url.spec.whatwg.org
*/
type ContractConfig = {
address: Address,
abiUrl?: string | null,
blockCreated: number,
};
/**
* Represents a list of contract names. No standard contracts are defined.
* The list may vary from one DApp to another for the same network.
* @typedef ContractName
*/
type ContractName = string;
/**
* Represents the configuration for block explorers in the network configuration.
* The URLs for address, transaction, and NFT lookups are **relative** to the `root` URL.
* URLs **MUST** be relative to the `root` URL.
* URLs **MUST** include special parameters, like `:block`, `:address`, `:tx`, `:token` for relevant properties (see examples).
* @typedef ExplorerConfig
* @property root - The root URL of the block explorer (e.g., `https://freescan.example.com`).
* @property block - The relative URL path for viewing a specific block (e.g., `/block/:block`). If not available, this can be `null`.
* @property address - The relative URL path for viewing a specific address (e.g., `/address/:address`). If not available, this can be `null`.
* @property tx - The relative URL path for viewing a transaction (e.g., `/tx/:tx`). If not available, this can be `null`.
* @property nft - The relative URL path for viewing a specific NFT (e.g., `/nft/:address/:token`). If not available, this can be `null`.
*/
type ExplorerConfig = {
root: string,
address: string | null,
tx: string | null,
nft: string | null,
block: string | null,
};
/**
* Represents RPC node endpoints available in the network.
*
* @typedef RpcConfig
* @property url - The URL under which the RPC is available (e.g. `https://public-node.example.com/rpc`).
*/
type RpcConfig = {
url: string,
}
/**
* Represents relation of the network to other networks.
* e.g. a network is a testnet of a mainnet, a network is Layer 2 above some other network,
* a network has a bridge to another network, etc.
*
* @typedef RelationsConfig
* @property mainnetChainId - Chain ID of the recommended mainnet network if current network is a testnet. MUST be `null` for mainnet.
* @property parentChainId - Chain ID of a network that current network is built on top of (e.g. if current network is L2, it will be L1 chain id)
*/
type RelationsConfig = {
mainnetChainId: number | null,
parentChainId: number | null,
};
/**
* Represents the configuration for the native currency used in the network.
*
* @typedef NativeCurrencyConfig
* @property name - The name of the native currency (e.g., "Ether").
* @property symbol - The symbol of the native currency (e.g., "ETH").
* @property decimals - The number of decimal places the currency uses (e.g., 18 for Ether).
*/
type NativeCurrencyConfig = {
name: string,
symbol: string,
decimals: number,
};
/**
* Represents the configuration for a specific Ethereum network used in the DApp.
* This includes details such as the network name, testnet status, RPC configurations,
* explorers, and smart contracts related to the network.
*
* @typedef NetworkConfig
* @property name - The name of the network (e.g., "Ethereum", "Base").
* @property testnet - A flag indicating whether the network is a testnet (`true`) or mainnet (`false`).
* @property nativeCurrency - Configuration for the network's native currency (e.g., ETH).
* @property relations - Relationships with other networks (e.g., parent network or L1/L2 or other bridged networks).
* @property rpcs - A record of RPC configurations for different nodes in the network, where keys represent the node names (e.g., "publicnode").
* @property explorers - Optional record for block explorer configurations (e.g., nft marketplaces).
* @property contracts - A record of contract configurations where the values may be `null` if the contract is not deployed.
* @see https://url.spec.whatwg.org
*/
type NetworkConfig = {
name: string,
testnet: boolean,
nativeCurrency: NativeCurrencyConfig,
relations: RelationsConfig,
rpcs: Record<string, RpcConfig | null>,
explorers: Record<string, ExplorerConfig | null>,
contracts: Record<ContractName, ContractConfig | null>,
};
/**
* Represents the configuration for a DApp, including version information, a summary,
* an optional description, and the network configurations.
*
* @typedef {Object} Configuration
* @property version - The version of the configuration, typically following semantic versioning (e.g., "1.0.0").
* @property timestamp - The timestamp in ISO 8601-compatible format of when the configuration was last updated (e.g., "2025-01-01T12:00:00Z").
* @property abiRoot - Optional root URL for the ABI (Application Binary Interface) if it is hosted externally.
* @property summary - A brief summary or title for the configuration (e.g., "RealPhotos Network Configuration").
* @property description - An optional detailed description of the configuration and its purpose.
* @property networks - A record of network configurations, where keys represent the network IDs (e.g., "1" for ethereum mainnet).
* @see https://url.spec.whatwg.org/
* @see https://semver.org
* @see https://www.iso.org/obp/ui/en/#iso:std:iso:8601:-1:ed-1:v1:en
*/
export default type Configuration = {
version: string,
timestamp: string,
summary: string,
description: string,
abiRoot: string | null,
networks: Record<string, NetworkConfig>,
};
Contracts listed SHOULD be limited to the subset necessary to perform specific functionality, as defined by the configuration author.
Implementations MUST ensure that contract names are consistent across all networks. While the contract binary code deployed to different networks under the same name in the configuration can differ, implementations SHOULD ensure they represent different versions of the same contract. Differences SHOULD be communicated through additional custom properties or different abiUrl. See Extensions.
Inlining contract ABIs MUST NOT be used to ensure effective memory management is possible in resource-constrained environments.
It is RECOMMENDED for a contract name to match the name in Solidity source code if the contract bytecode is verified on block explorers.
If a contract is not deployed on a specific network, the value null MUST explicitly indicate the absence of a contract.
Implementations MUST ignore non-documented properties or support their interpretation where technically feasible. This ensures the standard remains adaptable to future use cases, enabling developers to extend configurations without breaking compatibility. See Example Extensions.
Non-documented properties SHOULD support new features or extensions, but they MUST NOT modify or extend the basic types of existing properties. Union types SHOULD NOT be used in extensions. Their usage contradicts the Rationale of the standard. See Incorrect Extension Example.
Extensions SHOULD use extensible data structures by making sure additional properties can be added at any configuration layer.
Example of rigid and extensible data structures:
# Rigid data structure
type RigidConfiguration = {
documentationUrls: string[],
};
# Extensible data structure
type ExtensibleConfiguration = {
documentation: { url: string }[],
};
The design of this standard leverages the following key goals:
Use JSON as a universally supported data interchange format, with libraries available in virtually all modern programming languages and platforms. This ensures:
The human-readable structure makes it ideal for configuration files:
name and description provide context and improve usability for engineers working with the configuration.This standard is designed to support extensions by using JSON objects rather than rigid structures like basic types or arrays.
See Extensions.
This standard uses the chain ID as the unique identifier for networks rather than custom names because:
This standard emphasizes extensibility while maintaining backward compatibility where feasible. It ensures adaptability to the evolving requirements of Ethereum network configurations by prioritizing flexibility in its structure.
This standard aligns with EIP-3085, the Ethereum standard for wallet Add Ethereum Chain requests, wherever applicable. However, it diverges in scenarios where EIP-3085's rigid structure limits future enhancements.
By prioritizing extensibility over strict adherence to EIP-3085, this standard ensures compatibility with existing tools while enabling future-proof enhancements to network configurations.
Below is an example configuration for an ERC-721 project deployed to Ethereum Mainnet and Sepolia. This illustrates how the standard can represent multi-network setups.
{
"version": "0.0.1",
"timestamp": "2025-01-01T12:22:46.471Z",
"summary": "NFT Artwork",
"description": "Artwork published by independent artist. Carefully crafted with style in one of the creative studios of the world.",
"abiRoot": "https://nft-artwork.example.com/developer/abi",
"networks": {
"1": {
"name": "Ethereum Mainnet",
"testnet": false,
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"rpcs": {
"main": {
"url": "https://eth-rpc.example.com"
},
"backup": {
"url": "https://eth-rpc.backup.example.com"
}
},
"relations": {
"mainnetChainId": null,
"parentChainId": null
},
"explorers": {
"megascan": {
"root": "https://example.org",
"block": "/block/:block",
"address": "/address/:address",
"tx": "/tx/:tx",
"nft": "/nft/:address/:token"
},
"marketplace": {
"root": "https://nft.marketplace/networks/eth",
"block": null,
"address": "/:address",
"tx": null,
"nft": "/:address/:token"
}
},
"contracts": {
"Registry": {
"address": "0x57928ff7b0BBc3Ee4D84481e320DdB8B941f986A",
"blockCreated": 1234567,
"abiUrl": "./Registry.sol/Registry.json"
},
"OwnerWallet": {
"address": "0xC12237E57B088e9191BD8054Df4f5B772646a4B6",
"blockCreated": 1
}
}
},
"11155111": {
"name": "Sepolia",
"testnet": true,
"nativeCurrency": {
"name": "Sepolia Ether",
"symbol": "ETH",
"decimals": 18
},
"rpcs": {
"main": {
"url": "https://sepolia-rpc.example.com"
},
"backup": {
"url": "https://sepolia-rpc.backup.example.com"
}
},
"relations": {
"mainnetChainId": 1,
"parentChainId": null
},
"explorers": {
"megascan": {
"root": "https://sepolia.example.org",
"block": "/block/:block",
"address": "/address/:address",
"tx": "/tx/:tx",
"nft": "/nft/:address/:token"
},
"marketplace": {
"root": "https://testnets.nft.marketplace/networks/sepolia",
"block": null,
"address": "/:address",
"tx": null,
"nft": "/:address/:token"
}
},
"contracts": {
"Registry": {
"address": "0xE13471e6E5d11205AF290261f42108f89dCae72E",
"blockCreated": 183882,
"abiUrl": "./Registry.sol/Registry.json"
},
"OwnerWallet": {
"address": "0xC12237E57B088e9191BD8054Df4f5B772646a4B6",
"blockCreated": 1
}
}
}
}
}
Note: It is RECOMMENDED to use a two-space indentation format for better readability and debugging.
Here is an example implementation of a configuration loader in Swift Programming Language, demonstrating how to load and parse a configuration file compliant with this standard:
import Foundation
struct BlockchainConfig: Codable, LoggerProvider {
struct NetworkConfig: Codable {
struct Currency: Codable {
let name: String
let symbol: String
let decimals: Int
}
struct RPC: Codable {
let url: String
}
struct Relations: Codable {
let mainnetChainId: Int?
let parentChainId: Int?
}
struct Explorer: Codable {
let root: String
let block: String?
let address: String?
let tx: String?
let nft: String?
}
struct Contract: Codable {
let address: String
let blockCreated: Int
}
let name: String
let testnet: Bool
let nativeCurrency: Currency
let rpcs: [String: RPC]
let relations: Relations
let explorers: [String: Explorer?]
let contracts: [String: Contract?]
var registryAddress: String {
contracts["Registry"]!.address
}
var megascan: Explorer {
explorers["megascan"]!
}
func blockExplorerUrl(address: String) -> URL {
let fullPath = megascan.address!.replacingOccurrences(of: ":address", with: address)
return URL(string: megascan.root + fullPath)!
}
func blockExplorerUrl(tx: String) -> URL {
let fullPath = megascan.tx!.replacingOccurrences(of: ":tx", with: tx)
return URL(string: megascan.root + fullPath)!
}
func blockExplorerUrl(contractAddress: String, tokenId: Data) -> URL {
let fullPath = explorers["nftmarketplace"]!.nft!
.replacingOccurrences(of: ":address", with: contractAddress)
.replacingOccurrences(of: ":token", with: tokenId.decString)
return URL(string: megascan.root + fullPath)!
}
}
let version: String
let timestamp: String
let description: String
let networks: [String: NetworkConfig]
static func load() -> BlockchainConfig {
guard let url = Bundle.main.url(forResource: "config", withExtension: "json") else {
fatalError("Blockchain config not found.")
}
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode(BlockchainConfig.self, from: data)
} catch {
fatalError("JSON Config parse error: \(error)")
}
}
}
This Swift code provides a structured way to handle the configuration JSON and includes utility methods for accessing contract-related information.
Example of an extension that MUST be supported: adding an API token alongside the RPC URL when it cannot be embedded directly into the URL
(e.g., passed via an Authorization: Bearer <token> header).
type ExtendedRpcConfig = RpcConfig & {
// Bearer token for API authentication
apiKey: string,
// Maximum requests allowed per hour
hourlyThroughput?: number,
// API key restricted by contract address
contractRestrictionEnabled?: boolean,
};
const rpcs: Record<string, ExtendedRpcConfig> = {
"restrictedNode": {
url: "https://example.com/eth/rpc",
apiKey: "ecc2fc8b494b60bcc9faa90d750183f2",
hourlyThroughput: 40,
contractRestrictionEnabled: true,
},
};
Ensure Security Considerations are followed to protect your API key.
Example of an extension that MUST be supported: incorporating additional attributes to support ERC-1967 transparent proxies and ERC-165 interface identification.
type ExtendedContractConfig = ContractConfig & {
implementation?: string;
proxyAdmin?: string;
supportsInterface: string[];
};
const contracts: Record<string, ExtendedContractConfig> = {
Registry: {
"address": "0x57928ff7b0BBc3Ee4D84481e320DdB8B941f986A",
"blockCreated": 123456,
"implementation": "0x489B4BC8bCA12cCeafb84aa78918b8E28594C391",
"proxyAdmin": "0xFd72EeDa802481027e4200E61A5dF052287eec27",
// ERC-721 & ERC-165 interfaces
"supportsInterface": ["0x01ffc9a7", "0x80ac58cd"]
}
};
Example of an incorrect extension that MUST NOT be supported: representing blockCreated as a hexadecimal string instead of an integer.
const contracts = {
Registry: {
"address": "0x57928ff7b0BBc3Ee4D84481e320DdB8B941f986A",
"blockCreated": "0x2af821",
}
};
It causes existing implementation to fail parsing blockCreated property expecting it to be a number, not a string.
The network configuration defined by this standard is intended to include only public information that is already accessible through tools like block explorers. By structuring this information in a standardized format, the configuration becomes more convenient and efficient for developers to use while maintaining the integrity of public data.
To ensure security and privacy, the following considerations MUST be adhered to:
Exposing API keys must be handled with caution and accompanied by a thorough evaluation of associated risks. When including API keys, the following restrictions and best practices SHOULD be implemented:
Consider implementing IP restrictions where possible.
Rate Limits:
Include throughput limits (e.g., hourly or daily request caps) to mitigate abuse.
Usage:
The configuration MUST NOT include the following sensitive data:
The overall security risks of using this standard are minimal due to its compatibility with established Ethereum ecosystem security practices, including:
By aligning with proven methodologies and widely accepted protocols, the standard minimizes potential vulnerabilities while ensuring robust functionality.
Copyright and related rights waived via CC0.