// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; /** * @title AereTreasury * @notice DAO treasury for AERE Network. Holds protocol-owned AERE. * @dev Spends are queued by the owner (governance contract post-handover) with a * timelock; anyone can execute after the delay. Provides transparency and * gives the community a window to react to malicious proposals. */ contract AereTreasury is Ownable, ReentrancyGuard { uint256 public constant TIMELOCK = 2 days; struct Spend { address to; uint256 amount; uint64 eta; bool executed; bool cancelled; string reason; } Spend[] public spends; event Funded(address indexed from, uint256 amount); event SpendQueued(uint256 indexed id, address indexed to, uint256 amount, uint64 eta, string reason); event SpendExecuted(uint256 indexed id); event SpendCancelled(uint256 indexed id); receive() external payable { emit Funded(msg.sender, msg.value); } function queueSpend(address to, uint256 amount, string calldata reason) external onlyOwner returns (uint256 id) { require(to != address(0) && amount > 0, "params"); require(amount <= address(this).balance, "insufficient"); uint64 eta = uint64(block.timestamp + TIMELOCK); spends.push(Spend({to: to, amount: amount, eta: eta, executed: false, cancelled: false, reason: reason})); id = spends.length - 1; emit SpendQueued(id, to, amount, eta, reason); } function executeSpend(uint256 id) external nonReentrant { Spend storage s = spends[id]; require(!s.executed && !s.cancelled, "done"); require(block.timestamp >= s.eta, "timelock"); require(address(this).balance >= s.amount, "balance"); s.executed = true; (bool ok, ) = s.to.call{value: s.amount}(""); require(ok, "transfer"); emit SpendExecuted(id); } function cancelSpend(uint256 id) external onlyOwner { Spend storage s = spends[id]; require(!s.executed && !s.cancelled, "done"); s.cancelled = true; emit SpendCancelled(id); } function spendCount() external view returns (uint256) { return spends.length; } }