2. Write contract

2.1 Create Counter.sol

cd contracts
nano Counter.sol

copy this to counter.sol

// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;

/// @title A simple counter contract
contract Counter {
  uint32 private _count;

  /// @notice Returns the current count
  function getCount() external view returns (uint32) {
    return _count;
  }

  /// @notice Increments the counter by a specific value
  function increment(uint32 value) external {
    _count += value;
  }

  /// @notice Decrements the counter by a specific value
  function decrement(uint32 value) external {
    require(_count >= value, "Counter: cannot decrement below zero");
    _count -= value;
  }
}

2.2 Translation

cd ..
npx hardhat compile

=⇒ output :

Compiled 1 Solidity file successfully

2.3 Creat Counter.ts

cd test
nano Counter.ts

coppy this to counter.ts

import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";

describe("Counter", function () {
  it("empty test", async function () {
    console.log("Cool! The test basic skeleton is running!");
  });
});

2.4. Run the first test

cd ..
npx hardhat test

output :

  Counter
    Cool! The test basic skeleton is running!
      ✔ empty test

  1 passing

2.5 Add signers

cd ~/fhevm-counter-demo/test
nano Counter.ts

coppy this to counter.ts

import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";

type Signers = {
  owner: HardhatEthersSigner;
  alice: HardhatEthersSigner;
  bob: HardhatEthersSigner;
};

describe("Counter", function () {
  let signers: Signers;

  before(async function () {
    const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
    signers = { owner: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
  });

  it("should work", async function () {
    console.log(`owner: ${signers.owner.address}`);
    console.log(`alice: ${signers.alice.address}`);
    console.log(`bob: ${signers.bob.address}`);
  });
});

run again

npx hardhat test

output :

owner: 0x...
alice: 0x...
bob: 0x...
  ✔ should work

2.6 add deploy fixture

cd ~/fhevm-counter-demo/test
nano Counter.ts

coppy this to Counter.ts import { Counter, Counter__factory } from "../types";

import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";

type Signers = {
  deployer: HardhatEthersSigner;
  alice: HardhatEthersSigner;
  bob: HardhatEthersSigner;
};

async function deployFixture() {
  const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
  const counterContract = (await factory.deploy()) as Counter;
  const counterContractAddress = await counterContract.getAddress();
  return { counterContract, counterContractAddress };
}

describe("Counter", function () {
  let signers: Signers;
  let counterContract: Counter;
  let counterContractAddress: string;

  before(async function () {
    const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
    signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
  });

  beforeEach(async () => {
    ({ counterContract, counterContractAddress } = await deployFixture());
  });

  it("should be deployed", async function () {
    console.log(`Deployed at: ${counterContractAddress}`);
    expect(ethers.isAddress(counterContractAddress)).to.eq(true);
  });
});

run test again

npx hardhat test

output:

Deployed at: 0x...
  ✔ should be deployedDeployed at: 0x...
  ✔ should be deployed

2.7 test getCount, increment, decrement

cd ~/fhevm-counter-demo/test
nano Counter.ts

coppy this :

import { Counter, Counter__factory } from "../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";

type Signers = {
  deployer: HardhatEthersSigner;
  alice: HardhatEthersSigner;
  bob: HardhatEthersSigner;
};

async function deployFixture() {
  const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
  const counter = (await factory.deploy()) as Counter;
  const address = await counter.getAddress();
  return { counter, address };
}

describe("Counter", function () {
  let signers: Signers;
  let counter: Counter;
  let address: string;

  // Khởi tạo 3 tài khoản: deployer, alice, bob
  before(async () => {
    const s = await ethers.getSigners();
    signers = { deployer: s[0], alice: s[1], bob: s[2] };
  });

  // Deploy hợp đồng mới trước mỗi test
  beforeEach(async () => {
    ({ counter, address } = await deployFixture());
  });

  // Test 1: Kiểm tra deploy thành công
  it("should be deployed", async () => {
    console.log("Deployed at:", address);
    expect(ethers.isAddress(address)).to.be.true;
  });

  // Test 2: Giá trị ban đầu = 0
  it("count should be zero", async () => {
    const count = await counter.getCount();
    expect(count).to.eq(0);
  });

  // Test 3: Tăng 1 đơn vị
  it("increment by 1", async () => {
    await counter.connect(signers.alice).increment(1);
    const count = await counter.getCount();
    expect(count).to.eq(1);
  });

  // Test 4: Tăng 5 → Giảm 3 → Kết quả = 2
  it("decrement by 1", async () => {
    await counter.connect(signers.alice).increment(5);
    await counter.connect(signers.alice).decrement(3);
    const count = await counter.getCount();
    expect(count).to.eq(2);
  });
});
cd ..
npx hardhat test

output :

  Counter
    Deployed at: 0x5FbDB2315678afecb367f032d93F642f64180aa3
      should be deployed
      count should be zero
      increment by 1
      decrement by 1

  4 passing (1s)

Last updated