FRAX Price: $1.03 (+6.37%)

Contract

0xBfAc23cb2007ee377bd1bf03edF376f5D532f058

Overview

FRAX Balance | FXTL Balance

0 FRAX | 11,595 FXTL

FRAX Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Age:180D
Reset Filter

Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction and > 10 Token Transfers found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
253390942025-09-10 7:21:39136 days ago1757488899  Contract Creation0 FRAX

Cross-Chain Transactions
Loading...
Loading

Minimal Proxy Contract for 0xbe852f3b38f65d5e251a3127c25dc21fa9a9129e

Contract Name:
EchoCampaign

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

// ███████╗ ██████╗██╗  ██╗ ██████╗   ███╗   ███╗ █████╗ ██████╗ ██╗  ██╗███████╗████████╗
// ██╔════╝██╔════╝██║  ██║██╔═══██╗  ████╗ ████║██╔══██╗██╔══██╗██║ ██╔╝██╔════╝╚══██╔══╝
// █████╗  ██║     ███████║██║   ██║  ██╔████╔██║███████║██████╔╝█████╔╝ █████╗     ██║
// ██╔══╝  ██║     ██╔══██║██║   ██║  ██║╚██╔╝██║██╔══██║██╔══██╗██╔═██╗ ██╔══╝     ██║
// ███████╗╚██████╗██║  ██║╚██████╔╝  ██║ ╚═╝ ██║██║  ██║██║  ██║██║  ██╗███████╗   ██║
// ╚══════╝ ╚═════╝╚═╝  ╚═╝ ╚═════╝   ╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝   ╚═╝
// ================================= EchoCampaign V2 =================================
// =================================== Summer 2025 ===================================

import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IEchoAdministration} from "../../interfaces/IEchoAdministration.sol";
import {IEchoCampaignFactory} from "../../interfaces/IEchoCampaignFactory.sol";
import {IEchoContents} from "../../interfaces/IEchoContents.sol";
import {IEchoCampaign, QualificationData, BudgetInfo, SocialKPIs, QA_METHOD, Status, Finalizer, Token, PostInfo, BasicInfo, CampaignInitData, KPI} from "../../interfaces/IEchoCampaign.sol";
import {Clone} from "../../libs/Clone.sol";
import {UD60x18, ud, unwrap} from "@prb/math/src/UD60x18.sol";

/// @title Echo Campaign contract
/// @author Dynabits.org
contract EchoCampaign is IEchoCampaign, Clone {
    /********************************\
    |-*-*-*-*-*   STATES   *-*-*-*-*-|
    \********************************/
    Status private _status;
    bool private no_mid_refunding;
    bool private _campaignPaused;
    uint232 private _totalPosts;
    uint256 private _totalEffectiveKPIs;
    uint256 private _totalEligibleContents;
    uint256 private _totalPaidAmount;
    string private _ipfsCID;
    BudgetInfo private _budgetInfo;
    SocialKPIs private _socialKPIs;
    QualificationData[] private _qaData;
    mapping(string => bool) public registeredActionKind;
    mapping(string => uint256) public contentEffectiveKPI;

    /*******************************\
    |-*-*-*-*   CONSTANTS   *-*-*-*-|
    \*******************************/
    address private immutable FRAX_USD;
    // FOR PRODUCTION
    // 0xFc00000000000000000000000000000000000001;
    // FOR TESTNET
    // 0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E;
    // FOR LOCAL TEST
    // 0xecE1B93b2dA75C6EE3b78D158416df18Ae9FEE15;

    /********************************\
    |-*-*-*-*-*   EVENTS   *-*-*-*-*-|
    \********************************/
    event CampaignPauseToggled(bool indexed paused, address campaign);
    event BudgetIncreased(
        uint256 indexed addedAmount,
        uint256 indexed lastBudget,
        uint256 indexed newBudget,
        address donator,
        address campaign
    );
    event CampaignFinalized(
        address indexed finalizer,
        Finalizer indexed finalizerType,
        uint256 indexed refundedAmount,
        uint256 newBudget,
        address campaign
    );
    event Claimed(
        address indexed contentCreator,
        uint256 indexed rewardedAmount,
        uint256 indexed refundAmount,
        uint256 totalPaidAmount,
        string contentLink,
        address campaign
    );
    event CampaignFinished(uint256 indexed finishedAt, address campaign);

    /********************************\
    |-*-*-*-*-*   ERRORS   *-*-*-*-*-|
    \********************************/
    error ONLY_FACTORY();
    error ONLY_CONTENTS();
    error ONLY_PROTOCOL_ADMIN();
    error ONLY_OWNER();
    error ONLY_FINALIZER_ORACLE();
    error CAMPAIGN_PAUSED();
    error INVALID_CONTENT_LINK();
    error NO_CONTENT_AUTHOR();
    error DUPLICATE_QA(string qa);
    error DUPLICATE_KPI(string kpi);
    error ONLY_IN_PROGRESS_STATUS(Status status);
    error ONLY_UPCOMING_OR_IN_PROGRESS_STATUS(Status status);
    error ONLY_FINISHED_CAMPAIGN_STATUS();
    error ONLY_CLAIMABLE_STATUS();
    error NOT_ELIGIBLE_FOR_WITHDRAW();
    error CHECK_QAS_OR_KPIS();
    error GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(string qaKind);
    error GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(string kpiKind);

    /*******************************\
    |-*-*-*-*   MODIFIERS   *-*-*-*-|
    \*******************************/
    modifier onlyContents() {
        if (msg.sender != contents()) revert ONLY_CONTENTS();
        _;
    }

    modifier onlyProtocolAdmin() {
        if (msg.sender != IEchoAdministration(admin()).protocolAdmin())
            revert ONLY_PROTOCOL_ADMIN();
        _;
    }

    modifier onlyOwner() {
        if (msg.sender != owner()) revert ONLY_OWNER();

        _;
    }

    modifier onlyFinalizerOracle() {
        (, , , address finalizerOracle) = IEchoAdministration(admin())
            .modelData(
                address(1),
                keccak256(abi.encodePacked("CONFIG: Finalizer"))
            );

        if (msg.sender != finalizerOracle) revert ONLY_FINALIZER_ORACLE();
        _;
    }

    modifier onlyInProgressStatus() {
        if (currentStatus() != Status.inProgress)
            revert ONLY_IN_PROGRESS_STATUS(currentStatus());

        _;
    }

    /******************************\
    |-*-*-*-*   BUILT-IN   *-*-*-*-|
    \******************************/
    constructor() {
        if (block.chainid == 252)
            FRAX_USD = 0xFc00000000000000000000000000000000000001;
        else if (block.chainid == 2522)
            FRAX_USD = 0xa35Bd0843e49F4eCF7Fb716FDADdc86f4482dB8E;
        else FRAX_USD = 0xecE1B93b2dA75C6EE3b78D158416df18Ae9FEE15;
    }

    /// @inheritdoc IEchoCampaign
    function init(
        string calldata ipfsCID,
        BudgetInfo calldata budgetInfo_,
        SocialKPIs calldata socialKPIs_,
        QualificationData[] calldata qaData_
    ) external {
        // Ensure only the factory contract can initialize.
        if (msg.sender != factory()) revert ONLY_FACTORY();

        // Store campaign metadata.
        _ipfsCID = ipfsCID;
        _budgetInfo = budgetInfo_;
        _socialKPIs = socialKPIs_;

        uint256 i;
        string memory action;

        // Register Qualification Actions.
        while (i < qaData_.length) {
            _qaData.push(qaData_[i]);

            // Determine the QA method prefix.
            if (qaData_[i].method == QA_METHOD.AI) action = "QA-AI: ";
            else if (qaData_[i].method == QA_METHOD.COMMUNITY)
                action = "QA-C: ";
            else action = "QA-V: ";

            // Revert the operation if a duplicate qualification is detected.
            if (registeredActionKind[string.concat(action, qaData_[i].kind)])
                revert DUPLICATE_QA(string.concat(action, qaData_[i].kind));

            // Register the Qualification.
            registeredActionKind[string.concat(action, qaData_[i].kind)] = true;

            unchecked {
                i++;
            }
        }

        // Register Social KPI Actions.
        delete i;
        action = string.concat(socialKPIs_.social, ": ");

        while (i < socialKPIs_.kpis.length) {
            // Revert the operation if a duplicate KPI is detected.
            if (
                registeredActionKind[
                    string.concat(action, socialKPIs_.kpis[i].method)
                ]
            )
                revert DUPLICATE_KPI(
                    string.concat(action, socialKPIs_.kpis[i].method)
                );

            // Register the SocialKPI.
            registeredActionKind[
                string.concat(action, socialKPIs_.kpis[i].method)
            ] = true;

            unchecked {
                i++;
            }
        }
    }

    /********************************\
    |-*-*-*   ADMINISTRATION   *-*-*-|
    \********************************/
    /// @inheritdoc IEchoCampaign
    function togglePauseCampaign() external onlyProtocolAdmin {
        // Toggle the paused state of the campaign.
        _campaignPaused = !_campaignPaused;

        // Emit an event to notify the state change.
        emit CampaignPauseToggled(_campaignPaused, address(this));
    }

    /// @inheritdoc IEchoCampaign
    function finalizeInProgressCampaign()
        external
        onlyProtocolAdmin
        onlyInProgressStatus
    {
        // Calculate and refund the possible remaining budget.
        uint256 refundedAmount = _refundPossibleAmountLeft();

        // Emit event indicating campaign finalization.
        emit CampaignFinalized(
            msg.sender,
            Finalizer.protocolAdmin,
            refundedAmount,
            _budgetInfo.reservedAmount,
            address(this)
        );
    }

    /// @notice Recover ERC20 tokens
    /// @param tokenAddress address of ERC20 token
    /// @param tokenAmount amount to be withdrawn
    function recoverERC20(address tokenAddress, uint256 tokenAmount)
        external
        onlyProtocolAdmin
    {
        // bytes4(keccak256(bytes('transfer(address,uint256)')));
        (bool success, bytes memory data) = tokenAddress.call(
            abi.encodeWithSelector(0xa9059cbb, msg.sender, tokenAmount)
        );
        require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "TransferHelper: TRANSFER_FAILED"
        );
    }

    /*******************************\
    |*-*-*-*   EXTERNALS   *-*-*-*-*|
    \*******************************/
    /// @inheritdoc IEchoCampaign
    function increaseBudget(uint256 amount) external {
        // Ensure the campaign is in an `upcoming` or `inProgress` status.
        if (
            currentStatus() != Status.upcoming &&
            currentStatus() != Status.inProgress
        ) revert ONLY_UPCOMING_OR_IN_PROGRESS_STATUS(currentStatus());

        // Transfer the funds from sender to contract.
        IERC20(_budgetInfo.token).transferFrom(
            msg.sender,
            address(this),
            amount
        );

        // Update the reserved budget amount.
        unchecked {
            _budgetInfo.reservedAmount += uint128(amount);
        }

        // Emit event notifying budget increase.
        emit BudgetIncreased(
            amount,
            _budgetInfo.reservedAmount - amount,
            _budgetInfo.reservedAmount,
            msg.sender,
            address(this)
        );
    }

    /// @inheritdoc IEchoCampaign
    function applyContent(string calldata contentLink)
        external
        onlyInProgressStatus
    {
        // Ensure the content link is not empty.
        if (bytes(contentLink).length == 0) revert INVALID_CONTENT_LINK();

        // Calculate the application fee.
        uint256 totalFee = applicationFee();

        // Transfer the application fee to protocol admin.
        if (totalFee != 0)
            IERC20(FRAX_USD).transferFrom(
                msg.sender,
                IEchoAdministration(admin()).protocolAdmin(),
                totalFee
            );

        // Increment the total posts count.
        _totalPosts++;

        // Register the content link in the EchoContents contract.
        IEchoContents(contents()).addContentLink(
            contentLink,
            _budgetInfo.maxPerPost
        );
    }

    /// @inheritdoc IEchoCampaign
    function finalizeFinishedCampaign() external onlyFinalizerOracle {
        // Ensure campaign is in the 'finished' status.
        if (currentStatus() != Status.finished)
            revert ONLY_FINISHED_CAMPAIGN_STATUS();

        // Calculate and refund the remaining budget.
        uint256 refundedAmount = _refundPossibleAmountLeft();

        // Emit event notifying campaign finalization.
        emit CampaignFinalized(
            msg.sender,
            Finalizer.finalizerOracle,
            refundedAmount,
            _budgetInfo.reservedAmount,
            address(this)
        );
    }

    /// @inheritdoc IEchoCampaign
    function updateKPIs(
        string calldata contentLink,
        uint256 additionalContentEffectiveKPI,
        bool additional
    )
        external
        onlyContents
        returns (
            uint256 totalEffectiveKPIs,
            uint256 contentEffectiveKPI_,
            uint256 totalEligibleContents,
            int8 eligibilityPolarity
        )
    {
        // If this is the first KPI update for the content, increase the eligible content count.
        if (contentEffectiveKPI[contentLink] == 0) {
            _totalEligibleContents++;
            eligibilityPolarity = 1;
        }

        // Modify KPI based on the 'additional' flag.
        if (additional) {
            _totalEffectiveKPIs += additionalContentEffectiveKPI;
            contentEffectiveKPI[contentLink] += additionalContentEffectiveKPI;
        } else {
            _totalEffectiveKPIs -= additionalContentEffectiveKPI;
            contentEffectiveKPI[contentLink] -= additionalContentEffectiveKPI;

            if (contentEffectiveKPI[contentLink] == 0) {
                _totalEligibleContents--;
                eligibilityPolarity--;
            }
        }

        // Return updated KPI values.
        totalEffectiveKPIs = _totalEffectiveKPIs;
        contentEffectiveKPI_ = contentEffectiveKPI[contentLink];
        totalEligibleContents = _totalEligibleContents;
    }

    /// @inheritdoc IEchoCampaign
    function claim(string calldata contentLink) external {
        // Ensure the campaign is in a claimable state.
        if (_status != Status.claimable) revert ONLY_CLAIMABLE_STATUS();

        // Retrieve content registration details and author's action kind.
        (bool contentRegistered, , , uint256 actionKindRes) = IEchoContents(
            contents()
        ).contentToCampaign(address(this), contentLink, "CONFIG: AuthorWallet");

        // Revert if the content is not registered under this campaign.
        if (!contentRegistered) revert INVALID_CONTENT_LINK();

        // Ensure the content has an associated author registered by the oracle.
        if (actionKindRes == 0) revert NO_CONTENT_AUTHOR();

        // Check if the content has an effective KPI score and is eligible for rewards.
        if (contentEffectiveKPI[contentLink] == 0)
            revert NOT_ELIGIBLE_FOR_WITHDRAW();

        // Calculate the reward amount based on the content's KPI ratio.
        uint256 contentKPIratio = (contentEffectiveKPI[contentLink] * 1e6) /
            _totalEffectiveKPIs;

        uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
            1e6;
        uint256 refundAmount;

        // Adjust reserved budget and determine refund amount if applicable.
        if (_budgetInfo.maxPerPost == 0) {
            if (_totalEligibleContents == 1)
                rewardAmount = _budgetInfo.reservedAmount - _totalPaidAmount;
        } else {
            if (rewardAmount >= _budgetInfo.maxPerPost)
                rewardAmount = _budgetInfo.maxPerPost;

            if (_totalEligibleContents != 1) {
                if (!no_mid_refunding && rewardAmount < _budgetInfo.maxPerPost)
                    refundAmount = _budgetInfo.maxPerPost - rewardAmount;
            } else {
                if (
                    rewardAmount < _budgetInfo.reservedAmount - _totalPaidAmount
                )
                    refundAmount =
                        (_budgetInfo.reservedAmount - _totalPaidAmount) -
                        rewardAmount;
            }

            // Refund excess funds if applicable.
            if (refundAmount != 0) {
                IERC20(_budgetInfo.token).transfer(
                    refundAddress(),
                    refundAmount
                );
            }
        }

        // Transfer the calculated reward to the content's registered author.
        IERC20(_budgetInfo.token).transfer(
            address(uint160(actionKindRes)),
            rewardAmount
        );

        // Remove the content's KPI score tracking and decrement the eligible content counter.
        delete contentEffectiveKPI[contentLink];
        _totalEligibleContents--;
        _totalPaidAmount += rewardAmount + refundAmount;

        // Emit an event to log the claim transaction.
        emit Claimed(
            address(uint160(actionKindRes)),
            rewardAmount,
            refundAmount,
            _totalPaidAmount,
            contentLink,
            address(this)
        );

        // If all eligible claims have been processed, finalize the campaign.
        if (_totalEligibleContents == 0) {
            _status = Status.finalized;
            delete _budgetInfo.reservedAmount;
            emit CampaignFinished(block.timestamp, address(this));
        }
    }

    /*****************************\
    |-*-*-*-*   GETTERS   *-*-*-*-|
    \*****************************/
    /// @inheritdoc IEchoCampaign
    function budgetInfo() external view returns (BudgetInfo memory) {
        return _budgetInfo;
    }

    /// @inheritdoc IEchoCampaign
    function socialKPIs() external view returns (SocialKPIs memory) {
        return _socialKPIs;
    }

    /// @inheritdoc IEchoCampaign
    function qaData() external view returns (QualificationData[] memory) {
        return _qaData;
    }

    /// @inheritdoc IEchoCampaign
    function rewardEstimation(
        QualificationData[] calldata qas,
        KPI[] calldata kpis
    ) external view returns (uint256) {
        // Validate input arrays to ensure they are non-empty
        // and match expected campaign `_qaData` & `_socialKPIs` lengths.
        if (
            qas.length == 0 ||
            kpis.length == 0 ||
            qas.length != _qaData.length ||
            kpis.length != _socialKPIs.kpis.length
        ) revert CHECK_QAS_OR_KPIS();

        uint256 i;
        UD60x18 contentQaScore = ud(1e18);
        string memory action;

        // Determine the QA method prefix for action identification.
        if (qas[i].method == QA_METHOD.AI) action = "QA-AI: ";
        else if (qas[i].method == QA_METHOD.COMMUNITY) action = "QA-C: ";
        else action = "QA-V: ";

        // Calculate the content QA score based on provided qualifications.
        while (i < qas.length) {
            if (qas[i].pct == 0) break;

            // Ensure the QA kind exists within the campaign.
            if (!registeredActionKind[string.concat(action, qas[i].kind)])
                revert GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN(
                    string.concat(action, qas[i].kind)
                );

            // Note: There is no explicit check for duplicate QA evaluations.

            contentQaScore = contentQaScore.mul(
                ud(qas[i].pct * 1e13).pow(ud(_qaData[i].pct * 1e16))
            );

            unchecked {
                i++;
            }
        }

        // If the loop did not process all QAs, return zero as a no reward case.
        if (i != qas.length) return 0;

        delete i;
        action = string.concat(_socialKPIs.social, ": ");
        UD60x18 contentKPIscore = ud(0);

        // Calculate the content KPI score based on provided KPIs.
        while (i < kpis.length) {
            // Ensure the KPI kind exists within the campaign.
            if (!registeredActionKind[string.concat(action, kpis[i].method)])
                revert GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN(
                    string.concat(action, kpis[i].method)
                );

            // Note: There is no explicit check to ensure KPIs are in the correct order.
            // It is assumed they are provided in the correct order.

            // Ensure that the provided KPI meets the required minimum threshold.
            // If the submitted KPI value is lower than the predefined minimum,
            // set `contentKPIscore` to zero and exit the loop as a no reward case.
            if (_socialKPIs.kpis[i].min > kpis[i].min) {
                contentKPIscore = ud(0);
                break;
            }

            // Accumulate the KPI score based on predefined percentages and the KPI ratio.
            contentKPIscore = contentKPIscore.add(
                ud(uint256(_socialKPIs.kpis[i].pct) * 1e16).mul(
                    ud(kpis[i].min).div(ud(_socialKPIs.kpis[i].ratio))
                )
            );

            unchecked {
                i++;
            }
        }

        if (i != kpis.length) return 0;

        // Compute the final effective KPI score.
        uint256 effectiveKPI = (contentQaScore.mul(contentKPIscore)).unwrap();

        // Calculate the content KPI ratio relative to the total effective KPIs.
        uint256 contentKPIratio = (effectiveKPI * 1e6) /
            (_totalEffectiveKPIs + effectiveKPI);

        // Determine the reward amount based on the reserved campaign budget.
        uint256 rewardAmount = (_budgetInfo.reservedAmount * contentKPIratio) /
            1e6;

        // Ensure the reward does not exceed the maximum allowed per post.
        if (
            _budgetInfo.maxPerPost != 0 &&
            rewardAmount >= _budgetInfo.maxPerPost
        ) {
            rewardAmount = _budgetInfo.maxPerPost;
        }

        return rewardAmount;
    }

    /// @inheritdoc IEchoCampaign
    function details()
        external
        view
        returns (
            BasicInfo memory basicInfo_,
            CampaignInitData memory initData_,
            BudgetInfo memory budgetInfo_,
            Token memory token_,
            SocialKPIs memory socialKPIs_,
            QualificationData[] memory qaData_,
            PostInfo memory postInfo_,
            Status status_
        )
    {
        basicInfo_ = BasicInfo(
            name(),
            _ipfsCID,
            owner(),
            startTime(),
            endTime()
        );
        initData_ = IEchoCampaignFactory(factory()).campaignNameToInitData(
            name()
        );
        budgetInfo_ = _budgetInfo;
        token_ = Token(
            IERC20Metadata(_budgetInfo.token).name(),
            IERC20Metadata(_budgetInfo.token).symbol(),
            IERC20Metadata(_budgetInfo.token).decimals()
        );
        socialKPIs_ = _socialKPIs;
        qaData_ = _qaData;

        (uint256 lastPostTime, uint256 lastUpdatedTime) = IEchoContents(
            contents()
        ).campaignContentInfo(address(this));

        postInfo_ = PostInfo(
            _totalPosts,
            _totalEligibleContents,
            _totalEffectiveKPIs,
            lastPostTime,
            lastUpdatedTime,
            applicationFee()
        );
        status_ = currentStatus();
    }

    /// @inheritdoc IEchoCampaign
    function getContentCreator(string calldata contentLink)
        external
        view
        returns (address)
    {
        (bool contentRegistered, , , uint256 contentCreator) = IEchoContents(
            contents()
        ).contentToCampaign(address(this), contentLink, "CONFIG: AuthorWallet");

        if (!contentRegistered) revert INVALID_CONTENT_LINK();

        return address(uint160(contentCreator));
    }

    /// @inheritdoc IEchoCampaign
    function currentStatus() public view returns (Status) {
        // If the status has been explicitly set to claimable or finalized, return it directly.
        if (_status == Status.claimable || _status == Status.finalized)
            return _status;

        // If the current time is before the campaign start, mark it as upcoming.
        if (block.timestamp <= startTime()) return Status.upcoming;

        // If the campaign is active (between start and end time) and not finalized.
        if (block.timestamp > startTime() && block.timestamp <= endTime())
            if (_campaignPaused) return Status.paused;
            else return Status.inProgress;

        // If the current time is past the campaign end, mark it as finished.
        return Status.finished;
    }

    /// @inheritdoc IEchoCampaign
    function applicationFee() public view returns (uint256) {
        // Get the total number of qualification data entries.
        uint256 length = _qaData.length;
        uint256 i;
        uint256 totalFee;
        uint256 tempFee;
        string memory action;

        // Iterate through each qualification data entry to compute its respective oracle fee.
        while (i < length) {
            // Determine the action type based on the QA method.
            if (_qaData[i].method == QA_METHOD.AI) action = "QA-AI: ";
            else if (_qaData[i].method == QA_METHOD.COMMUNITY)
                action = "QA-C: ";
            else action = "QA-V: ";

            // Fetch the fee associated with the given QA kind from the administration contract.
            (, , tempFee, ) = IEchoAdministration(admin()).modelData(
                _qaData[i].oracle,
                keccak256(abi.encodePacked(action, _qaData[i].kind))
            );

            if (tempFee == 0 || tempFee == 1) delete tempFee;

            unchecked {
                // Accumulate the fee.
                totalFee += tempFee;
                i++;
            }
        }

        // Reset iteration variables for the next calculation.
        delete i;
        length = _socialKPIs.kpis.length;
        action = string.concat(_socialKPIs.social, ": ");

        // Iterate through each social KPI to compute its respective fee.
        while (i < length) {
            // Fetch the fee associated with the KPI from the administration contract.
            (, , tempFee, ) = IEchoAdministration(admin()).modelData(
                address(0),
                keccak256(abi.encodePacked(action, _socialKPIs.kpis[i].method))
            );

            if (tempFee == 0 || tempFee == 1) delete tempFee;

            unchecked {
                // Accumulate the fee.
                totalFee += tempFee;
                i++;
            }
        }

        // Return the total accumulated fee for both QA data and social KPIs.
        return totalFee;
    }

    /// @inheritdoc IEchoCampaign
    function name() public view returns (string memory) {
        return
            IEchoCampaignFactory(factory()).campaignNameHashToName(nameHash());
    }

    /// @inheritdoc IEchoCampaign
    function contents() public view returns (address) {
        return IEchoCampaignFactory(factory()).ECHO_CONTENTS();
    }

    /// @inheritdoc IEchoCampaign
    function admin() public pure returns (address) {
        return _getArgAddress(0);
    }

    /// @inheritdoc IEchoCampaign
    function factory() public pure returns (address) {
        return _getArgAddress(20);
    }

    /// @inheritdoc IEchoCampaign
    function owner() public pure returns (address) {
        return _getArgAddress(40);
    }

    /// @inheritdoc IEchoCampaign
    function refundAddress() public pure returns (address) {
        return _getArgAddress(60);
    }

    /// @inheritdoc IEchoCampaign
    function nameHash() public pure returns (bytes32) {
        return bytes32(_getArgUint256(80));
    }

    /// @inheritdoc IEchoCampaign
    function startTime() public pure returns (uint256) {
        return _getArgUint256(112);
    }

    /// @inheritdoc IEchoCampaign
    function endTime() public pure returns (uint256) {
        return _getArgUint256(144);
    }

    /*****************************\
    |-*-*-*-*   PRIVATE   *-*-*-*-|
    \*****************************/
    function _refundPossibleAmountLeft() private returns (uint128) {
        uint128 refundAmount;
        _status = Status.claimable;

        if (
            _budgetInfo.maxPerPost != 0 &&
            _totalEligibleContents * _budgetInfo.maxPerPost <
            _budgetInfo.reservedAmount
        ) {
            refundAmount = uint128(
                _budgetInfo.reservedAmount -
                    (_totalEligibleContents * _budgetInfo.maxPerPost)
            );

            IERC20(_budgetInfo.token).transfer(refundAddress(), refundAmount);

            _budgetInfo.reservedAmount -= refundAmount;
            return refundAmount;
        }

        if (_budgetInfo.maxPerPost == 0 && _totalEligibleContents == 0) {
            IERC20(_budgetInfo.token).transfer(
                owner(),
                _budgetInfo.reservedAmount
            );

            delete _budgetInfo.reservedAmount;
            return refundAmount;
        }

        no_mid_refunding = true;
        return refundAmount;
    }
}

File 2 of 31 : UD60x18.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

/*

██████╗ ██████╗ ██████╗ ███╗   ███╗ █████╗ ████████╗██╗  ██╗
██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║  ██║
██████╔╝██████╔╝██████╔╝██╔████╔██║███████║   ██║   ███████║
██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║   ██║   ██╔══██║
██║     ██║  ██║██████╔╝██║ ╚═╝ ██║██║  ██║   ██║   ██║  ██║
╚═╝     ╚═╝  ╚═╝╚═════╝ ╚═╝     ╚═╝╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝

██╗   ██╗██████╗  ██████╗  ██████╗ ██╗  ██╗ ██╗ █████╗
██║   ██║██╔══██╗██╔════╝ ██╔═████╗╚██╗██╔╝███║██╔══██╗
██║   ██║██║  ██║███████╗ ██║██╔██║ ╚███╔╝ ╚██║╚█████╔╝
██║   ██║██║  ██║██╔═══██╗████╔╝██║ ██╔██╗  ██║██╔══██╗
╚██████╔╝██████╔╝╚██████╔╝╚██████╔╝██╔╝ ██╗ ██║╚█████╔╝
 ╚═════╝ ╚═════╝  ╚═════╝  ╚═════╝ ╚═╝  ╚═╝ ╚═╝ ╚════╝

*/

import "./ud60x18/Casting.sol";
import "./ud60x18/Constants.sol";
import "./ud60x18/Conversions.sol";
import "./ud60x18/Errors.sol";
import "./ud60x18/Helpers.sol";
import "./ud60x18/Math.sol";
import "./ud60x18/ValueType.sol";

// SPDX-License-Identifier: BSD
pragma solidity 0.8.30;

/// @title Clone
/// @author zefram.eth
/// @notice Provides helper functions for reading immutable args from calldata
contract Clone {
    /// @notice Reads an immutable arg with type address
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgAddress(
        uint256 argOffset
    ) internal pure returns (address arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0x60, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint256
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint256(
        uint256 argOffset
    ) internal pure returns (uint256 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := calldataload(add(offset, argOffset))
        }
    }

    /// @notice Reads a uint256 array stored in the immutable args.
    /// @param argOffset The offset of the arg in the packed data
    /// @param arrLen Number of elements in the array
    /// @return arr The array
    function _getArgUint256Array(
        uint256 argOffset,
        uint64 arrLen
    ) internal pure returns (uint256[] memory arr) {
        uint256 offset = _getImmutableArgsOffset();
        uint256 el;
        arr = new uint256[](arrLen);
        for (uint64 i = 0; i < arrLen; i++) {
            // solhint-disable-next-line no-inline-assembly
            assembly {
                el := calldataload(add(add(offset, argOffset), mul(i, 32)))
            }
            arr[i] = el;
        }
        return arr;
    }

    /// @notice Reads an immutable arg with type uint64
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint64(
        uint256 argOffset
    ) internal pure returns (uint64 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xc0, calldataload(add(offset, argOffset)))
        }
    }

    /// @notice Reads an immutable arg with type uint8
    /// @param argOffset The offset of the arg in the packed data
    /// @return arg The arg value
    function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) {
        uint256 offset = _getImmutableArgsOffset();
        // solhint-disable-next-line no-inline-assembly
        assembly {
            arg := shr(0xf8, calldataload(add(offset, argOffset)))
        }
    }

    /// @return offset The offset of the packed immutable args in calldata
    function _getImmutableArgsOffset() internal pure returns (uint256 offset) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            offset := sub(
                calldatasize(),
                add(shr(240, calldataload(sub(calldatasize(), 2))), 2)
            )
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

/***************************\
|*-*-*-*    TYPES    *-*-*-*|
\***************************/

enum QA_METHOD {
    AI,
    COMMUNITY,
    VOTE
}

enum Status {
    upcoming,
    inProgress,
    paused,
    finished,
    claimable,
    finalized
}

enum Finalizer {
    protocolAdmin,
    finalizerOracle
}

struct CampaignInitData {
    uint64 initTime;
    address clonedCampaign;
    address implementation;
}

struct BasicInfo {
    string name;
    string ipfsCID;
    address owner;
    uint256 startTime;
    uint256 endTime;
}

struct PostInfo {
    uint256 totalPosts;
    uint256 totalEligibleContents;
    uint256 totalEffectiveKPIs;
    uint256 lastPostTime;
    uint256 lastUpdatedTime;
    uint256 applicationFee;
}

struct Token {
    string name;
    string symbol;
    uint256 decimals;
}

struct BudgetInfo {
    address token;
    uint128 maxPerPost;
    uint128 reservedAmount;
}

struct PostData {
    string postLink;
    address applicant;
}

struct QualificationData {
    QA_METHOD method;
    uint88 pct;
    address oracle;
    string kind;
}

struct KPI {
    uint8 pct;
    uint248 min;
    uint256 ratio;
    string method;
    string extra;
}

struct SocialKPIs {
    string social;
    KPI[] kpis;
}

/**
 * @title IEchoCampaign
 * @notice Defines the interface for Echo Campaign contracts, which manage campaign
 *         settings, budgets, KPIs, content submissions, and reward distribution.
 * @dev This interface provides function definitions for interacting with campaign
 *      data, including retrieving budget details, tracking content KPIs, and
 *      managing administrative actions.
 * @author Dynabits.org
 */
interface IEchoCampaign {
    /*******************************\
    |*-*-*-*   EXTERNALS   *-*-*-*-*|
    \*******************************/

    /**
     * @notice Initializes the contract with provided parameters.
     * @dev This function can only be called by the factory contract.
     * It sets the IPFS CID, budget information, social KPIs, and qualification data.
     * Additionally, it registers action kinds based on QA methods and social KPIs.
     * @param ipfsCID The IPFS Content Identifier (CID) associated with the data.
     * @param budgetInfo_ Struct containing budget-related information.
     * @param socialKPIs_ Struct containing social Key Performance Indicators (KPIs).
     * @param qaData_ An array of QualificationData structs defining QA methods and kinds.
     * Reverts:
     * - `ONLY_FACTORY` if the caller is not the factory contract.
     * - `DUPLICATE_QA` if there is a duplicate qualification.
     * - `DUPLICATE_KPI` if there is a duplicate KPI.
     */
    function init(
        string calldata ipfsCID,
        BudgetInfo calldata budgetInfo_,
        SocialKPIs calldata socialKPIs_,
        QualificationData[] calldata qaData_
    ) external;

    /**
     * @notice Toggles the campaign's pause state.
     * @dev Only callable by the protocol administrator.
     *
     * Emits:
     * - `CampaignPauseToggled` with the new state.
     *
     * Reverts:
     * - `UNAUTHORIZED_CALLER` if the caller is not the protocol administrator.
     */
    function togglePauseCampaign() external;

    /**
     * @notice Finalizes a campaign that is in progress.
     * @dev Only callable by the protocol administrator when the campaign status is `inProgress`.
     *      Refunds any unspent budget and marks the campaign as finalized.
     *
     * Emits:
     * - `CampaignFinalized` with the protocol administrator as the finalizer and the refunded amount.
     *
     * Reverts:
     * - `UNAUTHORIZED_CALLER` if the caller is not the protocol administrator.
     * - `ONLY_IN_PROGRESS_STATUS` if the campaign is not currently in progress.
     */
    function finalizeInProgressCampaign() external;

    /**
     * @notice Increases the campaign budget by a specified amount.
     * @dev Transfers the specified amount of tokens from the caller to the campaign
     *      and updates the reserved budget.
     * @param amount The amount of tokens to add to the campaign budget.
     *
     * Emits:
     * - `BudgetIncreased` with the added amount, previous budget, new budget, and sender address.
     *
     * Reverts:
     * - `ONLY_UPCOMING_OR_IN_PROGRESS_STATUS` if the campaign is not in `upcoming` or `inProgress` status.
     */
    function increaseBudget(uint256 amount) external;

    /**
     * @notice Submits content for the campaign.
     * @dev Adds a content link to the campaign without performing authentication checks at submission time.
     *      Authentication is enforced at the claiming stage.
     * @param contentLink The link to the submitted content.
     *
     * Reverts:
     * - `ONLY_IN_PROGRESS_STATUS` if the campaign is not in `inProgress` status.
     * - `INVALID_CONTENT_LINK` if the provided content link is empty.
     */
    function applyContent(string calldata contentLink) external;

    /**
     * @notice Finalizes a campaign that has reached the finished status.
     * @dev Ensures the campaign is in the `finished` status before finalization.
     *      Refunds any unspent budget and marks the campaign as finalized.
     *
     * Emits:
     * - `CampaignFinalized` with the finalizer and the refunded amount.
     *
     * Reverts:
     * - `ONLY_FINISHED_CAMPAIGN_STATUS` if the campaign is not in `finished` status.
     */
    function finalizeFinishedCampaign() external;

    /**
     * @notice Updates the KPI metrics for a specific content link.
     * @dev Modifies the total and per-content effective KPIs based on the provided values.
     *      If the content has no prior KPI value, it is counted as an eligible content.
     *
     * @param contentLink The unique identifier (link) of the content being updated.
     * @param additionalContentEffectiveKPI The KPI value to be added or subtracted.
     * @param additional A boolean indicating whether to increase or decrease the KPI.
     *
     * @return totalEffectiveKPIs The updated total sum of all effective KPIs.
     * @return contentEffectiveKPI_ The updated KPI for the specified content.
     * @return totalEligibleContents The updated count of eligible contents.
     * @return eligibilityPolarity must be either 1, 0, or -1 and determines how the total eligible content count is updated.
     *
     * Requirements:
     * - Caller must be the EchoContetns contract.
     */
    function updateKPIs(
        string calldata contentLink,
        uint256 additionalContentEffectiveKPI,
        bool additional
    )
        external
        returns (
            uint256 totalEffectiveKPIs,
            uint256 contentEffectiveKPI_,
            uint256 totalEligibleContents,
            int8 eligibilityPolarity
        );

    /**
     * @notice Allows the reward claim for registered and eligible content whose author has been registered by the oracle.
     * @dev This function verifies that the campaign status is `claimable`, ensures the content is registered,
     *      checks its effective KPI score, calculates the reward based on the KPI ratio, and distributes
     *      the appropriate amount. If a max reward per post is defined, any excess funds are refunded.
     *      Once all eligible claims have been processed, the campaign is finished.
     * @param contentLink The unique identifier (URL) of the content for which the claim is being made.
     *
     * Requirements:
     * - The campaign status must be `claimable`.
     * - The content must be registered under the campaign.
     * - The content must have an effective KPI score greater than zero.
     *
     * Emits:
     * - `Claimed` event upon successful claim, indicating the content, recipient, reward amount, and refund amount.
     * - `CampaignFinished` event is emitted when all eligible contents have been claimed and the campaign is finished.
     *
     * Reverts:
     * - `ONLY_CLAIMABLE_STATUS()` if the campaign is not in the `claimable` status.
     * - `INVALID_CONTENT_LINK()` if the content is not registered in the campaign.
     * - `NO_CONTENT_AUTHOR()` if the content's author has not been registered by the oracle before claiming.
     * - `NOT_ELIGIBLE_FOR_WITHDRAW()` if the content has no effective KPI score.
     */
    function claim(string calldata contentLink) external;

    /*****************************\
    |-*-*-*-*   GETTERS   *-*-*-*-|
    \*****************************/

    /// @notice Retrieves the budget information for the campaign.
    /// @return budgetInfo_ The budget details, including reserved amount and token information.
    function budgetInfo() external view returns (BudgetInfo memory budgetInfo_);

    /// @notice Retrieves the social KPIs associated with the campaign.
    /// @return socialKPIs_ The set of social KPIs used for evaluating campaign performance.
    function socialKPIs() external view returns (SocialKPIs memory socialKPIs_);

    /// @notice Retrieves the qualification data for the campaign.
    /// @return qaData_ An array of qualification data required for content evaluation.
    function qaData()
        external
        view
        returns (QualificationData[] memory qaData_);

    /// @notice Checks whether a specific action kind is registered in the campaign.
    /// @param actionKind The string representation of the action kind to check.
    /// @return isRegistered A boolean indicating whether the action kind is registered.
    function registeredActionKind(string calldata actionKind)
        external
        view
        returns (bool);

    /// @notice Retrieves the effective KPI score for a given content link.
    /// @param contentLink The unique identifier (URL or hash) of the submitted content.
    /// @return kpiScore The effective KPI score assigned to the content.
    function contentEffectiveKPI(string calldata contentLink)
        external
        view
        returns (uint256);

    /**
     * @notice Estimates the reward amount for content based on provided QA and KPI data.
     * @dev Ensures QA and KPI inputs match the campaign's predefined requirements.
     * @param qas An array of qualification data for the content.
     * @param kpis An array of KPI data for the content.
     * @return rewardAmount The estimated reward amount in campaign token units.
     */
    function rewardEstimation(
        QualificationData[] calldata qas,
        KPI[] calldata kpis
    ) external view returns (uint256 rewardAmount);

    /**
     * @notice Retrieves comprehensive details about the campaign.
     * @return basicInfo_ Basic campaign details such as name, owner, and time period.
     * @return initData_ Initialization data from the campaign factory.
     * @return budgetInfo_ Budget details including token and reserved amounts.
     * @return token_ Token metadata such as name, symbol, and decimals.
     * @return socialKPIs_ The social KPI details of the campaign.
     * @return qaData_ The qualification data required for content.
     * @return postInfo_ Statistics related to campaign content submissions.
     * @return status_ The current status of the campaign.
     */
    function details()
        external
        view
        returns (
            BasicInfo memory basicInfo_,
            CampaignInitData memory initData_,
            BudgetInfo memory budgetInfo_,
            Token memory token_,
            SocialKPIs memory socialKPIs_,
            QualificationData[] memory qaData_,
            PostInfo memory postInfo_,
            Status status_
        );

    /**
     * @notice Returns the address of the person who created the specified content and is eligible to claim it.
     * @param contentLink The URL of the submitted content.
     * @return The wallet address of the content creator for the given content.
               May return the zero address (0x0) if no record is found in the oracle.
     */
    function getContentCreator(string calldata contentLink)
        external
        view
        returns (address);

    /// @notice Retrieves the current status of the campaign.
    /// @return status_ The campaign's status as an enum value.
    function currentStatus() external view returns (Status status_);

    /// @notice Calculates the application fee required for content submission.
    /// @return fee The total application fee in campaign token units.
    function applicationFee() external view returns (uint256 fee);

    /// @notice Retrieves the campaign's name.
    /// @return campaignName The human-readable name of the campaign.
    function name() external view returns (string memory campaignName);

    /// @notice Retrieves the address of the Echo Contents contract.
    /// @return contentsAddress The address of the content management contract.
    function contents() external view returns (address contentsAddress);

    /// @notice Retrieves the address of the Echo Administration contract.
    /// @return adminAddress The address of the admin contract.
    function admin() external pure returns (address adminAddress);

    /// @notice Retrieves the address of the campaign factory.
    /// @return factoryAddress The address of the campaign factory contract.
    function factory() external pure returns (address factoryAddress);

    /// @notice Retrieves the owner of the campaign.
    /// @return ownerAddress The address of the campaign owner.
    function owner() external pure returns (address ownerAddress);

    /// @notice Retrieves the refund address for the campaign.
    /// @return refundAddr The address where refunds are sent.
    function refundAddress() external pure returns (address refundAddr);

    /// @notice Retrieves the hashed name of the campaign.
    /// @return campaignNameHash The bytes32 representation of the campaign name.
    function nameHash() external pure returns (bytes32 campaignNameHash);

    /// @notice Retrieves the start time of the campaign.
    /// @return startTimestamp The Unix timestamp of the campaign start time.
    function startTime() external pure returns (uint256 startTimestamp);

    /// @notice Retrieves the end time of the campaign.
    /// @return endTimestamp The Unix timestamp of the campaign end time.
    function endTime() external pure returns (uint256 endTimestamp);
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import {IEchoAdministration} from "./IEchoAdministration.sol";
import {IEchoCampaignFactory} from "./IEchoCampaignFactory.sol";
import {QA_METHOD} from "./IEchoCampaign.sol";
import {UD60x18} from "@prb/math/src/UD60x18.sol";

/***************************\
|*-*-*-*    TYPES    *-*-*-*|
\***************************/

struct ConfigOracleData {
    string kind;
    bytes value;
}

struct QualificationOracleData {
    QA_METHOD method;
    uint256 pct;
    string kind;
}

struct KPIsOracleData {
    string method;
    uint256 value;
}

struct SocialKPIsOracleData {
    string social;
    KPIsOracleData[] kpis;
}

struct CampaignAndContent {
    address campaign;
    string content;
}

struct CampaignContentInfo {
    uint128 lastUpdatedTime;
    uint128 lastPostTime;
    string[] contents;
}

struct ContentDetails {
    bool contentRegistered;
    uint8 totalQaKinds;
    uint240 lastUpdatedTime;
    UD60x18 contentOverallQaScore;
    mapping(string => uint256) actionKindToValue;
}

struct ContentsData {
    uint256 totalEffectiveKPIs;
    uint256 totalPosts;
    uint256 totalEligibleContents;
}

/**
 * @title Echo Contents Interface
 * @notice Manages content within Echo campaigns by enabling registration, updates, and evaluation.
 * @dev This interface facilitates content tracking, quality assessment through oracles, and KPI updates.
 *      - Allows campaigns to associate and manage content.
 *      - Supports qualification and configuration updates via oracles and protocol admin.
 *      - Enables KPI tracking based on predefined metrics.
 *      - Provides getter functions for efficient content retrieval and analytics.
 * @author Dynabits.org
 */
interface IEchoContents {
    /*******************************\
    |*-*-*-*   EXTERNALS   *-*-*-*-*|
    \*******************************/

    /**
     * @notice Registers a new content link for a campaign.
     * @dev Associates content with a campaign, ensuring only authorized campaigns can register.
     * @param contentLink The unique identifier (e.g., IPFS hash) of the content.
     * @param campaignMaxPerPost The campaign’s maximum payment per post.
     *
     * Requirements:
     * - The caller must be a registered campaign.
     * - The content must not have been previously registered for the same campaign.
     *
     * Effects:
     * - Stores the content reference associated with the campaign.
     * - Updates the campaign’s content records, including the last post timestamp.
     *
     * Emits:
     * - `ContentApplied` upon successful registration.
     *
     * Reverts:
     * - `ACCESS_DENIED` if the caller is not a registered campaign.
     * - `CONTENT_REGISTERED_FOR_CAMPAIGN_BEFORE` if already registered.
     */
    function addContentLink(string calldata contentLink, uint256 campaignMaxPerPost) external;

    /**
     * @notice Updates configuration and qualification data for a specific content in a campaign.
     * @dev Allows oracles or the protocol admin to set content-related data.
     * @param campaign The campaign's address.
     * @param contentLink The unique identifier of the content.
     * @param configs An array of configuration data from the oracle.
     * @param qualifications Qualification data used to evaluate the content.
     *
     * Requirements:
     * - At least one configuration or qualification must be provided.
     * - The caller must be the protocol admin or an authorized oracle.
     * - If the caller is an oracle, they must not be banned.
     * - The content must be registered under the campaign.
     * - Configuration updates require appropriate oracle permissions.
     * - Qualification updates must adhere to campaign-specific rules and valid percentage ranges.
     *
     * Emits:
     * - `ContentConfigsAdded` on successful configuration update.
     * - `NewQualificationsSettled` on successful qualification update.
     * - `ContentQAoverallScore` if the final QA score is calculated.
     * - `ReadyForKPIupdates` if the content qualifies for KPI updates.
     *
     * Reverts:
     * - `UNDEFINED_JOB` if no configurations or qualifications are provided.
     * - `ORACLE_IS_BAN` if the caller is a banned oracle.
     * - `CONTENT_DOESNT_EXIST` if the content is unregistered.
     * - `ORACLE_DOESNT_HAVE_REQUIRED_ACCESS` if the caller lacks permissions.
     * - `GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN` if an invalid qualification is provided.
     * - `QA_DATA_SETTLED_BEFORE` if qualifications were previously set and the caller is not the admin.
     * - `CHECK_QA_PCT` if the qualification percentage is out of range.
     */
    function setContentData(
        address campaign,
        string calldata contentLink,
        ConfigOracleData[] calldata configs,
        QualificationOracleData[] calldata qualifications
    ) external;

    /**
     * @notice Updates the KPI data for registered content within a campaign.
     * @dev Only the protocol admin or an authorized KPI oracle can update KPIs.
     *      If all required KPIs are met, the Effective KPI formula is applied.
     * @param campaign The address of the campaign.
     * @param contentLink The unique identifier of the content (e.g., IPFS hash).
     * @param socialKPIs The KPI data associated with the campaign’s social platform.
     *
     * Requirements:
     * - The caller must be the protocol admin or an authorized KPI oracle.
     * - A banned oracle cannot update KPIs unless they are the admin.
     * - The content must be registered under the specified campaign.
     * - The campaign must be in an `InProgress`, `Paused`, or `Finished` state.
     * - The content must have a valid non-zero QA score.
     * - The KPI action must be registered under the campaign.
     * - The oracle must have the required access to modify the KPI data.
     *
     * Effects:
     * - Updates KPI values for the specified content and campaign.
     * - Computes the Effective KPI value based on campaign rules and content QA score.
     * - Updates the campaign’s total Effective KPIs and eligible content count.
     *
     * Emits:
     * - `KPIsUpdated` when a KPI update is successfully processed.
     * - `ContentEffectiveKPIupdated` when the Effective KPI score is updated.
     *
     * Reverts:
     * - `ORACLE_IS_BAN` if the caller is a banned oracle.
     * - `CONTENT_DOESNT_EXIST` if the content is not registered.
     * - `ONLY_IN_PROGRESS_OR_PAUSED_OR_FINISHED_CAMPAIGN` if the campaign is in an invalid state.
     * - `ZERO_CONTENT_QA_SCORE` if the content has a QA score of zero.
     * - `DIFFERENT_SOCIALS` if the provided KPI does not match the campaign’s social KPI.
     * - `GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN` if the KPI type is not registered in the campaign.
     * - `ORACLE_DOESNT_HAVE_REQUIRED_ACCESS` if the caller lacks KPI update permissions.
     */
    function updateKPIs(
        address campaign,
        string calldata contentLink,
        SocialKPIsOracleData calldata socialKPIs
    ) external;

    /*****************************\
    |-*-*-*-*   GETTERS   *-*-*-*-|
    \*****************************/

    /**
     * @notice Retrieve the registered content along with its associated campaign address.
     * @param index The index in the contents array.
     * @return campaign The address of the campaign associated with the content.
     * @return content The registered content.
     */
    function contents(uint256 index)
        external
        view
        returns (address campaign, string memory content);

    /// @notice Retrieves the address of the Echo Administration contract.
    /// @return The address of the Echo Administration contract.
    function ECHO_ADMIN() external view returns (IEchoAdministration);

    /// @notice Retrieves the address of the Echo Campaign Factory contract.
    /// @return The address of the Echo Campaign Factory contract.
    function ECHO_FACTORY() external view returns (IEchoCampaignFactory);

    /**
     * @notice Retrieve the content-related timestamps for a given campaign.
     * @param campaign The address of the campaign.
     * @return lastUpdatedTime The timestamp of the last update made to the campaign's content.
     * @return lastPostTime The timestamp of the last content post associated with the campaign.
     */
    function campaignContentInfo(address campaign)
        external
        view
        returns (uint256 lastUpdatedTime, uint256 lastPostTime);

    /**
     * @notice Retrieve information about a specific content registered for a given campaign.
     * @param campaign The address of the campaign associated with the content.
     * @param contentLink The unique link or identifier of the content.
     * @param actionKind The concatenated social platform and the intended method, such as "X: Like" (optional).
     * @return contentRegistered A boolean indicating whether the content is registered under the campaign.
     * @return totalQaKinds The total number of qualifications set for the content in the campaign.
     * @return lastUpdatedTime The timestamp of the last update made to the content.
     * @return actionKindValue The corresponding value for the provided action kind, if applicable.
     */
    function contentToCampaign(
        address campaign,
        string calldata contentLink,
        string calldata actionKind
    )
        external
        view
        returns (
            bool contentRegistered,
            uint256 totalQaKinds,
            uint256 lastUpdatedTime,
            uint256 actionKindValue
        );

    /**
     * @notice Returns recent registered contents and their campaigns in paginated format.
     * @dev If page = 0, returns the last 10 registered contents.
     * @param page The page number.
     * @return currentPage The current page number.
     * @return totalPages The total number of pages.
     * @return pagedArray A list of 10 recent contents with campaign addresses.
     */
    function paginatedContentsWithCampaigns(uint256 page)
        external
        view
        returns (
            uint256 currentPage,
            uint256 totalPages,
            CampaignAndContent[] memory pagedArray
        );

    /**
     * @notice Returns recent registered contents for a specific campaign in paginated format.
     * @dev If page = 0, returns the last 10 contents for the campaign.
     * @param campaign The campaign's address.
     * @param page The page number.
     * @return currentPage The current page number.
     * @return totalPages The total number of pages.
     * @return pagedArray A list of 10 recent contents associated with the campaign.
     */
    function paginatedCampaignContents(uint256 page, address campaign)
        external
        view
        returns (
            uint256 currentPage,
            uint256 totalPages,
            string[] memory pagedArray
        );
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import {CampaignInitData, BudgetInfo, SocialKPIs, QualificationData} from "./IEchoCampaign.sol";

/***************************\
|*-*-*-*    TYPES    *-*-*-*|
\***************************/

/// @notice Structure containing the initialization parameters for a new campaign.
/// @dev This struct encapsulates all key configurations required when creating a campaign.
struct InitCampaign {
    uint256 startTime; /// Start time of the campaign (Unix timestamp).
    uint256 endTime; /// End time of the campaign (Unix timestamp).
    address refundAddress; /// Address to receive refunds (required if maxPerPost is set).
    string ipfsCID; /// IPFS content identifier (CID) for storing metadata.
    BudgetInfo budgetInfo; /// Budget-related configurations for the campaign.
    SocialKPIs socialKPIs; /// Social Key Performance Indicators (KPIs) for the campaign.
    QualificationData[] qaData; /// Array of qualification methods used for campaign validation.
}

/// @title Echo Campaign Factory Interface
/// @notice This interface defines the core functionality for deploying campaign contracts.
/// @author Dynabits.org
interface IEchoCampaignFactory {
    /*******************************\
    |*-*-*-*   EXTERNALS   *-*-*-*-*|
    \*******************************/

    /// @notice Updates the campaign implementation contract.
    /// @dev This function allows the protocol admin to change the underlying campaign logic by deploying a new contract.
    /// @param newImplementation The address of the new campaign implementation.
    function changeImplementation(address newImplementation) external;

    /**
     * @notice Deploys a new campaign contract with the specified parameters.
     * @dev Performs necessary validation checks before deployment, including:
     *      - Ensuring valid input parameters (e.g., campaign name, budget, duration).
     *      - Enforcing compliance with qualification and KPI requirements.
     *      - Deducting protocol fees and transferring budget to the campaign contract.
     * @param owner The address of the campaign owner.
     * @param name The unique name of the campaign (must be between 8 and 64 characters).
     * @param initCampaign A struct containing all initial parameters for campaign deployment.
     * @return clonedCampaign The address of the newly deployed campaign contract.
     *
     * Requirements:
     * - `owner` must be a valid, non-zero address.
     * - `name` must be unique and between 8 to 64 characters in length.
     * - `initCampaign.refundAddress` must be valid if `maxPerPost` is set.
     * - `initCampaign.ipfsCID` must be a non-empty string.
     * - The campaign start time must not be in the past.
     * - The campaign end time must be later than the start time.
     * - The campaign duration must not exceed the protocol's maximum allowed time.
     * - The campaign's budget token must be whitelisted.
     * - `maxPerPost` must not exceed `reservedAmount`, if set.
     * - At least one qualification method (QA method) must be provided.
     * - The total qualification percentages (`pct`) must sum up to 100%.
     * - At least one KPI must be registered.
     * - The sum of KPI percentages must be exactly 100%.
     * - The provided social network and KPIs must be allowed by the protocol.
     *
     * Effects:
     * - Deploys a new campaign contract as a clone of the base implementation.
     * - Deducts the protocol fee from the reserved campaign budget.
     * - Initializes the campaign contract with the provided parameters.
     * - Transfers the remaining budget to the newly created campaign.
     * - Stores campaign metadata for tracking and uniqueness validation.
     *
     * Emits:
     * - `CampaignCreated` event upon successful deployment of the campaign.
     *
     * Reverts:
     * - `ZERO_ADDRESS` if `owner` or `refundAddress` (when required) is zero.
     * - `NAME_LENGTH_LOWER_THAN_8_BYTES` if the name is too short.
     * - `NAME_LENGTH_LONGER_THAN_64_BYTES` if the name is too long.
     * - `NULL_CID` if the campaign IPFS CID is empty.
     * - `NAME_EXISTS` if a campaign with the same name already exists.
     * - `START_TIME_IN_PAST` if the campaign start time is invalid.
     * - `END_TIME_LOWER_THAN_START_TIME` if the end time is before or equal to the start time.
     * - `CHECK_MAX_CAMPAIGN_DURATION` if the duration exceeds the protocol’s limit.
     * - `MAX_PER_POST_BIGGER_THAN_RESERVED_AMOUNT` if `maxPerPost` exceeds the available budget.
     * - `NO_QUALIFICATION` if no qualification method is provided.
     * - `QA_METHOD_OR_ORACLE_NOT_ALLOWED` if the qualification method or oracle is unauthorized.
     * - `SUM_OF_QUALIFICATION_PARAMS_MUST_BE_100` if qualification percentages do not total 100%.
     * - `NO_KPIS` if no KPIs are provided.
     * - `SUM_OF_SOCIAL_KPI_PARAMS_MUST_BE_100` if KPI percentages do not sum to 100%.
     * - `ONLY_ALLOWED_SOCIAL_KPIS` if an unapproved social KPI is used.
     * - `SOCIAL_KPI_RATIO_IS_ZERO` if a KPI lacks a predefined ratio in the protocol.
     */
    function createCampaign(
        address owner,
        string calldata name,
        InitCampaign memory initCampaign
    ) external returns (address clonedCampaign);

    /*****************************\
    |-*-*-*-*   GETTERS   *-*-*-*-|
    \*****************************/

    /// @notice Retrieves the current implementation contract for campaigns.
    /// @dev This address is used for cloning new campaign instances.
    /// @return The address of the campaign implementation contract.
    function implementation() external view returns (address);

    /// @notice Retrieves the address of a deployed campaign by its index.
    /// @param index The index of the campaign in the `allCampaigns` array.
    /// @return The address of the deployed campaign.
    function allCampaigns(uint256 index) external view returns (address);

    /**
     * @notice Retrieves the original campaign name from its hashed value.
     * @dev Useful for verifying unique campaign names.
     * @param nameHash The hashed value of the campaign name.
     * @return The original campaign name.
     */
    function campaignNameHashToName(bytes32 nameHash)
        external
        view
        returns (string memory);

    /// @notice Retrieves the address of the Echo Administration contract.
    /// @return The address of the Echo Administration contract.
    function ECHO_ADMIN() external view returns (address);

    /// @notice Retrieves the address of the Echo Contents contract.
    /// @return The address of the Echo Contents contract.
    function ECHO_CONTENTS() external view returns (address);

    /**
     * @notice Retrieves initialization details for a specific campaign.
     * @dev Provides useful data for off-chain applications and analytics.
     * @param name The name of the deployed campaign.
     * @return The timestamp of deployment, the deployed contract address, and the structural contract used.
     */
    function campaignNameToInitData(string memory name)
        external
        view
        returns (CampaignInitData memory);

    /**
     * @notice Retrieves a paginated list of recently deployed campaigns.
     * @dev Returns 10 campaign addresses per page, with pagination for historical retrieval.
     *      - If `page = 0`, returns the 10 most recent campaigns.
     *      - Optimized for off-chain use cases as an alternative to event logs.
     * @param page The page number requested.
     * @return currentPage The current page number.
     * @return totalPages The total number of available pages.
     * @return pagedArray A list of up to 10 recently deployed campaign addresses.
     */
    function paginatedCampaigns(uint256 page)
        external
        view
        returns (
            uint256 currentPage,
            uint256 totalPages,
            address[] memory pagedArray
        );
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

/***************************\
|*-*-*-*    TYPES    *-*-*-*|
\***************************/

enum ActionStatus {
    NA,
    Config,
    QA,
    KPI
}

struct ActionAndKind {
    ActionStatus actionStatus;
    string action;
    string kind;
}

struct ModelData {
    ActionStatus actionStatus;
    uint8 socialKPIratio;
    uint248 fee;
    address oracle;
}

/// @title Echo Administration Interface
/// @notice Defines administrative functions for managing campaign settings, token whitelisting, and oracle permissions.
/// @author Dynabits.org
interface IEchoAdministration {
    /*******************************\
    |*-*-*-*   EXTERNALS   *-*-*-*-*|
    \*******************************/

    /// @notice Transfers protocol admin rights to a new address.
    /// @dev Only the current protocol admin can call this function. The new admin address cannot be the zero address.
    /// @param newProtocolAdmin The address of the new protocol admin.
    function changeProtocolAdmin(address newProtocolAdmin) external;

    /// @notice Sets the campaign fee rate as a percentage of the allocated token at creation.
    /// @dev The value must be between 0 and 1e5.
    /// @param newCampaignFeeRate The new campaign creation fee rate.
    function changeCampaignFeeRate(uint24 newCampaignFeeRate) external;

    /// @notice Sets the maximum campaign duration.
    /// @dev A value of zero means there is no maximum campaign duration.
    /// @param newMaxCampaignTime The maximum campaign duration in seconds.
    function setMaxCampaignTime(uint72 newMaxCampaignTime) external;

    /// @notice Whitelists a new ERC20 token for use in campaigns.
    /// @dev A token cannot be whitelisted more than once. The whitelist status is checked during campaign creation.
    /// @param token The ERC20 token address.
    function whitelistToken(address token) external;

    /// @notice Removes an ERC20 token from the whitelist, preventing its selection as a campaign budget token.
    /// @param token The ERC20 token address.
    function removeWhitelistedToken(address token) external;

    /// @notice Toggles full access permissions for an oracle.
    /// @dev This function can be used to temporarily or permanently disable an oracle’s access to campaign-related functionalities.
    /// @param oracle The address of the oracle.
    function toggleOracleFullAccess(address oracle) external;

    /**
     * @notice Sets or updates the model data for a given action and kind.
     * @dev Configures an action-kind mapping with an associated oracle, fee, and status.
     * @param actionAndKind The struct containing the action and kind identifiers.
     * @param model The struct containing model details such as the oracle, fee, status, and social KPI ratio.
     *
     * Requirements:
     * - `actionAndKind.action` and `actionAndKind.kind` must not be empty.
     * - `model.actionStatus` must be one of `QA`, `KPI`, or `CONFIG`.
     * - Actions must match their respective `actionStatus`:
     *   - `QA` actions: `QA-AI`, `QA-C`, `QA-V`
     *   - `KPI` actions: `KPI`
     *   - `CONFIG` actions: `CONFIG`
     * - QA oracles must have a nonzero fee.
     * - KPI oracles must have both a nonzero fee and a nonzero social KPI ratio.
     * - CONFIG actions always have a zero fee.
     *
     * Effects:
     * - Adds or updates an action-kind mapping.
     * - Removes model data if `model.oracle` is zero.
     *
     * Emits:
     * - `ModelSettled` if a model is set or updated.
     * - `ModelRemoved` if a model is removed.
     *
     * Reverts:
     * - `EMPTY_ACTION_OR_KIND_PROVIDED` if either action or kind is empty.
     * - `NA_ACTION_STATUS` if the provided action status is `NA`.
     * - `NOT_RELATIVE_ACTION` if the action does not match its required type.
     * - `UNDEFINED_PROCESS` if a QA oracle has a fee but no valid address.
     * - `ZERO_FEE_PROVIDED` if a required fee is zero.
     * - `ZERO_SOCIAL_KPI_PROVIDED` if a required social KPI ratio is zero.
     */
    function setModel(
        ActionAndKind calldata actionAndKind,
        ModelData calldata model
    ) external;

    /*****************************\
    |-*-*-*-*   GETTERS   *-*-*-*-|
    \*****************************/

    /// @notice Returns the campaign creation fee rate as a percentage.
    /// @dev This value is deducted from the allocated budget immediately upon campaign creation.
    /// @return The campaign creation fee rate.
    function campaignFeeRate() external view returns (uint24);

    /// @notice Returns the maximum allowed duration for campaigns.
    /// @dev A campaign’s duration (end time - start time) must be less than or equal to this value at creation.
    /// @return The maximum campaign duration in seconds.
    function maxCampaignTime() external view returns (uint72);

    /// @notice Returns the protocol admin address.
    /// @dev The protocol admin has full access to infrastructural modifications.
    /// @return The address of the protocol admin.
    function protocolAdmin() external view returns (address);

    /// @notice Checks if a given ERC20 token is whitelisted for campaign use.
    /// @param token The ERC20 token address.
    /// @return A boolean indicating whether the token is whitelisted.
    function whitelistedToken(address token) external view returns (bool);

    /**
     * @notice Checks if an oracle has been banned.
     * @dev A banned oracle loses all permissions to interact with EchoContents and campaign contracts.
     * @param oracle The address of the oracle.
     * @return A boolean indicating whether the oracle is banned.
     */
    function disallowedOracle(address oracle) external view returns (bool);

    /**
     * @notice Returns the number of available methods and parameters for a given action.
     * @dev Examples:
     * - KPI actions (e.g., `X`) can have parameters like `Like` and `Retweet`.
     * - Qualification actions (`QA-V`) can have parameters like `Accuracy` and `Originality`.
     * @param action The action type, such as `X`, `QA-V`, or `Config`.
     * @return The number of available methods for the action.
     */
    function totalKindMethods(string calldata action)
        external
        view
        returns (uint256);

    /**
     * @notice Retrieves the original action and kind from a given hashed identifier.
     * @dev Useful for reducing gas costs and simplifying comparisons.
     * @param hashedActionKind The hash of the concatenated action and kind.
     * @return The decoded action and kind as a string.
     */
    function hashToActionKind(bytes32 hashedActionKind)
        external
        view
        returns (string memory);

    /**
     * @notice Returns model data associated with an oracle and a hashed action-kind.
     * @param oracle_ The address of the oracle.
     * @param hashedActionKind The hashed action-kind identifier.
     * @return actionStatus The action status of the model.
     * @return socialKPIratio The social KPI ratio.
     * @return fee The associated fee for the action.
     * @return oracle The address of the oracle managing the action.
     */
    function modelData(address oracle_, bytes32 hashedActionKind)
        external
        view
        returns (
            ActionStatus actionStatus,
            uint8 socialKPIratio,
            uint248 fee,
            address oracle
        );

    /**
     * @notice Checks if an oracle has the required permissions for given action-kind hashes.
     * @dev Access is determined based on the oracle's address:
     * - `address(0)`: Checks if the hashed action-kind exists for KPI.
     * - `address(1)`: Checks if the hashed action-kind exists for CONFIG.
     * - Any other address: Checks if the oracle has QA access.
     * @param oracle The oracle’s address.
     * @param hashedActionsKind An array of hashed action-kind values to verify.
     * @return A boolean indicating whether the oracle is authorized.
     */
    function allowedActionKind(
        address oracle,
        bytes32[] calldata hashedActionsKind
    ) external view returns (bool);
}

// 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);
}

// 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);
}

File 10 of 31 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The unsigned 60.18-decimal fixed-point number representation, which can have up to 60 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the Solidity type uint256.
/// @dev The value type is defined here so it can be imported in all other files.
type UD60x18 is uint256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoSD59x18,
    Casting.intoUint128,
    Casting.intoUint256,
    Casting.intoUint40,
    Casting.unwrap
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.ln,
    Math.log10,
    Math.log2,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes the functions in this library callable on the UD60x18 type.
using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.xor
} for UD60x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the UD60x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.or as |,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.sub as -,
    Helpers.xor as ^
} for UD60x18 global;

File 11 of 31 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { wrap } from "./Casting.sol";
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_UD60x18,
    uMAX_WHOLE_UD60x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { UD60x18 } from "./ValueType.sol";

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the arithmetic average of x and y using the following formula:
///
/// $$
/// avg(x, y) = (x & y) + ((xUint ^ yUint) / 2)
/// $$
///
/// In English, this is what this formula does:
///
/// 1. AND x and y.
/// 2. Calculate half of XOR x and y.
/// 3. Add the two results together.
///
/// This technique is known as SWAR, which stands for "SIMD within a register". You can read more about it here:
/// https://devblogs.microsoft.com/oldnewthing/20220207-00/?p=106223
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The arithmetic average as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    unchecked {
        result = wrap((xUint & yUint) + ((xUint ^ yUint) >> 1));
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev This is optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_UD60x18`.
///
/// @param x The UD60x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint > uMAX_WHOLE_UD60x18) {
        revert Errors.PRBMath_UD60x18_Ceil_Overflow(x);
    }

    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `UNIT - remainder`.
        let delta := sub(uUNIT, remainder)

        // Equivalent to `x + remainder > 0 ? delta : 0`.
        result := add(x, mul(delta, gt(remainder, 0)))
    }
}

/// @notice Divides two UD60x18 numbers, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @param x The numerator as a UD60x18 number.
/// @param y The denominator as a UD60x18 number.
/// @param result The quotient as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv(x.unwrap(), uUNIT, y.unwrap()));
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Requirements:
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xUint > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        uint256 doubleUnitProduct = xUint * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method.
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in UD60x18.
///
/// @param x The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
    if (xUint > uEXP2_MAX_INPUT) {
        revert Errors.PRBMath_UD60x18_Exp2_InputTooBig(x);
    }

    // Convert x to the 192.64-bit fixed-point format.
    uint256 x_192x64 = (xUint << 64) / uUNIT;

    // Pass x to the {Common.exp2} function, which uses the 192.64-bit fixed-point number representation.
    result = wrap(Common.exp2(x_192x64));
}

/// @notice Yields the greatest whole number less than or equal to x.
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
/// @param x The UD60x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        // Equivalent to `x % UNIT`.
        let remainder := mod(x, uUNIT)

        // Equivalent to `x - remainder > 0 ? remainder : 0)`.
        result := sub(x, mul(remainder, gt(remainder, 0)))
    }
}

/// @notice Yields the excess beyond the floor of x using the odd function definition.
/// @dev See https://en.wikipedia.org/wiki/Fractional_part.
/// @param x The UD60x18 number to get the fractional part of.
/// @param result The fractional part of x as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function frac(UD60x18 x) pure returns (UD60x18 result) {
    assembly ("memory-safe") {
        result := mod(x, uUNIT)
    }
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$, rounding down.
///
/// @dev Requirements:
/// - x * y must fit in UD60x18.
///
/// @param x The first operand as a UD60x18 number.
/// @param y The second operand as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();
    if (xUint == 0 || yUint == 0) {
        return ZERO;
    }

    unchecked {
        // Checking for overflow this way is faster than letting Solidity do it.
        uint256 xyUint = xUint * yUint;
        if (xyUint / xUint != yUint) {
            revert Errors.PRBMath_UD60x18_Gm_Overflow(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        result = wrap(Common.sqrt(xyUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The UD60x18 number for which to calculate the inverse.
/// @return result The inverse as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(uUNIT_SQUARED / x.unwrap());
    }
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(UD60x18 x) pure returns (UD60x18 result) {
    unchecked {
        // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
        // {log2} can return is ~196_205294292027477728.
        result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
    }
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The UD60x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this assembly block is the standard multiplication operation, not {UD60x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 59) }
        default { result := uMAX_UD60x18 }
    }

    if (result.unwrap() == uMAX_UD60x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The UD60x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    if (xUint < uUNIT) {
        revert Errors.PRBMath_UD60x18_Log_InputTooSmall(x);
    }

    unchecked {
        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(xUint / uUNIT);

        // This is the integer part of the logarithm as a UD60x18 number. The operation can't overflow because n
        // n is at most 255 and UNIT is 1e18.
        uint256 resultUint = n * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        uint256 y = xUint >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultUint);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        uint256 DOUBLE_UNIT = 2e18;
        for (uint256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultUint += delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        result = wrap(resultUint);
    }
}

/// @notice Multiplies two UD60x18 numbers together, returning a new UD60x18 number.
///
/// @dev Uses {Common.mulDiv} to enable overflow-safe multiplication and division.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
///
/// @dev See the documentation in {Common.mulDiv18}.
/// @param x The multiplicand as a UD60x18 number.
/// @param y The multiplier as a UD60x18 number.
/// @return result The product as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(Common.mulDiv18(x.unwrap(), y.unwrap()));
}

/// @notice Raises x to the power of y.
///
/// For $1 \leq x \leq \infty$, the following standard formula is used:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// For $0 \leq x \lt 1$, since the unsigned {log2} is undefined, an equivalent formula is used:
///
/// $$
/// i = \frac{1}{x}
/// w = 2^{log_2{i} * y}
/// x^y = \frac{1}{w}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2} and {mul}.
/// - Returns `UNIT` for 0^0.
/// - It may not perform well with very small values of x. Consider using SD59x18 as an alternative.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a UD60x18 number.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();
    uint256 yUint = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xUint == 0) {
        return yUint == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xUint == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yUint == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yUint == uUNIT) {
        return x;
    }

    // If x is greater than `UNIT`, use the standard formula.
    if (xUint > uUNIT) {
        result = exp2(mul(log2(x), y));
    }
    // Conversely, if x is less than `UNIT`, use the equivalent formula.
    else {
        UD60x18 i = wrap(uUNIT_SQUARED / xUint);
        UD60x18 w = exp2(mul(log2(i), y));
        result = wrap(uUNIT_SQUARED / w.unwrap());
    }
}

/// @notice Raises x (a UD60x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - The result must fit in UD60x18.
///
/// @param x The base as a UD60x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(UD60x18 x, uint256 y) pure returns (UD60x18 result) {
    // Calculate the first iteration of the loop in advance.
    uint256 xUint = x.unwrap();
    uint256 resultUint = y & 1 > 0 ? xUint : uUNIT;

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    for (y >>= 1; y > 0; y >>= 1) {
        xUint = Common.mulDiv18(xUint, xUint);

        // Equivalent to `y % 2 == 1`.
        if (y & 1 > 0) {
            resultUint = Common.mulDiv18(resultUint, xUint);
        }
    }
    result = wrap(resultUint);
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must be less than `MAX_UD60x18 / UNIT`.
///
/// @param x The UD60x18 number for which to calculate the square root.
/// @return result The result as a UD60x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(UD60x18 x) pure returns (UD60x18 result) {
    uint256 xUint = x.unwrap();

    unchecked {
        if (xUint > uMAX_UD60x18 / uUNIT) {
            revert Errors.PRBMath_UD60x18_Sqrt_Overflow(x);
        }
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two UD60x18 numbers.
        // In this case, the two numbers are both the square root.
        result = wrap(Common.sqrt(xUint * uUNIT));
    }
}

File 12 of 31 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the UD60x18 type.
function add(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the UD60x18 type.
function and2(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal operation (==) in the UD60x18 type.
function eq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the UD60x18 type.
function gt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the UD60x18 type.
function gte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the UD60x18 type.
function isZero(UD60x18 x) pure returns (bool result) {
    // This wouldn't work if x could be negative.
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the UD60x18 type.
function lshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the UD60x18 type.
function lt(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the UD60x18 type.
function lte(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the checked modulo operation (%) in the UD60x18 type.
function mod(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the UD60x18 type.
function neq(UD60x18 x, UD60x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the UD60x18 type.
function not(UD60x18 x) pure returns (UD60x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the UD60x18 type.
function or(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the UD60x18 type.
function rshift(UD60x18 x, uint256 bits) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the UD60x18 type.
function sub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the UD60x18 type.
function uncheckedAdd(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the UD60x18 type.
function uncheckedSub(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the UD60x18 type.
function xor(UD60x18 x, UD60x18 y) pure returns (UD60x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 13 of 31 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

/// @notice Thrown when ceiling a number overflows UD60x18.
error PRBMath_UD60x18_Ceil_Overflow(UD60x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows UD60x18.
error PRBMath_UD60x18_Convert_Overflow(uint256 x);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_UD60x18_Exp_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_UD60x18_Exp2_InputTooBig(UD60x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows UD60x18.
error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18.
error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_UD60x18_IntoUint40_Overflow(UD60x18 x);

/// @notice Thrown when taking the logarithm of a number less than 1.
error PRBMath_UD60x18_Log_InputTooSmall(UD60x18 x);

/// @notice Thrown when calculating the square root overflows UD60x18.
error PRBMath_UD60x18_Sqrt_Overflow(UD60x18 x);

File 14 of 31 : Conversions.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { uMAX_UD60x18, uUNIT } from "./Constants.sol";
import { PRBMath_UD60x18_Convert_Overflow } from "./Errors.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Converts a UD60x18 number to a simple integer by dividing it by `UNIT`.
/// @dev The result is rounded toward zero.
/// @param x The UD60x18 number to convert.
/// @return result The same number in basic integer form.
function convert(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x) / uUNIT;
}

/// @notice Converts a simple integer to UD60x18 by multiplying it by `UNIT`.
///
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UD60x18 / UNIT`.
///
/// @param x The basic integer to convert.
/// @param result The same number converted to UD60x18.
function convert(uint256 x) pure returns (UD60x18 result) {
    if (x > uMAX_UD60x18 / uUNIT) {
        revert PRBMath_UD60x18_Convert_Overflow(x);
    }
    unchecked {
        result = UD60x18.wrap(x * uUNIT);
    }
}

File 15 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD60x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as a UD60x18 number.
UD60x18 constant E = UD60x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
uint256 constant uEXP_MAX_INPUT = 133_084258667509499440;
UD60x18 constant EXP_MAX_INPUT = UD60x18.wrap(uEXP_MAX_INPUT);

/// @dev The maximum input permitted in {exp2}.
uint256 constant uEXP2_MAX_INPUT = 192e18 - 1;
UD60x18 constant EXP2_MAX_INPUT = UD60x18.wrap(uEXP2_MAX_INPUT);

/// @dev Half the UNIT number.
uint256 constant uHALF_UNIT = 0.5e18;
UD60x18 constant HALF_UNIT = UD60x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as a UD60x18 number.
uint256 constant uLOG2_10 = 3_321928094887362347;
UD60x18 constant LOG2_10 = UD60x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as a UD60x18 number.
uint256 constant uLOG2_E = 1_442695040888963407;
UD60x18 constant LOG2_E = UD60x18.wrap(uLOG2_E);

/// @dev The maximum value a UD60x18 number can have.
uint256 constant uMAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_584007913129639935;
UD60x18 constant MAX_UD60x18 = UD60x18.wrap(uMAX_UD60x18);

/// @dev The maximum whole value a UD60x18 number can have.
uint256 constant uMAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457_000000000000000000;
UD60x18 constant MAX_WHOLE_UD60x18 = UD60x18.wrap(uMAX_WHOLE_UD60x18);

/// @dev PI as a UD60x18 number.
UD60x18 constant PI = UD60x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD60x18.
uint256 constant uUNIT = 1e18;
UD60x18 constant UNIT = UD60x18.wrap(uUNIT);

/// @dev The unit number squared.
uint256 constant uUNIT_SQUARED = 1e36;
UD60x18 constant UNIT_SQUARED = UD60x18.wrap(uUNIT_SQUARED);

/// @dev Zero as a UD60x18 number.
UD60x18 constant ZERO = UD60x18.wrap(0);

File 16 of 31 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_SD59x18 } from "../sd59x18/Constants.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "./ValueType.sol";

/// @notice Casts a UD60x18 number into SD1x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(int256(uMAX_SD1x18))) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(uint64(xUint)));
}

/// @notice Casts a UD60x18 number into UD2x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uMAX_UD2x18) {
        revert CastingErrors.PRBMath_UD60x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(xUint));
}

/// @notice Casts a UD60x18 number into SD59x18.
/// @dev Requirements:
/// - x must be less than or equal to `uMAX_SD59x18`.
function intoSD59x18(UD60x18 x) pure returns (SD59x18 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > uint256(uMAX_SD59x18)) {
        revert CastingErrors.PRBMath_UD60x18_IntoSD59x18_Overflow(x);
    }
    result = SD59x18.wrap(int256(xUint));
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev This is basically an alias for {unwrap}.
function intoUint256(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Casts a UD60x18 number into uint128.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT128`.
function intoUint128(UD60x18 x) pure returns (uint128 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT128) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint128_Overflow(x);
    }
    result = uint128(xUint);
}

/// @notice Casts a UD60x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD60x18 x) pure returns (uint40 result) {
    uint256 xUint = UD60x18.unwrap(x);
    if (xUint > MAX_UINT40) {
        revert CastingErrors.PRBMath_UD60x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Alias for {wrap}.
function ud60x18(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

/// @notice Unwraps a UD60x18 number into uint256.
function unwrap(UD60x18 x) pure returns (uint256 result) {
    result = UD60x18.unwrap(x);
}

/// @notice Wraps a uint256 number into the UD60x18 value type.
function wrap(uint256 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(x);
}

File 17 of 31 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions used in both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

/*//////////////////////////////////////////////////////////////////////////
                                    CONSTANTS
//////////////////////////////////////////////////////////////////////////*/

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
    // use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
    // variables such that product = prod1 * 2^256 + prod0.
    uint256 prod0; // Least significant 256 bits of the product
    uint256 prod1; // Most significant 256 bits of the product
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    // Handle non-overflow cases, 256 by 256 division.
    if (prod1 == 0) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

    ////////////////////////////////////////////////////////////////////////////
    // 512 by 256 division
    ////////////////////////////////////////////////////////////////////////////

    // Make division exact by subtracting the remainder from [prod1 prod0].
    uint256 remainder;
    assembly ("memory-safe") {
        // Compute remainder using the mulmod Yul instruction.
        remainder := mulmod(x, y, denominator)

        // Subtract 256 bit number from 512-bit number.
        prod1 := sub(prod1, gt(remainder, prod0))
        prod0 := sub(prod0, remainder)
    }

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

            // Divide [prod1 prod0] by lpotdod.
            prod0 := div(prod0, lpotdod)

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

        // Shift in bits from prod1 into prod0.
        prod0 |= prod1 * flippedLpotdod;

        // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
        // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
        // four bits. That is, denominator * inv = 1 mod 2^4.
        uint256 inverse = (3 * denominator) ^ 2;

        // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
        // in modular arithmetic, doubling the correct bits in each step.
        inverse *= 2 - denominator * inverse; // inverse mod 2^8
        inverse *= 2 - denominator * inverse; // inverse mod 2^16
        inverse *= 2 - denominator * inverse; // inverse mod 2^32
        inverse *= 2 - denominator * inverse; // inverse mod 2^64
        inverse *= 2 - denominator * inverse; // inverse mod 2^128
        inverse *= 2 - denominator * inverse; // inverse mod 2^256

        // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
        // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
        // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
        // is no longer required.
        result = prod0 * inverse;
    }
}

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
    // doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
    // precision into the expected uint128 result.
    unchecked {
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}

File 18 of 31 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The unsigned 2.18-decimal fixed-point number representation, which can have up to 2 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type uint64. This is useful when end users want to use uint64 to save gas, e.g. with tight variable packing in contract
/// storage.
type UD2x18 is uint64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD1x18,
    Casting.intoSD59x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for UD2x18 global;

File 19 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @dev Euler's number as a UD2x18 number.
UD2x18 constant E = UD2x18.wrap(2_718281828459045235);

/// @dev The maximum value a UD2x18 number can have.
uint64 constant uMAX_UD2x18 = 18_446744073709551615;
UD2x18 constant MAX_UD2x18 = UD2x18.wrap(uMAX_UD2x18);

/// @dev PI as a UD2x18 number.
UD2x18 constant PI = UD2x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of UD2x18.
UD2x18 constant UNIT = UD2x18.wrap(1e18);
uint64 constant uUNIT = 1e18;

File 20 of 31 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;
import "./Helpers.sol" as Helpers;
import "./Math.sol" as Math;

/// @notice The signed 59.18-decimal fixed-point number representation, which can have up to 59 digits and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int256.
type SD59x18 is int256;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoInt256,
    Casting.intoSD1x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                            MATHEMATICAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Math.abs,
    Math.avg,
    Math.ceil,
    Math.div,
    Math.exp,
    Math.exp2,
    Math.floor,
    Math.frac,
    Math.gm,
    Math.inv,
    Math.log10,
    Math.log2,
    Math.ln,
    Math.mul,
    Math.pow,
    Math.powu,
    Math.sqrt
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

using {
    Helpers.add,
    Helpers.and,
    Helpers.eq,
    Helpers.gt,
    Helpers.gte,
    Helpers.isZero,
    Helpers.lshift,
    Helpers.lt,
    Helpers.lte,
    Helpers.mod,
    Helpers.neq,
    Helpers.not,
    Helpers.or,
    Helpers.rshift,
    Helpers.sub,
    Helpers.uncheckedAdd,
    Helpers.uncheckedSub,
    Helpers.uncheckedUnary,
    Helpers.xor
} for SD59x18 global;

/*//////////////////////////////////////////////////////////////////////////
                                    OPERATORS
//////////////////////////////////////////////////////////////////////////*/

// The global "using for" directive makes it possible to use these operators on the SD59x18 type.
using {
    Helpers.add as +,
    Helpers.and2 as &,
    Math.div as /,
    Helpers.eq as ==,
    Helpers.gt as >,
    Helpers.gte as >=,
    Helpers.lt as <,
    Helpers.lte as <=,
    Helpers.mod as %,
    Math.mul as *,
    Helpers.neq as !=,
    Helpers.not as ~,
    Helpers.or as |,
    Helpers.sub as -,
    Helpers.unary as -,
    Helpers.xor as ^
} for SD59x18 global;

File 21 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

// NOTICE: the "u" prefix stands for "unwrapped".

/// @dev Euler's number as an SD59x18 number.
SD59x18 constant E = SD59x18.wrap(2_718281828459045235);

/// @dev The maximum input permitted in {exp}.
int256 constant uEXP_MAX_INPUT = 133_084258667509499440;
SD59x18 constant EXP_MAX_INPUT = SD59x18.wrap(uEXP_MAX_INPUT);

/// @dev Any value less than this returns 0 in {exp}.
int256 constant uEXP_MIN_THRESHOLD = -41_446531673892822322;
SD59x18 constant EXP_MIN_THRESHOLD = SD59x18.wrap(uEXP_MIN_THRESHOLD);

/// @dev The maximum input permitted in {exp2}.
int256 constant uEXP2_MAX_INPUT = 192e18 - 1;
SD59x18 constant EXP2_MAX_INPUT = SD59x18.wrap(uEXP2_MAX_INPUT);

/// @dev Any value less than this returns 0 in {exp2}.
int256 constant uEXP2_MIN_THRESHOLD = -59_794705707972522261;
SD59x18 constant EXP2_MIN_THRESHOLD = SD59x18.wrap(uEXP2_MIN_THRESHOLD);

/// @dev Half the UNIT number.
int256 constant uHALF_UNIT = 0.5e18;
SD59x18 constant HALF_UNIT = SD59x18.wrap(uHALF_UNIT);

/// @dev $log_2(10)$ as an SD59x18 number.
int256 constant uLOG2_10 = 3_321928094887362347;
SD59x18 constant LOG2_10 = SD59x18.wrap(uLOG2_10);

/// @dev $log_2(e)$ as an SD59x18 number.
int256 constant uLOG2_E = 1_442695040888963407;
SD59x18 constant LOG2_E = SD59x18.wrap(uLOG2_E);

/// @dev The maximum value an SD59x18 number can have.
int256 constant uMAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_792003956564819967;
SD59x18 constant MAX_SD59x18 = SD59x18.wrap(uMAX_SD59x18);

/// @dev The maximum whole value an SD59x18 number can have.
int256 constant uMAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MAX_WHOLE_SD59x18 = SD59x18.wrap(uMAX_WHOLE_SD59x18);

/// @dev The minimum value an SD59x18 number can have.
int256 constant uMIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_792003956564819968;
SD59x18 constant MIN_SD59x18 = SD59x18.wrap(uMIN_SD59x18);

/// @dev The minimum whole value an SD59x18 number can have.
int256 constant uMIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728_000000000000000000;
SD59x18 constant MIN_WHOLE_SD59x18 = SD59x18.wrap(uMIN_WHOLE_SD59x18);

/// @dev PI as an SD59x18 number.
SD59x18 constant PI = SD59x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD59x18.
int256 constant uUNIT = 1e18;
SD59x18 constant UNIT = SD59x18.wrap(1e18);

/// @dev The unit number squared.
int256 constant uUNIT_SQUARED = 1e36;
SD59x18 constant UNIT_SQUARED = SD59x18.wrap(uUNIT_SQUARED);

/// @dev Zero as an SD59x18 number.
SD59x18 constant ZERO = SD59x18.wrap(0);

File 22 of 31 : ValueType.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Casting.sol" as Casting;

/// @notice The signed 1.18-decimal fixed-point number representation, which can have up to 1 digit and up to 18
/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity
/// type int64. This is useful when end users want to use int64 to save gas, e.g. with tight variable packing in contract
/// storage.
type SD1x18 is int64;

/*//////////////////////////////////////////////////////////////////////////
                                    CASTING
//////////////////////////////////////////////////////////////////////////*/

using {
    Casting.intoSD59x18,
    Casting.intoUD2x18,
    Casting.intoUD60x18,
    Casting.intoUint256,
    Casting.intoUint128,
    Casting.intoUint40,
    Casting.unwrap
} for SD1x18 global;

File 23 of 31 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @dev Euler's number as an SD1x18 number.
SD1x18 constant E = SD1x18.wrap(2_718281828459045235);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMAX_SD1x18 = 9_223372036854775807;
SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18);

/// @dev The maximum value an SD1x18 number can have.
int64 constant uMIN_SD1x18 = -9_223372036854775808;
SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18);

/// @dev PI as an SD1x18 number.
SD1x18 constant PI = SD1x18.wrap(3_141592653589793238);

/// @dev The unit number, which gives the decimal precision of SD1x18.
SD1x18 constant UNIT = SD1x18.wrap(1e18);
int64 constant uUNIT = 1e18;

File 24 of 31 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import { uMAX_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { UD2x18 } from "./ValueType.sol";

/// @notice Casts a UD2x18 number into SD1x18.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(uMAX_SD1x18)) {
        revert Errors.PRBMath_UD2x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xUint));
}

/// @notice Casts a UD2x18 number into SD59x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18.
function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x))));
}

/// @notice Casts a UD2x18 number into UD60x18.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18.
function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) {
    result = UD60x18.wrap(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint128.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint128.
function intoUint128(UD2x18 x) pure returns (uint128 result) {
    result = uint128(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint256.
/// @dev There is no overflow check because the domain of UD2x18 is a subset of uint256.
function intoUint256(UD2x18 x) pure returns (uint256 result) {
    result = uint256(UD2x18.unwrap(x));
}

/// @notice Casts a UD2x18 number into uint40.
/// @dev Requirements:
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(UD2x18 x) pure returns (uint40 result) {
    uint64 xUint = UD2x18.unwrap(x);
    if (xUint > uint64(Common.MAX_UINT40)) {
        revert Errors.PRBMath_UD2x18_IntoUint40_Overflow(x);
    }
    result = uint40(xUint);
}

/// @notice Alias for {wrap}.
function ud2x18(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

/// @notice Unwrap a UD2x18 number into uint64.
function unwrap(UD2x18 x) pure returns (uint64 result) {
    result = UD2x18.unwrap(x);
}

/// @notice Wraps a uint64 number into UD2x18.
function wrap(uint64 x) pure returns (UD2x18 result) {
    result = UD2x18.wrap(x);
}

File 25 of 31 : Math.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as Errors;
import {
    uEXP_MAX_INPUT,
    uEXP2_MAX_INPUT,
    uEXP_MIN_THRESHOLD,
    uEXP2_MIN_THRESHOLD,
    uHALF_UNIT,
    uLOG2_10,
    uLOG2_E,
    uMAX_SD59x18,
    uMAX_WHOLE_SD59x18,
    uMIN_SD59x18,
    uMIN_WHOLE_SD59x18,
    UNIT,
    uUNIT,
    uUNIT_SQUARED,
    ZERO
} from "./Constants.sol";
import { wrap } from "./Helpers.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Calculates the absolute value of x.
///
/// @dev Requirements:
/// - x must be greater than `MIN_SD59x18`.
///
/// @param x The SD59x18 number for which to calculate the absolute value.
/// @param result The absolute value of x as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function abs(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Abs_MinSD59x18();
    }
    result = xInt < 0 ? wrap(-xInt) : x;
}

/// @notice Calculates the arithmetic average of x and y.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The arithmetic average as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function avg(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    unchecked {
        // This operation is equivalent to `x / 2 +  y / 2`, and it can never overflow.
        int256 sum = (xInt >> 1) + (yInt >> 1);

        if (sum < 0) {
            // If at least one of x and y is odd, add 1 to the result, because shifting negative numbers to the right
            // rounds toward negative infinity. The right part is equivalent to `sum + (x % 2 == 1 || y % 2 == 1)`.
            assembly ("memory-safe") {
                result := add(sum, and(or(xInt, yInt), 1))
            }
        } else {
            // Add 1 if both x and y are odd to account for the double 0.5 remainder truncated after shifting.
            result = wrap(sum + (xInt & yInt & 1));
        }
    }
}

/// @notice Yields the smallest whole number greater than or equal to x.
///
/// @dev Optimized for fractional value inputs, because every whole value has (1e18 - 1) fractional counterparts.
/// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be less than or equal to `MAX_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to ceil.
/// @param result The smallest whole number greater than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ceil(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt > uMAX_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Ceil_Overflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt > 0) {
                resultInt += uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Divides two SD59x18 numbers, returning a new SD59x18 number.
///
/// @dev This is an extension of {Common.mulDiv} for signed numbers, which works by computing the signs and the absolute
/// values separately.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv}.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The denominator must not be zero.
/// - The result must fit in SD59x18.
///
/// @param x The numerator as an SD59x18 number.
/// @param y The denominator as an SD59x18 number.
/// @param result The quotient as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function div(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Div_InputTooSmall();
    }

    // Get hold of the absolute values of x and y.
    uint256 xAbs;
    uint256 yAbs;
    unchecked {
        xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
        yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
    }

    // Compute the absolute value (x*UNIT÷y). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv(xAbs, uint256(uUNIT), yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Div_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Calculates the natural exponent of x using the following formula:
///
/// $$
/// e^x = 2^{x * log_2{e}}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}.
///
/// Requirements:
/// - Refer to the requirements in {exp2}.
/// - x must be less than 133_084258667509499441.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();

    // Any input less than the threshold returns zero.
    // This check also prevents an overflow for very small numbers.
    if (xInt < uEXP_MIN_THRESHOLD) {
        return ZERO;
    }

    // This check prevents values greater than 192e18 from being passed to {exp2}.
    if (xInt > uEXP_MAX_INPUT) {
        revert Errors.PRBMath_SD59x18_Exp_InputTooBig(x);
    }

    unchecked {
        // Inline the fixed-point multiplication to save gas.
        int256 doubleUnitProduct = xInt * uLOG2_E;
        result = exp2(wrap(doubleUnitProduct / uUNIT));
    }
}

/// @notice Calculates the binary exponent of x using the binary fraction method using the following formula:
///
/// $$
/// 2^{-x} = \frac{1}{2^x}
/// $$
///
/// @dev See https://ethereum.stackexchange.com/q/79903/24693.
///
/// Notes:
/// - If x is less than -59_794705707972522261, the result is zero.
///
/// Requirements:
/// - x must be less than 192e18.
/// - The result must fit in SD59x18.
///
/// @param x The exponent as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function exp2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        // The inverse of any number less than the threshold is truncated to zero.
        if (xInt < uEXP2_MIN_THRESHOLD) {
            return ZERO;
        }

        unchecked {
            // Inline the fixed-point inversion to save gas.
            result = wrap(uUNIT_SQUARED / exp2(wrap(-xInt)).unwrap());
        }
    } else {
        // Numbers greater than or equal to 192e18 don't fit in the 192.64-bit format.
        if (xInt > uEXP2_MAX_INPUT) {
            revert Errors.PRBMath_SD59x18_Exp2_InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x_192x64 = uint256((xInt << 64) / uUNIT);

            // It is safe to cast the result to int256 due to the checks above.
            result = wrap(int256(Common.exp2(x_192x64)));
        }
    }
}

/// @notice Yields the greatest whole number less than or equal to x.
///
/// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional
/// counterparts. See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
///
/// Requirements:
/// - x must be greater than or equal to `MIN_WHOLE_SD59x18`.
///
/// @param x The SD59x18 number to floor.
/// @param result The greatest whole number less than or equal to x, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function floor(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < uMIN_WHOLE_SD59x18) {
        revert Errors.PRBMath_SD59x18_Floor_Underflow(x);
    }

    int256 remainder = xInt % uUNIT;
    if (remainder == 0) {
        result = x;
    } else {
        unchecked {
            // Solidity uses C fmod style, which returns a modulus with the same sign as x.
            int256 resultInt = xInt - remainder;
            if (xInt < 0) {
                resultInt -= uUNIT;
            }
            result = wrap(resultInt);
        }
    }
}

/// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right.
/// of the radix point for negative numbers.
/// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
/// @param x The SD59x18 number to get the fractional part of.
/// @param result The fractional part of x as an SD59x18 number.
function frac(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % uUNIT);
}

/// @notice Calculates the geometric mean of x and y, i.e. $\sqrt{x * y}$.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x * y must fit in SD59x18.
/// - x * y must not be negative, since complex numbers are not supported.
///
/// @param x The first operand as an SD59x18 number.
/// @param y The second operand as an SD59x18 number.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function gm(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == 0 || yInt == 0) {
        return ZERO;
    }

    unchecked {
        // Equivalent to `xy / x != y`. Checking for overflow this way is faster than letting Solidity do it.
        int256 xyInt = xInt * yInt;
        if (xyInt / xInt != yInt) {
            revert Errors.PRBMath_SD59x18_Gm_Overflow(x, y);
        }

        // The product must not be negative, since complex numbers are not supported.
        if (xyInt < 0) {
            revert Errors.PRBMath_SD59x18_Gm_NegativeProduct(x, y);
        }

        // We don't need to multiply the result by `UNIT` here because the x*y product picked up a factor of `UNIT`
        // during multiplication. See the comments in {Common.sqrt}.
        uint256 resultUint = Common.sqrt(uint256(xyInt));
        result = wrap(int256(resultUint));
    }
}

/// @notice Calculates the inverse of x.
///
/// @dev Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x must not be zero.
///
/// @param x The SD59x18 number for which to calculate the inverse.
/// @return result The inverse as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function inv(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(uUNIT_SQUARED / x.unwrap());
}

/// @notice Calculates the natural logarithm of x using the following formula:
///
/// $$
/// ln{x} = log_2{x} / log_2{e}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
/// - The precision isn't sufficiently fine-grained to return exactly `UNIT` when the input is `E`.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the natural logarithm.
/// @return result The natural logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function ln(SD59x18 x) pure returns (SD59x18 result) {
    // Inline the fixed-point multiplication to save gas. This is overflow-safe because the maximum value that
    // {log2} can return is ~195_205294292027477728.
    result = wrap(log2(x).unwrap() * uUNIT / uLOG2_E);
}

/// @notice Calculates the common logarithm of x using the following formula:
///
/// $$
/// log_{10}{x} = log_2{x} / log_2{10}
/// $$
///
/// However, if x is an exact power of ten, a hard coded value is returned.
///
/// @dev Notes:
/// - Refer to the notes in {log2}.
///
/// Requirements:
/// - Refer to the requirements in {log2}.
///
/// @param x The SD59x18 number for which to calculate the common logarithm.
/// @return result The common logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log10(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    // Note that the `mul` in this block is the standard multiplication operation, not {SD59x18.mul}.
    // prettier-ignore
    assembly ("memory-safe") {
        switch x
        case 1 { result := mul(uUNIT, sub(0, 18)) }
        case 10 { result := mul(uUNIT, sub(1, 18)) }
        case 100 { result := mul(uUNIT, sub(2, 18)) }
        case 1000 { result := mul(uUNIT, sub(3, 18)) }
        case 10000 { result := mul(uUNIT, sub(4, 18)) }
        case 100000 { result := mul(uUNIT, sub(5, 18)) }
        case 1000000 { result := mul(uUNIT, sub(6, 18)) }
        case 10000000 { result := mul(uUNIT, sub(7, 18)) }
        case 100000000 { result := mul(uUNIT, sub(8, 18)) }
        case 1000000000 { result := mul(uUNIT, sub(9, 18)) }
        case 10000000000 { result := mul(uUNIT, sub(10, 18)) }
        case 100000000000 { result := mul(uUNIT, sub(11, 18)) }
        case 1000000000000 { result := mul(uUNIT, sub(12, 18)) }
        case 10000000000000 { result := mul(uUNIT, sub(13, 18)) }
        case 100000000000000 { result := mul(uUNIT, sub(14, 18)) }
        case 1000000000000000 { result := mul(uUNIT, sub(15, 18)) }
        case 10000000000000000 { result := mul(uUNIT, sub(16, 18)) }
        case 100000000000000000 { result := mul(uUNIT, sub(17, 18)) }
        case 1000000000000000000 { result := 0 }
        case 10000000000000000000 { result := uUNIT }
        case 100000000000000000000 { result := mul(uUNIT, 2) }
        case 1000000000000000000000 { result := mul(uUNIT, 3) }
        case 10000000000000000000000 { result := mul(uUNIT, 4) }
        case 100000000000000000000000 { result := mul(uUNIT, 5) }
        case 1000000000000000000000000 { result := mul(uUNIT, 6) }
        case 10000000000000000000000000 { result := mul(uUNIT, 7) }
        case 100000000000000000000000000 { result := mul(uUNIT, 8) }
        case 1000000000000000000000000000 { result := mul(uUNIT, 9) }
        case 10000000000000000000000000000 { result := mul(uUNIT, 10) }
        case 100000000000000000000000000000 { result := mul(uUNIT, 11) }
        case 1000000000000000000000000000000 { result := mul(uUNIT, 12) }
        case 10000000000000000000000000000000 { result := mul(uUNIT, 13) }
        case 100000000000000000000000000000000 { result := mul(uUNIT, 14) }
        case 1000000000000000000000000000000000 { result := mul(uUNIT, 15) }
        case 10000000000000000000000000000000000 { result := mul(uUNIT, 16) }
        case 100000000000000000000000000000000000 { result := mul(uUNIT, 17) }
        case 1000000000000000000000000000000000000 { result := mul(uUNIT, 18) }
        case 10000000000000000000000000000000000000 { result := mul(uUNIT, 19) }
        case 100000000000000000000000000000000000000 { result := mul(uUNIT, 20) }
        case 1000000000000000000000000000000000000000 { result := mul(uUNIT, 21) }
        case 10000000000000000000000000000000000000000 { result := mul(uUNIT, 22) }
        case 100000000000000000000000000000000000000000 { result := mul(uUNIT, 23) }
        case 1000000000000000000000000000000000000000000 { result := mul(uUNIT, 24) }
        case 10000000000000000000000000000000000000000000 { result := mul(uUNIT, 25) }
        case 100000000000000000000000000000000000000000000 { result := mul(uUNIT, 26) }
        case 1000000000000000000000000000000000000000000000 { result := mul(uUNIT, 27) }
        case 10000000000000000000000000000000000000000000000 { result := mul(uUNIT, 28) }
        case 100000000000000000000000000000000000000000000000 { result := mul(uUNIT, 29) }
        case 1000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 30) }
        case 10000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 31) }
        case 100000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 32) }
        case 1000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 33) }
        case 10000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 34) }
        case 100000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 35) }
        case 1000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 36) }
        case 10000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 37) }
        case 100000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 38) }
        case 1000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 39) }
        case 10000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 40) }
        case 100000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 41) }
        case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 42) }
        case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 43) }
        case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 44) }
        case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 45) }
        case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 46) }
        case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 47) }
        case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 48) }
        case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 49) }
        case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 50) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 51) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 52) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 53) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 54) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 55) }
        case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 56) }
        case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 57) }
        case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(uUNIT, 58) }
        default { result := uMAX_SD59x18 }
    }

    if (result.unwrap() == uMAX_SD59x18) {
        unchecked {
            // Inline the fixed-point division to save gas.
            result = wrap(log2(x).unwrap() * uUNIT / uLOG2_10);
        }
    }
}

/// @notice Calculates the binary logarithm of x using the iterative approximation algorithm:
///
/// $$
/// log_2{x} = n + log_2{y}, \text{ where } y = x*2^{-n}, \ y \in [1, 2)
/// $$
///
/// For $0 \leq x \lt 1$, the input is inverted:
///
/// $$
/// log_2{x} = -log_2{\frac{1}{x}}
/// $$
///
/// @dev See https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation.
///
/// Notes:
/// - Due to the lossy precision of the iterative approximation, the results are not perfectly accurate to the last decimal.
///
/// Requirements:
/// - x must be greater than zero.
///
/// @param x The SD59x18 number for which to calculate the binary logarithm.
/// @return result The binary logarithm as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function log2(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt <= 0) {
        revert Errors.PRBMath_SD59x18_Log_InputTooSmall(x);
    }

    unchecked {
        int256 sign;
        if (xInt >= uUNIT) {
            sign = 1;
        } else {
            sign = -1;
            // Inline the fixed-point inversion to save gas.
            xInt = uUNIT_SQUARED / xInt;
        }

        // Calculate the integer part of the logarithm.
        uint256 n = Common.msb(uint256(xInt / uUNIT));

        // This is the integer part of the logarithm as an SD59x18 number. The operation can't overflow
        // because n is at most 255, `UNIT` is 1e18, and the sign is either 1 or -1.
        int256 resultInt = int256(n) * uUNIT;

        // Calculate $y = x * 2^{-n}$.
        int256 y = xInt >> n;

        // If y is the unit number, the fractional part is zero.
        if (y == uUNIT) {
            return wrap(resultInt * sign);
        }

        // Calculate the fractional part via the iterative approximation.
        // The `delta >>= 1` part is equivalent to `delta /= 2`, but shifting bits is more gas efficient.
        int256 DOUBLE_UNIT = 2e18;
        for (int256 delta = uHALF_UNIT; delta > 0; delta >>= 1) {
            y = (y * y) / uUNIT;

            // Is y^2 >= 2e18 and so in the range [2e18, 4e18)?
            if (y >= DOUBLE_UNIT) {
                // Add the 2^{-m} factor to the logarithm.
                resultInt = resultInt + delta;

                // Halve y, which corresponds to z/2 in the Wikipedia article.
                y >>= 1;
            }
        }
        resultInt *= sign;
        result = wrap(resultInt);
    }
}

/// @notice Multiplies two SD59x18 numbers together, returning a new SD59x18 number.
///
/// @dev Notes:
/// - Refer to the notes in {Common.mulDiv18}.
///
/// Requirements:
/// - Refer to the requirements in {Common.mulDiv18}.
/// - None of the inputs can be `MIN_SD59x18`.
/// - The result must fit in SD59x18.
///
/// @param x The multiplicand as an SD59x18 number.
/// @param y The multiplier as an SD59x18 number.
/// @return result The product as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function mul(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();
    if (xInt == uMIN_SD59x18 || yInt == uMIN_SD59x18) {
        revert Errors.PRBMath_SD59x18_Mul_InputTooSmall();
    }

    // Get hold of the absolute values of x and y.
    uint256 xAbs;
    uint256 yAbs;
    unchecked {
        xAbs = xInt < 0 ? uint256(-xInt) : uint256(xInt);
        yAbs = yInt < 0 ? uint256(-yInt) : uint256(yInt);
    }

    // Compute the absolute value (x*y÷UNIT). The resulting value must fit in SD59x18.
    uint256 resultAbs = Common.mulDiv18(xAbs, yAbs);
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Mul_Overflow(x, y);
    }

    // Check if x and y have the same sign using two's complement representation. The left-most bit represents the sign (1 for
    // negative, 0 for positive or zero).
    bool sameSign = (xInt ^ yInt) > -1;

    // If the inputs have the same sign, the result should be positive. Otherwise, it should be negative.
    unchecked {
        result = wrap(sameSign ? int256(resultAbs) : -int256(resultAbs));
    }
}

/// @notice Raises x to the power of y using the following formula:
///
/// $$
/// x^y = 2^{log_2{x} * y}
/// $$
///
/// @dev Notes:
/// - Refer to the notes in {exp2}, {log2}, and {mul}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {exp2}, {log2}, and {mul}.
///
/// @param x The base as an SD59x18 number.
/// @param y Exponent to raise x to, as an SD59x18 number
/// @return result x raised to power y, as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function pow(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    int256 yInt = y.unwrap();

    // If both x and y are zero, the result is `UNIT`. If just x is zero, the result is always zero.
    if (xInt == 0) {
        return yInt == 0 ? UNIT : ZERO;
    }
    // If x is `UNIT`, the result is always `UNIT`.
    else if (xInt == uUNIT) {
        return UNIT;
    }

    // If y is zero, the result is always `UNIT`.
    if (yInt == 0) {
        return UNIT;
    }
    // If y is `UNIT`, the result is always x.
    else if (yInt == uUNIT) {
        return x;
    }

    // Calculate the result using the formula.
    result = exp2(mul(log2(x), y));
}

/// @notice Raises x (an SD59x18 number) to the power y (an unsigned basic integer) using the well-known
/// algorithm "exponentiation by squaring".
///
/// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring.
///
/// Notes:
/// - Refer to the notes in {Common.mulDiv18}.
/// - Returns `UNIT` for 0^0.
///
/// Requirements:
/// - Refer to the requirements in {abs} and {Common.mulDiv18}.
/// - The result must fit in SD59x18.
///
/// @param x The base as an SD59x18 number.
/// @param y The exponent as a uint256.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function powu(SD59x18 x, uint256 y) pure returns (SD59x18 result) {
    uint256 xAbs = uint256(abs(x).unwrap());

    // Calculate the first iteration of the loop in advance.
    uint256 resultAbs = y & 1 > 0 ? xAbs : uint256(uUNIT);

    // Equivalent to `for(y /= 2; y > 0; y /= 2)`.
    uint256 yAux = y;
    for (yAux >>= 1; yAux > 0; yAux >>= 1) {
        xAbs = Common.mulDiv18(xAbs, xAbs);

        // Equivalent to `y % 2 == 1`.
        if (yAux & 1 > 0) {
            resultAbs = Common.mulDiv18(resultAbs, xAbs);
        }
    }

    // The result must fit in SD59x18.
    if (resultAbs > uint256(uMAX_SD59x18)) {
        revert Errors.PRBMath_SD59x18_Powu_Overflow(x, y);
    }

    unchecked {
        // Is the base negative and the exponent odd? If yes, the result should be negative.
        int256 resultInt = int256(resultAbs);
        bool isNegative = x.unwrap() < 0 && y & 1 == 1;
        if (isNegative) {
            resultInt = -resultInt;
        }
        result = wrap(resultInt);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - Only the positive root is returned.
/// - The result is rounded toward zero.
///
/// Requirements:
/// - x cannot be negative, since complex numbers are not supported.
/// - x must be less than `MAX_SD59x18 / UNIT`.
///
/// @param x The SD59x18 number for which to calculate the square root.
/// @return result The result as an SD59x18 number.
/// @custom:smtchecker abstract-function-nondet
function sqrt(SD59x18 x) pure returns (SD59x18 result) {
    int256 xInt = x.unwrap();
    if (xInt < 0) {
        revert Errors.PRBMath_SD59x18_Sqrt_NegativeInput(x);
    }
    if (xInt > uMAX_SD59x18 / uUNIT) {
        revert Errors.PRBMath_SD59x18_Sqrt_Overflow(x);
    }

    unchecked {
        // Multiply x by `UNIT` to account for the factor of `UNIT` picked up when multiplying two SD59x18 numbers.
        // In this case, the two numbers are both the square root.
        uint256 resultUint = Common.sqrt(uint256(xInt * uUNIT));
        result = wrap(int256(resultUint));
    }
}

File 26 of 31 : Helpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { wrap } from "./Casting.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Implements the checked addition operation (+) in the SD59x18 type.
function add(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() + y.unwrap());
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and(SD59x18 x, int256 bits) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & bits);
}

/// @notice Implements the AND (&) bitwise operation in the SD59x18 type.
function and2(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    return wrap(x.unwrap() & y.unwrap());
}

/// @notice Implements the equal (=) operation in the SD59x18 type.
function eq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() == y.unwrap();
}

/// @notice Implements the greater than operation (>) in the SD59x18 type.
function gt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() > y.unwrap();
}

/// @notice Implements the greater than or equal to operation (>=) in the SD59x18 type.
function gte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() >= y.unwrap();
}

/// @notice Implements a zero comparison check function in the SD59x18 type.
function isZero(SD59x18 x) pure returns (bool result) {
    result = x.unwrap() == 0;
}

/// @notice Implements the left shift operation (<<) in the SD59x18 type.
function lshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() << bits);
}

/// @notice Implements the lower than operation (<) in the SD59x18 type.
function lt(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() < y.unwrap();
}

/// @notice Implements the lower than or equal to operation (<=) in the SD59x18 type.
function lte(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() <= y.unwrap();
}

/// @notice Implements the unchecked modulo operation (%) in the SD59x18 type.
function mod(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() % y.unwrap());
}

/// @notice Implements the not equal operation (!=) in the SD59x18 type.
function neq(SD59x18 x, SD59x18 y) pure returns (bool result) {
    result = x.unwrap() != y.unwrap();
}

/// @notice Implements the NOT (~) bitwise operation in the SD59x18 type.
function not(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(~x.unwrap());
}

/// @notice Implements the OR (|) bitwise operation in the SD59x18 type.
function or(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() | y.unwrap());
}

/// @notice Implements the right shift operation (>>) in the SD59x18 type.
function rshift(SD59x18 x, uint256 bits) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() >> bits);
}

/// @notice Implements the checked subtraction operation (-) in the SD59x18 type.
function sub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() - y.unwrap());
}

/// @notice Implements the checked unary minus operation (-) in the SD59x18 type.
function unary(SD59x18 x) pure returns (SD59x18 result) {
    result = wrap(-x.unwrap());
}

/// @notice Implements the unchecked addition operation (+) in the SD59x18 type.
function uncheckedAdd(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() + y.unwrap());
    }
}

/// @notice Implements the unchecked subtraction operation (-) in the SD59x18 type.
function uncheckedSub(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(x.unwrap() - y.unwrap());
    }
}

/// @notice Implements the unchecked unary minus operation (-) in the SD59x18 type.
function uncheckedUnary(SD59x18 x) pure returns (SD59x18 result) {
    unchecked {
        result = wrap(-x.unwrap());
    }
}

/// @notice Implements the XOR (^) bitwise operation in the SD59x18 type.
function xor(SD59x18 x, SD59x18 y) pure returns (SD59x18 result) {
    result = wrap(x.unwrap() ^ y.unwrap());
}

File 27 of 31 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "./Errors.sol" as CastingErrors;
import { MAX_UINT128, MAX_UINT40 } from "../Common.sol";
import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol";
import { SD1x18 } from "../sd1x18/ValueType.sol";
import { uMAX_UD2x18 } from "../ud2x18/Constants.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD59x18 } from "./ValueType.sol";

/// @notice Casts an SD59x18 number into int256.
/// @dev This is basically a functional alias for {unwrap}.
function intoInt256(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Casts an SD59x18 number into SD1x18.
/// @dev Requirements:
/// - x must be greater than or equal to `uMIN_SD1x18`.
/// - x must be less than or equal to `uMAX_SD1x18`.
function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < uMIN_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Underflow(x);
    }
    if (xInt > uMAX_SD1x18) {
        revert CastingErrors.PRBMath_SD59x18_IntoSD1x18_Overflow(x);
    }
    result = SD1x18.wrap(int64(xInt));
}

/// @notice Casts an SD59x18 number into UD2x18.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UD2x18`.
function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Underflow(x);
    }
    if (xInt > int256(uint256(uMAX_UD2x18))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD2x18_Overflow(x);
    }
    result = UD2x18.wrap(uint64(uint256(xInt)));
}

/// @notice Casts an SD59x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD59x18 x) pure returns (UD60x18 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD59x18 x) pure returns (uint256 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint256_Underflow(x);
    }
    result = uint256(xInt);
}

/// @notice Casts an SD59x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `uMAX_UINT128`.
function intoUint128(SD59x18 x) pure returns (uint128 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT128))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint128_Overflow(x);
    }
    result = uint128(uint256(xInt));
}

/// @notice Casts an SD59x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD59x18 x) pure returns (uint40 result) {
    int256 xInt = SD59x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Underflow(x);
    }
    if (xInt > int256(uint256(MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD59x18_IntoUint40_Overflow(x);
    }
    result = uint40(uint256(xInt));
}

/// @notice Alias for {wrap}.
function sd(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Alias for {wrap}.
function sd59x18(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

/// @notice Unwraps an SD59x18 number into int256.
function unwrap(SD59x18 x) pure returns (int256 result) {
    result = SD59x18.unwrap(x);
}

/// @notice Wraps an int256 number into SD59x18.
function wrap(int256 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(x);
}

File 28 of 31 : Casting.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import "../Common.sol" as Common;
import "./Errors.sol" as CastingErrors;
import { SD59x18 } from "../sd59x18/ValueType.sol";
import { UD2x18 } from "../ud2x18/ValueType.sol";
import { UD60x18 } from "../ud60x18/ValueType.sol";
import { SD1x18 } from "./ValueType.sol";

/// @notice Casts an SD1x18 number into SD59x18.
/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18.
function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) {
    result = SD59x18.wrap(int256(SD1x18.unwrap(x)));
}

/// @notice Casts an SD1x18 number into UD2x18.
/// - x must be positive.
function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD2x18_Underflow(x);
    }
    result = UD2x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into UD60x18.
/// @dev Requirements:
/// - x must be positive.
function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUD60x18_Underflow(x);
    }
    result = UD60x18.wrap(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint256.
/// @dev Requirements:
/// - x must be positive.
function intoUint256(SD1x18 x) pure returns (uint256 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x);
    }
    result = uint256(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint128.
/// @dev Requirements:
/// - x must be positive.
function intoUint128(SD1x18 x) pure returns (uint128 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x);
    }
    result = uint128(uint64(xInt));
}

/// @notice Casts an SD1x18 number into uint40.
/// @dev Requirements:
/// - x must be positive.
/// - x must be less than or equal to `MAX_UINT40`.
function intoUint40(SD1x18 x) pure returns (uint40 result) {
    int64 xInt = SD1x18.unwrap(x);
    if (xInt < 0) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Underflow(x);
    }
    if (xInt > int64(uint64(Common.MAX_UINT40))) {
        revert CastingErrors.PRBMath_SD1x18_ToUint40_Overflow(x);
    }
    result = uint40(uint64(xInt));
}

/// @notice Alias for {wrap}.
function sd1x18(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

/// @notice Unwraps an SD1x18 number into int64.
function unwrap(SD1x18 x) pure returns (int64 result) {
    result = SD1x18.unwrap(x);
}

/// @notice Wraps an int64 number into SD1x18.
function wrap(int64 x) pure returns (SD1x18 result) {
    result = SD1x18.wrap(x);
}

File 29 of 31 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { UD2x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in SD1x18.
error PRBMath_UD2x18_IntoSD1x18_Overflow(UD2x18 x);

/// @notice Thrown when trying to cast a UD2x18 number that doesn't fit in uint40.
error PRBMath_UD2x18_IntoUint40_Overflow(UD2x18 x);

File 30 of 31 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD59x18 } from "./ValueType.sol";

/// @notice Thrown when taking the absolute value of `MIN_SD59x18`.
error PRBMath_SD59x18_Abs_MinSD59x18();

/// @notice Thrown when ceiling a number overflows SD59x18.
error PRBMath_SD59x18_Ceil_Overflow(SD59x18 x);

/// @notice Thrown when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMath_SD59x18_Convert_Overflow(int256 x);

/// @notice Thrown when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMath_SD59x18_Convert_Underflow(int256 x);

/// @notice Thrown when dividing two numbers and one of them is `MIN_SD59x18`.
error PRBMath_SD59x18_Div_InputTooSmall();

/// @notice Thrown when dividing two numbers and one of the intermediary unsigned results overflows SD59x18.
error PRBMath_SD59x18_Div_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the natural exponent of a base greater than 133_084258667509499441.
error PRBMath_SD59x18_Exp_InputTooBig(SD59x18 x);

/// @notice Thrown when taking the binary exponent of a base greater than 192e18.
error PRBMath_SD59x18_Exp2_InputTooBig(SD59x18 x);

/// @notice Thrown when flooring a number underflows SD59x18.
error PRBMath_SD59x18_Floor_Underflow(SD59x18 x);

/// @notice Thrown when taking the geometric mean of two numbers and their product is negative.
error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y);

/// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18.
error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18.
error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18.
error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18.
error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128.
error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256.
error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x);

/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40.
error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x);

/// @notice Thrown when taking the logarithm of a number less than or equal to zero.
error PRBMath_SD59x18_Log_InputTooSmall(SD59x18 x);

/// @notice Thrown when multiplying two numbers and one of the inputs is `MIN_SD59x18`.
error PRBMath_SD59x18_Mul_InputTooSmall();

/// @notice Thrown when multiplying two numbers and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Mul_Overflow(SD59x18 x, SD59x18 y);

/// @notice Thrown when raising a number to a power and the intermediary absolute result overflows SD59x18.
error PRBMath_SD59x18_Powu_Overflow(SD59x18 x, uint256 y);

/// @notice Thrown when taking the square root of a negative number.
error PRBMath_SD59x18_Sqrt_NegativeInput(SD59x18 x);

/// @notice Thrown when the calculating the square root overflows SD59x18.
error PRBMath_SD59x18_Sqrt_Overflow(SD59x18 x);

File 31 of 31 : Errors.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

import { SD1x18 } from "./ValueType.sol";

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18.
error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18.
error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128.
error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256.
error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x);

/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40.
error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x);

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "@prb/math/=lib/prb-math/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
  ]
}

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CAMPAIGN_PAUSED","type":"error"},{"inputs":[],"name":"CHECK_QAS_OR_KPIS","type":"error"},{"inputs":[{"internalType":"string","name":"kpi","type":"string"}],"name":"DUPLICATE_KPI","type":"error"},{"inputs":[{"internalType":"string","name":"qa","type":"string"}],"name":"DUPLICATE_QA","type":"error"},{"inputs":[{"internalType":"string","name":"kpiKind","type":"string"}],"name":"GIVEN_KPI_KIND_DOESNT_EXIST_ON_CAMPAIGN","type":"error"},{"inputs":[{"internalType":"string","name":"qaKind","type":"string"}],"name":"GIVEN_QA_KIND_DOESNT_EXIST_ON_CAMPAIGN","type":"error"},{"inputs":[],"name":"INVALID_CONTENT_LINK","type":"error"},{"inputs":[],"name":"NOT_ELIGIBLE_FOR_WITHDRAW","type":"error"},{"inputs":[],"name":"NO_CONTENT_AUTHOR","type":"error"},{"inputs":[],"name":"ONLY_CLAIMABLE_STATUS","type":"error"},{"inputs":[],"name":"ONLY_CONTENTS","type":"error"},{"inputs":[],"name":"ONLY_FACTORY","type":"error"},{"inputs":[],"name":"ONLY_FINALIZER_ORACLE","type":"error"},{"inputs":[],"name":"ONLY_FINISHED_CAMPAIGN_STATUS","type":"error"},{"inputs":[{"internalType":"enum Status","name":"status","type":"uint8"}],"name":"ONLY_IN_PROGRESS_STATUS","type":"error"},{"inputs":[],"name":"ONLY_OWNER","type":"error"},{"inputs":[],"name":"ONLY_PROTOCOL_ADMIN","type":"error"},{"inputs":[{"internalType":"enum Status","name":"status","type":"uint8"}],"name":"ONLY_UPCOMING_OR_IN_PROGRESS_STATUS","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"PRBMath_MulDiv18_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Exp2_InputTooBig","type":"error"},{"inputs":[{"internalType":"UD60x18","name":"x","type":"uint256"}],"name":"PRBMath_UD60x18_Log_InputTooSmall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"addedAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"lastBudget","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newBudget","type":"uint256"},{"indexed":false,"internalType":"address","name":"donator","type":"address"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"BudgetIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"finalizer","type":"address"},{"indexed":true,"internalType":"enum Finalizer","name":"finalizerType","type":"uint8"},{"indexed":true,"internalType":"uint256","name":"refundedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBudget","type":"uint256"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"finishedAt","type":"uint256"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"paused","type":"bool"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"CampaignPauseToggled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contentCreator","type":"address"},{"indexed":true,"internalType":"uint256","name":"rewardedAmount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"refundAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPaidAmount","type":"uint256"},{"indexed":false,"internalType":"string","name":"contentLink","type":"string"},{"indexed":false,"internalType":"address","name":"campaign","type":"address"}],"name":"Claimed","type":"event"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"applicationFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"applyContent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"budgetInfo","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"contentEffectiveKPI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contents","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentStatus","outputs":[{"internalType":"enum Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"details","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"ipfsCID","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"}],"internalType":"struct BasicInfo","name":"basicInfo_","type":"tuple"},{"components":[{"internalType":"uint64","name":"initTime","type":"uint64"},{"internalType":"address","name":"clonedCampaign","type":"address"},{"internalType":"address","name":"implementation","type":"address"}],"internalType":"struct CampaignInitData","name":"initData_","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo_","type":"tuple"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"decimals","type":"uint256"}],"internalType":"struct Token","name":"token_","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs_","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData_","type":"tuple[]"},{"components":[{"internalType":"uint256","name":"totalPosts","type":"uint256"},{"internalType":"uint256","name":"totalEligibleContents","type":"uint256"},{"internalType":"uint256","name":"totalEffectiveKPIs","type":"uint256"},{"internalType":"uint256","name":"lastPostTime","type":"uint256"},{"internalType":"uint256","name":"lastUpdatedTime","type":"uint256"},{"internalType":"uint256","name":"applicationFee","type":"uint256"}],"internalType":"struct PostInfo","name":"postInfo_","type":"tuple"},{"internalType":"enum Status","name":"status_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"finalizeFinishedCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeInProgressCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"}],"name":"getContentCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"increaseBudget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"ipfsCID","type":"string"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint128","name":"maxPerPost","type":"uint128"},{"internalType":"uint128","name":"reservedAmount","type":"uint128"}],"internalType":"struct BudgetInfo","name":"budgetInfo_","type":"tuple"},{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"socialKPIs_","type":"tuple"},{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qaData_","type":"tuple[]"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"qaData","outputs":[{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refundAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"registeredActionKind","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum QA_METHOD","name":"method","type":"uint8"},{"internalType":"uint88","name":"pct","type":"uint88"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"string","name":"kind","type":"string"}],"internalType":"struct QualificationData[]","name":"qas","type":"tuple[]"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"name":"rewardEstimation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"socialKPIs","outputs":[{"components":[{"internalType":"string","name":"social","type":"string"},{"components":[{"internalType":"uint8","name":"pct","type":"uint8"},{"internalType":"uint248","name":"min","type":"uint248"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"string","name":"method","type":"string"},{"internalType":"string","name":"extra","type":"string"}],"internalType":"struct KPI[]","name":"kpis","type":"tuple[]"}],"internalType":"struct SocialKPIs","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"togglePauseCampaign","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contentLink","type":"string"},{"internalType":"uint256","name":"additionalContentEffectiveKPI","type":"uint256"},{"internalType":"bool","name":"additional","type":"bool"}],"name":"updateKPIs","outputs":[{"internalType":"uint256","name":"totalEffectiveKPIs","type":"uint256"},{"internalType":"uint256","name":"contentEffectiveKPI_","type":"uint256"},{"internalType":"uint256","name":"totalEligibleContents","type":"uint256"},{"internalType":"int8","name":"eligibilityPolarity","type":"int8"}],"stateMutability":"nonpayable","type":"function"}]

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.