// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /** * @title PolicyMath * @notice Deterministic GRU M0/M1 rules: 1.2× reserves on M0; ≤5× M1 vs M0 per line. * @dev Must match docs/hybx-omnl/HYBX_OMNL_POLICY_SPEC.md */ library PolicyMath { uint256 internal constant M0_NUM = 12; uint256 internal constant M0_DEN = 10; uint256 internal constant M1_CAP_NUM = 5; uint256 internal constant M1_CAP_DEN = 1; /// @notice Minimum reserves R required to back S0 units of M0 (ceil(1.2 * S0)). function minReservesForM0(uint256 s0) internal pure returns (uint256) { return Math.mulDiv(s0, M0_NUM, M0_DEN, Math.Rounding.Ceil); } /// @notice Maximum M1 supply allowed for S0 units of M0 (5 * S0). function maxM1ForM0(uint256 s0) internal pure returns (uint256) { return Math.mulDiv(s0, M1_CAP_NUM, M1_CAP_DEN); } function m0BackingOk(uint256 s0, uint256 r) internal pure returns (bool) { return r >= minReservesForM0(s0); } function m1ExpansionOk(uint256 s0, uint256 s1) internal pure returns (bool) { return s1 <= maxM1ForM0(s0); } function isCompliant(uint256 s0, uint256 s1, uint256 r) internal pure returns (bool) { return m0BackingOk(s0, r) && m1ExpansionOk(s0, s1); } function canMintM0(uint256 s0Before, uint256 mintAmount, uint256 r) internal pure returns (bool) { unchecked { uint256 s0After = s0Before + mintAmount; return m0BackingOk(s0After, r); } } function canMintM1(uint256 s0, uint256 s1Before, uint256 mintAmount) internal pure returns (bool) { unchecked { uint256 s1After = s1Before + mintAmount; return m1ExpansionOk(s0, s1After); } } }