Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/tranches/Accounting.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { UD60x18, pow, mul } from "@prb/math/src/ud60x18/Math.sol";
import { IAccounting } from "./interfaces/IAccounting.sol";
import { IStrataCDO } from "./interfaces/IStrataCDO.sol";
import { IAprPairFeed } from "./interfaces/IAprPairFeed.sol";
import { CDOComponent } from "./base/CDOComponent.sol";
import { UD60x18Ext } from "./utils/UD60x18Ext.sol";
/**
* @title CDO::Accounting
* @dev Pure math contract to track the in-flow and out-flow of assets and balance the gain/loss between Junior (Jrt) and Senior (Srt) Tranche Value Locked (TVL).
*/
contract Accounting is IAccounting, CDOComponent {
uint256 constant SECONDS_PER_YEAR = 31_536_000;
int64 private constant APR_FEED_BOUNDARY_MAX = 2e12; // 200%
int64 private constant APR_FEED_BOUNDARY_MIN = 0;
uint256 private constant APR_FEED_DECIMALS = 12;
/// @dev The oracle to fetch the latest APR floor and APR base.
/// @notice When the oracle is updated, it can actively push the latest values to this contract, allowing us to adjust srtTargetIndex.
IAprPairFeed public aprPairFeed;
/// @dev External floor target APR for Srt
UD60x18 public aprTarget;
/// @dev External APR for the underlying protocol
UD60x18 public aprBase;
/// @dev The calculated target APR for Srt; the primary objective in calculations.
UD60x18 public aprSrt;
uint256 public indexTimestamp;
uint256 public srtTargetIndex;
uint256 public reserveBps;
uint256 constant PERCENTAGE_100 = 1e18;
uint256 constant RESERVE_BPS_MAX = 0.02e18;
/// @dev Latest balances at T0 (latest protocol interrogation)
uint256 public nav;
uint256 public jrtNav;
uint256 public srtNav;
uint256 public reserveNav;
/// @dev Risk parameters for Risk Premium calculation.
/// @notice See `calculateRiskPremium()` for usage details.
UD60x18 public riskX;
UD60x18 public riskY;
UD60x18 public riskK;
/// @notice The hard minimum TVLjrt/TVLsrt ratio.
/// @dev If the Jrt TVL falls below this ratio relative to Srt,
/// Jrt withdrawals are no longer allowed.
// In case of low APR, Jrt will still be responsible for funding Srt returns, even if it falls below this ratio
uint256 public minimumJrtSrtRatio;
/// @notice The protective buffer above the minimum Jrt/Srt ratio.
/// @dev If the ratio falls below this threshold, deposits into the Srt vault
/// are disabled earlier to prevent front-running the hard floor.
/// Jrt withdrawals remain possible until the hard minimum is reached.
uint256 public minimumJrtSrtRatioBuffer;
error InvalidNavSplit(uint256 navT1, uint256 jrtAssets, uint256 srtAssets, uint256 reserveAssets);
error ReserveTooLow(uint256 reserveNav, uint256 requestedNav);
event AprPairFeedChanged(address aprPairFeed);
event AprDataChangedViaPush(UD60x18 aprTarget, UD60x18 aprBase);
event ReservePercentageChanged(uint256 reserveBps);
event RiskParametersChanged(UD60x18 x, UD60x18 y, UD60x18 k);
event MinimumJrtSrtRatioChanged(uint256 ratio);
event MinimumJrtSrtRatioBufferChanged(uint256 ratio);
function initialize(
address owner_,
address acm_,
IStrataCDO cdo_,
IAprPairFeed aprPairFeed_
) public virtual initializer {
AccessControlled_init(owner_, acm_);
cdo = cdo_;
aprPairFeed = aprPairFeed_;
riskX = UD60x18.wrap(0.2e18);
riskY = UD60x18.wrap(0.2e18);
riskK = UD60x18.wrap(0.3e18);
srtTargetIndex = 1e18;
indexTimestamp = block.timestamp;
minimumJrtSrtRatio = 0.05e18;
minimumJrtSrtRatioBuffer = 0.06e18;
}
/// @notice Returns the updated total assets for each tranche and the reserve
/// @dev This method is used by the Tranches to get their updated total assets for the current block
/// @param navT1 The current total Net Asset Value
/// @return jrtNavT1 The updated Junior Tranche TVL
/// @return srtNavT1 The updated Senior Tranche TVL
/// @return reserveNavT1 The updated Reserve TVL
function totalAssets (uint256 navT1) public view returns (uint256 jrtNavT1, uint256 srtNavT1, uint256 reserveNavT1) {
(
jrtNavT1,
srtNavT1,
reserveNavT1
) = calculateNAVSplit(nav, jrtNav, srtNav, reserveNav, navT1);
return (jrtNavT1, srtNavT1, reserveNavT1);
}
/// @notice Returns the current saved total assets for each tranche and the reserve
/// @dev These values represent the state at the last update, not necessarily the current block
/// @return jrtNavT0 The last saved Junior Tranche TVL
/// @return srtNavT0 The last saved Senior Tranche TVL
/// @return reserveNavT0 The last saved Reserve TVL
function totalAssetsT0 () public view returns (uint256 jrtNavT0, uint256 srtNavT0, uint256 reserveNavT0) {
return (jrtNav, srtNav, reserveNav);
}
/// @notice Returns current reserve value
/// @dev This method returns the maximum amount that `reduceReserve` can handle
/// @return The current reserve Net Asset Value (NAV)
function totalReserve () external view returns (uint256) {
(,,uint256 reserveNavT1) = totalAssets(cdo.totalStrategyAssets());
return reserveNavT1;
}
/// @notice Reduces the reserve by the specified amount
/// @dev This function is called by the CDO contract to reduce the reserve
/// @dev The CDO contract is responsible for withdrawing the appropriate amount to the treasury
/// @param amount The amount by which to reduce the reserve NAV
function reduceReserve (uint256 amount) external onlyCDO {
updateAccountingInner(cdo.totalStrategyAssets());
if (amount > reserveNav) {
revert ReserveTooLow(reserveNav, amount);
}
reserveNav = reserveNav - amount;
nav = nav - amount;
// Fetch APRs and force recalculate aprSrt, as JRT and SRT TVLs may have changed.
(bool modified, UD60x18 aprTarget_, UD60x18 aprBase_) = fetchAprs();
if (modified == false) {
// Recalculates aprSrt based on new TVL ratio and old APRs
updateAprSrt(aprTarget_, aprBase_);
}
}
function maxWithdraw(bool isJrt) external view returns (uint256) {
if (isJrt) {
uint256 minJrt = srtNav * minimumJrtSrtRatio / 1e18;
return Math.saturatingSub(jrtNav, minJrt);
}
// srt
return srtNav;
}
function maxDeposit(bool isJrt) external view returns (uint256) {
if (isJrt) {
return type(uint256).max;
}
uint256 maxSrt = jrtNav * 1e18 / minimumJrtSrtRatioBuffer;
return Math.saturatingSub(maxSrt, srtNav);
}
/// @notice Updates the accounting for the CDO, calculating new TVL split
/// @dev This method should be called before any deposits or withdrawals in tranches
/// @dev It calculates the new TVL split, allowing tranches to accurately calculate their share prices
/// @param navT1 The current total assets (Net Asset Value) held by the CDO in the strategy
function updateAccounting (uint256 navT1) external onlyCDO {
updateAccountingInner(navT1);
}
/// @notice Updates the Net Asset Values (NAVs) after deposits or withdrawals
/// @dev This method should be called after any deposits or withdrawals are made in the tranches
/// @dev It adjusts the NAVs for both Junior and Senior tranches, as well as the total NAV
/// @param jrtAssetsIn Amount of assets deposited into the Junior tranche
/// @param jrtAssetsOut Amount of assets withdrawn from the Junior tranche
/// @param srtAssetsIn Amount of assets deposited into the Senior tranche
/// @param srtAssetsOut Amount of assets withdrawn from the Senior tranche
function updateBalanceFlow (
uint256 jrtAssetsIn,
uint256 jrtAssetsOut,
uint256 srtAssetsIn,
uint256 srtAssetsOut
) external onlyCDO {
jrtNav = jrtNav + jrtAssetsIn - jrtAssetsOut;
srtNav = srtNav + srtAssetsIn - srtAssetsOut;
nav = nav + jrtAssetsIn + srtAssetsIn - jrtAssetsOut - srtAssetsOut;
(bool modified, UD60x18 aprTarget_, UD60x18 aprBase_) = fetchAprs();
if (modified == false) {
// Recalculates aprSrt based on new TVL ratio and old APRs
updateAprSrt(aprTarget_, aprBase_);
}
}
/// @notice Calculates the updated Net Asset Values (NAVs) for Junior, Senior tranches, and Reserve
/// @dev This function performs the following operations:
/// 1. Calculates and distributes gains or losses across tranches
/// 2. Allocates a portion of gains to the Reserve based on reserveBps
/// 3. Junior tranche initially receives all gain, but this is later adjusted
/// 4. Ensures the Senior tranche reaches its target APR using Junior tranche funds
/// 5. Verifies the final NAV split is consistent with the total NAV
/// @param navT0 Total Net Asset Value at the previous timestamp
/// @param jrtNavT0 Junior tranche NAV at the previous timestamp
/// @param srtNavT0 Senior tranche NAV at the previous timestamp
/// @param reserveNavT0 Reserve NAV at the previous timestamp
/// @param navT1 Current total Net Asset Value
/// @return jrtNavT1 Updated Junior tranche NAV
/// @return srtNavT1 Updated Senior tranche NAV
/// @return reserveNavT1 Updated Reserve NAV
function calculateNAVSplit (
uint256 navT0,
uint256 jrtNavT0,
uint256 srtNavT0,
uint256 reserveNavT0,
uint256 navT1
) public view returns (uint256 jrtNavT1, uint256 srtNavT1, uint256 reserveNavT1) {
if (jrtNavT0 == 0 && srtNavT0 == 0 && navT1 > 0) {
// No deposits yet, however Strategy reports gain, move all to reserve.
return (0, 0, navT1);
}
int256 gain_dT = int256(navT1) - int256(navT0);
if (gain_dT < 0) {
// Should never happen to USDe, jic: cover by Jrt, then Reserve, then Srt
uint256 loss = uint256(-gain_dT);
uint256 jrtLoss = Math.min(jrtNavT0, loss);
loss -= jrtLoss;
uint256 reserveLoss = Math.min(reserveNavT0, loss);
loss -= reserveLoss;
uint256 srtLoss = Math.min(srtNavT0, loss);
require(srtLoss == loss, "Loss>navT0");
jrtNavT0 -= jrtLoss;
srtNavT0 -= srtLoss;
reserveNavT0 -= reserveLoss;
gain_dT = 0;
}
uint256 gain_dTAbs = uint256(gain_dT);
// #1 Final new reserve
uint256 reserve_dT = 0;
if (gain_dTAbs > 0 && reserveBps > 0) {
reserve_dT = gain_dTAbs * reserveBps / PERCENTAGE_100;
gain_dTAbs -= reserve_dT;
}
reserveNavT1 = reserveNavT0 + reserve_dT;
// Give the total gain (if any) to Juniors, later here, we subtract from Juniors the desired Gain of Seniors
jrtNavT1 = jrtNavT0 + gain_dTAbs;
// Calculate Srt gain
uint256 srtTargetIndexT1 = getSrtTargetIndexT1();
// Gain = Assets * (TargetIndex1 / TargetIndex0 - 1);
int256 srtGainTarget = calculateGain(srtNavT0, srtTargetIndexT1, srtTargetIndex);
if (srtGainTarget < 0) {
// Should never happen, jic: transfer the loss to Juniors as profit
uint256 loss = uint256(-srtGainTarget);
uint256 srtLoss = Math.min(srtNavT0, loss);
srtNavT0 -= srtLoss;
jrtNavT1 += srtLoss;
srtGainTarget = 0;
}
uint256 srtGainTargetAbs = Math.min(
uint256(srtGainTarget),
Math.saturatingSub(jrtNavT1, 1e18)
);
// #2 Final new Jrt
jrtNavT1 = jrtNavT1 - srtGainTargetAbs;
// #3 Final new Srt
srtNavT1 = srtNavT0 + srtGainTargetAbs;
if (navT1 != (jrtNavT1 + srtNavT1 + reserveNavT1)) {
revert InvalidNavSplit(navT1, jrtNavT1, srtNavT1, reserveNavT1);
}
return (jrtNavT1, srtNavT1, reserveNavT1);
}
function updateAccountingInner (uint256 navT1) internal {
(
uint256 jrtNavT1,
uint256 srtNavT1,
uint256 reserveNavT1
) = calculateNAVSplit(nav, jrtNav, srtNav, reserveNav, navT1);
updateIndex();
nav = navT1;
jrtNav = jrtNavT1;
srtNav = srtNavT1;
reserveNav = reserveNavT1;
}
/// @notice Calculates the target index for the current block
function getSrtTargetIndexT1 () internal view returns (uint256) {
return calculateTargetIndex(srtTargetIndex, indexTimestamp, block.timestamp, aprSrt);
}
/// @notice Computes the accrual index at t1 given the prior index, elapsed time, and APR
function calculateTargetIndex (uint256 targetIndex, uint256 t0, uint256 t1, UD60x18 apr) internal pure returns (uint256) {
uint256 dt = t1 - t0;
if (dt == 0) {
return targetIndex;
}
// Calculate the interest factor: (APR * time elapsed) / seconds per year
uint256 interestFactor = apr.unwrap() * dt / SECONDS_PER_YEAR;
// Apply the interest factor to the initial target index
// newIndex = oldIndex * (1 + interestFactor)
uint256 targetIndexT1 = targetIndex * (1e18 + interestFactor) / 1e18;
return targetIndexT1;
}
function calculateRiskPremium () internal view returns (UD60x18){
UD60x18 tvlRatio = UD60x18.wrap(srtNav == 0 ? 0 : (srtNav * 1e18 / (srtNav + jrtNav)));
UD60x18 riskPremium = calculateRiskPremiumInner(riskX, riskY, riskK, tvlRatio);
return riskPremium;
}
function calculateRiskPremiumInner (UD60x18 x, UD60x18 y, UD60x18 k, UD60x18 tvlRatioSrt) internal pure returns (UD60x18){
// RiskPremium = x + y * TVL_ratio_sr ^ k
UD60x18 riskPremium = x + y * pow(tvlRatioSrt, k);
return riskPremium;
}
// Fetch APRs from Feed
function fetchAprs () internal returns (bool modified, UD60x18 aprTargetT1, UD60x18 aprBaseT1) {
if (address(aprPairFeed) == address(0)) {
return (false, aprTarget, aprBase);
}
IAprPairFeed.TRound memory round = aprPairFeed.latestRoundData();
aprTargetT1 = normalizeAprFromFeed(round.aprTarget);
aprBaseT1 = normalizeAprFromFeed(round.aprBase);
if (aprTargetT1 != aprTarget || aprBaseT1 != aprBase) {
aprTarget = aprTargetT1;
aprBase = aprBaseT1;
updateAprSrt(aprTargetT1, aprBaseT1);
return (true, aprTargetT1, aprBaseT1);
}
return (false, aprTargetT1, aprBaseT1);
}
function updateIndex () internal {
srtTargetIndex = getSrtTargetIndexT1();
indexTimestamp = block.timestamp;
}
function updateAprSrt (UD60x18 aprTarget_, UD60x18 aprBase_) internal {
UD60x18 risk = calculateRiskPremium();
UD60x18 aprSrt1 = mul(aprBase_, UD60x18.wrap(1e18) - risk);
aprSrt = UD60x18Ext.max(aprTarget_, aprSrt1);
}
/// @dev Calculates the desired gain based on the change in target index over a period
/// @return The calculated gain (positive) or loss (negative) as an int256
function calculateGain (uint256 navT0, uint256 targetIndexT1, uint256 targetIndexT0) internal pure returns (int256) {
// Gain = Assets * (TargetIndex1 / TargetIndex0 - 1);
return int256(navT0 * targetIndexT1 / targetIndexT0) - int256(navT0);
}
/// @dev Converts APR from Feed's compact format (12 decimal places, stored in 1 SLOT) to UD60x18
/// @dev Ensures the APR is within the acceptable range for Accounting
/// @return The APR value as a UD60x18
function normalizeAprFromFeed (/* SD7x12 */ int64 apr) internal pure returns (UD60x18) {
if (apr < APR_FEED_BOUNDARY_MIN) {
apr = APR_FEED_BOUNDARY_MIN;
}
if (apr > APR_FEED_BOUNDARY_MAX) {
apr = APR_FEED_BOUNDARY_MAX;
}
return UD60x18.wrap(uint256(int256(apr)) * (10 ** (18 - APR_FEED_DECIMALS)));
}
/*****************************************************************************
* External configuration Methods *
*****************************************************************************/
// Trigger fetching new APRs to update srtTargetIndex
function onAprChanged () external onlyRole(UPDATER_FEED_ROLE) {
updateAccountingInner(cdo.totalStrategyAssets());
(bool modified, UD60x18 aprTarget_, UD60x18 aprBase_) = fetchAprs();
if (modified) {
emit AprDataChangedViaPush(aprTarget_, aprBase_);
}
}
/// @notice Sets the risk premium parameters used in calculating the risk-adjusted APR
/// @param riskX_ Base risk premium
/// @param riskY_ Coefficient for the TVL ratio-dependent component
/// @param riskK_ Exponent for the TVL ratio in the risk calculation
/// @dev Only callable by accounts with UPDATER_STRAT_CONFIG_ROLE
function setRiskParameters (
UD60x18 riskX_,
UD60x18 riskY_,
UD60x18 riskK_
) external onlyRole(UPDATER_STRAT_CONFIG_ROLE) {
updateAccountingInner(cdo.totalStrategyAssets());
riskX = riskX_;
riskY = riskY_;
riskK = riskK_;
UD60x18 risk = calculateRiskPremiumInner(riskX_, riskY_, riskK_, UD60x18.wrap(1e18));
require(risk.unwrap() < PERCENTAGE_100, ">=100%");
emit RiskParametersChanged(riskX_, riskY_, riskK_);
updateAprSrt(aprTarget, aprBase);
}
/// @notice Sets the APRs Feed contract for fetching APR target and APR base
/// @dev This feed provides the external APR values used in calculations
/// @param aprPairFeed_ The address of the new APRs Feed contract
/// @dev Only callable by the protocol owner
function setAprPairFeed (IAprPairFeed aprPairFeed_) external onlyOwner {
// integrity check
require(aprPairFeed_.decimals() == APR_FEED_DECIMALS, "InvalidFeed");
aprPairFeed = aprPairFeed_;
emit AprPairFeedChanged(address(aprPairFeed_));
}
/// @notice Sets the percentage of gains allocated to the reserve
/// @param bps The new reserve percentage in basis points (1e18 = 100%)
/// @dev Only callable by the protocol owner
/// @dev The maximum allowed value is defined by RESERVE_BPS_MAX
function setReserveBps (uint256 bps) external onlyOwner {
require(bps <= RESERVE_BPS_MAX && bps != reserveBps, "InvalidNewReserve");
updateAccountingInner(cdo.totalStrategyAssets());
reserveBps = bps;
emit ReservePercentageChanged(reserveBps);
}
/// @notice Sets the hard minimum Jrt/Srt ratio below which Jrt withdrawals are blocked.
function setMinimumJrtSrtRatio (uint256 ratio) external onlyOwner {
// initial: min Jrt = .05x Srt; allow up-to min Jrt = 100x Srt
require(ratio <= minimumJrtSrtRatioBuffer, "RatioAboveSoftFloor");
minimumJrtSrtRatio = ratio;
emit MinimumJrtSrtRatioChanged(ratio);
}
/// @notice Sets the protective buffer ratio at which Srt deposits are halted.
function setMinimumJrtSrtRatioBuffer (uint256 ratio) external onlyOwner {
// initial: min Jrt = .05x Srt; allow up-to min Jrt = 100x Srt
require(ratio <= 100 * PERCENTAGE_100, "RatioTooHigh");
require(ratio >= minimumJrtSrtRatio && ratio != 0, "RatioBelowHardFloor");
minimumJrtSrtRatioBuffer = ratio;
emit MinimumJrtSrtRatioBufferChanged(ratio);
}
}
"
},
"contracts/tranches/interfaces/IStrataCDO.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import { IStrategy } from "./IStrategy.sol";
import { ITranche } from "./ITranche.sol";
interface IStrataCDO {
function strategy() external view returns (IStrategy);
function totalAssets (address tranche) external view returns (uint256);
function totalStrategyAssets () external view returns (uint256);
function updateAccounting () external;
function deposit (address tranche, address token, uint256 tokenAmount, uint256 baseAssets) external;
function withdraw (address tranche, address token, uint256 tokenAmount, uint256 baseAssets, address owner, address receiver) external;
function maxWithdraw(address tranche) external view returns (uint256);
function maxDeposit(address tranche) external view returns (uint256);
// reverts if neither Jrt nor Srt
function isJrt (address tranche) external view returns (bool);
function jrtVault() external view returns (ITranche);
function srtVault() external view returns (ITranche);
}
"
},
"@prb/math/src/ud60x18/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
uEXP_MAX_INPUT,
uEXP2_MAX_INPUT,
uHALF_UNIT,
uLOG2_10,
uLOG2_E,
uMAX_UD60x18,
uMAX_WHOLE_UD60x18,
UNIT,
uUNIT,
uUNIT_SQUARED,
ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
unchecked {
result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
}
}
/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x ≤ MAX_WHOLE_UD60x18
///
/// @param x The UD60x18 number to ceil.
/// @return result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint > uMAX_WHOLE_UD60x18) {
revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
}
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `UNIT - remainder`.
let delta := sub(uUNIT, remainder)
// Equivalent to `x + remainder > 0 ? delta : 0`.
result := add(x, mul(delta, gt(remainder, 0)))
}
}
/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @return result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}
/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x ≤ 133_084258667509499440
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192e18 from being passed to {exp2}.
if (xUint > uEXP_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
}
unchecked {
// Inline the fixed-point multiplication to save gas.
uint256 doubleUnitProduct = xUint * uLOG2_E;
result = exp2(wrap(doubleUnitProduct / uUNIT));
}
}
/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x < 192e18
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
// Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
if (xUint > uEXP2_MAX_INPUT) {
revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
}
// Convert x to the 192.64-bit fixed-point format.
uint256 x_192x64 = (xUint << 64) / uUNIT;
// Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
result = wrap(Common.exp2(x_192x64));
}
/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @return result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.
let remainder := mod(x, uUNIT)
// Equivalent to `x - remainder > 0 ? remainder : 0)`.
result := sub(x, mul(remainder, gt(remainder, 0)))
}
}
/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @return result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
assembly ("memory-safe") {
result := mod(x, uUNIT)
}
}
/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
if (xUint == 0 || yUint == 0) {
return ZERO;
}
unchecked {
// Checking for overflow this way is faster than letting Solidity do it.
uint256 xyUint = xUint * yUint;
if (xyUint / xUint != yUint) {
revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
}
// We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
// during multiplication. See the comments in {Common.sqrt}.
result = wrap(Common.sqrt(xyUint));
}
}
/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
result = wrap(uUNIT_SQUARED / x.unwrap());
}
}
/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
unchecked {
// Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
// {log2} can return is ~196_205294292027477728.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}
}
/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
// Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
// prettier-ignore
assembly ("memory-safe") {
switch x
case 1 { result := mul(uUNIT, sub(0, 18)) }
case 10 { result := mul(uUNIT, sub(1, 18)) }
case 100 { result := mul(uUNIT, sub(2, 18)) }
case 1000 { result := mul(uUNIT, sub(3, 18)) }
case 10000 { result := mul(uUNIT, sub(4, 18)) }
case 100000 { result := mul(uUNIT, sub(5, 18)) }
case 1000000 { result := mul(uUNIT, sub(6, 18)) }
case 10000000 { result := mul(uUNIT, sub(7, 18)) }
case 100000000 { result := mul(uUNIT, sub(8, 18)) }
case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
case 1000000000000000000 { result := 0 }
case 10000000000000000000 { result := uUNIT }
case 100000000000000000000 { result := mul(uUNIT, 2) }
case 1000000000000000000000 { result := mul(uUNIT, 3) }
case 10000000000000000000000 { result := mul(uUNIT, 4) }
case 100000000000000000000000 { result := mul(uUNIT, 5) }
case 1000000000000000000000000 { result := mul(uUNIT, 6) }
case 10000000000000000000000000 { result := mul(uUNIT, 7) }
case 100000000000000000000000000 { result := mul(uUNIT, 8) }
case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
default { result := uMAX_UD60x18 }
}
if (result.unwrap() == uMAX_UD60x18) {
unchecked {
// Inline the fixed-point division to save gas.
result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
}
}
}
/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \ ext{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x ≥ UNIT
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
unchecked {
// Calculate the integer part of the logarithm.
uint256 n = Common.msb(xUint / uUNIT);
// This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
// n is at most 255 and UNIT is 1e18.
uint256 resultUint = n * uUNIT;
// Calculate $y = x * 2^{-n}$.
uint256 y = xUint >> n;
// If y is the unit number, the fractional part is zero.
if (y == uUNIT) {
return wrap(resultUint);
}
// Calculate the fractional part via the iterative approximation.
// The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
uint256 DOUBLE_UNIT = 2e18;
for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
y = (y * y) / uUNIT;
// Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
if (y >= DOUBLE_UNIT) {
// Add the 2^{-m} factor to the logarithm.
resultUint += delta;
// Halve y, which corresponds to z/2 in the Wikipedia article.
y >>= 1;
}
}
result = wrap(resultUint);
}
}
/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}
/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
uint256 yUint = y.unwrap();
// If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
if (xUint == 0) {
return yUint == 0 ? UNIT : ZERO;
}
// If x is `UNIT`, the result is always `UNIT`.
else if (xUint == uUNIT) {
return UNIT;
}
// If y is zero, the result is always `UNIT`.
if (yUint == 0) {
return UNIT;
}
// If y is `UNIT`, the result is always x.
else if (yUint == uUNIT) {
return x;
}
// If x is > UNIT, use the standard formula.
if (xUint > uUNIT) {
result = exp2(mul(log2(x), y));
}
// Conversely, if x < UNIT, use the equivalent formula.
else {
UD60x18 i = wrap(uUNIT_SQUARED / xUint);
UD60x18 w = exp2(mul(log2(i), y));
result = wrap(uUNIT_SQUARED / w.unwrap());
}
}
/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
// Calculate the first iteration of the loop in advance.
uint256 xUint = x.unwrap();
uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;
// Equivalent to `for(y /= 2; y > 0; y /= 2)`.
for (y >>= 1; y > 0; y >>= 1) {
xUint = Common.mulDiv18(xUint, xUint);
// Equivalent to `y % 2 == 1`.
if (y & 1 > 0) {
resultUint = Common.mulDiv18(resultUint, xUint);
}
}
result = wrap(resultUint);
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x ≤ MAX_UD60x18 / UNIT
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
uint256 xUint = x.unwrap();
unchecked {
if (xUint > uMAX_UD60x18 / uUNIT) {
revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
}
// Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
// In this case, the two numbers are both the square root.
result = wrap(Common.sqrt(xUint * uUNIT));
}
}
"
},
"@openzeppelin/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLe
Submitted on: 2025-10-31 16:11:07
Comments
Log in to comment.
No comments yet.