Address Contract Verified
Address
0x787ccd7fCD64d35E34DD7c16a2C6604755eecB76
Balance
0 ETH
Nonce
1
Code Size
4963 bytes
Creator
0x1915F8fE...6621 at tx 0xa5ecb86a...41beed
Indexed Transactions
0
Contract Bytecode
4963 bytes
0x608060405234801561000f575f80fd5b50600436106101bb575f3560e01c8063b0e21e8a116100f3578063e41b886811610093578063f2fde38b1161006e578063f2fde38b1461037c578063f42ebbe01461038f578063f5ae497a146103a2578063ffa1ad74146103b5575f80fd5b8063e41b886814610342578063e4467f3514610355578063e9a2c29314610368575f80fd5b8063bd2e2a2d116100ce578063bd2e2a2d146102fa578063d4ce6dc51461030d578063dc3327d114610320578063e30c397814610331575f80fd5b8063b0e21e8a146102b7578063b8ca3b83146102df578063bcdfd7c2146102e7575f80fd5b8063462a43521161015e57806379ba50971161013957806379ba509714610279578063850a1501146102815780638da5cb5b14610294578063ae4e7a14146102a4575f80fd5b8063462a43521461024d578063514afc1a1461025e578063715018a614610271575f80fd5b8063164efc4211610199578063164efc421461021657806325ff967e1461021e5780632c240f9e1461023157806342ed82d414610245575f80fd5b8063014e8e90146101bf578063056d1528146101c9578063117bb2da146101eb575b5f80fd5b6101c76103e7565b005b6008546101d69060ff1681565b60405190151581526020015b60405180910390f35b6101fe6101f9366004610e3b565b61046e565b6040516001600160a01b0390911681526020016101e2565b6101c76107e4565b6101c761022c366004610ef5565b61081f565b6006546101d690600160b81b900460ff1681565b6101c7610883565b6002546001600160a01b03166101fe565b6101c761026c366004610f6e565b6108ef565b6101c7610915565b6101c761092e565b6004546101fe906001600160a01b031681565b5f546001600160a01b03166101fe565b6005546101fe906001600160a01b031681565b6006546102cc90600160a01b900461ffff1681565b60405161ffff90911681526020016101e2565b6102cc601481565b6101c76102f5366004610f6e565b610977565b6101c7610308366004610f90565b61099d565b6101c761031b366004610f90565b6109fa565b6003546001600160a01b03166101fe565b6001546001600160a01b03166101fe565b6101c7610350366004610f90565b610a77565b6101c7610363366004610fa9565b610af4565b6006546101d690600160b01b900460ff1681565b6101c761038a366004610f90565b610b7c565b6101c761039d366004610f90565b610bec565b6006546101fe906001600160a01b031681565b6103da60405180604001604052806006815260200165076312e302e360d41b81525081565b6040516101e2919061100f565b6005546001600160a01b0316336001600160a01b03161461041b5760405163a8ed10f560e01b815260040160405180910390fd5b600580546001600160a01b0319908116909155600480543392168217905560408051918252517f3714e1f41e46bee362d3d427b1d58a8a93e4f41cb17fa4c2b55bfad5eec40eb7916020908290030190a1565b6006545f90600160b01b900460ff1615610517576003546001600160a01b031663babcc539336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156104d6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104fa9190611021565b6105175760405163685b9fa960e11b815260040160405180910390fd5b6006548690600160b81b900460ff16156105b45760025460405163babcc53960e01b81526001600160a01b0383811660048301529091169063babcc53990602401602060405180830381865afa158015610573573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105979190611021565b6105b4576040516309eaa6cb60e11b815260040160405180910390fd5b6004546001600160a01b03166105dd57604051636d52fdaf60e01b815260040160405180910390fd5b60065460408051629a9b7b60e01b815290515f928b928b926001600160a01b03909216918291629a9b7b9160048083019260209291908290030181865afa15801561062a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061064e919061103c565b610659906001611053565b6040805160608101825261ffff808e16825260068054600160a01b90049091166020808401919091526004546001600160a01b0316838501528351808501855291825265076312e302e360d41b8282015292516106bd97969594938e929101611078565b60405160208183030381529060405290505f6007826040516020016106e39291906111b4565b60405160208183030381529060405290508051602082015ff093508361070b573d5f803e3d5ffd5b6040516001600160a01b03851681527f83a48fbcfc991335314e74d0496aab6a1987e992ddc85dddbcc4d6dd6ef2e9fc9060200160405180910390a161075084610c42565b6107656001600160a01b038b16338689610c9f565b6001600160a01b03841663ac6fdffb336040516001600160e01b031960e084901b1681526001600160a01b03909116600482015260248101899052604481018890526064015f604051808303815f87803b1580156107c1575f80fd5b505af11580156107d3573d5f803e3d5ffd5b505050505050509695505050505050565b6107ec610cf9565b60085460ff1615610810576040516301861b0d60e71b815260040160405180910390fd5b6008805460ff19166001179055565b610827610cf9565b60085460ff161561084b576040516301861b0d60e71b815260040160405180910390fd5b60078282604051602001610861939291906111d8565b6040516020818303038152906040526007908161087e919061123a565b505050565b61088b610cf9565b60065f9054906101000a90046001600160a01b03166001600160a01b0316635b34b8a86040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156108d7575f80fd5b505af11580156108e9573d5f803e3d5ffd5b50505050565b6108f7610cf9565b60068054911515600160b81b0260ff60b81b19909216919091179055565b604051630f02531960e31b815260040160405180910390fd5b60015433906001600160a01b0316811461096b5760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b61097481610d27565b50565b61097f610cf9565b60068054911515600160b01b0260ff60b01b19909216919091179055565b6109a5610cf9565b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f31e6d1bb06ec7bb4a4473b9c71ff9effbc1ed3a524fffcff55a20b54d07a7013906020015b60405180910390a150565b610a02610cf9565b6001600160a01b038116610a295760405163d92e233d60e01b815260040160405180910390fd5b600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f1f200025ef6dea1e52c7b2cdcf064910adbc03aa7034e4c5dedb301e0c42ffda906020016109ef565b610a7f610cf9565b6001600160a01b038116610aa65760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f16b79ce352b5e04946ca662a3854cc3aceb688395a6895f9f36374a7c6f33fad906020016109ef565b610afc610cf9565b601461ffff82161115610b2f57604051634bc811d160e01b815261ffff8216600482015260146024820152604401610962565b6006805461ffff60a01b1916600160a01b61ffff8416021790556040517f7fb16701ce34f6e63628ecafdb04a0b337d29d42597d65619a1ace94b3ed4f31906109ef9060019084906112fa565b610b84610cf9565b600180546001600160a01b0383166001600160a01b03199091168117909155610bb45f546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b610bf4610cf9565b600680546001600160a01b0319166001600160a01b0383169081179091556040519081527ff41bcdc5653b8b164bb747ed008de10ccfe33e09d3b06767183afc0deaa7927d906020016109ef565b60065460405163a8c0f72560e01b81526001600160a01b0383811660048301529091169063a8c0f725906024015f604051808303815f87803b158015610c86575f80fd5b505af1158015610c98573d5f803e3d5ffd5b5050505050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526108e9908590610d40565b5f546001600160a01b03163314610d255760405163118cdaa760e01b8152336004820152602401610962565b565b600180546001600160a01b031916905561097481610dac565b5f8060205f8451602086015f885af180610d5f576040513d5f823e3d81fd5b50505f513d91508115610d76578060011415610d83565b6001600160a01b0384163b155b156108e957604051635274afe760e01b81526001600160a01b0385166004820152602401610962565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80356001600160a01b0381168114610e11575f80fd5b919050565b803561ffff81168114610e11575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f805f805f80868803610120811215610e52575f80fd5b610e5b88610dfb565b9650610e6960208901610dfb565b9550610e7760408901610e16565b94506080605f1982011215610e8a575f80fd5b506040516080810181811067ffffffffffffffff82111715610eae57610eae610e27565b8060405250606088013581526080880135602082015260a0880135604082015260c088013560608201528093505060e0870135915061010087013590509295509295509295565b5f8060208385031215610f06575f80fd5b823567ffffffffffffffff80821115610f1d575f80fd5b818501915085601f830112610f30575f80fd5b813581811115610f3e575f80fd5b866020828501011115610f4f575f80fd5b60209290920196919550909350505050565b8015158114610974575f80fd5b5f60208284031215610f7e575f80fd5b8135610f8981610f61565b9392505050565b5f60208284031215610fa0575f80fd5b610f8982610dfb565b5f60208284031215610fb9575f80fd5b610f8982610e16565b5f5b83811015610fdc578181015183820152602001610fc4565b50505f910152565b5f8151808452610ffb816020860160208601610fc2565b601f01601f19169290920160200192915050565b602081525f610f896020830184610fe4565b5f60208284031215611031575f80fd5b8151610f8981610f61565b5f6020828403121561104c575f80fd5b5051919050565b8082018082111561107257634e487b7160e01b5f52601160045260245ffd5b92915050565b5f61018060018060a01b03808b168452808a166020850152808916604085015287606085015261ffff8088511660808601528060208901511660a0860152508060408801511660c085015250845160e0840152602085015161010084015260408501516101208401526060850151610140840152806101608401526110ff81840185610fe4565b9a9950505050505050505050565b600181811c9082168061112157607f821691505b60208210810361113f57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f81546111518161110d565b60018281168015611169576001811461117e576111aa565b60ff19841687528215158302870194506111aa565b855f526020805f205f5b858110156111a15781548a820152908401908201611188565b50505082870194505b5050505092915050565b5f6111bf8285611145565b83516111cf818360208801610fc2565b01949350505050565b5f6111e38286611145565b838582375f930192835250909392505050565b601f82111561087e57805f5260205f20601f840160051c8101602085101561121b5750805b601f840160051c820191505b81811015610c98575f8155600101611227565b815167ffffffffffffffff81111561125457611254610e27565b61126881611262845461110d565b846111f6565b602080601f83116001811461129b575f84156112845750858301515b5f19600386901b1c1916600185901b1785556112f2565b5f85815260208120601f198616915b828110156112c9578886015182559484019460019091019084016112aa565b50858210156112e657878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b604081016002841061131a57634e487b7160e01b5f52602160045260245ffd5b92815261ffff919091166020909101529056fea2646970667358221220702624c78cad40ff742ccf56d6daf1fc2295de64b402df0f9993a037e594e2a064736f6c63430008180033
Verified Source Code Full Match
Compiler: v0.8.24+commit.e11b9ed9
EVM: cancun
Optimization: Yes (200 runs)
ALTBCFactory.sol 88 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import "lib/liquidity-base/src/common/IErrors.sol";
import {ALTBCPool, FeeInfo, IERC20, SafeERC20} from "src/amm/ALTBCPool.sol";
import {ALTBCFactoryDeployed} from "src/common/IALTBCEvents.sol";
import {ALTBCInput} from "src/amm/ALTBC.sol";
import {FactoryBase} from "lib/liquidity-base/src/factory/FactoryBase.sol";
import "lib/liquidity-base/src/common/IEvents.sol";
import {ILPToken} from "lib/liquidity-base/src/common/ILPToken.sol";
/**
* @title Pool Factory
* @dev creates the pools in an automated and permissioned fashion
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
contract ALTBCFactory is FactoryBase {
using SafeERC20 for IERC20;
bytes altbcBytecode;
bool public isByteCodeImmutable;
string public constant VERSION = "v1.0.0";
modifier onlyIfByteCodeNotImmutable() {
if (isByteCodeImmutable) revert ByteCodeImmutable();
_;
}
/**
* @dev constructor receives and saves the ALTBCPool byte code to bypass contract side limit
*/
constructor() {
emit ALTBCFactoryDeployed(VERSION);
}
/**
* @dev deploys an ALTBC pool
* @param _xToken address of the X token (x axis)
* @param _yToken address of the Y token (y axis)
* @param _lpFee percentage of the fees in percentage basis points
* @param _tbcInput input data for the pool
* @param _xAdd the initial liquidity of xTokens that will be transferred to the pool
* @return deployedPool the address of the deployed pool
* @notice Only allowed deployers can deploy pools and only allowed yTokens are allowed
*/
function createPool(
address _xToken,
address _yToken,
uint16 _lpFee,
ALTBCInput memory _tbcInput,
uint256 _xAdd,
uint256 _wInactive
) external onlyAllowedDeployers onlyAllowedYTokens(_yToken) returns (address deployedPool) {
if (protocolFeeCollector == address(0)) revert NoProtocolFeeCollector();
bytes memory _constructor = abi.encode(
_xToken,
_yToken,
lpTokenAddress,
ILPToken(lpTokenAddress).currentTokenId() + 1,
FeeInfo(_lpFee, protocolFee, protocolFeeCollector),
_tbcInput,
VERSION
);
bytes memory deployBytecode = abi.encodePacked(altbcBytecode, _constructor);
assembly {
deployedPool := create(0, add(deployBytecode, 0x20), mload(deployBytecode))
if iszero(deployedPool) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
emit PoolCreated(deployedPool);
_addPoolToAllowList(deployedPool);
IERC20(_xToken).safeTransferFrom(_msgSender(), address(deployedPool), _xAdd);
ALTBCPool(deployedPool).initializePool(_msgSender(), _xAdd, _wInactive);
}
function setByteCode(bytes calldata _byteCode) external onlyOwner onlyIfByteCodeNotImmutable {
altbcBytecode = abi.encodePacked(altbcBytecode, _byteCode);
}
function makeByteCodeImmutable() external onlyOwner onlyIfByteCodeNotImmutable {
isByteCodeImmutable = true;
}
}
IErrors.sol 58 lines
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.24; /** * @dev File that contains all the errors for the project * @notice this file should be then imported in the contract files to use the errors. */ /**** CofNErrors ****/ error NegativeValue(); error DnTooLarge(); /**** PythonUtilErrors ****/ error bytesLargerThanUint256(); error diffGreaterThanUint256(); /**** PoolErrors ****/ error YTokenNotAllowed(); error XandYTokensAreTheSame(); error BeyondLiquidity(); error LPFeeAboveMax(uint16 proposedFee, uint16 maxFee); error YTokenDecimalsGT18(); error XTokenDecimalsIsNot18(); error ZeroValueNotAllowed(); error InvalidToken(); error XOutOfBounds(uint256 howMuch); error NotEnoughCollateral(); error ProtocolFeeAboveMax(uint16 proposedFee, uint16 maxFee); error NotProtocolFeeCollector(); error NotProposedProtocolFeeCollector(); error NoProtocolFeeCollector(); error CannotDepositInactiveLiquidity(); error InactiveLiquidityExceedsLimit(); error CCannotBeZero(); error VCannotBeZero(); error xMinCannotBeZero(); error MaxSlippageReached(); error LPTokenWithdrawalAmountExceedsAllowance(); error QTooHigh(); error TransactionExpired(); /**** PoolFactoryErrors ****/ error NotAnAllowedDeployer(); error ByteCodeImmutable(); /**** Input Errors ****/ error ZeroAddress(); /**** ERC721 Errors ****/ error URIQueryForNonexistentToken(); error PoolNotAllowed(); error TokenNotFromPool(); error PoolAlreadyAllowed(); error NotProposedFactory(address factoryAddressProposed); error NotFactory(); /**** Ownership Errors ****/ error RenouncingOwnershipForbidden();
ALTBCPool.sol 532 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {PoolBase, FeeInfo, IERC20, SafeERC20, CalculatorBase, MathLibs, packedFloat} from "lib/liquidity-base/src/amm/base/PoolBase.sol";
import "lib/liquidity-base/src/common/IErrors.sol";
import {ALTBCEquations} from "src/amm/ALTBCEquations.sol";
import {ALTBCInput, ALTBCDef} from "src/amm/ALTBC.sol";
import {ALTBCPoolDeployed, ALTBCCurveState} from "src/common/IALTBCEvents.sol";
import {Initializable} from "lib/liquidity-base/lib/solady/src/utils/Initializable.sol";
import {SafeCast} from "lib/liquidity-base/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
import {ILPToken} from "lib/liquidity-base/src/common/ILPToken.sol";
/**
* @title Adjustable Linear TBC Pool
* @dev This contract serves the purpose of facilitating swaps between a pair
* of tokens, where one is an xToken and the other one is a yToken.
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
contract ALTBCPool is PoolBase, Initializable {
using SafeERC20 for IERC20;
using ALTBCEquations for ALTBCDef;
using ALTBCEquations for uint256;
using MathLibs for int256;
using ALTBCEquations for packedFloat;
using MathLibs for packedFloat;
using SafeCast for uint;
using SafeCast for int;
ALTBCDef public tbc;
/**
* @dev constructor
* @param _xToken address of the X token (x axis)
* @param _yToken address of the Y token (y axis)
* @param fees fee infomation
* @param _tbcInput input parameters for the TBC
*/
constructor(
address _xToken,
address _yToken,
address _lpToken,
uint256 _inactiveLpId,
FeeInfo memory fees,
ALTBCInput memory _tbcInput,
string memory _VERSION
) PoolBase(_xToken, _yToken, _lpToken, _inactiveLpId, fees) {
_validateTBC(_tbcInput);
tbc.V = int(_tbcInput._V).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
tbc.xMin = int(_tbcInput._xMin).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
tbc.C = int(_tbcInput._C).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
tbc.calculateBn(x);
tbc.c = int(_tbcInput._lowerPrice).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
emit ALTBCPoolDeployed(_xToken, _yToken, _VERSION, fees._lpFee, fees._protocolFee, fees._protocolFeeCollector, _tbcInput);
}
/**
* @dev This is the function to initialize the pool.
* @param deployer The address of the deployer
* @param ___wInactive initial inactive liquidity for the pool
*/
function initializePool(address deployer, uint256 initialLiq, uint256 ___wInactive) external onlyOwner initializer {
_w = int(initialLiq).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
packedFloat __wInactive = int(___wInactive).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
tbc.xMax = tbc.xMin.add(_w);
packedFloat wActive = _w.sub(__wInactive);
checkInactiveLiquidity(wActive, __wInactive);
x = tbc.xMin;
_updateParameters();
packedFloat D0 = tbc.calculateDn(x);
packedFloat initialRJ = D0.div((_w.sub(__wInactive)));
ILPToken(lpToken).mintTokenAndUpdate(deployer, __wInactive, type(int256).max.toPackedFloat(0));
emit PositionMinted(inactiveLpId, _msgSender(), true);
emit LiquidityDeposited(deployer, inactiveLpId, ___wInactive, 0);
ILPToken(lpToken).mintTokenAndUpdate(deployer, wActive, initialRJ);
emit PositionMinted(activeLpId, _msgSender(), false);
emit LiquidityDeposited(deployer, activeLpId, initialLiq - ___wInactive, 0);
_emitCurveState();
_transferOwnership(deployer);
}
/**
* @dev This is the function to simulate a liquidity deposit into the pool.
* @param _A The amount of xToken being deposited as liquidity in the simulation.
* @param _B The amount of yToken being deposited as liquidity in the simulation.
* @return A calculated A value which is the amount of xToken that will be deposited
* @return B calculated B value which is the amount of yToken that will be deposited
* @return Q calculated Q value which is the ratio of this provided liquidity unit to the total liquidity of the pool
* @return ratio calculated ratio of xToken to yToken required for the deposit
* @return qFloat calculated qFloat value which is the ratio of this provided liquidity unit to the total liquidity of the pool in packedFloat format
*/
function simulateLiquidityDeposit(
uint256 _A,
uint256 _B
) public view returns (uint256 A, uint256 B, uint256 Q, int256 ratio, packedFloat qFloat, packedFloat L) {
packedFloat AFloat;
packedFloat BFloat;
L = tbc.calculateL(x);
(AFloat, BFloat, qFloat) = tbc.calculateQ(
x,
(_A.toInt256()).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE),
(_B.toInt256()).toPackedFloat(int(yDecimalDiff) - int(POOL_NATIVE_DECIMALS)),
L,
tbc.calculateDn(x)
);
A = AFloat.convertpackedFloatToWAD().toUint256();
B = BFloat.convertpackedFloatToSpecificDecimals(int(POOL_NATIVE_DECIMALS) - int(yDecimalDiff)).toUint256();
Q = qFloat.convertpackedFloatToWAD().toUint256();
if (BFloat.lt(ALTBCEquations.FLOAT_WAD)) {
ratio = type(int256).max; // The closest we can get to infinity
} else {
ratio = AFloat.div(BFloat).convertpackedFloatToDoubleWAD();
}
}
function tokenDepositUpdate(uint256 tokenId, packedFloat wj) internal returns (uint256) {
packedFloat h = retrieveH();
if (tokenId == 0) {
tokenId = ILPToken(lpToken).mintTokenAndUpdate(_msgSender(), wj, h);
emit PositionMinted(tokenId, _msgSender(), false);
} else {
(packedFloat w_hat, packedFloat r_hat) = ILPToken(lpToken).getLPToken(tokenId);
packedFloat newWj = wj.add(w_hat);
packedFloat newRj = h.calculateLastRevenueClaim(wj, r_hat, w_hat);
ILPToken(lpToken).updateLPToken(tokenId, newWj, newRj);
}
return tokenId;
}
/**
* @dev This is the function to deposit liquidity into the pool.
* @notice If the tokenId provided is owned by the lp, this tokenId will be updated based on liquidity deposit
* @param tokenId The tokenId owned by the liquidity provider.
* @param _A The amount of xToken being deposited as liquidity.
* @param _B The amount of yToken being deposited as liquidity.
* @param _minA The minimum acceptable amount of xToken actually deposited as liquidity.
* @param _minB The minimum acceptable amount of yToken actually deposited as liquidity.
* @param expires Timestamp at which the deposit transaction will expire.
* @return A calculated A value
* @return B calculated B value
*/
function depositLiquidity(
uint256 tokenId,
uint256 _A,
uint256 _B,
uint256 _minA,
uint256 _minB,
uint256 expires
) external whenNotPaused checkExpiration(expires) returns (uint256 A, uint256 B) {
// Inactive NFT check
if (tokenId == inactiveLpId) {
revert CannotDepositInactiveLiquidity();
}
packedFloat L;
packedFloat qFloat;
(A, B, , , qFloat, L) = simulateLiquidityDeposit(_A, _B);
if (A == 0 && B == 0) revert ZeroValueNotAllowed();
_checkSlippage(A, _minA);
_checkSlippage(B, _minB);
IERC20(xToken).safeTransferFrom(_msgSender(), address(this), A);
IERC20(yToken).safeTransferFrom(_msgSender(), address(this), B);
tbc.calculateZ(L, _w, _wInactive(), qFloat, false);
packedFloat wj = qFloat.mul(_w);
packedFloat multiplier = ALTBCEquations.FLOAT_1.add(qFloat);
x = tbc._liquidityUpdateHelper(x, multiplier);
_w = _w.add(wj); // add the additional liquidity to the total liquidity
tokenId = tokenDepositUpdate(tokenId, wj);
emit LiquidityDeposited(_msgSender(), tokenId, A, B);
_emitCurveState();
}
/**
* @dev This is the function to simulate a liquidity withdrawal from the pool.
* @dev To get rj and uj, call the getLPToken function and pass in the rj and uj values
* @param tokenId The tokenId owned by the liquidity provider.
* @param uj The amount of liquidity being withdrawn
* @param rj The revenue accrued to the liquidity position
* @param _uj The amount of liquidity being withdrawn in packedFloat format
* @return Ax The amount of xToken to be received
* @return Ay The amount of yToken to be received
* @return revenueAccrued The amount of revenue accrued to the liquidity position
* @return q The ratio of this provided liquidity unit to the total liquidity of the pool
*/
function simulateWithdrawLiquidity(
uint256 tokenId,
uint256 uj,
packedFloat _uj
) public view returns (uint256 Ax, uint256 Ay, uint256 revenueAccrued, packedFloat q, packedFloat L, packedFloat wj, packedFloat rj) {
if (uj == 0 && _uj.eq(ALTBCEquations.FLOAT_0)) revert ZeroValueNotAllowed();
else if (uj != 0) {
_uj = (uj.toInt256()).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
}
(wj, rj) = ILPToken(lpToken).getLPToken(tokenId);
if (wj.lt(_uj)) revert LPTokenWithdrawalAmountExceedsAllowance();
L = tbc.calculateL(x);
packedFloat hn = tbc.calculateH(L, _w, _wInactive(), _collectedLPFees);
// STEP 1 - Get q and multiplier
q = _uj.div(_w);
// STEP 2 - Calc amount out
{
packedFloat rawAx = q.mul(tbc.xMax.sub(x));
Ax = rawAx.convertpackedFloatToWAD().toUint256();
}
{
packedFloat rawAy = q.mul(tbc.calculateDn(x).sub(tbc.calculateL(x)));
// check for lower bound before casting to int
Ay = rawAy.lt(ALTBCEquations.FLOAT_WAD)
? 0
: rawAy.convertpackedFloatToSpecificDecimals(int(POOL_NATIVE_DECIMALS) - int(yDecimalDiff)).toUint256();
}
packedFloat revenuePerLiquidity = hn.sub(rj);
// STEP 3 - Calculate revenue accrued. This check is important due to the wInactive position rj value.
revenueAccrued = revenuePerLiquidity.gt(ALTBCEquations.FLOAT_0)
? _uj.mul(revenuePerLiquidity).convertpackedFloatToSpecificDecimals(int(POOL_NATIVE_DECIMALS) - int(yDecimalDiff)).toUint256()
: 0;
}
/**
* @dev This is the function to withdraw partial liquidity from the pool.
* @param tokenId The tokenId owned by the liquidity provider.
* @param uj The amount of liquidity being withdrawn
* @param recipient address that receives withdrawn liquidity
* @param _minAx The minimum acceptable amount of xToken actually withdrawn from liquidity.
* @param _minAy The minimum acceptable amount of yToken actually withdrawn from liquidity.
* @param expires Timestamp at which the withdraw transaction will expire.
*/
function withdrawPartialLiquidity(
uint256 tokenId,
uint256 uj,
address recipient,
uint256 _minAx,
uint256 _minAy,
uint256 expires
) external checkExpiration(expires) {
packedFloat _uj = (uj.toInt256()).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE);
_withdrawLiquidity(tokenId, _uj, recipient, _minAx, _minAy);
}
/**
* @dev This is the function to withdraw all token liquidity from the pool.
* @param tokenId The tokenId owned by the liquidity provider.
* @param recipient address that receives withdrawn liquidity
* @param _minAx The minimum acceptable amount of xToken actually withdrawn from liquidity.
* @param _minAy The minimum acceptable amount of yToken actually withdrawn from liquidity.
* @param expires Timestamp at which the withdraw transaction will expire.
*/
function withdrawAllLiquidity(
uint256 tokenId,
address recipient,
uint256 _minAx,
uint256 _minAy,
uint256 expires
) external checkExpiration(expires) {
(packedFloat wj, ) = ILPToken(lpToken).getLPToken(tokenId);
_withdrawLiquidity(tokenId, wj, recipient, _minAx, _minAy);
}
/**
* @dev This is the function to withdraw liquidity from the pool.
* @param tokenId The tokenId owned by the liquidity provider.
* @param _uj The amount of liquidity being withdrawn
* @param recipient address that receives withdrawn liquidity
* @param _minAx The minimum acceptable amount of xToken actually withdrawn from liquidity.
* @param _minAy The minimum acceptable amount of yToken actually withdrawn from liquidity.
*/
function _withdrawLiquidity(uint256 tokenId, packedFloat _uj, address recipient, uint256 _minAx, uint256 _minAy) internal {
// We update the revenue of the lp token before calculating the amount out for efficiency purposes
if (ILPToken(lpToken).ownerOf(tokenId) != _msgSender()) revert InvalidToken();
(
uint256 Ax,
uint256 Ay,
uint256 revenueAccrued,
packedFloat q,
packedFloat L,
packedFloat wj,
packedFloat rj
) = simulateWithdrawLiquidity(tokenId, 0, _uj);
{
packedFloat newWj = wj.sub(_uj);
_checkSlippage(Ax, _minAx);
_checkSlippage(Ay, _minAy);
// Update pool state
ILPToken(lpToken).updateLPTokenWithdrawal(tokenId, newWj, rj);
if (tokenId == activeLpId) {
(packedFloat __wInactive, ) = ILPToken(lpToken).getLPToken(inactiveLpId);
checkInactiveLiquidity(newWj, __wInactive);
}
}
packedFloat multiplier = ALTBCEquations.FLOAT_1.sub(q);
// Update Z with current _w and _wInactive values
if (tokenId == inactiveLpId) {
tbc.Zn = tbc.Zn.add((q.mul(L)));
} else {
tbc.calculateZ(L, _w, _wInactive(), q, true);
}
// if multiplier is 0 the pool will have no liquidity and should be closed
if (multiplier.eq(ALTBCEquations.FLOAT_0)) {
_pause();
_transferOwnership(address(0));
} else {
x = tbc._liquidityUpdateHelper(x, multiplier);
}
{
// Update LPToken and W
_w = _w.sub(_uj);
// Transfer the liquidity amounts to the lp
recipient = recipient == address(0) ? _msgSender() : recipient;
IERC20(xToken).safeTransfer(recipient, Ax);
IERC20(yToken).safeTransfer(recipient, Ay + revenueAccrued);
_emitLiquidityWithdrawn(tokenId, Ax, Ay, revenueAccrued, recipient);
_emitCurveState();
}
}
// This is a helper function to avoid stack too deep errors
/**
* @dev This is the function to emit the LiquidityWithdrawn event.
* @param tokenId The tokenId owned by the liquidity provider.
* @param Ax The amount of xToken to be received
* @param Ay The amount of yToken to be received
* @param revenueAccrued The amount of revenue accrued to the liquidity position
* @param recipient The address that receives the withdrawn liquidity
*/
function _emitLiquidityWithdrawn(uint256 tokenId, uint256 Ax, uint256 Ay, uint256 revenueAccrued, address recipient) private {
emit LiquidityWithdrawn(_msgSender(), tokenId, Ax, Ay, revenueAccrued, recipient);
}
/**
* @dev This is the function to withdraw revenue from the pool.
* @param tokenId The tokenId owned by the liquidity provider.
* @param Q The amount of revenue being withdrawn
* @return revenue The amount of revenue being withdrawn
*/
function withdrawRevenue(uint256 tokenId, uint256 Q, address recipient) external returns (uint256 revenue) {
if (Q == 0) revert ZeroValueNotAllowed();
if (ILPToken(lpToken).ownerOf(tokenId) != _msgSender() || (tokenId == inactiveLpId)) revert InvalidToken();
packedFloat _Q = (Q.toInt256()).toPackedFloat(int(yDecimalDiff) - int(POOL_NATIVE_DECIMALS));
(, packedFloat _wj, packedFloat _rj, packedFloat tokenRevenueAvailable, ) = _getRevenueAvailable(tokenId);
if (_Q.gt(tokenRevenueAvailable)) revert QTooHigh();
packedFloat updatedRj = _rj.add(_Q.div(_wj));
ILPToken(lpToken).updateLPToken(tokenId, _wj, updatedRj);
revenue = _normalizeTokenDecimals(false, Q);
recipient = recipient == address(0) ? _msgSender() : recipient;
IERC20(yToken).safeTransfer(recipient, revenue);
emit RevenueWithdrawn(_msgSender(), tokenId, revenue, recipient);
}
/**
* @dev This is the function to get the revenue available for a liquidity position.
* @param tokenId The tokenId representing the liquidity position
* @return _revenueAvailable The amount of revenue available for the liquidity position
*/
function revenueAvailable(uint256 tokenId) public view returns (uint256 _revenueAvailable) {
(, , , , _revenueAvailable) = _getRevenueAvailable(tokenId);
}
/**
* @dev This is the function to get the revenue available for a liquidity provider.
* @param tokenId The tokenId owned by the liquidity provider
* @return hn The total revenue per liquidity unit for the pool
* @return _wj The amount of liquidity units of the specified token
* @return _rj The revenue accrued to the liquidity position
* @return _revenueAvailable The amount of revenue available for the liquidity provider
*/
function _getRevenueAvailable(
uint256 tokenId
)
internal
view
returns (packedFloat hn, packedFloat _wj, packedFloat _rj, packedFloat _revenueAvailable, uint256 revenueAvailableUint)
{
hn = retrieveH();
(_wj, _rj) = ILPToken(lpToken).getLPToken(tokenId);
_revenueAvailable = _wj.calculateRevenueAvailable(hn, _rj);
revenueAvailableUint = _revenueAvailable.lt(ALTBCEquations.FLOAT_WAD)
? 0
: _revenueAvailable.convertpackedFloatToSpecificDecimals(int(POOL_NATIVE_DECIMALS) - int(yDecimalDiff)).toUint256();
}
/**
* @dev This is the function to retrieve the current spot price of the x token.
* @return sPrice the price in YToken Decimals
* @notice x + 1 is used for returning the price of the next token sold, not the price of the last token sold
*/
function _spotPrice() internal view override returns (packedFloat sPrice) {
// Price P(N+1) = f(x(n+1));
sPrice = tbc.calculatefx(x.add(int(1).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE)));
}
/**
* @dev This function updates the state of the math values of the pool.
*/
function _updateParameters() internal override {
// Calculate Dn using Bn and cn before they get updated
packedFloat oldBn = tbc.b;
// Calculate Bn (Sn)
tbc.calculateBn(x);
// we update c only if x is not zero
if (packedFloat.unwrap(x) > 0) tbc.calculateC(x, oldBn);
}
/**
* @dev This function calculates the amount of token X required for the user to purchase a specific amount of Token Y (buy y with x : out perspective).
* @param _amountOfY desired amount of token Y
* @return amountOfX required amount of token X
*/
function _calculateAmountOfXRequiredBuyingY(packedFloat _amountOfY) internal view override returns (packedFloat amountOfX) {
packedFloat comparisonDn = tbc.calculateDn(tbc.xMin);
packedFloat Dn = tbc.calculateDn(x);
// Dn - An >= D(Xmin, bn, cn)
if (Dn.sub(_amountOfY).lt(comparisonDn)) {
revert NotEnoughCollateral();
}
// Xn+1 = 2Dn / (cn + sqrt(cn^2 + 2bn*Dn+1)) where Dn+1 = Dn - An
packedFloat _updatedX = tbc.calculateXofNPlus1(Dn.sub(_amountOfY)); // XOutOfBounds is impossible to be triggered in this scenario. arithmetic overflow instead
amountOfX = x.sub(_updatedX);
}
/**
* @dev This function calculates the amount of token Y required for the user to purchase a specific amount of Token X (buy x with y : out perspective).
* @param _amountOfX desired amount of token X (also known as An in the spec)
* @return amountOfY required amount of token Y
*/
function _calculateAmountOfYRequiredBuyingX(packedFloat _amountOfX) internal view override returns (packedFloat amountOfY) {
// Xn + An
packedFloat _updatedX = x.add(_amountOfX);
// Xn + An <= Xmax
if (_updatedX.gt(tbc.xMax)) revert XOutOfBounds(_updatedX.sub(tbc.xMax).sub(tbc.xMin).convertpackedFloatToWAD().toUint256());
// Dn+1 - Dn
packedFloat Dn = tbc.calculateDn(x);
packedFloat DnPlusOne = tbc.calculateDn(_updatedX);
amountOfY = DnPlusOne.sub(Dn);
}
/**
* @dev This function calculates the amount of token Y the user will receive when selling token X (sell x for y : in perspective).
* @param _amountOfX amount of token X to be sold
* @return amountOfY amount of token Y to be received
*/
function _calculateAmountOfYReceivedSellingX(packedFloat _amountOfX) internal view override returns (packedFloat amountOfY) {
// Xn - An >= Xmin
if (tbc.xMin.gt(x.sub(_amountOfX))) revert XOutOfBounds(tbc.xMin.add(_amountOfX.sub(x)).convertpackedFloatToWAD().toUint256());
// Xn+1 = Xn - An
packedFloat _updatedX = x.sub(_amountOfX);
// Dn+1 - Dn
packedFloat Dn = tbc.calculateDn(x);
packedFloat DnPlusOne = tbc.calculateDn(_updatedX);
amountOfY = Dn.sub(DnPlusOne);
}
/**
* @dev This function calculates the amount of token X the user will receive when selling token Y (sell y for x : in perspective).
* @param _amountOfY amount of token Y to be sold
* @return amountOfX amount of token X to be received
*/
function _calculateAmountOfXReceivedSellingY(packedFloat _amountOfY) internal view override returns (packedFloat amountOfX) {
// Dn
packedFloat Dn = tbc.calculateDn(x);
packedFloat DMax = tbc.calculateDn(tbc.xMax);
// Dn + An >= D(Xmax, bn, cn)
if (Dn.add(_amountOfY).gt(DMax)) {
revert DnTooLarge();
}
// Xn+1 = 2Dn / (cn + sqrt(cn^2 + 2bn*Dn+1)) where Dn+1 = Dn + An
packedFloat _updatedX = tbc.calculateXofNPlus1(Dn.add(_amountOfY));
amountOfX = _updatedX.sub(x);
}
/**
* @dev A helper function to validate most of constructor's inputs.
* @param _tbcInput input parameters for the TBC
*/
function _validateTBC(ALTBCInput memory _tbcInput) internal pure {
if (_tbcInput._C == 0) revert CCannotBeZero();
if (_tbcInput._V == 0) revert VCannotBeZero();
if (_tbcInput._xMin == 0) revert xMinCannotBeZero();
}
/**
* @dev Check for ration of inactive to active (token Id 2) liquidity, reverts if ratio is above threshold
* @param _active active liquidity units
* @param _inactive inactive liquidity units
* @notice The threshold is set to 1% of the active liquidity units
*/
function checkInactiveLiquidity(packedFloat _active, packedFloat _inactive) internal pure {
if (_inactive.eq(ALTBCEquations.FLOAT_0)) return;
if (_active.le(ALTBCEquations.FLOAT_0)) revert InactiveLiquidityExceedsLimit();
if (_active.div(_inactive.add(_active)).lt(ACTIVE_LIQUIDITY_MINIMUM)) revert InactiveLiquidityExceedsLimit();
}
function retrieveH() public view returns (packedFloat h) {
h = tbc.calculateH(tbc.calculateL(x), _w, _wInactive(), _collectedLPFees);
}
function _emitCurveState() internal override {
emit ALTBCCurveState(tbc, x);
}
}
IALTBCEvents.sol 19 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import "liquidity-base/src/common/IEvents.sol";
import {ALTBCInput, ALTBCDef} from "src/amm/ALTBC.sol";
event ALTBCFactoryDeployed(string _version);
event ALTBCPoolDeployed(
address indexed _xToken,
address indexed _yToken,
string _version,
uint16 _lpFee,
uint16 _protocolFee,
address _protocolFeeCollector,
ALTBCInput _tbcInput
);
event ALTBCCurveState(ALTBCDef altbc, packedFloat x);
ALTBC.sol 29 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {packedFloat} from "liquidity-base/src/amm/mathLibs/MathLibs.sol";
/**
* @title TBC Data Structures
* @dev All TBC definitions can be found here.
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
/// ALTBC
struct ALTBCDef {
packedFloat b;
packedFloat c;
packedFloat C;
packedFloat xMin;
packedFloat xMax;
packedFloat V;
packedFloat Zn;
}
struct ALTBCInput {
uint256 _lowerPrice;
uint256 _V;
uint256 _xMin;
uint256 _C;
}
FactoryBase.sol 167 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import "../common/IErrors.sol";
import {IFactory} from "../factory/IFactory.sol";
import {IAllowList} from "../allowList/IAllowList.sol";
import {CommonEvents, IPoolEvents, IFactoryEvents} from "../common/IEvents.sol";
import {ILPToken} from "../../src/common/ILPToken.sol";
/**
* @title Pool Factory
* @dev creates the pools in an automated and permissioned fashion
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
abstract contract FactoryBase is Ownable2Step, IFactory {
uint16 public constant MAX_PROTOCOL_FEE = 20;
address yTokenAllowList;
address deployerAllowList;
address public protocolFeeCollector;
address public proposedProtocolFeeCollector;
address public lpTokenAddress;
uint16 public protocolFee;
bool public gateDeployers;
bool public gateYTokens;
constructor() Ownable(_msgSender()) {}
modifier onlyAllowedDeployers() {
// only gate the deployers if the switch is turned on.
if (gateDeployers){
if (!IAllowList(deployerAllowList).isAllowed(_msgSender())) revert NotAnAllowedDeployer();
}
_;
}
modifier onlyAllowedYTokens(address _yToken) {
// only gate the Y-Tokens if the switch is turned on.
if (gateYTokens){
if (!IAllowList(yTokenAllowList).isAllowed(_yToken)) revert YTokenNotAllowed();
}
_;
}
modifier onlyProposedProtocolFeeCollector() {
if (_msgSender() != proposedProtocolFeeCollector) revert NotProposedProtocolFeeCollector();
_;
}
/**
* @dev sets the y token allow list
* @param _address of the allow list contract
*/
function setYTokenAllowList(address _address) external onlyOwner {
if (_address == address(0)) revert ZeroAddress();
yTokenAllowList = _address;
emit SetYTokenAllowList(_address);
}
/**
* @dev gets the y token allow list
* @return _address of the current allow list contract
*/
function getYTokenAllowList() external view returns (address) {
return yTokenAllowList;
}
/**
* @dev sets the deployer allow list
* @param _address of the allow list contract
* @notice Only the owner can set the deployer allow list
*/
function setDeployerAllowList(address _address) external onlyOwner {
if (_address == address(0)) revert ZeroAddress();
deployerAllowList = _address;
emit SetDeployerAllowList(_address);
}
/**
* @dev gets the deployer allow list
* @return _address of the current allow list contract
*/
function getDeployerAllowList() external view returns (address) {
return deployerAllowList;
}
/**
* @dev This is the function to update the protocol fees per trade.
* @param _protocolFee percentage of the transaction that will get collected as fees (in percentage basis points:
* 10000 -> 100.00%; 500 -> 5.00%; 1 -> 0.01%)
* @notice Only the owner can set the protocol fee
*/
function setProtocolFee(uint16 _protocolFee) public onlyOwner {
if (_protocolFee > MAX_PROTOCOL_FEE) revert ProtocolFeeAboveMax({proposedFee: _protocolFee, maxFee: MAX_PROTOCOL_FEE});
protocolFee = _protocolFee;
emit CommonEvents.FeeSet(CommonEvents.FeeCollectionType.PROTOCOL, _protocolFee);
}
/**
* @dev function to propose a new protocol fee collector
* @param _protocolFeeCollector the new fee collector
* @notice that only the current fee collector address can call this function
*/
function proposeProtocolFeeCollector(address _protocolFeeCollector) external onlyOwner {
// slither-disable-start missing-zero-check // unnecessary
proposedProtocolFeeCollector = _protocolFeeCollector;
// slither-disable-end missing-zero-check
emit ProtocolFeeCollectorProposed(_protocolFeeCollector);
}
/**
* @dev function to confirm a new protocol fee collector
* @notice that only the already proposed fee collector can call this function
*/
function confirmProtocolFeeCollector() external onlyProposedProtocolFeeCollector {
delete proposedProtocolFeeCollector;
protocolFeeCollector = _msgSender();
emit ProtocolFeeCollectorConfirmed(_msgSender());
}
/**
* @dev Overriden rounounceOwnership from Ownable.sol
* @notice This method prevents irreversible loss of admin rights
*/
function renounceOwnership() public pure override {
revert RenouncingOwnershipForbidden();
}
/**
* @dev confirm the factory address
* @notice Used for the LPToken facotry role. Only the owner is allowed to accept the factory role
*/
function acceptLPTokenRole() external onlyOwner {
ILPToken(lpTokenAddress).confirmFactoryAddress();
}
/**
* @dev set the LPToken address
* @param LPTokenAddress the address of the LPToken contract
* @notice Only the owner can set the LPToken address
* @notice This function is used to set the LPToken address for the factory
*/
function setLPTokenAddress(address LPTokenAddress) external onlyOwner {
lpTokenAddress = LPTokenAddress;
emit LPTokenAddressSet(LPTokenAddress);
}
/**
* @dev add pool address to LPToken allow list
* @param pool the address of the pool to be added to the LPToken allow list
* @notice Only the owner can set the LPToken address
* @notice Used to allow pools to update LPTokens
*/
function _addPoolToAllowList(address pool) internal {
ILPToken(lpTokenAddress).addPoolToAllowList(pool);
}
function setGateDeployers(bool _gateDeployers) external onlyOwner {
gateDeployers = _gateDeployers;
}
function setGateYTokens(bool _gateYTokens) external onlyOwner {
gateYTokens = _gateYTokens;
}
}
IEvents.sol 69 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {packedFloat} from "../amm/mathLibs/MathLibs.sol";
/**
* @dev File that contains all the events for the project
* @author @oscarsernarosero @mpetersoCode55 @cirsteve @palmerg4
* @notice this file should be then inherited in the contract interfaces to use the events.
*/
/**
* @dev events common for the pool and the factory contract
* @notice any change in this interface most likely means a breaking change with monitoring services
*/
interface CommonEvents {
enum FeeCollectionType {
LP,
PROTOCOL
}
event ProtocolFeeCollectorProposed(address _collector);
event ProtocolFeeCollectorConfirmed(address _collector);
event FeeSet(FeeCollectionType _feeType, uint16 _fee);
}
/**
* @dev events for the pool contract
* @notice any change in this interface most likely means a breaking change with monitoring services
*/
interface IPoolEvents is CommonEvents {
event FeesCollected(FeeCollectionType _feeType, address _collector, uint256 _amount);
event Swap(address _tokenIn, uint256 _amountIn, uint256 _amountOut, uint256 _minOut, address _recipient);
event RevenueWithdrawn(address _collector, uint256 tokenId, uint256 _amount, address _recipient);
event LiquidityWithdrawn(
address lp,
uint tokenId,
uint256 amountOutXToken,
uint256 amountOutYToken,
uint256 revenue,
address _recipient
);
event LiquidityDeposited(address _sender, uint256 _tokenId, uint256 _A, uint256 _B);
event LPTokenUpdated(uint256 tokenId, packedFloat wj, packedFloat hn);
event FeesGenerated(uint256 lpFee, uint256 protocolFee);
event PositionMinted(uint256 tokenId, address owner, bool isInactive);
}
/**
* @dev events for the pool-factory contract
* @notice any change in this interface most likely means a breaking change with monitoring services
*/
interface IFactoryEvents is CommonEvents {
event PoolCreated(address _pool);
event SetYTokenAllowList(address _allowedList);
event SetDeployerAllowList(address _allowedList);
event LPTokenAddressSet(address _LPTokenAddres);
}
interface IAllowListEvents {
event AllowListDeployed();
event AddressAllowed(address _address, bool _allowed);
}
interface ILPTokenEvents{
event ALTBCPositionTokenDeployed();
event PoolAddedToAllowList(address pool, uint256 inactiveTokenId);
event FactoryProposed(address factory);
event FactoryConfirmed(address factory);
event LPTokenUpdated(uint256 tokenId, packedFloat wj, packedFloat hn);
}
ILPToken.sol 107 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC721} from "../../lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import {IERC721Enumerable} from "../../lib/openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {packedFloat} from "../amm/mathLibs/MathLibs.sol";
/**
* @title Interface Liquidity Provider Token
* @dev This contract serves as the LP Token associated with all ALTBC liquidity positions.
* @dev Revenue and liquidity position are stored in the LP Token data and updated by the pool contract.
* @author @palmerg4 @oscarsernarosero @cirsteve
*/
struct LPTokenS {
packedFloat wj;
packedFloat rj;
}
interface ILPToken is IERC721, IERC721Enumerable {
/**
* @dev Get the liquidity share and last claimed amount for an lpToken
* @param tokenId The token id of the lpToken being updated
* @return wj the amount of the lpToken
* @return rj the last revenue claim of the lpToken
*/
function getLPToken(uint256 tokenId) external view returns (packedFloat wj, packedFloat rj);
/**
* @dev Get the inactive status tokenId
* @param tokenId The token id of the lpToken being queried
* @return bool true if the token is inactive
*/
function inactiveToken(uint256 tokenId) external view returns (bool);
/**
* @dev Mints a new lpToken to a liquidity provider and updated the value associated with this new lpToken
* @notice The internal version of the mint method. Used in the constructor, in order to circumvent ownership transfers.
* @param lp The address of the liquidity provider owning the lpToken being updated
* @param wj The amount of liquidity provided by the liquidity provider
* @param hn The revenue parameter of the pool associated with the lpToken contract
* @notice this function should be gated to only allwed pools
*/
function mintTokenAndUpdate(address lp, packedFloat wj, packedFloat hn) external returns (uint256 tokenId);
/**
* @dev Updates the values wj and rj of tokenId
* @param tokenId The token id of the lpToken being updated
* @param _wj The amount of liquidity associated with the lpToken being updated
* @param _rj The amount of revenue associated with the lpToken being updated
* @notice this function should be gated to only Ids that belong to the caller pool
*/
function updateLPToken(uint256 tokenId, packedFloat _wj, packedFloat _rj) external;
/**
* @dev Updates the amount of liquidity associated with an LP Token. Used when withdrawing a full or partial liquidity position. * @param _tokenId The token id of the lpToken being updated
* @param _wj The amount of liquidity the LP would like to withdraw
* @param _rj The new value of _rj
* @notice this function should be gated to only allwed pools
*/
function updateLPTokenWithdrawal(uint256 _tokenId, packedFloat _wj, packedFloat _rj) external;
/**
* @dev gets current token id which means the latest token id to be minted
* @return the current token id
*/
function currentTokenId() external view returns (uint256);
/**
* @dev add a pool to the allow list
* @param pool the address of the pool to be added
* @notice Only the factory should be able to add pools to the allow list
*/
function addPoolToAllowList(address pool) external;
/**
* @dev tells is a pool is allowed
* @param pool the address of the pool to be added
* @return true if the pool is allowed
*/
function isPoolAllowed(address pool) external view returns (bool);
/**
* @dev propose the factory address
* @param factory the address of the proposed factory
* @notice Only the owner should be able to propose a factory
*/
function proposeFactoryAddress(address factory) external;
/**
* @dev gets the factory address
* @return the address of the factory
*/
function factoryAddressProposed() external view returns (address);
/**
* @dev confirm the factory address
* @notice Only the proposed factory should be able to confirm the factory address
*/
function confirmFactoryAddress() external;
/**
* @dev gets the factory address
* @return the address of the factory
*/
function factoryAddress() external view returns (address);
}
PoolBase.sol 423 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {Ownable2Step, Ownable} from "../../../lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import {Pausable} from "../../../lib/openzeppelin-contracts/contracts/utils/Pausable.sol";
import {IERC20Metadata} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeCast} from "../../../lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol";
import {IPool} from "./IPool.sol";
import "../../common/IErrors.sol";
import {CalculatorBase, packedFloat} from "./CalculatorBase.sol";
import {FeeInfo, TBCType} from "../../common/TBC.sol";
import {MathLibs} from "../mathLibs/MathLibs.sol";
import {ILPToken} from "../../common/ILPToken.sol";
/**
* @title Pool Base
* @dev This contract implements the core of the Pool interface and is meant to be an abstract base for all the pools.
* Any pool implementation must inherits this contract and implement all the functions from CalculatorBase.
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
abstract contract PoolBase is IPool, CalculatorBase, Ownable2Step, Pausable {
using SafeERC20 for IERC20;
using MathLibs for int256;
using MathLibs for packedFloat;
using SafeCast for uint256;
using SafeCast for int256;
int256 constant POOL_NATIVE_DECIMALS_NEGATIVE = 0 - int(POOL_NATIVE_DECIMALS);
packedFloat constant ACTIVE_LIQUIDITY_MINIMUM =
packedFloat.wrap(57620416476552669756370498094228058638175904844907685462261821436554916134912); // .01 in packedFloat
address public immutable xToken;
address public immutable yToken;
address public immutable lpToken;
uint256 public immutable inactiveLpId;
uint256 public immutable activeLpId;
/**
* @dev difference in decimal precision between y token and x token
*/
uint256 immutable yDecimalDiff;
/**
* @dev balance of x token that has been swapped out of the Pool
*/
packedFloat public x;
/**
* @dev fee percentage for swaps for the LP
*/
uint16 lpFee;
/**
* @dev fee percentage for swaps for the protocol
*/
uint16 protocolFee;
/**
* @dev protocol-fee collector address
*/
address protocolFeeCollector;
/**
* @dev proposed protocol-fee collector address
*/
address proposedProtocolFeeCollector;
/**
* @dev currently claimable fee balance
*/
packedFloat _collectedLPFees;
/**
* @dev currently claimable protocol fee balance
*/
uint256 collectedProtocolFees;
/**
* @dev total liquidity share
*/
packedFloat _w;
modifier onlyProtocolFeeCollector() {
if (_msgSender() != protocolFeeCollector) revert NotProtocolFeeCollector();
_;
}
modifier onlyProposedProtocolFeeCollector() {
if (_msgSender() != proposedProtocolFeeCollector) revert NotProposedProtocolFeeCollector();
_;
}
modifier checkExpiration(uint _expires) {
if (_expires < block.timestamp) revert TransactionExpired();
_;
}
/**
* @dev constructor
* @param _xToken address of the X token (x axis)
* @param _yToken address of the Y token (y axis)
* @param fees fee information
*/
constructor(address _xToken, address _yToken, address _lpToken, uint _inactiveLpId, FeeInfo memory fees) Ownable(_msgSender()) {
_validateInput(_xToken, _yToken, fees._protocolFeeCollector);
// slither-disable-start missing-zero-check // This is done in the _validateInput function
xToken = _xToken;
yToken = _yToken;
lpToken = _lpToken;
inactiveLpId = _inactiveLpId;
activeLpId = _inactiveLpId + 1;
protocolFeeCollector = _msgSender(); // temporary measure to avoid role failure
setLPFee(fees._lpFee);
setProtocolFee(fees._protocolFee);
protocolFeeCollector = fees._protocolFeeCollector;
// slither-disable-end missing-zero-check
yDecimalDiff = POOL_NATIVE_DECIMALS - IERC20Metadata(_yToken).decimals();
/// implementation contract must transfer ownership and emit a PoolDeployed event
}
/**
* @dev This is the main function of the pool to swap.
* @param _tokenIn the address of the token being given to the pool in exchange for another token
* @param _amountIn the amount of the ERC20 _tokenIn to exchange into the Pool
* @param _minOut the amount of the other token in the pair minimum to be received for the
* _amountIn of _tokenIn.
* @param _recipient address to receive tokens out
* @param _expires timestamp at which the swap transaction will expire
* @return amountOut the actual amount of the token coming out of the Pool as result of the swap
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
*/
function swap(
address _tokenIn,
uint256 _amountIn,
uint256 _minOut,
address _recipient,
uint256 _expires
) external whenNotPaused checkExpiration(_expires) returns (uint256 amountOut, uint256 lpFeeAmount, uint256 protocolFeeAmount) {
bool sellingX = _tokenIn == xToken;
//slither-disable-start reentrancy-benign // the recipient of the transfer is this contract
IERC20(sellingX ? xToken : yToken).safeTransferFrom(_msgSender(), address(this), _amountIn);
if (_minOut == 0) revert ZeroValueNotAllowed();
(amountOut, lpFeeAmount, protocolFeeAmount) = simSwap(_tokenIn, _amountIn);
_checkSlippage(amountOut, _minOut);
x = sellingX
? x.sub((_amountIn.toInt256()).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE))
: x.add(int(amountOut).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE));
// slither-disable-end reentrancy-benign
// slither-disable-start reentrancy-events // the recipient of the initial transfer is this contract
_updateParameters();
_collectedLPFees = _collectedLPFees.add(
int(lpFeeAmount).toPackedFloat(int(yDecimalDiff) - int(POOL_NATIVE_DECIMALS)).div(_w.sub(_wInactive()))
);
collectedProtocolFees += protocolFeeAmount;
emit FeesGenerated(lpFeeAmount, protocolFeeAmount);
emit Swap(_tokenIn, _amountIn, amountOut, _minOut, _recipient);
_emitCurveState();
// slither-disable-end reentrancy-events
IERC20(sellingX ? yToken : xToken).safeTransfer(_recipient == address(0) ? _msgSender() : _recipient, amountOut);
}
/**
* @dev This is a simulation of the swap function. Useful to get marginal prices
* @param _tokenIn the address of the token being sold
* @param _amountIn the amount of the ERC20 _tokenIn to sell to the Pool
* @return amountOut the amount of the token coming out of the Pool as result of the swap (main returned value)
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
*/
function simSwap(
address _tokenIn,
uint256 _amountIn
) public view returns (uint256 amountOut, uint256 lpFeeAmount, uint256 protocolFeeAmount) {
bool sellingX = _tokenIn == xToken;
if (!sellingX && _tokenIn != yToken) revert InvalidToken();
uint minAmountIn = 1;
if (lpFee > 0 && !sellingX) ++minAmountIn;
if (protocolFee > 0 && !sellingX) ++minAmountIn;
if (_amountIn < minAmountIn) revert ZeroValueNotAllowed();
if (!sellingX) {
lpFeeAmount = _determineFeeAmountSell(_amountIn, lpFee);
protocolFeeAmount = _determineFeeAmountSell(_amountIn, protocolFee);
_amountIn -= (lpFeeAmount + protocolFeeAmount); // fees are always coming out from the pool
_amountIn = _normalizeTokenDecimals(true, _amountIn);
}
packedFloat rawAmountOut = sellingX
? _calculateAmountOfYReceivedSellingX((_amountIn).toInt256().toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE))
: _calculateAmountOfXReceivedSellingY((_amountIn).toInt256().toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE));
amountOut = rawAmountOut.convertpackedFloatToWAD().toUint256();
if (sellingX) {
amountOut = _normalizeTokenDecimals(false, amountOut);
// slither-disable-start incorrect-equality
if (amountOut == 0) return (0, 0, 0);
// slither-disable-end incorrect-equality
lpFeeAmount = _determineFeeAmountSell(amountOut, lpFee);
protocolFeeAmount = _determineFeeAmountSell(amountOut, protocolFee);
amountOut -= (lpFeeAmount + protocolFeeAmount);
}
}
/**
* @dev This is a simulation of the swap function from the perspective of purchasing a specific amount. Useful to get marginal price.
* @param _tokenout the address of the token being bought
* @param _amountOut the amount of the ERC20 _tokenOut to buy from the Pool
* @return amountIn the amount necessary of the token coming into the Pool for the desired amountOut of the swap (main returned value)
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
* @notice lpFeeAmount and protocolFeeAmount are already factored in the amountIn. This is useful only to know how much of the amountIn
* will go towards fees.
*/
function simSwapReversed(
address _tokenout,
uint256 _amountOut
) public view returns (uint256 amountIn, uint256 lpFeeAmount, uint256 protocolFeeAmount) {
bool buyingX = _tokenout == xToken;
if (!buyingX && _tokenout != yToken) revert InvalidToken();
if (buyingX) {
packedFloat amountInRaw = _calculateAmountOfYRequiredBuyingX(int(_amountOut).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE));
uint256 uamountInRaw = uint(amountInRaw.convertpackedFloatToWAD());
uamountInRaw = _normalizeTokenDecimals(false, uamountInRaw); // reversed logic because swap is reversed
(protocolFeeAmount, lpFeeAmount) = _determineProtocolAndLPFeesBuy(uamountInRaw);
amountIn = uamountInRaw + lpFeeAmount + protocolFeeAmount;
} else {
(protocolFeeAmount, lpFeeAmount) = _determineProtocolAndLPFeesBuy(_amountOut);
_amountOut = _normalizeTokenDecimals(true, _amountOut + protocolFeeAmount + lpFeeAmount); // reversed logic because swap is reversed
packedFloat amountInRaw = _calculateAmountOfXRequiredBuyingY(int(_amountOut).toPackedFloat(POOL_NATIVE_DECIMALS_NEGATIVE));
amountIn = uint(amountInRaw.convertpackedFloatToWAD());
}
}
/**
* @dev This is the function to activate/deactivate trading.
* @param _enable pass True to enable or False to disable
*/
function enableSwaps(bool _enable) external virtual onlyOwner {
if (_enable) _unpause();
else _pause();
}
/**
* @dev This is the function to update the LP fees per trading.
* @param _fee percentage of the transaction that will get collected as fees (in percentage basis points:
* 1500 -> 15.00%; 500 -> 5.00%; 1 -> 0.01%)
*/
function setLPFee(uint16 _fee) public onlyOwner {
if (_fee > MAX_LP_FEE) revert LPFeeAboveMax(_fee, MAX_LP_FEE);
lpFee = _fee;
emit FeeSet(FeeCollectionType.LP, _fee);
}
/**
* @dev This is the function to update the protocol fees per trading.
* @param _protocolFee percentage of the transaction that will get collected as fees (in percentage basis points:
* 10000 -> 100.00%; 500 -> 5.00%; 1 -> 0.01%)
*/
function setProtocolFee(uint16 _protocolFee) public onlyProtocolFeeCollector {
if (_protocolFee > MAX_PROTOCOL_FEE) revert ProtocolFeeAboveMax({proposedFee: _protocolFee, maxFee: MAX_PROTOCOL_FEE});
protocolFee = _protocolFee;
emit FeeSet(FeeCollectionType.PROTOCOL, _protocolFee);
}
/**
* @dev This function collects the protocol fees from the Pool.
*/
function collectProtocolFees(address _recipient) external onlyProtocolFeeCollector {
uint256 collectedAmount = collectedProtocolFees;
delete collectedProtocolFees;
emit FeesCollected(FeeCollectionType.PROTOCOL, _msgSender(), collectedAmount);
IERC20(yToken).safeTransfer(_recipient, collectedAmount);
}
/**
* @dev function to propose a new protocol fee collector
* @param _protocolFeeCollector the new fee collector
* @notice that only the current fee collector address can call this function
*/
function proposeProtocolFeeCollector(address _protocolFeeCollector) external onlyProtocolFeeCollector {
// slither-disable-start missing-zero-check // unnecessary
proposedProtocolFeeCollector = _protocolFeeCollector;
// slither-disable-end missing-zero-check
emit ProtocolFeeCollectorProposed(_protocolFeeCollector);
}
/**
* @dev function to confirm a new protocol fee collector
* @notice that only the already proposed fee collector can call this function
*/
function confirmProtocolFeeCollector() external onlyProposedProtocolFeeCollector {
delete proposedProtocolFeeCollector;
protocolFeeCollector = _msgSender();
emit ProtocolFeeCollectorConfirmed(_msgSender());
}
/**
* @dev This is the function to retrieve the current spot price of the x token.
* @return sPrice the price in YToken Decimals
*/
function spotPrice() public view returns (uint256 sPrice) {
packedFloat sPriceRaw = _spotPrice();
sPrice = uint(sPriceRaw.convertpackedFloatToWAD());
if (yDecimalDiff != 0) {
sPrice = _normalizeTokenDecimals(false, sPrice);
}
}
function getFeeInfo() external view returns (uint16, uint16, address, address, uint256) {
return (lpFee, protocolFee, protocolFeeCollector, proposedProtocolFeeCollector, collectedProtocolFees);
}
/**
* @dev A helper function to validate most of constructor's inputs.
* @param _xToken address of the X token (x axis)
* @param _yToken address of the Y token (y axis)
*/
function _validateInput(address _xToken, address _yToken, address _protocolFeeCollector) internal view {
if (_xToken == address(0) || _yToken == address(0) || _protocolFeeCollector == address(0)) revert ZeroAddress();
if (_xToken == _yToken) revert XandYTokensAreTheSame();
if (IERC20Metadata(_xToken).decimals() != 18) revert XTokenDecimalsIsNot18();
if (IERC20Metadata(_yToken).decimals() > 18) revert YTokenDecimalsGT18();
}
/**
* @dev This function normalizes an input amount to or from native decimal value.
* @param isInput if true, it assumes that the tokens are being received into the pool and therefore it
* multiplies/adds the zeros necessary to make it a native-decimal value. It divides otherwise.
* @param rawAmount amount to normalize
* @return normalizedAmount the normalized value
*/
function _normalizeTokenDecimals(bool isInput, uint rawAmount) internal view returns (uint normalizedAmount) {
if (yDecimalDiff == 0) normalizedAmount = rawAmount;
else normalizedAmount = isInput ? rawAmount * (10 ** yDecimalDiff) : rawAmount / (10 ** yDecimalDiff);
}
/**
* @dev This function determines the amount of fees when doing a simSwap (Sell simulation).
* @param amountOfY the amount to calculate the fees from
* @return feeAmount the amount of fees
*
*/
function _determineFeeAmountSell(uint256 amountOfY, uint16 _fee) private pure returns (uint256 feeAmount) {
if (_fee > 0) feeAmount = (amountOfY * _fee) / PERCENTAGE_DENOM + 1;
}
/**
* @dev This function determines the adjusted amount of y tokens needed accounting for fees when doing a simSwapReverse (buy simulation).
* Equation:
*
* yAmount - yAmount * fee = realYAmount, in other words: yAmount * (1 - fee) = realYAmount
* Thefore,
* adjustedYAmount = yAmount / (1 - fee),
* and
* yAmount * fee = adjustedYAmount - yAmount,
* which gives us
* yAmount * fee = yAmount / (1 - fee) - yAmount = (yAmount * fee) / (1 - fee)
*
* @param originalAmountOfY the amount to adjust with fees
* @return yFees the amount necessary to add to yAmount to get the expected yAmount after fees
*/
function _determineFeeAmountBuy(uint256 originalAmountOfY, uint16 _fee) private pure returns (uint256 yFees) {
yFees = (originalAmountOfY * _fee) / (PERCENTAGE_DENOM - _fee) + 1; // we add 1 to round up
}
/**
* @dev this functions returns the value of both protocol and LP fees that need to be added to the original amount of yTokens in order
* for it to have the desired effect in simSwapReversed (buy simulation).
* @param originalAmountOfY the net amount of yTokens expressed in its native decimals that are desired to be used in a buy operation.
* @return amountProtocolFee the amount of yTokens that will be destined towards protocol fees expressed in WADs of yTokens.
* This value should be added to originalAmountOfY for it to have the desired effect.
* @return amountLPFee the amount of yTokens that will be destined towards LP fees expressed in WADs of yTokens. This
* value should be added to originalAmountOfY for it to have the desired effect.
*/
function _determineProtocolAndLPFeesBuy(
uint256 originalAmountOfY
) internal view returns (uint256 amountProtocolFee, uint256 amountLPFee) {
if (lpFee + protocolFee == 0) return (0, 0);
else {
uint totalAmountFees = _determineFeeAmountBuy(originalAmountOfY, lpFee + protocolFee);
if (lpFee == 0) (amountProtocolFee, amountLPFee) = (totalAmountFees, 0);
else if (protocolFee == 0) (amountProtocolFee, amountLPFee) = (0, totalAmountFees);
else {
++totalAmountFees; // we add 1 to the total amount of fees to account for rounding down edge cases where 1 of the 2 results could be 0
if (lpFee > protocolFee) {
amountLPFee = (totalAmountFees * lpFee) / (protocolFee + lpFee);
amountProtocolFee = totalAmountFees - amountLPFee;
} else {
amountProtocolFee = (totalAmountFees * protocolFee) / (protocolFee + lpFee);
amountLPFee = totalAmountFees - amountProtocolFee;
}
}
}
}
/**
* @dev This function checks to verify the amount out will be greater than or equal to the minimum expected amount out.
* @param _amountOut the actual amount being provided out by the swap
* @param _minOut the expected amount out to compare against
*/
function _checkSlippage(uint256 _amountOut, uint256 _minOut) internal pure {
if (_amountOut < _minOut) revert MaxSlippageReached();
}
/**
* @dev returns the current total liquidity in the Pool
* @return w
*/
function w() external view returns (uint256) {
return uint(_w.convertpackedFloatToWAD());
}
function _wInactive() internal view returns (packedFloat wI) {
(wI, ) = ILPToken(lpToken).getLPToken(inactiveLpId);
}
}
ALTBCEquations.sol 224 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {MathLibs, packedFloat} from "liquidity-base/src/amm/mathLibs/MathLibs.sol";
import {ALTBCDef} from "src/amm/ALTBC.sol";
import {NegativeValue} from "liquidity-base/src/common/IErrors.sol";
/**
* @title Equations used by the ALTBC AMM
* @author @oscarsernarosero @mpetersoCode55 @cirsteve @palmerg4
*/
library ALTBCEquations {
using MathLibs for int256;
using MathLibs for packedFloat;
packedFloat constant FLOAT_2 = packedFloat.wrap(0x7f6c00000000000000000000000000000f0bdc21abb48db201e86d4000000000); // encoded previously to save gas
packedFloat constant FLOAT_NEG_1 = packedFloat.wrap(0x7f6d00000000000000000000000000000785ee10d5da46d900f436a000000000); // encoded previously to save gas
packedFloat constant FLOAT_0 = packedFloat.wrap(0); // encoded previously to save gas
packedFloat constant FLOAT_1 = packedFloat.wrap(0x7f6c00000000000000000000000000000785ee10d5da46d900f436a000000000); // encoded previously to save gas
packedFloat constant FLOAT_WAD = packedFloat.wrap(57507338264406853159277167054180511853162945875507645848942038639672188469248);
/**
* @dev This function calculates B(n) and stores it in the tbc definition as b.
* @notice The result will be a packedFloat
* @notice Bn is equal to V / (Xn + C) in the spec
* @param altbc the tbc definition
* @param Xn the X value at n
*/
function calculateBn(ALTBCDef storage altbc, packedFloat Xn) internal {
altbc.b = altbc.V.div(Xn.add(altbc.C));
}
/**
* @dev This function calculates f(x) at n.
* @notice The result for f(x) will be a packedFloat.
* @notice This equation is used to calculate the spot price of the x token and is equal to (bn * x) + cn
* @param altbc the tbc definition
* @param x value for x at n
* @return result the calculated f(x), this value will be a packedFloat
*/
function calculatefx(ALTBCDef storage altbc, packedFloat x) internal view returns (packedFloat result) {
result = altbc.b.mul(x).add(altbc.c);
}
/**
* @dev This function calculates D at n.
* @notice The result for Dn will be a packedFloat
* @notice This equation is used to calculate the area under the curve at n and is equal to (1/2)(bn*x^2) + cn*x
* @param altbc the tbc definition
* @param x value for x at n
* @return result
*/
function calculateDn(ALTBCDef storage altbc, packedFloat x) internal view returns (packedFloat result) {
result = ((altbc.b.mul((x).mul(x))).div(FLOAT_2)).add(altbc.c.mul(x));
}
/**
* @dev This function calculates h at n which is the total revenue per unit of liquidity at time n.
* @notice This method is implemented using packedFloats and the float128 library
* @notice This equation in the spec is equal to (Ln + Zn) / (Wn - wInactive) + phi
* @param L the x coordinate
* @param W the total amount of units of liquidity in circulation
* @param phi the total amount of units of liquidity in circulation
* @return result the calculated h
*/
function calculateH(
ALTBCDef storage altbc,
packedFloat L,
packedFloat W,
packedFloat wInactive,
packedFloat phi
) internal view returns (packedFloat result) {
result = ((L.add(altbc.Zn)).div((W.sub(wInactive)))).add(phi);
}
/**
* @dev This function calculates the value of Xn+1.
* @notice This method is implemented using packedFloats and the float128 library
* @notice This equation in the spec is equal to 2Dn / (c + sqrt(c^2 + 2bDn))
* @param altbc the tbc definition
* @param Dn the area under the curve.
* @return newX the calculated Xn+1
*/
function calculateXofNPlus1(ALTBCDef storage altbc, packedFloat Dn) internal view returns (packedFloat newX) {
newX = FLOAT_2.mul(Dn).div(altbc.c.add((altbc.c.mul(altbc.c).add(FLOAT_2.mul(altbc.b).mul(Dn))).sqrt()));
}
/**
* @dev This function calculates the parameter c and stores it in the tbc definition.
* @param altbc the tbc definition.
* @param Xn the x coordinate
* @param oldBn the previous state of b
*/
function calculateC(ALTBCDef storage altbc, packedFloat Xn, packedFloat oldBn) internal {
packedFloat firstTerm;
if (altbc.b.gt(oldBn)) {
firstTerm = (altbc.b.sub(oldBn)).div(FLOAT_2);
firstTerm = firstTerm.mul(Xn);
if (firstTerm.gt(altbc.c)) revert NegativeValue();
else altbc.c = altbc.c.sub(firstTerm);
} else {
firstTerm = (oldBn.sub(altbc.b)).div(FLOAT_2);
firstTerm = firstTerm.mul(Xn);
altbc.c = altbc.c.add(firstTerm);
}
}
/**
* @dev This function calculates the last revenue claim to be stored in the associated LPToken variable rj. The result will be a WAD value.
* @notice The result for last revenue claim will be a Float.
* @param hn The revenue parameter. Expected to be a Float.
* @param wj The share of the pool's liquidity the associated LPToken represents. Expected to be a Float.
* @param r_hat The current last revenue claim value of the associated LPToken. Expected to be a Float.
* @param w_hat The current liquidity amount of the associated LPToken. Expected to be a Float.
*/
function calculateLastRevenueClaim(
packedFloat hn,
packedFloat wj,
packedFloat r_hat,
packedFloat w_hat
) internal pure returns (packedFloat) {
return hn.mul(wj).add(r_hat.mul(w_hat)).div(w_hat.add(wj));
}
/**
* @dev This function calculates the parameter L.
* @param altbc the tbc definition.
* @param Xn the x coordinate
* @return result the calculate L parameter.
*/
function calculateL(ALTBCDef storage altbc, packedFloat Xn) internal view returns (packedFloat result) {
packedFloat firstTerm = (altbc.xMin.add(altbc.C)).divL(Xn.add(altbc.C));
packedFloat ln = firstTerm.ln();
packedFloat secondTerm = ((altbc.b.mul(Xn)).add((altbc.c.mul(FLOAT_2)))).add(altbc.V.mul(ln));
result = secondTerm.mul(altbc.xMin.div(FLOAT_2));
}
/**
* @dev This function calculates the parameter Z, which is a balancing quantity used to ensure fair LP accounting.
* @param altbc the tbc definition.
* @param Ln the liquidity parameter.
* @param Wn the total amount of units of liquidity in circulation.
* @param WIn the total amount of units of liquidity in circulation.
* @param q the liquidity units to receive in exchange for A and B
* @param withdrawal the boolean value for withdrawal
*/
function calculateZ(ALTBCDef storage altbc, packedFloat Ln, packedFloat Wn, packedFloat WIn, packedFloat q, bool withdrawal) internal {
if (withdrawal) {
q = q.mul(FLOAT_NEG_1);
}
packedFloat activeLiquidity = Wn.sub(WIn);
altbc.Zn = activeLiquidity.eq(FLOAT_0)
? FLOAT_0
: altbc.Zn.add(((WIn.div((activeLiquidity))).mul(q)).mul((Ln.add(altbc.Zn)))).add((q.mul(altbc.Zn)));
}
/**
* @dev This function calculates q.
* @param altbc the tbc definition.
* @param Xn the x coordinate
* @param _A The amount of incoming X Token.
* @param _B The amount of incoming collateral.
* @param L the liquidity parameter.
* @param Dn The current area under the curve.
* @return A the actual amount to take for token x
* @return B the actual amount to take for token y
* @return q the liquidity units to receive in exchange for A and B
*/
function calculateQ(
ALTBCDef storage altbc,
packedFloat Xn,
packedFloat _A,
packedFloat _B,
packedFloat L,
packedFloat Dn
) internal view returns (packedFloat A, packedFloat B, packedFloat q) {
packedFloat deltaX = altbc.xMax.sub(Xn);
packedFloat deltaD = Dn.sub(L);
if (deltaD.lt(FLOAT_0)) deltaD = FLOAT_0;
packedFloat bParam = _B.mul(deltaX);
packedFloat aParam = _A.mul(deltaD);
if ((bParam).le(aParam)) {
B = _B;
if (deltaD.lt(FLOAT_WAD)) {
A = _A;
q = A.div(deltaX);
} else {
A = deltaX.div(deltaD).mul(_B);
q = B.div(deltaD);
}
} else {
A = _A;
B = deltaD.div(deltaX).mul(_A);
q = A.div(deltaX);
}
}
/**
* @dev This function calculates the revenue available for a given LPToken.
* @param wj The share of the pool's liquidity the associated LPToken represents.
* @param hn The revenue parameter.
* @param rj The last revenue claim for the associated LPToken.
* @return result The calculated revenue available for the LPToken.
*/
function calculateRevenueAvailable(packedFloat wj, packedFloat hn, packedFloat rj) internal pure returns (packedFloat result) {
return wj.mul(hn.sub(rj));
}
/**
* @dev This function updates related tbc variables when a liquidity deposit or withdrawal is made
* @param altbc the tbc definition.
* @param Xn the x coordinate.
* @param multiplier The value for multiplier for pool state
* @return x The updated x value.
*/
function _liquidityUpdateHelper(ALTBCDef storage altbc, packedFloat Xn, packedFloat multiplier) internal returns (packedFloat x) {
x = Xn.mul(multiplier);
altbc.b = altbc.b.div(multiplier);
altbc.xMax = altbc.xMax.mul(multiplier);
altbc.xMin = altbc.xMin.mul(multiplier);
altbc.C = altbc.C.mul(multiplier);
}
}
Initializable.sol 185 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Initializable mixin for the upgradeable contracts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
abstract contract Initializable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The contract is already initialized.
error InvalidInitialization();
/// @dev The contract is not initializing.
error NotInitializing();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Triggered when the contract has been initialized.
event Initialized(uint64 version);
/// @dev `keccak256(bytes("Initialized(uint64)"))`.
bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The default initializable slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
///
/// Bits Layout:
/// - [0] `initializing`
/// - [1..64] `initializedVersion`
bytes32 private constant _INITIALIZABLE_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return a custom storage slot if required.
function _initializableSlot() internal pure virtual returns (bytes32) {
return _INITIALIZABLE_SLOT;
}
/// @dev Guards an initializer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `initializer`.
///
/// This is similar to `reinitializer(1)`, except that in the context of a constructor,
/// an `initializer` guarded function can be invoked multiple times.
/// This can be useful during testing and is not expected to be used in production.
///
/// Emits an {Initialized} event.
modifier initializer() virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
// Set `initializing` to 1, `initializedVersion` to 1.
sstore(s, 3)
// If `!(initializing == 0 && initializedVersion == 0)`.
if i {
// If `!(address(this).code.length == 0 && initializedVersion == 1)`.
if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
}
}
_;
/// @solidity memory-safe-assembly
assembly {
if s {
// Set `initializing` to 0, `initializedVersion` to 1.
sstore(s, 2)
// Emit the {Initialized} event.
mstore(0x20, 1)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Guards an reinitialzer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `reinitializer`.
///
/// Emits an {Initialized} event.
modifier reinitializer(uint64 version) virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
version := and(version, 0xffffffffffffffff) // Clean upper bits.
let i := sload(s)
// If `initializing == 1 || initializedVersion >= version`.
if iszero(lt(and(i, 1), lt(shr(1, i), version))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
// Set `initializing` to 1, `initializedVersion` to `version`.
sstore(s, or(1, shl(1, version)))
}
_;
/// @solidity memory-safe-assembly
assembly {
// Set `initializing` to 0, `initializedVersion` to `version`.
sstore(s, shl(1, version))
// Emit the {Initialized} event.
mstore(0x20, version)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
/// @dev Guards a function such that it can only be called in the scope
/// of a function guarded with `initializer` or `reinitializer`.
modifier onlyInitializing() virtual {
_checkInitializing();
_;
}
/// @dev Reverts if the contract is not initializing.
function _checkInitializing() internal view virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
if iszero(and(1, sload(s))) {
mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
///
/// Calling this in the constructor will prevent the contract from being initialized
/// or reinitialized. It is recommended to use this to lock implementation contracts
/// that are designed to be called through proxies.
///
/// Emits an {Initialized} event the first time it is successfully called.
function _disableInitializers() internal virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
if and(i, 1) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
let uint64max := shr(192, s) // Computed to save bytecode.
if iszero(eq(shr(1, i), uint64max)) {
// Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
sstore(s, shl(1, uint64max))
// Emit the {Initialized} event.
mstore(0x20, uint64max)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Returns the highest version that has been initialized.
function _getInitializedVersion() internal view virtual returns (uint64 version) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
version := shr(1, sload(s))
}
}
/// @dev Returns whether the contract is currently initializing.
function _isInitializing() internal view virtual returns (bool result) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
result := and(1, sload(s))
}
}
}
SafeCast.sol 1162 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}
MathLibs.sol 192 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import "./lib/MathUtils.sol";
import {Ln} from "../../../lib/float128/src/Ln.sol";
import {Float128} from "../../../lib/float128/src/Float128.sol";
import {packedFloat} from "../../../lib/float128/src/Types.sol";
/**
* @title Abstraction Layer between Equations and the underlying Math libraries
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
* @dev Wrapper functions to act as an abstraction layer between Equations and the Math library we're using.
* @notice current implementation is using the float128 library for floating-point math operations.
*/
library MathLibs {
using MathUtils for uint256;
using Ln for packedFloat;
using Float128 for packedFloat;
using Float128 for int;
uint256 constant WAD = 1e18;
/**
* @dev adds 2 signed floating point numbers
* @param a the first addend
* @param b the second addend
* @return r the result of a + b
* @notice this version of the function uses only the packedFloat type
*/
function add(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = a.add(b);
}
/**
* @dev gets the difference between 2 signed floating point numbers
* @param a the minuend
* @param b the subtrahend
* @return r the result of a - b
* @notice this version of the function uses only the packedFloat type
*/
function sub(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = a.sub(b);
}
/**
* @dev gets the multiplication of 2 signed floating point numbers
* @param a the first factor
* @param b the second factor
* @return r the result of a * b
* @notice this version of the function uses only the packedFloat type
*/
function mul(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = a.mul(b);
}
/**
* @dev gets the division of 2 signed floating point numbers
* @param a the numerator
* @param b the denominator
* @return r the result of a / b
* @notice this version of the function uses only the packedFloat type
*/
function div(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = a.div(b);
}
function divL(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = a.divL(b);
}
/**
* @dev gets the square root of a signed floating point
* @notice only positive numbers can get its square root calculated through this function
* @param a the numerator to get the square root of
* @return r the result of √a
* @notice this version of the function uses only the packedFloat type
*/
function sqrt(packedFloat a) internal pure returns (packedFloat r) {
r = a.sqrt();
}
/**
* @dev performs a greater than comparison
* @param a the first term
* @param b the second term
* @return r retVal the result of a > b
* @notice this version of the function uses only the packedFloat type
*/
function gt(packedFloat a, packedFloat b) internal pure returns (bool r) {
r = a.gt(b);
}
/**
* @dev performs a less than comparison
* @param a the first term
* @param b the second term
* @return r retVal the result of a < b
* @notice this version of the function uses only the packedFloat type
*/
function lt(packedFloat a, packedFloat b) internal pure returns (bool r) {
r = a.lt(b);
}
/**
* @dev performs a less or equal to comparison
* @param a the first term
* @param b the second term
* @return r retVal the result of a < b
* @notice this version of the function uses only the packedFloat type
*/
function le(packedFloat a, packedFloat b) internal pure returns (bool r) {
r = a.le(b);
}
/**
* @dev performs an equality comparison
* @param a the first term
* @param b the second term
* @return r true if a is equal to b
* @notice this version of the function uses only the packedFloat type
*/
function eq(packedFloat a, packedFloat b) internal pure returns (bool r) {
r = a.eq(b);
}
/**
* @dev encodes a pair of signed integer values describing a floating point number into a packedFloat
* Examples: 1234.567 can be expressed as: 123456 x 10**(-3), or 1234560 x 10**(-4), or 12345600 x 10**(-5), etc.
* @notice the mantissa can hold a maximum of 38 digits. Any number with more digits will lose precision.
* @param mantissa the integer that holds the mantissa digits (38 digits max)
* @param exponent the exponent of the floating point number (between -16384 and +16383)
* @return float the encoded number. This value will ocupy a single 256-bit word and will hold the normalized
* version of the floating-point number (shifts the exponent enough times to have exactly 38 significant digits)
*/
function toPackedFloat(int mantissa, int exponent) internal pure returns (packedFloat float) {
float = mantissa.toPackedFloat(exponent);
}
/**
* @dev calculates the natural logarithm of a positive number
* @param x the number to get the natural logarithm from
* @return float the result of the natural logarithm of x as a float number
*/
function ln(packedFloat x) internal pure returns (packedFloat float) {
float = x.ln();
}
/**
* @dev decodes a packedFloat into its mantissa and its exponent
* @param float the floating-point number expressed as a packedFloat to decode
* @return mantissa the 38 mantissa digits of the floating-point number
* @return exponent the exponent of the floating-point number
*/
function decode(packedFloat float) internal pure returns (int mantissa, int exponent) {
(mantissa, exponent) = float.decode();
}
function convertpackedFloatToWAD(packedFloat value) internal pure returns (int256 result) {
return convertpackedFloatToSpecificDecimals(value, 18);
}
/**
* @dev converts a packedFloat to a specific number of decimals
* @param value the packedFloat to convert
* @param decimals the number of decimals to convert to
* @return result the resulting number with the specified number of decimals
*/
function convertpackedFloatToSpecificDecimals(packedFloat value, int decimals) internal pure returns (int256 result) {
(int256 mantissa, int256 exponent) = value.decode();
exponent *= -1;
if (mantissa == 0) {
result = 0;
} else {
if (exponent > decimals) {
uint256 diff = uint(exponent - decimals);
result = mantissa / int(10 ** diff);
} else {
uint256 diff = uint(decimals - exponent);
result = mantissa * int(10 ** diff);
}
}
}
/**
* @dev converts a packedFloat to a double WAD number
* @param value the packedFloat to convert
* @return result the resulting double WAD number
*/
function convertpackedFloatToDoubleWAD(packedFloat value) internal pure returns (int256 result) {
return convertpackedFloatToSpecificDecimals(value, 36);
}
}
Ownable2Step.sol 67 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
IFactory.sol 94 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IFactoryEvents} from "../common/IEvents.sol";
/**
* @title Pool Factory Interface
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
* @dev function signatures of a Pool Factory
*/
interface IFactory is IFactoryEvents {
/**
* @dev version of the pool factory
* @return the version of the pool factory
*/
function VERSION() external view returns (string memory);
/**
* @dev sets the y token allow list
* @param _address of the allow list contract
*/
function setYTokenAllowList(address _address) external;
/**
* @dev gets the y token allow list
* @return _address of the current allow list contract
*/
function getYTokenAllowList() external returns (address _address);
/**
* @dev sets the deployer allow list
* @param _address of the allow list contract
*/
function setDeployerAllowList(address _address) external;
/**
* @dev gets the deployer allow list
* @return _address of the current allow list contract
*/
function getDeployerAllowList() external returns (address _address);
/**
* @dev fee percentage for swaps for the protocol
* @return the percentage for swaps in basis points that will go towards the protocol
*/
function protocolFee() external returns (uint16);
/**
* @dev protocol-fee collector address
* @return the current protocolFeeCollector address
*/
function protocolFeeCollector() external returns (address);
/**
* @dev proposed protocol-fee collector address
* @return the current proposedProtocolFeeCollector address
*/
function proposedProtocolFeeCollector() external returns (address);
/**
* @dev This is the function to update the protocol fees per trading.
* @param _protocolFee percentage of the transaction that will get collected as fees (in percentage basis points:
* 10000 -> 100.00%; 500 -> 5.00%; 1 -> 0.01%)
*/
function setProtocolFee(uint16 _protocolFee) external;
/**
* @dev function to propose a new protocol fee collector
* @param _protocolFeeCollector the new fee collector
* @notice that only the current fee collector address can call this function
*/
function proposeProtocolFeeCollector(address _protocolFeeCollector) external;
/**
* @dev function to confirm a new protocol fee collector
* @notice that only the already proposed fee collector can call this function
*/
function confirmProtocolFeeCollector() external;
/**
* @dev confirm the factory address
* @notice Used for the LPToken facotry role. Only the owner is allowed to accept the factory role
*/
function acceptLPTokenRole() external;
/**
* @dev set the LPToken address
* @param LPTokenAddress the address of the LPToken contract
* @notice Only the owner can set the LPToken address
* @notice This function is used to set the LPToken address for the factory
*/
function setLPTokenAddress(address LPTokenAddress) external;
}
IAllowList.sol 31 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IAllowListEvents} from "../common/IEvents.sol";
/**
* @title Allowed List Interface
* @dev holds the signature of an allowed list
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
interface IAllowList is IAllowListEvents {
/**
* @dev Tells if an address is allowed
* @param _address address of the yToken or deployer.
* @return true if _address is allowed in a pool.
*/
function isAllowed(address _address) external view returns (bool);
/**
* @dev Adds an address to the allowed list
* @param _address address to be allowed
*/
function addToAllowList(address _address) external;
/**
* @dev Removes an address from the list
* @param _address address to remove from list
*/
function removeFromAllowList(address _address) external;
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
IERC721Enumerable.sol 29 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}
Pausable.sol 119 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
IERC20Metadata.sol 26 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed 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.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (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.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens 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.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
SafeERC20.sol 198 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
IPool.sol 159 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IPoolEvents} from "../../common/IEvents.sol";
/**
* @title IPool Interface
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
* @dev function signatures for all Pools
*/
interface IPool is IPoolEvents {
/**
* @dev This is the main function of the pool to swap.
* @param _tokenIn the address of the token being given to the pool in exchange for another token
* @param _amountIn the amount of the ERC20 _tokenIn to exchange into the Pool
* @param _minOut the amount of the other token in the pair minimum to be received for the_amountIn of _tokenIn.
* @param _recipient address to receive tokens out
* @param _expires timestamp at which the swap transaction will expire
* @return amountOut the actual amount of the token coming out of the Pool as result of the swap
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
*/
function swap(
address _tokenIn,
uint256 _amountIn,
uint256 _minOut,
address _recipient,
uint256 _expires
) external returns (uint256 amountOut, uint256 lpFeeAmount, uint256 protocolFeeAmount);
/**
* @dev This is the function to retrieve the current spot price of the x token.
* @return sPrice the price in YToken Decimals
*/
function spotPrice() external returns (uint256 sPrice);
/**
* @dev This is a simulation of the swap function. Useful to get marginal price.
* @param _tokenIn the address of the token being sold
* @param _amountIn the amount of the ERC20 _tokenIn to sell to the Pool
* @return amountOut the amount of the token coming out of the Pool as result of the swap (main returned value)
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
*/
function simSwap(
address _tokenIn,
uint256 _amountIn
) external returns (uint256 amountOut, uint256 lpFeeAmount, uint256 protocolFeeAmount);
/**
* @dev This is a simulation of the swap function from the perspective of purchasing a specific amount. Useful to get marginal price.
* @param _tokenout the address of the token being bought
* @param _amountOut the amount of the ERC20 _tokenOut to buy from the Pool
* @return amountIn the amount necessary of the token coming into the Pool for the desired amountOut of the swap (main returned value)
* @return lpFeeAmount the amount of the Y token that's being dedicated to fees for the LP
* @return protocolFeeAmount the amount of the Y token that's being dedicated to fees for the protocol
* @notice lpFeeAmount and protocolFeeAmount are already factored in the amountIn. This is useful only to know how much of the amountIn
* will go towards fees.
*/
function simSwapReversed(
address _tokenout,
uint256 _amountOut
) external returns (uint256 amountIn, uint256 lpFeeAmount, uint256 protocolFeeAmount);
/**
* @dev A function to get the address of the x token of the pool.
* @return the address of the x token of the pool
* @notice this value is immutable
*/
function xToken() external view returns (address);
/**
* @dev A function to get the address of the Y token of the pool.
* @return the address of the Y token of the pool
* @notice this value is immutable
*/
function yToken() external view returns (address);
/**
* @dev This is the function to activate/deactivate trading.
* @param _enable pass True to enable or False to disable
*/
function enableSwaps(bool _enable) external;
/**
* @dev This is the function to update the LP fees per trading.
* @param _fee percentage of the transaction that will get collected as fees (in percentage basis points:
* 10000 -> 100.00%; 500 -> 5.00%; 1 -> 0.01%)
*/
function setLPFee(uint16 _fee) external;
/**
* @dev This is the function to update the protocol fees per trading.
* @param _protocolFee percentage of the transaction that will get collected as fees (in percentage basis points:
* 10000 -> 100.00%; 500 -> 5.00%; 1 -> 0.01%)
*/
function setProtocolFee(uint16 _protocolFee) external;
/**
* @dev function to propose a new protocol fee collector
* @param _protocolFeeCollector the new fee collector
* @notice that only the current fee collector address can call this function
*/
function proposeProtocolFeeCollector(address _protocolFeeCollector) external;
/**
* @dev function to confirm a new protocol fee collector
* @notice that only the already proposed fee collector can call this function
*/
function confirmProtocolFeeCollector() external;
/**
* @dev This function allows the owner of the lp token to pull accrued revenue from the Pool.
* @param tokenId the id of the LP token to withdraw revenue for
* @param Q the amount of revenue to withdraw
* @param recipient address to send the revenue to
* @return revenue the normalized amount of revenue actually withdrawn
*/
function withdrawRevenue(uint256 tokenId, uint256 Q, address recipient) external returns (uint256 revenue);
/**
* @dev This function collects the protocol fees from the Pool.
* @param _recipient address that receives the fees
*/
function collectProtocolFees(address _recipient) external;
/**
* @dev fee percentage for swaps for the LPs and for the protocol
* @return lpFee the percentage for swaps in basis points that will go towards the LPs
* @return protocolFee the percentage for swaps in basis points that will go towards the protocol
* @return protocolFeeCollector address of the account with the privilage of collecting the the protocol fees
* @return proposedProtocolFeeCollector the address proposed to be the new protocolFeeCollector
* @return collectedProtocolFees the available amount of protocol fees to be collected
*/
function getFeeInfo()
external
view
returns (
uint16 lpFee,
uint16 protocolFee,
address protocolFeeCollector,
address proposedProtocolFeeCollector,
uint256 collectedProtocolFees
);
/**
* @dev returns the current total liquidity in the Pool
* @return w
*/
function w() external returns (uint256);
/**
* @dev This is the function to get the revenue available for a liquidity position.
* @param tokenId The tokenId representing the liquidity position
* @return _revenueAvailable The amount of revenue available for the liquidity position
*/
function revenueAvailable(uint256 tokenId) external view returns (uint256);
}
CalculatorBase.sol 57 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import "../../common/IErrors.sol";
import {Constants} from "../../common/Constants.sol";
import {packedFloat} from "../mathLibs/MathLibs.sol";
/**
* @title Calculator Base Abstract Contract
* @dev This contract serves as the base for all the calculators.
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
abstract contract CalculatorBase is Constants {
/**
* @dev This is the function to retrieve the current spot price of the x token.
* @return sPrice the price in YToken Decimals
*/
function _spotPrice() internal view virtual returns (packedFloat sPrice);
/**
* @dev This function updates the state of the math values of the pool.
*/
function _updateParameters() internal virtual;
/**
* @dev This function calculates the amount of token X required for the user to purchase a specific amount of Token Y (buy y with x : out perspective).
* @param _amountOfY desired amount of token Y
*/
function _calculateAmountOfXRequiredBuyingY(packedFloat _amountOfY) internal view virtual returns (packedFloat amountOfX);
/**
* @dev This function calculates the amount of token Y required for the user to purchase a specific amount of Token X (buy x with y : out perspective).
* @param _amountOfX desired amount of token X
* @return amountOfY required amount of token Y
*/
function _calculateAmountOfYRequiredBuyingX(packedFloat _amountOfX) internal view virtual returns (packedFloat amountOfY);
/**
* @dev This function calculates the amount of token Y the user will receive when selling token X (sell x for y : in perspective).
* @param _amountOfX amount of token X to be sold
* @return amountOfY amount of token Y to be received
*/
function _calculateAmountOfYReceivedSellingX(packedFloat _amountOfX) internal view virtual returns (packedFloat amountOfY);
/**
* This function calculates the amount of token X the user will receive when selling token Y (sell y for x : in perspective).
* @param _amountOfY amount of token Y to be sold
* @return amountOfX amount of token X to be received
*/
function _calculateAmountOfXReceivedSellingY(packedFloat _amountOfY) internal view virtual returns (packedFloat amountOfX);
/**
* @dev this function emits an event with the state of the curve
*/
function _emitCurveState() internal virtual;
}
TBC.sol 21 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @title TBC Data Structures
* @dev All TBC definitions can be found here.
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
/// TBC Enum
enum TBCType {
ALTBC,
URQTBC
}
struct FeeInfo {
uint16 _lpFee;
uint16 _protocolFee;
address _protocolFeeCollector;
}
MathUtils.sol 45 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @title Utility function for equations
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
library MathUtils {
uint256 constant WAD = 1e18;
/**
* @dev This function converts a raw number to a WAD number
* @param value The number to be converted
* @return result resulting WAD number
*/
function convertToWAD(uint256 value) internal pure returns (uint256 result) {
result = value * WAD;
}
/**
* @dev This function converts a WAD number to a raw number
* @param value The number to be converted
* @return result resulting raw number
*/
function convertToRaw(uint256 value) internal pure returns (uint256 result) {
result = value / WAD;
}
/**
* @dev this function tells how many WADs a number needs to be divided by to get to 0
* @param x the number to be divided
* @return precisionSlashingFactor the number of WADs needed to be divided to get to 0
*/
function findWADsToSlashTo0(uint256 x) internal pure returns (uint256 precisionSlashingFactor) {
// this loop could be possibly run only 5 times since a 256-bit number can only shift 59 bits
// 5 times to cover the totality of the bits.
while (x > 0) {
// we shift enough bits to the right to emulate a division by WAD
x = x >> 59; // shifting 59 bits to the right is the same as dividing by 0.57e18
unchecked {
++precisionSlashingFactor;
}
}
}
}
Ln.sol 982 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {packedFloat} from "./Types.sol";
import {Float128} from "./Float128.sol";
/**
* @title Natural Logarithm Floating-Point Library
* @dev the library uses the type packedFloat whih is a uint under the hood
* @author Inspired by a Python proposal by @miguel-ot and @neel-thrackle, and refined/implemented in Solidity by @oscarsernarosero @PmpetersoCode55 @VoR0220
*/
library Ln {
using Float128 for packedFloat;
// These constants are used in an inline assembly block and must direct number constants
uint constant MANTISSA_MASK = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint constant MANTISSA_SIGN_MASK = 0x1000000000000000000000000000000000000000000000000000000000000;
uint constant MANTISSA_L_FLAG_MASK = 0x2000000000000000000000000000000000000000000000000000000000000;
uint constant EXPONENT_MASK = 0xfffc000000000000000000000000000000000000000000000000000000000000;
uint constant ZERO_OFFSET = 8192;
uint constant EXPONENT_BIT = 242;
uint constant MAX_M_DIGIT_NUMBER = 99999999999999999999999999999999999999;
uint constant MIN_M_DIGIT_NUMBER = 10000000000000000000000000000000000000;
uint constant MAX_L_DIGIT_NUMBER = 999999999999999999999999999999999999999999999999999999999999999999999999;
uint constant MIN_L_DIGIT_NUMBER = 100000000000000000000000000000000000000000000000000000000000000000000000;
// ln specific variables
// ln(2) from Wolfram
// 0.6931471805599453094172321214581765680755001343602552541206800094933936219696947
// ln(1.1) from Wolfram
// 0.095310179804324860043952123280765092220605365308644199185239808163001014
int constant ln10_70 = 23025850929940456840179914546843642076011014886287729760333279009675726;
int constant ln2_70 = 6931471805599453094172321214581765680755001343602552541206800094933936;
int constant ln1dot1_70 = 953101798043248600439521232807650922206053653086441991852398081630010;
int constant ln1dot01_70 = 99503308531680828482153575442607416886796099400587978646095597668666;
int constant ln1dot001_70 = 9995003330835331668093989205350114607550623931665519970196668289003;
// ln10, ln2 and ln1.1 represented as integers with M significant digits
int constant ln10_M = ln10_70 / int(10 ** (uint(70 - Float128.MAX_DIGITS_M + 1)));
int constant ln2_M = ln2_70 / int(10 ** (uint(70 - Float128.MAX_DIGITS_M)));
int constant ln1dot1_M = ln1dot1_70 / int(10 ** (uint(70 - Float128.MAX_DIGITS_M - 1)));
int constant ln1dot01_M = ln1dot01_70 / int(10 ** (uint(70 - Float128.MAX_DIGITS_M - 2)));
int constant ln1dot001_M = ln1dot001_70 / int(10 ** (uint(70 - Float128.MAX_DIGITS_M - 3)));
// ln10, ln2 and ln1.1 represented as float128
packedFloat constant ln10 = packedFloat.wrap(57634551253070896831007164474234001986315550567012630870766974200712100735196);
packedFloat constant ln2 = packedFloat.wrap(57627483864811783293688831284231030312298529498551182469036031073505904270823);
packedFloat constant ln1dot1 = packedFloat.wrap(57620416476552669756370498094228058638261215024712010322305773559835681227132);
packedFloat constant ln1dot01 = packedFloat.wrap(57613349088293556219052164904225086964202098217851863814911488587192353072694);
packedFloat constant ln1dot001 = packedFloat.wrap(57606281700034442681733831714222115290139235007041033827277788478998076322779);
// Natural Logs functions
/**
* @dev determine the natural log of the input
* @param input the number of which to derive the natural log.
* @return result log of the input as a packedFloat
* @notice passing a large-mantissa input will have better precision results.
*/
function ln(packedFloat input) public pure returns (packedFloat result) {
uint mantissa;
int exponent;
bool inputL;
assembly {
mantissa := and(input, MANTISSA_MASK)
if or(iszero(input), and(input, MANTISSA_SIGN_MASK)) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 22) // Revert reason length
mstore(add(ptr, 0x44), "float128: ln undefined")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
if iszero(mantissa) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
inputL := gt(and(input, MANTISSA_L_FLAG_MASK), 0)
exponent := sub(shr(EXPONENT_BIT, and(input, EXPONENT_MASK)), ZERO_OFFSET)
let isInvalid := 0
if inputL {
// Check if mantissa A has exactly 72 digits
isInvalid := or(isInvalid, or(lt(mantissa, MIN_L_DIGIT_NUMBER), gt(mantissa, MAX_L_DIGIT_NUMBER)))
}
if iszero(inputL) {
// Check if mantissa A has exactly 38 digits
isInvalid := or(isInvalid, or(lt(mantissa, MIN_M_DIGIT_NUMBER), gt(mantissa, MAX_M_DIGIT_NUMBER)))
}
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
}
if (
exponent == 0 - int(inputL ? Float128.MAX_DIGITS_L_MINUS_1 : Float128.MAX_DIGITS_M_MINUS_1) &&
mantissa == (inputL ? Float128.MIN_L_DIGIT_NUMBER : Float128.MIN_M_DIGIT_NUMBER)
) return packedFloat.wrap(0);
result = ln_helper(mantissa, exponent, inputL);
}
/**
* @dev Natural Log Helper function
* @param mantissa the integer that holds the mantissa digits (38 digits max)
* @param exp the exponent of the floating point number (between -8192 and +8191)
* @param inputL use positive exponent
* @return result the log of the input from ln function
*/
function ln_helper(uint mantissa, int exp, bool inputL) private pure returns (packedFloat result) {
int positiveExp = exp * -1;
if ((inputL && int(Float128.MAX_DIGITS_L) > positiveExp) || (!inputL && int(Float128.MAX_DIGITS_M) > positiveExp)) {
if (inputL) {
mantissa /= Float128.BASE_TO_THE_DIGIT_DIFF;
exp += int(Float128.DIGIT_DIFF_L_M);
}
uint q1 = Float128.BASE_TO_THE_MAX_DIGITS_M_X_2 / mantissa;
uint r1 = Float128.BASE_TO_THE_MAX_DIGITS_M_X_2 % mantissa;
uint q2 = (Float128.BASE_TO_THE_MAX_DIGITS_M * r1) / mantissa;
uint one_over_argument_in_long_int = q1 * Float128.BASE_TO_THE_MAX_DIGITS_M + q2;
uint m10 = one_over_argument_in_long_int > Float128.MAX_76_DIGIT_NUMBER ? 77 : 76;
uint one_over_arguments_76 = one_over_argument_in_long_int;
uint m76 = m10;
m76 -= Float128.DIGIT_DIFF_76_L;
one_over_arguments_76 /= Float128.BASE_TO_THE_DIFF_76_L;
if (m76 > Float128.MAX_DIGITS_L) {
--m76;
one_over_arguments_76 /= Float128.BASE;
}
int exp_one_over_argument = 0 - int(Float128.MAX_DIGITS_M) - int(Float128.MAX_DIGITS_M_X_2) - exp;
packedFloat a = packedFloat.wrap(0).sub(ln(Float128.toPackedFloat(int(one_over_arguments_76), 0 - int(m76))));
result = a.sub(Float128.toPackedFloat((exp_one_over_argument + int(m10)), 0).mul(ln10));
} else {
int256 m10 = inputL ? int(Float128.MAX_DIGITS_L) + exp : int(Float128.MAX_DIGITS_M) + exp;
exp -= m10;
mantissa *= (inputL ? Float128.BASE_TO_THE_DIFF_76_L : Float128.BASE_TO_THE_MAX_DIGITS_M);
exp -= int(inputL ? Float128.DIGIT_DIFF_L_M : Float128.MAX_DIGITS_M);
uint256 k;
uint256 multiplier_k;
if (mantissa > (25 * (10 ** 74))) {
if (mantissa > (50 * (10 ** 74))) {
multiplier_k = 1;
} else {
k = 1;
multiplier_k = 2;
}
} else {
if (mantissa > (125 * 10 ** 73)) {
k = 2;
multiplier_k = 4;
} else {
k = 3;
multiplier_k = 8;
}
}
mantissa *= multiplier_k;
uint uMantissa = mantissa;
uint256 q1;
(q1, uMantissa) = calculateQ1(uMantissa);
// We find the suitable value of q2 and the multiplier (1.014)**q2
// so that 0.986 <= (1.014)**q2 * updated_x <= 1
// We use the following intervals:
// (index -> lower bound of the interval)
// 0 -> 9 * 10**75
// 1 -> 9072 * 10**72
// 2 -> 9199 * 10**72
// 3 -> 9328 * 10**72
// 4 -> 9459 * 10**72
// 5 -> 9591 * 10**72
// 6 -> 9725 * 10**72
// 7 -> 9860 * 10**72
// partition_1014 = [0.9, 0.9072, 0.9199, 0.9328, 0.9459, 0.9591, 0.9725, 0.986, 1]
uint256 q2;
(q2, uMantissa) = calculateQ2(uMantissa);
// We find the suitable value of q3 and the multiplier (1.0013)**q3
// so that 0.9949 <= (1.0013)**q3 * updated_x <= 1
// We use the following intervals:
// (index -> lower bound of the interval)
// 0 -> 986 * 10**73
// 1 -> 987274190490 * 10**64
// 2 -> 988557646937 * 10**64
// 3 -> 989842771878 * 10**64
// 4 -> 991129567482 * 10**64
// 5 -> 992418035920 * 10**64
// 6 -> 993708179366 * 10**64
// 7 -> 995 * 10**73
// partition_10013 = [0.986, 0.987274190490, 0.988557646937, 0.989842771878, 0.991129567482, 0.992418035920, 0.993708179366, 0.995, 1]
uint256 q3;
(q3, uMantissa) = calculateQ3(uMantissa);
result = intermediateTermAddition(result, k, q1, q2, q3, m10, uMantissa);
}
}
/**
* @dev Intermediate Addition Helper
* @param result the result from the calling ln()
* @param k value passed from ln
* @param q1 BASE_TO_THE_MAX_DIGITS_M_X_2 / mantissa
* @param q2 (BASE_TO_THE_MAX_DIGITS_M * r1) / mantissa
* @param q3 value returned from calculateQ3 function
* @param m10 one_over_argument_in_long_int > MAX_76_DIGIT_NUMBER ? 77 : 76;
* @param mantissa the 38 mantissa digits of the floating-point number
* @return finalResult Log of the input from ln function
*/
function intermediateTermAddition(
packedFloat result,
uint256 k,
uint256 q1,
uint256 q2,
uint256 q3,
int256 m10,
uint256 mantissa
) private pure returns (packedFloat finalResult) {
// Now digits has already been updated
int z_int = 10 ** 76 - int(mantissa);
int len_z_int = int(Float128.findNumberOfDigits(uint(z_int)));
if (z_int != 0) {
int diff = len_z_int - 38;
z_int = diff < 0 ? int(uint(z_int) * 10 ** uint(diff * -1)) : int(uint(z_int) / 10 ** uint(diff));
}
packedFloat z = Float128.toPackedFloat(z_int, (len_z_int - 76 - 38));
// Number of terms of the Taylor series:
int terms = 15;
result = z;
packedFloat z_to_j = z;
for (uint j = 2; j < uint(terms + 1); j++) {
z_to_j = z_to_j.mul(z);
result = result.add(z_to_j.div(Float128.toPackedFloat(int(j), int(0))));
}
packedFloat lnB = Float128.toPackedFloat(13902905168991420865477877458246859530, -39);
packedFloat lnC = Float128.toPackedFloat(12991557316200501157605555658804528711, -40);
finalResult = finalTermAddition(result, k, q1, q2, q3, m10, lnB, lnC);
}
/**
* @dev Final Addition Helper Function
* @param result the result from the calling ln()
* @param k value passed from ln
* @param q1 BASE_TO_THE_MAX_DIGITS_M_X_2 / mantissa
* @param q2 (BASE_TO_THE_MAX_DIGITS_M * r1) / mantissa
* @param q3 value returned from calculateQ3 function
* @param m10 one_over_argument_in_long_int > MAX_76_DIGIT_NUMBER ? 77 : 76
* @param lnB toPackedFloat(13902905168991420865477877458246859530, -39)
* @param lnC toPackedFloat(12991557316200501157605555658804528711, -40)
*/
function finalTermAddition(
packedFloat result,
uint256 k,
uint256 q1,
uint256 q2,
uint256 q3,
int256 m10,
packedFloat lnB,
packedFloat lnC
) private pure returns (packedFloat finalResult) {
packedFloat firstTerm = result.add(Float128.toPackedFloat(int(k), 0).mul(ln2));
packedFloat secondTerm = firstTerm.add(Float128.toPackedFloat(int(q1), 0).mul(ln1dot1));
packedFloat thirdTerm = secondTerm.add(Float128.toPackedFloat(int(q2), 0).mul(lnB));
packedFloat fourthTerm = thirdTerm.add(Float128.toPackedFloat(int(q3), 0).mul(lnC));
packedFloat fifthTerm = fourthTerm.sub(Float128.toPackedFloat(int(m10), 0).mul(ln10));
finalResult = fifthTerm.mul(Float128.toPackedFloat(-1, 0));
}
/**
* @dev Helper function to calculate q1 in LN function
* @param mantissa the 38 mantissa digits of the floating-point number
* @return q1 uint value of q1
* @return updatedMantissa updated mantissa for q1
*/
function calculateQ1(uint256 mantissa) internal pure returns (uint256 q1, uint256 updatedMantissa) {
if (mantissa > (68300000 * 10 ** 68)) {
if (mantissa > (82000000 * 10 ** 68)) {
if (mantissa > (90000000 * 10 ** 68)) {
q1 = 0;
// multiplier_q1
updatedMantissa = mantissa;
} else {
q1 = 1;
updatedMantissa = mantissa + mantissa / 10;
}
} else {
if (mantissa > (75000000 * 10 ** 68)) {
q1 = 2;
updatedMantissa = mantissa + (2 * mantissa) / 10 + mantissa / 100;
} else {
q1 = 3;
updatedMantissa = mantissa + (3 * mantissa) / 10 + (3 * mantissa) / 100 + mantissa / 1000;
}
}
} else {
if (mantissa > (56400000 * 10 ** 68)) {
if (mantissa > (62000000 * 10 ** 68)) {
q1 = 4;
updatedMantissa = mantissa + (4 * mantissa) / 10 + (6 * mantissa) / 100 + (4 * mantissa) / 1000 + mantissa / 10000;
} else {
q1 = 5;
updatedMantissa = mantissa + (6 * mantissa) / 10 + (1 * mantissa) / 100 + (5 * mantissa) / 10000 + (1 * mantissa) / 100000;
}
} else {
if (mantissa > (51200000 * 10 ** 68)) {
q1 = 6;
updatedMantissa =
mantissa +
(7 * mantissa) /
10 +
(7 * mantissa) /
100 +
(1 * mantissa) /
1000 +
(5 * mantissa) /
10000 +
(6 * mantissa) /
100000 +
(1 * mantissa) /
1000000;
} else {
q1 = 7;
// multiplier_q1 = 1.1 ** 7 # = 1.9487171
updatedMantissa =
mantissa +
(9 * mantissa) /
10 +
(4 * mantissa) /
100 +
(8 * mantissa) /
1000 +
(7 * mantissa) /
10000 +
(1 * mantissa) /
100000 +
(7 * mantissa) /
1000000 +
(1 * mantissa) /
10000000;
}
}
}
}
/**
* @dev Helper function to calculate q2 in LN function
* @param mantissa the 38 mantissa digits of the floating-point number
* @return q2 uint value of q2
* @return updatedMantissa updated mantissa for q2
*/
function calculateQ2(uint256 mantissa) internal pure returns (uint256 q2, uint256 updatedMantissa) {
if (mantissa > (9459 * 10 ** 72)) {
if (mantissa > (9725 * 10 ** 72)) {
if (mantissa > 9860 * 10 ** 72) {
q2 = 0;
// multiplier_q2 = 1
updatedMantissa = mantissa;
} else {
q2 = 1;
updatedMantissa = mantissa + (1 * mantissa) / 100 + (4 * mantissa) / 1000;
}
} else {
if (mantissa > (9591 * 10 ** 72)) {
q2 = 2;
// multiplier_q2 = 1.028196
updatedMantissa = mantissa + (2 * mantissa) / 100 + (8 * mantissa) / 1000 + (1 * mantissa) / 10000 + (9 * mantissa) / 100000 + (6 * mantissa) / 1000000;
} else {
q2 = 3;
// multiplier_q2 = 1.042590744
updatedMantissa =
mantissa +
(4 * mantissa) /
100 +
(2 * mantissa) /
1000 +
(5 * mantissa) /
10000 +
(9 * mantissa) /
100000 +
(0 * mantissa) /
1000000 +
(7 * mantissa) /
10000000 +
(4 * mantissa) /
100000000 +
(4 * mantissa) /
1000000000;
}
}
} else {
if (mantissa > (9199 * 10 ** 72)) {
if (mantissa > (9328 * 10 ** 72)) {
q2 = 4;
// multiplier_q2 = 1.057187014416
updatedMantissa =
mantissa +
(5 * mantissa) /
100 +
(7 * mantissa) /
1000 +
(1 * mantissa) /
10000 +
(8 * mantissa) /
100000 +
(7 * mantissa) /
1000000 +
(0 * mantissa) /
10000000 +
(1 * mantissa) /
100000000 +
(4 * mantissa) /
1000000000 +
(4 * mantissa) /
10000000000 +
(1 * mantissa) /
100000000000 +
(6 * mantissa) /
1000000000000;
} else {
q2 = 5;
// multiplier_q2 = 1.071987632617824
// updatedMantissa = mantissa + 7 * mantissa / 100 + 1 * mantissa / 1000
// + 9 * mantissa / 10000 + 8 * mantissa / 100000 + 7 * mantissa / 1000000
// + 6 * mantissa / 10000000 + 3 * mantissa / 100000000 + 2 * mantissa / 1000000000
// + 6 * mantissa / 10000000000 + 1 * mantissa / 100000000000 + 7 * mantissa / 1000000000000
// + 8 * mantissa / 10000000000000 + 2 * mantissa / 100000000000000 + 4 * mantissa / 1000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 7 * mantissa / 100
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 100))
// 1 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mantissa, 1000))
// 9 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000))
// 8 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100000))
// 7 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 1000000))
// 6 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 10000000))
// 3 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 100000000))
// 2 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 1000000000))
// 6 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 10000000000))
// 1 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000000000))
// 7 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 1000000000000))
// 8 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000000000000))
// 2 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 100000000000000))
// 4 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 1000000000000000))
}
}
} else {
if (mantissa > (9072 * 10 ** 72)) {
q2 = 6;
// multiplier_q2 = 1.086995459474473536
// updatedMantissa = mantissa + 8 * mantissa / 100 + 6 * mantissa / 1000
// + 9 * mantissa / 10000 + 9 * mantissa / 100000 + 5 * mantissa / 1000000
// + 4 * mantissa / 10000000 + 5 * mantissa / 100000000 + 9 * mantissa / 1000000000
// + 4 * mantissa / 10000000000 + 7 * mantissa / 100000000000 + 4 * mantissa / 1000000000000
// + 4 * mantissa / 10000000000000 + 7 * mantissa / 100000000000000 + 3 * mantissa / 1000000000000000
// + 5 * mantissa / 10000000000000000 + 3 * mantissa / 100000000000000000 + 6 * mantissa / 1000000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 8 * mantissa / 100
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100))
// 6 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000))
// 9 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000))
// 9 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 100000))
// 5 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000))
// 4 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 10000000))
// 5 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 100000000))
// 9 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 1000000000))
// 4 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 10000000000))
// 7 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 100000000000))
// 4 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 1000000000000))
// 4 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 10000000000000))
// 7 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 100000000000000))
// 3 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 1000000000000000))
// 5 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 10000000000000000))
// 3 * mantissa / 100000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 100000000000000000))
// 6 * mantissa / 1000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000000000000000))
}
} else {
q2 = 7;
// multiplier_q2 = 1.102213395907116165504
// updatedMantissa = mantissa + 1 * mantissa / 10 + 2 * mantissa / 1000
// + 2 * mantissa / 10000 + 1 * mantissa / 100000 + 3 * mantissa / 1000000
// + 3 * mantissa / 10000000 + 9 * mantissa / 100000000 + 5 * mantissa / 1000000000
// + 9 * mantissa / 10000000000 + 7 * mantissa / 1000000000000
// + 1 * mantissa / 10000000000000 + 1 * mantissa / 100000000000000 + 6 * mantissa / 1000000000000000
// + 1 * mantissa / 10000000000000000 + 6 * mantissa / 100000000000000000 + 5 * mantissa / 1000000000000000000
// + 5 * mantissa / 10000000000000000000 + 4 * mantissa / 1000000000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 1 * mantissa / 10
updatedMantissa := add(updatedMantissa, div(mantissa, 10))
// 2 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 1000))
// 2 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 10000))
// 1 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000))
// 3 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 1000000))
// 3 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 10000000))
// 9 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 100000000))
// 5 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000000))
// 9 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000))
// 7 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 1000000000000))
// 1 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 10000000000000))
// 1 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000000000000))
// 6 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000000000000))
// 1 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 10000000000000000))
// 6 * mantissa / 100000000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 100000000000000000))
// 5 * mantissa / 1000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000000000000000))
// 5 * mantissa / 10000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 10000000000000000000))
// 4 * mantissa / 1000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 1000000000000000000000))
}
}
}
}
}
/**
* @dev Helper function to calculate q3 in LN function
* @param mantissa the 38 mantissa digits of the floating-point number
* @return q3 uint value of q3
* @return updatedMantissa updated mantissa for q3
*/
function calculateQ3(uint256 mantissa) internal pure returns (uint256 q3, uint256 updatedMantissa) {
if (mantissa > (991129567482 * 10 ** 64)) {
if (mantissa > (993708179366 * 10 ** 64)) {
if (mantissa > (995 * 10 ** 73)) {
q3 = 0;
// multiplier_q3 = 1
updatedMantissa = mantissa;
} else {
q3 = 1;
// multiplier_q3 = 1.0013
updatedMantissa = mantissa + (1 * mantissa) / 1000 + (3 * mantissa) / 10000;
}
} else {
if (mantissa > (992418035920 * 10 ** 64)) {
q3 = 2;
// multiplier_q3 = 1.00260169
updatedMantissa = mantissa + (2 * mantissa) / 1000 + (6 * mantissa) / 10000 + (1 * mantissa) / 1000000 + (6 * mantissa) / 10000000 + (9 * mantissa) / 100000000;
} else {
q3 = 3;
// multiplier_q3 = 1.003905072197
updatedMantissa =
mantissa +
(3 * mantissa) /
1000 +
(9 * mantissa) /
10000 +
(5 * mantissa) /
1000000 +
(7 * mantissa) /
100000000 +
(2 * mantissa) /
1000000000 +
(1 * mantissa) /
10000000000 +
(9 * mantissa) /
100000000000 +
(7 * mantissa) /
1000000000000;
}
}
} else {
if (mantissa > (988557646937 * 10 ** 64)) {
if (mantissa > (989842771878 * 10 ** 64)) {
q3 = 4;
// multiplier_q3 = 1.0052101487908561
// updatedMantissa = mantissa + 5 * mantissa / 1000 + 2 * mantissa / 10000
// + 1 * mantissa / 100000 + 1 * mantissa / 10000000 + 4 * mantissa / 100000000
// + 8 * mantissa / 1000000000 + 7 * mantissa / 10000000000 + 9 * mantissa / 100000000000
// + 8 * mantissa / 10000000000000 + 5 * mantissa / 100000000000000 + 6 * mantissa / 1000000000000000 + 1 * mantissa / 10000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 5 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000))
// 2 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 10000))
// 1 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000))
// 1 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mantissa, 10000000))
// 4 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 100000000))
// 8 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 1000000000))
// 7 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 10000000000))
// 9 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 100000000000))
// 8 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000000000000))
// 5 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 100000000000000))
// 6 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000000000000))
// 1 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 10000000000000000))
}
} else {
q3 = 5;
// multiplier_q3 = 1.00651692198428421293
// updatedMantissa = mantissa + 6 * mantissa / 1000 + 5 * mantissa / 10000
// + 1 * mantissa / 100000 + 6 * mantissa / 1000000 + 9 * mantissa / 10000000 + 2 * mantissa / 100000000
// + 1 * mantissa / 1000000000 + 9 * mantissa / 10000000000 + 8 * mantissa / 100000000000 + 4 * mantissa / 1000000000000
// + 2 * mantissa / 10000000000000 + 8 * mantissa / 100000000000000 + 4 * mantissa / 1000000000000000 + 2 * mantissa / 10000000000000000
// + 1 * mantissa / 100000000000000000 + 2 * mantissa / 1000000000000000000 + 9 * mantissa / 10000000000000000000 + 3 * mantissa / 100000000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 6 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000))
// 5 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 10000))
// 1 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000))
// 6 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000))
// 9 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000))
// 2 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 100000000))
// 1 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 1000000000))
// 9 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000))
// 8 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100000000000))
// 4 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 1000000000000))
// 2 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 10000000000000))
// 8 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100000000000000))
// 4 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 1000000000000000))
// 2 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 10000000000000000))
// 1 * mantissa / 100000000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 100000000000000000))
// 2 * mantissa / 1000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 1000000000000000000))
// 9 * mantissa / 10000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000000000000))
// 3 * mantissa / 100000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 100000000000000000000))
}
}
} else {
if (mantissa > (987274190490 * 10 ** 64)) {
q3 = 6;
// multiplier_q3 = 1.007825393982863782406809
// updatedMantissa = mantissa + 7 * mantissa / 1000 + 8 * mantissa / 10000
// + 2 * mantissa / 100000 + 5 * mantissa / 1000000 + 3 * mantissa / 10000000 + 9 * mantissa / 100000000
// + 3 * mantissa / 1000000000 + 9 * mantissa / 10000000000 + 8 * mantissa / 100000000000 + 2 * mantissa / 1000000000000
// + 8 * mantissa / 10000000000000 + 6 * mantissa / 100000000000000 + 3 * mantissa / 1000000000000000 + 7 * mantissa / 10000000000000000
// + 8 * mantissa / 100000000000000000 + 2 * mantissa / 1000000000000000000 + 4 * mantissa / 10000000000000000000
// + 6 * mantissa / 1000000000000000000000 + 8 * mantissa / 10000000000000000000000 + 9 * mantissa / 1000000000000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 0 * mantissa / 10 (skip since it's zero)
// 0 * mantissa / 100 (skip since it's zero)
// 7 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 1000))
// 8 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000))
// 2 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 100000))
// 5 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000))
// 3 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 10000000))
// 9 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 100000000))
// 3 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 1000000000))
// 9 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000))
// 8 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100000000000))
// 2 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 1000000000000))
// 8 * mantissa / 10000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000000000000))
// 6 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 100000000000000))
// 3 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 1000000000000000))
// 7 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 10000000000000000))
// 8 * mantissa / 100000000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 100000000000000000))
// 2 * mantissa / 1000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 1000000000000000000))
// 4 * mantissa / 10000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 10000000000000000000))
// 0 * mantissa / 100000000000000000000 (skip since it's zero)
// 6 * mantissa / 1000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000000000000000000))
// 8 * mantissa / 10000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000000000000000000000))
// 0 * mantissa / 100000000000000000000000 (skip since it's zero)
// 9 * mantissa / 1000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 1000000000000000000000000))
}
} else {
q3 = 7;
// multiplier_q3 = 1.0091355669950415053239378517
// updatedMantissa = mantissa + 9 * mantissa / 1000 + 1 * mantissa / 10000
// + 3 * mantissa / 100000 + 5 * mantissa / 1000000 + 5 * mantissa / 10000000 + 6 * mantissa / 100000000
// + 6 * mantissa / 1000000000 + 9 * mantissa / 10000000000 + 9 * mantissa / 100000000000 + 5 * mantissa / 1000000000000
// + 4 * mantissa / 100000000000000 + 1 * mantissa / 1000000000000000 + 5 * mantissa / 10000000000000000
// + 5 * mantissa / 1000000000000000000 + 3 * mantissa / 10000000000000000000 + 2 * mantissa / 100000000000000000000
// + 3 * mantissa / 1000000000000000000000 + 9 * mantissa / 10000000000000000000000 + 3 * mantissa / 100000000000000000000000 + 7 * mantissa / 1000000000000000000000000
// + 8 * mantissa / 10000000000000000000000000 + 5 * mantissa / 100000000000000000000000000 + 1 * mantissa / 1000000000000000000000000000 + 7 * mantissa / 10000000000000000000000000000;
assembly {
// Start with the base value
updatedMantissa := mantissa
// 0 * mantissa / 10 (skip since it's zero)
// 0 * mantissa / 100 (skip since it's zero)
// 9 * mantissa / 1000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 1000))
// 1 * mantissa / 10000
updatedMantissa := add(updatedMantissa, div(mantissa, 10000))
// 3 * mantissa / 100000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 100000))
// 5 * mantissa / 1000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000))
// 5 * mantissa / 10000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 10000000))
// 6 * mantissa / 100000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 100000000))
// 6 * mantissa / 1000000000
updatedMantissa := add(updatedMantissa, div(mul(6, mantissa), 1000000000))
// 9 * mantissa / 10000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000))
// 9 * mantissa / 100000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 100000000000))
// 5 * mantissa / 1000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000000000))
// 0 * mantissa / 10000000000000 (skip since it's zero)
// 4 * mantissa / 100000000000000
updatedMantissa := add(updatedMantissa, div(mul(4, mantissa), 100000000000000))
// 1 * mantissa / 1000000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 1000000000000000))
// 5 * mantissa / 10000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 10000000000000000))
// 0 * mantissa / 100000000000000000 (skip since it's zero)
// 5 * mantissa / 1000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 1000000000000000000))
// 3 * mantissa / 10000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 10000000000000000000))
// 2 * mantissa / 100000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(2, mantissa), 100000000000000000000))
// 3 * mantissa / 1000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 1000000000000000000000))
// 9 * mantissa / 10000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(9, mantissa), 10000000000000000000000))
// 3 * mantissa / 100000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(3, mantissa), 100000000000000000000000))
// 7 * mantissa / 1000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 1000000000000000000000000))
// 8 * mantissa / 10000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(8, mantissa), 10000000000000000000000000))
// 5 * mantissa / 100000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(5, mantissa), 100000000000000000000000000))
// 1 * mantissa / 1000000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mantissa, 1000000000000000000000000000))
// 7 * mantissa / 10000000000000000000000000000
updatedMantissa := add(updatedMantissa, div(mul(7, mantissa), 10000000000000000000000000000))
}
}
}
}
}
}
Float128.sol 1359 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Uint512} from "../lib/Uint512.sol";
import {packedFloat} from "./Types.sol";
/**
* @title Floating point Library base 10 with 38 or 72 digits signed
* @dev the library uses the type packedFloat which is a uint under the hood
* @author Inspired by a Python proposal by @miguel-ot and refined/implemented in Solidity by @oscarsernarosero @Palmerg4
*/
library Float128 {
uint constant MANTISSA_MASK = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint constant MANTISSA_SIGN_MASK = 0x1000000000000000000000000000000000000000000000000000000000000;
uint constant MANTISSA_L_FLAG_MASK = 0x2000000000000000000000000000000000000000000000000000000000000;
uint constant EXPONENT_MASK = 0xfffc000000000000000000000000000000000000000000000000000000000000;
uint constant TWO_COMPLEMENT_SIGN_MASK = 0x8000000000000000000000000000000000000000000000000000000000000000;
uint constant BASE = 10;
uint constant ZERO_OFFSET = 8192;
uint constant ZERO_OFFSET_MINUS_1 = 8191;
uint constant EXPONENT_BIT = 242;
uint constant MAX_DIGITS_M = 38;
uint constant MAX_DIGITS_M_X_2 = 76;
uint constant MAX_DIGITS_M_MINUS_1 = 37;
uint constant MAX_DIGITS_M_PLUS_1 = 39;
uint constant MAX_DIGITS_L = 72;
uint constant MAX_DIGITS_L_MINUS_1 = 71;
uint constant MAX_DIGITS_L_PLUS_1 = 73;
uint constant DIGIT_DIFF_L_M = 34;
uint constant DIGIT_DIFF_L_M_PLUS_1 = 35;
uint constant DIGIT_DIFF_76_L_MINUS_1 = 3;
uint constant DIGIT_DIFF_76_L = 4;
uint constant DIGIT_DIFF_76_L_PLUS_1 = 5;
uint constant MAX_M_DIGIT_NUMBER = 99999999999999999999999999999999999999;
uint constant MIN_M_DIGIT_NUMBER = 10000000000000000000000000000000000000;
uint constant MAX_L_DIGIT_NUMBER = 999999999999999999999999999999999999999999999999999999999999999999999999;
uint constant MIN_L_DIGIT_NUMBER = 100000000000000000000000000000000000000000000000000000000000000000000000;
uint constant BASE_TO_THE_MAX_DIGITS_L = 1000000000000000000000000000000000000000000000000000000000000000000000000;
uint constant BASE_TO_THE_DIGIT_DIFF = 10000000000000000000000000000000000;
uint constant BASE_TO_THE_DIGIT_DIFF_PLUS_1 = 100000000000000000000000000000000000;
uint constant BASE_TO_THE_MAX_DIGITS_M_MINUS_1 = 10000000000000000000000000000000000000;
uint constant BASE_TO_THE_MAX_DIGITS_M = 100000000000000000000000000000000000000;
uint constant BASE_TO_THE_MAX_DIGITS_M_PLUS_1 = 1000000000000000000000000000000000000000;
uint constant BASE_TO_THE_MAX_DIGITS_M_X_2 = 10000000000000000000000000000000000000000000000000000000000000000000000000000;
uint constant BASE_TO_THE_DIFF_76_L_MINUS_1 = 1_000;
uint constant BASE_TO_THE_DIFF_76_L = 10_000;
uint constant BASE_TO_THE_DIFF_76_L_PLUS_1 = 100_000;
uint constant MAX_75_DIGIT_NUMBER = 999999999999999999999999999999999999999999999999999999999999999999999999999;
uint constant MAX_76_DIGIT_NUMBER = 9999999999999999999999999999999999999999999999999999999999999999999999999999;
int constant MAXIMUM_EXPONENT = -18; // guarantees all results will have at least 18 decimals in the M size. Autoscales to L if necessary
/**
* @dev adds 2 signed floating point numbers
* @param a the first addend
* @param b the second addend
* @return r the result of a + b
*/
function add(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
uint addition;
bool isSubtraction;
bool sameExponent;
if (packedFloat.unwrap(a) == 0) return b;
if (packedFloat.unwrap(b) == 0) return a;
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
isSubtraction := xor(and(a, MANTISSA_SIGN_MASK), and(b, MANTISSA_SIGN_MASK))
// we extract the exponent and mantissas for both
let aExp := shr(EXPONENT_BIT, a)
let bExp := shr(EXPONENT_BIT, b)
let aMan := and(a, MANTISSA_MASK)
let bMan := and(b, MANTISSA_MASK)
let isInvalid := 0
if or(iszero(aMan), iszero(bMan)) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
isInvalid := or(
or(
and(aL, or(lt(aMan, MIN_L_DIGIT_NUMBER), gt(aMan, MAX_L_DIGIT_NUMBER))),
and(iszero(aL), or(lt(aMan, MIN_M_DIGIT_NUMBER), gt(aMan, MAX_M_DIGIT_NUMBER)))
),
or(
and(bL, or(lt(bMan, MIN_L_DIGIT_NUMBER), gt(bMan, MAX_L_DIGIT_NUMBER))),
and(iszero(bL),or(lt(bMan, MIN_M_DIGIT_NUMBER), gt(bMan, MAX_M_DIGIT_NUMBER)))
)
)
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
// we check exponents cannot maliciously underflow while expanding, or underflow on a similar result
if or(lt(aExp, MAX_DIGITS_M_X_2), lt(bExp, MAX_DIGITS_M_X_2)) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 19) // Revert reason length
mstore(add(ptr, 0x44), "float128: underflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
// we check that the result of an addition won't overflow while normalizing
if and(
iszero(isSubtraction),
or(gt(aExp, sub(shl(1, ZERO_OFFSET), MAX_DIGITS_M_X_2)), gt(bExp, sub(shl(1, ZERO_OFFSET), MAX_DIGITS_M_X_2)))
) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 18) // Revert reason length
mstore(add(ptr, 0x44), "float128: overflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, DIGIT_DIFF_L_M)
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, DIGIT_DIFF_L_M)
}
// we adjust the significant digits and set the exponent of the result
if gt(aExp, bExp) {
r := shl(EXPONENT_BIT, sub(aExp, DIGIT_DIFF_76_L))
let adj := sub(shr(EXPONENT_BIT, r), bExp)
let neg := and(TWO_COMPLEMENT_SIGN_MASK, adj)
if neg {
bMan := mul(bMan, exp(BASE, sub(0, adj)))
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
}
if iszero(neg) {
bMan := sdiv(bMan, exp(BASE, adj))
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
}
}
if gt(bExp, aExp) {
r := shl(EXPONENT_BIT, sub(bExp, DIGIT_DIFF_76_L))
let adj := sub(shr(EXPONENT_BIT, r), aExp)
let neg := and(TWO_COMPLEMENT_SIGN_MASK, adj)
if neg {
aMan := mul(aMan, exp(BASE, sub(0, adj)))
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
}
if iszero(neg) {
aMan := sdiv(aMan, exp(BASE, adj))
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
}
}
// // if exponents are the same, we don't need to adjust the mantissas. We just set the result's exponent
if eq(aExp, bExp) {
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
r := shl(EXPONENT_BIT, sub(aExp, DIGIT_DIFF_76_L))
sameExponent := 1
}
// now we convert to 2's complement to carry out the operation
if and(b, MANTISSA_SIGN_MASK) {
bMan := sub(0, bMan)
}
if and(a, MANTISSA_SIGN_MASK) {
aMan := sub(0, aMan)
}
// now we can add/subtract
addition := add(aMan, bMan)
// encoding the unnormalized result
if and(TWO_COMPLEMENT_SIGN_MASK, addition) {
r := or(r, MANTISSA_SIGN_MASK) // assign the negative sign
addition := sub(0, addition) // convert back from 2's complement
}
if iszero(addition) {
r := 0
}
}
// normalization
if (packedFloat.unwrap(r) > 0) {
uint rExp;
assembly {
rExp := shr(EXPONENT_BIT, r)
}
if (isSubtraction) {
// subtraction case can have a number of digits anywhere from 1 to 76
// we might get a normalized result, so we only normalize if necessary
if (!((addition <= MAX_M_DIGIT_NUMBER && addition >= MIN_M_DIGIT_NUMBER) || (addition <= MAX_L_DIGIT_NUMBER && addition >= MIN_L_DIGIT_NUMBER))) {
uint digitsMantissa = findNumberOfDigits(addition);
assembly {
let mantissaReducer := sub(digitsMantissa, MAX_DIGITS_M)
let isResultL := slt(MAXIMUM_EXPONENT, add(sub(rExp, ZERO_OFFSET), mantissaReducer))
if isResultL {
mantissaReducer := sub(mantissaReducer, DIGIT_DIFF_L_M)
r := or(r, MANTISSA_L_FLAG_MASK)
}
let negativeReducer := and(TWO_COMPLEMENT_SIGN_MASK, mantissaReducer)
if negativeReducer {
addition := mul(addition, exp(BASE, sub(0, mantissaReducer)))
r := sub(r, shl(EXPONENT_BIT, sub(0, mantissaReducer)))
}
if iszero(negativeReducer) {
addition := div(addition, exp(BASE, mantissaReducer))
r := add(r, shl(EXPONENT_BIT, mantissaReducer))
}
}
} else if (addition >= MIN_L_DIGIT_NUMBER && rExp < (ZERO_OFFSET - uint(MAXIMUM_EXPONENT * -1) - DIGIT_DIFF_L_M)) {
assembly {
addition := sdiv(addition, BASE_TO_THE_DIGIT_DIFF)
r := add(r, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
}
} else {
// addition case is simpler since it can only have 2 possibilities: same digits as its addends,
// or + 1 digits due to an "overflow"
assembly {
let isGreaterThan76Digits := gt(addition, MAX_76_DIGIT_NUMBER)
let maxExp := sub(sub(add(ZERO_OFFSET, MAXIMUM_EXPONENT), MAX_DIGITS_M), isGreaterThan76Digits)
let _isM := or(eq(rExp, maxExp), lt(rExp, maxExp))
if _isM {
addition := div(addition, BASE_TO_THE_MAX_DIGITS_M)
r := add(r, shl(EXPONENT_BIT, MAX_DIGITS_M))
}
if iszero(_isM) {
addition := div(addition, BASE_TO_THE_DIFF_76_L)
r := add(r, shl(EXPONENT_BIT, DIGIT_DIFF_76_L))
r := add(r, MANTISSA_L_FLAG_MASK)
}
if or(gt(addition, MAX_L_DIGIT_NUMBER), and(lt(addition, MIN_L_DIGIT_NUMBER), gt(addition, MAX_M_DIGIT_NUMBER))) {
addition := div(addition, BASE)
r := add(r, shl(EXPONENT_BIT, 1))
}
}
}
assembly {
r := or(r, addition)
}
}
}
/**
* @dev gets the difference between 2 signed floating point numbers
* @param a the minuend
* @param b the subtrahend
* @return r the result of a - b
* @notice this version of the function uses only the packedFloat type
*/
function sub(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
uint addition;
bool isSubtraction;
bool sameExponent;
if (packedFloat.unwrap(a) == 0) {
assembly {
if gt(b, 0) {
b := xor(MANTISSA_SIGN_MASK, b)
}
}
return b;
}
if (packedFloat.unwrap(b) == 0) return a;
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
isSubtraction := eq(and(a, MANTISSA_SIGN_MASK), and(b, MANTISSA_SIGN_MASK))
// we extract the exponent and mantissas for both
let aExp := shr(EXPONENT_BIT, a)
let bExp := shr(EXPONENT_BIT, b)
let aMan := and(a, MANTISSA_MASK)
let bMan := and(b, MANTISSA_MASK)
let isInvalid := 0
if or(iszero(aMan), iszero(bMan)) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
isInvalid := or(
or(
and(aL, or(lt(aMan, MIN_L_DIGIT_NUMBER), gt(aMan, MAX_L_DIGIT_NUMBER))),
and(iszero(aL), or(lt(aMan, MIN_M_DIGIT_NUMBER), gt(aMan, MAX_M_DIGIT_NUMBER)))
),
or(
and(bL, or(lt(bMan, MIN_L_DIGIT_NUMBER), gt(bMan, MAX_L_DIGIT_NUMBER))),
and(iszero(bL),or(lt(bMan, MIN_M_DIGIT_NUMBER), gt(bMan, MAX_M_DIGIT_NUMBER)))
)
)
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
if or(lt(aExp, MAX_DIGITS_M_X_2), lt(bExp, MAX_DIGITS_M_X_2)) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 19) // Revert reason length
mstore(add(ptr, 0x44), "float128: underflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
// we check that the result of an addition won't overflow while normalizing
if and(
iszero(isSubtraction),
or(gt(aExp, sub(shl(1, ZERO_OFFSET), MAX_DIGITS_M_X_2)), gt(bExp, sub(shl(1, ZERO_OFFSET), MAX_DIGITS_M_X_2)))
) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 18) // Revert reason length
mstore(add(ptr, 0x44), "float128: overflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
// we make sure both of them are size L before continuing
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, DIGIT_DIFF_L_M)
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, DIGIT_DIFF_L_M)
}
// we adjust the significant digits and set the exponent of the result
if gt(aExp, bExp) {
r := shl(EXPONENT_BIT, sub(aExp, DIGIT_DIFF_76_L))
let adj := sub(shr(EXPONENT_BIT, r), bExp)
let neg := and(TWO_COMPLEMENT_SIGN_MASK, adj)
if neg {
bMan := mul(bMan, exp(BASE, sub(0, adj)))
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
}
if iszero(neg) {
bMan := sdiv(bMan, exp(BASE, adj))
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
}
}
if gt(bExp, aExp) {
r := shl(EXPONENT_BIT, sub(bExp, DIGIT_DIFF_76_L))
let adj := sub(shr(EXPONENT_BIT, r), aExp)
let neg := and(TWO_COMPLEMENT_SIGN_MASK, adj)
if neg {
aMan := mul(aMan, exp(BASE, sub(0, adj)))
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
}
if iszero(neg) {
aMan := sdiv(aMan, exp(BASE, adj))
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
}
}
// // if exponents are the same, we don't need to adjust the mantissas. We just set the result's exponent
if eq(aExp, bExp) {
aMan := mul(aMan, BASE_TO_THE_DIFF_76_L)
bMan := mul(bMan, BASE_TO_THE_DIFF_76_L)
r := shl(EXPONENT_BIT, sub(aExp, DIGIT_DIFF_76_L))
sameExponent := 1
}
// now we convert to 2's complement to carry out the operation
if iszero(and(b, MANTISSA_SIGN_MASK)) {
bMan := sub(0, bMan)
}
if and(a, MANTISSA_SIGN_MASK) {
aMan := sub(0, aMan)
}
// now we can add/subtract
addition := add(aMan, bMan)
// encoding the unnormalized result
if and(TWO_COMPLEMENT_SIGN_MASK, addition) {
r := or(r, MANTISSA_SIGN_MASK) // assign the negative sign
addition := sub(0, addition) // convert back from 2's complement
}
if iszero(addition) {
r := 0
}
}
// normalization
if (packedFloat.unwrap(r) > 0) {
uint rExp;
assembly {
rExp := shr(EXPONENT_BIT, r)
}
if (isSubtraction) {
// subtraction case can have a number of digits anywhere from 1 to 76
// we might get a normalized result, so we only normalize if necessary
if (!((addition <= MAX_M_DIGIT_NUMBER && addition >= MIN_M_DIGIT_NUMBER) || (addition <= MAX_L_DIGIT_NUMBER && addition >= MIN_L_DIGIT_NUMBER))) {
uint digitsMantissa = findNumberOfDigits(addition);
assembly {
let mantissaReducer := sub(digitsMantissa, MAX_DIGITS_M)
let isResultL := slt(MAXIMUM_EXPONENT, add(sub(rExp, ZERO_OFFSET), mantissaReducer))
if isResultL {
mantissaReducer := sub(mantissaReducer, DIGIT_DIFF_L_M)
r := or(r, MANTISSA_L_FLAG_MASK)
}
let negativeReducer := and(TWO_COMPLEMENT_SIGN_MASK, mantissaReducer)
if negativeReducer {
addition := mul(addition, exp(BASE, sub(0, mantissaReducer)))
r := sub(r, shl(EXPONENT_BIT, sub(0, mantissaReducer)))
}
if iszero(negativeReducer) {
addition := div(addition, exp(BASE, mantissaReducer))
r := add(r, shl(EXPONENT_BIT, mantissaReducer))
}
}
} else if (addition >= MIN_L_DIGIT_NUMBER && rExp < (ZERO_OFFSET - uint(MAXIMUM_EXPONENT * -1) - DIGIT_DIFF_L_M)) {
assembly {
addition := sdiv(addition, BASE_TO_THE_DIGIT_DIFF)
r := add(r, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
}
} else {
// addition case is simpler since it can only have 2 possibilities: same digits as its addends,
// or + 1 digits due to an "overflow"
assembly {
let isGreaterThan76Digits := gt(addition, MAX_76_DIGIT_NUMBER)
let maxExp := sub(sub(sub(add(ZERO_OFFSET, MAXIMUM_EXPONENT), DIGIT_DIFF_L_M), DIGIT_DIFF_76_L), isGreaterThan76Digits)
let _isM := or(eq(rExp, maxExp), lt(rExp, maxExp))
if _isM {
addition := div(addition, BASE_TO_THE_MAX_DIGITS_M)
r := add(r, shl(EXPONENT_BIT, MAX_DIGITS_M))
}
if iszero(_isM) {
addition := div(addition, BASE_TO_THE_DIFF_76_L)
r := add(r, shl(EXPONENT_BIT, DIGIT_DIFF_76_L))
r := add(r, MANTISSA_L_FLAG_MASK)
}
if or(gt(addition, MAX_L_DIGIT_NUMBER), and(lt(addition, MIN_L_DIGIT_NUMBER), gt(addition, MAX_M_DIGIT_NUMBER))) {
addition := div(addition, BASE)
r := add(r, shl(EXPONENT_BIT, 1))
}
}
}
assembly {
r := or(r, addition)
}
}
}
/**
* @dev gets the product of 2 signed floating point numbers
* @param a the multiplicand
* @param b the multiplier
* @return r the result of a * b
*/
function mul(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
uint rMan;
uint rExp;
uint r0;
uint r1;
bool Loperation;
if (packedFloat.unwrap(a) == 0 || packedFloat.unwrap(b) == 0) return packedFloat.wrap(0);
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
Loperation := or(aL, bL)
// we extract the exponent and mantissas for both
let aExp := shr(EXPONENT_BIT, a)
let bExp := shr(EXPONENT_BIT, b)
let aMan := and(a, MANTISSA_MASK)
let bMan := and(b, MANTISSA_MASK)
let isInvalid := 0
if or(iszero(aMan), iszero(bMan)) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
isInvalid := or(
or(
and(aL, or(lt(aMan, MIN_L_DIGIT_NUMBER), gt(aMan, MAX_L_DIGIT_NUMBER))),
and(iszero(aL), or(lt(aMan, MIN_M_DIGIT_NUMBER), gt(aMan, MAX_M_DIGIT_NUMBER)))
),
or(
and(bL, or(lt(bMan, MIN_L_DIGIT_NUMBER), gt(bMan, MAX_L_DIGIT_NUMBER))),
and(iszero(bL),or(lt(bMan, MIN_M_DIGIT_NUMBER), gt(bMan, MAX_M_DIGIT_NUMBER)))
)
)
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
// underflow can happen due to malicious encoding, or product of very negative exponents
if or(or(lt(aExp, MAX_DIGITS_M_X_2), lt(bExp, MAX_DIGITS_M_X_2)), lt(add(aExp, bExp), add(ZERO_OFFSET, MAX_DIGITS_M_X_2))) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 19) // Revert reason length
mstore(add(ptr, 0x44), "float128: underflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
// overflow can happen if exponents can go over 2*ZERO_OFFSET - 1: aExp - Z + bExp - Z > Z - 1 --> aExp + bExp > 3Z - 1
if gt(add(aExp, bExp), sub(mul(3, ZERO_OFFSET), MAX_DIGITS_M_X_2)) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 18) // Revert reason length
mstore(add(ptr, 0x44), "float128: overflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
if Loperation {
// we make sure both of them are size L before continuing
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, DIGIT_DIFF_L_M)
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, DIGIT_DIFF_L_M)
}
rExp := sub(add(aExp, bExp), ZERO_OFFSET)
let mm := mulmod(aMan, bMan, not(0))
r0 := mul(aMan, bMan)
r1 := sub(sub(mm, r0), lt(mm, r0))
}
if iszero(Loperation) {
rMan := mul(aMan, bMan)
rExp := sub(add(aExp, bExp), ZERO_OFFSET)
}
}
if (Loperation) {
// MIN_L_DIGIT_NUMBER is equal to BASE ** (MAX_L_DIGITS - 1).
// We avoid losing the lsd this way, but we could get 1 extra digit
rMan = Uint512.div512x256(r0, r1, MIN_L_DIGIT_NUMBER);
assembly {
rExp := add(rExp, MAX_DIGITS_L_MINUS_1)
let hasExtraDigit := gt(rMan, MAX_L_DIGIT_NUMBER)
let maxExp := sub(sub(add(ZERO_OFFSET, MAXIMUM_EXPONENT), DIGIT_DIFF_L_M), hasExtraDigit)
Loperation := gt(rExp, maxExp)
// if not, we then know that it is a 2k-1-digit number
if and(Loperation, hasExtraDigit) {
rMan := div(rMan, BASE)
rExp := add(rExp, 1)
}
if iszero(Loperation) {
if hasExtraDigit {
rMan := div(rMan, BASE_TO_THE_DIGIT_DIFF_PLUS_1)
rExp := add(rExp, DIGIT_DIFF_L_M_PLUS_1)
}
if iszero(hasExtraDigit) {
rMan := div(rMan, BASE_TO_THE_DIGIT_DIFF)
rExp := add(rExp, DIGIT_DIFF_L_M)
}
}
}
} else {
assembly {
// multiplication between 2 numbers with k digits can result in a number between 2*k - 1 and 2*k digits
// we check first if rMan is a 2k-digit number
let is76digit := gt(rMan, MAX_75_DIGIT_NUMBER)
let maxExp := add(sub(sub(add(ZERO_OFFSET, MAXIMUM_EXPONENT), DIGIT_DIFF_L_M), DIGIT_DIFF_76_L), iszero(is76digit))
Loperation := gt(rExp, maxExp)
if is76digit {
if Loperation {
rMan := div(rMan, BASE_TO_THE_DIFF_76_L)
rExp := add(rExp, DIGIT_DIFF_76_L)
}
if iszero(Loperation) {
rMan := div(rMan, BASE_TO_THE_MAX_DIGITS_M)
rExp := add(rExp, MAX_DIGITS_M)
}
}
// if not, we then know that it is a 2k-1-digit number
if iszero(is76digit) {
if Loperation {
rMan := div(rMan, BASE_TO_THE_DIFF_76_L_MINUS_1)
rExp := add(rExp, DIGIT_DIFF_76_L_MINUS_1)
}
if iszero(Loperation) {
rMan := div(rMan, BASE_TO_THE_MAX_DIGITS_M_MINUS_1)
rExp := add(rExp, MAX_DIGITS_M_MINUS_1)
}
}
}
}
assembly {
r := or(xor(and(a, MANTISSA_SIGN_MASK), and(b, MANTISSA_SIGN_MASK)), or(rMan, shl(EXPONENT_BIT, rExp)))
if Loperation {
r := or(r, MANTISSA_L_FLAG_MASK)
}
}
}
/**
* @dev gets the quotient of 2 signed floating point numbers
* @param a the numerator
* @param b the denominator
* @return r the result of a / b
*/
function div(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = div(a, b, false);
}
/**
* @dev gets the quotient of 2 signed floating point numbers which results in a large mantissa (72 digits) for better precision
* @param a the numerator
* @param b the denominator
* @return r the result of a / b
*/
function divL(packedFloat a, packedFloat b) internal pure returns (packedFloat r) {
r = div(a, b, true);
}
/**
* @dev gets the remainder of 2 signed floating point numbers
* @param a the numerator
* @param b the denominator
* @param rL Large mantissa flag for the result. If true, the result will be force to use 72 digits for the mansitssa
* @return r the result of a / b
*/
function div(packedFloat a, packedFloat b, bool rL) internal pure returns (packedFloat r) {
assembly {
if iszero(b) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 26) // Revert reason length
mstore(add(ptr, 0x44), "float128: division by zero")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
}
if (packedFloat.unwrap(a) == 0) return a;
uint rMan;
uint rExp;
uint a0;
uint a1;
uint aMan;
uint aExp;
uint bMan;
uint bExp;
bool Loperation;
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
// if a is zero then the result will be zero
aExp := shr(EXPONENT_BIT, a)
bExp := shr(EXPONENT_BIT, b)
aMan := and(a, MANTISSA_MASK)
bMan := and(b, MANTISSA_MASK)
let isInvalid := 0
if or(iszero(aMan), iszero(bMan)) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
isInvalid := or(
or(
and(aL, or(lt(aMan, MIN_L_DIGIT_NUMBER), gt(aMan, MAX_L_DIGIT_NUMBER))),
and(iszero(aL), or(lt(aMan, MIN_M_DIGIT_NUMBER), gt(aMan, MAX_M_DIGIT_NUMBER)))
),
or(
and(bL, or(lt(bMan, MIN_L_DIGIT_NUMBER), gt(bMan, MAX_L_DIGIT_NUMBER))),
and(iszero(bL),or(lt(bMan, MIN_M_DIGIT_NUMBER), gt(bMan, MAX_M_DIGIT_NUMBER)))
)
)
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
// underflow can happen due to malicious encoding, or division of a very negative exponent by a very positive exponent
// large-mantissa operations makes it riskier for division to underflow. A skewed lower bound of 2 * MAX_DIGITS_M_X_2 is necessary
if or(or(lt(aExp, MAX_DIGITS_M_X_2), lt(bExp, MAX_DIGITS_M_X_2)), slt(sub(aExp, bExp), sub(shl(1, MAX_DIGITS_M_X_2), ZERO_OFFSET))) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 19) // Revert reason length
mstore(add(ptr, 0x44), "float128: underflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
// overflow can happen if exponents can get to at least ZERO_OFFSET: aExp - Z - bExp + Z >= Z --> aExp - bExp >= Z
// we add the protection buffer of MAX_DIGITS_M_X_2
if sgt(sub(aExp, bExp), sub(ZERO_OFFSET, MAX_DIGITS_M_X_2)) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 18) // Revert reason length
mstore(add(ptr, 0x44), "float128: overflow")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
Loperation := or(
or(rL, or(aL, bL)),
// we add 1 to the calculation because division could result in an extra digit which will increase
// the value of the exponent hence potentially violating maximum exponent
sgt(add(sub(sub(sub(aExp, ZERO_OFFSET), MAX_DIGITS_M), sub(bExp, ZERO_OFFSET)), 1), MAXIMUM_EXPONENT)
)
if Loperation {
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, DIGIT_DIFF_L_M)
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, DIGIT_DIFF_L_M)
}
let mm := mulmod(aMan, BASE_TO_THE_MAX_DIGITS_L, not(0))
a0 := mul(aMan, BASE_TO_THE_MAX_DIGITS_L)
a1 := sub(sub(mm, a0), lt(mm, a0))
aExp := sub(aExp, MAX_DIGITS_L)
}
if iszero(Loperation) {
// we add 38 more digits of precision
aMan := mul(aMan, BASE_TO_THE_MAX_DIGITS_M)
aExp := sub(aExp, MAX_DIGITS_M)
}
}
if (Loperation) {
rMan = Uint512.div512x256(a0, a1, bMan);
unchecked {
rExp = (aExp + ZERO_OFFSET) - bExp;
}
} else {
assembly {
rMan := div(aMan, bMan)
rExp := sub(add(aExp, ZERO_OFFSET), bExp)
}
}
assembly {
if iszero(Loperation) {
let hasExtraDigit := gt(rMan, MAX_M_DIGIT_NUMBER)
if hasExtraDigit {
// we need to truncate the last digit
rExp := add(rExp, 1)
rMan := div(rMan, BASE)
}
}
if Loperation {
let hasExtraDigit := gt(rMan, MAX_L_DIGIT_NUMBER)
let maxExp := sub(sub(add(ZERO_OFFSET, MAXIMUM_EXPONENT), DIGIT_DIFF_L_M), hasExtraDigit)
Loperation := or(gt(rExp, maxExp), rL)
if and(Loperation, hasExtraDigit) {
// we need to truncate the last digit
rExp := add(rExp, 1)
rMan := div(rMan, BASE)
}
if iszero(Loperation) {
if hasExtraDigit {
rExp := add(rExp, DIGIT_DIFF_L_M_PLUS_1)
rMan := div(rMan, BASE_TO_THE_DIGIT_DIFF_PLUS_1)
}
if iszero(hasExtraDigit) {
rExp := add(rExp, DIGIT_DIFF_L_M)
rMan := div(rMan, BASE_TO_THE_DIGIT_DIFF)
}
}
}
r := or(xor(and(a, MANTISSA_SIGN_MASK), and(b, MANTISSA_SIGN_MASK)), or(rMan, shl(EXPONENT_BIT, rExp)))
if Loperation {
r := or(r, MANTISSA_L_FLAG_MASK)
}
}
}
/**
* @dev get the square root of a signed floating point
* @notice only positive numbers can have their square root calculated through this function
* @param a the numerator to get the square root of
* @return r the result of √a
*/
function sqrt(packedFloat a) internal pure returns (packedFloat r) {
if (packedFloat.unwrap(a) == 0) return a;
uint s;
int aExp;
uint x;
uint aMan;
uint256 roundedDownResult;
bool aL;
assembly {
if and(a, MANTISSA_SIGN_MASK) {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Selector for method Error(string)
mstore(add(ptr, 0x04), 0x20) // String offset
mstore(add(ptr, 0x24), 32) // Revert reason length
mstore(add(ptr, 0x44), "float128: squareroot of negative")
revert(ptr, 0x64) // Revert data length is 4 bytes for selector and 3 slots of 0x20 bytes
}
aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
aMan := and(a, MANTISSA_MASK)
aExp := shr(EXPONENT_BIT, a)
let isInvalid := 0
if iszero(aMan) {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 24)
mstore(add(ptr, 0x44), "float128: corrupted zero")
revert(ptr, 0x64)
}
if aL {
// Check if mantissa A has exactly 72 digits
isInvalid := or(isInvalid, or(lt(aMan, MIN_L_DIGIT_NUMBER), gt(aMan, MAX_L_DIGIT_NUMBER)))
}
if iszero(aL) {
// Check if mantissa A has exactly 38 digits
isInvalid := or(isInvalid, or(lt(aMan, MIN_M_DIGIT_NUMBER), gt(aMan, MAX_M_DIGIT_NUMBER)))
}
// Revert if validation fails
if isInvalid {
let ptr := mload(0x40)
mstore(ptr, 0x08c379a000000000000000000000000000000000000000000000000000000000)
mstore(add(ptr, 0x04), 0x20)
mstore(add(ptr, 0x24), 28)
mstore(add(ptr, 0x44), "float128: unnormalized float")
revert(ptr, 0x64)
}
}
if ((aL && aExp > int(ZERO_OFFSET) - int(DIGIT_DIFF_L_M - 1)) || (!aL && aExp > int(ZERO_OFFSET) - int(MAX_DIGITS_M / 2 - 1))) {
if (!aL) {
aMan *= BASE_TO_THE_DIGIT_DIFF;
aExp -= int(DIGIT_DIFF_L_M);
}
aExp -= int(ZERO_OFFSET);
if (aExp % 2 != 0) {
aMan *= BASE;
--aExp;
}
(uint a0, uint a1) = Uint512.mul256x256(aMan, BASE_TO_THE_MAX_DIGITS_L);
uint rMan = Uint512.sqrt512(a0, a1);
int rExp = aExp - int(MAX_DIGITS_L);
bool Lresult = true;
unchecked {
rExp = (rExp) / 2;
if (rMan > MAX_L_DIGIT_NUMBER) {
rMan /= BASE;
++rExp;
}
if (rExp <= MAXIMUM_EXPONENT - int(DIGIT_DIFF_L_M)) {
rMan /= BASE_TO_THE_DIGIT_DIFF;
rExp += int(DIGIT_DIFF_L_M);
Lresult = false;
}
rExp += int(ZERO_OFFSET);
}
assembly {
r := or(or(shl(EXPONENT_BIT, rExp), rMan), mul(Lresult, MANTISSA_L_FLAG_MASK))
}
}
// we need the exponent to be even so we can calculate the square root correctly
else {
assembly {
if iszero(mod(aExp, 2)) {
if aL {
x := mul(aMan, BASE_TO_THE_DIFF_76_L)
aExp := sub(aExp, DIGIT_DIFF_76_L)
}
if iszero(aL) {
x := mul(aMan, BASE_TO_THE_MAX_DIGITS_M)
aExp := sub(aExp, MAX_DIGITS_M)
}
}
if mod(aExp, 2) {
if aL {
x := mul(aMan, BASE_TO_THE_DIFF_76_L_PLUS_1)
aExp := sub(aExp, DIGIT_DIFF_76_L_PLUS_1)
}
if iszero(aL) {
x := mul(aMan, BASE_TO_THE_MAX_DIGITS_M_PLUS_1)
aExp := sub(aExp, MAX_DIGITS_M_PLUS_1)
}
}
s := 1
let xAux := x
let cmp := or(gt(xAux, 0x100000000000000000000000000000000), eq(xAux, 0x100000000000000000000000000000000))
xAux := sar(mul(cmp, 128), xAux)
s := shl(mul(cmp, 64), s)
cmp := or(gt(xAux, 0x10000000000000000), eq(xAux, 0x10000000000000000))
xAux := sar(mul(cmp, 64), xAux)
s := shl(mul(cmp, 32), s)
cmp := or(gt(xAux, 0x100000000), eq(xAux, 0x100000000))
xAux := sar(mul(cmp, 32), xAux)
s := shl(mul(cmp, 16), s)
cmp := or(gt(xAux, 0x10000), eq(xAux, 0x10000))
xAux := sar(mul(cmp, 16), xAux)
s := shl(mul(cmp, 8), s)
cmp := or(gt(xAux, 0x100), eq(xAux, 0x100))
xAux := sar(mul(cmp, 8), xAux)
s := shl(mul(cmp, 4), s)
cmp := or(gt(xAux, 0x10), eq(xAux, 0x10))
xAux := sar(mul(cmp, 4), xAux)
s := shl(mul(cmp, 2), s)
s := shl(mul(or(gt(xAux, 0x4), eq(xAux, 0x4)), 1), s)
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
s := shr(1, add(div(x, s), s))
roundedDownResult := div(x, s)
if or(gt(s, roundedDownResult), eq(s, roundedDownResult)) {
s := roundedDownResult
}
// exponent should now be half of what it was
aExp := add(div(sub(aExp, ZERO_OFFSET), 2), ZERO_OFFSET)
// if we have extra digits, we know it comes from the extra digit to make the exponent even
if gt(s, MAX_M_DIGIT_NUMBER) {
aExp := add(aExp, 1)
s := div(s, BASE)
}
// final encoding
r := or(shl(EXPONENT_BIT, aExp), s)
}
}
}
/**
* @dev performs a less than comparison
* @param a the first term
* @param b the second term
* @return retVal the result of a < b
*/
function lt(packedFloat a, packedFloat b) internal pure returns (bool retVal) {
if (packedFloat.unwrap(a) == packedFloat.unwrap(b)) return false;
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
let aNeg := gt(and(a, MANTISSA_SIGN_MASK), 0)
let bNeg := gt(and(b, MANTISSA_SIGN_MASK), 0)
let isAZero := iszero(a)
let isBZero := iszero(b)
let zeroFound := or(isAZero, isBZero)
if zeroFound {
if or(and(isAZero, and(iszero(isBZero), iszero(bNeg))), and(isBZero, aNeg)) {
retVal := true
}
}
if iszero(zeroFound) {
let aExp := and(a, EXPONENT_MASK)
let bExp := and(b, EXPONENT_MASK)
let aMan := and(a, MANTISSA_MASK)
let bMan := and(b, MANTISSA_MASK)
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
if xor(aNeg, bNeg) {
retVal := aNeg
}
if and(iszero(aNeg), iszero(bNeg)) {
if eq(aExp, bExp) {
retVal := lt(aMan, bMan)
}
if lt(aExp, bExp) {
retVal := true
}
}
if and(aNeg, bNeg) {
if eq(aExp, bExp) {
retVal := gt(aMan, bMan)
}
if gt(aExp, bExp) {
retVal := true
}
}
}
}
}
/**
* @dev performs a less than or equals to comparison
* @param a the first term
* @param b the second term
* @return retVal the result of a <= b
*/
function le(packedFloat a, packedFloat b) internal pure returns (bool retVal) {
if (packedFloat.unwrap(a) == packedFloat.unwrap(b)) return true;
assembly {
let aL := gt(and(a, MANTISSA_L_FLAG_MASK), 0)
let bL := gt(and(b, MANTISSA_L_FLAG_MASK), 0)
let aNeg := gt(and(a, MANTISSA_SIGN_MASK), 0)
let bNeg := gt(and(b, MANTISSA_SIGN_MASK), 0)
let isAZero := iszero(a)
let isBZero := iszero(b)
let zeroFound := or(isAZero, isBZero)
if zeroFound {
if or(and(isAZero, iszero(bNeg)), and(isBZero, aNeg)) {
retVal := true
}
}
if iszero(zeroFound) {
let aExp := and(a, EXPONENT_MASK)
let bExp := and(b, EXPONENT_MASK)
let aMan := and(a, MANTISSA_MASK)
let bMan := and(b, MANTISSA_MASK)
if iszero(aL) {
aMan := mul(aMan, BASE_TO_THE_DIGIT_DIFF)
aExp := sub(aExp, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
if iszero(bL) {
bMan := mul(bMan, BASE_TO_THE_DIGIT_DIFF)
bExp := sub(bExp, shl(EXPONENT_BIT, DIGIT_DIFF_L_M))
}
if xor(aNeg, bNeg) {
retVal := aNeg
}
if and(iszero(aNeg), iszero(bNeg)) {
if eq(aExp, bExp) {
retVal := lt(aMan, bMan)
}
if lt(aExp, bExp) {
retVal := true
}
}
if and(aNeg, bNeg) {
if eq(aExp, bExp) {
retVal := gt(aMan, bMan)
}
if gt(aExp, bExp) {
retVal := true
}
}
}
}
}
/**
* @dev performs a greater than comparison
* @param a the first term
* @param b the second term
* @return retVal the result of a > b
*/
function gt(packedFloat a, packedFloat b) internal pure returns (bool retVal) {
if (packedFloat.unwrap(a) == packedFloat.unwrap(...
// [truncated — 62452 bytes total]
Types.sol 11 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /**************************************************************************************************************************** * The mantissa can be in 2 sizes: M: 38 digits, or L: 72 digits * * Packed Float Bitmap: * * 255 ... EXPONENT ... 242, L_MATISSA_FLAG (241), MANTISSA_SIGN (240), 239 ... MANTISSA L..., 127 .. MANTISSA M ... 0 * * The exponent is signed using the offset zero to 8191. max values: -8192 and +8191. * ****************************************************************************************************************************/ type packedFloat is uint256;
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @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[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
IERC1363.sol 86 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}
Constants.sol 18 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @title Common Constants
* @author @oscarsernarosero @mpetersoCode55 @cirsteve
*/
contract Constants {
uint256 constant POOL_NATIVE_DECIMALS = 18;
uint256 constant FULL_NATIVE_TOKEN = 10 ** POOL_NATIVE_DECIMALS;
uint256 constant PERCENTAGE_DENOM = 10_000;
uint16 constant MAX_PROTOCOL_FEE = 20;
uint16 constant MAX_LP_FEE = 5_000 - MAX_PROTOCOL_FEE;
function getPoolConstants() public pure returns (uint, uint, uint, uint16, uint16) {
return (POOL_NATIVE_DECIMALS, FULL_NATIVE_TOKEN, PERCENTAGE_DENOM, MAX_PROTOCOL_FEE, MAX_LP_FEE);
}
}
Uint512.sol 228 lines
// SPDX-License-Identifier: GPLv3
pragma solidity ^0.8.24;
/**
* @title Uint512 Math Library
* @dev Ported from https://github.com/SimonSuckut/Solidity_Uint512 functions updated to internal for gas optimization and
* added new functions which are excerpts of the original functions for usage in other libraries
* @author @oscarsernarosero @mpetersoCode55 @cirsteve @Palmerg4
*/
library Uint512 {
/**
* @notice Calculates the product of two uint256
* @notice This method has been changed from the original Uint512 library: visibility changed from public to internal
* @dev Used the chinese remainder theoreme
* @param a A uint256 representing the first factor
* @param b A uint256 representing the second factor
* @return r0 The result as an uint512. r0 contains the lower bits
* @return r1 The higher bits of the result
*/
function mul256x256(uint256 a, uint256 b) internal pure returns (uint256 r0, uint256 r1) {
assembly {
let mm := mulmod(a, b, not(0))
r0 := mul(a, b)
r1 := sub(sub(mm, r0), lt(mm, r0))
}
}
/**
* @notice Calculates the division of a 512 bit unsigned integer by a 256 bit integer. It
* requires the result to fit in a 256 bit integer
* @notice This method has been changed from the original Uint512 library: visibility changed from public to internal
* @dev For a detailed explaination see:
* https://www.researchgate.net/publication/235765881_Efficient_long_division_via_Montgomery_multiply
* @param a0 A uint256 representing the low bits of the nominator
* @param a1 A uint256 representing the high bits of the nominator
* @param b A uint256 representing the denominator
* @return r The result as an uint256. Result must have at most 256 bit
*/
function div512x256(uint256 a0, uint256 a1, uint256 b) internal pure returns (uint256 r) {
assembly {
// calculate the remainder
let rem := mulmod(a1, not(0), b)
rem := addmod(rem, a1, b)
rem := addmod(rem, a0, b)
// subtract the remainder
a1 := sub(a1, lt(a0, rem))
a0 := sub(a0, rem)
// The integer space mod 2**256 is not an abilian group on the multiplication operation. In fact the
// multiplicative inserve only exists for odd numbers. The denominator gets shifted right until the
// least significant bit is 1. To do this we find the biggest power of 2 that devides the denominator.
let pow := and(sub(0, b), b)
// slither-disable-start divide-before-multiply
b := div(b, pow)
// Also shift the nominator. We only shift a0 and the lower bits of a1 which are transfered into a0
// by the shift operation. a1 no longer required for the calculation. This might sound odd, but in
// fact under the conditions that r < 2**255 and a / b = (r * a) + rem with rem = 0 the value of a1
// is uniquely identified. Thus the value is not needed for the calculation.
a0 := div(a0, pow)
pow := add(div(sub(0, pow), pow), 1)
a0 := or(a0, mul(a1, pow))
}
// if a1 is zero after the shifting, a single word division is sufficient
if (a1 == 0) return a0 / b;
assembly {
// Calculate the multiplicative inverse mod 2**256 of b. See the paper for details.
//slither-disable-next-line incorrect-exp
let inv := xor(mul(3, b), 2)
inv := mul(inv, sub(2, mul(b, inv)))
inv := mul(inv, sub(2, mul(b, inv)))
inv := mul(inv, sub(2, mul(b, inv)))
inv := mul(inv, sub(2, mul(b, inv)))
inv := mul(inv, sub(2, mul(b, inv)))
inv := mul(inv, sub(2, mul(b, inv)))
// slither-disable-end divide-before-multiply
r := mul(a0, inv)
}
}
/**
* @notice Calculates the square root of x, rounding down
* @notice This method has been changed from the original Uint512 library: visibility changed from public to internal
* @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
* @param x The uint256 number for which to calculate the square root
* @return s The square root as an uint256
*/
function sqrt256(uint256 x) internal pure returns (uint256 s) {
if (x == 0) return 0;
assembly {
s := 1
let xAux := x
let cmp := or(gt(xAux, 0x100000000000000000000000000000000), eq(xAux, 0x100000000000000000000000000000000))
xAux := sar(mul(cmp, 128), xAux)
s := shl(mul(cmp, 64), s)
cmp := or(gt(xAux, 0x10000000000000000), eq(xAux, 0x10000000000000000))
xAux := sar(mul(cmp, 64), xAux)
s := shl(mul(cmp, 32), s)
cmp := or(gt(xAux, 0x100000000), eq(xAux, 0x100000000))
xAux := sar(mul(cmp, 32), xAux)
s := shl(mul(cmp, 16), s)
cmp := or(gt(xAux, 0x10000), eq(xAux, 0x10000))
xAux := sar(mul(cmp, 16), xAux)
s := shl(mul(cmp, 8), s)
cmp := or(gt(xAux, 0x100), eq(xAux, 0x100))
xAux := sar(mul(cmp, 8), xAux)
s := shl(mul(cmp, 4), s)
cmp := or(gt(xAux, 0x10), eq(xAux, 0x10))
xAux := sar(mul(cmp, 4), xAux)
s := shl(mul(cmp, 2), s)
s := shl(mul(or(gt(xAux, 0x8), eq(xAux, 0x8)), 2), s)
}
unchecked {
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
s = (s + x / s) >> 1;
uint256 roundedDownResult = x / s;
return s >= roundedDownResult ? roundedDownResult : s;
}
}
/**
* @notice Calculates the square root of a 512 bit unsigned integer, rounding down
* @notice This method has been changed from the original Uint512 library: visibility changed from public to internal
* @dev Uses the Karatsuba Square Root method. See https://hal.inria.fr/inria-00072854/document for details
* @param a0 A uint256 representing the low bits of the input
* @param a1 A uint256 representing the high bits of the input
* @return s The square root as an uint256. Result has at most 256 bit
*/
function sqrt512(uint256 a0, uint256 a1) internal pure returns (uint256 s) {
// A simple 256 bit square root is sufficient
if (a1 == 0) return sqrt256(a0);
// The used algorithm has the pre-condition a1 >= 2**254
uint256 shift;
assembly {
let digits := mul(lt(a1, 0x100000000000000000000000000000000), 128)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x1000000000000000000000000000000000000000000000000), 64)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x100000000000000000000000000000000000000000000000000000000), 32)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x1000000000000000000000000000000000000000000000000000000000000), 16)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x100000000000000000000000000000000000000000000000000000000000000), 8)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x1000000000000000000000000000000000000000000000000000000000000000), 4)
a1 := shl(digits, a1)
shift := add(shift, digits)
digits := mul(lt(a1, 0x4000000000000000000000000000000000000000000000000000000000000000), 2)
a1 := shl(digits, a1)
shift := add(shift, digits)
a1 := or(a1, shr(sub(256, shift), a0))
a0 := shl(shift, a0)
}
uint256 sp = sqrt256(a1);
uint256 rp = a1 - (sp * sp);
uint256 nom;
uint256 denom;
uint256 u;
uint256 q;
assembly {
nom := or(shl(128, rp), shr(128, a0))
denom := shl(1, sp)
// slither-disable-start divide-before-multiply
q := div(nom, denom)
u := mod(nom, denom)
// The nominator can be bigger than 2**256. We know that rp < (sp+1) * (sp+1). As sp can be
// at most floor(sqrt(2**256 - 1)) we can conclude that the nominator has at most 513 bits
// set. An expensive 512x256 bit division can be avoided by treating the bit at position 513 manually
let carry := shr(128, rp)
let x := mul(carry, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
q := add(q, div(x, denom))
u := add(u, add(carry, mod(x, denom)))
q := add(q, div(u, denom))
u := mod(u, denom)
}
unchecked {
s = (sp << 128) + q;
uint256 rl = ((u << 128) | (a0 & 0xffffffffffffffffffffffffffffffff));
uint256 rr = q * q;
// slither-disable-end divide-before-multiply
if ((q >> 128) > (u >> 128) || (((q >> 128) == (u >> 128)) && rl < rr)) {
s = s - 1;
}
return s >> (shift / 2);
}
}
}
IERC20.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
IERC165.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";
Read Contract
MAX_PROTOCOL_FEE 0xb8ca3b83 → uint16
VERSION 0xffa1ad74 → string
gateDeployers 0xe9a2c293 → bool
gateYTokens 0x2c240f9e → bool
getDeployerAllowList 0xdc3327d1 → address
getYTokenAllowList 0x462a4352 → address
isByteCodeImmutable 0x056d1528 → bool
lpTokenAddress 0xf5ae497a → address
owner 0x8da5cb5b → address
pendingOwner 0xe30c3978 → address
proposedProtocolFeeCollector 0xae4e7a14 → address
protocolFee 0xb0e21e8a → uint16
protocolFeeCollector 0x850a1501 → address
renounceOwnership 0x715018a6
Write Contract 14 functions
These functions modify contract state and require a wallet transaction to execute.
acceptLPTokenRole 0x42ed82d4
No parameters
acceptOwnership 0x79ba5097
No parameters
confirmProtocolFeeCollector 0x014e8e90
No parameters
createPool 0x3a7117d1
address _xToken
address _yToken
uint16 _lpFee
tuple _tbcInput
uint256 _xAdd
uint256 _wInactive
returns: address
makeByteCodeImmutable 0x164efc42
No parameters
proposeProtocolFeeCollector 0xbd2e2a2d
address _protocolFeeCollector
setByteCode 0x25ff967e
bytes _byteCode
setDeployerAllowList 0xd4ce6dc5
address _address
setGateDeployers 0xbcdfd7c2
bool _gateDeployers
setGateYTokens 0x514afc1a
bool _gateYTokens
setLPTokenAddress 0xf42ebbe0
address LPTokenAddress
setProtocolFee 0xe4467f35
uint16 _protocolFee
setYTokenAllowList 0xe41b8868
address _address
transferOwnership 0xf2fde38b
address newOwner
Recent Transactions
No transactions found for this address