aere-contracts/test/gov-stack-selftest.local.js
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

110 lines
5.0 KiB
JavaScript

const { expect } = require("chai");
const { ethers, network } = require("hardhat");
// Local validation of the AereGovernor + TimelockController binding machinery
// (block-number clock). Mirrors scripts/deploy-governance-stack.js self-test but
// drives block progression with evm_mine.
async function mine(n = 1) {
for (let i = 0; i < n; i++) await network.provider.send("evm_mine");
}
describe("AereGovernor binding self-test (local)", function () {
it("propose -> vote -> queue -> execute flips an owned target", async function () {
const [deployer] = await ethers.getSigners();
const Votes = await ethers.getContractFactory("GovSelfTestVotes");
const votes = await Votes.deploy();
await votes.waitForDeployment();
await (await votes.mint(deployer.address, ethers.parseEther("1000000"))).wait();
await (await votes.delegate(deployer.address)).wait();
const TL = await ethers.getContractFactory("TimelockController");
const timelock = await TL.deploy(0, [], [], deployer.address);
await timelock.waitForDeployment();
const Gov = await ethers.getContractFactory("AereGovernor");
const gov = await Gov.deploy(await votes.getAddress(), await timelock.getAddress(), 1, 5, 0, 4);
await gov.waitForDeployment();
const PROPOSER = await timelock.PROPOSER_ROLE();
const EXECUTOR = await timelock.EXECUTOR_ROLE();
const CANCELLER = await timelock.CANCELLER_ROLE();
await (await timelock.grantRole(PROPOSER, await gov.getAddress())).wait();
await (await timelock.grantRole(EXECUTOR, await gov.getAddress())).wait();
await (await timelock.grantRole(CANCELLER, await gov.getAddress())).wait();
const Target = await ethers.getContractFactory("GovSelfTestTarget");
const target = await Target.deploy();
await target.waitForDeployment();
await (await target.transferOwnership(await timelock.getAddress())).wait();
expect(await target.owner()).to.equal(await timelock.getAddress());
expect(await target.value()).to.equal(0n);
await mine(2);
const targets = [await target.getAddress()];
const values = [0n];
const calldatas = [target.interface.encodeFunctionData("setValue", [42])];
const description = "local selftest setValue(42)";
const descHash = ethers.id(description);
const proposalId = await gov.hashProposal(targets, values, calldatas, descHash);
await (await gov.propose(targets, values, calldatas, description)).wait();
const snapshot = await gov.proposalSnapshot(proposalId);
const deadline = await gov.proposalDeadline(proposalId);
// advance past snapshot -> Active
const cur = await ethers.provider.getBlockNumber();
await mine(Number(snapshot) - cur + 1);
expect(Number(await gov.state(proposalId))).to.equal(1); // Active
await (await gov.castVote(proposalId, 1)).wait();
const cur2 = await ethers.provider.getBlockNumber();
await mine(Number(deadline) - cur2 + 1);
expect(Number(await gov.state(proposalId))).to.equal(4); // Succeeded
await (await gov.queue(targets, values, calldatas, descHash)).wait();
expect(Number(await gov.state(proposalId))).to.equal(5); // Queued
await mine(1);
await (await gov.execute(targets, values, calldatas, descHash)).wait();
expect(Number(await gov.state(proposalId))).to.equal(7); // Executed
expect(await target.value()).to.equal(42n);
});
it("gAERE adapter is non-transferable and zero-supply against empty staking", async function () {
const [deployer, other] = await ethers.getSigners();
const Mock = await ethers.getContractFactory("MockStakingForVotes");
const stakingMock = await Mock.deploy();
await stakingMock.waitForDeployment();
const lockedMock = await Mock.deploy();
await lockedMock.waitForDeployment();
const G = await ethers.getContractFactory("AereStakedGovVotes");
const g = await G.deploy(await stakingMock.getAddress(), await lockedMock.getAddress());
await g.waitForDeployment();
// empty staking -> zero power
expect(await g.stakePowerOf(deployer.address)).to.equal(0n);
await (await g.sync(deployer.address)).wait();
expect(await g.totalSupply()).to.equal(0n);
// seed mock delegation, sync mints mirrored balance
await (await stakingMock.setValidator(deployer.address, ethers.parseEther("500"))).wait();
await (await stakingMock.setDelegation(deployer.address, other.address, ethers.parseEther("1234"))).wait();
expect(await g.stakePowerOf(other.address)).to.equal(ethers.parseEther("1234"));
expect(await g.stakePowerOf(deployer.address)).to.equal(ethers.parseEther("500")); // self-stake
await (await g.sync(other.address)).wait();
expect(await g.balanceOf(other.address)).to.equal(ethers.parseEther("1234"));
// non-transferable
await expect(g.connect(other).transfer(deployer.address, 1n)).to.be.revertedWith("gAERE: non-transferable mirror");
// unstake -> sync burns
await (await stakingMock.setDelegation(deployer.address, other.address, 0n)).wait();
await (await g.sync(other.address)).wait();
expect(await g.balanceOf(other.address)).to.equal(0n);
});
});