This commit is contained in:
2026-10-20 10:48:13 +02:00
parent 5d32f0a3da
commit d2171d3f69
18 changed files with 3706 additions and 7 deletions

View File

@@ -0,0 +1,119 @@
---
name: hardhat-toolbox-mocha-ethers
description: Use alongside the `hardhat` skill when the project depends on `@nomicfoundation/hardhat-toolbox-mocha-ethers`. Covers the ethers helpers exposed on `network.create()`, contract interaction (`ethers.deployContract`, calling functions, `connect`), TypeChain-typed contract instances, and the chai matchers from `@nomicfoundation/hardhat-ethers-chai-matchers` (`.to.emit`, `.to.be.revertedWith*`, `.to.changeEtherBalance(s)`).
metadata:
package: "@nomicfoundation/hardhat-toolbox-mocha-ethers"
---
# Hardhat toolbox: ethers + Mocha
This skill builds on the core **`hardhat`** skill. Load that first for test organization, the `network.create()` shape, `networkHelpers`, fixtures, and the typechecking workflow. Everything below hangs off the connection returned by `network.create()`.
This stack runs tests under **Mocha** (global `describe` / `it`) and asserts with **chai**. Mocha doesn't await `describe` callbacks, so the connection is set up at the top of the file with top-level `await`:
```ts
import { expect } from "chai";
import { network } from "hardhat";
const { ethers, networkHelpers } = await network.create();
describe("Counter", function () {
// ...
});
```
One connection per file is the norm. State isolation between tests comes from `loadFixture` (see the `hardhat` skill), not from creating a fresh connection per `describe`.
## `ethers`: signers and contract interaction
Contract types are generated by **TypeChain** from the compiled ABI. Run `hardhat build` before typechecking so the types reflect the latest contracts.
```ts
// Provider (read-only chain data)
const blockNumber = await ethers.provider.getBlockNumber();
const balance = await ethers.provider.getBalance("0xabc...");
// Signers (for sending transactions)
const [owner, alice, bob] = await ethers.getSigners(); // default funded accounts
// Utilities
const oneEth = ethers.parseEther("1.0");
// Deploy a contract, returns a fully typed instance.
// Pass constructor args as the second argument, and (optionally) a signer
// other than the default as the third.
const counter = await ethers.deployContract("Counter");
const counterWithArgs = await ethers.deployContract("Counter", [42n]);
const counterFromAlice = await ethers.deployContract("Counter", [], alice);
// Read state and call functions. Args and return types are type-checked
// against the ABI at compile time; passing wrong types or wrong argument
// counts is a TypeScript error.
const value = await counter.x();
await counter.inc();
await counter.incBy(3n);
// Send from a different signer
await counter.connect(alice).inc();
// Send ETH along with a payable call
await counter.deposit({ value: oneEth });
// Attach to an already-deployed contract
const existing = await ethers.getContractAt("Counter", "0xabc...");
```
Inside a `loadFixture` setup function (see the `hardhat` skill for the surrounding pattern), `ethers.deployContract` is the canonical deploy step:
```ts
async function deployCounterFixture() {
const counter = await ethers.deployContract("Counter");
return { counter };
}
const { counter } = await networkHelpers.loadFixture(deployCounterFixture);
```
To send a transaction from an arbitrary address (not one of the default-funded accounts), fund it for gas and pull an impersonating signer with `ethers.getImpersonatedSigner` — it handles the impersonation internally, no separate `networkHelpers.impersonateAccount` call needed:
```ts
const addr = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
await networkHelpers.setBalance(addr, ethers.parseEther("1.0"));
const signer = await ethers.getImpersonatedSigner(addr);
await counter.connect(signer).inc();
```
## Chai matchers: Ethereum-specific assertions
The `hardhat-ethers-chai-matchers` plugin extends chai's `expect` with matchers for transactions, events, and balance changes. Pass the **transaction promise** (the result of a write call, unawaited) into `expect`:
```ts
import { expect } from "chai";
// Reverts
await expect(counter.connect(banned).inc()).to.revert(ethers);
await expect(counter.connect(banned).inc()).to.be.revertedWith(
"only the owner can increment the counter",
);
await expect(counter.connect(banned).inc()).to.be.revertedWithCustomError(
counter,
"Unauthorized",
);
await expect(counter.connect(banned).inc())
.to.be.revertedWithCustomError(counter, "Unauthorized")
.withArgs(banned.address);
// Events
await expect(counter.inc()).to.emit(counter, "Increment");
await expect(counter.inc()).to.emit(counter, "Increment").withArgs(1n);
// ETH balance changes (positive = received, negative = spent, before gas)
await expect(game.claim()).to.changeEtherBalance(ethers, winner, PRIZE);
await expect(game.claim()).to.changeEtherBalances(
ethers,
[winner, loser],
[PRIZE, -STAKE],
);
```
For plain assertions (equality, arrays, types), use `expect` from chai directly without the Ethereum matchers.

View File

@@ -0,0 +1,152 @@
---
name: hardhat
description: Use when working with Hardhat 3 projects — writing or modifying Solidity tests, TypeScript tests, or any code touching hardhat.config.ts, the `hardhat` import, or `network.create()`. Covers test-layer choice, forge-std cheatcodes, the network connection API, `networkHelpers`, and the compile-then-typecheck workflow. For toolbox-specific guidance (clients, contract calls, assertions), also load the matching `hardhat-toolbox-*` skill.
metadata:
package: "hardhat"
---
# Hardhat 3
This skill covers Hardhat 3 itself. The toolbox layer (clients, contract interaction, ecosystem-specific assertions) lives in companion skills — load whichever matches the project:
- `@nomicfoundation/hardhat-toolbox-viem` → also load the **`hardhat-toolbox-viem`** skill.
- `@nomicfoundation/hardhat-toolbox-mocha-ethers` → also load the **`hardhat-toolbox-mocha-ethers`** skill.
## Test organization
Hardhat 3 supports two distinct test layers:
**Solidity tests** (`.t.sol` files in `contracts/`, or any `.sol` file in `test/`) are the default choice for unit tests on individual contracts. They run directly in the EVM, compile-check against the real ABI, and have access to cheatcodes for EVM state manipulation. Any public function whose name starts with `test` is executed as a test case.
**TypeScript tests** (`.ts` files in `test/`) are the right choice when a test requires off-chain orchestration: multi-contract interactions, fixture reuse across a large suite, assertions about gas or ETH balances from the outside, or integration scenarios driven by external state.
Reach for TypeScript only when Solidity isn't enough. Solidity tests cover all contract logic; TypeScript tests cover the end-to-end flow as a user or script would experience it.
```bash
hardhat test # run all tests (Solidity + TypeScript)
hardhat test solidity # Solidity tests only
```
TypeScript tests are run either with `hardhat test nodejs` or with `hardhat test mocha`, depending on the toolbox or plugins being used: the viem-based toolbox uses `nodejs` while the ethers+mocha toolbox uses `mocha`.
Pass `--coverage` to collect Solidity coverage while running tests. It works on the main `hardhat test` task as well as the `solidity` and TypeScript subtasks (`nodejs` / `mocha`), so you can scope coverage to a single test layer when needed.
## Solidity tests
Any contract that contains at least one `test*` function is treated as a test contract. Use `forge-std` (if present in `package.json`) for assertions and cheatcodes:
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import { Test } from "forge-std/Test.sol";
import { Counter } from "./Counter.sol";
contract CounterTest is Test {
Counter counter;
function setUp() public {
counter = new Counter();
}
function test_InitialValueIsZero() public view {
assertEq(counter.x(), 0);
}
function test_IncByIncreasesByAmount() public {
counter.incBy(3);
assertEq(counter.x(), 3);
}
// Fuzz test: Hardhat runs this with many random inputs automatically
function testFuzz_Inc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x, "Value after calling inc x times should be x");
}
}
```
The `vm` object (from `forge-std/Test.sol`) exposes cheatcodes for EVM state manipulation. Commonly used ones:
| Cheatcode | Effect |
| --- | --- |
| `vm.prank(addr)` | Sets `msg.sender` for the next call only |
| `vm.startPrank(addr)` / `vm.stopPrank()` | Sets `msg.sender` for a range of calls |
| `vm.deal(addr, amount)` | Sets an account's ETH balance |
| `vm.warp(timestamp)` | Sets `block.timestamp` |
| `vm.roll(blockNum)` | Sets `block.number` |
| `vm.expectRevert(...)` | Asserts the next call reverts |
| `vm.expectEmit(...)` | Asserts the next call emits a specific event |
## TypeScript tests and the network connection
The central object in a TypeScript test is the **network connection**, created by `network.create()`. Toolbox plugins extend it with helper objects (e.g. `viem` or `ethers`, plus `networkHelpers`):
```ts
import { network } from "hardhat";
const { networkHelpers } = await network.create();
// Toolbox plugins also expose viem / ethers on this object;
// see the matching hardhat-toolbox-* skill.
```
Hardhat 3 only works with ESM, so top-level `await` is available. Each call to `network.create()` produces an **isolated blockchain state**. A top-level network connection can be used when the same connection and its associated state can be shared across all tests in the file. For more isolation, you can create a new connection for each test suite or even each test case.
### `networkHelpers`: EVM state manipulation
`networkHelpers` wraps all of Hardhat's development-only helpers behind a typed, ergonomic API. Prefer it over raw JSON-RPC calls:
```ts
// Time
await networkHelpers.time.increase(60); // mine a block 60 s ahead
await networkHelpers.time.increaseTo(target); // mine to a specific timestamp
const now = await networkHelpers.time.latest(); // latest block timestamp
await networkHelpers.time.setNextBlockTimestamp(ts); // set without mining
// Blocks
await networkHelpers.mine(); // mine one block
await networkHelpers.mine(5); // mine several blocks
// Accounts
await networkHelpers.impersonateAccount(addr);
await networkHelpers.setBalance(addr, 10n ** 18n);
// Block parameters
await networkHelpers.setPrevRandao(value); // set block.prevrandao for the next block
await networkHelpers.setNextBlockBaseFeePerGas(baseFee);
// Fixtures
//
// loadFixture runs the setup once and snapshots the state. Subsequent calls
// restore the snapshot instead of re-deploying, much faster for large suites.
// The deploy step inside the fixture is toolbox-specific (e.g.
// viem.deployContract, an ethers ContractFactory) — see the matching
// hardhat-toolbox-* skill.
async function deployCounter() {
// ... toolbox-specific deploy ...
return { counter };
}
const { counter } = await networkHelpers.loadFixture(deployCounter);
// loadFixture requires a named function for caching to work (not an
// arrow/anonymous function)
// Snapshots (manual)
const snap = await networkHelpers.takeSnapshot();
// ... do things ...
await snap.restore();
```
For plain TypeScript assertions (equality, arrays, types), use `node:assert/strict`. For ecosystem-specific assertions (reverts, events, balance changes), see the matching `hardhat-toolbox-*` skill.
## Typechecking
Generated contract types are derived from the compiled ABI (viem in the viem toolbox, TypeChain in the ethers toolbox), so recompile first, then typecheck. The example below uses npm; swap `npx` for whichever package manager runner this project uses:
```bash
npx hardhat build && npx tsc --noEmit
```
The compiler will catch wrong argument types, missing arguments, and invalid options (e.g. `value` on a non-payable function) before you even start the test runner. Make this a habit: it surfaces many mistakes faster than running the full test suite.

1
tokenizerRendu/code/.claude Symbolic link
View File

@@ -0,0 +1 @@
.agents

View File

@@ -0,0 +1,2 @@
BSC_TESTNET_RPC=https://data-seed-prebsc-1-s1.binance.org:8545/
PRIVATE_KEY=

31
tokenizerRendu/code/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Node modules
/node_modules
# Compilation output
/dist
# pnpm deploy output
/bundle
# Hardhat Build Artifacts
/artifacts
# Hardhat compilation (v2) support directory
/cache
# Typechain output
/types
# Environment files
.env
.env.*
!.env.example
# Hardhat coverage reports
/coverage
# Gas snapshot files
/.gas-snapshot
# Snapshot cheatcodes output
/snapshots

View File

@@ -0,0 +1,22 @@
# Node modules
/node_modules
# Compilation output
/dist
# pnpm deploy output
/bundle
# Hardhat Build Artifacts
/artifacts
# Hardhat compilation (v2) support directory
/cache
# Typechain output
/types
# Environment files
.env
.env.*
!.env.example

View File

@@ -0,0 +1,20 @@
# Hardhat + ethers project
## Project layout
```
contracts/ Solidity source files (*.sol) and unit tests (*.t.sol)
test/ TypeScript integration tests and Solidity unit tests (*.sol)
ignition/ Hardhat Ignition deployment modules
scripts/ Standalone scripts run with `hardhat run`
hardhat.config.ts
```
## Working in this project
When writing or modifying tests, configuring `hardhat.config.ts`, or interacting with the network from TypeScript, invoke the **`hardhat`** skill. It covers Solidity and TypeScript testing, how to choose between them, `forge-std` cheatcodes, the `network.create()` API, `networkHelpers`, and the compile-then-typecheck workflow. The skill itself points to the matching `hardhat-toolbox-*` skill for toolbox-specific guidance (signers, contract interaction, assertions).
## Docs
- Hardhat 3 — https://hardhat.org/llms.txt
- ethers.js — https://docs.ethers.org/v6/

View File

@@ -0,0 +1 @@
AGENTS.md

View File

@@ -1,14 +1,75 @@
# Code — smart contract(s) # Code — FGC42Forge (Hardhat 3)
Place ici les sources du token (ex. `contracts/`, `hardhat.config.js`, tests). Sources du token **FGC42Forge** (symbole **FG42**), standard **BEP-20**, déploiement prévu sur **BNB Smart Chain Testnet** (chainId **97**).
**Exigences sujet :** code commenté, noms de variables et fonctions explicites, standard **BEP-20** (ou équivalent documenté). Stack : **Solidity**, **Hardhat 3**, **OpenZeppelin**, tests **Mocha + ethers** et tests Solidity (`.t.sol`).
## Commandes (à compléter) > Le contrat `FGC42Forge.sol` sera ajouté à l’étape 3. Le dossier contient encore l’exemple **Counter** généré par `npx hardhat --init`.
```bash ## Prérequis
# Exemple Hardhat — adapter
- Node.js ≥ 18 (Hardhat 3 recommande v22+)
- Compte testnet avec **tBNB** (ex. MetaMask, réseau BSC Testnet)
## Installation
```shell
cd code
npm install npm install
npx hardhat compile ```
## Variables d’environnement
Copier le modèle et remplir les valeurs **localement** (ne jamais committer `.env`) :
```shell
cp .env.example .env
```
| Variable | Description |
|----------|-------------|
| `BSC_TESTNET_RPC` | URL RPC du testnet BSC (défaut dans `.env.example`) |
| `PRIVATE_KEY` | Clé privée du compte de **test** uniquement (préfixe `0x`) |
Hardhat lit ces variables via `hardhat.config.ts` et `dotenv`.
## Commandes utiles
Compiler les contrats :
```shell
npx hardhat build
```
Lancer tous les tests (exemple Counter, en attendant FGC42Forge) :
```shell
npx hardhat test npx hardhat test
``` ```
Tests séparés :
```shell
npx hardhat test solidity
npx hardhat test mocha
```
## Réseau `bscTestnet`
Le réseau **`bscTestnet`** est déclaré dans `hardhat.config.ts`. Pour un déploiement (plus tard, avec Ignition ou un script) :
```shell
npx hardhat ignition deploy --network bscTestnet ignition/modules/<module>.ts
```
Remplacer le module par celui du token une fois le contrat prêt.
## Structure
```
contracts/ Contrats Solidity (+ tests .t.sol)
test/ Tests TypeScript (Mocha)
ignition/ Modules de déploiement Hardhat Ignition
scripts/ Scripts `hardhat run`
hardhat.config.ts
```

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.34;
contract Counter {
uint public x;
event Increment(uint by);
function inc() public {
x++;
emit Increment(1);
}
function incBy(uint by) public {
require(by > 0, "incBy: increment should be positive");
x += by;
emit Increment(by);
}
}

View File

@@ -0,0 +1,32 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.34;
import {Counter} from "./Counter.sol";
import {Test} from "forge-std/Test.sol";
// Solidity tests are compatible with foundry, so they
// use the same syntax and offer the same functionality.
contract CounterTest is Test {
Counter counter;
function setUp() public {
counter = new Counter();
}
function test_InitialValue() public view {
require(counter.x() == 0, "Initial value should be 0");
}
function testFuzz_Inc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}
require(counter.x() == x, "Value after calling inc x times should be x");
}
function test_IncByZero() public {
vm.expectRevert();
counter.incBy(0);
}
}

View File

@@ -0,0 +1,39 @@
import hardhatToolboxMochaEthersPlugin from "@nomicfoundation/hardhat-toolbox-mocha-ethers";
import { configVariable, defineConfig } from "hardhat/config";
import "dotenv/config";
export default defineConfig({
plugins: [hardhatToolboxMochaEthersPlugin],
solidity: {
profiles: {
default: {
version: "0.8.34",
},
production: {
version: "0.8.34",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
},
networks: {
hardhatMainnet: {
type: "edr-simulated",
chainType: "l1",
},
hardhatOp: {
type: "edr-simulated",
chainType: "op",
},
bscTestnet: {
type: "http",
chainType: "l1",
url: configVariable("BSC_TESTNET_RPC"),
accounts: [configVariable("PRIVATE_KEY")],
},
},
});

View File

@@ -0,0 +1,9 @@
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
export default buildModule("CounterModule", (m) => {
const counter = m.contract("Counter");
m.call(counter, "incBy", [5n]);
return { counter };
});

3089
tokenizerRendu/code/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
{
"name": "code",
"version": "1.0.0",
"description": "Place ici les sources du token (ex. `contracts/`, `hardhat.config.js`, tests).",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@nomicfoundation/hardhat-ethers": "^4.2.1",
"@nomicfoundation/hardhat-ignition": "^3.1.9",
"@nomicfoundation/hardhat-toolbox": "^7.0.0",
"@nomicfoundation/hardhat-toolbox-mocha-ethers": "^4.0.0",
"@types/chai": "^5.2.3",
"@types/chai-as-promised": "^8.0.2",
"@types/mocha": "^10.0.10",
"@types/node": "^22.20.5",
"chai": "^6.3.0",
"ethers": "^6.17.0",
"forge-std": "github:foundry-rs/forge-std#v1.16.2",
"hardhat": "^3.20.0",
"mocha": "^12.0.3",
"typescript": "~6.0.3"
},
"type": "module",
"dependencies": {
"@openzeppelin/contracts": "^5.6.1",
"dotenv": "^18.0.8"
}
}

View File

@@ -0,0 +1,22 @@
import { network } from "hardhat";
const { ethers } = await network.create({
network: "hardhatOp",
chainType: "op",
});
console.log("Sending transaction using the OP chain type");
const [sender] = await ethers.getSigners();
console.log("Sending 1 wei from", sender.address, "to itself");
console.log("Sending L2 transaction");
const tx = await sender.sendTransaction({
to: sender.address,
value: 1n,
});
await tx.wait();
console.log("Transaction sent successfully");

View File

@@ -0,0 +1,36 @@
import { expect } from "chai";
import { network } from "hardhat";
const { ethers } = await network.create();
describe("Counter", function () {
it("Should emit the Increment event when calling the inc() function", async function () {
const counter = await ethers.deployContract("Counter");
await expect(counter.inc()).to.emit(counter, "Increment").withArgs(1n);
});
it("The sum of the Increment events should match the current value", async function () {
const counter = await ethers.deployContract("Counter");
const deploymentBlockNumber = await ethers.provider.getBlockNumber();
// run a series of increments
for (let i = 1; i <= 10; i++) {
await counter.incBy(i);
}
const events = await counter.queryFilter(
counter.filters.Increment(),
deploymentBlockNumber,
"latest",
);
// check that the aggregated events match the current value
let total = 0n;
for (const event of events) {
total += event.args.by;
}
expect(await counter.x()).to.equal(total);
});
});

View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"lib": ["es2023"],
"module": "node20",
"target": "es2023",
"skipLibCheck": true,
"outDir": "dist",
"types": ["node", "mocha"],
"verbatimModuleSyntax": true
}
}