ваше сообщение коммита

This commit is contained in:
2025-07-29 18:07:21 +03:00
parent ce42899afc
commit 0f2270a08a
58 changed files with 5367 additions and 5931 deletions

482
backend/test/DLE.test.js Normal file
View File

@@ -0,0 +1,482 @@
/**
* 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");
});
});
});