- Remove executable permissions from configuration files (.editorconfig, .env.example, .gitignore) - Remove executable permissions from documentation files (README.md, LICENSE, SECURITY.md) - Remove executable permissions from web assets (HTML, CSS, JS files) - Remove executable permissions from data files (JSON, SQL, YAML, requirements.txt) - Remove executable permissions from source code files across all apps - Add executable permissions to Python
332 lines
14 KiB
JavaScript
332 lines
14 KiB
JavaScript
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
describe("AgentStaking System", function () {
|
|
let agentStaking, aitbcToken;
|
|
let owner, agent, staker1, staker2;
|
|
|
|
beforeEach(async function () {
|
|
// Get signers
|
|
[owner, agent, staker1, staker2] = await ethers.getSigners();
|
|
|
|
// Deploy mock AITBC token
|
|
const MockERC20 = await ethers.getContractFactory("MockERC20");
|
|
aitbcToken = await MockERC20.deploy("AITBC Token", "AITBC", ethers.utils.parseEther("1000000"));
|
|
await aitbcToken.deployed();
|
|
|
|
// Deploy AgentStaking contract
|
|
const AgentStaking = await ethers.getContractFactory("AgentStaking");
|
|
agentStaking = await AgentStaking.deploy(aitbcToken.address);
|
|
await agentStaking.deployed();
|
|
|
|
// Transfer tokens to stakers
|
|
await aitbcToken.transfer(staker1.address, ethers.utils.parseEther("10000"));
|
|
await aitbcToken.transfer(staker2.address, ethers.utils.parseEther("10000"));
|
|
});
|
|
|
|
describe("Agent Registration", function () {
|
|
it("Should register an agent successfully", async function () {
|
|
const tx = await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"https://example.com/metadata",
|
|
["AI", "ML", "NLP"]
|
|
);
|
|
|
|
const receipt = await tx.wait();
|
|
const event = receipt.events.find(e => e.event === "AgentRegistered");
|
|
|
|
expect(event.args.agentAddress).to.equal(agent.address);
|
|
expect(event.args.name).to.equal("Test Agent");
|
|
expect(event.args.metadataURI).to.equal("https://example.com/metadata");
|
|
});
|
|
|
|
it("Should fail if agent is already registered", async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
|
|
await expect(
|
|
agentStaking.connect(agent).registerAgent(
|
|
"Another Agent",
|
|
"metadata2",
|
|
["ML"]
|
|
)
|
|
).to.be.revertedWith("Agent already registered");
|
|
});
|
|
|
|
it("Should update agent metadata", async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
|
|
await agentStaking.connect(agent).updateAgentMetadata(
|
|
"Updated Agent",
|
|
"https://updated.com/metadata",
|
|
["AI", "ML", "CV"]
|
|
);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.name).to.equal("Updated Agent");
|
|
expect(agentInfo.metadataURI).to.equal("https://updated.com/metadata");
|
|
});
|
|
});
|
|
|
|
describe("Staking Operations", function () {
|
|
beforeEach(async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
});
|
|
|
|
it("Should stake tokens successfully", async function () {
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
|
|
const tx = await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
const receipt = await tx.wait();
|
|
const event = receipt.events.find(e => e.event === "TokensStaked");
|
|
|
|
expect(event.args.staker).to.equal(staker1.address);
|
|
expect(event.args.agent).to.equal(agent.address);
|
|
expect(event.args.amount).to.equal(stakeAmount);
|
|
});
|
|
|
|
it("Should track total staked per agent", async function () {
|
|
const stakeAmount1 = ethers.utils.parseEther("500");
|
|
const stakeAmount2 = ethers.utils.parseEther("300");
|
|
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount1);
|
|
await aitbcToken.connect(staker2).approve(agentStaking.address, stakeAmount2);
|
|
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount1);
|
|
await agentStaking.connect(staker2).stake(agent.address, stakeAmount2);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.totalStaked).to.equal(stakeAmount1.add(stakeAmount2));
|
|
});
|
|
|
|
it("Should unstake tokens successfully", async function () {
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
|
|
// Fast forward past unstaking delay
|
|
await ethers.provider.send("evm_increaseTime", [86400 * 7]); // 7 days
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
const initialBalance = await aitbcToken.balanceOf(staker1.address);
|
|
|
|
await agentStaking.connect(staker1).unstake(agent.address, stakeAmount);
|
|
|
|
const finalBalance = await aitbcToken.balanceOf(staker1.address);
|
|
expect(finalBalance).to.equal(initialBalance.add(stakeAmount));
|
|
});
|
|
|
|
it("Should fail to unstake before delay period", async function () {
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
|
|
await expect(
|
|
agentStaking.connect(staker1).unstake(agent.address, stakeAmount)
|
|
).to.be.revertedWith("Unstaking delay not met");
|
|
});
|
|
|
|
it("Should fail to unstake more than staked", async function () {
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
const unstakeAmount = ethers.utils.parseEther("1500");
|
|
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
|
|
// Fast forward past unstaking delay
|
|
await ethers.provider.send("evm_increaseTime", [86400 * 7]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
await expect(
|
|
agentStaking.connect(staker1).unstake(agent.address, unstakeAmount)
|
|
).to.be.revertedWith("Insufficient staked amount");
|
|
});
|
|
});
|
|
|
|
describe("Reward Distribution", function () {
|
|
beforeEach(async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
});
|
|
|
|
it("Should distribute rewards proportionally", async function () {
|
|
const rewardAmount = ethers.utils.parseEther("100");
|
|
|
|
await aitbcToken.transfer(agentStaking.address, rewardAmount);
|
|
|
|
const initialBalance = await aitbcToken.balanceOf(staker1.address);
|
|
|
|
await agentStaking.distributeRewards(agent.address, rewardAmount);
|
|
|
|
const finalBalance = await aitbcToken.balanceOf(staker1.address);
|
|
expect(finalBalance).to.equal(initialBalance.add(rewardAmount));
|
|
});
|
|
|
|
it("Should handle multiple stakers proportionally", async function () {
|
|
// Add second staker
|
|
const stakeAmount2 = ethers.utils.parseEther("500");
|
|
await aitbcToken.connect(staker2).approve(agentStaking.address, stakeAmount2);
|
|
await agentStaking.connect(staker2).stake(agent.address, stakeAmount2);
|
|
|
|
const rewardAmount = ethers.utils.parseEther("150");
|
|
await aitbcToken.transfer(agentStaking.address, rewardAmount);
|
|
|
|
const initialBalance1 = await aitbcToken.balanceOf(staker1.address);
|
|
const initialBalance2 = await aitbcToken.balanceOf(staker2.address);
|
|
|
|
await agentStaking.distributeRewards(agent.address, rewardAmount);
|
|
|
|
const finalBalance1 = await aitbcToken.balanceOf(staker1.address);
|
|
const finalBalance2 = await aitbcToken.balanceOf(staker2.address);
|
|
|
|
// Staker1 had 1000 tokens, Staker2 had 500 tokens (2:1 ratio)
|
|
// So rewards should be distributed 100:50
|
|
expect(finalBalance1).to.equal(initialBalance1.add(ethers.utils.parseEther("100")));
|
|
expect(finalBalance2).to.equal(initialBalance2.add(ethers.utils.parseEther("50")));
|
|
});
|
|
});
|
|
|
|
describe("Agent Performance Tracking", function () {
|
|
beforeEach(async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
});
|
|
|
|
it("Should record successful performance", async function () {
|
|
await agentStaking.recordPerformance(agent.address, true, 95);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.successfulTasks).to.equal(1);
|
|
expect(agentInfo.totalTasks).to.equal(1);
|
|
expect(agentInfo.successRate).to.equal(10000); // 100% in basis points
|
|
});
|
|
|
|
it("Should record failed performance", async function () {
|
|
await agentStaking.recordPerformance(agent.address, false, 60);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.successfulTasks).to.equal(0);
|
|
expect(agentInfo.totalTasks).to.equal(1);
|
|
expect(agentInfo.successRate).to.equal(0);
|
|
});
|
|
|
|
it("Should calculate success rate correctly", async function () {
|
|
// Record multiple performances
|
|
await agentStaking.recordPerformance(agent.address, true, 90);
|
|
await agentStaking.recordPerformance(agent.address, true, 85);
|
|
await agentStaking.recordPerformance(agent.address, false, 70);
|
|
await agentStaking.recordPerformance(agent.address, true, 95);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.successfulTasks).to.equal(3);
|
|
expect(agentInfo.totalTasks).to.equal(4);
|
|
expect(agentInfo.successRate).to.equal(7500); // 75% in basis points
|
|
});
|
|
|
|
it("Should update average accuracy", async function () {
|
|
await agentStaking.recordPerformance(agent.address, true, 90);
|
|
await agentStaking.recordPerformance(agent.address, true, 80);
|
|
await agentStaking.recordPerformance(agent.address, true, 85);
|
|
|
|
const agentInfo = await agentStaking.getAgentInfo(agent.address);
|
|
expect(agentInfo.averageAccuracy).to.equal(8500); // 85% in basis points
|
|
});
|
|
});
|
|
|
|
describe("Slashing Mechanism", function () {
|
|
beforeEach(async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
|
|
const stakeAmount = ethers.utils.parseEther("1000");
|
|
await aitbcToken.connect(staker1).approve(agentStaking.address, stakeAmount);
|
|
await agentStaking.connect(staker1).stake(agent.address, stakeAmount);
|
|
});
|
|
|
|
it("Should slash agent stake for misconduct", async function () {
|
|
const slashAmount = ethers.utils.parseEther("100");
|
|
|
|
const initialContractBalance = await aitbcToken.balanceOf(agentStaking.address);
|
|
|
|
await agentStaking.slashStake(agent.address, slashAmount, "Test slash reason");
|
|
|
|
const finalContractBalance = await aitbcToken.balanceOf(agentStaking.address);
|
|
expect(finalContractBalance).to.equal(initialContractBalance.sub(slashAmount));
|
|
});
|
|
|
|
it("Should emit slash event", async function () {
|
|
const slashAmount = ethers.utils.parseEther("100");
|
|
|
|
const tx = await agentStaking.slashStake(agent.address, slashAmount, "Test reason");
|
|
const receipt = await tx.wait();
|
|
const event = receipt.events.find(e => e.event === "StakeSlashed");
|
|
|
|
expect(event.args.agent).to.equal(agent.address);
|
|
expect(event.args.amount).to.equal(slashAmount);
|
|
expect(event.args.reason).to.equal("Test reason");
|
|
});
|
|
|
|
it("Should fail to slash more than total staked", async function () {
|
|
const totalStaked = await agentStaking.getAgentStakedAmount(agent.address);
|
|
const slashAmount = totalStaked.add(ethers.utils.parseEther("1"));
|
|
|
|
await expect(
|
|
agentStaking.slashStake(agent.address, slashAmount, "Excessive slash")
|
|
).to.be.revertedWith("Slash amount exceeds total staked");
|
|
});
|
|
});
|
|
|
|
describe("Access Control", function () {
|
|
it("Should only allow owner to set performance recorder", async function () {
|
|
await expect(
|
|
agentStaking.connect(staker1).setPerformanceRecorder(staker2.address)
|
|
).to.be.revertedWith("Ownable: caller is not the owner");
|
|
});
|
|
|
|
it("Should allow owner to set performance recorder", async function () {
|
|
await agentStaking.setPerformanceRecorder(staker2.address);
|
|
expect(await agentStaking.performanceRecorder()).to.equal(staker2.address);
|
|
});
|
|
|
|
it("Should only allow performance recorder to record performance", async function () {
|
|
await agentStaking.connect(agent).registerAgent(
|
|
"Test Agent",
|
|
"metadata",
|
|
["AI"]
|
|
);
|
|
|
|
await expect(
|
|
agentStaking.connect(staker1).recordPerformance(agent.address, true, 90)
|
|
).to.be.revertedWith("Not authorized to record performance");
|
|
});
|
|
});
|
|
});
|