Skip to main content

Overview

Complete guide for testing ZKScore smart contracts using Hardhat testing framework.

Prerequisites

  • Hardhat: v2.17.0 or higher
  • Node.js: v16.0.0 or higher
  • Test Tokens: ETH for testing transactions

Setup

Installation

npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init

Configuration

// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

module.exports = {
  solidity: "0.8.19",
  networks: {
    hardhat: {
      chainId: 31337
    },
    localhost: {
      url: "http://127.0.0.1:8545"
    }
  }
};

Test Structure

Basic Test Setup

// test/IdentitySBT.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("IdentitySBT", function () {
  let identitySBT;
  let owner;
  let user1;
  let user2;
  
  beforeEach(async function () {
    [owner, user1, user2] = await ethers.getSigners();
    
    const IdentitySBT = await ethers.getContractFactory("IdentitySBT");
    identitySBT = await IdentitySBT.deploy(
      "ZKScore Identity",
      "ZKSID",
      "https://api.onzks.com/metadata/identity/"
    );
    await identitySBT.deployed();
  });
  
  describe("Minting", function () {
    it("Should mint identity successfully", async function () {
      const zksId = "alice.zks";
      const displayName = "Alice Smith";
      const avatarUrl = "https://example.com/avatar.jpg";
      
      const mintingFee = await identitySBT.mintingFee();
      
      await expect(
        identitySBT.connect(user1).mintIdentity(
          zksId,
          displayName,
          avatarUrl,
          { value: mintingFee }
        )
      ).to.emit(identitySBT, "IdentityMinted")
        .withArgs(user1.address, 1, zksId);
      
      const identity = await identitySBT.getIdentity(user1.address);
      expect(identity.exists).to.be.true;
      expect(identity.zksId).to.equal(zksId);
    });
  });
});

Identity SBT Tests

Minting Tests

describe("Identity Minting", function () {
  it("Should prevent duplicate ZKS IDs", async function () {
    const zksId = "alice.zks";
    const mintingFee = await identitySBT.mintingFee();
    
    // First mint should succeed
    await identitySBT.connect(user1).mintIdentity(
      zksId,
      "Alice Smith",
      "https://example.com/avatar.jpg",
      { value: mintingFee }
    );
    
    // Second mint with same ZKS ID should fail
    await expect(
      identitySBT.connect(user2).mintIdentity(
        zksId,
        "Alice Johnson",
        "https://example.com/avatar2.jpg",
        { value: mintingFee }
      )
    ).to.be.revertedWith("ZKS_ID_ALREADY_TAKEN");
  });
  
  it("Should prevent second identity minting", async function () {
    const mintingFee = await identitySBT.mintingFee();
    
    // First identity
    await identitySBT.connect(user1).mintIdentity(
      "alice.zks",
      "Alice Smith",
      "https://example.com/avatar.jpg",
      { value: mintingFee }
    );
    
    // Second identity should fail
    await expect(
      identitySBT.connect(user1).mintIdentity(
        "alice2.zks",
        "Alice Smith 2",
        "https://example.com/avatar2.jpg",
        { value: mintingFee }
      )
    ).to.be.revertedWith("IDENTITY_ALREADY_EXISTS");
  });
});

Activation Tests

describe("Identity Activation", function () {
  it("Should activate identity successfully", async function () {
    const mintingFee = await identitySBT.mintingFee();
    
    // Mint identity
    await identitySBT.connect(user1).mintIdentity(
      "alice.zks",
      "Alice Smith",
      "https://example.com/avatar.jpg",
      { value: mintingFee }
    );
    
    // Activate identity
    await expect(
      identitySBT.connect(user1).activateIdentity()
    ).to.emit(identitySBT, "IdentityActivated")
      .withArgs(user1.address, 1, "alice.zks");
    
    const isActivated = await identitySBT.isActivated(user1.address);
    expect(isActivated).to.be.true;
  });
});

Score Calculator Tests

Score Calculation Tests

describe("Score Calculator", function () {
  let scoreCalculator;
  
  beforeEach(async function () {
    const ScoreCalculator = await ethers.getContractFactory("ScoreCalculator");
    scoreCalculator = await ScoreCalculator.deploy(
      identitySBT.address,
      owner.address
    );
    await scoreCalculator.deployed();
  });
  
  it("Should calculate score correctly", async function () {
    // Setup user with identity
    const mintingFee = await identitySBT.mintingFee();
    await identitySBT.connect(user1).mintIdentity(
      "alice.zks",
      "Alice Smith",
      "https://example.com/avatar.jpg",
      { value: mintingFee }
    );
    await identitySBT.connect(user1).activateIdentity();
    
    // Update score
    const newScore = 750;
    const breakdown = {
      defi: 200,
      nft: 150,
      social: 100,
      trading: 200,
      governance: 50,
      gaming: 25,
      identity: 25,
      trust: 0
    };
    
    await scoreCalculator.updateScore(
      user1.address,
      newScore,
      breakdown
    );
    
    const score = await scoreCalculator.getScore(user1.address);
    expect(score.total).to.equal(newScore);
    
    const scoreBreakdown = await scoreCalculator.getScoreBreakdown(user1.address);
    expect(scoreBreakdown.defi).to.equal(breakdown.defi);
  });
});

Achievement Registry Tests

Achievement Creation Tests

describe("Achievement Registry", function () {
  let achievementRegistry;
  
  beforeEach(async function () {
    const AchievementRegistry = await ethers.getContractFactory("AchievementRegistry");
    achievementRegistry = await AchievementRegistry.deploy();
    await achievementRegistry.deployed();
  });
  
  it("Should create achievement successfully", async function () {
    const achievementData = {
      name: "DeFi Pioneer",
      description: "Complete your first DeFi transaction",
      category: "defi",
      rarity: "common",
      points: 50,
      requirements: {
        type: "transaction_count",
        protocol: "uniswap",
        minimum: 1
      },
      imageUrl: "https://api.onzks.com/achievements/defi-pioneer.png"
    };
    
    await achievementRegistry.createAchievement(
      achievementData.name,
      achievementData.description,
      achievementData.category,
      achievementData.rarity,
      achievementData.points,
      achievementData.requirements,
      achievementData.imageUrl
    );
    
    const achievement = await achievementRegistry.getAchievement(1);
    expect(achievement.name).to.equal(achievementData.name);
    expect(achievement.category).to.equal(achievementData.category);
  });
});

Integration Tests

End-to-End Tests

describe("ZKScore Integration", function () {
  it("Should complete full user journey", async function () {
    // 1. Mint identity
    const mintingFee = await identitySBT.mintingFee();
    await identitySBT.connect(user1).mintIdentity(
      "alice.zks",
      "Alice Smith",
      "https://example.com/avatar.jpg",
      { value: mintingFee }
    );
    
    // 2. Activate identity
    await identitySBT.connect(user1).activateIdentity();
    
    // 3. Update score
    await scoreCalculator.updateScore(
      user1.address,
      800,
      {
        defi: 300,
        nft: 200,
        social: 100,
        trading: 150,
        governance: 25,
        gaming: 25,
        identity: 0,
        trust: 0
      }
    );
    
    // 4. Create achievement
    await achievementRegistry.createAchievement(
      "DeFi Pioneer",
      "Complete your first DeFi transaction",
      "defi",
      "common",
      50,
      { type: "transaction_count", protocol: "uniswap", minimum: 1 },
      "https://api.onzks.com/achievements/defi-pioneer.png"
    );
    
    // 5. Claim achievement
    await achievementRegistry.connect(user1).claimAchievement(1);
    
    // Verify final state
    const identity = await identitySBT.getIdentity(user1.address);
    const score = await scoreCalculator.getScore(user1.address);
    const achievements = await achievementRegistry.getUserAchievements(user1.address);
    
    expect(identity.exists).to.be.true;
    expect(score.total).to.equal(800);
    expect(achievements.length).to.equal(1);
  });
});

Running Tests

Test Commands

# Run all tests
npx hardhat test

# Run specific test file
npx hardhat test test/IdentitySBT.test.js

# Run tests with gas reporting
REPORT_GAS=true npx hardhat test

# Run tests with coverage
npx hardhat coverage

Test Coverage

# Install coverage tool
npm install --save-dev solidity-coverage

# Run coverage
npx hardhat coverage

Best Practices

  1. Comprehensive Testing: Test all functions and edge cases
  2. Event Testing: Verify events are emitted correctly
  3. Gas Testing: Monitor gas usage for optimization
  4. Integration Testing: Test complete user journeys
  5. Error Testing: Test all error conditions