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); }); });