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
Copy
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init
Configuration
Copy
// 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
Copy
// 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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
# 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
Copy
# Install coverage tool
npm install --save-dev solidity-coverage
# Run coverage
npx hardhat coverage
Best Practices
- Comprehensive Testing: Test all functions and edge cases
- Event Testing: Verify events are emitted correctly
- Gas Testing: Monitor gas usage for optimization
- Integration Testing: Test complete user journeys
- Error Testing: Test all error conditions