> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainstack.com/llms.txt
> Use this file to discover all available pages before exploring further.

# X Layer tooling

> Connect MetaMask, ethers.js, viem, web3.py, Foundry, and Hardhat to an X Layer Mainnet or Testnet node on Chainstack. Chain configuration, the OKB gas token, contract verification on Sourcify, and the X Layer behaviors that affect tooling.

X Layer is OKX's Ethereum layer 2, built on the [OP Stack](https://docs.optimism.io/) with OKB as the gas token. It is EVM-equivalent, so the standard Ethereum libraries and frameworks work against an X Layer node without modification. Chainstack serves X Layer Mainnet and X Layer Testnet on Global Nodes with archive data and both the `debug_*` and `trace_*` namespaces. See [Debug and trace APIs](/docs/debug-and-trace-apis#x-layer).

## Chain configuration

| Property | Mainnet | Testnet |
| - | - | - |
| Chain ID | `196` | `1952` |
| Native gas currency | OKB | OKB |
| Decimals | `18` | `18` |
| Block time | 1 second | 1 second |
| RPC URL | your Chainstack [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials) | your Chainstack [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials) |
| Multicall3 | `0xcA11bde05977b3631167028862bE2a173976CA11` | `0xcA11bde05977b3631167028862bE2a173976CA11` |
| Block explorer | [web3.okx.com/explorer/x-layer](https://web3.okx.com/explorer/x-layer) | [web3.okx.com/explorer/x-layer-testnet](https://web3.okx.com/explorer/x-layer-testnet) |
| Faucet | — | [web3.okx.com/xlayer/faucet](https://web3.okx.com/xlayer/faucet) |

## MetaMask

Add X Layer as a custom network. On Chainstack, get your [X Layer endpoint](/docs/manage-your-node#view-node-access-and-credentials), then in MetaMask select **Add a custom network** and fill in:

* Network name — X Layer Mainnet
* Default RPC URL — your Chainstack X Layer Mainnet endpoint
* Chain ID — `196`
* Currency symbol — OKB
* Block explorer URL — `https://web3.okx.com/explorer/x-layer`

For X Layer Testnet, use your Chainstack X Layer Testnet endpoint, chain ID `1952`, and the block explorer URL `https://web3.okx.com/explorer/x-layer-testnet`.

## ethers.js

Install [ethers.js](https://docs.ethers.org/):

<CodeGroup>
  ```shell Shell theme={"system"}
  npm install ethers
  ```
</CodeGroup>

<CodeGroup>
  ```javascript index.js theme={"system"}
  const { JsonRpcProvider, formatEther } = require("ethers");

  const provider = new JsonRpcProvider("CHAINSTACK_NODE_URL");

  async function main() {
    const network = await provider.getNetwork();
    console.log("Chain ID:", network.chainId.toString());

    const block = await provider.getBlockNumber();
    console.log("Block:", block);

    const balance = await provider.getBalance("0xe538905cf8410324e03A5A23C1c177a474D59b2b");
    console.log("Balance:", formatEther(balance), "OKB");
  }

  main();
  ```
</CodeGroup>

The address in the example is the wrapped OKB (WOKB) contract on X Layer Mainnet, which holds the OKB deposited into it.

To receive new blocks as X Layer produces them, connect to the WSS endpoint and subscribe:

<CodeGroup>
  ```javascript index.js theme={"system"}
  const { WebSocketProvider } = require("ethers");

  const provider = new WebSocketProvider("CHAINSTACK_WSS_URL");

  provider.on("block", (blockNumber) => {
    console.log("New block:", blockNumber);
  });
  ```
</CodeGroup>

## viem

viem ships `xLayer` (chain `196`) and `xLayerTestnet` (chain `1952`) chain definitions in `viem/chains`. Their default RPC URLs are OKX's public endpoints, so pass your Chainstack endpoint to `http()`. Both definitions include the Multicall3 address, so `client.multicall` batches contract reads into a single `eth_call`.

<CodeGroup>
  ```shell Shell theme={"system"}
  npm install viem
  ```
</CodeGroup>

<CodeGroup>
  ```javascript index.mjs theme={"system"}
  import { createPublicClient, http, formatEther, erc20Abi } from "viem";
  import { xLayer } from "viem/chains";

  const client = createPublicClient({
    chain: xLayer,
    transport: http("CHAINSTACK_NODE_URL"),
  });

  console.log("Chain ID:", await client.getChainId());
  console.log("Block:", await client.getBlockNumber());

  const wokb = "0xe538905cf8410324e03A5A23C1c177a474D59b2b";

  const balance = await client.getBalance({ address: wokb });
  console.log("Balance:", formatEther(balance), "OKB");

  const [symbol, totalSupply] = await client.multicall({
    contracts: [
      { address: wokb, abi: erc20Abi, functionName: "symbol" },
      { address: wokb, abi: erc20Abi, functionName: "totalSupply" },
    ],
    allowFailure: false,
  });
  console.log(symbol, "supply:", formatEther(totalSupply));
  ```
</CodeGroup>

For X Layer Testnet, import `xLayerTestnet` instead and pass your Chainstack X Layer Testnet endpoint.

## web3.py

Install [web3.py](https://web3py.readthedocs.io/):

<CodeGroup>
  ```shell Shell theme={"system"}
  pip install web3
  ```
</CodeGroup>

<CodeGroup>
  ```python main.py theme={"system"}
  from web3 import Web3

  web3 = Web3(Web3.HTTPProvider("CHAINSTACK_NODE_URL"))

  print("Connected:", web3.is_connected())
  print("Chain ID:", web3.eth.chain_id)
  print("Block:", web3.eth.block_number)

  address = Web3.to_checksum_address("0xe538905cf8410324e03a5a23c1c177a474d59b2b")
  balance = web3.eth.get_balance(address)
  print("Balance:", web3.from_wei(balance, "ether"), "OKB")
  ```
</CodeGroup>

`from_wei(balance, "ether")` converts from the smallest unit with 18 decimals, which is the same conversion for OKB as for ETH.

## Foundry

Install [Foundry](https://getfoundry.sh/) and pass your Chainstack endpoint with `--rpc-url`.

Read chain data with `cast`:

<CodeGroup>
  ```shell Shell theme={"system"}
  cast chain-id --rpc-url CHAINSTACK_NODE_URL
  cast block-number --rpc-url CHAINSTACK_NODE_URL
  cast balance 0xe538905cf8410324e03A5A23C1c177a474D59b2b --ether --rpc-url CHAINSTACK_NODE_URL
  ```
</CodeGroup>

Deploy a contract with `forge create`. Without `--broadcast`, `forge create` only simulates the deployment.

<CodeGroup>
  ```shell Shell theme={"system"}
  forge create src/Counter.sol:Counter \
    --rpc-url CHAINSTACK_NODE_URL \
    --private-key YOUR_PRIVATE_KEY \
    --broadcast
  ```
</CodeGroup>

Verify the deployed contract on [Sourcify](https://sourcify.dev/), which supports both X Layer networks and does not need an API key. For X Layer Testnet, pass `--chain 1952`.

<CodeGroup>
  ```shell Shell theme={"system"}
  forge verify-contract DEPLOYED_CONTRACT_ADDRESS src/Counter.sol:Counter \
    --chain 196 \
    --verifier sourcify \
    --watch
  ```
</CodeGroup>

X Layer's own explorers take verifications through OKLink, which needs an OKLink API key. See X Layer's [contract verification guide](https://web3.okx.com/onchainos/dev-docs/xlayer/developer/verify-a-smart-contract/verifying-contract).

## Hardhat

In a [Hardhat 3](https://hardhat.org/) project that uses `@nomicfoundation/hardhat-toolbox-viem`, add the X Layer networks to `hardhat.config.ts`:

<CodeGroup>
  ```typescript hardhat.config.ts theme={"system"}
  import { configVariable, defineConfig } from "hardhat/config";
  import hardhatToolboxViem from "@nomicfoundation/hardhat-toolbox-viem";

  export default defineConfig({
    plugins: [hardhatToolboxViem],
    solidity: "0.8.28",
    networks: {
      xlayer: {
        type: "http",
        url: configVariable("XLAYER_RPC_URL"),
        accounts: [configVariable("XLAYER_PRIVATE_KEY")],
      },
      xlayerTestnet: {
        type: "http",
        url: configVariable("XLAYER_TESTNET_RPC_URL"),
        accounts: [configVariable("XLAYER_PRIVATE_KEY")],
      },
    },
  });
  ```
</CodeGroup>

`configVariable` reads the endpoints and the key from environment variables or the Hardhat keystore, which keeps them out of the config file.

Deploy with an Ignition module, for example `ignition/modules/Counter.ts`:

<CodeGroup>
  ```typescript ignition/modules/Counter.ts theme={"system"}
  import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

  export default buildModule("CounterModule", (m) => {
    const counter = m.contract("Counter");
    return { counter };
  });
  ```
</CodeGroup>

Then verify on Sourcify with the `verify sourcify` task. Hardhat has no built-in Etherscan or Blockscout explorer for X Layer, so the plain `hardhat verify` task reports the chain as not supported for those two providers.

<CodeGroup>
  ```shell Shell theme={"system"}
  npx hardhat ignition deploy ignition/modules/Counter.ts --network xlayer
  npx hardhat verify sourcify --network xlayer DEPLOYED_CONTRACT_ADDRESS
  ```
</CodeGroup>

## X Layer behaviors that affect tooling

### Transactions pay no L1 data fee

X Layer sets the L1 fee scalars in the `GasPriceOracle` predeploy at `0x420000000000000000000000000000000000000F` to zero, so the OP Stack L1 data fee is zero. Transaction receipts still carry the `l1Fee`, `l1GasUsed`, `l1GasPrice`, and `l1BlobBaseFee` fields, with `l1Fee` at `0x0`, and the total cost of a transaction is `gasUsed × effectiveGasPrice`.

### Pending transactions are never delivered

X Layer is an OP Stack chain, so pending transactions stay with the sequencer. `eth_subscribe("newPendingTransactions")` and `eth_newPendingTransactionFilter` return an ID but deliver nothing, and `txpool_status` always reads empty. The `pending` block tag returns the latest block, not the Flashblocks preconfirmation state that X Layer's own Flashblocks RPC serves. See [What gives you mempool access](/docs/mempool-configuration#what-gives-you-mempool-access).

### Block-count limits and the proof window

X Layer produces a block every second, so a 10,000-block `eth_getLogs` request covers under 3 hours of chain time. `eth_getProof` serves the latest 256 blocks, about 4 minutes of chain time. See [EVM range limits](/docs/limits#evm-range-limits) and [EVM `eth_getProof` history limits](/docs/limits#evm-eth_getproof-history-limits).
