- Update priority areas from "100% COMPLETE" to "Next Priority Areas" with phase 4 focus - Mark Smart Contract Development as 🔄 NEXT and Advanced AI Features as 🔄 FUTURE - Restructure development timeline with Q2 2026 marked as COMPLETED, Q3 2026 as CURRENT PHASE - Add Q4 2026 future planning section with weeks 25-36 roadmap - Reorganize next development steps into completed and future sections - Add comprehensive success metrics and
440 lines
17 KiB
JavaScript
440 lines
17 KiB
JavaScript
import { expect } from "chai";
|
|
import pkg from "hardhat";
|
|
const { ethers } = pkg;
|
|
|
|
describe("Phase 4 Modular Smart Contracts", function () {
|
|
let deployer, user1, user2, user3;
|
|
|
|
// Contracts
|
|
let contractRegistry;
|
|
let treasuryManager;
|
|
let rewardDistributor;
|
|
let performanceAggregator;
|
|
let stakingPoolFactory;
|
|
let daoGovernanceEnhanced;
|
|
let aiToken;
|
|
|
|
// Test constants
|
|
const INITIAL_SUPPLY = ethers.parseEther("1000000");
|
|
const MIN_STAKE = ethers.parseEther("100");
|
|
const BUDGET_AMOUNT = ethers.parseEther("10000");
|
|
const REWARD_AMOUNT = ethers.parseEther("1000");
|
|
|
|
beforeEach(async function () {
|
|
[deployer, user1, user2, user3] = await ethers.getSigners();
|
|
|
|
// Deploy AIToken for testing
|
|
const AIToken = await ethers.getContractFactory("AIToken");
|
|
aiToken = await AIToken.deploy(INITIAL_SUPPLY);
|
|
await aiToken.deployed();
|
|
|
|
// Transfer tokens to users
|
|
await aiToken.transfer(user1.address, ethers.parseEther("10000"));
|
|
await aiToken.transfer(user2.address, ethers.parseEther("10000"));
|
|
await aiToken.transfer(user3.address, ethers.parseEther("10000"));
|
|
|
|
// Deploy ContractRegistry
|
|
const ContractRegistry = await ethers.getContractFactory("ContractRegistry");
|
|
contractRegistry = await ContractRegistry.deploy();
|
|
await contractRegistry.deployed();
|
|
|
|
// Deploy TreasuryManager
|
|
const TreasuryManager = await ethers.getContractFactory("TreasuryManager");
|
|
treasuryManager = await TreasuryManager.deploy(aiToken.address);
|
|
await treasuryManager.deployed();
|
|
|
|
// Deploy RewardDistributor
|
|
const RewardDistributor = await ethers.getContractFactory("RewardDistributor");
|
|
rewardDistributor = await RewardDistributor.deploy();
|
|
await rewardDistributor.deployed();
|
|
|
|
// Deploy PerformanceAggregator
|
|
const PerformanceAggregator = await ethers.getContractFactory("PerformanceAggregator");
|
|
performanceAggregator = await PerformanceAggregator.deploy();
|
|
await performanceAggregator.deployed();
|
|
|
|
// Deploy StakingPoolFactory
|
|
const StakingPoolFactory = await ethers.getContractFactory("StakingPoolFactory");
|
|
stakingPoolFactory = await StakingPoolFactory.deploy(aiToken.address);
|
|
await stakingPoolFactory.deployed();
|
|
|
|
// Deploy DAOGovernanceEnhanced
|
|
const DAOGovernanceEnhanced = await ethers.getContractFactory("DAOGovernanceEnhanced");
|
|
daoGovernanceEnhanced = await DAOGovernanceEnhanced.deploy(aiToken.address, MIN_STAKE);
|
|
await daoGovernanceEnhanced.deployed();
|
|
|
|
// Initialize all contracts
|
|
await treasuryManager.initialize(contractRegistry.address);
|
|
await rewardDistributor.initialize(contractRegistry.address);
|
|
await performanceAggregator.initialize(contractRegistry.address);
|
|
await stakingPoolFactory.initialize(contractRegistry.address);
|
|
await daoGovernanceEnhanced.initialize(contractRegistry.address);
|
|
|
|
// Register contracts in registry
|
|
await contractRegistry.registerContract(
|
|
ethers.keccak256(ethers.toUtf8Bytes("TreasuryManager")),
|
|
treasuryManager.address
|
|
);
|
|
await contractRegistry.registerContract(
|
|
ethers.keccak256(ethers.toUtf8Bytes("RewardDistributor")),
|
|
rewardDistributor.address
|
|
);
|
|
await contractRegistry.registerContract(
|
|
ethers.keccak256(ethers.toUtf8Bytes("PerformanceAggregator")),
|
|
performanceAggregator.address
|
|
);
|
|
await contractRegistry.registerContract(
|
|
ethers.keccak256(ethers.toUtf8Bytes("StakingPoolFactory")),
|
|
stakingPoolFactory.address
|
|
);
|
|
await contractRegistry.registerContract(
|
|
ethers.keccak256(ethers.toUtf8Bytes("DAOGovernanceEnhanced")),
|
|
daoGovernanceEnhanced.address
|
|
);
|
|
});
|
|
|
|
describe("ContractRegistry", function () {
|
|
it("Should register and retrieve contracts", async function () {
|
|
const testContractId = ethers.keccak256(ethers.toUtf8Bytes("TestContract"));
|
|
|
|
await contractRegistry.registerContract(testContractId, user1.address);
|
|
|
|
const retrievedAddress = await contractRegistry.getContract(testContractId);
|
|
expect(retrievedAddress).to.equal(user1.address);
|
|
});
|
|
|
|
it("Should update contract addresses", async function () {
|
|
const testContractId = ethers.keccak256(ethers.toUtf8Bytes("TestContract"));
|
|
|
|
await contractRegistry.registerContract(testContractId, user1.address);
|
|
await contractRegistry.updateContract(testContractId, user2.address);
|
|
|
|
const retrievedAddress = await contractRegistry.getContract(testContractId);
|
|
expect(retrievedAddress).to.equal(user2.address);
|
|
});
|
|
|
|
it("Should list all contracts", async function () {
|
|
const [ids, addresses] = await contractRegistry.listContracts();
|
|
expect(ids.length).to.be.greaterThan(0);
|
|
expect(addresses.length).to.equal(ids.length);
|
|
});
|
|
});
|
|
|
|
describe("TreasuryManager", function () {
|
|
it("Should create budget categories", async function () {
|
|
await treasuryManager.createBudgetCategory("development", BUDGET_AMOUNT);
|
|
|
|
const budget = await treasuryManager.getBudgetBalance("development");
|
|
expect(budget).to.equal(BUDGET_AMOUNT);
|
|
});
|
|
|
|
it("Should allocate funds", async function () {
|
|
await treasuryManager.createBudgetCategory("development", BUDGET_AMOUNT);
|
|
|
|
// Deposit funds to treasury
|
|
await aiToken.connect(deployer).transfer(treasuryManager.address, BUDGET_AMOUNT);
|
|
|
|
await treasuryManager.allocateFunds("development", user1.address, ethers.utils.parseEther("1000"));
|
|
|
|
const allocation = await treasuryManager.getAllocation(1);
|
|
expect(allocation[0]).to.equal(user1.address);
|
|
expect(allocation[1]).to.equal(ethers.utils.parseEther("1000"));
|
|
});
|
|
|
|
it("Should release vested funds", async function () {
|
|
await treasuryManager.createBudgetCategory("development", BUDGET_AMOUNT);
|
|
|
|
// Deposit funds to treasury
|
|
await aiToken.connect(deployer).transfer(treasuryManager.address, BUDGET_AMOUNT);
|
|
|
|
await treasuryManager.allocateFunds("development", user1.address, ethers.utils.parseEther("1000"));
|
|
|
|
// Fast forward time
|
|
await ethers.provider.send("evm_increaseTime", [31 * 24 * 60 * 60]); // 31 days
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
const userBalanceBefore = await aiToken.balanceOf(user1.address);
|
|
await treasuryManager.releaseVestedFunds(1);
|
|
const userBalanceAfter = await aiToken.balanceOf(user1.address);
|
|
|
|
expect(userBalanceAfter.sub(userBalanceBefore)).to.equal(ethers.utils.parseEther("1000"));
|
|
});
|
|
});
|
|
|
|
describe("RewardDistributor", function () {
|
|
it("Should create reward pools", async function () {
|
|
await rewardDistributor.createRewardPool(aiToken.address, REWARD_AMOUNT);
|
|
|
|
const poolBalance = await rewardDistributor.getPoolBalance(1);
|
|
expect(poolBalance).to.equal(REWARD_AMOUNT);
|
|
});
|
|
|
|
it("Should distribute rewards", async function () {
|
|
await rewardDistributor.createRewardPool(aiToken.address, REWARD_AMOUNT);
|
|
|
|
// Deposit tokens to reward distributor
|
|
await aiToken.connect(deployer).transfer(rewardDistributor.address, REWARD_AMOUNT);
|
|
|
|
const recipients = [user1.address, user2.address];
|
|
const amounts = [ethers.utils.parseEther("500"), ethers.utils.parseEther("500")];
|
|
|
|
await rewardDistributor.distributeRewards(1, recipients, amounts);
|
|
|
|
const userRewards = await rewardDistributor.getUserRewards(user1.address);
|
|
expect(userRewards).to.equal(ethers.utils.parseEther("500"));
|
|
});
|
|
|
|
it("Should claim rewards", async function () {
|
|
await rewardDistributor.createRewardPool(aiToken.address, REWARD_AMOUNT);
|
|
|
|
// Deposit tokens to reward distributor
|
|
await aiToken.connect(deployer).transfer(rewardDistributor.address, REWARD_AMOUNT);
|
|
|
|
const recipients = [user1.address];
|
|
const amounts = [ethers.utils.parseEther("500")];
|
|
|
|
await rewardDistributor.distributeRewards(1, recipients, amounts);
|
|
|
|
const userBalanceBefore = await aiToken.balanceOf(user1.address);
|
|
await rewardDistributor.claimReward(1);
|
|
const userBalanceAfter = await aiToken.balanceOf(user1.address);
|
|
|
|
// Account for claim fee
|
|
const expectedAmount = ethers.utils.parseEther("500").mul(9990).div(10000); // 0.1% fee
|
|
expect(userBalanceAfter.sub(userBalanceBefore)).to.equal(expectedAmount);
|
|
});
|
|
});
|
|
|
|
describe("PerformanceAggregator", function () {
|
|
it("Should update agent performance", async function () {
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
|
|
const reputation = await performanceAggregator.getReputationScore(user1.address);
|
|
expect(reputation).to.be.greaterThan(0);
|
|
});
|
|
|
|
it("Should calculate APY multiplier", async function () {
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
|
|
const multiplier = await performanceAggregator.calculateAPYMultiplier(8000);
|
|
expect(multiplier).to.be.greaterThan(10000); // Greater than 1x
|
|
});
|
|
|
|
it("Should maintain performance history", async function () {
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8500);
|
|
|
|
const history = await performanceAggregator.getPerformanceHistory(user1.address);
|
|
expect(history.length).to.equal(2);
|
|
});
|
|
});
|
|
|
|
describe("StakingPoolFactory", function () {
|
|
it("Should create staking pools", async function () {
|
|
await stakingPoolFactory.createPool("Basic Pool", 500, 30 * 24 * 60 * 60);
|
|
|
|
const poolDetails = await stakingPoolFactory.getPoolDetails(1);
|
|
expect(poolDetails.poolName).to.equal("Basic Pool");
|
|
expect(poolDetails.baseAPY).to.equal(500);
|
|
});
|
|
|
|
it("Should allow staking", async function () {
|
|
await stakingPoolFactory.createPool("Basic Pool", 500, 30 * 24 * 60 * 60);
|
|
|
|
await stakingPoolFactory.stakeInPool(1, MIN_STAKE);
|
|
|
|
const positionDetails = await stakingPoolFactory.getPositionDetails(1);
|
|
expect(positionDetails.amount).to.equal(MIN_STAKE);
|
|
expect(positionDetails.staker).to.equal(user1.address);
|
|
});
|
|
|
|
it("Should calculate pool performance", async function () {
|
|
await stakingPoolFactory.createPool("Basic Pool", 500, 30 * 24 * 60 * 60);
|
|
|
|
const performance = await stakingPoolFactory.getPoolPerformance(1);
|
|
expect(performance).to.be.greaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe("DAOGovernanceEnhanced", function () {
|
|
it("Should allow staking", async function () {
|
|
await daoGovernanceEnhanced.stake(MIN_STAKE);
|
|
|
|
const stakerInfo = await daoGovernanceEnhanced.getStakerInfo(user1.address);
|
|
expect(stakerInfo.amount).to.equal(MIN_STAKE);
|
|
expect(stakerInfo.isActive).to.be.true;
|
|
});
|
|
|
|
it("Should create proposals", async function () {
|
|
await daoGovernanceEnhanced.stake(MIN_STAKE);
|
|
|
|
const proposalId = await daoGovernanceEnhanced.createProposal(
|
|
"",
|
|
"test proposal",
|
|
7 * 24 * 60 * 60,
|
|
0, // TREASURY_ALLOCATION
|
|
treasuryManager.address,
|
|
"0x",
|
|
0
|
|
);
|
|
|
|
expect(proposalId).to.be.greaterThan(0);
|
|
});
|
|
|
|
it("Should allow voting", async function () {
|
|
await daoGovernanceEnhanced.stake(MIN_STAKE);
|
|
|
|
const proposalId = await daoGovernanceEnhanced.createProposal(
|
|
"",
|
|
"test proposal",
|
|
7 * 24 * 60 * 60,
|
|
0,
|
|
treasuryManager.address,
|
|
"0x",
|
|
0
|
|
);
|
|
|
|
await daoGovernanceEnhanced.castVote(proposalId, 1); // Vote for
|
|
|
|
const proposalInfo = await daoGovernanceEnhanced.getProposalInfo(proposalId);
|
|
expect(proposalInfo.forVotes).to.be.greaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe("Integration Tests", function () {
|
|
it("Should integrate TreasuryManager with DAOGovernanceEnhanced", async function () {
|
|
// Set up budget category
|
|
await treasuryManager.createBudgetCategory("governance", BUDGET_AMOUNT);
|
|
await aiToken.connect(deployer).transfer(treasuryManager.address, BUDGET_AMOUNT);
|
|
|
|
// Create and execute proposal for treasury allocation
|
|
await daoGovernanceEnhanced.stake(MIN_STAKE);
|
|
|
|
const proposalId = await daoGovernanceEnhanced.createProposal(
|
|
"",
|
|
"treasury allocation",
|
|
7 * 24 * 60 * 60,
|
|
0, // TREASURY_ALLOCATION
|
|
treasuryManager.address,
|
|
treasuryManager.interface.encodeFunctionData("allocateFunds", ["governance", user1.address, ethers.utils.parseEther("1000")]),
|
|
0
|
|
);
|
|
|
|
await daoGovernanceEnhanced.castVote(proposalId, 1);
|
|
|
|
// Fast forward voting period
|
|
await ethers.provider.send("evm_increaseTime", [8 * 24 * 60 * 60]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
await daoGovernanceEnhanced.executeProposal(proposalId);
|
|
|
|
const allocation = await treasuryManager.getAllocation(1);
|
|
expect(allocation[0]).to.equal(user1.address);
|
|
});
|
|
|
|
it("Should integrate PerformanceAggregator with StakingPoolFactory", async function () {
|
|
// Update agent performance
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
|
|
// Create staking pool
|
|
await stakingPoolFactory.createPool("Performance Pool", 500, 30 * 24 * 60 * 60);
|
|
|
|
// Stake in pool
|
|
await stakingPoolFactory.stakeInPool(1, MIN_STAKE);
|
|
|
|
// Check if performance affects staking
|
|
const positionDetails = await stakingPoolFactory.getPositionDetails(1);
|
|
expect(positionDetails.amount).to.equal(MIN_STAKE);
|
|
});
|
|
|
|
it("Should integrate RewardDistributor with PerformanceAggregator", async function () {
|
|
// Update agent performance
|
|
await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
|
|
// Create performance-based reward
|
|
await rewardDistributor.createPerformanceReward(user1.address, ethers.utils.parseEther("100"));
|
|
|
|
// Check if performance multiplier is applied
|
|
const userRewards = await rewardDistributor.getUserRewards(user1.address);
|
|
expect(userRewards).to.be.greaterThan(0);
|
|
});
|
|
|
|
it("Should handle cross-contract communication through registry", async function () {
|
|
// Test that contracts can find each other through registry
|
|
const treasuryAddress = await stakingPoolFactory.registry.getContract(
|
|
ethers.utils.keccak256(ethers.utils.toUtf8Bytes("TreasuryManager"))
|
|
);
|
|
|
|
expect(treasuryAddress).to.equal(treasuryManager.address);
|
|
|
|
const performanceAddress = await treasuryManager.registry.getContract(
|
|
ethers.utils.keccak256(ethers.utils.toUtf8Bytes("PerformanceAggregator"))
|
|
);
|
|
|
|
expect(performanceAddress).to.equal(performanceAggregator.address);
|
|
});
|
|
});
|
|
|
|
describe("Security Tests", function () {
|
|
it("Should prevent unauthorized contract registration", async function () {
|
|
const testContractId = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("UnauthorizedContract"));
|
|
|
|
await expect(
|
|
contractRegistry.connect(user1).registerContract(testContractId, user1.address)
|
|
).to.be.reverted;
|
|
});
|
|
|
|
it("Should prevent invalid budget allocations", async function () {
|
|
await treasuryManager.createBudgetCategory("development", BUDGET_AMOUNT);
|
|
|
|
await expect(
|
|
treasuryManager.allocateFunds("development", user1.address, BUDGET_AMOUNT.add(1))
|
|
).to.be.reverted;
|
|
});
|
|
|
|
it("Should prevent invalid voting", async function () {
|
|
const proposalId = await daoGovernanceEnhanced.createProposal(
|
|
"",
|
|
"test proposal",
|
|
7 * 24 * 60 * 60,
|
|
0,
|
|
treasuryManager.address,
|
|
"0x",
|
|
0
|
|
);
|
|
|
|
await expect(
|
|
daoGovernanceEnhanced.castVote(proposalId, 3) // Invalid vote type
|
|
).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe("Gas Optimization Tests", function () {
|
|
it("Should track gas usage for key operations", async function () {
|
|
// Test gas usage for contract registration
|
|
const testContractId = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("GasTest"));
|
|
const tx = await contractRegistry.registerContract(testContractId, user1.address);
|
|
const receipt = await tx.wait();
|
|
|
|
console.log("Gas used for contract registration:", receipt.gasUsed.toString());
|
|
|
|
// Test gas usage for performance update
|
|
const tx2 = await performanceAggregator.updateAgentPerformance(user1.address, 8000);
|
|
const receipt2 = await tx2.wait();
|
|
|
|
console.log("Gas used for performance update:", receipt2.gasUsed.toString());
|
|
|
|
// Test gas usage for staking
|
|
await stakingPoolFactory.createPool("Gas Test Pool", 500, 30 * 24 * 60 * 60);
|
|
const tx3 = await stakingPoolFactory.stakeInPool(1, MIN_STAKE);
|
|
const receipt3 = await tx3.wait();
|
|
|
|
console.log("Gas used for staking:", receipt3.gasUsed.toString());
|
|
});
|
|
});
|
|
|
|
after(async function () {
|
|
console.log("\n🎉 All Phase 4 modular contract tests completed!");
|
|
});
|
|
});
|