Files
DLE/backend/test/DLE.test.js

482 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
* All rights reserved.
*
* This software is proprietary and confidential.
* Unauthorized copying, modification, or distribution is prohibited.
*
* For licensing inquiries: info@hb3-accelerator.com
* Website: https://hb3-accelerator.com
* GitHub: https://github.com/HB3-ACCELERATOR
*/
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("DLE Smart Contract", function () {
let DLE;
let dle;
let owner;
let partner1;
let partner2;
let partner3;
let addrs;
beforeEach(async function () {
// Получаем аккаунты
[owner, partner1, partner2, partner3, ...addrs] = await ethers.getSigners();
// Деплоим контракт
const DLEFactory = await ethers.getContractFactory("DLE");
const config = {
name: "Digital Legal Entity",
symbol: "DLE",
location: "Moscow, Russia",
coordinates: "55.7558,37.6176",
jurisdiction: 1, // Россия
oktmo: 45000000000,
okvedCodes: ["62.01", "62.02", "62.03"],
kpp: 770101001,
quorumPercentage: 60, // 60%
initialPartners: [partner1.address, partner2.address, partner3.address],
initialAmounts: [ethers.parseEther("1000"), ethers.parseEther("1000"), ethers.parseEther("1000")],
supportedChainIds: [1, 137, 56, 42161] // Ethereum, Polygon, BSC, Arbitrum
};
dle = await DLEFactory.deploy(config, 1); // ChainId = 1 (Ethereum)
await dle.waitForDeployment();
});
describe("Деплой и инициализация", function () {
it("Должен правильно инициализировать DLE", async function () {
const dleInfo = await dle.getDLEInfo();
expect(dleInfo.name).to.equal("Digital Legal Entity");
expect(dleInfo.symbol).to.equal("DLE");
expect(dleInfo.location).to.equal("Moscow, Russia");
expect(dleInfo.jurisdiction).to.equal(1);
expect(dleInfo.isActive).to.be.true;
});
it("Должен распределить начальные токены", async function () {
expect(await dle.balanceOf(partner1.address)).to.equal(ethers.parseEther("1000"));
expect(await dle.balanceOf(partner2.address)).to.equal(ethers.parseEther("1000"));
expect(await dle.balanceOf(partner3.address)).to.equal(ethers.parseEther("1000"));
});
it("Должен установить кворум", async function () {
expect(await dle.quorumPercentage()).to.equal(60);
});
it("Должен настроить поддерживаемые цепочки", async function () {
expect(await dle.isChainSupported(1)).to.be.true; // Ethereum
expect(await dle.isChainSupported(137)).to.be.true; // Polygon
expect(await dle.isChainSupported(56)).to.be.true; // BSC
expect(await dle.isChainSupported(42161)).to.be.true; // Arbitrum
expect(await dle.isChainSupported(999)).to.be.false; // Неподдерживаемая цепочка
});
});
describe("Система голосования", function () {
it("Должен создать предложение", async function () {
const description = "Передать 100 токенов от Partner1 к Partner2";
const duration = 7 * 24 * 60 * 60; // 7 дней
const operation = ethers.AbiCoder.defaultAbiCoder().encode(
["bytes4", "bytes"],
[
"0xa9059cbb", // transfer(address,uint256) selector
ethers.AbiCoder.defaultAbiCoder().encode(
["address", "uint256"],
[partner2.address, ethers.parseEther("100")]
)
]
);
const tx = await dle.connect(partner1).createProposal(
description,
duration,
operation,
1 // governanceChainId = Ethereum
);
const receipt = await tx.wait();
const event = receipt.logs.find(log =>
log.fragment && log.fragment.name === "ProposalCreated"
);
expect(event).to.not.be.undefined;
expect(await dle.proposalCounter()).to.equal(1);
});
it("Должен голосовать за предложение", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Голосуем за предложение
await dle.connect(partner1).vote(0, true);
await dle.connect(partner2).vote(0, true);
const proposal = await dle.proposals(0);
expect(proposal.forVotes).to.equal(ethers.parseEther("2000")); // 1000 + 1000
expect(proposal.againstVotes).to.equal(0);
});
it("Должен проверить результат голосования", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Голосуем за предложение (60% от 3000 = 1800)
await dle.connect(partner1).vote(0, true); // 1000
await dle.connect(partner2).vote(0, true); // 1000
await dle.connect(partner3).vote(0, true); // 1000
const [passed, quorumReached] = await dle.checkProposalResult(0);
expect(passed).to.be.true;
expect(quorumReached).to.be.true;
});
it("Не должен позволить голосовать дважды", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Первое голосование
await dle.connect(partner1).vote(0, true);
// Второе голосование должно упасть
await expect(
dle.connect(partner1).vote(0, true)
).to.be.revertedWith("Already voted");
});
it("Не должен позволить голосовать без токенов", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Голосование без токенов должно упасть
await expect(
dle.connect(addrs[0]).vote(0, true)
).to.be.revertedWith("No tokens to vote");
});
});
describe("Мультиподпись", function () {
it("Должен создать мультиподпись операцию", async function () {
const operationHash = ethers.keccak256(ethers.toUtf8Bytes("test operation"));
const duration = 7 * 24 * 60 * 60;
const tx = await dle.connect(partner1).createMultiSigOperation(
operationHash,
duration
);
const receipt = await tx.wait();
const event = receipt.logs.find(log =>
log.fragment && log.fragment.name === "MultiSigOperationCreated"
);
expect(event).to.not.be.undefined;
expect(await dle.multiSigCounter()).to.equal(1);
});
it("Должен подписать мультиподпись операцию", async function () {
const operationHash = ethers.keccak256(ethers.toUtf8Bytes("test operation"));
const duration = 7 * 24 * 60 * 60;
await dle.connect(partner1).createMultiSigOperation(
operationHash,
duration
);
// Подписываем операцию
await dle.connect(partner1).signMultiSigOperation(0, true);
await dle.connect(partner2).signMultiSigOperation(0, true);
const operation = await dle.multiSigOperations(0);
expect(operation.forSignatures).to.equal(ethers.parseEther("2000")); // 1000 + 1000
expect(operation.againstSignatures).to.equal(0);
});
it("Должен проверить результат мультиподписи", async function () {
const operationHash = ethers.keccak256(ethers.toUtf8Bytes("test operation"));
const duration = 7 * 24 * 60 * 60;
await dle.connect(partner1).createMultiSigOperation(
operationHash,
duration
);
// Подписываем операцию (60% от 3000 = 1800)
await dle.connect(partner1).signMultiSigOperation(0, true); // 1000
await dle.connect(partner2).signMultiSigOperation(0, true); // 1000
await dle.connect(partner3).signMultiSigOperation(0, true); // 1000
const [passed, quorumReached] = await dle.checkMultiSigResult(0);
expect(passed).to.be.true;
expect(quorumReached).to.be.true;
});
});
describe("Мульти-чейн синхронизация", function () {
it("Должен синхронизировать голоса из другой цепочки", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Синхронизируем голоса из другой цепочки
await dle.connect(partner1).syncVoteFromChain(
0, // proposalId
137, // fromChainId (Polygon)
ethers.parseEther("500"), // forVotes
ethers.parseEther("200"), // againstVotes
"0x" // proof
);
const proposal = await dle.proposals(0);
expect(proposal.forVotes).to.equal(ethers.parseEther("500"));
expect(proposal.againstVotes).to.equal(ethers.parseEther("200"));
});
it("Должен синхронизировать мультиподпись из другой цепочки", async function () {
const operationHash = ethers.keccak256(ethers.toUtf8Bytes("test operation"));
const duration = 7 * 24 * 60 * 60;
await dle.connect(partner1).createMultiSigOperation(
operationHash,
duration
);
// Синхронизируем мультиподпись из другой цепочки
await dle.connect(partner1).syncMultiSigFromChain(
0, // operationId
137, // fromChainId (Polygon)
ethers.parseEther("800"), // forSignatures
ethers.parseEther("300"), // againstSignatures
"0x" // proof
);
const operation = await dle.multiSigOperations(0);
expect(operation.forSignatures).to.equal(ethers.parseEther("800"));
expect(operation.againstSignatures).to.equal(ethers.parseEther("300"));
});
it("Должен проверить готовность синхронизации", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Проверяем готовность синхронизации
const isReady = await dle.checkSyncReadiness(0);
expect(isReady).to.be.true;
});
});
describe("Управление модулями", function () {
it("Должен добавить модуль", async function () {
const moduleId = ethers.keccak256(ethers.toUtf8Bytes("TreasuryModule"));
const moduleAddress = addrs[0].address;
await dle.connect(partner1).addModule(moduleId, moduleAddress);
expect(await dle.isModuleActive(moduleId)).to.be.true;
expect(await dle.getModuleAddress(moduleId)).to.equal(moduleAddress);
});
it("Должен удалить модуль", async function () {
const moduleId = ethers.keccak256(ethers.toUtf8Bytes("TreasuryModule"));
const moduleAddress = addrs[0].address;
await dle.connect(partner1).addModule(moduleId, moduleAddress);
await dle.connect(partner1).removeModule(moduleId);
expect(await dle.isModuleActive(moduleId)).to.be.false;
});
it("Не должен позволить добавить модуль без токенов", async function () {
const moduleId = ethers.keccak256(ethers.toUtf8Bytes("TreasuryModule"));
const moduleAddress = addrs[0].address;
await expect(
dle.connect(addrs[0]).addModule(moduleId, moduleAddress)
).to.be.revertedWith("Must hold tokens to add module");
});
});
describe("Проверка подключений", function () {
it("Должен проверить подключение к цепочке", async function () {
expect(await dle.checkChainConnection(1)).to.be.true; // Ethereum
expect(await dle.checkChainConnection(137)).to.be.true; // Polygon
expect(await dle.checkChainConnection(56)).to.be.true; // BSC
expect(await dle.checkChainConnection(42161)).to.be.true; // Arbitrum
expect(await dle.checkChainConnection(999)).to.be.false; // Неподдерживаемая цепочка
});
it("Должен получить количество поддерживаемых цепочек", async function () {
expect(await dle.getSupportedChainCount()).to.equal(4);
});
it("Должен получить ID поддерживаемой цепочки", async function () {
expect(await dle.getSupportedChainId(0)).to.equal(1); // Ethereum
expect(await dle.getSupportedChainId(1)).to.equal(137); // Polygon
expect(await dle.getSupportedChainId(2)).to.equal(56); // BSC
expect(await dle.getSupportedChainId(3)).to.equal(42161); // Arbitrum
});
});
describe("Исполнение операций", function () {
it("Должен исполнить предложение с передачей токенов", async function () {
// Создаем предложение для передачи токенов
const description = "Передать 100 токенов от Partner1 к Partner2";
const duration = 7 * 24 * 60 * 60;
const operation = ethers.AbiCoder.defaultAbiCoder().encode(
["bytes4", "bytes"],
[
"0xa9059cbb", // transfer(address,uint256) selector
ethers.AbiCoder.defaultAbiCoder().encode(
["address", "uint256"],
[partner2.address, ethers.parseEther("100")]
)
]
);
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Голосуем за предложение
await dle.connect(partner1).vote(0, true);
await dle.connect(partner2).vote(0, true);
await dle.connect(partner3).vote(0, true);
// Ждем окончания голосования
await ethers.provider.send("evm_increaseTime", [7 * 24 * 60 * 60]);
await ethers.provider.send("evm_mine");
// Исполняем предложение
await dle.connect(partner1).executeProposal(0);
// Проверяем, что токены переданы
expect(await dle.balanceOf(partner1.address)).to.equal(ethers.parseEther("900")); // 1000 - 100
expect(await dle.balanceOf(partner2.address)).to.equal(ethers.parseEther("1100")); // 1000 + 100
});
});
describe("Безопасность", function () {
it("Не должен позволить создать предложение без токенов", async function () {
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await expect(
dle.connect(addrs[0]).createProposal(
description,
duration,
operation,
1
)
).to.be.revertedWith("Must hold tokens to create proposal");
});
it("Не должен позволить голосовать после окончания срока", async function () {
// Создаем предложение с коротким сроком
const description = "Тестовое предложение";
const duration = 1; // 1 секунда
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Ждем окончания срока
await ethers.provider.send("evm_increaseTime", [2]);
await ethers.provider.send("evm_mine");
// Голосование должно упасть
await expect(
dle.connect(partner1).vote(0, true)
).to.be.revertedWith("Voting ended");
});
it("Не должен позволить исполнить предложение до окончания срока", async function () {
// Создаем предложение
const description = "Тестовое предложение";
const duration = 7 * 24 * 60 * 60;
const operation = "0x";
await dle.connect(partner1).createProposal(
description,
duration,
operation,
1
);
// Голосуем за предложение
await dle.connect(partner1).vote(0, true);
await dle.connect(partner2).vote(0, true);
await dle.connect(partner3).vote(0, true);
// Исполнение должно упасть
await expect(
dle.connect(partner1).executeProposal(0)
).to.be.revertedWith("Voting not ended");
});
});
});