- Remove duplicate `/marketplace/gpu/{gpu_id}` endpoint from marketplace_gpu.py
- Remove marketplace_gpu router inclusion from main.py (already included elsewhere)
- Fix staking service staker_count logic to check existing stakes before increment/decrement
- Add minimum stake amount validation (100 AITBC)
- Add proper error handling for stake not found cases
- Fix staking pool update to commit and refresh after modifications
- Update CLI send_transaction to use chain
249 lines
8.7 KiB
JavaScript
249 lines
8.7 KiB
JavaScript
import { expect } from "chai";
|
|
import hardhat from "hardhat";
|
|
const { ethers } = hardhat;
|
|
|
|
describe("AgentStaking High-Priority Tests", function () {
|
|
let aitbcToken, performanceVerifier, agentStaking;
|
|
let deployer, staker, agentWallet;
|
|
let stakeId;
|
|
|
|
beforeEach(async function () {
|
|
[deployer, staker, agentWallet] = await ethers.getSigners();
|
|
|
|
// Deploy AIToken
|
|
const AIToken = await ethers.getContractFactory("AIToken");
|
|
aitbcToken = await AIToken.deploy(ethers.parseUnits("1000000", 18));
|
|
await aitbcToken.waitForDeployment();
|
|
|
|
// Deploy mock verifier (simple contract with verify function)
|
|
const MockVerifier = await ethers.getContractFactory("MockVerifier");
|
|
performanceVerifier = await MockVerifier.deploy();
|
|
await performanceVerifier.waitForDeployment();
|
|
|
|
// Deploy AgentStaking
|
|
const AgentStaking = await ethers.getContractFactory("AgentStaking");
|
|
agentStaking = await AgentStaking.deploy(
|
|
await aitbcToken.getAddress(),
|
|
await performanceVerifier.getAddress()
|
|
);
|
|
await agentStaking.waitForDeployment();
|
|
|
|
// Mint tokens to staker
|
|
await aitbcToken.mint(staker.address, ethers.parseEther("10000"));
|
|
await aitbcToken.connect(staker).approve(
|
|
await agentStaking.getAddress(),
|
|
ethers.parseEther("1000000000")
|
|
);
|
|
|
|
// Add supported agent
|
|
await agentStaking.addSupportedAgent(
|
|
agentWallet.address,
|
|
0 // PerformanceTier.BRONZE
|
|
);
|
|
});
|
|
|
|
describe("Test 1.1.1: Create stake with valid parameters", function () {
|
|
it("Should create stake with valid parameters", async function () {
|
|
const stakeAmount = ethers.parseEther("1000"); // 1000 AITBC
|
|
const lockPeriod = 30 * 24 * 60 * 60; // 30 days in seconds
|
|
|
|
// Create stake
|
|
stakeId = await agentStaking.connect(staker).stakeOnAgent.staticCall(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false // autoCompound
|
|
);
|
|
|
|
const tx = await agentStaking.connect(staker).stakeOnAgent(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
const receipt = await tx.wait();
|
|
|
|
// Verify StakeCreated event
|
|
await expect(tx)
|
|
.to.emit(agentStaking, "StakeCreated")
|
|
.withArgs(
|
|
stakeId,
|
|
staker.address,
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
5 // APY
|
|
);
|
|
|
|
// Verify stake details
|
|
const stake = await agentStaking.getStake(stakeId);
|
|
expect(stake[0]).to.equal(staker.address); // staker
|
|
expect(stake[1]).to.equal(agentWallet.address); // agentWallet
|
|
expect(stake[2]).to.equal(stakeAmount); // amount
|
|
expect(stake[6]).to.equal(0); // status = ACTIVE
|
|
expect(stake[9]).to.equal(0); // agentTier = BRONZE
|
|
|
|
// Verify staker's balance decreased
|
|
const stakerBalance = await aitbcToken.balanceOf(staker.address);
|
|
expect(stakerBalance).to.equal(ethers.parseEther("9000"));
|
|
});
|
|
|
|
it("Should calculate correct APY for Bronze tier with 30-day lock", async function () {
|
|
const stakeAmount = ethers.parseEther("1000");
|
|
const lockPeriod = 30 * 24 * 60 * 60;
|
|
|
|
const tx = await agentStaking.connect(staker).stakeOnAgent(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
const receipt = await tx.wait();
|
|
|
|
// Get stake details to check APY
|
|
const logs = await agentStaking.queryFilter(agentStaking.filters.StakeCreated());
|
|
const log = logs[logs.length - 1];
|
|
const apy = log.args.apy;
|
|
|
|
// Base APY = 5%
|
|
// Bronze tier multiplier = 1.0
|
|
// 30-day lock multiplier = 1.0
|
|
// Expected APY = 5% * 1.0 * 1.0 = 5%
|
|
expect(apy).to.be.closeTo(5, 1); // Allow small rounding error
|
|
});
|
|
});
|
|
|
|
describe("Test 1.4.1: Initiate unbonding after lock period", function () {
|
|
beforeEach(async function () {
|
|
const stakeAmount = ethers.parseEther("1000");
|
|
const lockPeriod = 30 * 24 * 60 * 60;
|
|
|
|
stakeId = await agentStaking.connect(staker).stakeOnAgent.staticCall(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
|
|
await agentStaking.connect(staker).stakeOnAgent(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
});
|
|
|
|
it("Should initiate unbonding after lock period ends", async function () {
|
|
// Advance time by 30 days
|
|
await ethers.provider.send("evm_increaseTime", [30 * 24 * 60 * 60]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
// Calculate rewards before unbonding
|
|
const rewardsBefore = await agentStaking.calculateRewards(stakeId);
|
|
|
|
// Initiate unbonding
|
|
const tx = await agentStaking.connect(staker).unbondStake(stakeId);
|
|
const receipt = await tx.wait();
|
|
|
|
// Verify stake status changed to UNBONDING
|
|
const stake = await agentStaking.getStake(stakeId);
|
|
expect(stake[6]).to.equal(1); // status = UNBONDING
|
|
|
|
|
|
// Verify rewards were calculated
|
|
const rewardsAfter = await agentStaking.calculateRewards(stakeId);
|
|
expect(rewardsAfter).to.be.greaterThanOrEqual(rewardsBefore);
|
|
});
|
|
|
|
it("Should fail to unbond before lock period ends", async function () {
|
|
// Try to unbond immediately
|
|
await expect(
|
|
agentStaking.connect(staker).unbondStake(stakeId)
|
|
).to.be.revertedWith("Lock period not ended");
|
|
});
|
|
});
|
|
|
|
describe("Test 1.4.3: Complete unbonding after unbonding period", function () {
|
|
beforeEach(async function () {
|
|
const stakeAmount = ethers.parseEther("1000");
|
|
const lockPeriod = 30 * 24 * 60 * 60;
|
|
|
|
stakeId = await agentStaking.connect(staker).stakeOnAgent.staticCall(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
|
|
await agentStaking.connect(staker).stakeOnAgent(
|
|
agentWallet.address,
|
|
stakeAmount,
|
|
lockPeriod,
|
|
false
|
|
);
|
|
|
|
// Advance time by 30 days (lock period)
|
|
await ethers.provider.send("evm_increaseTime", [30 * 24 * 60 * 60]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
// Initiate unbonding
|
|
await agentStaking.connect(staker).unbondStake(stakeId);
|
|
});
|
|
|
|
it("Should complete unbonding after unbonding period", async function () {
|
|
// Get accumulated rewards before completion
|
|
const stakeBefore = await agentStaking.getStake(stakeId);
|
|
const accumulatedRewards = stakeBefore[8];
|
|
|
|
// Advance time by 7 days (unbonding period)
|
|
await ethers.provider.send("evm_increaseTime", [7 * 24 * 60 * 60]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
// Get staker balance before completion
|
|
const balanceBefore = await aitbcToken.balanceOf(staker.address);
|
|
|
|
// Complete unbonding
|
|
const tx = await agentStaking.connect(staker).completeUnbonding(stakeId);
|
|
const receipt = await tx.wait();
|
|
|
|
// Verify StakeCompleted event
|
|
const event = receipt.logs.find(log => log.fragment?.name === "StakeCompleted");
|
|
expect(event).to.exist;
|
|
expect(event.args[0]).to.equal(stakeId); // stakeId
|
|
expect(event.args[1]).to.equal(staker.address); // staker
|
|
expect(event.args[2]).to.equal(ethers.parseEther("900")); // totalAmount
|
|
expect(event.args[3]).to.be.at.least(0); // totalRewards
|
|
|
|
// Verify stake status changed to COMPLETED
|
|
const stakeAfter = await agentStaking.getStake(stakeId);
|
|
expect(stakeAfter[6]).to.equal(2); // status = COMPLETED
|
|
|
|
// Verify staker received stake amount (900 after penalty) + rewards
|
|
const balanceAfter = await aitbcToken.balanceOf(staker.address);
|
|
expect(balanceAfter).to.be.greaterThan(balanceBefore + ethers.parseEther("900"));
|
|
});
|
|
|
|
it("Should apply early unbonding penalty if completed within 30 days", async function () {
|
|
// Get accumulated rewards before completion
|
|
const stakeBefore = await agentStaking.getStake(stakeId);
|
|
const accumulatedRewards = stakeBefore[7];
|
|
|
|
// Advance time by only 10 days (less than 30-day penalty window)
|
|
await ethers.provider.send("evm_increaseTime", [10 * 24 * 60 * 60]);
|
|
await ethers.provider.send("evm_mine");
|
|
|
|
// Get staker balance before completion
|
|
const balanceBefore = await aitbcToken.balanceOf(staker.address);
|
|
|
|
// Complete unbonding
|
|
const tx = await agentStaking.connect(staker).completeUnbonding(stakeId);
|
|
const receipt = await tx.wait();
|
|
|
|
// Verify penalty was applied (10% of 1000 AITBC = 100 AITBC)
|
|
const balanceAfter = await aitbcToken.balanceOf(staker.address);
|
|
const expectedBalance = balanceBefore + ethers.parseEther("900") + accumulatedRewards; // 1000 - 100 penalty + rewards
|
|
expect(balanceAfter).to.be.closeTo(expectedBalance, ethers.parseEther("0.01"));
|
|
});
|
|
});
|
|
});
|