# Ekx Hardhat

> Solidity development with Hardhat as used in our older contract repos — hardhat.config.ts network setup, TypeChain typings, ethers-based tests with chai matchers, gas reporting, coverage, Hardhat Ignition deployments and Etherscan verification. Use when a repo has hardhat.config.ts rather than foundry.toml, or when deciding whether to migrate a Hardhat repo to Foundry.

- Skill: `ekinoxis-evm/ekx-hardhat` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ekinoxis-evm/ekx-hardhat`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ekinoxis-evm/ekx-hardhat/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Ekinoxis-evm (https://skillmd.com/u/ekinoxis-evm)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ekinoxis-evm/ekx-hardhat

---


# Hardhat

Used in the two older contract repos, and alongside Foundry elsewhere for
scripting and verification.

**For new contract repos, prefer Foundry** — see [`../ekx-foundry/SKILL.md`](../ekx-foundry/SKILL.md).
Hardhat stays where it already is, and where we need its JS ecosystem
(Ignition, TypeChain typings consumed directly by a Next.js frontend).

Docs: https://hardhat.org/docs

---

## Config

A config trimmed to the shape we use:

```ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.24",
    settings: { optimizer: { enabled: true, runs: 200 } },
  },
  networks: {
    base:        { url: process.env.BASE_MAINNET_RPC_URL || "https://mainnet.base.org", chainId: 8453,  accounts: [process.env.DEPLOYER_PRIVATE_KEY!] },
    baseSepolia: { url: process.env.BASE_SEPOLIA_RPC_URL || "https://sepolia.base.org", chainId: 84532, accounts: [process.env.DEPLOYER_PRIVATE_KEY!] },
    localhost:   { url: "http://127.0.0.1:8545", chainId: 1337 },
  },
  etherscan: { apiKey: { base: process.env.BASESCAN_API_KEY!, baseSepolia: process.env.BASESCAN_API_KEY! } },
  gasReporter: { enabled: process.env.REPORT_GAS === "true", currency: "USD" },
};
export default config;
```

Guard the `accounts` array — `[undefined!]` crashes the whole config load, so every
command fails even ones that need no key. Prefer
`accounts: process.env.DEPLOYER_PRIVATE_KEY ? [process.env.DEPLOYER_PRIVATE_KEY] : []`.

---

## The toolbox

`@nomicfoundation/hardhat-toolbox` bundles what all five repos install anyway:
hardhat-ethers, chai-matchers, network-helpers, verify, TypeChain, gas-reporter,
solidity-coverage.

---

## Commands

```bash
npx hardhat compile
npx hardhat test
npx hardhat test --grep "bid"
REPORT_GAS=true npx hardhat test
npx hardhat coverage
npx hardhat node                                    # local chain
npx hardhat run scripts/deploy.ts --network baseSepolia
npx hardhat verify --network baseSepolia <ADDR> "arg1" "arg2"
```

---

## Tests

```ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { loadFixture, time } from "@nomicfoundation/hardhat-network-helpers";

describe("DogRental", () => {
  async function deploy() {
    const [owner, renter] = await ethers.getSigners();
    const usdc = await (await ethers.getContractFactory("MockUSDC")).deploy();
    const rental = await (await ethers.getContractFactory("DogRental")).deploy(usdc.target);
    return { rental, usdc, owner, renter };
  }

  it("reverts when payment is short", async () => {
    const { rental, renter } = await loadFixture(deploy);
    await expect(rental.connect(renter).book(1, 1_000_000n))
      .to.be.revertedWithCustomError(rental, "InsufficientPayment");
  });
});
```

`loadFixture` snapshots and rewinds state — far faster than redeploying per test.
`time.increase(86400)` for time travel.

**ethers v6 note:** it is `contract.target`, not `contract.address`. Values are
`bigint`, not BigNumber — `1_000_000n`, and no `.mul()`/`.add()`. Every one of our
repos is on v6; old tutorials showing v5 will not run.

---

## TypeChain → frontend

Generate typings the frontend imports directly:

```ts
typechain: { outDir: "typechain-types", target: "ethers-v6" }
```

Regenerate on every ABI change (`npx hardhat compile` does it) or the frontend types
lie about the contract.

---

## Ignition

```ts
// ignition/modules/Auction.ts
export default buildModule("Auction", (m) => {
  const auction = m.contract("Auction", [m.getParameter("usdc")]);
  return { auction };
});
```

```bash
npx hardhat ignition deploy ignition/modules/Auction.ts --network baseSepolia --verify
```

Ignition is resumable — a failed multi-contract deploy picks up where it stopped
instead of redeploying everything. That is its one real advantage over a plain script.

---

## Gotchas

1. **ethers v6 API break** — `.target`, bigints, no BigNumber math.
2. **`accounts: [undefined]`** breaks every command. Guard it.
3. **`MockUSDC` is for local nodes only.** Base Sepolia has real Circle USDC at `0x036CbD53842c5426634e7929541eC2318f3dcF7e` — use it, and the faucet at https://faucet.circle.com.
4. **Verification needs exact constructor args** in the same order and encoding.
5. **`hardhat-toolbox` pins peer deps hard.** Upgrading one plugin alone usually breaks the install; upgrade the toolbox as a unit.

---

## Migrating to Foundry?

Worth it when the repo is contract-heavy and test-heavy (fuzzing, invariants, fork
tests are all better in Foundry). Not worth it for a repo whose value is in the
Ignition deployment graph and the TypeChain types the frontend consumes.
`@nomicfoundation/hardhat-foundry` lets both coexist.

