// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import"./Errors.sol"asCastingErrors;
import { MAX_UINT128, MAX_UINT40 } from"../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from"../sd1x18/Constants.sol";
import { SD1x18 } from"../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from"../ud2x18/Constants.sol";
import { UD2x18 } from"../ud2x18/ValueType.sol";
import { UD60x18 } from"../ud60x18/ValueType.sol";
import { SD59x18 } from"./ValueType.sol";
/// @notice Casts an SD59x18 number into int256./// @dev This is basically a functional alias for {unwrap}.functionintoInt256(SD59x18 x) purereturns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Casts an SD59x18 number into SD1x18./// @dev Requirements:/// - x must be greater than or equal to `uMIN_SD1x18`./// - x must be less than or equal to `uMAX_SD1x18`.functionintoSD1x18(SD59x18 x) purereturns (SD1x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt < uMIN_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
}
if (xInt > uMAX_SD1x18) {
revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
}
result = SD1x18.wrap(int64(xInt));
}
/// @notice Casts an SD59x18 number into UD2x18./// @dev Requirements:/// - x must be positive./// - x must be less than or equal to `uMAX_UD2x18`.functionintoUD2x18(SD59x18 x) purereturns (UD2x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt <0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
}
if (xInt >int256(uint256(uMAX_UD2x18))) {
revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
}
result = UD2x18.wrap(uint64(uint256(xInt)));
}
/// @notice Casts an SD59x18 number into UD60x18./// @dev Requirements:/// - x must be positive.functionintoUD60x18(SD59x18 x) purereturns (UD60x18 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt <0) {
revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
}
result = UD60x18.wrap(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint256./// @dev Requirements:/// - x must be positive.functionintoUint256(SD59x18 x) purereturns (uint256 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt <0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
}
result =uint256(xInt);
}
/// @notice Casts an SD59x18 number into uint128./// @dev Requirements:/// - x must be positive./// - x must be less than or equal to `uMAX_UINT128`.functionintoUint128(SD59x18 x) purereturns (uint128 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt <0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
}
if (xInt >int256(uint256(MAX_UINT128))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
}
result =uint128(uint256(xInt));
}
/// @notice Casts an SD59x18 number into uint40./// @dev Requirements:/// - x must be positive./// - x must be less than or equal to `MAX_UINT40`.functionintoUint40(SD59x18 x) purereturns (uint40 result) {
int256 xInt = SD59x18.unwrap(x);
if (xInt <0) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
}
if (xInt >int256(uint256(MAX_UINT40))) {
revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
}
result =uint40(uint256(xInt));
}
/// @notice Alias for {wrap}.functionsd(int256 x) purereturns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Alias for {wrap}.functionsd59x18(int256 x) purereturns (SD59x18 result) {
result = SD59x18.wrap(x);
}
/// @notice Unwraps an SD59x18 number into int256.functionunwrap(SD59x18 x) purereturns (int256 result) {
result = SD59x18.unwrap(x);
}
/// @notice Wraps an int256 number into SD59x18.functionwrap(int256 x) purereturns (SD59x18 result) {
result = SD59x18.wrap(x);
}
Contract Source Code
File 2 of 17: Common.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;// Common.sol//// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not// always operate with SD59x18 and UD60x18 numbers./*//////////////////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*//// @notice Thrown when the resultant value in {mulDiv} overflows uint256.errorPRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);
/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.errorPRBMath_MulDiv18_Overflow(uint256 x, uint256 y);
/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.errorPRBMath_MulDivSigned_InputTooSmall();
/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.errorPRBMath_MulDivSigned_Overflow(int256 x, int256 y);
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*//// @dev The maximum value a uint128 number can have.uint128constant MAX_UINT128 =type(uint128).max;
/// @dev The maximum value a uint40 number can have.uint40constant MAX_UINT40 =type(uint40).max;
/// @dev The unit number, which the decimal precision of the fixed-point types.uint256constant UNIT =1e18;
/// @dev The unit number inverted mod 2^256.uint256constant UNIT_INVERSE =78156646155174841979727994598816262306175212592076161876661_508869554232690281;
/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant/// bit in the binary representation of `UNIT`.uint256constant UNIT_LPOTD =262144;
/*//////////////////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////////////////*//// @notice Calculates the binary exponent of x using the binary fraction method./// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693./// @param x The exponent as an unsigned 192.64-bit fixed-point number./// @return result The result as an unsigned 60.18-decimal fixed-point number./// @custom:smtchecker abstract-function-nondetfunctionexp2(uint256 x) purereturns (uint256 result) {
unchecked {
// Start from 0.5 in the 192.64-bit fixed-point format.
result =0x800000000000000000000000000000000000000000000000;
// The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points://// 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.// 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing// a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,// we know that `x & 0xFF` is also 1.if (x &0xFF00000000000000>0) {
if (x &0x8000000000000000>0) {
result = (result *0x16A09E667F3BCC909) >>64;
}
if (x &0x4000000000000000>0) {
result = (result *0x1306FE0A31B7152DF) >>64;
}
if (x &0x2000000000000000>0) {
result = (result *0x1172B83C7D517ADCE) >>64;
}
if (x &0x1000000000000000>0) {
result = (result *0x10B5586CF9890F62A) >>64;
}
if (x &0x800000000000000>0) {
result = (result *0x1059B0D31585743AE) >>64;
}
if (x &0x400000000000000>0) {
result = (result *0x102C9A3E778060EE7) >>64;
}
if (x &0x200000000000000>0) {
result = (result *0x10163DA9FB33356D8) >>64;
}
if (x &0x100000000000000>0) {
result = (result *0x100B1AFA5ABCBED61) >>64;
}
}
if (x &0xFF000000000000>0) {
if (x &0x80000000000000>0) {
result = (result *0x10058C86DA1C09EA2) >>64;
}
if (x &0x40000000000000>0) {
result = (result *0x1002C605E2E8CEC50) >>64;
}
if (x &0x20000000000000>0) {
result = (result *0x100162F3904051FA1) >>64;
}
if (x &0x10000000000000>0) {
result = (result *0x1000B175EFFDC76BA) >>64;
}
if (x &0x8000000000000>0) {
result = (result *0x100058BA01FB9F96D) >>64;
}
if (x &0x4000000000000>0) {
result = (result *0x10002C5CC37DA9492) >>64;
}
if (x &0x2000000000000>0) {
result = (result *0x1000162E525EE0547) >>64;
}
if (x &0x1000000000000>0) {
result = (result *0x10000B17255775C04) >>64;
}
}
if (x &0xFF0000000000>0) {
if (x &0x800000000000>0) {
result = (result *0x1000058B91B5BC9AE) >>64;
}
if (x &0x400000000000>0) {
result = (result *0x100002C5C89D5EC6D) >>64;
}
if (x &0x200000000000>0) {
result = (result *0x10000162E43F4F831) >>64;
}
if (x &0x100000000000>0) {
result = (result *0x100000B1721BCFC9A) >>64;
}
if (x &0x80000000000>0) {
result = (result *0x10000058B90CF1E6E) >>64;
}
if (x &0x40000000000>0) {
result = (result *0x1000002C5C863B73F) >>64;
}
if (x &0x20000000000>0) {
result = (result *0x100000162E430E5A2) >>64;
}
if (x &0x10000000000>0) {
result = (result *0x1000000B172183551) >>64;
}
}
if (x &0xFF00000000>0) {
if (x &0x8000000000>0) {
result = (result *0x100000058B90C0B49) >>64;
}
if (x &0x4000000000>0) {
result = (result *0x10000002C5C8601CC) >>64;
}
if (x &0x2000000000>0) {
result = (result *0x1000000162E42FFF0) >>64;
}
if (x &0x1000000000>0) {
result = (result *0x10000000B17217FBB) >>64;
}
if (x &0x800000000>0) {
result = (result *0x1000000058B90BFCE) >>64;
}
if (x &0x400000000>0) {
result = (result *0x100000002C5C85FE3) >>64;
}
if (x &0x200000000>0) {
result = (result *0x10000000162E42FF1) >>64;
}
if (x &0x100000000>0) {
result = (result *0x100000000B17217F8) >>64;
}
}
if (x &0xFF000000>0) {
if (x &0x80000000>0) {
result = (result *0x10000000058B90BFC) >>64;
}
if (x &0x40000000>0) {
result = (result *0x1000000002C5C85FE) >>64;
}
if (x &0x20000000>0) {
result = (result *0x100000000162E42FF) >>64;
}
if (x &0x10000000>0) {
result = (result *0x1000000000B17217F) >>64;
}
if (x &0x8000000>0) {
result = (result *0x100000000058B90C0) >>64;
}
if (x &0x4000000>0) {
result = (result *0x10000000002C5C860) >>64;
}
if (x &0x2000000>0) {
result = (result *0x1000000000162E430) >>64;
}
if (x &0x1000000>0) {
result = (result *0x10000000000B17218) >>64;
}
}
if (x &0xFF0000>0) {
if (x &0x800000>0) {
result = (result *0x1000000000058B90C) >>64;
}
if (x &0x400000>0) {
result = (result *0x100000000002C5C86) >>64;
}
if (x &0x200000>0) {
result = (result *0x10000000000162E43) >>64;
}
if (x &0x100000>0) {
result = (result *0x100000000000B1721) >>64;
}
if (x &0x80000>0) {
result = (result *0x10000000000058B91) >>64;
}
if (x &0x40000>0) {
result = (result *0x1000000000002C5C8) >>64;
}
if (x &0x20000>0) {
result = (result *0x100000000000162E4) >>64;
}
if (x &0x10000>0) {
result = (result *0x1000000000000B172) >>64;
}
}
if (x &0xFF00>0) {
if (x &0x8000>0) {
result = (result *0x100000000000058B9) >>64;
}
if (x &0x4000>0) {
result = (result *0x10000000000002C5D) >>64;
}
if (x &0x2000>0) {
result = (result *0x1000000000000162E) >>64;
}
if (x &0x1000>0) {
result = (result *0x10000000000000B17) >>64;
}
if (x &0x800>0) {
result = (result *0x1000000000000058C) >>64;
}
if (x &0x400>0) {
result = (result *0x100000000000002C6) >>64;
}
if (x &0x200>0) {
result = (result *0x10000000000000163) >>64;
}
if (x &0x100>0) {
result = (result *0x100000000000000B1) >>64;
}
}
if (x &0xFF>0) {
if (x &0x80>0) {
result = (result *0x10000000000000059) >>64;
}
if (x &0x40>0) {
result = (result *0x1000000000000002C) >>64;
}
if (x &0x20>0) {
result = (result *0x10000000000000016) >>64;
}
if (x &0x10>0) {
result = (result *0x1000000000000000B) >>64;
}
if (x &0x8>0) {
result = (result *0x10000000000000006) >>64;
}
if (x &0x4>0) {
result = (result *0x10000000000000003) >>64;
}
if (x &0x2>0) {
result = (result *0x10000000000000001) >>64;
}
if (x &0x1>0) {
result = (result *0x10000000000000001) >>64;
}
}
// In the code snippet below, two operations are executed simultaneously://// 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1// accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.// 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.//// The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,// integer part, $2^n$.
result *= UNIT;
result >>= (191- (x >>64));
}
}
/// @notice Finds the zero-based index of the first 1 in the binary representation of x.////// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set////// Each step in this implementation is equivalent to this high-level code:////// ```solidity/// if (x >= 2 ** 128) {/// x >>= 128;/// result += 128;/// }/// ```////// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948////// The Yul instructions used below are:////// - "gt" is "greater than"/// - "or" is the OR bitwise operator/// - "shl" is "shift left"/// - "shr" is "shift right"////// @param x The uint256 number for which to find the index of the most significant bit./// @return result The index of the most significant bit as a uint256./// @custom:smtchecker abstract-function-nondetfunctionmsb(uint256 x) purereturns (uint256 result) {
// 2^128assembly ("memory-safe") {
let factor :=shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^64assembly ("memory-safe") {
let factor :=shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^32assembly ("memory-safe") {
let factor :=shl(5, gt(x, 0xFFFFFFFF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^16assembly ("memory-safe") {
let factor :=shl(4, gt(x, 0xFFFF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^8assembly ("memory-safe") {
let factor :=shl(3, gt(x, 0xFF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^4assembly ("memory-safe") {
let factor :=shl(2, gt(x, 0xF))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^2assembly ("memory-safe") {
let factor :=shl(1, gt(x, 0x3))
x :=shr(factor, x)
result :=or(result, factor)
}
// 2^1// No need to shift x any more.assembly ("memory-safe") {
let factor :=gt(x, 0x1)
result :=or(result, factor)
}
}
/// @notice Calculates floor(x*y÷denominator) with 512-bit precision.////// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.////// Notes:/// - The result is rounded down.////// Requirements:/// - The denominator must not be zero./// - The result must fit in uint256.////// @param x The multiplicand as a uint256./// @param y The multiplier as a uint256./// @param denominator The divisor as a uint256./// @return result The result as a uint256./// @custom:smtchecker abstract-function-nondetfunctionmulDiv(uint256 x, uint256 y, uint256 denominator) purereturns (uint256 result) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256// variables such that product = prod1 * 2^256 + prod0.uint256 prod0; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly ("memory-safe") {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
unchecked {
return prod0 / denominator;
}
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.if (prod1 >= denominator) {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
////////////////////////////////////////////////////////////////////////////// 512 by 256 division////////////////////////////////////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using the mulmod Yul instruction.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512-bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
unchecked {
// Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow// because the denominator cannot be zero at this point in the function execution. The result is always >= 1.// For more detail, see https://cs.stackexchange.com/q/138556/92363.uint256 lpotdod = denominator & (~denominator +1);
uint256 flippedLpotdod;
assembly ("memory-safe") {
// Factor powers of two out of denominator.
denominator :=div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 :=div(prod0, lpotdod)
// Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.// `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.// However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
flippedLpotdod :=add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * flippedLpotdod;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.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^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// 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^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
}
}
/// @notice Calculates floor(x*y÷1e18) with 512-bit precision.////// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.////// Notes:/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}./// - The result is rounded down./// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:////// $$/// \begin{cases}/// x * y = MAX\_UINT256 * UNIT \\/// (x * y) \% UNIT \geq \frac{UNIT}{2}/// \end{cases}/// $$////// Requirements:/// - Refer to the requirements in {mulDiv}./// - The result must fit in uint256.////// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number./// @param y The multiplier as an unsigned 60.18-decimal fixed-point number./// @return result The result as an unsigned 60.18-decimal fixed-point number./// @custom:smtchecker abstract-function-nondetfunctionmulDiv18(uint256 x, uint256 y) purereturns (uint256 result) {
uint256 prod0;
uint256 prod1;
assembly ("memory-safe") {
let mm :=mulmod(x, y, not(0))
prod0 :=mul(x, y)
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 ==0) {
unchecked {
return prod0 / UNIT;
}
}
if (prod1 >= UNIT) {
revert PRBMath_MulDiv18_Overflow(x, y);
}
uint256 remainder;
assembly ("memory-safe") {
remainder :=mulmod(x, y, UNIT)
result :=mul(
or(
div(sub(prod0, remainder), UNIT_LPOTD),
mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
),
UNIT_INVERSE
)
}
}
/// @notice Calculates floor(x*y÷denominator) with 512-bit precision.////// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.////// Notes:/// - Unlike {mulDiv}, the result is rounded toward zero.////// Requirements:/// - Refer to the requirements in {mulDiv}./// - None of the inputs can be `type(int256).min`./// - The result must fit in int256.////// @param x The multiplicand as an int256./// @param y The multiplier as an int256./// @param denominator The divisor as an int256./// @return result The result as an int256./// @custom:smtchecker abstract-function-nondetfunctionmulDivSigned(int256 x, int256 y, int256 denominator) purereturns (int256 result) {
if (x ==type(int256).min|| y ==type(int256).min|| denominator ==type(int256).min) {
revert PRBMath_MulDivSigned_InputTooSmall();
}
// Get hold of the absolute values of x, y and the denominator.uint256 xAbs;
uint256 yAbs;
uint256 dAbs;
unchecked {
xAbs = x <0 ? uint256(-x) : uint256(x);
yAbs = y <0 ? uint256(-y) : uint256(y);
dAbs = denominator <0 ? uint256(-denominator) : uint256(denominator);
}
// Compute the absolute value of x*y÷denominator. The result must fit in int256.uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
if (resultAbs >uint256(type(int256).max)) {
revert PRBMath_MulDivSigned_Overflow(x, y);
}
// Get the signs of x, y and the denominator.uint256 sx;
uint256 sy;
uint256 sd;
assembly ("memory-safe") {
// This works thanks to two's complement.// "sgt" stands for "signed greater than" and "sub(0,1)" is max uint256.
sx :=sgt(x, sub(0, 1))
sy :=sgt(y, sub(0, 1))
sd :=sgt(denominator, sub(0, 1))
}
// XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.// If there are, the result should be negative. Otherwise, it should be positive.unchecked {
result = sx ^ sy ^ sd ==0 ? -int256(resultAbs) : int256(resultAbs);
}
}
/// @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:/// - If x is not a perfect square, the result is rounded down./// - Credits to OpenZeppelin for the explanations in comments below.////// @param x The uint256 number for which to calculate the square root./// @return result The result as a uint256./// @custom:smtchecker abstract-function-nondetfunctionsqrt(uint256 x) purereturns (uint256 result) {
if (x ==0) {
return0;
}
// For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.//// We know that the "msb" (most significant bit) of x is a power of 2 such that we have://// $$// msb(x) <= x <= 2*msb(x)$// $$//// We write $msb(x)$ as $2^k$, and we get://// $$// k = log_2(x)// $$//// Thus, we can write the initial inequality as://// $$// 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\// sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\// 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}// $$//// Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.uint256 xAux =uint256(x);
result =1;
if (xAux >=2**128) {
xAux >>=128;
result <<=64;
}
if (xAux >=2**64) {
xAux >>=64;
result <<=32;
}
if (xAux >=2**32) {
xAux >>=32;
result <<=16;
}
if (xAux >=2**16) {
xAux >>=16;
result <<=8;
}
if (xAux >=2**8) {
xAux >>=8;
result <<=4;
}
if (xAux >=2**4) {
xAux >>=4;
result <<=2;
}
if (xAux >=2**2) {
result <<=1;
}
// At this point, `result` is an estimation with at least one bit of precision. We know the true value has at// most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision// doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of// precision into the expected uint128 result.unchecked {
result = (result + x / result) >>1;
result = (result + x / result) >>1;
result = (result + x / result) >>1;
result = (result + x / result) >>1;
result = (result + x / result) >>1;
result = (result + x / result) >>1;
result = (result + x / result) >>1;
// If x is not a perfect square, round down the result.uint256 roundedDownResult = x / result;
if (result >= roundedDownResult) {
result = roundedDownResult;
}
}
}
Contract Source Code
File 3 of 17: Constants.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import { SD1x18 } from"./ValueType.sol";
/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);
/// @dev The maximum value an SD1x18 number can have.int64constant uMAX_SD1x18 =9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);
/// @dev The maximum value an SD1x18 number can have.int64constant uMIN_SD1x18 =-9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);
/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);
/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int256constant uUNIT =1e18;
Contract Source Code
File 4 of 17: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 5 of 17: Conversions.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import { uMAX_UD60x18, uUNIT } from"./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from"./Errors.sol";
import { UD60x18 } from"./ValueType.sol";
/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`./// @dev The result is rounded down./// @param x The UD60x18 number to convert./// @return result The same number in basic integer form.functionconvert(UD60x18 x) purereturns (uint256 result) {
result = UD60x18.unwrap(x) / uUNIT;
}
/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.////// @dev Requirements:/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.////// @param x The basic integer to convert./// @param result The same number converted to UD60x18.functionconvert(uint256 x) purereturns (UD60x18 result) {
if (x > uMAX_UD60x18 / uUNIT) {
revert PRBMath_UD60x18_Convert_Overflow(x);
}
unchecked {
result = UD60x18.wrap(x * uUNIT);
}
}
Contract Source Code
File 6 of 17: ERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)pragmasolidity ^0.8.0;import"./IERC20.sol";
import"./extensions/IERC20Metadata.sol";
import"../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/contractERC20isContext, IERC20, IERC20Metadata{
mapping(address=>uint256) private _balances;
mapping(address=>mapping(address=>uint256)) private _allowances;
uint256private _totalSupply;
stringprivate _name;
stringprivate _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/functiondecimals() publicviewvirtualoverridereturns (uint8) {
return18;
}
/**
* @dev See {IERC20-totalSupply}.
*/functiontotalSupply() publicviewvirtualoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/functionbalanceOf(address account) publicviewvirtualoverridereturns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/functiontransfer(address to, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
returntrue;
}
/**
* @dev See {IERC20-allowance}.
*/functionallowance(address owner, address spender) publicviewvirtualoverridereturns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionapprove(address spender, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
returntrue;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualoverridereturns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
returntrue;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionincreaseAllowance(address spender, uint256 addedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
returntrue;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/functiondecreaseAllowance(address spender, uint256 subtractedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
returntrue;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/function_transfer(addressfrom,
address to,
uint256 amount
) internalvirtual{
require(from!=address(0), "ERC20: transfer from the zero address");
require(to !=address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/function_mint(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/function_burn(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/function_approve(address owner,
address spender,
uint256 amount
) internalvirtual{
require(owner !=address(0), "ERC20: approve from the zero address");
require(spender !=address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/function_spendAllowance(address owner,
address spender,
uint256 amount
) internalvirtual{
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance !=type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom,
address to,
uint256 amount
) internalvirtual{}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_afterTokenTransfer(addressfrom,
address to,
uint256 amount
) internalvirtual{}
}
Contract Source Code
File 7 of 17: Errors.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import { UD2x18 } from"./ValueType.sol";
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.errorPRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);
/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.errorPRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);
Contract Source Code
File 8 of 17: Fenix.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.17;/***********************************************************************************************************************
..:^~!?YPB&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
7 .:~JP#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
& !: :7G@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@G J@@#GY7~^.. ^P@@@@@@@@@@@@@Y!YYYYJG@@@@@@@77YYYJP@@@@@@@@&!&@@@@@@@@JP@@@@@@@@7#@@@@@@@G7&@@@@@@@G!&@
@@J 7@@@@@@@@@@&GJ^ ?@@@@@@@@@@@^J@@@@@@@@@@@@@.P@@@@@@@@@@@@@& ~?&@@@@@@~J@@@@@@@@.G@@@@@@@@#!?@@@@#!?&@@
@@@J ~P#@@@@@@@@@@@@@@&! B@@@@@@@@@~J@@@@@@@@@@@@@:P@@@@@@@@@@@@@&.@B~Y@@@@@~J@@@@@@@@:G@@@@@@@@@@G~P&?!&@@@@
@@@@G ~G@@@@@@@@@@@@@@@@@@@@Y G@@@@@@@@~^YYYP@@@@@@@@@:~YYYG@@@@@@@@@&.@@@P~P@@@~J@@@@@@@@:G@@@@@@@@@@@&. !@@@@@@
@@@@@&^^&@@@@@@@@@@@@@@@@@@@@@@@: @@@@@@@@~Y@@@@@@@@@@@@@:G@@@@@@@@@@@@@&.@@@@@Y~B@!J@@@@@@@@:G@@@@@@@@@@Y~B@Y~B@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@&5!^^!P~ G@@@@@@@~J@@@@@@@@@@@@@:G@@@@@@@@@@@@@&.@@@@@@&?7.J@@@@@@@@.G@@@@@@@@P~P@@@@&7!&@@
@@@@@@@@@@@@@@@@@@@@@@@@Y B@@@@@@@!5@@@@@@@@@@@@@^!5555Y#@@@@@@@&:@@@@@@@@&^Y@@@@@@@@^B@@@@@@&!J@@@@@@@@#!Y@
@@@@@@@@@@@@@@@@@@@@@@@& ~@@@@@@@@@@@@@@@@@@@@@@@@@&&&&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&@
@@@@@@@@@@@@@@@@@@@@@@@@7 J@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@#7. .^Y&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
***********************************************************************************************************************/import { UD60x18, convert, wrap, unwrap, ud, E, ZERO } from"@prb/math/UD60x18.sol";
import { ERC20 } from"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC165 } from"@openzeppelin/contracts/interfaces/IERC165.sol";
import { IBurnableToken } from"xen-crypto/interfaces/IBurnableToken.sol";
import { IBurnRedeemable } from"xen-crypto/interfaces/IBurnRedeemable.sol";
enumStatus {
ACTIVE,
DEFER,
END
}
structStake {
Status status;
uint40 startTs;
uint40 deferralTs;
uint40 endTs;
uint16 term;
uint256 fenix;
uint256 shares;
uint256 payout;
}
structReward {
uint40 id;
uint40 rewardTs;
uint256 fenix;
address caller;
}
///----------------------------------------------------------------------------------------------------------------/// Events///----------------------------------------------------------------------------------------------------------------libraryFenixError{
errorWrongCaller(address caller);
errorAddressZero();
errorBalanceZero();
errorTermZero();
errorTermGreaterThanMax();
errorStakeNotActive();
errorStakeNotEnded();
errorStakeLate();
errorCooldownActive();
errorStakeStatusAlreadySet(Status status);
errorSizeGreaterThanMax();
}
/// @title FENIX pays you to hold your own crypto/// @author Joe Blau <joe@atomize.xyz>/// @notice FENIX pays you to hold your own crypto/// @dev Fenix is an ERC20 token that pays you to hold your own crypto.contractFenixisIBurnRedeemable, IERC165, ERC20("FENIX", "FENIX") {
///----------------------------------------------------------------------------------------------------------------/// Constants///----------------------------------------------------------------------------------------------------------------addresspublicconstant XEN_ADDRESS =0x06450dEe7FD2Fb8E39061434BAbCFC05599a6Fb8;
uint256publicconstant XEN_BURN_RATIO =10_000;
uint256publicconstant MAX_STAKE_LENGTH_DAYS =7_777;
uint256internalconstant UINT256_MAX =type(uint256).max;
uint256internalconstant ONE_DAY_TS =86_400; // (1 day)uint256internalconstant ONE_EIGHTY_DAYS_TS =15_552_000; // 86_400 * 180 (180 days)uint256internalconstant REWARD_COOLDOWN_TS =7_862_400; // 86_400 * 7 * 13 (13 weeks)uint256internalconstant REWARD_LAUNCH_COOLDOWN_TS =1_814_400; // 86_400 * 7 * 3 (3 weeks)
UD60x18 publicconstant ANNUAL_INFLATION_RATE = UD60x18.wrap(0.016180339887498948e18);
UD60x18 internalconstant ONE = UD60x18.wrap(1e18);
UD60x18 internalconstant ONE_YEAR_DAYS = UD60x18.wrap(365);
///----------------------------------------------------------------------------------------------------------------/// Variables///----------------------------------------------------------------------------------------------------------------uint40publicimmutable genesisTs;
uint256public cooldownUnlockTs;
uint256public rewardPoolSupply =0;
uint256public shareRate =1e18;
uint256public equityPoolSupply =0;
uint256public equityPoolTotalShares =0;
mapping(address=> Stake[]) internal stakes;
Reward[] internal rewards;
///----------------------------------------------------------------------------------------------------------------/// Events///----------------------------------------------------------------------------------------------------------------/// @notice Stake has been started/// @dev Size and Time bonus have been calculated to burn FENIX in exchnge for equity to start stake/// @param _stake the stake objecteventStartStake(Stake indexed _stake);
/// @notice Stake has been deferred/// @dev Remove the stake and it's equity from the pool/// @param _stake the stake objecteventDeferStake(Stake indexed _stake);
/// @notice Stake has been ended/// @dev Remove the stake from the users stakes and mint the payout into the stakers wallet/// @param _stake the stake objecteventEndStake(Stake indexed _stake);
/// @notice Reward Pool has been flushed/// @dev Flushed reward pool into staker pooleventFlushRewardPool(Reward indexed reward);
/// @notice Share rate has been updated/// @dev Share rate has been updated/// @param _shareRate the new share rateeventUpdateShareRate(uint256indexed _shareRate);
///----------------------------------------------------------------------------------------------------------------/// Contract///----------------------------------------------------------------------------------------------------------------constructor() {
genesisTs =uint40(block.timestamp);
cooldownUnlockTs =block.timestamp+ REWARD_LAUNCH_COOLDOWN_TS;
}
/// @notice Evaluate if the contract supports the interface/// @dev Evaluate if the contract supports burning tokens/// @param interfaceId the interface to evaluatefunctionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IBurnRedeemable).interfaceId|| interfaceId ==this.supportsInterface.selector;
}
/// @notice Mint FENIX tokens/// @dev Mint FENIX tokens to the user address/// @param user the address of the user to mint FENIX tokens for/// @param amount the amount of FENIX tokens to mintfunctiononTokenBurned(address user, uint256 amount) external{
if (_msgSender() != XEN_ADDRESS) revert FenixError.WrongCaller(_msgSender());
if (user ==address(0)) revert FenixError.AddressZero();
if (amount ==0) revert FenixError.BalanceZero();
uint256 fenix = amount / XEN_BURN_RATIO;
rewardPoolSupply += fenix;
_mint(user, fenix);
emit Redeemed(user, XEN_ADDRESS, address(this), amount, fenix);
}
/// @notice Burn XEN tokens/// @dev Execute proof of burn on remote contract to burn XEN tokens/// @param xen the amount of XEN to burn from the current wallet addressfunctionburnXEN(uint256 xen) public{
IBurnableToken(XEN_ADDRESS).burn(_msgSender(), xen);
}
/// @notice Starts a stake/// @dev Initialize a stake for the current wallet address/// @param fenix the amount of fenix to stake/// @param term the number of days to stakefunctionstartStake(uint256 fenix, uint256 term) public{
if (fenix ==0) revert FenixError.BalanceZero();
if (term ==0) revert FenixError.TermZero();
uint40 startTs =uint40(block.timestamp);
uint40 endTs =uint40(block.timestamp+ (term * ONE_DAY_TS));
uint256 bonus = calculateBonus(fenix, term);
uint256 shares = calculateShares(bonus);
UD60x18 time = ud(term).div(ONE_YEAR_DAYS);
uint256 inflatedSupply = unwrap(ud(fenix).mul((ONE.add(ANNUAL_INFLATION_RATE)).pow(time)));
uint256 newShares = unwrap(ud(shares).mul(ud(inflatedSupply)));
equityPoolSupply += inflatedSupply;
equityPoolTotalShares += newShares;
Stake memory _stake = Stake(Status.ACTIVE, startTs, 0, endTs, uint16(term), fenix, newShares, 0);
stakes[_msgSender()].push(_stake);
_burn(_msgSender(), fenix);
emit StartStake(_stake);
}
/// @notice Defer stake until future date/// @dev Defer a stake by removing the supply allocated to the stake from the pool/// @param stakeIndex the index of the stake to defer/// @param stakerAddress the address of the stake owner that will be deferredfunctiondeferStake(uint256 stakeIndex, address stakerAddress) public{
if (stakes[stakerAddress].length<= stakeIndex) revert FenixError.StakeNotActive();
Stake memory _stake = stakes[stakerAddress][stakeIndex];
if (_stake.status != Status.ACTIVE) return;
if (block.timestamp< _stake.endTs && _msgSender() != stakerAddress)
revert FenixError.WrongCaller(_msgSender());
UD60x18 rewardPercent = ZERO;
if (block.timestamp> _stake.endTs) {
rewardPercent = ud(calculateLatePayout(_stake));
} else {
rewardPercent = ud(calculateEarlyPayout(_stake));
}
UD60x18 poolSharePercent = ud(_stake.shares).div(ud(equityPoolTotalShares));
UD60x18 stakerPoolSupplyPercent = poolSharePercent.mul(rewardPercent);
uint256 equitySupply = unwrap(ud(equityPoolSupply).mul(stakerPoolSupplyPercent));
Stake memory deferredStake = Stake(
Status.DEFER,
_stake.startTs,
uint40(block.timestamp),
_stake.endTs,
_stake.term,
_stake.fenix,
_stake.shares,
equitySupply
);
stakes[stakerAddress][stakeIndex] = deferredStake;
equityPoolTotalShares -= _stake.shares;
equityPoolSupply -= equitySupply;
emit DeferStake(deferredStake);
}
/// @notice End a stake/// @dev End a stake by allocating the stake supply to the stakers wallet/// @param stakeIndex the index of the stake to endfunctionendStake(uint256 stakeIndex) public{
deferStake(stakeIndex, _msgSender());
Stake memory _stake = stakes[_msgSender()][stakeIndex];
if (_stake.status == Status.END) revert FenixError.StakeStatusAlreadySet(Status.END);
_mint(_msgSender(), _stake.payout);
uint256 returnOnStake = unwrap(ud(_stake.payout).div(ud(_stake.fenix)));
if (returnOnStake > shareRate) {
shareRate = returnOnStake;
emit UpdateShareRate(shareRate);
}
Stake memory endedStake = Stake(
Status.END,
_stake.startTs,
_stake.deferralTs,
_stake.endTs,
_stake.term,
_stake.fenix,
_stake.shares,
_stake.payout
);
stakes[_msgSender()][stakeIndex] = endedStake;
emit EndStake(endedStake);
}
/// @notice Calculate bonus/// @dev Use fenix amount and term to calculate size and time bonus used for pool equity stake/// @param fenix the amount of fenix used to calculate the equity stake/// @param term the term of the stake in days used to calculate the pool equity stake/// @return bonus the bonus for pool equity stakefunctioncalculateBonus(uint256 fenix, uint256 term) publicpurereturns (uint256) {
UD60x18 sizeBonus = ud(calculateSizeBonus(fenix));
UD60x18 timeBonus = ud(calculateTimeBonus(term));
UD60x18 bonus = sizeBonus.mul(E.pow(timeBonus));
return unwrap(bonus);
}
/// @notice Calculate size bonus/// @dev Use fenix amount to calculate the size bonus used for pool equity stake/// @param fenix the amount of fenix used to calculate the equity stake/// @return bonus the size bonus for pool equity stakefunctioncalculateSizeBonus(uint256 fenix) publicpurereturns (uint256) {
if (fenix >= (UINT256_MAX -3)) revert FenixError.SizeGreaterThanMax();
return unwrap(ONE.sub((ud(fenix).add(ONE)).inv()));
}
/// @notice Calculate time bonus/// @dev Use term to calculate the time bonus used for pool equity stake/// @param term the term of the stake in days used to calculate the pool equity stake/// @return bonus the time bonus for pool equity stakefunctioncalculateTimeBonus(uint256 term) publicpurereturns (uint256) {
if (term > MAX_STAKE_LENGTH_DAYS) revert FenixError.TermGreaterThanMax();
UD60x18 timeBonus = ONE.add(ud(term).div(ud(MAX_STAKE_LENGTH_DAYS)));
return unwrap(timeBonus);
}
/// @notice Calculate shares/// @dev Use bonus to calculate the number of shares to be issued to the staker/// @param bonus the bonus to calculate the shares from/// @return shares the number of shares to be issued to the stakerfunctioncalculateShares(uint256 bonus) publicviewreturns (uint256) {
UD60x18 shares = ud(bonus).div(ud(shareRate));
return unwrap(shares);
}
/// @notice Calculate the early end stake penalty/// @dev Calculates the early end stake penality to be split between the pool and the staker/// @param stake the stake to calculate the penalty for/// @return reward the reward percentage for the stakefunctioncalculateEarlyPayout(Stake memory stake) publicviewreturns (uint256) {
if (block.timestamp< stake.startTs || stake.status != Status.ACTIVE) revert FenixError.StakeNotActive();
if (block.timestamp> stake.endTs) revert FenixError.StakeLate();
uint256 termDelta =block.timestamp- stake.startTs;
uint256 scaleTerm = stake.term * ONE_DAY_TS;
UD60x18 reward = (convert(termDelta).div(convert(scaleTerm))).powu(2);
return unwrap(reward);
}
/// @notice Calculate the late end stake penalty/// @dev Calculates the late end stake penality to be split between the pool and the staker/// @param stake a parameter just like in doxygen (must be followed by parameter name)/// @return reward the reward percentage for the stakefunctioncalculateLatePayout(Stake memory stake) publicviewreturns (uint256) {
if (block.timestamp< stake.startTs || stake.status != Status.ACTIVE) revert FenixError.StakeNotActive();
if (block.timestamp< stake.endTs) revert FenixError.StakeNotEnded();
uint256 lateTs =block.timestamp- stake.endTs;
if (lateTs > ONE_EIGHTY_DAYS_TS) return0;
UD60x18 penalty = ud(lateTs).div(ud(ONE_EIGHTY_DAYS_TS)).powu(3);
UD60x18 reward = ONE.sub(penalty);
return unwrap(reward);
}
/// @notice Flush reward pool/// @dev Flush reward pool to stake poolfunctionflushRewardPool() public{
if (block.timestamp< cooldownUnlockTs) revert FenixError.CooldownActive();
uint256 cooldownPeriods = (block.timestamp- cooldownUnlockTs) / REWARD_COOLDOWN_TS;
equityPoolSupply += rewardPoolSupply;
cooldownUnlockTs += REWARD_COOLDOWN_TS + (cooldownPeriods * REWARD_COOLDOWN_TS);
Reward memory reward = Reward(uint40(rewards.length), uint40(block.timestamp), rewardPoolSupply, _msgSender());
rewardPoolSupply =0;
rewards.push(reward);
emit FlushRewardPool(reward);
}
/// @notice Get stake for address at index/// @dev Read stake from stakes mapping stake array/// @param stakerAddress address of stake owner/// @param stakeIndex index of stake to read/// @return stakefunctionstakeFor(address stakerAddress, uint256 stakeIndex) publicviewreturns (Stake memory) {
return stakes[stakerAddress][stakeIndex];
}
/// @notice Get stake count for address/// @dev Read stake count from stakes mapping/// @param stakerAddress address of stake owner/// @return stake countfunctionstakeCount(address stakerAddress) publicviewreturns (uint256) {
return stakes[stakerAddress].length;
}
/// @notice Get reward for index/// @dev Read reward from rewards array/// @param index index of reward to read/// @return rewardfunctionrewardFor(uint256 index) publicviewreturns (Reward memory) {
return rewards[index];
}
/// @notice Get reward count/// @dev Read reward count from rewards array/// @return reward countfunctionrewardCount() publicviewreturns (uint256) {
return rewards.length;
}
}
Contract Source Code
File 9 of 17: Helpers.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import { wrap } from"./Casting.sol";
import { UD60x18 } from"./ValueType.sol";
/// @notice Implements the checked addition operation (+) in the UD60x18 type.functionadd(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() + y.unwrap());
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.functionand(UD60x18 x, uint256 bits) purereturns (UD60x18 result) {
result = wrap(x.unwrap() & bits);
}
/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.functionand2(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() & y.unwrap());
}
/// @notice Implements the equal operation (==) in the UD60x18 type.functioneq(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() == y.unwrap();
}
/// @notice Implements the greater than operation (>) in the UD60x18 type.functiongt(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() > y.unwrap();
}
/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.functiongte(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() >= y.unwrap();
}
/// @notice Implements a zero comparison check function in the UD60x18 type.functionisZero(UD60x18 x) purereturns (bool result) {
// This wouldn't work if x could be negative.
result = x.unwrap() ==0;
}
/// @notice Implements the left shift operation (<<) in the UD60x18 type.functionlshift(UD60x18 x, uint256 bits) purereturns (UD60x18 result) {
result = wrap(x.unwrap() << bits);
}
/// @notice Implements the lower than operation (<) in the UD60x18 type.functionlt(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() < y.unwrap();
}
/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.functionlte(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() <= y.unwrap();
}
/// @notice Implements the checked modulo operation (%) in the UD60x18 type.functionmod(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() % y.unwrap());
}
/// @notice Implements the not equal operation (!=) in the UD60x18 type.functionneq(UD60x18 x, UD60x18 y) purereturns (bool result) {
result = x.unwrap() != y.unwrap();
}
/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.functionnot(UD60x18 x) purereturns (UD60x18 result) {
result = wrap(~x.unwrap());
}
/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.functionor(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() | y.unwrap());
}
/// @notice Implements the right shift operation (>>) in the UD60x18 type.functionrshift(UD60x18 x, uint256 bits) purereturns (UD60x18 result) {
result = wrap(x.unwrap() >> bits);
}
/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.functionsub(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() - y.unwrap());
}
/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.functionuncheckedAdd(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() + y.unwrap());
}
}
/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.functionuncheckedSub(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
unchecked {
result = wrap(x.unwrap() - y.unwrap());
}
}
/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.functionxor(UD60x18 x, UD60x18 y) purereturns (UD60x18 result) {
result = wrap(x.unwrap() ^ y.unwrap());
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 13 of 17: IERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 14 of 17: IERC20Metadata.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)pragmasolidity ^0.8.0;import"../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/interfaceIERC20MetadataisIERC20{
/**
* @dev Returns the name of the token.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the symbol of the token.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the decimals places of the token.
*/functiondecimals() externalviewreturns (uint8);
}
Contract Source Code
File 15 of 17: Math.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import"../Common.sol"asCommon;
import"./Errors.sol"asErrors;
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 down.////// @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-nondetfunctionavg(UD60x18 x, UD60x18 y) purereturns (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 must be less than or equal to `MAX_WHOLE_UD60x18`.////// @param x The UD60x18 number to ceil./// @param result The smallest whole number greater than or equal to x, as a UD60x18 number./// @custom:smtchecker abstract-function-nondetfunctionceil(UD60x18 x) purereturns (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 + delta * (remainder > 0 ? 1 : 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./// @param result The quotient as a UD60x18 number./// @custom:smtchecker abstract-function-nondetfunctiondiv(UD60x18 x, UD60x18 y) purereturns (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 must be less than 133_084258667509499441.////// @param x The exponent as a UD60x18 number./// @return result The result as a UD60x18 number./// @custom:smtchecker abstract-function-nondetfunctionexp(UD60x18 x) purereturns (UD60x18 result) {
uint256 xUint = x.unwrap();
// This check prevents values greater than 192 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 must be less than 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-nondetfunctionexp2(UD60x18 x) purereturns (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./// @param result The greatest whole number less than or equal to x, as a UD60x18 number./// @custom:smtchecker abstract-function-nondetfunctionfloor(UD60x18 x) purereturns (UD60x18 result) {
assembly ("memory-safe") {
// Equivalent to `x % UNIT`.let remainder :=mod(x, uUNIT)
// Equivalent to `x - remainder * (remainder > 0 ? 1 : 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./// @param result The fractional part of x as a UD60x18 number./// @custom:smtchecker abstract-function-nondetfunctionfrac(UD60x18 x) purereturns (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-nondetfunctiongm(UD60x18 x, UD60x18 y) purereturns (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 down.////// 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-nondetfunctioninv(UD60x18 x) purereturns (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-nondetfunctionln(UD60x18 x) purereturns (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-nondetfunctionlog10(UD60x18 x) purereturns (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-ignoreassembly ("memory-safe") {
switch x
case1 { result :=mul(uUNIT, sub(0, 18)) }
case10 { result :=mul(uUNIT, sub(1, 18)) }
case100 { result :=mul(uUNIT, sub(2, 18)) }
case1000 { result :=mul(uUNIT, sub(3, 18)) }
case10000 { result :=mul(uUNIT, sub(4, 18)) }
case100000 { result :=mul(uUNIT, sub(5, 18)) }
case1000000 { result :=mul(uUNIT, sub(6, 18)) }
case10000000 { result :=mul(uUNIT, sub(7, 18)) }
case100000000 { result :=mul(uUNIT, sub(8, 18)) }
case1000000000 { result :=mul(uUNIT, sub(9, 18)) }
case10000000000 { result :=mul(uUNIT, sub(10, 18)) }
case100000000000 { result :=mul(uUNIT, sub(11, 18)) }
case1000000000000 { result :=mul(uUNIT, sub(12, 18)) }
case10000000000000 { result :=mul(uUNIT, sub(13, 18)) }
case100000000000000 { result :=mul(uUNIT, sub(14, 18)) }
case1000000000000000 { result :=mul(uUNIT, sub(15, 18)) }
case10000000000000000 { result :=mul(uUNIT, sub(16, 18)) }
case100000000000000000 { result :=mul(uUNIT, sub(17, 18)) }
case1000000000000000000 { result :=0 }
case10000000000000000000 { result := uUNIT }
case100000000000000000000 { result :=mul(uUNIT, 2) }
case1000000000000000000000 { result :=mul(uUNIT, 3) }
case10000000000000000000000 { result :=mul(uUNIT, 4) }
case100000000000000000000000 { result :=mul(uUNIT, 5) }
case1000000000000000000000000 { result :=mul(uUNIT, 6) }
case10000000000000000000000000 { result :=mul(uUNIT, 7) }
case100000000000000000000000000 { result :=mul(uUNIT, 8) }
case1000000000000000000000000000 { result :=mul(uUNIT, 9) }
case10000000000000000000000000000 { result :=mul(uUNIT, 10) }
case100000000000000000000000000000 { result :=mul(uUNIT, 11) }
case1000000000000000000000000000000 { result :=mul(uUNIT, 12) }
case10000000000000000000000000000000 { result :=mul(uUNIT, 13) }
case100000000000000000000000000000000 { result :=mul(uUNIT, 14) }
case1000000000000000000000000000000000 { result :=mul(uUNIT, 15) }
case10000000000000000000000000000000000 { result :=mul(uUNIT, 16) }
case100000000000000000000000000000000000 { result :=mul(uUNIT, 17) }
case1000000000000000000000000000000000000 { result :=mul(uUNIT, 18) }
case10000000000000000000000000000000000000 { result :=mul(uUNIT, 19) }
case100000000000000000000000000000000000000 { result :=mul(uUNIT, 20) }
case1000000000000000000000000000000000000000 { result :=mul(uUNIT, 21) }
case10000000000000000000000000000000000000000 { result :=mul(uUNIT, 22) }
case100000000000000000000000000000000000000000 { result :=mul(uUNIT, 23) }
case1000000000000000000000000000000000000000000 { result :=mul(uUNIT, 24) }
case10000000000000000000000000000000000000000000 { result :=mul(uUNIT, 25) }
case100000000000000000000000000000000000000000000 { result :=mul(uUNIT, 26) }
case1000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 27) }
case10000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 28) }
case100000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 29) }
case1000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 30) }
case10000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 31) }
case100000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 32) }
case1000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 33) }
case10000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 34) }
case100000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 35) }
case1000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 36) }
case10000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 37) }
case100000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 38) }
case1000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 39) }
case10000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 40) }
case100000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 41) }
case1000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 42) }
case10000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 43) }
case100000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 44) }
case1000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 45) }
case10000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 46) }
case100000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 47) }
case1000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 48) }
case10000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 49) }
case100000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 50) }
case1000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 51) }
case10000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 52) }
case100000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 53) }
case1000000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 54) }
case10000000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 55) }
case100000000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 56) }
case1000000000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 57) }
case10000000000000000000000000000000000000000000000000000000000000000000000000000 { result :=mul(uUNIT, 58) }
case100000000000000000000000000000000000000000000000000000000000000000000000000000 { 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.////// For $0 \leq x < 1$, the logarithm is calculated as:////// $$/// 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 must be greater than zero.////// @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-nondetfunctionlog2(UD60x18 x) purereturns (UD60x18 result) {
uint256 xUint = x.unwrap();
if (xUint < uUNIT) {
revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
}
unchecked {
// Calculate the integer part of the logarithm, add it to the result and finally calculate $y = x * 2^{-n}$.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;
// This is $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;
// 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-nondetfunctionmul(UD60x18 x, UD60x18 y) purereturns (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-nondetfunctionpow(UD60x18 x, UD60x18 y) purereturns (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`.elseif (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.elseif (yUint == uUNIT) {
return x;
}
// If x is greater than `UNIT`, use the standard formula.if (xUint > uUNIT) {
result = exp2(mul(log2(x), y));
}
// Conversely, if x is less than `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-nondetfunctionpowu(UD60x18 x, uint256 y) purereturns (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 down.////// Requirements:/// - x must be less than `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-nondetfunctionsqrt(UD60x18 x) purereturns (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));
}
}
// SPDX-License-Identifier: MITpragmasolidity >=0.8.19;import"./Casting.sol"asCasting;
import"./Helpers.sol"asHelpers;
import"./Math.sol"asMath;
/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity/// type int256.type SD59x18 isint256;
/*//////////////////////////////////////////////////////////////////////////
CASTING
//////////////////////////////////////////////////////////////////////////*/using {
Casting.intoInt256,
Casting.intoSD1x18,
Casting.intoUD2x18,
Casting.intoUD60x18,
Casting.intoUint256,
Casting.intoUint128,
Casting.intoUint40,
Casting.unwrap
} forSD59x18global;
/*//////////////////////////////////////////////////////////////////////////
MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/using {
Math.abs,
Math.avg,
Math.ceil,
Math.div,
Math.exp,
Math.exp2,
Math.floor,
Math.frac,
Math.gm,
Math.inv,
Math.log10,
Math.log2,
Math.ln,
Math.mul,
Math.pow,
Math.powu,
Math.sqrt
} forSD59x18global;
/*//////////////////////////////////////////////////////////////////////////
HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/using {
Helpers.add,
Helpers.and,
Helpers.eq,
Helpers.gt,
Helpers.gte,
Helpers.isZero,
Helpers.lshift,
Helpers.lt,
Helpers.lte,
Helpers.mod,
Helpers.neq,
Helpers.not,
Helpers.or,
Helpers.rshift,
Helpers.sub,
Helpers.uncheckedAdd,
Helpers.uncheckedSub,
Helpers.uncheckedUnary,
Helpers.xor
} forSD59x18global;
/*//////////////////////////////////////////////////////////////////////////
OPERATORS
//////////////////////////////////////////////////////////////////////////*/// The global "using for" directive makes it possible to use these operators on the SD59x18 type.using {
Helpers.addas+,
Helpers.and2as&,
Math.divas/,
Helpers.eqas==,
Helpers.gtas>,
Helpers.gteas>=,
Helpers.ltas<,
Helpers.lteas<=,
Helpers.modas%,
Math.mulas*,
Helpers.neqas!=,
Helpers.notas~,
Helpers.oras|,
Helpers.subas-,
Helpers.unaryas-,
Helpers.xoras^
} forSD59x18global;