Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x787ccd7fCD64d35E34DD7c16a2C6604755eecB76
Balance 0 ETH
Nonce 1
Code Size 4963 bytes
Indexed Transactions 0
External Etherscan · Sourcify

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