// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /** * @title CoverageProbes * @notice TEST-ONLY helpers for the round-2 branch-coverage suite. None of these * is deployed to any network; they exist purely so that failure branches * which cannot be driven by an EOA become reachable from Hardhat. * * They are excluded from coverage instrumentation (see .solcover.js) so * the measured per-file table stays apples-to-apples with the baseline. */ /// @notice Refuses every plain AERE transfer (no receive, no fallback), but can /// still ORIGINATE calls via `exec`. This is what makes the /// `(bool ok, ) = x.call{value: v}(""); require(ok)` FALSE branches /// reachable: an EOA recipient always accepts value, a contract with no /// receive() does not. contract EtherRejector { /// Forward an arbitrary call so this contract can be the msg.sender that /// triggers a payout back to itself (which then fails). function exec(address target, bytes calldata data) external returns (bytes memory) { (bool ok, bytes memory ret) = target.call(data); if (!ok) { assembly { revert(add(ret, 0x20), mload(ret)) } } return ret; } /// Same, with value attached (used to register/delegate from a contract). function execValue(address target, bytes calldata data, uint256 value) external payable returns (bytes memory) { (bool ok, bytes memory ret) = target.call{ value: value }(data); if (!ok) { assembly { revert(add(ret, 0x20), mload(ret)) } } return ret; } /// Accept an ERC-721 so this contract can hold a fee-stream NFT. function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { return this.onERC721Received.selector; } // Deliberately NO receive() and NO fallback(): plain value transfers revert. } /// @notice `owner()` returns the zero address, exercising the /// `o != address(0)` FALSE side of AereFeeMonetizationV2.controlsContract /// (a target with an owner() that answers, but answers zero). contract ZeroOwnerProbe { function owner() external pure returns (address) { return address(0); } } /// @notice Has code but no `owner()` at all, so the staticcall hits the fallback /// and the try/catch CATCH arm in controlsContract is taken. contract NoOwnerProbe { uint256 public marker = 1; } /// @notice Accepts AERE and, on the FIRST value transfer after being armed, /// re-enters a target with stored calldata. The re-entry is made with a /// low-level call so the ReentrancyGuard's revert is CAUGHT here and the /// outer, legitimate call still completes — the same shape as /// MockReentrantBountyToken, which is what lets the blocked branch be /// observed instead of unwinding the whole transaction. contract ReentrantEtherProbe { address public target; bytes public payload; bool public armed; bool public reentryAttempted; bool public reentryBlocked; function arm(address _target, bytes calldata _payload) external { target = _target; payload = _payload; armed = true; reentryAttempted = false; reentryBlocked = false; } /// Originate a call (so this contract can be the delegator / NFT holder). function exec(address to, bytes calldata data) external returns (bytes memory) { (bool ok, bytes memory ret) = to.call(data); if (!ok) { assembly { revert(add(ret, 0x20), mload(ret)) } } return ret; } function execValue(address to, bytes calldata data, uint256 value) external payable returns (bytes memory) { (bool ok, bytes memory ret) = to.call{ value: value }(data); if (!ok) { assembly { revert(add(ret, 0x20), mload(ret)) } } return ret; } function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { return this.onERC721Received.selector; } function _tryReenter() internal { if (!armed || target == address(0)) return; armed = false; // one-shot, or the guard would be re-tested forever reentryAttempted = true; (bool ok, ) = target.call(payload); reentryBlocked = !ok; } receive() external payable { _tryReenter(); } } /// @notice ERC20 whose transferFrom re-enters a target once. AereSink.flush pulls /// with transferFrom BEFORE it does anything else, so this is the hook /// that reaches flush's own ReentrancyGuard. contract ReentrantFlushToken { mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; address public target; bytes public payload; bool public armed; bool public reentryAttempted; bool public reentryBlocked; function mint(address to, uint256 amt) external { balanceOf[to] += amt; } function arm(address _target, bytes calldata _payload) external { target = _target; payload = _payload; armed = true; } function approve(address spender, uint256 amt) external returns (bool) { allowance[msg.sender][spender] = amt; return true; } function _tryReenter() internal { if (!armed || target == address(0)) return; armed = false; reentryAttempted = true; (bool ok, ) = target.call(payload); reentryBlocked = !ok; } function transfer(address to, uint256 amt) external returns (bool) { balanceOf[msg.sender] -= amt; balanceOf[to] += amt; return true; } function transferFrom(address from, address to, uint256 amt) external returns (bool) { _tryReenter(); balanceOf[from] -= amt; balanceOf[to] += amt; return true; } } /// @notice ERC20 whose transfer REVERTS outright (rather than returning false). /// AereSink._safeTransfer checks `!ok || (ret.length > 0 && !decode)`; /// the return-false token drives the SECOND leg, and only a token that /// actually reverts drives the FIRST (`!ok`). contract RevertingTransferToken { mapping(address => uint256) public balanceOf; function mint(address to, uint256 amt) external { balanceOf[to] += amt; } function transfer(address, uint256) external pure returns (bool) { revert("token: transfer disabled"); } function transferFrom(address, address, uint256) external pure returns (bool) { revert("token: transferFrom disabled"); } function approve(address, uint256) external pure returns (bool) { return true; } } /// @notice V2-style router whose swap reverts with EMPTY revert data. /// AereSink's catch block does `err.length > 0 ? string(err) : "swap-failed"`. /// A router that reverts with a reason string drives the TRUE side; only /// a bare `revert()` (zero-length returndata) drives the FALSE side. contract EmptyRevertRouter { function swapExactTokensForTokens( uint256, uint256, address[] calldata, address, uint256 ) external pure returns (uint256[] memory) { revert(); } } /// @notice Price oracle that reverts for ONE designated asset only. /// AereSink._computeOracleFloor has TWO nested try/catch arms; the shared /// MockPriceOracle can only revert globally, which reaches the OUTER /// catch. Reverting for the AERE leg alone is the only way to reach the /// INNER catch. contract SelectiveRevertOracle { mapping(address => uint256) public prices; mapping(address => bool) public revertsFor; function setPrice(address asset, uint256 price) external { prices[asset] = price; } function setRevertFor(address asset, bool b) external { revertsFor[asset] = b; } function getPrice(address asset) external view returns (uint256) { require(!revertsFor[asset], "oracle: asset paused"); return prices[asset]; } }