Remove outdated GPU marketplace endpoint and fix staking service logic
- 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
This commit is contained in:
248
contracts/test/AgentStaking.test.js
Normal file
248
contracts/test/AgentStaking.test.js
Normal file
@@ -0,0 +1,248 @@
|
||||
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"));
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user