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.
211 lines
9.2 KiB
JavaScript
211 lines
9.2 KiB
JavaScript
// =============================================================================
|
|
// REAL Hardhat coverage tests for contracts/AereFeeBurnVault.sol.
|
|
//
|
|
// AereFeeBurnVault is an IN-USE contract: live on chain 2800 at
|
|
// 0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6 (deployments/fee-burn-vault.json),
|
|
// referenced in aerenew/sdk-js/src/addresses.ts, and the destination of 37.5%
|
|
// of protocol fees routed by AereCoinbaseSplitter. The scoped coverage run in
|
|
// aerenew/docs/AERE-TEST-COVERAGE-REPORT.md measured it at 0/0/0/0 because none
|
|
// of the six economic-core tests call its functions.
|
|
//
|
|
// These tests exercise EVERY function and EVERY reachable branch, prioritising
|
|
// branch coverage (revert paths + boundary conditions), as required by the task.
|
|
//
|
|
// The one genuinely unreachable branch is the `require(ok, "zero-send failed")`
|
|
// FALSE path in sweepToZero(): a low-level call to address(0) with sufficient
|
|
// balance always succeeds on the EVM (it is a value transfer to a bare account),
|
|
// so that failure branch cannot be driven from a test. It is documented here
|
|
// rather than faked.
|
|
//
|
|
// No em-dashes anywhere in this file.
|
|
// =============================================================================
|
|
|
|
const { expect } = require("chai");
|
|
const { ethers } = require("hardhat");
|
|
|
|
const ONE = 10n ** 18n;
|
|
const ZERO = ethers.ZeroAddress;
|
|
const ADDR_ZERO_BURN = "0x0000000000000000000000000000000000000000";
|
|
|
|
describe("AereFeeBurnVault (in-use fee burn endpoint) — full branch battery", function () {
|
|
let deployer, alice, bob, vault, vaultAddr, waere, waereAddr;
|
|
|
|
beforeEach(async function () {
|
|
[deployer, alice, bob] = await ethers.getSigners();
|
|
|
|
vault = await (await ethers.getContractFactory("AereFeeBurnVault")).deploy();
|
|
await vault.waitForDeployment();
|
|
vaultAddr = await vault.getAddress();
|
|
|
|
// A real, compliant ERC20 whose transferFrom returns true: WAERE.
|
|
waere = await (await ethers.getContractFactory("contracts/WAERE.sol:WAERE")).deploy();
|
|
await waere.waitForDeployment();
|
|
waereAddr = await waere.getAddress();
|
|
});
|
|
|
|
/* ------------------------------- native burn ------------------------------ */
|
|
|
|
describe("receive() — plain native AERE transfer is a burn", function () {
|
|
it("credits totalBurnedAERE and emits AereBurned", async function () {
|
|
const amt = 3n * ONE;
|
|
await expect(alice.sendTransaction({ to: vaultAddr, value: amt }))
|
|
.to.emit(vault, "AereBurned")
|
|
.withArgs(alice.address, amt, amt);
|
|
|
|
expect(await vault.totalBurnedAERE()).to.equal(amt);
|
|
expect(await ethers.provider.getBalance(vaultAddr)).to.equal(amt);
|
|
expect(await vault.currentAEREBalance()).to.equal(amt);
|
|
});
|
|
|
|
it("accumulates across multiple senders (running total in the event)", async function () {
|
|
await alice.sendTransaction({ to: vaultAddr, value: ONE });
|
|
await bob.sendTransaction({ to: vaultAddr, value: 2n * ONE });
|
|
expect(await vault.totalBurnedAERE()).to.equal(3n * ONE);
|
|
expect(await vault.currentAEREBalance()).to.equal(3n * ONE);
|
|
});
|
|
});
|
|
|
|
describe("burn() — explicit native burn entrypoint", function () {
|
|
it("credits totalBurnedAERE and emits AereBurned with the running total", async function () {
|
|
const amt = 5n * ONE;
|
|
await expect(vault.connect(alice).burn({ value: amt }))
|
|
.to.emit(vault, "AereBurned")
|
|
.withArgs(alice.address, amt, amt);
|
|
expect(await vault.totalBurnedAERE()).to.equal(amt);
|
|
});
|
|
|
|
it("burn() and receive() share the same counter", async function () {
|
|
await vault.connect(alice).burn({ value: ONE });
|
|
await alice.sendTransaction({ to: vaultAddr, value: ONE });
|
|
expect(await vault.totalBurnedAERE()).to.equal(2n * ONE);
|
|
});
|
|
|
|
it("burn() with zero value is a no-op success that still emits (amount 0)", async function () {
|
|
await expect(vault.connect(alice).burn({ value: 0 }))
|
|
.to.emit(vault, "AereBurned")
|
|
.withArgs(alice.address, 0, 0);
|
|
expect(await vault.totalBurnedAERE()).to.equal(0);
|
|
});
|
|
});
|
|
|
|
/* -------------------------------- burnToken ------------------------------- */
|
|
|
|
describe("burnToken() — ERC20 burn pull path (both sides of all 3 requires)", function () {
|
|
it("SUCCESS: pulls the token, bumps per-token counter, emits TokenBurned", async function () {
|
|
const amt = 7n * ONE;
|
|
// Fund alice with WAERE and approve the vault.
|
|
await alice.sendTransaction({ to: waereAddr, value: amt });
|
|
await waere.connect(alice).approve(vaultAddr, amt);
|
|
|
|
await expect(vault.connect(alice).burnToken(waereAddr, amt))
|
|
.to.emit(vault, "TokenBurned")
|
|
.withArgs(waereAddr, alice.address, amt, amt);
|
|
|
|
expect(await vault.totalBurnedToken(waereAddr)).to.equal(amt);
|
|
expect(await waere.balanceOf(vaultAddr)).to.equal(amt);
|
|
// Native counter untouched by an ERC20 burn.
|
|
expect(await vault.totalBurnedAERE()).to.equal(0);
|
|
});
|
|
|
|
it("SUCCESS: per-token counter accumulates across calls", async function () {
|
|
await alice.sendTransaction({ to: waereAddr, value: 10n * ONE });
|
|
await waere.connect(alice).approve(vaultAddr, 10n * ONE);
|
|
await vault.connect(alice).burnToken(waereAddr, 4n * ONE);
|
|
await vault.connect(alice).burnToken(waereAddr, 6n * ONE);
|
|
expect(await vault.totalBurnedToken(waereAddr)).to.equal(10n * ONE);
|
|
});
|
|
|
|
it("REVERT: zero token address (require token != address(0))", async function () {
|
|
await expect(vault.connect(alice).burnToken(ZERO, ONE)).to.be.revertedWith(
|
|
"BurnVault: zero token"
|
|
);
|
|
});
|
|
|
|
it("REVERT: zero amount (require amount > 0)", async function () {
|
|
await expect(vault.connect(alice).burnToken(waereAddr, 0)).to.be.revertedWith(
|
|
"BurnVault: zero amount"
|
|
);
|
|
});
|
|
|
|
it("REVERT: transferFrom returns false (require ok) via a non-compliant token", async function () {
|
|
const bad = await (await ethers.getContractFactory("MockReturnFalseERC20")).deploy();
|
|
await bad.waitForDeployment();
|
|
const badAddr = await bad.getAddress();
|
|
await bad.mint(alice.address, ONE);
|
|
await bad.connect(alice).approve(vaultAddr, ONE);
|
|
|
|
await expect(vault.connect(alice).burnToken(badAddr, ONE)).to.be.revertedWith(
|
|
"BurnVault: transferFrom failed"
|
|
);
|
|
// Counter never moved on the failed pull.
|
|
expect(await vault.totalBurnedToken(badAddr)).to.equal(0);
|
|
});
|
|
|
|
it("REVERT: transferFrom reverts (no approval) surfaces the token's own revert", async function () {
|
|
await alice.sendTransaction({ to: waereAddr, value: ONE });
|
|
// No approve() -> WAERE.transferFrom reverts on insufficient allowance.
|
|
await expect(vault.connect(alice).burnToken(waereAddr, ONE)).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
/* ------------------------------- sweepToZero ------------------------------ */
|
|
|
|
describe("sweepToZero() — forward accumulated native AERE to address(0)", function () {
|
|
it("SUCCESS: sends the whole balance to 0x0, bumps totalSentToZero, emits", async function () {
|
|
const amt = 8n * ONE;
|
|
await vault.connect(alice).burn({ value: amt });
|
|
expect(await vault.currentAEREBalance()).to.equal(amt);
|
|
|
|
const zeroBefore = await ethers.provider.getBalance(ADDR_ZERO_BURN);
|
|
|
|
await expect(vault.connect(bob).sweepToZero())
|
|
.to.emit(vault, "AereSentToZero")
|
|
.withArgs(amt, amt);
|
|
|
|
// Vault emptied, sent-to-zero counter reflects the sweep.
|
|
expect(await vault.currentAEREBalance()).to.equal(0);
|
|
expect(await vault.totalSentToZero()).to.equal(amt);
|
|
// address(0) actually received the value.
|
|
expect(await ethers.provider.getBalance(ADDR_ZERO_BURN)).to.equal(zeroBefore + amt);
|
|
// The gross-burned counter is a lifetime figure and is NOT reset by a sweep.
|
|
expect(await vault.totalBurnedAERE()).to.equal(amt);
|
|
});
|
|
|
|
it("SUCCESS: two sweeps accumulate totalSentToZero", async function () {
|
|
await vault.connect(alice).burn({ value: 2n * ONE });
|
|
await vault.connect(bob).sweepToZero();
|
|
await vault.connect(alice).burn({ value: 3n * ONE });
|
|
await vault.connect(bob).sweepToZero();
|
|
expect(await vault.totalSentToZero()).to.equal(5n * ONE);
|
|
expect(await vault.currentAEREBalance()).to.equal(0);
|
|
});
|
|
|
|
it("REVERT: nothing to sweep when balance is zero (require bal > 0)", async function () {
|
|
await expect(vault.connect(alice).sweepToZero()).to.be.revertedWith(
|
|
"BurnVault: nothing to sweep"
|
|
);
|
|
});
|
|
|
|
it("REVERT: nothing to sweep AFTER a full sweep empties the vault", async function () {
|
|
await vault.connect(alice).burn({ value: ONE });
|
|
await vault.connect(bob).sweepToZero();
|
|
// Second sweep with a zero balance reverts.
|
|
await expect(vault.connect(bob).sweepToZero()).to.be.revertedWith(
|
|
"BurnVault: nothing to sweep"
|
|
);
|
|
});
|
|
});
|
|
|
|
/* -------------------------------- read views ------------------------------ */
|
|
|
|
describe("currentAEREBalance() view", function () {
|
|
it("tracks the live native balance across burn and sweep", async function () {
|
|
expect(await vault.currentAEREBalance()).to.equal(0);
|
|
await vault.connect(alice).burn({ value: 4n * ONE });
|
|
expect(await vault.currentAEREBalance()).to.equal(4n * ONE);
|
|
await vault.connect(bob).sweepToZero();
|
|
expect(await vault.currentAEREBalance()).to.equal(0);
|
|
});
|
|
});
|
|
});
|