- Add comprehensive test files for core contracts (ContractRegistry, TreasuryManager, AgentMarketplaceV2, EscrowService, DynamicPricing) - Add Foundry fuzz tests for ContractRegistry, TreasuryManager, and AgentMarketplaceV2 - Add deployment automation scripts (deploy-automation.js, verify-deployment.js, monitor-contracts.js) - Fix Hardhat/toolbox version compatibility in package.json - Update smart-contract-tests.yml workflow to include deployment job
344 lines
11 KiB
JavaScript
344 lines
11 KiB
JavaScript
import { expect } from "chai";
|
|
import hardhat from "hardhat";
|
|
const { ethers } = hardhat;
|
|
|
|
describe("EscrowService", function () {
|
|
let escrowService, aitbcToken, aiPowerRental, paymentProcessor;
|
|
let deployer, depositor, beneficiary, arbiter;
|
|
|
|
const ESCROW_AMOUNT = ethers.parseEther("100");
|
|
const INITIAL_SUPPLY = ethers.parseUnits("1000000", 18);
|
|
|
|
beforeEach(async function () {
|
|
[deployer, depositor, beneficiary, arbiter] = await ethers.getSigners();
|
|
|
|
// Deploy AIToken
|
|
const AIToken = await ethers.getContractFactory("AIToken");
|
|
aitbcToken = await AIToken.deploy(INITIAL_SUPPLY);
|
|
await aitbcToken.waitForDeployment();
|
|
|
|
// Transfer tokens to depositor
|
|
await aitbcToken.transfer(depositor.address, ethers.parseEther("10000"));
|
|
|
|
// Deploy AIPowerRental (mock)
|
|
const AIPowerRental = await ethers.getContractFactory("AIPowerRental");
|
|
aiPowerRental = await AIPowerRental.deploy();
|
|
await aiPowerRental.waitForDeployment();
|
|
|
|
// Deploy AITBCPaymentProcessor (mock)
|
|
const AITBCPaymentProcessor = await ethers.getContractFactory("AITBCPaymentProcessor");
|
|
paymentProcessor = await AITBCPaymentProcessor.deploy();
|
|
await paymentProcessor.waitForDeployment();
|
|
|
|
// Deploy EscrowService
|
|
const EscrowService = await ethers.getContractFactory("EscrowService");
|
|
escrowService = await EscrowService.deploy(
|
|
await aitbcToken.getAddress(),
|
|
await aiPowerRental.getAddress(),
|
|
await paymentProcessor.getAddress()
|
|
);
|
|
await escrowService.waitForDeployment();
|
|
|
|
// Approve escrow service to spend depositor's tokens
|
|
await aitbcToken.connect(depositor).approve(
|
|
await escrowService.getAddress(),
|
|
ethers.parseEther("1000000000")
|
|
);
|
|
|
|
// Authorize arbiter
|
|
await escrowService.connect(deployer).authorizeArbiter(arbiter.address);
|
|
});
|
|
|
|
describe("Deployment", function () {
|
|
it("Should deploy with correct token address", async function () {
|
|
expect(await escrowService.aitbcToken()).to.equal(await aitbcToken.getAddress());
|
|
});
|
|
|
|
it("Should set deployer as owner", async function () {
|
|
expect(await escrowService.owner()).to.equal(deployer.address);
|
|
});
|
|
|
|
it("Should set default configuration values", async function () {
|
|
expect(await escrowService.minEscrowAmount()).to.equal(1e15);
|
|
expect(await escrowService.maxEscrowAmount()).to.equal(1e22);
|
|
expect(await escrowService.minTimeLock()).to.equal(300);
|
|
expect(await escrowService.maxTimeLock()).to.equal(86400 * 30);
|
|
});
|
|
});
|
|
|
|
describe("Escrow Creation", function () {
|
|
it("Should create standard escrow", async function () {
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600, // 1 hour time lock
|
|
0, // Standard escrow type
|
|
0, // Manual release condition
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
|
|
const escrowId = receipt.logs[0].args[0];
|
|
expect(escrowId).to.not.be.undefined;
|
|
});
|
|
|
|
it("Should emit EscrowCreated event", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
)
|
|
).to.emit(escrowService, "EscrowCreated");
|
|
});
|
|
|
|
it("Should revert if amount is below minimum", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
1e14, // Below minimum
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
)
|
|
).to.be.reverted;
|
|
});
|
|
|
|
it("Should revert if amount is above maximum", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
1e23, // Above maximum
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
)
|
|
).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe("Escrow Funding", function () {
|
|
let escrowId;
|
|
|
|
beforeEach(async function () {
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
escrowId = receipt.logs[0].args[0];
|
|
});
|
|
|
|
it("Should fund escrow", async function () {
|
|
await escrowService.connect(depositor).fundEscrow(escrowId);
|
|
|
|
const escrow = await escrowService.escrowAccounts(escrowId);
|
|
expect(escrow.amount).to.equal(ESCROW_AMOUNT);
|
|
});
|
|
|
|
it("Should emit EscrowFunded event", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).fundEscrow(escrowId)
|
|
).to.emit(escrowService, "EscrowFunded");
|
|
});
|
|
|
|
it("Should revert if escrow already funded", async function () {
|
|
await escrowService.connect(depositor).fundEscrow(escrowId);
|
|
|
|
await expect(
|
|
escrowService.connect(depositor).fundEscrow(escrowId)
|
|
).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe("Escrow Release", function () {
|
|
let escrowId;
|
|
|
|
beforeEach(async function () {
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
escrowId = receipt.logs[0].args[0];
|
|
|
|
await escrowService.connect(depositor).fundEscrow(escrowId);
|
|
|
|
// Fast forward time past time lock
|
|
await ethers.provider.send("evm_increaseTime", [3601]);
|
|
await ethers.provider.send("evm_mine");
|
|
});
|
|
|
|
it("Should release escrow to beneficiary", async function () {
|
|
const beneficiaryBalance = await aitbcToken.balanceOf(beneficiary.address);
|
|
|
|
await escrowService.connect(depositor).releaseEscrow(escrowId, "Service completed");
|
|
|
|
const newBeneficiaryBalance = await aitbcToken.balanceOf(beneficiary.address);
|
|
expect(newBeneficiaryBalance).to.be.gt(beneficiaryBalance);
|
|
});
|
|
|
|
it("Should emit EscrowReleased event", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).releaseEscrow(escrowId, "Service completed")
|
|
).to.emit(escrowService, "EscrowReleased");
|
|
});
|
|
|
|
it("Should revert if time lock not passed", async function () {
|
|
// Create new escrow
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
const newEscrowId = receipt.logs[0].args[0];
|
|
|
|
await escrowService.connect(depositor).fundEscrow(newEscrowId);
|
|
|
|
await expect(
|
|
escrowService.connect(depositor).releaseEscrow(newEscrowId, "Service completed")
|
|
).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe("Escrow Refund", function () {
|
|
let escrowId;
|
|
|
|
beforeEach(async function () {
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
escrowId = receipt.logs[0].args[0];
|
|
|
|
await escrowService.connect(depositor).fundEscrow(escrowId);
|
|
});
|
|
|
|
it("Should refund escrow to depositor", async function () {
|
|
const depositorBalance = await aitbcToken.balanceOf(depositor.address);
|
|
|
|
await escrowService.connect(arbiter).refundEscrow(escrowId, "Service not provided");
|
|
|
|
const newDepositorBalance = await aitbcToken.balanceOf(depositor.address);
|
|
expect(newDepositorBalance).to.be.gt(depositorBalance);
|
|
});
|
|
|
|
it("Should emit EscrowRefunded event", async function () {
|
|
await expect(
|
|
escrowService.connect(arbiter).refundEscrow(escrowId, "Service not provided")
|
|
).to.emit(escrowService, "EscrowRefunded");
|
|
});
|
|
|
|
it("Should revert if not authorized arbiter", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).refundEscrow(escrowId, "Service not provided")
|
|
).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe("Arbiter Management", function () {
|
|
it("Should authorize arbiter", async function () {
|
|
await escrowService.connect(deployer).authorizeArbiter(beneficiary.address);
|
|
|
|
expect(await escrowService.authorizedArbiters(beneficiary.address)).to.be.true;
|
|
});
|
|
|
|
it("Should revoke arbiter", async function () {
|
|
await escrowService.connect(deployer).authorizeArbiter(beneficiary.address);
|
|
await escrowService.connect(deployer).revokeArbiter(beneficiary.address);
|
|
|
|
expect(await escrowService.authorizedArbiters(beneficiary.address)).to.be.false;
|
|
});
|
|
|
|
it("Should revert if non-owner authorizes arbiter", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).authorizeArbiter(beneficiary.address)
|
|
).to.be.revertedWithCustomError(escrowService, "OwnableUnauthorizedAccount");
|
|
});
|
|
});
|
|
|
|
describe("Configuration Updates", function () {
|
|
it("Should update minimum escrow amount", async function () {
|
|
await escrowService.connect(deployer).updateMinEscrowAmount(2e15);
|
|
|
|
expect(await escrowService.minEscrowAmount()).to.equal(2e15);
|
|
});
|
|
|
|
it("Should update maximum escrow amount", async function () {
|
|
await escrowService.connect(deployer).updateMaxEscrowAmount(2e22);
|
|
|
|
expect(await escrowService.maxEscrowAmount()).to.equal(2e22);
|
|
});
|
|
|
|
it("Should update platform fee percentage", async function () {
|
|
await escrowService.connect(deployer).updatePlatformFee(100); // 1%
|
|
|
|
expect(await escrowService.platformFeePercentage()).to.equal(100);
|
|
});
|
|
|
|
it("Should revert if non-owner updates configuration", async function () {
|
|
await expect(
|
|
escrowService.connect(depositor).updateMinEscrowAmount(2e15)
|
|
).to.be.revertedWithCustomError(escrowService, "OwnableUnauthorizedAccount");
|
|
});
|
|
});
|
|
|
|
describe("Escrow Queries", function () {
|
|
let escrowId;
|
|
|
|
beforeEach(async function () {
|
|
const tx = await escrowService.connect(depositor).createEscrow(
|
|
beneficiary.address,
|
|
ESCROW_AMOUNT,
|
|
3600,
|
|
0,
|
|
0,
|
|
ethers.ZeroHash
|
|
);
|
|
const receipt = await tx.wait();
|
|
escrowId = receipt.logs[0].args[0];
|
|
|
|
await escrowService.connect(depositor).fundEscrow(escrowId);
|
|
});
|
|
|
|
it("Should get escrow details", async function () {
|
|
const escrow = await escrowService.escrowAccounts(escrowId);
|
|
expect(escrow.depositor).to.equal(depositor.address);
|
|
expect(escrow.beneficiary).to.equal(beneficiary.address);
|
|
expect(escrow.amount).to.equal(ESCROW_AMOUNT);
|
|
});
|
|
|
|
it("Should get depositor escrows", async function () {
|
|
const escrows = await escrowService.depositorEscrows(depositor.address);
|
|
expect(escrows.length).to.be.gte(1);
|
|
});
|
|
|
|
it("Should get beneficiary escrows", async function () {
|
|
const escrows = await escrowService.beneficiaryEscrows(beneficiary.address);
|
|
expect(escrows.length).to.be.gte(1);
|
|
});
|
|
});
|
|
});
|