Some checks failed
CI/CD Pipeline / Lint and Format (push) Failing after 46s
CI/CD Pipeline / Terraform Validation (push) Failing after 35s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 37s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 1m50s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 2m19s
Validation / validate-genesis (push) Successful in 51s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 10s
CI/CD Pipeline / Solidity Contracts (push) Failing after 12m56s
Validation / validate-smart-contracts (push) Failing after 12s
CI/CD Pipeline / Security Scanning (push) Failing after 15m52s
Validation / validate-security (push) Failing after 10m59s
Validation / validate-documentation (push) Failing after 17s
Validate Token List / validate (push) Failing after 30s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 26s
Verify Deployment / Verify Deployment (push) Failing after 56s
163 lines
6.2 KiB
Solidity
163 lines
6.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {InstrumentRegistry} from "./InstrumentRegistry.sol";
|
|
import {ReserveCommitmentStore} from "./ReserveCommitmentStore.sol";
|
|
import {OMNLCircuitBreaker} from "./OMNLCircuitBreaker.sol";
|
|
import {PolicyMath} from "./PolicyMath.sol";
|
|
|
|
/**
|
|
* @title ComplianceCore
|
|
* @notice Aggregates on-chain supply + attested reserves; enforces policy; emits audit events.
|
|
* @dev Integrated minters call assertCanMintM0/M1 before minting.
|
|
*/
|
|
contract ComplianceCore {
|
|
InstrumentRegistry public immutable registry;
|
|
ReserveCommitmentStore public immutable reserves;
|
|
OMNLCircuitBreaker public immutable breakers;
|
|
|
|
event ComplianceSnapshot(
|
|
bytes32 indexed lineId,
|
|
uint256 s0,
|
|
uint256 s1,
|
|
uint256 r,
|
|
uint256 validUntil,
|
|
bytes32 evidenceHash,
|
|
bytes32 merkleRoot,
|
|
bool m0Ok,
|
|
bool m1Ok,
|
|
bool attestationStale,
|
|
bool policyOk,
|
|
bool reportingCompliant
|
|
);
|
|
error ComplianceBlocked(bytes32 lineId, string reason);
|
|
error UnknownLine(bytes32 lineId);
|
|
|
|
constructor(address registry_, address reserves_, address breakers_) {
|
|
require(registry_ != address(0) && reserves_ != address(0) && breakers_ != address(0), "ComplianceCore: zero");
|
|
registry = InstrumentRegistry(registry_);
|
|
reserves = ReserveCommitmentStore(reserves_);
|
|
breakers = OMNLCircuitBreaker(breakers_);
|
|
}
|
|
|
|
function _loadSupplies(bytes32 lineId) internal view returns (uint256 s0, uint256 s1) {
|
|
InstrumentRegistry.Line memory line = registry.getLine(lineId);
|
|
if (line.tokenM0 == address(0)) revert UnknownLine(lineId);
|
|
s0 = IERC20(line.tokenM0).totalSupply();
|
|
s1 = IERC20(line.tokenM1).totalSupply();
|
|
}
|
|
|
|
function _stale(uint256 validUntil) internal view returns (bool) {
|
|
return block.timestamp > validUntil;
|
|
}
|
|
|
|
/**
|
|
* @notice Full deterministic compliance view for dashboards and off-chain API.
|
|
* @dev reportingCompliant is false when attestation is stale or breakers trip, even if pure math (policyOk) holds.
|
|
*/
|
|
function getCompliance(bytes32 lineId)
|
|
external
|
|
view
|
|
returns (
|
|
uint256 s0,
|
|
uint256 s1,
|
|
uint256 r,
|
|
uint256 validUntil,
|
|
bytes32 evidenceHash,
|
|
bytes32 merkleRoot,
|
|
uint256 minR,
|
|
uint256 maxS1,
|
|
bool m0Ok,
|
|
bool m1Ok,
|
|
bool attestationStale,
|
|
bool policyOk,
|
|
bool operational,
|
|
bool reportingCompliant
|
|
)
|
|
{
|
|
(s0, s1) = _loadSupplies(lineId);
|
|
ReserveCommitmentStore.Commitment memory c = reserves.getCommitment(lineId);
|
|
r = c.R;
|
|
validUntil = c.validUntil;
|
|
evidenceHash = c.evidenceHash;
|
|
merkleRoot = c.merkleRoot;
|
|
minR = PolicyMath.minReservesForM0(s0);
|
|
maxS1 = PolicyMath.maxM1ForM0(s0);
|
|
attestationStale = _stale(validUntil);
|
|
m0Ok = PolicyMath.m0BackingOk(s0, r);
|
|
m1Ok = PolicyMath.m1ExpansionOk(s0, s1);
|
|
policyOk = PolicyMath.isCompliant(s0, s1, r);
|
|
operational = breakers.isLineOperational(lineId);
|
|
reportingCompliant = policyOk && !attestationStale && operational;
|
|
}
|
|
|
|
/// @notice Reverts if global/line pause or policy would fail after minting delta M0.
|
|
function assertCanMintM0(bytes32 lineId, uint256 mintAmount) external view {
|
|
if (!breakers.isLineOperational(lineId)) revert ComplianceBlocked(lineId, "paused");
|
|
InstrumentRegistry.Line memory line = registry.getLine(lineId);
|
|
if (line.tokenM0 == address(0)) revert UnknownLine(lineId);
|
|
if (!line.active) revert ComplianceBlocked(lineId, "inactive");
|
|
|
|
ReserveCommitmentStore.Commitment memory c = reserves.getCommitment(lineId);
|
|
if (breakers.enforceStaleBlockM0() && _stale(c.validUntil)) {
|
|
revert ComplianceBlocked(lineId, "stale_attestation");
|
|
}
|
|
|
|
uint256 s0 = IERC20(line.tokenM0).totalSupply();
|
|
if (!PolicyMath.canMintM0(s0, mintAmount, c.R)) {
|
|
revert ComplianceBlocked(lineId, "m0_backing");
|
|
}
|
|
}
|
|
|
|
/// @notice Reverts if global/line pause or M1 cap exceeded after mint.
|
|
function assertCanMintM1(bytes32 lineId, uint256 mintAmount) external view {
|
|
if (!breakers.isLineOperational(lineId)) revert ComplianceBlocked(lineId, "paused");
|
|
InstrumentRegistry.Line memory line = registry.getLine(lineId);
|
|
if (line.tokenM0 == address(0)) revert UnknownLine(lineId);
|
|
if (!line.active) revert ComplianceBlocked(lineId, "inactive");
|
|
|
|
ReserveCommitmentStore.Commitment memory c = reserves.getCommitment(lineId);
|
|
if (breakers.enforceStaleBlockM1() && _stale(c.validUntil)) {
|
|
revert ComplianceBlocked(lineId, "stale_attestation");
|
|
}
|
|
|
|
uint256 s0 = IERC20(line.tokenM0).totalSupply();
|
|
uint256 s1 = IERC20(line.tokenM1).totalSupply();
|
|
if (!PolicyMath.canMintM1(s0, s1, mintAmount)) {
|
|
revert ComplianceBlocked(lineId, "m1_cap");
|
|
}
|
|
}
|
|
|
|
/// @notice Permissionless heartbeat for indexers — emits current snapshot (costs gas for caller).
|
|
function emitComplianceSnapshot(bytes32 lineId) external {
|
|
InstrumentRegistry.Line memory line = registry.getLine(lineId);
|
|
if (line.tokenM0 == address(0)) revert UnknownLine(lineId);
|
|
|
|
uint256 s0 = IERC20(line.tokenM0).totalSupply();
|
|
uint256 s1 = IERC20(line.tokenM1).totalSupply();
|
|
ReserveCommitmentStore.Commitment memory c = reserves.getCommitment(lineId);
|
|
bool stale_ = _stale(c.validUntil);
|
|
bool m0Ok = PolicyMath.m0BackingOk(s0, c.R);
|
|
bool m1Ok = PolicyMath.m1ExpansionOk(s0, s1);
|
|
bool policyOk = PolicyMath.isCompliant(s0, s1, c.R);
|
|
bool operational = breakers.isLineOperational(lineId);
|
|
bool reporting = policyOk && !stale_ && operational;
|
|
|
|
emit ComplianceSnapshot(
|
|
lineId,
|
|
s0,
|
|
s1,
|
|
c.R,
|
|
c.validUntil,
|
|
c.evidenceHash,
|
|
c.merkleRoot,
|
|
m0Ok,
|
|
m1Ok,
|
|
stale_,
|
|
policyOk,
|
|
reporting
|
|
);
|
|
}
|
|
}
|