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.
101 lines
4.9 KiB
Solidity
101 lines
4.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.23;
|
|
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
/**
|
|
* @title AereNavOracleAdapter — owner-attested per-asset RWA NAV price feed
|
|
* @notice Fixes audit finding F12. AereLendingOracle's FeedType.NavOracle path
|
|
* calls `IAereNavOracleAdapter(source).latestForAsset(bytes32)` and
|
|
* expects back a per-asset (nav, timestamp) pair it can normalise to an
|
|
* 18-decimal collateral price. The deployed AereNavOracle
|
|
* (0xC8D12E44f10b03477330b35115b432750831fEBD) does NOT and CANNOT
|
|
* satisfy that interface:
|
|
*
|
|
* - It stores Merkle ROOTS of per-epoch reserve snapshots, not
|
|
* per-asset values. Given only a bytes32 asset key you cannot
|
|
* recover a value from a root; you would need the full leaf
|
|
* preimage (asset, chainId, reserveAmount, decimals) PLUS a Merkle
|
|
* proof, and even then `verifyReserve` only returns a boolean.
|
|
* - Its leaf field is a total RESERVE AMOUNT (proof-of-backing, e.g.
|
|
* "5,000,000 USDC locked on mainnet"), which is a quantity, not a
|
|
* per-unit price. It does not map to a lending collateral price.
|
|
*
|
|
* So a passthrough adapter that "reads" the NavOracle is impossible.
|
|
* The honest fix (audit option (a)) is a PURPOSE-BUILT RWA-NAV price
|
|
* adapter with an OWNER-SET per-asset value feed — exactly the
|
|
* "Foundation-attested NAV pulled ... for RWAs that don't have a live
|
|
* market price" that AereLendingOracle's own NatSpec describes.
|
|
*
|
|
* DESIGN / SAFETY:
|
|
* - `setNav` is owner-only (Foundation). The adapter is the upstream
|
|
* FEED SOURCE for a NavOracle-type feed. The AereLendingOracle
|
|
* layered on top still enforces its own staleness window and
|
|
* deviation cap on every read, so a single attested value cannot
|
|
* jump the lending price beyond `maxDeviationBps`.
|
|
* - `latestForAsset` returns (0,0) for an un-attested asset, which
|
|
* AereLendingOracle already treats as InvalidFeedResponse. It never
|
|
* returns a future-dated timestamp.
|
|
* - NAV values are stored in the adapter's native decimals; the
|
|
* consuming feed's `sourceDecimals` handles normalisation. The
|
|
* Foundation convention is 18-decimal NAVs (sourceDecimals = 18).
|
|
*
|
|
* PROVENANCE:
|
|
* - `NAV_ORACLE` records the associated AereNavOracle whose public
|
|
* Merkle reserve attestations back these assets. It is stored for
|
|
* transparency ONLY and is NEVER read to derive a price (see above).
|
|
*/
|
|
contract AereNavOracleAdapter is Ownable {
|
|
|
|
/// @notice The associated reserve-attestation oracle (AereNavOracle).
|
|
/// Recorded for provenance; NOT read to derive prices (its data is
|
|
/// Merkle-rooted reserve totals, not per-asset prices).
|
|
address public immutable NAV_ORACLE;
|
|
|
|
struct Reading {
|
|
uint256 nav; // per-asset value in the adapter's native decimals (18 by convention)
|
|
uint64 timestamp; // block.timestamp at attestation
|
|
bool set;
|
|
}
|
|
|
|
/// @notice assetKey -> latest attested reading.
|
|
mapping(bytes32 => Reading) public readings;
|
|
|
|
/* --------------------------------- events ------------------------------- */
|
|
|
|
event NavSet(bytes32 indexed assetKey, uint256 nav, uint64 timestamp);
|
|
|
|
/* --------------------------------- errors ------------------------------- */
|
|
|
|
error ZeroNav();
|
|
|
|
/* ------------------------------- constructor ---------------------------- */
|
|
|
|
/// @param navOracle The associated AereNavOracle (provenance only; may be
|
|
/// address(0) if none). Not read for prices.
|
|
constructor(address navOracle) {
|
|
NAV_ORACLE = navOracle;
|
|
}
|
|
|
|
/* --------------------------------- admin -------------------------------- */
|
|
|
|
/// @notice Foundation attests the current per-asset NAV for `assetKey`.
|
|
/// Stamps `block.timestamp` so downstream staleness checks work.
|
|
function setNav(bytes32 assetKey, uint256 nav) external onlyOwner {
|
|
if (nav == 0) revert ZeroNav();
|
|
readings[assetKey] = Reading({ nav: nav, timestamp: uint64(block.timestamp), set: true });
|
|
emit NavSet(assetKey, nav, uint64(block.timestamp));
|
|
}
|
|
|
|
/* --------------------------------- view --------------------------------- */
|
|
|
|
/// @notice Matches AereLendingOracle's IAereNavOracleAdapter. Returns the
|
|
/// latest attested (nav, timestamp) for `assetKey`, or (0,0) if the
|
|
/// asset has never been attested (LendingOracle treats that as
|
|
/// InvalidFeedResponse).
|
|
function latestForAsset(bytes32 assetKey) external view returns (uint256 nav, uint64 timestamp) {
|
|
Reading storage r = readings[assetKey];
|
|
return (r.nav, r.timestamp);
|
|
}
|
|
}
|