# Local Testing and Forking

{% hint style="info" %}
This page shows how to spin up a local EVM node with **Hardhat** (and optionally **Anvil**) and fork **Somnia Testnet (Shannon)** or **Somnia Mainnet** for realistic testing.
{% endhint %}

***

## Prerequisites

* Node.js 20+
* Hardhat (or Foundry/Anvil if you prefer)
* A `.env` file with Somnia RPCs:

{% code title=".env (example)" %}

```bash
# .env (example)
SOMNIA_RPC_MAINNET=https://api.infra.mainnet.somnia.network/
SOMNIA_RPC_TESTNET=https://dream-rpc.somnia.network/

# Optional: pin block numbers for reproducible forks
FORK_BLOCK_MAINNET=
FORK_BLOCK_TESTNET=
```

{% endcode %}

> Do not commit real keys/tokens. Use environment variables.

***

## Quick Start (Hardhat)

{% stepper %}
{% step %}
{% code title="Install packages" %}

```bash
npm i -D hardhat @nomicfoundation/hardhat-ethers ethers dotenv
npx hardhat # if project not initialized yet
cp .env.example .env || true
```

{% endcode %}
{% endstep %}

{% step %}
{% code title="hardhat.config.ts" %}

```ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-ethers";
import * as dotenv from "dotenv";
dotenv.config();

const cfgFromEnv = {
  mainnetUrl: process.env.SOMNIA_RPC_MAINNET || "https://api.infra.mainnet.somnia.network/",
  testnetUrl: process.env.SOMNIA_RPC_TESTNET || "https://dream-rpc.somnia.network/",
  forkMainnetBlock: process.env.FORK_BLOCK_MAINNET ? Number(process.env.FORK_BLOCK_MAINNET) : undefined,
  forkTestnetBlock: process.env.FORK_BLOCK_TESTNET ? Number(process.env.FORK_BLOCK_TESTNET) : undefined,
};

const config: HardhatUserConfig = {
  solidity: "0.8.19",
  networks: {
    hardhat: {
      chainId: 31337,
    },
    localhost: {
      url: "http://127.0.0.1:8545",
      chainId: 31337,
    },
    somnia_testnet: {
      url: cfgFromEnv.testnetUrl,
      chainId: 50312,
    },
    somnia_mainnet: {
      url: cfgFromEnv.mainnetUrl,
      chainId: 5031,
    },
  },
};

export default config;
```

{% endcode %}
{% endstep %}
{% endstepper %}

***

## Testing Your DApp Locally

Once your Hardhat environment is set up, you can write and run tests for your smart contracts. This ensures your code works as expected before deploying it.

**Create a Simple Smart Contract**

First, create a basic smart contract to test. The `Counter.sol` contract below includes fundamental functions for incrementing a counter and retrieving its current value. Save this file in your project's `contracts` directory.

{% code title="contracts/Counter.sol" %}

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

contract Counter {
    uint256 private count;

    event CountedTo(uint256 number);

    function getCount() public view returns (uint256) {
        return count;
    }

    function increment() public {
        count += 1;
        emit CountedTo(count);
    }
}
```

{% endcode %}

### **Write a Test File**

Next, write a test file to verify the functionality of your smart contract. The `Counter.test.ts` file below deploys the `Counter` contract on a local test network, calls the `increment` function, and checks whether the outcome is as expected. Save this file in your project's `test` directory.

{% code title="test/Counter.test.ts" %}

```ts
import { expect } from "chai";
import { ethers } from "hardhat";

describe("Counter Contract", function () {
  it("Should increment the count by 1", async function () {
    const Counter = await ethers.getContractFactory("Counter");
    const counter = await Counter.deploy();
    await counter.waitForDeployment();

    expect(await counter.getCount()).to.equal(0);

    const tx = await counter.increment();
    await tx.wait();

    expect(await counter.getCount()).to.equal(1);
  });

  it("Should emit a CountedTo event", async function () {
    const Counter = await ethers.getContractFactory("Counter");
    const counter = await Counter.deploy();
    await counter.waitForDeployment();

    await expect(counter.increment()).to.emit(counter, "CountedTo").withArgs(1);
  });
});
```

{% endcode %}

### **Run Your Tests**

To run your tests, navigate to your project's root directory in the terminal and use the following command. This command will execute your test scripts using Hardhat's built-in test network and display the results.

```bash
# Run tests on the default in-process Hardhat network
npx hardhat test

# Or, start a local node in a separate terminal
npx hardhat node

# Then run tests against it
npx hardhat test --network localhost
```

***

## Forking Somnia (Testnet vs Mainnet)

*Forking is the process of copying the state of a live network, like Somnia Mainnet or Testnet, at a specific block and creating a simulation of it on your local machine. This powerful feature allows you to test how your contract will interact with other deployed contracts on the live network (such as a DEX, oracle, or NFT marketplace) using real-world data, but without any of the risk or cost.*

You can fork Shannon Testnet for faster iteration or Mainnet for production-like state. For deterministic CI, always pin a blockNumber.

{% tabs %}
{% tab title="Fork Testnet (Shannon)" %}

```ts
hardhat: {
  forking: {
    url: process.env.SOMNIA_RPC_TESTNET!,
    blockNumber: process.env.FORK_BLOCK_TESTNET ? Number(process.env.FORK_BLOCK_TESTNET) : undefined,
  },
}
```

```bash
# in-process fork
npx hardhat test

# persistent node
npx hardhat node --fork $SOMNIA_RPC_TESTNET ${FORK_BLOCK_TESTNET:+--fork-block-number $FORK_BLOCK_TESTNET}
```

{% endtab %}

{% tab title="Fork Mainnet" %}

```ts
hardhat: {
  forking: {
    url: process.env.SOMNIA_RPC_MAINNET!,
    blockNumber: process.env.FORK_BLOCK_MAINNET ? Number(process.env.FORK_BLOCK_MAINNET) : undefined,
  },
}
```

```bash
# in-process fork
npx hardhat test

# persistent node
npx hardhat node --fork $SOMNIA_RPC_MAINNET ${FORK_BLOCK_MAINNET:+--fork-block-number $FORK_BLOCK_MAINNET}
```

{% endtab %}
{% endtabs %}

> Testnet (STT) is ideal for validating flows and cheaper RPC limits; Mainnet (SOMI) reflects real contract/state and gas rules.

***

## Handy RPC Tricks (Hardhat)

These RPC methods provided by Hardhat Network allow you to manipulate the blockchain state for advanced testing scenarios.

### **Impersonate an account**

This allows you to execute transactions from any wallet address on the forked chain, which is perfect for testing functions with admin privileges or interacting with contracts using an account that holds a large amount of tokens ("whale").

{% code title="impersonate.ts" %}

```ts
import { ethers, network } from "hardhat";

async function main() {
  const target = "0xYourSomniaAddress"; // account to impersonate
  await network.provider.request({ method: "hardhat_impersonateAccount", params: [target] });
  const signer = await ethers.getSigner(target);

  await network.provider.send("hardhat_setBalance", [
    target,
    "0x152d02c7e14af6800000" // 1000 ether in wei
  ]);

  console.log("Impersonating:", await signer.getAddress());
}

main().catch((e) => { console.error(e); process.exit(1); });
```

{% endcode %}

### **Time travel**

This feature lets you change the timestamp of future blocks. It's incredibly useful for testing time-dependent smart contract logic, such as vesting schedules, lock-up periods, or any functionality that relies on `block.timestamp`.

```ts
await network.provider.send("evm_setNextBlockTimestamp", [Math.floor(Date.now()/1000) + 3600]);
await network.provider.send("evm_mine");
```

### **Snapshot and Revert**

This allows you to save the current state of the blockchain and later restore it instantly. It's an efficient way to isolate your tests, ensuring that each test case starts from the same clean state without needing to restart the local node.

```ts
const id = await network.provider.send("evm_snapshot");
// ... run actions ...
await network.provider.send("evm_revert", [id]);
```

### **Reset fork**

This command resets the local Hardhat node to a fresh state forked from the blockchain. You can use it to switch to a different block number or even a different RPC endpoint without restarting your entire testing process.

```ts
await network.provider.request({
  method: "hardhat_reset",
  params: [{ forking: { url: process.env.SOMNIA_RPC_TESTNET!, blockNumber: Number(process.env.FORK_BLOCK_TESTNET||0) || undefined } }]
});
```

***

## Alternative: Anvil (Foundry)

{% tabs %}
{% tab title="Fork Testnet" %}

```bash
anvil --fork-url $SOMNIA_RPC_TESTNET ${FORK_BLOCK_TESTNET:+--fork-block-number $FORK_BLOCK_TESTNET} --port 8546
```

{% endtab %}

{% tab title="Fork Mainnet" %}

```bash
anvil --fork-url $SOMNIA_RPC_MAINNET ${FORK_BLOCK_MAINNET:+--fork-block-number $FORK_BLOCK_MAINNET} --port 8546
```

{% endtab %}

{% tab title="Notes" %}
Point your app/tests to `http://127.0.0.1:8546`.

Anvil JSON-RPC is Hardhat-compatible for most calls (`evm_*`). Replace `hardhat_*` with Anvil equivalents where needed.
{% endtab %}
{% endtabs %}

***

## Tips & Best Practices

* Always pin block numbers in forks for reproducibility.
* Prefer `localhost` network when you need state persistence across multiple test files.
* Keep Anvil/Hardhat on separate ports if you run both simultaneously.
* Use labels/comments in tests to describe assumptions tied to a specific fork block.
* Avoid draining RPC rate limits: cache fixtures, use snapshots, and fork testnet for most flows.

***

## Common Issues

<details>

<summary>I<strong>nsufficient funds for gas</strong></summary>

Top up native balance with \`hardhat\_setBalance\`.

</details>

<details>

<summary>N<strong>once too high / replacement underpriced</strong></summary>

Reset account state (new snapshot) or use a fresh signer.

</details>

<details>

<summary><strong>Contract already deployed at address</strong></summary>

On persistent localhost, restart node or change deployer nonce.

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.somnia.network/developer/development-frameworks/local-testing-and-forking.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
