aere-contracts/contracts/compliance/ChainalysisOracleWrapper.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
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.
2026-07-20 01:02:37 +03:00

78 lines
3.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title ChainalysisOracleWrapper — read-only proxy to Chainalysis's free
* on-chain sanctions oracle
* @notice Chainalysis publishes a free, public sanctions oracle on Ethereum
* mainnet (and other major chains) exposing `isSanctioned(address)`.
* AERE wraps that oracle on Chain 2800 by holding the deployed
* address as an immutable, providing the same interface, and
* letting AERE dApps + institutional integrators query a SINGLE
* endpoint without having to know the upstream provider's address.
*
* This contract makes a forwarding STATICCALL to the upstream
* oracle. If the upstream rejects (e.g. paused, migrated, doesn't
* exist on AERE yet), this contract returns false rather than
* reverting — failing closed but not bricking the dApp.
*
* @dev Phase 1 (Q3 2026 ship):
* - UPSTREAM_ORACLE is settable ONCE by Foundation owner via
* setUpstream() and immutable after `lockUpstream()`.
* - Foundation MUST call lockUpstream() before declaring the
* registry "production-ready". Pre-lock, the contract is
* considered configurable.
*
* Phase 2 (Q1 2027):
* - Multiple upstream oracles polled, response aggregated via
* "ANY-true" rule (sanctioned by any source ⇒ sanctioned).
* - Per-source attestation events for transparency.
*
* FORWARD INTERFACE: bool isSanctioned(address)
* (matches Chainalysis canonical SanctionsList contract)
*/
interface IChainalysisUpstream {
function isSanctioned(address account) external view returns (bool);
}
contract ChainalysisOracleWrapper is Ownable {
address public upstreamOracle;
bool public locked;
event UpstreamSet(address oldOracle, address newOracle);
event UpstreamLocked(address finalOracle);
error AlreadyLocked();
error ZeroAddress();
function setUpstream(address newOracle) external onlyOwner {
if (locked) revert AlreadyLocked();
if (newOracle == address(0)) revert ZeroAddress();
emit UpstreamSet(upstreamOracle, newOracle);
upstreamOracle = newOracle;
}
function lockUpstream() external onlyOwner {
if (locked) revert AlreadyLocked();
if (upstreamOracle == address(0)) revert ZeroAddress();
locked = true;
emit UpstreamLocked(upstreamOracle);
}
/// @notice Returns true if the upstream Chainalysis oracle reports
/// `account` as sanctioned. Returns false on any upstream
/// error (failing closed but never reverting the consumer).
function isSanctioned(address account) external view returns (bool sanctioned) {
address up = upstreamOracle;
if (up == address(0)) return false;
try IChainalysisUpstream(up).isSanctioned(account) returns (bool s) {
return s;
} catch {
return false;
}
}
}