aere-contracts/contracts/AereFeeBurnVault.sol
Aere Network acac2f00a6
Some checks failed
contracts-ci / Install (lockfile) → compile → full test suite (push) Has been cancelled
contracts-ci / Ethereum interop (EIP-2537 BLS, prague hardfork) (push) Has been cancelled
contracts-ci / PQC known-answer tests (NIST vectors) (push) Has been cancelled
contracts-ci / Coverage (scoped, with artifacts) (push) Has been cancelled
The unpublished line of work joins the sanitized public line
The published line and the local line had no common ancestor: the public one
carried the redaction pass, the local one carried three weeks of corrections
that never shipped. This commit ports the local work onto the public line,
keeps every public redaction, and extends the same discretion to seven client
mentions that were still named in published comments.

Carried: LICENSE year and LICENSING.md; the measured burn figures replacing
the deflation claim (the vault holds ~0.137 AERE of 2.8 billion, and burn is
a share of validator coinbase revenue, which is zero today); 'audited' removed
from next to Bouncy Castle; citation paths rewritten to published form with
CITATIONS-UNRESOLVED.md remeasured 2026-08-11; VERIFY-POLICY.md; slashing and
ownership comments brought down to what the code does; the AerePyth repair;
the shutter test helper the tests cite; runnable package.json entries; the CI
file split into a GitHub/Gitea twin pair with a real measured test-run status;
and the .gitignore hardening written after a compiled artifact leaked a local
path in a sibling repository. A false '2-of-3 multisig' description of the
owner account is corrected to what the chain measures: an externally owned
account. The self-audit findings catalog stays unpublished pending an explicit
decision.
2026-08-15 13:59:30 +03:00

139 lines
7.2 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title AereFeeBurnVault
* @notice Permanent, on-chain burn destination for protocol revenue. Read the
* two sections below before quoting any burn number from this contract:
* what is burned is not what the whitepaper wording suggests, and the
* amount burned to date is effectively zero.
*
* WHAT IS BURNED, STATED PRECISELY. The Aere burn is a cut of the VALIDATOR
* (coinbase) block reward. It is NOT a base-fee burn and NOT a share of
* transaction fees. The default rate is 37.5% (burnBps = 3750) and it is hard
* capped at 50%: AereCoinbaseSplitterV2 enforces the cap with the named
* constant MAX_BURN_BPS = 5000, and AereCoinbaseSplitter (V1) enforces the same
* ceiling with an unnamed `require(_bps <= 5000)` inside `setBurnBps`. Whitepaper
* section 3.3 words the burn as a share of all transaction fees; that wording
* predates the implemented mechanism and is superseded by this paragraph, which
* matches AERE-PROTOCOL-SPECIFICATION.md, AERE-EIP-COMPATIBILITY-MATRIX.md and
* AERE-ENGINEERING-SECURITY-SPEC.md, all three of which state in terms that the
* Aere burn must not be described as a base-fee burn.
*
* REALIZED BURN TO DATE, MEASURED. Chain 2800, deployment
* 0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6, measured at block 11,967,981 on
* 2026-08-02 with `eth_getProof` (not with a nonce read, which can return a
* false zero outside the public RPC state window):
*
* balance = 137352594046167719 wei = 0.13735 AERE
* totalBurnedAERE() = 137352594046167719 wei (identical)
* totalSentToZero() = 0 (`sweepToZero` has NEVER run)
*
* Against a fixed supply of 2,800,000,000 AERE that is about 0.0000000049
* percent of supply. The reason is arithmetic, not malfunction: validator
* coinbase revenue on this chain is currently zero, and a percentage of zero is
* zero. Every burn percentage stated anywhere in this repository is therefore a
* CONDITIONAL RATE that would apply to future validator revenue, not a
* description of tokens destroyed to date. AERE is NOT deflationary today.
* Anyone can recheck both numbers with one `eth_getProof` and one `eth_call`
* against the address above, and is encouraged to.
*
* BYTECODE IDENTITY AFTER THIS CORRECTION, MEASURED. The two sections above are
* a comment-only change, and a comment-only change still moves the appended CBOR
* metadata hash, because that hash covers the source text. Recompiled with the
* profile the deployed contract was built under (solc 0.8.19, optimizer enabled
* with runs = 200, viaIR = true) and compared byte for byte against the live code
* at 0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6, this file yields the same 1313
* bytes with ZERO differing bytes outside the metadata trailer. Every difference
* falls inside that trailer, which is the fingerprint of the source text itself.
* The executable contract is unchanged; only its fingerprint moved. In Sourcify
* terms the older text was a full (exact) match and this text is a partial match.
* The previous, misleading text is what produced the deployed fingerprint; that is
* a reason to record the fact here, not a reason to keep publishing a false claim.
*
* Design:
* - Stateless sink: anyone can send native AERE here via `burn()`, or any
* ERC-20 via `burnToken()`. The contract has NO withdraw function and
* NO admin escape hatch, so funds that enter are permanently removed
* from circulation.
* - Counters: tracks `totalBurnedAERE` and per-token `totalBurnedToken[token]`
* so realized burn can be queried in a single read. `totalBurnedAERE` is a
* cumulative arrival counter, not a supply-destruction figure; see the
* `sweepToZero` note below for the difference.
* - Sources: protocol contracts that earn revenue (AereSwapRouter, the NFT
* marketplace, future card-fee router, etc.) can route a configurable
* share to this address. The vault does not care WHO sends; any value
* arriving here is burned.
*
* This contract holds no privilege over any other system. Ownable is not
* needed because there are no admin operations.
*/
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract AereFeeBurnVault {
/// Total native AERE that has flowed into this contract.
/// Equal to address(this).balance EXCEPT after the eventual
/// "send to zero-address" sweep. See the `sweepToZero` note below.
uint256 public totalBurnedAERE;
/// Cumulative native AERE actually forwarded to address(0).
uint256 public totalSentToZero;
/// Per-ERC20 lifetime burn counter.
mapping(address => uint256) public totalBurnedToken;
event AereBurned(address indexed from, uint256 amount, uint256 newTotal);
event TokenBurned(address indexed token, address indexed from, uint256 amount, uint256 newTotal);
event AereSentToZero(uint256 amount, uint256 newTotalSentToZero);
/// Plain ETH transfer = burn.
receive() external payable {
totalBurnedAERE += msg.value;
emit AereBurned(msg.sender, msg.value, totalBurnedAERE);
}
/// Explicit native-AERE burn (same as `receive`, but lets callers attach a tag in the event log).
function burn() external payable {
totalBurnedAERE += msg.value;
emit AereBurned(msg.sender, msg.value, totalBurnedAERE);
}
/// Burn an ERC-20 by pulling it from `msg.sender` via prior approval.
/// Tokens are accepted but never sent anywhere; they stay on this contract
/// forever. Counters reflect cumulative arrivals.
function burnToken(address token, uint256 amount) external {
require(token != address(0), "BurnVault: zero token");
require(amount > 0, "BurnVault: zero amount");
require(IERC20(token).transferFrom(msg.sender, address(this), amount), "BurnVault: transferFrom failed");
totalBurnedToken[token] += amount;
emit TokenBurned(token, msg.sender, amount, totalBurnedToken[token]);
}
/// Optional: forward accumulated native AERE to address(0).
/// QBFT Besu permits sending to address(0); the AERE is then unreachable.
/// Anyone can call this; it has no parameters and no admin gate. This is
/// the final removal step. Until it is called, AERE sits on this contract,
/// which is unreachable for any other reason but is still a live account.
/// MEASURED at block 11,967,981 on 2026-08-02: `totalSentToZero` is 0, so
/// this function has NEVER been called on chain 2800 and no AERE has been
/// sent to address(0) by this vault.
function sweepToZero() external {
uint256 bal = address(this).balance;
require(bal > 0, "BurnVault: nothing to sweep");
totalSentToZero += bal;
(bool ok, ) = address(0).call{ value: bal }("");
require(ok, "BurnVault: zero-send failed");
emit AereSentToZero(bal, totalSentToZero);
}
/// Convenience read.
function currentAEREBalance() external view returns (uint256) {
return address(this).balance;
}
}