ETH Price: $1,981.60 (-4.51%)

Contract

0x03dE624FeB08C0eDeff779ca5702AEf4B85D7f06
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Borrow From231997702025-08-22 23:01:11196 days ago1755903671IN
Juicebox: REV Loans V4
0 ETH0.000869370.87898459
Borrow From231505332025-08-16 2:10:59202 days ago1755310259IN
Juicebox: REV Loans V4
0 ETH0.002152112.26848896
Borrow From228632812025-07-06 22:56:11243 days ago1751842571IN
Juicebox: REV Loans V4
0 ETH0.002128392.24351943
Borrow From226871292025-06-12 7:55:35267 days ago1749714935IN
Juicebox: REV Loans V4
0 ETH0.004743723.64899514
Borrow From225414692025-05-22 22:42:59288 days ago1747953779IN
Juicebox: REV Loans V4
0 ETH0.001229861.25126744
Borrow From225340052025-05-21 21:40:35289 days ago1747863635IN
Juicebox: REV Loans V4
0 ETH0.00118441.32896279
Borrow From225339002025-05-21 21:19:35289 days ago1747862375IN
Juicebox: REV Loans V4
0 ETH0.001086121.24255887
Borrow From225338802025-05-21 21:15:35289 days ago1747862135IN
Juicebox: REV Loans V4
0 ETH0.000976861.11755705
Borrow From225187022025-05-19 18:11:47291 days ago1747678307IN
Juicebox: REV Loans V4
0 ETH0.001957821.85737082

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Pay231997702025-08-22 23:01:11196 days ago1755903671
Juicebox: REV Loans V4
0.00655401 ETH
Transfer231997702025-08-22 23:01:11196 days ago1755903671
Juicebox: REV Loans V4
0.24643083 ETH
Pay231997702025-08-22 23:01:11196 days ago1755903671
Juicebox: REV Loans V4
0.0026216 ETH
Transfer231997702025-08-22 23:01:11196 days ago1755903671
Juicebox: REV Loans V4
0.25560644 ETH
Pay231505332025-08-16 2:10:59202 days ago1755310259
Juicebox: REV Loans V4
0.00181606 ETH
Transfer231505332025-08-16 2:10:59202 days ago1755310259
Juicebox: REV Loans V4
0.06828391 ETH
Pay231505332025-08-16 2:10:59202 days ago1755310259
Juicebox: REV Loans V4
0.00072642 ETH
Transfer231505332025-08-16 2:10:59202 days ago1755310259
Juicebox: REV Loans V4
0.0708264 ETH
Pay228632812025-07-06 22:56:11243 days ago1751842571
Juicebox: REV Loans V4
0.00061954 ETH
Transfer228632812025-07-06 22:56:11243 days ago1751842571
Juicebox: REV Loans V4
0.02329498 ETH
Pay228632812025-07-06 22:56:11243 days ago1751842571
Juicebox: REV Loans V4
0.00024781 ETH
Transfer228632812025-07-06 22:56:11243 days ago1751842571
Juicebox: REV Loans V4
0.02416234 ETH
Pay226871292025-06-12 7:55:35267 days ago1749714935
Juicebox: REV Loans V4
0.00014276 ETH
Transfer226871292025-06-12 7:55:35267 days ago1749714935
Juicebox: REV Loans V4
0.00536788 ETH
Pay226871292025-06-12 7:55:35267 days ago1749714935
Juicebox: REV Loans V4
0.0000571 ETH
Transfer226871292025-06-12 7:55:35267 days ago1749714935
Juicebox: REV Loans V4
0.00556774 ETH
Pay225414692025-05-22 22:42:59288 days ago1747953779
Juicebox: REV Loans V4
0.00600492 ETH
Transfer225414692025-05-22 22:42:59288 days ago1747953779
Juicebox: REV Loans V4
0.22578534 ETH
Pay225414692025-05-22 22:42:59288 days ago1747953779
Juicebox: REV Loans V4
0.00240197 ETH
Transfer225414692025-05-22 22:42:59288 days ago1747953779
Juicebox: REV Loans V4
0.23419224 ETH
Pay225340052025-05-21 21:40:35289 days ago1747863635
Juicebox: REV Loans V4
0.05968734 ETH
Transfer225340052025-05-21 21:40:35289 days ago1747863635
Juicebox: REV Loans V4
0.05550922 ETH
Pay225340052025-05-21 21:40:35289 days ago1747863635
Juicebox: REV Loans V4
0.00119374 ETH
Transfer225340052025-05-21 21:40:35289 days ago1747863635
Juicebox: REV Loans V4
0.11639031 ETH
Pay225339002025-05-21 21:19:35289 days ago1747862375
Juicebox: REV Loans V4
0.0002313 ETH
View All Internal Transactions
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
REVLoans

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 550 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {mulDiv} from "@prb/math/src/Common.sol";
import {IAllowanceTransfer} from "@uniswap/permit2/src/interfaces/IAllowanceTransfer.sol";
import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBPayoutTerminal} from "@bananapus/core/src/interfaces/IJBPayoutTerminal.sol";
import {IJBPrices} from "@bananapus/core/src/interfaces/IJBPrices.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {IJBTerminal} from "@bananapus/core/src/interfaces/IJBTerminal.sol";
import {IJBTokenUriResolver} from "@bananapus/core/src/interfaces/IJBTokenUriResolver.sol";
import {JBCashOuts} from "@bananapus/core/src/libraries/JBCashOuts.sol";
import {JBConstants} from "@bananapus/core/src/libraries/JBConstants.sol";
import {JBFees} from "@bananapus/core/src/libraries/JBFees.sol";
import {JBRulesetMetadataResolver} from "@bananapus/core/src/libraries/JBRulesetMetadataResolver.sol";
import {JBSurplus} from "@bananapus/core/src/libraries/JBSurplus.sol";
import {JBAccountingContext} from "@bananapus/core/src/structs/JBAccountingContext.sol";
import {JBRuleset} from "@bananapus/core/src/structs/JBRuleset.sol";
import {JBSingleAllowance} from "@bananapus/core/src/structs/JBSingleAllowance.sol";

import {IREVDeployer} from "./interfaces/IREVDeployer.sol";
import {IREVLoans} from "./interfaces/IREVLoans.sol";
import {REVLoan} from "./structs/REVLoan.sol";
import {REVLoanSource} from "./structs/REVLoanSource.sol";

/// @notice A contract for borrowing from revnets.
/// @dev Tokens used as collateral are burned, and reminted when the loan is paid off. This keeps the revnet's token
/// structure orderly.
/// @dev The borrowable amount is the same as the cash out amount.
/// @dev An upfront fee is taken when a loan is created. 2.5% is charged by the underlying protocol, 2.5% is charged
/// by the
/// revnet issuing the loan, and a variable amount charged by the revnet that receives the fees. This variable amount is
/// chosen by the borrower, the more paid upfront, the longer the prepaid duration. The loan can be repaid anytime
/// within the prepaid duration without additional fees.
/// After the prepaid duration, the loan will increasingly cost more to pay off. After 10 years, the loan collateral
/// cannot be
/// recouped.
/// @dev The loaned amounts include the fees taken, meaning the amount paid back is the amount borrowed plus the fees.
contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
    // A library that parses the packed ruleset metadata into a friendlier format.
    using JBRulesetMetadataResolver for JBRuleset;

    // A library that adds default safety checks to ERC20 functionality.
    using SafeERC20 for IERC20;

    //*********************************************************************//
    // --------------------------- custom errors ------------------------- //
    //*********************************************************************//

    error REVLoans_CollateralExceedsLoan(uint256 collateralToReturn, uint256 loanCollateral);
    error REVLoans_InvalidPrepaidFeePercent(uint256 prepaidFeePercent, uint256 min, uint256 max);
    error REVLoans_NotEnoughCollateral();
    error REVLoans_OverflowAlert(uint256 value, uint256 limit);
    error REVLoans_OverMaxRepayBorrowAmount(uint256 maxRepayBorrowAmount, uint256 repayBorrowAmount);
    error REVLoans_PermitAllowanceNotEnough(uint256 allowanceAmount, uint256 requiredAmount);
    error REVLoans_NewBorrowAmountGreaterThanLoanAmount(uint256 newBorrowAmount, uint256 loanAmount);
    error REVLoans_NoMsgValueAllowed();
    error REVLoans_LoanExpired(uint256 timeSinceLoanCreated, uint256 loanLiquidationDuration);
    error REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(uint256 newBorrowAmount, uint256 loanAmount);
    error REVLoans_RevnetsMismatch(address revnetOwner, address revnets);
    error REVLoans_Unauthorized(address caller, address owner);
    error REVLoans_UnderMinBorrowAmount(uint256 minBorrowAmount, uint256 borrowAmount);
    error REVLoans_ZeroCollateralLoanIsInvalid();

    //*********************************************************************//
    // ------------------------- public constants ------------------------ //
    //*********************************************************************//

    /// @dev After the prepaid duration, the loan will cost more to pay off. After 10 years, the loan
    /// collateral cannot be recouped. This means paying 50% of the loan amount upfront will pay for having access to
    /// the remaining 50% for 10 years,
    /// whereas paying 0% of the loan upfront will cost 100% of the loan amount to be paid off after 10 years. After 10
    /// years with repayment, both loans cost 100% and are liquidated.
    uint256 public constant override LOAN_LIQUIDATION_DURATION = 3650 days;

    /// @dev The maximum amount of a loan that can be prepaid at the time of borrowing, in terms of JBConstants.MAX_FEE.
    uint256 public constant override MAX_PREPAID_FEE_PERCENT = 500;

    /// @dev A fee of 1% is charged by the $REV revnet.
    uint256 public constant override REV_PREPAID_FEE_PERCENT = 10; // 1%

    /// @dev A fee of 2.5% is charged by the loan's source upfront.
    uint256 public constant override MIN_PREPAID_FEE_PERCENT = 25; // 2.5%

    //*********************************************************************//
    // -------------------- private constant properties ------------------ //
    //*********************************************************************//

    /// @notice Just a kind reminder to our readers.
    /// @dev Used in loan token ID generation.
    uint256 private constant _ONE_TRILLION = 1_000_000_000_000;

    //*********************************************************************//
    // --------------- public immutable stored properties ---------------- //
    //*********************************************************************//

    /// @notice The permit2 utility.
    IPermit2 public immutable override PERMIT2;

    /// @notice The controller of revnets that use this loans contract.
    IJBController public immutable override CONTROLLER;

    /// @notice Mints ERC-721s that represent project ownership and transfers.
    IREVDeployer public immutable override REVNETS;

    /// @notice The directory of terminals and controllers for revnets.
    IJBDirectory public immutable override DIRECTORY;

    /// @notice A contract that stores prices for each revnet.
    IJBPrices public immutable override PRICES;

    /// @notice Mints ERC-721s that represent revnet ownership and transfers.
    IJBProjects public immutable override PROJECTS;

    /// @notice The ID of the REV revnet that will receive the fees.
    uint256 public immutable override REV_ID;

    //*********************************************************************//
    // --------------------- public stored properties -------------------- //
    //*********************************************************************//

    /// @notice An indication if a revnet currently has outstanding loans from the specified terminal in the specified
    /// token.
    /// @custom:param revnetId The ID of the revnet issuing the loan.
    /// @custom:param terminal The terminal that the loan is issued from.
    /// @custom:param token The token being loaned.
    mapping(uint256 revnetId => mapping(IJBPayoutTerminal terminal => mapping(address token => bool))) public override
        isLoanSourceOf;

    /// @notice The amount of loans that have been created.
    /// @custom:param revnetId The ID of the revnet to get the number of loans from.
    mapping(uint256 revnetId => uint256) public override numberOfLoansFor;

    /// @notice The contract resolving each project ID to its ERC721 URI.
    IJBTokenUriResolver public override tokenUriResolver;

    /// @notice The total amount loaned out by a revnet from a specified terminal in a specified token.
    /// @custom:param revnetId The ID of the revnet issuing the loan.
    /// @custom:param terminal The terminal that the loan is issued from.
    /// @custom:param token The token being loaned.
    mapping(uint256 revnetId => mapping(IJBPayoutTerminal terminal => mapping(address token => uint256)))
        public
        override totalBorrowedFrom;

    /// @notice The total amount of collateral supporting a revnet's loans.
    /// @custom:param revnetId The ID of the revnet issuing the loan.
    mapping(uint256 revnetId => uint256) public override totalCollateralOf;

    //*********************************************************************//
    // --------------------- internal stored properties ------------------ //
    //*********************************************************************//

    /// @notice The sources of each revnet's loan.
    /// @custom:member revnetId The ID of the revnet issuing the loan.
    mapping(uint256 revnetId => REVLoanSource[]) internal _loanSourcesOf;

    /// @notice The loans.
    /// @custom:member The ID of the loan.
    mapping(uint256 loanId => REVLoan) internal _loanOf;

    //*********************************************************************//
    // -------------------------- constructor ---------------------------- //
    //*********************************************************************//

    /// @param revnets A contract from which revnets using this loans contract are deployed.
    /// @param revId The ID of the REV revnet that will receive the fees.
    /// @param owner The owner of the contract that can set the URI resolver.
    /// @param permit2 A permit2 utility.
    /// @param trustedForwarder A trusted forwarder of transactions to this contract.
    constructor(
        IREVDeployer revnets,
        uint256 revId,
        address owner,
        IPermit2 permit2,
        address trustedForwarder
    )
        ERC721("REV Loans", "$REVLOAN")
        ERC2771Context(trustedForwarder)
        Ownable(owner)
    {
        REVNETS = revnets;
        CONTROLLER = revnets.CONTROLLER();
        DIRECTORY = revnets.DIRECTORY();
        PRICES = revnets.CONTROLLER().PRICES();
        PROJECTS = revnets.PROJECTS();
        REV_ID = revId;
        PERMIT2 = permit2;
    }

    //*********************************************************************//
    // ------------------------- external views -------------------------- //
    //*********************************************************************//

    /// @notice The amount that can be borrowed from a revnet.
    /// @param revnetId The ID of the revnet to check for borrowable assets from.
    /// @param collateralCount The amount of collateral used to secure the loan.
    /// @param decimals The decimals the resulting fixed point value will include.
    /// @param currency The currency that the resulting amount should be in terms of.
    /// @return borrowableAmount The amount that can be borrowed from the revnet.
    function borrowableAmountFrom(
        uint256 revnetId,
        uint256 collateralCount,
        uint256 decimals,
        uint256 currency
    )
        external
        view
        returns (uint256)
    {
        return _borrowableAmountFrom({
            revnetId: revnetId,
            collateralCount: collateralCount,
            decimals: decimals,
            currency: currency,
            terminals: DIRECTORY.terminalsOf(revnetId)
        });
    }

    /// @notice Get a loan.
    /// @custom:member The ID of the loan.
    function loanOf(uint256 loanId) external view override returns (REVLoan memory) {
        return _loanOf[loanId];
    }

    /// @notice The sources of each revnet's loan.
    /// @custom:member revnetId The ID of the revnet issuing the loan.
    function loanSourcesOf(uint256 revnetId) external view override returns (REVLoanSource[] memory) {
        return _loanSourcesOf[revnetId];
    }

    //*********************************************************************//
    // -------------------------- public views --------------------------- //
    //*********************************************************************//

    /// @notice Determines the source fee amount for a loan being paid off a certain amount.
    /// @param loan The loan having its source fee amount determined.
    /// @param amount The amount being paid off.
    /// @return sourceFeeAmount The source fee amount for the loan.
    function determineSourceFeeAmount(REVLoan memory loan, uint256 amount) public view returns (uint256) {
        return _determineSourceFeeAmount(loan, amount);
    }

    /// @notice Returns the URI where the ERC-721 standard JSON of a loan is hosted.
    /// @param loanId The ID of the loan to get a URI of.
    /// @return The token URI to use for the provided `loanId`.
    function tokenURI(uint256 loanId) public view override returns (string memory) {
        // Keep a reference to the resolver.
        IJBTokenUriResolver resolver = tokenUriResolver;

        // If there's no resolver, there's no URI.
        if (resolver == IJBTokenUriResolver(address(0))) return "";

        // Return the resolved URI.
        return resolver.getUri(loanId);
    }

    /// @notice The revnet ID for the loan with the provided loan ID.
    /// @param loanId The loan ID of the loan to get the revent ID of.
    /// @return The ID of the revnet.
    function revnetIdOfLoanWith(uint256 loanId) public pure override returns (uint256) {
        return loanId / _ONE_TRILLION;
    }

    //*********************************************************************//
    // -------------------------- internal views ------------------------- //
    //*********************************************************************//

    /// @notice Checks this contract's balance of a specific token.
    /// @param token The address of the token to get this contract's balance of.
    /// @return This contract's balance.
    function _balanceOf(address token) internal view returns (uint256) {
        // If the `token` is native, get the native token balance.
        return token == JBConstants.NATIVE_TOKEN ? address(this).balance : IERC20(token).balanceOf(address(this));
    }

    /// @dev The amount that can be borrowed from a revnet given a certain amount of collateral.
    /// @param revnetId The ID of the revnet to check for borrowable assets from.
    /// @param collateralCount The amount of collateral that the loan will be collateralized with.
    /// @param decimals The decimals the resulting fixed point value will include.
    /// @param currency The currency that the resulting amount should be in terms of.
    /// @param terminals The terminals that the funds are being borrowed from.
    /// @return borrowableAmount The amount that can be borrowed from the revnet.
    function _borrowableAmountFrom(
        uint256 revnetId,
        uint256 collateralCount,
        uint256 decimals,
        uint256 currency,
        IJBTerminal[] memory terminals
    )
        internal
        view
        returns (uint256)
    {
        // Keep a reference to the current stage.
        // slither-disable-next-line unused-return
        (JBRuleset memory currentStage,) = CONTROLLER.currentRulesetOf(revnetId);

        // Get the surplus of all the revnet's terminals in terms of the native currency.
        uint256 totalSurplus = JBSurplus.currentSurplusOf({
            projectId: revnetId,
            terminals: terminals,
            accountingContexts: new JBAccountingContext[](0),
            decimals: decimals,
            currency: currency
        });

        // Get the total amount the revnet currently has loaned out, in terms of the native currency with 18
        // decimals.
        uint256 totalBorrowed = _totalBorrowedFrom({revnetId: revnetId, decimals: decimals, currency: currency});

        // Get the total amount of tokens in circulation.
        uint256 totalSupply = CONTROLLER.totalTokenSupplyWithReservedTokensOf(revnetId);

        // Get a refeerence to the collateral being used to secure loans.
        uint256 totalCollateral = totalCollateralOf[revnetId];

        // Proportional.
        return JBCashOuts.cashOutFrom({
            surplus: totalSurplus + totalBorrowed,
            cashOutCount: collateralCount,
            totalSupply: totalSupply + totalCollateral,
            cashOutTaxRate: currentStage.cashOutTaxRate()
        });
    }

    /// @notice The amount of the loan that should be borrowed for the given collateral amount.
    /// @param loan The loan having its borrow amount determined.
    /// @param revnetId The ID of the revnet to check for borrowable assets from.
    /// @param collateralCount The amount of collateral that the loan will be collateralized with.
    /// @return borrowAmount The amount of the loan that should be borrowed.
    function _borrowAmountFrom(
        REVLoan storage loan,
        uint256 revnetId,
        uint256 collateralCount
    )
        internal
        view
        returns (uint256)
    {
        // If there's no collateral, there's no loan.
        if (collateralCount == 0) return 0;

        // Get a reference to the accounting context for the source.
        JBAccountingContext memory accountingContext =
            loan.source.terminal.accountingContextForTokenOf({projectId: revnetId, token: loan.source.token});

        // Keep a reference to the revnet's terminals.
        IJBTerminal[] memory terminals = DIRECTORY.terminalsOf(revnetId);

        return _borrowableAmountFrom({
            revnetId: revnetId,
            collateralCount: collateralCount,
            decimals: accountingContext.decimals,
            currency: accountingContext.currency,
            terminals: terminals
        });
    }

    /// @dev `ERC-2771` specifies the context as being a single address (20 bytes).
    function _contextSuffixLength() internal view override(ERC2771Context, Context) returns (uint256) {
        return super._contextSuffixLength();
    }

    /// @notice Determines the source fee amount for a loan being paid off a certain amount.
    /// @param loan The loan having its source fee amount determined.
    /// @param amount The amount being paid off.
    /// @return sourceFeeAmount The source fee amount for the loan.
    function _determineSourceFeeAmount(
        REVLoan memory loan,
        uint256 amount
    )
        internal
        view
        returns (uint256 sourceFeeAmount)
    {
        // Keep a reference to the time since the loan was created.
        uint256 timeSinceLoanCreated = block.timestamp - loan.createdAt;

        // If the loan period has passed the prepaid time frame, take a fee.
        if (timeSinceLoanCreated > loan.prepaidDuration) {
            // If the loan period has passed the liqidation time frame, do not allow loan management.
            if (timeSinceLoanCreated > LOAN_LIQUIDATION_DURATION) {
                revert REVLoans_LoanExpired(timeSinceLoanCreated, LOAN_LIQUIDATION_DURATION);
            }

            // Get a reference to the amount prepaid for the full loan.
            uint256 prepaid = JBFees.feeAmountFrom({amountBeforeFee: loan.amount, feePercent: loan.prepaidFeePercent});

            uint256 fullSourceFeeAmount = JBFees.feeAmountFrom({
                amountBeforeFee: loan.amount - prepaid,
                feePercent: mulDiv(timeSinceLoanCreated, JBConstants.MAX_FEE, LOAN_LIQUIDATION_DURATION)
            });

            // Get a reference to the amount that would have to be paid down to make the loan fully paid.
            uint256 amountInFull = loan.amount + fullSourceFeeAmount;

            // Calculate the source fee amount for the amount being paid off.
            sourceFeeAmount = mulDiv(fullSourceFeeAmount, amount, amountInFull);
        }
    }

    /// @notice Generate a ID for a loan given a revnet ID and a loan number within that revnet.
    /// @param revnetId The ID of the revnet to generate a loan ID for.
    /// @param loanNumber The loan number of the loan within the revnet.
    /// @return The token ID of the 721.
    function _generateLoanId(uint256 revnetId, uint256 loanNumber) internal pure returns (uint256) {
        return (revnetId * _ONE_TRILLION) + loanNumber;
    }

    /// @notice The calldata. Preferred to use over `msg.data`.
    /// @return calldata The `msg.data` of this call.
    function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
        return ERC2771Context._msgData();
    }

    /// @notice The message's sender. Preferred to use over `msg.sender`.
    /// @return sender The address which sent this call.
    function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
        return ERC2771Context._msgSender();
    }

    /// @notice The total borrowed amount from a revnet.
    /// @param revnetId The ID of the revnet to check for borrowed assets from.
    /// @param decimals The decimals the resulting fixed point value will include.
    /// @param currency The currency the resulting value will be in terms of.
    /// @return borrowedAmount The total amount borrowed.
    function _totalBorrowedFrom(
        uint256 revnetId,
        uint256 decimals,
        uint256 currency
    )
        internal
        view
        returns (uint256 borrowedAmount)
    {
        // Keep a reference to all sources being used to loaned out from this revnet.
        REVLoanSource[] memory sources = _loanSourcesOf[revnetId];

        // Iterate over all sources being used to loaned out.
        for (uint256 i; i < sources.length; i++) {
            // Get a reference to the token being iterated on.
            REVLoanSource memory source = sources[i];

            // Get a reference to the accounting context for the source.
            JBAccountingContext memory accountingContext =
                source.terminal.accountingContextForTokenOf({projectId: revnetId, token: source.token});

            // Normalize the price to the provided currency and decimals.
            uint256 pricePerUnit = accountingContext.currency == currency
                ? 10 ** decimals
                : PRICES.pricePerUnitOf({
                    projectId: revnetId,
                    pricingCurrency: accountingContext.currency,
                    unitCurrency: currency,
                    decimals: decimals
                });

            // Get a reference to the amount of tokens loaned out.
            uint256 tokensLoaned = totalBorrowedFrom[revnetId][source.terminal][source.token];

            borrowedAmount += mulDiv(tokensLoaned, 10 ** decimals, pricePerUnit);
        }
    }

    //*********************************************************************//
    // ---------------------- external transactions ---------------------- //
    //*********************************************************************//

    /// @notice Open a loan by borrowing from a revnet.
    /// @param revnetId The ID of the revnet being borrowed from.
    /// @param source The source of the loan being borrowed.
    /// @param minBorrowAmount The minimum amount being borrowed, denominated in the token of the source's accounting
    /// context.
    /// @param collateralCount The amount of tokens to use as collateral for the loan.
    /// @param beneficiary The address that'll receive the borrowed funds and the tokens resulting from fee payments.
    /// @param prepaidFeePercent The fee percent that will be charged upfront from the revnet being borrowed from.
    /// Prepaying a fee is cheaper than paying later.
    /// @return loanId The ID of the loan created from borrowing.
    /// @return loan The loan created from borrowing.
    function borrowFrom(
        uint256 revnetId,
        REVLoanSource calldata source,
        uint256 minBorrowAmount,
        uint256 collateralCount,
        address payable beneficiary,
        uint256 prepaidFeePercent
    )
        public
        override
        returns (uint256 loanId, REVLoan memory)
    {
        // Get a reference to the revnet owner.
        address revnetOwner = PROJECTS.ownerOf(revnetId);

        // Make sure the revnet was deployed with the deployer this loan expects.
        if (revnetOwner != address(REVNETS)) revert REVLoans_RevnetsMismatch(revnetOwner, address(REVNETS));

        // A loan needs to have collateral.
        if (collateralCount == 0) revert REVLoans_ZeroCollateralLoanIsInvalid();

        // Make sure the prepaid fee percent is between `MIN_PREPAID_FEE_PERCENT` and `MAX_PREPAID_FEE_PERCENT`. Meaning
        // an 16 year loan can be paid upfront with a
        // payment of 50% of the borrowed assets, the cheapest possible rate.
        if (prepaidFeePercent < MIN_PREPAID_FEE_PERCENT || prepaidFeePercent > MAX_PREPAID_FEE_PERCENT) {
            revert REVLoans_InvalidPrepaidFeePercent(
                prepaidFeePercent, MIN_PREPAID_FEE_PERCENT, MAX_PREPAID_FEE_PERCENT
            );
        }

        // Get a reference to the loan ID.
        loanId = _generateLoanId({revnetId: revnetId, loanNumber: ++numberOfLoansFor[revnetId]});

        // Get a reference to the loan being created.
        REVLoan storage loan = _loanOf[loanId];

        // Set the loan's values.
        loan.source = source;
        loan.createdAt = uint40(block.timestamp);
        loan.prepaidFeePercent = uint16(prepaidFeePercent);
        loan.prepaidDuration = uint32(mulDiv(prepaidFeePercent, LOAN_LIQUIDATION_DURATION, MAX_PREPAID_FEE_PERCENT));

        // Get the amount of the loan.
        uint256 borrowAmount = _borrowAmountFrom({loan: loan, revnetId: revnetId, collateralCount: collateralCount});

        // Make sure the minimum borrow amount is met.
        if (borrowAmount < minBorrowAmount) revert REVLoans_UnderMinBorrowAmount(minBorrowAmount, borrowAmount);

        // Get the amount of additional fee to take for the revnet issuing the loan.
        uint256 sourceFeeAmount = JBFees.feeAmountFrom({amountBeforeFee: borrowAmount, feePercent: prepaidFeePercent});

        // Borrow the amount.
        _adjust({
            loan: loan,
            revnetId: revnetId,
            newBorrowAmount: borrowAmount,
            newCollateralCount: collateralCount,
            sourceFeeAmount: sourceFeeAmount,
            beneficiary: beneficiary
        });

        // Mint the loan.
        _mint({to: _msgSender(), tokenId: loanId});

        emit Borrow({
            loanId: loanId,
            revnetId: revnetId,
            loan: loan,
            source: source,
            borrowAmount: borrowAmount,
            collateralCount: collateralCount,
            sourceFeeAmount: sourceFeeAmount,
            beneficiary: beneficiary,
            caller: _msgSender()
        });

        return (loanId, loan);
    }

    /// @notice Cleans up any liquiditated loans.
    /// @dev Since some loans may be reallocated or paid off, loans within startingLoanId and startingLoanId + count may
    /// be skipped, so choose these parameters carefully to avoid extra gas usage.
    /// @param revnetId The ID of the revnet to liquidate loans from.
    /// @param startingLoanId The ID of the loan to start iterating from.
    /// @param count The amount of loans iterate over since the last liquidated loan.
    function liquidateExpiredLoansFrom(uint256 revnetId, uint256 startingLoanId, uint256 count) external override {
        // Iterate over the desired number of loans to check for liquidation.
        for (uint256 i; i < count; i++) {
            // Get a reference to the next loan ID.
            uint256 loanId = _generateLoanId({revnetId: revnetId, loanNumber: startingLoanId + i});

            // Get a reference to the loan being iterated on.
            REVLoan memory loan = _loanOf[loanId];

            // If the loan doesn't exist, there's nothing left to liquidate.
            // slither-disable-next-line incorrect-equality
            if (loan.createdAt == 0) break;

            // Keep a reference to the loan's owner.
            address owner = _ownerOf(loanId);

            // If the loan is already burned, or if it hasn't passed its liquidation duration, continue.
            if (owner == address(0) || (block.timestamp <= loan.createdAt + LOAN_LIQUIDATION_DURATION)) continue;

            // Burn the loan.
            _burn(loanId);

            if (loan.collateral > 0) {
                // Decrement the total amount of collateral tokens supporting loans from this revnet.
                totalCollateralOf[revnetId] -= loan.collateral;
            }

            if (loan.amount > 0) {
                // Decrement the amount loaned.
                totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] -= loan.amount;
            }

            emit Liquidate({loanId: loanId, revnetId: revnetId, loan: loan, caller: _msgSender()});
        }
    }

    /// @notice Refinances a loan by transferring extra collateral from an existing loan to a new loan.
    /// @dev Useful if a loan's collateral has gone up in value since the loan was created.
    /// @dev Refinancing a loan will burn the original and create two new loans.
    /// @param loanId The ID of the loan to reallocate collateral from.
    /// @param collateralCountToTransfer The amount of collateral to transfer from the original loan.
    /// @param source The source of the loan to create.
    /// @param minBorrowAmount The minimum amount being borrowed, denominated in the token of the source's accounting
    /// context.
    /// @param collateralCountToAdd The amount of collateral to add to the loan.
    /// @param beneficiary The address that'll receive the borrowed funds and the tokens resulting from fee payments.
    /// @param prepaidFeePercent The fee percent that will be charged upfront from the revnet being borrowed from.
    /// @return reallocatedLoanId The ID of the loan being reallocated.
    /// @return newLoanId The ID of the new loan.
    /// @return reallocatedLoan The loan being reallocated.
    /// @return newLoan The new loan created from reallocating collateral.
    function reallocateCollateralFromLoan(
        uint256 loanId,
        uint256 collateralCountToTransfer,
        REVLoanSource calldata source,
        uint256 minBorrowAmount,
        uint256 collateralCountToAdd,
        address payable beneficiary,
        uint256 prepaidFeePercent
    )
        external
        payable
        override
        returns (uint256 reallocatedLoanId, uint256 newLoanId, REVLoan memory reallocatedLoan, REVLoan memory newLoan)
    {
        // Make sure only the loan's owner can manage it.
        if (_ownerOf(loanId) != _msgSender()) revert REVLoans_Unauthorized(_msgSender(), _ownerOf(loanId));

        // Keep a reference to the revnet ID of the loan being reallocated.
        uint256 revnetId = revnetIdOfLoanWith(loanId);

        // Refinance the loan.
        (reallocatedLoanId, reallocatedLoan) = _reallocateCollateralFromLoan({
            loanId: loanId,
            revnetId: revnetId,
            collateralCountToRemove: collateralCountToTransfer
        });

        // Make a new loan with the leftover collateral from reallocating.
        (newLoanId, newLoan) = borrowFrom({
            revnetId: revnetId,
            source: source,
            minBorrowAmount: minBorrowAmount,
            collateralCount: collateralCountToTransfer + collateralCountToAdd,
            beneficiary: beneficiary,
            prepaidFeePercent: prepaidFeePercent
        });
    }

    /// @notice Allows the owner of a loan to pay it back or receive returned collateral no longer necessary to support
    /// the loan.
    /// @param loanId The ID of the loan being adjusted.
    /// @param maxRepayBorrowAmount The maximum amount being paid off, denominated in the token of the source's
    /// accounting context.
    /// @param collateralCountToReturn The amount of collateral to return being returned from the loan.
    /// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
    /// @param allowance An allowance to faciliate permit2 interactions.
    /// @return paidOffLoanId The ID of the loan after it's been paid off.
    /// @return paidOffloan The loan after it's been paid off.
    function repayLoan(
        uint256 loanId,
        uint256 maxRepayBorrowAmount,
        uint256 collateralCountToReturn,
        address payable beneficiary,
        JBSingleAllowance calldata allowance
    )
        external
        payable
        override
        returns (uint256 paidOffLoanId, REVLoan memory paidOffloan)
    {
        // Make sure only the loan's owner can manage it.
        if (_ownerOf(loanId) != _msgSender()) revert REVLoans_Unauthorized(_msgSender(), _ownerOf(loanId));

        // Keep a reference to the fee being iterated on.
        REVLoan storage loan = _loanOf[loanId];

        if (collateralCountToReturn > loan.collateral) {
            revert REVLoans_CollateralExceedsLoan(collateralCountToReturn, loan.collateral);
        }

        // Get a reference to the revnet ID of the loan being repaid.
        uint256 revnetId = revnetIdOfLoanWith(loanId);

        // Get the new borrow amount.
        uint256 newBorrowAmount = _borrowAmountFrom({
            loan: loan,
            revnetId: revnetId,
            collateralCount: loan.collateral - collateralCountToReturn
        });

        // Make sure the new borrow amount is less than the loan's amount.
        if (newBorrowAmount > loan.amount) {
            revert REVLoans_NewBorrowAmountGreaterThanLoanAmount(newBorrowAmount, loan.amount);
        }

        // Get the amount of the loan being repaid.
        uint256 repayBorrowAmount = loan.amount - newBorrowAmount;

        // Keep a reference to the fee that'll be taken.
        uint256 sourceFeeAmount = _determineSourceFeeAmount(loan, repayBorrowAmount);

        // Add the fee to the repay amount.
        repayBorrowAmount += sourceFeeAmount;

        // Accept the funds that'll be used to pay off loans.
        maxRepayBorrowAmount =
            _acceptFundsFor({token: loan.source.token, amount: maxRepayBorrowAmount, allowance: allowance});

        // Make sure the minimum borrow amount is met.
        if (repayBorrowAmount > maxRepayBorrowAmount) {
            revert REVLoans_OverMaxRepayBorrowAmount(maxRepayBorrowAmount, repayBorrowAmount);
        }

        (paidOffLoanId, paidOffloan) = _repayLoan({
            loanId: loanId,
            loan: loan,
            revnetId: revnetId,
            repayBorrowAmount: repayBorrowAmount,
            sourceFeeAmount: sourceFeeAmount,
            collateralCountToReturn: collateralCountToReturn,
            beneficiary: beneficiary
        });

        // If the max repay amount is greater than the repay amount, return the difference back to the payer.
        if (maxRepayBorrowAmount > repayBorrowAmount) {
            _transferFrom({
                from: address(this),
                to: payable(_msgSender()),
                token: loan.source.token,
                amount: maxRepayBorrowAmount - repayBorrowAmount
            });
        }
    }

    /// @notice Sets the address of the resolver used to retrieve the tokenURI of loans.
    /// @param resolver The address of the new resolver.
    function setTokenUriResolver(IJBTokenUriResolver resolver) external override onlyOwner {
        // Store the new resolver.
        tokenUriResolver = resolver;

        emit SetTokenUriResolver({resolver: resolver, caller: _msgSender()});
    }

    //*********************************************************************//
    // --------------------- internal transactions ----------------------- //
    //*********************************************************************//

    /// @notice Adds collateral to a loan.
    /// @param revnetId The ID of the revnet the loan is being added in.
    /// @param amount The new amount of collateral being added to the loan.
    function _addCollateralTo(uint256 revnetId, uint256 amount) internal {
        // Increment the total amount of collateral tokens.
        totalCollateralOf[revnetId] += amount;

        // Burn the tokens that are tracked as collateral.
        CONTROLLER.burnTokensOf({
            holder: _msgSender(),
            projectId: revnetId,
            tokenCount: amount,
            memo: "Adding collateral to loan"
        });
    }

    /// @notice Add a new amount to the loan that is greater than the previous amount.
    /// @param loan The loan being added to.
    /// @param revnetId The ID of the revnet the loan is being added in.
    /// @param addedBorrowAmount The amount being added to the loan, denominated in the token of the source's
    /// accounting context.
    /// @param sourceFeeAmount The amount of the fee being taken from the revnet acting as the source of the loan.
    /// @param feeTerminal The terminal that the fee will be paid to.
    /// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
    function _addTo(
        REVLoan memory loan,
        uint256 revnetId,
        uint256 addedBorrowAmount,
        uint256 sourceFeeAmount,
        IJBTerminal feeTerminal,
        address payable beneficiary
    )
        internal
    {
        // Register the source if this is the first time its being used for this revnet.
        if (!isLoanSourceOf[revnetId][loan.source.terminal][loan.source.token]) {
            isLoanSourceOf[revnetId][loan.source.terminal][loan.source.token] = true;
            _loanSourcesOf[revnetId].push(REVLoanSource({token: loan.source.token, terminal: loan.source.terminal}));
        }

        // Increment the amount of the token borrowed from the revnet from the terminal.
        totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] += addedBorrowAmount;

        uint256 netAmountPaidOut;
        {
            // Get a reference to the accounting context for the source.
            JBAccountingContext memory accountingContext =
                loan.source.terminal.accountingContextForTokenOf({projectId: revnetId, token: loan.source.token});

            // Pull the amount to be loaned out of the revnet. This will incure the protocol fee.
            // slither-disable-next-line unused-return
            netAmountPaidOut = loan.source.terminal.useAllowanceOf({
                projectId: revnetId,
                token: loan.source.token,
                amount: addedBorrowAmount,
                currency: accountingContext.currency,
                minTokensPaidOut: 0,
                beneficiary: payable(address(this)),
                feeBeneficiary: beneficiary,
                memo: "Lending out to a borrower"
            });
        }

        // Get the amount of additional fee to take for REV.
        uint256 revFeeAmount =
            JBFees.feeAmountFrom({amountBeforeFee: addedBorrowAmount, feePercent: REV_PREPAID_FEE_PERCENT});

        // Increase the allowance for the beneficiary.
        uint256 payValue = _beforeTransferTo({to: address(feeTerminal), token: loan.source.token, amount: revFeeAmount});

        // Pay the fee. Send the REV to the msg.sender.
        // slither-disable-next-line arbitrary-send-eth,unused-return
        try feeTerminal.pay{value: payValue}({
            projectId: REV_ID,
            token: loan.source.token,
            amount: revFeeAmount,
            beneficiary: beneficiary,
            minReturnedTokens: 0,
            memo: "Fee from loan",
            metadata: bytes(abi.encodePacked(revnetId))
        }) {} catch (bytes memory) {}

        // Transfer the remaining balance to the borrower.
        _transferFrom({
            from: address(this),
            to: beneficiary,
            token: loan.source.token,
            amount: netAmountPaidOut - revFeeAmount - sourceFeeAmount
        });
    }

    /// @notice Allows the owner of a loan to pay it back, add more, or receive returned collateral no longer necessary
    /// to support the loan.
    /// @param loan The loan being adjusted.
    /// @param revnetId The ID of the revnet the loan is being adjusted in.
    /// @param newBorrowAmount The new amount of the loan, denominated in the token of the source's accounting
    /// context.
    /// @param newCollateralCount The new amount of collateral backing the loan.
    /// @param sourceFeeAmount The amount of the fee being taken from the revnet acting as the source of the loan.
    /// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
    function _adjust(
        REVLoan storage loan,
        uint256 revnetId,
        uint256 newBorrowAmount,
        uint256 newCollateralCount,
        uint256 sourceFeeAmount,
        address payable beneficiary
    )
        internal
    {
        // Add to the loan if needed...
        if (newBorrowAmount > loan.amount) {
            // Keep a reference to the fee terminal.
            IJBTerminal feeTerminal = DIRECTORY.primaryTerminalOf(REV_ID, loan.source.token);

            // Add the new amount to the loan.
            _addTo({
                loan: loan,
                revnetId: revnetId,
                addedBorrowAmount: newBorrowAmount - loan.amount,
                sourceFeeAmount: sourceFeeAmount,
                feeTerminal: feeTerminal,
                beneficiary: beneficiary
            });
            // ... or pay off the loan if needed.
        } else if (loan.amount > newBorrowAmount) {
            _removeFrom({loan: loan, revnetId: revnetId, repaidBorrowAmount: loan.amount - newBorrowAmount});
        }

        // Add collateral if needed...
        if (newCollateralCount > loan.collateral) {
            _addCollateralTo({revnetId: revnetId, amount: newCollateralCount - loan.collateral});
            // ... or return collateral if needed.
        } else if (loan.collateral > newCollateralCount) {
            _returnCollateralFrom({
                revnetId: revnetId,
                collateralCount: loan.collateral - newCollateralCount,
                beneficiary: beneficiary
            });
        }

        // If there is a source fee, pay it.
        if (sourceFeeAmount > 0) {
            // Increase the allowance for the beneficiary.
            uint256 payValue = _beforeTransferTo({
                to: address(loan.source.terminal),
                token: loan.source.token,
                amount: sourceFeeAmount
            });

            // Pay the fee.
            // slither-disable-next-line unused-return
            loan.source.terminal.pay{value: payValue}({
                projectId: revnetId,
                token: loan.source.token,
                amount: sourceFeeAmount,
                beneficiary: beneficiary,
                minReturnedTokens: 0,
                memo: "Fee from loan",
                metadata: bytes(abi.encodePacked(REV_ID))
            });
        }

        // Store the loans updated values.
        loan.amount = uint112(newBorrowAmount);
        loan.collateral = uint112(newCollateralCount);
    }

    /// @notice Accepts an incoming token.
    /// @param token The token being accepted.
    /// @param amount The number of tokens being accepted.
    /// @param allowance The permit2 context.
    /// @return amount The number of tokens which have been accepted.
    function _acceptFundsFor(
        address token,
        uint256 amount,
        JBSingleAllowance memory allowance
    )
        internal
        returns (uint256)
    {
        // If the token is the native token, override `amount` with `msg.value`.
        if (token == JBConstants.NATIVE_TOKEN) return msg.value;

        // If the token is not native, revert if there is a non-zero `msg.value`.
        if (msg.value != 0) revert REVLoans_NoMsgValueAllowed();

        // Check if the metadata contains permit data.
        if (allowance.amount != 0) {
            // Make sure the permit allowance is enough for this payment. If not we revert early.
            if (allowance.amount < amount) {
                revert REVLoans_PermitAllowanceNotEnough(allowance.amount, amount);
            }

            // Keep a reference to the permit rules.
            IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer.PermitSingle({
                details: IAllowanceTransfer.PermitDetails({
                    token: token,
                    amount: allowance.amount,
                    expiration: allowance.expiration,
                    nonce: allowance.nonce
                }),
                spender: address(this),
                sigDeadline: allowance.sigDeadline
            });

            // Set the allowance to `spend` tokens for the user.
            try PERMIT2.permit({owner: _msgSender(), permitSingle: permitSingle, signature: allowance.signature}) {}
                catch (bytes memory) {}
        }

        // Get a reference to the balance before receiving tokens.
        uint256 balanceBefore = _balanceOf(token);

        // Transfer tokens to this terminal from the msg sender.
        _transferFrom({from: _msgSender(), to: payable(address(this)), token: token, amount: amount});

        // The amount should reflect the change in balance.
        return _balanceOf(token) - balanceBefore;
    }

    /// @notice Logic to be triggered before transferring tokens from this contract.
    /// @param to The address the transfer is going to.
    /// @param token The token being transferred.
    /// @param amount The number of tokens being transferred, as a fixed point number with the same number of decimals
    /// as the token specifies.
    /// @return payValue The value to attach to the transaction being sent.
    function _beforeTransferTo(address to, address token, uint256 amount) internal returns (uint256) {
        // If the token is the native token, no allowance needed.
        if (token == JBConstants.NATIVE_TOKEN) return amount;
        IERC20(token).safeIncreaseAllowance(to, amount);
        return 0;
    }

    /// @notice Pays down a loan.
    /// @param loanId The ID of the loan being paid down.
    /// @param loan The loan being paid down.
    /// @param repayBorrowAmount The amount being paid down from the loan, denominated in the token of the source's
    /// accounting context.
    /// @param sourceFeeAmount The amount of the fee being taken from the revnet acting as the source of the loan.
    /// @param collateralCountToReturn The amount of collateral being returned that the loan no longer requires.
    /// @param beneficiary The address receiving the returned collateral and any tokens resulting from paying fees.
    function _repayLoan(
        uint256 loanId,
        REVLoan storage loan,
        uint256 revnetId,
        uint256 repayBorrowAmount,
        uint256 sourceFeeAmount,
        uint256 collateralCountToReturn,
        address payable beneficiary
    )
        internal
        returns (uint256, REVLoan memory)
    {
        // Burn the original loan.
        _burn(loanId);

        // If the loan will carry no more amount or collateral, store its changes directly.
        // slither-disable-next-line incorrect-equality
        if (collateralCountToReturn == loan.collateral) {
            // Borrow in.
            _adjust({
                loan: loan,
                revnetId: revnetId,
                newBorrowAmount: 0,
                newCollateralCount: 0,
                sourceFeeAmount: sourceFeeAmount,
                beneficiary: beneficiary
            });

            emit RepayLoan({
                loanId: loanId,
                revnetId: revnetId,
                paidOffLoanId: loanId,
                loan: loan,
                paidOffLoan: loan,
                repayBorrowAmount: repayBorrowAmount,
                sourceFeeAmount: sourceFeeAmount,
                collateralCountToReturn: collateralCountToReturn,
                beneficiary: beneficiary,
                caller: _msgSender()
            });

            return (loanId, loan);
        } else {
            // Make a new loan with the remaining amount and collateral.
            // Get a reference to the replacement loan ID.
            uint256 paidOffLoanId = _generateLoanId({revnetId: revnetId, loanNumber: ++numberOfLoansFor[revnetId]});

            // Get a reference to the loan being paid off.
            REVLoan storage paidOffLoan = _loanOf[paidOffLoanId];

            // Set the paid off loan's values the same as the original loan.
            paidOffLoan.amount = loan.amount;
            paidOffLoan.collateral = loan.collateral;
            paidOffLoan.createdAt = loan.createdAt;
            paidOffLoan.prepaidFeePercent = loan.prepaidFeePercent;
            paidOffLoan.prepaidDuration = loan.prepaidDuration;
            paidOffLoan.source = loan.source;

            // Borrow in.
            _adjust({
                loan: paidOffLoan,
                revnetId: revnetId,
                newBorrowAmount: paidOffLoan.amount - (repayBorrowAmount - sourceFeeAmount),
                newCollateralCount: paidOffLoan.collateral - collateralCountToReturn,
                sourceFeeAmount: sourceFeeAmount,
                beneficiary: beneficiary
            });

            // Mint the replacement loan.
            _mint({to: _msgSender(), tokenId: paidOffLoanId});

            emit RepayLoan({
                loanId: loanId,
                revnetId: revnetId,
                paidOffLoanId: paidOffLoanId,
                loan: loan,
                paidOffLoan: paidOffLoan,
                repayBorrowAmount: repayBorrowAmount,
                sourceFeeAmount: sourceFeeAmount,
                collateralCountToReturn: collateralCountToReturn,
                beneficiary: beneficiary,
                caller: _msgSender()
            });

            return (paidOffLoanId, paidOffLoan);
        }
    }

    /// @notice Reallocates collateral from a loan by making a new loan based on the original, with reduced collateral.
    /// @param loanId The ID of the loan to reallocate collateral from.
    /// @param revnetId The ID of the revnet the loan is from.
    /// @param collateralCountToRemove The amount of collateral to remove from the loan.
    /// @return reallocatedLoanId The ID of the loan.
    /// @return reallocatedLoan The reallocated loan.
    function _reallocateCollateralFromLoan(
        uint256 loanId,
        uint256 revnetId,
        uint256 collateralCountToRemove
    )
        internal
        returns (uint256 reallocatedLoanId, REVLoan storage reallocatedLoan)
    {
        // Burn the original loan.
        _burn(loanId);

        // Keep a reference to loan having its collateral reduced.
        REVLoan storage loan = _loanOf[loanId];

        // Make sure there is enough collateral to transfer.
        if (collateralCountToRemove > loan.collateral) revert REVLoans_NotEnoughCollateral();

        // Keep a reference to the new collateral amount.
        uint256 newCollateralCount = loan.collateral - collateralCountToRemove;

        // Keep a reference to the new borrow amount.
        uint256 borrowAmount = _borrowAmountFrom({loan: loan, revnetId: revnetId, collateralCount: newCollateralCount});

        // Make sure the borrow amount is not less than the original loan's amount.
        if (borrowAmount < loan.amount) {
            revert REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows(borrowAmount, loan.amount);
        }

        // Get a reference to the replacement loan ID.
        reallocatedLoanId = _generateLoanId(revnetId, ++numberOfLoansFor[revnetId]);

        // Get a reference to the loan being created.
        reallocatedLoan = _loanOf[reallocatedLoanId];

        // Set the reallocated loan's values the same as the original loan.
        reallocatedLoan.amount = loan.amount;
        reallocatedLoan.collateral = loan.collateral;
        reallocatedLoan.createdAt = loan.createdAt;
        reallocatedLoan.prepaidFeePercent = loan.prepaidFeePercent;
        reallocatedLoan.prepaidDuration = loan.prepaidDuration;
        reallocatedLoan.source = loan.source;

        // Reduce the collateral of the reallocated loan.
        _adjust({
            loan: reallocatedLoan,
            revnetId: revnetId,
            newBorrowAmount: reallocatedLoan.amount, // Don't change the borrow amount.
            newCollateralCount: newCollateralCount,
            sourceFeeAmount: 0,
            beneficiary: payable(_msgSender()) // use the msgSender as the beneficiary, who will have the returned
                // collateral tokens debited from their balance for the new loan.
        });

        // Mint the replacement loan.
        _mint({to: _msgSender(), tokenId: reallocatedLoanId});

        emit ReallocateCollateral({
            loanId: loanId,
            revnetId: revnetId,
            reallocatedLoanId: reallocatedLoanId,
            reallocatedLoan: reallocatedLoan,
            removedcollateralCount: collateralCountToRemove,
            caller: _msgSender()
        });
    }

    /// @notice Pays off a loan.
    /// @param loan The loan being paid off.
    /// @param revnetId The ID of the revnet the loan is being paid off in.
    /// @param repaidBorrowAmount The amount being paid off, denominated in the token of the source's accounting
    /// context.
    function _removeFrom(REVLoan memory loan, uint256 revnetId, uint256 repaidBorrowAmount) internal {
        // Decrement the total amount of a token being loaned out by the revnet from its terminal.
        totalBorrowedFrom[revnetId][loan.source.terminal][loan.source.token] -= repaidBorrowAmount;

        // Increase the allowance for the beneficiary.
        uint256 payValue =
            _beforeTransferTo({to: address(loan.source.terminal), token: loan.source.token, amount: repaidBorrowAmount});

        // Add the loaned amount back to the revnet.
        loan.source.terminal.addToBalanceOf{value: payValue}({
            projectId: revnetId,
            token: loan.source.token,
            amount: repaidBorrowAmount,
            shouldReturnHeldFees: false,
            memo: "Paying off loan",
            metadata: bytes(abi.encodePacked(REV_ID))
        });
    }

    /// @notice Returns collateral from a loan.
    /// @param revnetId The ID of the revnet the loan is being returned in.
    /// @param collateralCount The amount of collateral being returned from the loan.
    /// @param beneficiary The address receiving the returned collateral.
    function _returnCollateralFrom(uint256 revnetId, uint256 collateralCount, address payable beneficiary) internal {
        // Decrement the total amount of collateral tokens.
        totalCollateralOf[revnetId] -= collateralCount;

        // Mint the collateral tokens back to the loan payer.
        // slither-disable-next-line unused-return,calls-loop
        CONTROLLER.mintTokensOf({
            projectId: revnetId,
            tokenCount: collateralCount,
            beneficiary: beneficiary,
            memo: "Removing collateral from loan",
            useReservedPercent: false
        });
    }

    /// @notice Transfers tokens.
    /// @param from The address to transfer tokens from.
    /// @param to The address to transfer tokens to.
    /// @param token The address of the token being transfered.
    /// @param amount The amount of tokens to transfer, as a fixed point number with the same number of decimals as the
    /// token.
    function _transferFrom(address from, address payable to, address token, uint256 amount) internal virtual {
        if (from == address(this)) {
            // If the token is native token, assume the `sendValue` standard.
            if (token == JBConstants.NATIVE_TOKEN) return Address.sendValue({recipient: to, amount: amount});

            // If the transfer is from this contract, use `safeTransfer`.
            return IERC20(token).safeTransfer({to: to, value: amount});
        }

        // If there's sufficient approval, transfer normally.
        if (IERC20(token).allowance(address(from), address(this)) >= amount) {
            return IERC20(token).safeTransferFrom({from: from, to: to, value: amount});
        }

        // Make sure the amount being paid is less than the maximum permit2 allowance.
        if (amount > type(uint160).max) revert REVLoans_OverflowAlert(amount, type(uint160).max);

        // Otherwise, attempt to use the `permit2` method.
        PERMIT2.transferFrom({from: from, to: to, amount: uint160(amount), token: token});
    }

    fallback() external payable {}
    receive() external payable {}
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBCashOutHook} from "@bananapus/core/src/interfaces/IJBCashOutHook.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBPayHook} from "@bananapus/core/src/interfaces/IJBPayHook.sol";
import {IJBRulesetDataHook} from "@bananapus/core/src/interfaces/IJBRulesetDataHook.sol";

interface IJB721Hook is IJBRulesetDataHook, IJBPayHook, IJBCashOutHook {
    function DIRECTORY() external view returns (IJBDirectory);
    function METADATA_ID_TARGET() external view returns (address);
    function PROJECT_ID() external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPrices} from "@bananapus/core/src/interfaces/IJBPrices.sol";
import {IJBRulesets} from "@bananapus/core/src/interfaces/IJBRulesets.sol";

import {IJB721Hook} from "./IJB721Hook.sol";
import {IJB721TiersHookStore} from "./IJB721TiersHookStore.sol";
import {IJB721TokenUriResolver} from "./IJB721TokenUriResolver.sol";
import {JB721InitTiersConfig} from "../structs/JB721InitTiersConfig.sol";
import {JB721TierConfig} from "../structs/JB721TierConfig.sol";
import {JB721TiersHookFlags} from "../structs/JB721TiersHookFlags.sol";
import {JB721TiersMintReservesConfig} from "../structs/JB721TiersMintReservesConfig.sol";
import {JB721TiersSetDiscountPercentConfig} from "../structs/JB721TiersSetDiscountPercentConfig.sol";

interface IJB721TiersHook is IJB721Hook {
    event AddPayCredits(
        uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
    );
    event AddTier(uint256 indexed tierId, JB721TierConfig tier, address caller);
    event Mint(
        uint256 indexed tokenId,
        uint256 indexed tierId,
        address indexed beneficiary,
        uint256 totalAmountPaid,
        address caller
    );
    event MintReservedNft(uint256 indexed tokenId, uint256 indexed tierId, address indexed beneficiary, address caller);
    event RemoveTier(uint256 indexed tierId, address caller);
    event SetBaseUri(string indexed baseUri, address caller);
    event SetContractUri(string indexed uri, address caller);
    event SetDiscountPercent(uint256 indexed tierId, uint256 discountPercent, address caller);
    event SetEncodedIPFSUri(uint256 indexed tierId, bytes32 encodedUri, address caller);
    event SetTokenUriResolver(IJB721TokenUriResolver indexed resolver, address caller);
    event UsePayCredits(
        uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
    );

    function RULESETS() external view returns (IJBRulesets);
    function STORE() external view returns (IJB721TiersHookStore);

    function baseURI() external view returns (string memory);
    function contractURI() external view returns (string memory);
    function firstOwnerOf(uint256 tokenId) external view returns (address);
    function payCreditsOf(address addr) external view returns (uint256);
    function pricingContext() external view returns (uint256, uint256, IJBPrices);

    function adjustTiers(JB721TierConfig[] calldata tierDataToAdd, uint256[] calldata tierIdsToRemove) external;
    function initialize(
        uint256 projectId,
        string memory name,
        string memory symbol,
        string memory baseUri,
        IJB721TokenUriResolver tokenUriResolver,
        string memory contractUri,
        JB721InitTiersConfig memory tiersConfig,
        JB721TiersHookFlags memory flags
    )
        external;
    function setDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
    function setDiscountPercentsOf(JB721TiersSetDiscountPercentConfig[] calldata configs) external;
    function mintFor(uint16[] calldata tierIds, address beneficiary) external returns (uint256[] memory tokenIds);
    function mintPendingReservesFor(JB721TiersMintReservesConfig[] calldata reserveMintConfigs) external;
    function mintPendingReservesFor(uint256 tierId, uint256 count) external;
    function setMetadata(
        string calldata baseUri,
        string calldata contractMetadataUri,
        IJB721TokenUriResolver tokenUriResolver,
        uint256 encodedIPFSUriTierId,
        bytes32 encodedIPFSUri
    )
        external;
}

File 4 of 139 : IJB721TiersHookDeployer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJB721TiersHook} from "./IJB721TiersHook.sol";
import {JBDeploy721TiersHookConfig} from "../structs/JBDeploy721TiersHookConfig.sol";

interface IJB721TiersHookDeployer {
    event HookDeployed(uint256 indexed projectId, IJB721TiersHook hook, address caller);

    function deployHookFor(
        uint256 projectId,
        JBDeploy721TiersHookConfig memory deployTiersHookConfig,
        bytes32 salt
    )
        external
        returns (IJB721TiersHook hook);
}

File 5 of 139 : IJB721TiersHookStore.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJB721TokenUriResolver} from "./IJB721TokenUriResolver.sol";
import {JB721Tier} from "../structs/JB721Tier.sol";
import {JB721TierConfig} from "../structs/JB721TierConfig.sol";
import {JB721TiersHookFlags} from "../structs/JB721TiersHookFlags.sol";

interface IJB721TiersHookStore {
    event CleanTiers(address indexed hook, address caller);

    function balanceOf(address hook, address owner) external view returns (uint256);
    function cashOutWeightOf(address hook, uint256[] calldata tokenIds) external view returns (uint256 weight);
    function defaultReserveBeneficiaryOf(address hook) external view returns (address);
    function encodedIPFSUriOf(address hook, uint256 tierId) external view returns (bytes32);
    function encodedTierIPFSUriOf(address hook, uint256 tokenId) external view returns (bytes32);
    function flagsOf(address hook) external view returns (JB721TiersHookFlags memory);
    function isTierRemoved(address hook, uint256 tierId) external view returns (bool);
    function maxTierIdOf(address hook) external view returns (uint256);
    function numberOfBurnedFor(address hook, uint256 tierId) external view returns (uint256);
    function numberOfPendingReservesFor(address hook, uint256 tierId) external view returns (uint256);
    function numberOfReservesMintedFor(address hook, uint256 tierId) external view returns (uint256);
    function reserveBeneficiaryOf(address hook, uint256 tierId) external view returns (address);
    function tierBalanceOf(address hook, address owner, uint256 tier) external view returns (uint256);
    function tierIdOfToken(uint256 tokenId) external pure returns (uint256);
    function tierOf(address hook, uint256 id, bool includeResolvedUri) external view returns (JB721Tier memory tier);
    function tierOfTokenId(
        address hook,
        uint256 tokenId,
        bool includeResolvedUri
    )
        external
        view
        returns (JB721Tier memory tier);

    function tiersOf(
        address hook,
        uint256[] calldata categories,
        bool includeResolvedUri,
        uint256 startingSortIndex,
        uint256 size
    )
        external
        view
        returns (JB721Tier[] memory tiers);

    function tierVotingUnitsOf(address hook, address account, uint256 tierId) external view returns (uint256 units);
    function tokenUriResolverOf(address hook) external view returns (IJB721TokenUriResolver);
    function totalCashOutWeight(address hook) external view returns (uint256 weight);
    function totalSupplyOf(address hook) external view returns (uint256);
    function votingUnitsOf(address hook, address account) external view returns (uint256 units);

    function cleanTiers(address hook) external;
    function recordAddTiers(JB721TierConfig[] calldata tierData) external returns (uint256[] memory tierIds);
    function recordBurn(uint256[] calldata tokenIds) external;
    function recordFlags(JB721TiersHookFlags calldata flag) external;
    function recordMint(
        uint256 amount,
        uint16[] calldata tierIds,
        bool isOwnerMint
    )
        external
        returns (uint256[] memory tokenIds, uint256 leftoverAmount);
    function recordMintReservesFor(uint256 tierId, uint256 count) external returns (uint256[] memory tokenIds);
    function recordRemoveTierIds(uint256[] calldata tierIds) external;
    function recordSetEncodedIPFSUriOf(uint256 tierId, bytes32 encodedIPFSUri) external;
    function recordSetDiscountPercentOf(uint256 tierId, uint256 discountPercent) external;
    function recordSetTokenUriResolver(IJB721TokenUriResolver resolver) external;
    function recordTransferForTier(uint256 tierId, address from, address to) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJB721TokenUriResolver {
    function tokenUriOf(address nft, uint256 tokenId) external view returns (string memory tokenUri);
}

File 7 of 139 : JB721InitTiersConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPrices} from "@bananapus/core/src/interfaces/IJBPrices.sol";

import {JB721TierConfig} from "./JB721TierConfig.sol";

/// @notice Config to initialize a `JB721TiersHook` with tiers and price data.
/// @dev The `tiers` must be sorted by price (from least to greatest).
/// @custom:member tiers The tiers to initialize the hook with.
/// @custom:member currency The currency that the tier prices are denoted in. See `JBPrices`.
/// @custom:member decimals The number of decimals in the fixed point tier prices.
/// @custom:member prices A contract that exposes price feeds that can be used to calculate prices in different
/// currencies. To only accept payments in `currency`, set `prices` to the zero address. See `JBPrices`.
struct JB721InitTiersConfig {
    JB721TierConfig[] tiers;
    uint32 currency;
    uint8 decimals;
    IJBPrices prices;
}

File 8 of 139 : JB721Tier.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member id The tier's ID.
/// @custom:member price The price to buy an NFT in this tier, in terms of the currency in its `JBInitTiersConfig`.
/// @custom:member remainingSupply The remaining number of NFTs which can be minted from this tier.
/// @custom:member initialSupply The total number of NFTs which can be minted from this tier.
/// @custom:member votingUnits The number of votes that each NFT in this tier gets.
/// @custom:member reserveFrequency The frequency at which an extra NFT is minted for the `reserveBeneficiary` from this
/// tier. With a `reserveFrequency` of 5, an extra NFT will be minted for the `reserveBeneficiary` for every 5 NFTs
/// purchased.
/// @custom:member reserveBeneficiary The address which receives any reserve NFTs from this tier.
/// @custom:member encodedIPFSUri The IPFS URI to use for each NFT in this tier.
/// @custom:member category The category that NFTs in this tier belongs to. Used to group NFT tiers.
/// @custom:member discountPercent The discount that should be applied to the tier.
/// @custom:member allowOwnerMint A boolean indicating whether the contract's owner can mint NFTs from this tier
/// on-demand.
/// @custom:member cannotBeRemoved A boolean indicating whether attempts to remove this tier will revert.
/// @custom:member cannotIncreaseDiscountPercent If the tier cannot have its discount increased.
/// @custom:member transfersPausable A boolean indicating whether transfers for NFTs in tier can be paused.
/// @custom:member resolvedUri A resolved token URI for NFTs in this tier. Only available if the NFT this tier belongs
/// to has a resolver.
struct JB721Tier {
    uint32 id;
    uint104 price;
    uint32 remainingSupply;
    uint32 initialSupply;
    uint104 votingUnits;
    uint16 reserveFrequency;
    address reserveBeneficiary;
    bytes32 encodedIPFSUri;
    uint24 category;
    uint8 discountPercent;
    bool allowOwnerMint;
    bool transfersPausable;
    bool cannotBeRemoved;
    bool cannotIncreaseDiscountPercent;
    string resolvedUri;
}

File 9 of 139 : JB721TierConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Config for a single NFT tier within a `JB721TiersHook`.
/// @custom:member price The price to buy an NFT in this tier, in terms of the currency in its `JBInitTiersConfig`.
/// @custom:member initialSupply The total number of NFTs which can be minted from this tier.
/// @custom:member votingUnits The number of votes that each NFT in this tier gets if `useVotingUnits` is true.
/// @custom:member reserveFrequency The frequency at which an extra NFT is minted for the `reserveBeneficiary` from this
/// tier. With a `reserveFrequency` of 5, an extra NFT will be minted for the `reserveBeneficiary` for every 5 NFTs
/// purchased.
/// @custom:member reserveBeneficiary The address which receives any reserve NFTs from this tier. Overrides the default
/// reserve beneficiary if one is set.
/// @custom:member encodedIPFSUri The IPFS URI to use for each NFT in this tier.
/// @custom:member category The category that NFTs in this tier belongs to. Used to group NFT tiers.
/// @custom:member discountPercent The discount that should be applied to the tier.
/// @custom:member allowOwnerMint A boolean indicating whether the contract's owner can mint NFTs from this tier
/// on-demand.
/// @custom:member useReserveBeneficiaryAsDefault A boolean indicating whether this tier's `reserveBeneficiary` should
/// be stored as the default beneficiary for all tiers.
/// @custom:member transfersPausable A boolean indicating whether transfers for NFTs in tier can be paused.
/// @custom:member useVotingUnits A boolean indicating whether the `votingUnits` should be used to calculate voting
/// power. If `useVotingUnits` is false, voting power is based on the tier's price.
/// @custom:member cannotBeRemoved If the tier cannot be removed once added.
/// @custom:member cannotIncreaseDiscount If the tier cannot have its discount increased.
struct JB721TierConfig {
    uint104 price;
    uint32 initialSupply;
    uint32 votingUnits;
    uint16 reserveFrequency;
    address reserveBeneficiary;
    bytes32 encodedIPFSUri;
    uint24 category;
    uint8 discountPercent;
    bool allowOwnerMint;
    bool useReserveBeneficiaryAsDefault;
    bool transfersPausable;
    bool useVotingUnits;
    bool cannotBeRemoved;
    bool cannotIncreaseDiscountPercent;
}

File 10 of 139 : JB721TiersHookFlags.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member noNewTiersWithReserves A boolean indicating whether attempts to add new tiers with a non-zero
/// `reserveFrequency` will revert.
/// @custom:member noNewTiersWithVotes A boolean indicating whether attempts to add new tiers with non-zero
/// `votingUnits` will revert.
/// @custom:member noNewTiersWithOwnerMinting A boolean indicating whether attempts to add new tiers with
/// `allowOwnerMint` set to true will revert.
/// @custom:member preventOverspending A boolean indicating whether payments attempting to spend more than the price of
/// the NFTs being minted will revert.
struct JB721TiersHookFlags {
    bool noNewTiersWithReserves;
    bool noNewTiersWithVotes;
    bool noNewTiersWithOwnerMinting;
    bool preventOverspending;
}

File 11 of 139 : JB721TiersMintReservesConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member tierId The ID of the tier to mint from.
/// @custom:member count The number of NFTs to mint from that tier.
struct JB721TiersMintReservesConfig {
    uint32 tierId;
    uint16 count;
}

File 12 of 139 : JB721TiersSetDiscountPercentConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member tierId The ID of the tier to set the discount percent for.
/// @custom:member discountPercent The discount percent to set for the tier.
struct JB721TiersSetDiscountPercentConfig {
    uint32 tierId;
    uint16 discountPercent;
}

File 13 of 139 : JBDeploy721TiersHookConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JB721InitTiersConfig} from "./JB721InitTiersConfig.sol";
import {JB721TiersHookFlags} from "./JB721TiersHookFlags.sol";
import {IJB721TokenUriResolver} from "../interfaces/IJB721TokenUriResolver.sol";

/// @custom:member name The NFT collection's name.
/// @custom:member symbol The NFT collection's symbol.
/// @custom:member baseUri The URI to use as a base for full NFT URIs.
/// @custom:member tokenUriResolver The contract responsible for resolving the URI for each NFT.
/// @custom:member contractUri The URI where this contract's metadata can be found.
/// @custom:member tiersConfig The NFT tiers and pricing config to launch the hook with.
/// @custom:member reserveBeneficiary The default reserved beneficiary for all tiers.
/// @custom:member flags A set of boolean options to configure the hook with.
struct JBDeploy721TiersHookConfig {
    string name;
    string symbol;
    string baseUri;
    IJB721TokenUriResolver tokenUriResolver;
    string contractUri;
    JB721InitTiersConfig tiersConfig;
    address reserveBeneficiary;
    JB721TiersHookFlags flags;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBPayHook} from "@bananapus/core/src/interfaces/IJBPayHook.sol";
import {IJBPrices} from "@bananapus/core/src/interfaces/IJBPrices.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {IJBRulesetDataHook} from "@bananapus/core/src/interfaces/IJBRulesetDataHook.sol";
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import {IUniswapV3SwapCallback} from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";

import {IWETH9} from "./external/IWETH9.sol";

interface IJBBuybackHook is IJBPayHook, IJBRulesetDataHook, IUniswapV3SwapCallback {
    event Swap(
        uint256 indexed projectId, uint256 amountToSwapWith, IUniswapV3Pool pool, uint256 amountReceived, address caller
    );
    event Mint(uint256 indexed projectId, uint256 leftoverAmount, uint256 tokenCount, address caller);
    event PoolAdded(uint256 indexed projectId, address indexed terminalToken, address pool, address caller);
    event TwapWindowChanged(uint256 indexed projectId, uint256 oldWindow, uint256 newWindow, address caller);
    event TwapSlippageToleranceChanged(
        uint256 indexed projectId, uint256 oldTolerance, uint256 newTolerance, address caller
    );

    function CONTROLLER() external view returns (IJBController);
    function DIRECTORY() external view returns (IJBDirectory);
    function PRICES() external view returns (IJBPrices);
    function MAX_TWAP_SLIPPAGE_TOLERANCE() external view returns (uint256);
    function MIN_TWAP_SLIPPAGE_TOLERANCE() external view returns (uint256);
    function MAX_TWAP_WINDOW() external view returns (uint256);
    function MIN_TWAP_WINDOW() external view returns (uint256);
    function TWAP_SLIPPAGE_DENOMINATOR() external view returns (uint256);
    function PROJECTS() external view returns (IJBProjects);
    function UNISWAP_V3_FACTORY() external view returns (address);
    function WETH() external view returns (IWETH9);

    function poolOf(uint256 projectId, address terminalToken) external view returns (IUniswapV3Pool pool);
    function projectTokenOf(uint256 projectId) external view returns (address projectTokenOf);
    function twapSlippageToleranceOf(uint256 projectId) external view returns (uint256 slippageTolerance);
    function twapWindowOf(uint256 projectId) external view returns (uint32 window);

    function setPoolFor(
        uint256 projectId,
        uint24 fee,
        uint32 twapWindow,
        uint256 twapSlippageTolerance,
        address terminalToken
    )
        external
        returns (IUniswapV3Pool newPool);
    function setTwapSlippageToleranceOf(uint256 projectId, uint256 newSlippageTolerance) external;
    function setTwapWindowOf(uint256 projectId, uint32 newWindow) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title Interface for WETH9
interface IWETH9 is IERC20 {
    /// @notice Deposit ether to get wrapped ether
    function deposit() external payable;

    /// @notice Withdraw wrapped ether to get ether
    function withdraw(uint256) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Context} from "@openzeppelin/contracts/utils/Context.sol";

import {IJBPermissioned} from "./../interfaces/IJBPermissioned.sol";
import {IJBPermissions} from "./../interfaces/IJBPermissions.sol";

/// @notice Modifiers to allow access to transactions based on which permissions the message's sender has.
abstract contract JBPermissioned is Context, IJBPermissioned {
    //*********************************************************************//
    // --------------------------- custom errors -------------------------- //
    //*********************************************************************//

    error JBPermissioned_Unauthorized(address account, address sender, uint256 projectId, uint256 permissionId);

    //*********************************************************************//
    // ---------------- public immutable stored properties --------------- //
    //*********************************************************************//

    /// @notice A contract storing permissions.
    IJBPermissions public immutable override PERMISSIONS;

    //*********************************************************************//
    // -------------------------- constructor ---------------------------- //
    //*********************************************************************//

    /// @param permissions A contract storing permissions.
    constructor(IJBPermissions permissions) {
        PERMISSIONS = permissions;
    }

    //*********************************************************************//
    // -------------------------- internal views ------------------------- //
    //*********************************************************************//

    /// @notice Require the message sender to be the account or have the relevant permission.
    /// @param account The account to allow.
    /// @param projectId The project ID to check the permission under.
    /// @param permissionId The required permission ID. The operator must have this permission within the specified
    /// project ID.
    function _requirePermissionFrom(address account, uint256 projectId, uint256 permissionId) internal view {
        address sender = _msgSender();
        if (
            sender != account
                && !PERMISSIONS.hasPermission({
                    operator: sender,
                    account: account,
                    projectId: projectId,
                    permissionId: permissionId,
                    includeRoot: true,
                    includeWildcardProjectId: true
                })
        ) revert JBPermissioned_Unauthorized(account, sender, projectId, permissionId);
    }

    /// @notice If the 'alsoGrantAccessIf' condition is truthy, proceed. Otherwise, require the message sender to be the
    /// account or
    /// have the relevant permission.
    /// @param account The account to allow.
    /// @param projectId The project ID to check the permission under.
    /// @param permissionId The required permission ID. The operator must have this permission within the specified
    /// project ID.
    /// @param alsoGrantAccessIf An override condition which will allow access regardless of permissions.
    function _requirePermissionAllowingOverrideFrom(
        address account,
        uint256 projectId,
        uint256 permissionId,
        bool alsoGrantAccessIf
    )
        internal
        view
    {
        if (alsoGrantAccessIf) return;
        _requirePermissionFrom(account, projectId, permissionId);
    }
}

File 17 of 139 : JBApprovalStatus.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice A ruleset's approval status in a ruleset approval hook.
enum JBApprovalStatus {
    Empty,
    Upcoming,
    Active,
    ApprovalExpected,
    Approved,
    Failed
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBAfterCashOutRecordedContext} from "./../structs/JBAfterCashOutRecordedContext.sol";

/// @notice Hook called after a terminal's `cashOutTokensOf(...)` logic completes (if passed by the ruleset's data
/// hook).
interface IJBCashOutHook is IERC165 {
    /// @notice This function is called by the terminal's `cashOutTokensOf(...)` function after the cash out has been
    /// recorded in the terminal store.
    /// @dev Critical business logic should be protected by appropriate access control.
    /// @param context The context passed in by the terminal, as a `JBAfterCashOutRecordedContext` struct.
    function afterCashOutRecordedWith(JBAfterCashOutRecordedContext calldata context) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IJBDirectory} from "./IJBDirectory.sol";
import {IJBDirectoryAccessControl} from "./IJBDirectoryAccessControl.sol";
import {IJBFundAccessLimits} from "./IJBFundAccessLimits.sol";
import {IJBPriceFeed} from "./IJBPriceFeed.sol";
import {IJBPrices} from "./IJBPrices.sol";
import {IJBProjects} from "./IJBProjects.sol";
import {IJBProjectUriRegistry} from "./IJBProjectUriRegistry.sol";
import {IJBRulesets} from "./IJBRulesets.sol";
import {IJBSplits} from "./IJBSplits.sol";
import {IJBTerminal} from "./IJBTerminal.sol";
import {IJBToken} from "./IJBToken.sol";
import {IJBTokens} from "./IJBTokens.sol";
import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";
import {JBRuleset} from "./../structs/JBRuleset.sol";
import {JBRulesetConfig} from "./../structs/JBRulesetConfig.sol";
import {JBRulesetMetadata} from "./../structs/JBRulesetMetadata.sol";
import {JBRulesetWithMetadata} from "./../structs/JBRulesetWithMetadata.sol";
import {JBSplit} from "./../structs/JBSplit.sol";
import {JBSplitGroup} from "./../structs/JBSplitGroup.sol";
import {JBTerminalConfig} from "./../structs/JBTerminalConfig.sol";

interface IJBController is IERC165, IJBProjectUriRegistry, IJBDirectoryAccessControl {
    event BurnTokens(
        address indexed holder, uint256 indexed projectId, uint256 tokenCount, string memo, address caller
    );
    event LaunchProject(uint256 rulesetId, uint256 projectId, string projectUri, string memo, address caller);
    event LaunchRulesets(uint256 rulesetId, uint256 projectId, string memo, address caller);
    event MintTokens(
        address indexed beneficiary,
        uint256 indexed projectId,
        uint256 tokenCount,
        uint256 beneficiaryTokenCount,
        string memo,
        uint256 reservedPercent,
        address caller
    );
    event PrepMigration(uint256 indexed projectId, address from, address caller);
    event QueueRulesets(uint256 rulesetId, uint256 projectId, string memo, address caller);
    event ReservedDistributionReverted(
        uint256 indexed projectId, JBSplit split, uint256 tokenCount, bytes reason, address caller
    );
    event SendReservedTokensToSplit(
        uint256 indexed projectId,
        uint256 indexed rulesetId,
        uint256 indexed groupId,
        JBSplit split,
        uint256 tokenCount,
        address caller
    );
    event SendReservedTokensToSplits(
        uint256 indexed rulesetId,
        uint256 indexed rulesetCycleNumber,
        uint256 indexed projectId,
        address owner,
        uint256 tokenCount,
        uint256 leftoverAmount,
        address caller
    );
    event SetUri(uint256 indexed projectId, string uri, address caller);

    function DIRECTORY() external view returns (IJBDirectory);
    function FUND_ACCESS_LIMITS() external view returns (IJBFundAccessLimits);
    function PRICES() external view returns (IJBPrices);
    function PROJECTS() external view returns (IJBProjects);
    function RULESETS() external view returns (IJBRulesets);
    function SPLITS() external view returns (IJBSplits);
    function TOKENS() external view returns (IJBTokens);

    function allRulesetsOf(
        uint256 projectId,
        uint256 startingId,
        uint256 size
    )
        external
        view
        returns (JBRulesetWithMetadata[] memory rulesets);
    function currentRulesetOf(uint256 projectId)
        external
        view
        returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);
    function getRulesetOf(
        uint256 projectId,
        uint256 rulesetId
    )
        external
        view
        returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);
    function latestQueuedRulesetOf(uint256 projectId)
        external
        view
        returns (JBRuleset memory, JBRulesetMetadata memory metadata, JBApprovalStatus);
    function pendingReservedTokenBalanceOf(uint256 projectId) external view returns (uint256);
    function totalTokenSupplyWithReservedTokensOf(uint256 projectId) external view returns (uint256);
    function upcomingRulesetOf(uint256 projectId)
        external
        view
        returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);

    function addPriceFeed(
        uint256 projectId,
        uint256 pricingCurrency,
        uint256 unitCurrency,
        IJBPriceFeed feed
    )
        external;
    function burnTokensOf(address holder, uint256 projectId, uint256 tokenCount, string calldata memo) external;
    function claimTokensFor(address holder, uint256 projectId, uint256 tokenCount, address beneficiary) external;
    function deployERC20For(
        uint256 projectId,
        string calldata name,
        string calldata symbol,
        bytes32 salt
    )
        external
        returns (IJBToken token);
    function launchProjectFor(
        address owner,
        string calldata projectUri,
        JBRulesetConfig[] calldata rulesetConfigurations,
        JBTerminalConfig[] memory terminalConfigurations,
        string calldata memo
    )
        external
        returns (uint256 projectId);
    function launchRulesetsFor(
        uint256 projectId,
        JBRulesetConfig[] calldata rulesetConfigurations,
        JBTerminalConfig[] memory terminalConfigurations,
        string calldata memo
    )
        external
        returns (uint256 rulesetId);
    function mintTokensOf(
        uint256 projectId,
        uint256 tokenCount,
        address beneficiary,
        string calldata memo,
        bool useReservedPercent
    )
        external
        returns (uint256 beneficiaryTokenCount);
    function queueRulesetsOf(
        uint256 projectId,
        JBRulesetConfig[] calldata rulesetConfigurations,
        string calldata memo
    )
        external
        returns (uint256 rulesetId);
    function sendReservedTokensToSplitsOf(uint256 projectId) external returns (uint256);
    function setSplitGroupsOf(uint256 projectId, uint256 rulesetId, JBSplitGroup[] calldata splitGroups) external;
    function setTokenFor(uint256 projectId, IJBToken token) external;
    function transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 creditCount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IJBProjects} from "./IJBProjects.sol";
import {IJBTerminal} from "./IJBTerminal.sol";

interface IJBDirectory {
    event AddTerminal(uint256 indexed projectId, IJBTerminal indexed terminal, address caller);
    event SetController(uint256 indexed projectId, IERC165 indexed controller, address caller);
    event SetIsAllowedToSetFirstController(address indexed addr, bool indexed isAllowed, address caller);
    event SetPrimaryTerminal(
        uint256 indexed projectId, address indexed token, IJBTerminal indexed terminal, address caller
    );
    event SetTerminals(uint256 indexed projectId, IJBTerminal[] terminals, address caller);

    function PROJECTS() external view returns (IJBProjects);

    function controllerOf(uint256 projectId) external view returns (IERC165);
    function isAllowedToSetFirstController(address account) external view returns (bool);
    function isTerminalOf(uint256 projectId, IJBTerminal terminal) external view returns (bool);
    function primaryTerminalOf(uint256 projectId, address token) external view returns (IJBTerminal);
    function terminalsOf(uint256 projectId) external view returns (IJBTerminal[] memory);

    function setControllerOf(uint256 projectId, IERC165 controller) external;
    function setIsAllowedToSetFirstController(address account, bool flag) external;
    function setPrimaryTerminalOf(uint256 projectId, address token, IJBTerminal terminal) external;
    function setTerminalsOf(uint256 projectId, IJBTerminal[] calldata terminals) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJBDirectoryAccessControl {
    function setControllerAllowed(uint256 projectId) external view returns (bool);
    function setTerminalsAllowed(uint256 projectId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBCurrencyAmount} from "./../structs/JBCurrencyAmount.sol";
import {JBFundAccessLimitGroup} from "./../structs/JBFundAccessLimitGroup.sol";

interface IJBFundAccessLimits {
    event SetFundAccessLimits(
        uint256 indexed rulesetId,
        uint256 indexed projectId,
        JBFundAccessLimitGroup fundAccessLimitGroup,
        address caller
    );

    function payoutLimitOf(
        uint256 projectId,
        uint256 rulesetId,
        address terminal,
        address token,
        uint256 currency
    )
        external
        view
        returns (uint256 payoutLimit);
    function payoutLimitsOf(
        uint256 projectId,
        uint256 rulesetId,
        address terminal,
        address token
    )
        external
        view
        returns (JBCurrencyAmount[] memory payoutLimits);
    function surplusAllowanceOf(
        uint256 projectId,
        uint256 rulesetId,
        address terminal,
        address token,
        uint256 currency
    )
        external
        view
        returns (uint256 surplusAllowance);
    function surplusAllowancesOf(
        uint256 projectId,
        uint256 rulesetId,
        address terminal,
        address token
    )
        external
        view
        returns (JBCurrencyAmount[] memory surplusAllowances);

    function setFundAccessLimitsFor(
        uint256 projectId,
        uint256 rulesetId,
        JBFundAccessLimitGroup[] memory fundAccessLimitGroups
    )
        external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBAfterPayRecordedContext} from "./../structs/JBAfterPayRecordedContext.sol";

/// @notice Hook called after a terminal's `pay(...)` logic completes (if passed by the ruleset's data hook).
interface IJBPayHook is IERC165 {
    /// @notice This function is called by the terminal's `pay(...)` function after the payment has been recorded in the
    /// terminal store.
    /// @dev Critical business logic should be protected by appropriate access control.
    /// @param context The context passed in by the terminal, as a `JBAfterPayRecordedContext` struct.
    function afterPayRecordedWith(JBAfterPayRecordedContext calldata context) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBSplits} from "./IJBSplits.sol";
import {IJBTerminal} from "./IJBTerminal.sol";
import {JBSplit} from "../structs/JBSplit.sol";

/// @notice A terminal that can send payouts.
interface IJBPayoutTerminal is IJBTerminal {
    event PayoutReverted(uint256 indexed projectId, JBSplit split, uint256 amount, bytes reason, address caller);
    event PayoutTransferReverted(
        uint256 indexed projectId,
        address addr,
        address token,
        uint256 amount,
        uint256 fee,
        bytes reason,
        address caller
    );
    event SendPayouts(
        uint256 indexed rulesetId,
        uint256 indexed rulesetCycleNumber,
        uint256 indexed projectId,
        address projectOwner,
        uint256 amount,
        uint256 amountPaidOut,
        uint256 fee,
        uint256 netLeftoverPayoutAmount,
        address caller
    );
    event SendPayoutToSplit(
        uint256 indexed projectId,
        uint256 indexed rulesetId,
        uint256 indexed group,
        JBSplit split,
        uint256 amount,
        uint256 netAmount,
        address caller
    );
    event UseAllowance(
        uint256 indexed rulesetId,
        uint256 indexed rulesetCycleNumber,
        uint256 indexed projectId,
        address beneficiary,
        address feeBeneficiary,
        uint256 amount,
        uint256 amountPaidOut,
        uint256 netAmountPaidOut,
        string memo,
        address caller
    );

    function sendPayoutsOf(
        uint256 projectId,
        address token,
        uint256 amount,
        uint256 currency,
        uint256 minTokensPaidOut
    )
        external
        returns (uint256 netLeftoverPayoutAmount);
    function useAllowanceOf(
        uint256 projectId,
        address token,
        uint256 amount,
        uint256 currency,
        uint256 minTokensPaidOut,
        address payable beneficiary,
        address payable feeBeneficiary,
        string calldata memo
    )
        external
        returns (uint256 netAmountPaidOut);
}

File 25 of 139 : IJBPermissioned.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPermissions} from "./IJBPermissions.sol";

interface IJBPermissioned {
    function PERMISSIONS() external view returns (IJBPermissions);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBPermissionsData} from "./../structs/JBPermissionsData.sol";

interface IJBPermissions {
    event OperatorPermissionsSet(
        address indexed operator,
        address indexed account,
        uint256 indexed projectId,
        uint8[] permissionIds,
        uint256 packed,
        address caller
    );

    function WILDCARD_PROJECT_ID() external view returns (uint256);

    function permissionsOf(address operator, address account, uint256 projectId) external view returns (uint256);

    function hasPermission(
        address operator,
        address account,
        uint256 projectId,
        uint256 permissionId,
        bool includeRoot,
        bool includeWildcardProjectId
    )
        external
        view
        returns (bool);

    function hasPermissions(
        address operator,
        address account,
        uint256 projectId,
        uint256[] calldata permissionIds,
        bool includeRoot,
        bool includeWildcardProjectId
    )
        external
        view
        returns (bool);

    function setPermissionsFor(address account, JBPermissionsData calldata permissionsData) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJBPriceFeed {
    function currentUnitPrice(uint256 targetDecimals) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPriceFeed} from "./IJBPriceFeed.sol";
import {IJBProjects} from "./IJBProjects.sol";

interface IJBPrices {
    event AddPriceFeed(
        uint256 indexed projectId,
        uint256 indexed pricingCurrency,
        uint256 indexed unitCurrency,
        IJBPriceFeed feed,
        address caller
    );

    function DEFAULT_PROJECT_ID() external view returns (uint256);
    function PROJECTS() external view returns (IJBProjects);

    function priceFeedFor(
        uint256 projectId,
        uint256 pricingCurrency,
        uint256 unitCurrency
    )
        external
        view
        returns (IJBPriceFeed);
    function pricePerUnitOf(
        uint256 projectId,
        uint256 pricingCurrency,
        uint256 unitCurrency,
        uint256 decimals
    )
        external
        view
        returns (uint256);

    function addPriceFeedFor(
        uint256 projectId,
        uint256 pricingCurrency,
        uint256 unitCurrency,
        IJBPriceFeed feed
    )
        external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJBProjectUriRegistry {
    function uriOf(uint256 projectId) external view returns (string memory);
    function setUriOf(uint256 projectId, string calldata uri) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import {IJBTokenUriResolver} from "./IJBTokenUriResolver.sol";

interface IJBProjects is IERC721 {
    event Create(uint256 indexed projectId, address indexed owner, address caller);
    event SetTokenUriResolver(IJBTokenUriResolver indexed resolver, address caller);

    function count() external view returns (uint256);
    function tokenUriResolver() external view returns (IJBTokenUriResolver);

    function createFor(address owner) external returns (uint256 projectId);
    function setTokenUriResolver(IJBTokenUriResolver resolver) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";

/// @notice `IJBRulesetApprovalHook`s are used to determine whether the next ruleset in the ruleset queue is approved or
/// rejected.
/// @dev Project rulesets are stored in a queue. Rulesets take effect after the previous ruleset in the queue ends, and
/// only if they are approved by the previous ruleset's approval hook.
interface IJBRulesetApprovalHook is IERC165 {
    function DURATION() external view returns (uint256);

    function approvalStatusOf(
        uint256 projectId,
        uint256 rulesetId,
        uint256 start
    )
        external
        view
        returns (JBApprovalStatus);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBBeforePayRecordedContext} from "./../structs/JBBeforePayRecordedContext.sol";
import {JBBeforeCashOutRecordedContext} from "./../structs/JBBeforeCashOutRecordedContext.sol";
import {JBCashOutHookSpecification} from "./../structs/JBCashOutHookSpecification.sol";
import {JBPayHookSpecification} from "./../structs/JBPayHookSpecification.sol";

/// @notice Data hooks can extend a terminal's core pay/cashout functionality by overriding the weight or memo. They can
/// also specify pay/cashout hooks for the terminal to fulfill, or allow addresses to mint a project's tokens on-demand.
/// @dev If a project's ruleset has `useDataHookForPay` or `useDataHookForCashOut` enabled, its `dataHook` is called by
/// the terminal upon payments/cashouts (respectively).
interface IJBRulesetDataHook is IERC165 {
    /// @notice A flag indicating whether an address has permission to mint a project's tokens on-demand.
    /// @dev A project's data hook can allow any address to mint its tokens.
    /// @param projectId The ID of the project whose token can be minted.
    /// @param addr The address to check the token minting permission of.
    /// @return flag A flag indicating whether the address has permission to mint the project's tokens on-demand.
    function hasMintPermissionFor(uint256 projectId, address addr) external view returns (bool flag);

    /// @notice The data calculated before a payment is recorded in the terminal store. This data is provided to the
    /// terminal's `pay(...)` transaction.
    /// @param context The context passed to this data hook by the `pay(...)` function as a `JBBeforePayRecordedContext`
    /// struct.
    /// @return weight The new `weight` to use, overriding the ruleset's `weight`.
    /// @return hookSpecifications The amount and data to send to pay hooks instead of adding to the terminal's balance.
    function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
        external
        view
        returns (uint256 weight, JBPayHookSpecification[] memory hookSpecifications);

    /// @notice The data calculated before a cash out is recorded in the terminal store. This data is provided to the
    /// terminal's `cashOutTokensOf(...)` transaction.
    /// @param context The context passed to this data hook by the `cashOutTokensOf(...)` function as a
    /// `JBBeforeCashOutRecordedContext` struct.
    /// @return cashOutTaxRate The rate determining the amount that should be reclaimable for a given surplus and token
    /// supply.
    /// @return cashOutCount The amount of tokens that should be considered cashed out.
    /// @return totalSupply The total amount of tokens that are considered to be existing.
    /// @return hookSpecifications The amount and data to send to cash out hooks instead of returning to the
    /// beneficiary.
    function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
        external
        view
        returns (
            uint256 cashOutTaxRate,
            uint256 cashOutCount,
            uint256 totalSupply,
            JBCashOutHookSpecification[] memory hookSpecifications
        );
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";
import {JBRuleset} from "./../structs/JBRuleset.sol";
import {IJBRulesetApprovalHook} from "./IJBRulesetApprovalHook.sol";

interface IJBRulesets {
    event RulesetInitialized(
        uint256 indexed rulesetId, uint256 indexed projectId, uint256 indexed basedOnId, address caller
    );
    event RulesetQueued(
        uint256 indexed rulesetId,
        uint256 indexed projectId,
        uint256 duration,
        uint256 weight,
        uint256 weightCutPercent,
        IJBRulesetApprovalHook approvalHook,
        uint256 metadata,
        uint256 mustStartAtOrAfter,
        address caller
    );

    event WeightCacheUpdated(uint256 projectId, uint112 weight, uint256 weightCutMultiple, address caller);

    function latestRulesetIdOf(uint256 projectId) external view returns (uint256);

    function currentApprovalStatusForLatestRulesetOf(uint256 projectId) external view returns (JBApprovalStatus);
    function currentOf(uint256 projectId) external view returns (JBRuleset memory ruleset);
    function deriveCycleNumberFrom(
        uint256 baseRulesetCycleNumber,
        uint256 baseRulesetStart,
        uint256 baseRulesetDuration,
        uint256 start
    )
        external
        returns (uint256);
    function deriveStartFrom(
        uint256 baseRulesetStart,
        uint256 baseRulesetDuration,
        uint256 mustStartAtOrAfter
    )
        external
        view
        returns (uint256 start);
    function deriveWeightFrom(
        uint256 projectId,
        uint256 baseRulesetStart,
        uint256 baseRulesetDuration,
        uint256 baseRulesetWeight,
        uint256 baseRulesetWeightCutPercent,
        uint256 baseRulesetCacheId,
        uint256 start
    )
        external
        view
        returns (uint256 weight);
    function getRulesetOf(uint256 projectId, uint256 rulesetId) external view returns (JBRuleset memory);
    function latestQueuedOf(uint256 projectId)
        external
        view
        returns (JBRuleset memory ruleset, JBApprovalStatus approvalStatus);
    function allOf(
        uint256 projectId,
        uint256 startingId,
        uint256 size
    )
        external
        view
        returns (JBRuleset[] memory rulesets);
    function upcomingOf(uint256 projectId) external view returns (JBRuleset memory ruleset);

    function queueFor(
        uint256 projectId,
        uint256 duration,
        uint256 weight,
        uint256 weightCutPercent,
        IJBRulesetApprovalHook approvalHook,
        uint256 metadata,
        uint256 mustStartAtOrAfter
    )
        external
        returns (JBRuleset memory ruleset);
    function updateRulesetWeightCache(uint256 projectId) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBSplitHookContext} from "../structs/JBSplitHookContext.sol";

/// @title Split hook
/// @notice Allows processing a single split with custom logic.
/// @dev The split hook's address should be set as the `hook` in the relevant split.
interface IJBSplitHook is IERC165 {
    /// @notice If a split has a split hook, payment terminals and controllers call this function while processing the
    /// split.
    /// @dev Critical business logic should be protected by appropriate access control. The tokens and/or native tokens
    /// are optimistically transferred to the split hook when this function is called.
    /// @param context The context passed by the terminal/controller to the split hook as a `JBSplitHookContext` struct:
    function processSplitWith(JBSplitHookContext calldata context) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBSplit} from "./../structs/JBSplit.sol";
import {JBSplitGroup} from "./../structs/JBSplitGroup.sol";

interface IJBSplits {
    event SetSplit(
        uint256 indexed projectId, uint256 indexed rulesetId, uint256 indexed groupId, JBSplit split, address caller
    );

    function FALLBACK_RULESET_ID() external view returns (uint256);

    function splitsOf(uint256 projectId, uint256 rulesetId, uint256 groupId) external view returns (JBSplit[] memory);

    function setSplitGroupsOf(uint256 projectId, uint256 rulesetId, JBSplitGroup[] memory splitGroups) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IJBPayHook} from "./IJBPayHook.sol";
import {JBAccountingContext} from "../structs/JBAccountingContext.sol";
import {JBAfterPayRecordedContext} from "../structs/JBAfterPayRecordedContext.sol";

/// @notice A terminal that accepts payments and can be migrated.
interface IJBTerminal is IERC165 {
    event AddToBalance(
        uint256 indexed projectId, uint256 amount, uint256 returnedFees, string memo, bytes metadata, address caller
    );
    event HookAfterRecordPay(
        IJBPayHook indexed hook, JBAfterPayRecordedContext context, uint256 specificationAmount, address caller
    );

    event MigrateTerminal(
        uint256 indexed projectId, address indexed token, IJBTerminal indexed to, uint256 amount, address caller
    );
    event Pay(
        uint256 indexed rulesetId,
        uint256 indexed rulesetCycleNumber,
        uint256 indexed projectId,
        address payer,
        address beneficiary,
        uint256 amount,
        uint256 newlyIssuedTokenCount,
        string memo,
        bytes metadata,
        address caller
    );
    event SetAccountingContext(uint256 indexed projectId, JBAccountingContext context, address caller);

    function accountingContextForTokenOf(
        uint256 projectId,
        address token
    )
        external
        view
        returns (JBAccountingContext memory);
    function accountingContextsOf(uint256 projectId) external view returns (JBAccountingContext[] memory);
    function currentSurplusOf(
        uint256 projectId,
        JBAccountingContext[] memory accountingContexts,
        uint256 decimals,
        uint256 currency
    )
        external
        view
        returns (uint256);

    function addAccountingContextsFor(uint256 projectId, JBAccountingContext[] calldata accountingContexts) external;
    function addToBalanceOf(
        uint256 projectId,
        address token,
        uint256 amount,
        bool shouldReturnHeldFees,
        string calldata memo,
        bytes calldata metadata
    )
        external
        payable;
    function migrateBalanceOf(uint256 projectId, address token, IJBTerminal to) external returns (uint256 balance);
    function pay(
        uint256 projectId,
        address token,
        uint256 amount,
        address beneficiary,
        uint256 minReturnedTokens,
        string calldata memo,
        bytes calldata metadata
    )
        external
        payable
        returns (uint256 beneficiaryTokenCount);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJBToken {
    function balanceOf(address account) external view returns (uint256);
    function canBeAddedTo(uint256 projectId) external view returns (bool);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);

    function initialize(string memory name, string memory symbol, address owner) external;
    function burn(address account, uint256 amount) external;
    function mint(address account, uint256 amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJBTokenUriResolver {
    function getUri(uint256 projectId) external view returns (string memory tokenUri);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBToken} from "./IJBToken.sol";

interface IJBTokens {
    event DeployERC20(
        uint256 indexed projectId, IJBToken indexed token, string name, string symbol, bytes32 salt, address caller
    );
    event Burn(
        address indexed holder,
        uint256 indexed projectId,
        uint256 count,
        uint256 creditBalance,
        uint256 tokenBalance,
        address caller
    );
    event ClaimTokens(
        address indexed holder,
        uint256 indexed projectId,
        uint256 creditBalance,
        uint256 count,
        address beneficiary,
        address caller
    );
    event Mint(
        address indexed holder, uint256 indexed projectId, uint256 count, bool tokensWereClaimed, address caller
    );
    event SetToken(uint256 indexed projectId, IJBToken indexed token, address caller);
    event TransferCredits(
        address indexed holder, uint256 indexed projectId, address indexed recipient, uint256 count, address caller
    );

    function creditBalanceOf(address holder, uint256 projectId) external view returns (uint256);
    function projectIdOf(IJBToken token) external view returns (uint256);
    function tokenOf(uint256 projectId) external view returns (IJBToken);
    function totalCreditSupplyOf(uint256 projectId) external view returns (uint256);

    function totalBalanceOf(address holder, uint256 projectId) external view returns (uint256 result);
    function totalSupplyOf(uint256 projectId) external view returns (uint256);

    function burnFrom(address holder, uint256 projectId, uint256 count) external;
    function claimTokensFor(address holder, uint256 projectId, uint256 count, address beneficiary) external;
    function deployERC20For(
        uint256 projectId,
        string calldata name,
        string calldata symbol,
        bytes32 salt
    )
        external
        returns (IJBToken token);
    function mintFor(address holder, uint256 projectId, uint256 count) external returns (IJBToken token);
    function setTokenFor(uint256 projectId, IJBToken token) external;
    function transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 count) external;
}

File 40 of 139 : JBCashOuts.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {mulDiv} from "@prb/math/src/Common.sol";

import {JBConstants} from "./JBConstants.sol";

/// @notice Cash out calculations.
library JBCashOuts {
    /// @notice Returns the amount of surplus terminal tokens which can be reclaimed based on the total surplus, the
    /// number of tokens being cashed out, the total token supply, and the ruleset's cash out tax rate.
    /// @param surplus The total amount of surplus terminal tokens.
    /// @param cashOutCount The number of tokens being cashed out, as a fixed point number with 18 decimals.
    /// @param totalSupply The total token supply, as a fixed point number with 18 decimals.
    /// @param cashOutTaxRate The current ruleset's cash out tax rate.
    /// @return reclaimableSurplus The amount of surplus tokens that can be reclaimed.
    function cashOutFrom(
        uint256 surplus,
        uint256 cashOutCount,
        uint256 totalSupply,
        uint256 cashOutTaxRate
    )
        internal
        pure
        returns (uint256)
    {
        // If the cash out tax rate is the max, no surplus can be reclaimed.
        if (cashOutTaxRate == JBConstants.MAX_CASH_OUT_TAX_RATE) return 0;

        // If the total supply is being cashed out, return the entire surplus.
        if (cashOutCount >= totalSupply) return surplus;

        // Get a reference to the linear proportion.
        uint256 base = mulDiv(surplus, cashOutCount, totalSupply);

        // These conditions are all part of the same curve.
        // Edge conditions are separated to minimize the operations performed in those cases.
        if (cashOutTaxRate == 0) {
            return base;
        }

        return mulDiv(
            base,
            (JBConstants.MAX_CASH_OUT_TAX_RATE - cashOutTaxRate) + mulDiv(cashOutTaxRate, cashOutCount, totalSupply),
            JBConstants.MAX_CASH_OUT_TAX_RATE
        );
    }
}

File 41 of 139 : JBConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Global constants used across Juicebox contracts.
library JBConstants {
    /// @notice Each chain's native token address in Juicebox is represented by
    /// 0x000000000000000000000000000000000000EEEe.
    address public constant NATIVE_TOKEN = address(0x000000000000000000000000000000000000EEEe);
    uint16 public constant MAX_RESERVED_PERCENT = 10_000;
    uint16 public constant MAX_CASH_OUT_TAX_RATE = 10_000;
    uint32 public constant MAX_WEIGHT_CUT_PERCENT = 1_000_000_000;
    uint32 public constant SPLITS_TOTAL_PERCENT = 1_000_000_000;
    uint16 public constant MAX_FEE = 1000;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {mulDiv} from "@prb/math/src/Common.sol";

import {JBConstants} from "./../libraries/JBConstants.sol";

/// @notice Fee calculations.
library JBFees {
    /// @notice Returns the amount of tokens to pay as a fee relative to the specified `amount`.
    /// @param amountAfterFee The amount that the fee is based on, as a fixed point number.
    /// @param feePercent The fee percent, out of `JBConstants.MAX_FEE`.
    /// @return The amount of tokens to pay as a fee, as a fixed point number with the same number of decimals as the
    /// provided `amount`.
    function feeAmountResultingIn(uint256 amountAfterFee, uint256 feePercent) internal pure returns (uint256) {
        // The amount of tokens from the `amount` to pay as a fee. If reverse, the fee taken from a payout of
        // `amount`.
        return mulDiv(amountAfterFee, JBConstants.MAX_FEE, JBConstants.MAX_FEE - feePercent) - amountAfterFee;
    }

    /// @notice Returns the fee that would have been paid based on an `amount` which has already had the fee subtracted
    /// from it.
    /// @param amountBeforeFee The amount that the fee is based on, as a fixed point number with the same amount of
    /// decimals as
    /// this terminal.
    /// @param feePercent The fee percent, out of `JBConstants.MAX_FEE`.
    /// @return The amount of the fee, as a fixed point number with the same amount of decimals as this terminal.
    function feeAmountFrom(uint256 amountBeforeFee, uint256 feePercent) internal pure returns (uint256) {
        // The amount of tokens from the `amount` to pay as a fee. If reverse, the fee taken from a payout of
        // `amount`.
        return mulDiv(amountBeforeFee, feePercent, JBConstants.MAX_FEE);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @notice Library to parse and create metadata to store {id: data} entries.
 *
 * @dev    Metadata are built as:
 *         - 32B of reserved space for the protocol
 *         - a lookup table `Id: offset`, defining the offset of the data for a given 4 bytes id.
 *           The offset fits 1 bytes, the ID 4 bytes. This table is padded to 32B.
 *         - the data for each id, padded to 32B each
 *
 *            +-----------------------+ offset: 0
 *            | 32B reserved          |
 *            +-----------------------+ offset: 1 = end of first 32B
 *            |      (ID1,offset1)    |
 *            |      (ID2,offset2)    |
 *            |       0's padding     |
 *            +-----------------------+ offset: offset1 = 1 + number of words taken by the padded table
 *            |       id1 data1       |
 *            | 0's padding           |
 *            +-----------------------+ offset: offset2 = offset1 + number of words taken by the data1
 *            |       id2 data2       |
 *            | 0's padding           |
 *            +-----------------------+
 */
library JBMetadataResolver {
    error JBMetadataResolver_DataNotPadded();
    error JBMetadataResolver_LengthMismatch();
    error JBMetadataResolver_MetadataTooLong();
    error JBMetadataResolver_MetadataTooShort();

    // The various sizes used in bytes.
    uint256 constant ID_SIZE = 4;
    uint256 constant ID_OFFSET_SIZE = 1;
    uint256 constant WORD_SIZE = 32;

    // The size that an ID takes in the lookup table (Identifier + Offset).
    uint256 constant TOTAL_ID_SIZE = 5; // ID_SIZE + ID_OFFSET_SIZE;

    // The amount of bytes to go forward to get to the offset of the next ID (aka. the end of the offset of the current
    // ID).
    uint256 constant NEXT_ID_OFFSET = 9; // TOTAL_ID_SIZE + ID_SIZE;

    // 1 word (32B) is reserved for the protocol .
    uint256 constant RESERVED_SIZE = 32; // 1 * WORD_SIZE;
    uint256 constant MIN_METADATA_LENGTH = 37; // RESERVED_SIZE + ID_SIZE + ID_OFFSET_SIZE;

    /// @notice Add an {id: data} entry to an existing metadata. This is an append-only mechanism.
    /// @param originalMetadata The original metadata
    /// @param idToAdd The id to add
    /// @param dataToAdd The data to add
    /// @return newMetadata The new metadata with the entry added
    function addToMetadata(
        bytes memory originalMetadata,
        bytes4 idToAdd,
        bytes memory dataToAdd
    )
        internal
        pure
        returns (bytes memory newMetadata)
    {
        // Empty original metadata and maybe something in the first 32 bytes: create new metadata
        if (originalMetadata.length <= RESERVED_SIZE) {
            return abi.encodePacked(bytes32(originalMetadata), bytes32(abi.encodePacked(idToAdd, uint8(2))), dataToAdd);
        }

        // There is something in the table offset, but not a valid entry - avoid overwriting
        if (originalMetadata.length < RESERVED_SIZE + ID_SIZE + 1) revert JBMetadataResolver_MetadataTooShort();

        // Make sure the data is padded to 32 bytes.
        if (dataToAdd.length < 32) revert JBMetadataResolver_DataNotPadded();

        // Get the first data offset - upcast to avoid overflow (same for other offset)...
        uint256 firstOffset = uint8(originalMetadata[RESERVED_SIZE + ID_SIZE]);

        // ...go back to the beginning of the previous word (ie the last word of the table, as it can be padded)
        uint256 lastWordOfTable = firstOffset - 1;

        // The last offset stored in the table and its index
        uint256 lastOffset;

        // The number of words taken by the last data stored
        uint256 numberOfWordslastData;

        // Iterate to find the last entry of the table, lastOffset - we start from the end as the first value
        // encountered
        // will be the last offset
        for (uint256 i = firstOffset * WORD_SIZE - 1; i > lastWordOfTable * WORD_SIZE - 1; i--) {
            // If the byte is not 0, this is the last offset we're looking for
            if (originalMetadata[i] != 0) {
                lastOffset = uint8(originalMetadata[i]);
                uint256 lastOffsetIndex = i;

                // No rounding as this should be padded to 32B
                numberOfWordslastData = (originalMetadata.length - lastOffset * WORD_SIZE) / WORD_SIZE;

                // Copy the reserved word and the table and remove the previous padding
                newMetadata = _sliceBytes(originalMetadata, 0, lastOffsetIndex + 1);

                // Check if the new entry is still fitting in this word
                if (i + TOTAL_ID_SIZE >= firstOffset * WORD_SIZE) {
                    // Increment every offset by 1 (as the table now takes one more word)
                    for (uint256 j = RESERVED_SIZE + ID_SIZE; j < lastOffsetIndex + 1; j += TOTAL_ID_SIZE) {
                        newMetadata[j] = bytes1(uint8(originalMetadata[j]) + 1);
                    }

                    // Increment the last offset so the new offset will be properly set too
                    lastOffset++;
                }

                break;
            }
        }

        // Add the new entry after the last entry of the table, the new offset is the last offset + the number of words
        // taken by the last data
        newMetadata = abi.encodePacked(newMetadata, idToAdd, bytes1(uint8(lastOffset + numberOfWordslastData)));

        // Pad as needed - inlined for gas saving
        uint256 paddedLength =
            newMetadata.length % WORD_SIZE == 0 ? newMetadata.length : (newMetadata.length / WORD_SIZE + 1) * WORD_SIZE;
        assembly {
            mstore(newMetadata, paddedLength)
        }

        // Add existing data at the end
        newMetadata = abi.encodePacked(
            newMetadata, _sliceBytes(originalMetadata, firstOffset * WORD_SIZE, originalMetadata.length)
        );

        // Pad as needed
        paddedLength =
            newMetadata.length % WORD_SIZE == 0 ? newMetadata.length : (newMetadata.length / WORD_SIZE + 1) * WORD_SIZE;
        assembly {
            mstore(newMetadata, paddedLength)
        }

        // Append new data at the end
        newMetadata = abi.encodePacked(newMetadata, dataToAdd);

        // Pad again again as needed
        paddedLength =
            newMetadata.length % WORD_SIZE == 0 ? newMetadata.length : (newMetadata.length / WORD_SIZE + 1) * WORD_SIZE;

        assembly {
            mstore(newMetadata, paddedLength)
        }
    }

    /// @notice Create the metadata for a list of {id:data}
    /// @dev Intended for offchain use (gas heavy)
    /// @param ids The list of ids
    /// @param datas The list of corresponding datas
    /// @return metadata The resulting metadata
    function createMetadata(bytes4[] memory ids, bytes[] memory datas) internal pure returns (bytes memory metadata) {
        if (ids.length != datas.length) revert JBMetadataResolver_LengthMismatch();

        // Add a first empty 32B for the protocol reserved word
        metadata = abi.encodePacked(bytes32(0));

        // First offset for the data is after the first reserved word...
        uint256 offset = 1;

        // ... and after the id/offset lookup table, rounding up to 32 bytes words if not a multiple
        offset += ((ids.length * JBMetadataResolver.TOTAL_ID_SIZE) - 1) / JBMetadataResolver.WORD_SIZE + 1;

        // Keep a reference to the number of ids.
        uint256 numberOfIds = ids.length;

        // For each id, add it to the lookup table with the next free offset, then increment the offset by the data
        // length (rounded up)
        for (uint256 i; i < numberOfIds; ++i) {
            // Set the data being iterated on.
            bytes memory data = datas[i];

            if (data.length < 32 || data.length % JBMetadataResolver.WORD_SIZE != 0) {
                revert JBMetadataResolver_DataNotPadded();
            }

            metadata = abi.encodePacked(metadata, ids[i], bytes1(uint8(offset)));
            offset += data.length / JBMetadataResolver.WORD_SIZE;

            // Overflowing a bytes1?
            if (offset > 255) revert JBMetadataResolver_MetadataTooLong();
        }

        // Pad the table to a multiple of 32B
        uint256 paddedLength = metadata.length % JBMetadataResolver.WORD_SIZE == 0
            ? metadata.length
            : (metadata.length / JBMetadataResolver.WORD_SIZE + 1) * JBMetadataResolver.WORD_SIZE;
        assembly {
            mstore(metadata, paddedLength)
        }

        // Keep a reference to the number of datas.
        uint256 numberOfDatas = datas.length;

        // Add each metadata to the array, each padded to 32 bytes
        for (uint256 i; i < numberOfDatas; i++) {
            metadata = abi.encodePacked(metadata, datas[i]);
            paddedLength = metadata.length % JBMetadataResolver.WORD_SIZE == 0
                ? metadata.length
                : (metadata.length / JBMetadataResolver.WORD_SIZE + 1) * JBMetadataResolver.WORD_SIZE;

            assembly {
                mstore(metadata, paddedLength)
            }
        }
    }

    /// @notice Parse the metadata to find the data for a specific ID
    /// @dev Returns false and an empty bytes if no data is found
    /// @param id The ID to find.
    /// @param metadata The metadata to parse.
    /// @return found Whether the {id:data} was found
    /// @return targetData The data for the ID (can be empty)
    function getDataFor(bytes4 id, bytes memory metadata) internal pure returns (bool found, bytes memory targetData) {
        // Either no data or empty one with only one selector (32+4+1)
        if (metadata.length <= MIN_METADATA_LENGTH) return (false, "");

        // Get the first data offset - upcast to avoid overflow (same for other offset)
        uint256 firstOffset = uint8(metadata[RESERVED_SIZE + ID_SIZE]);

        // Parse the id's to find id, stop when next offset == 0 or current = first offset
        for (uint256 i = RESERVED_SIZE; metadata[i + ID_SIZE] != bytes1(0) && i < firstOffset * WORD_SIZE;) {
            // Set the current offset.
            uint256 currentOffset = uint256(uint8(metadata[i + ID_SIZE]));

            bytes4 parsedId;
            assembly {
                parsedId := mload(add(add(metadata, 0x20), i))
            }

            // _id found?
            if (parsedId == id) {
                // Are we at the end of the lookup table (either at the start of data's or next offset is 0/in the
                // padding)
                // If not, only return until from this offset to the begining of the next offset
                uint256 end = (i + NEXT_ID_OFFSET >= firstOffset * WORD_SIZE || metadata[i + NEXT_ID_OFFSET] == 0)
                    ? metadata.length
                    : uint256(uint8(metadata[i + NEXT_ID_OFFSET])) * WORD_SIZE;

                return (true, _sliceBytes(metadata, currentOffset * WORD_SIZE, end));
            }
            unchecked {
                i += TOTAL_ID_SIZE;
            }
        }
    }

    /// @notice Returns an unique id following a suggested format (`xor(address(this), purpose name)` where purpose name
    /// is a string giving context to the id (Permit2, quoteForSwap, etc)
    /// @param purpose A string describing the purpose associated with the id
    /// @return id The resulting ID.
    function getId(string memory purpose) internal view returns (bytes4) {
        return getId(purpose, address(this));
    }

    /// @notice Returns an unique id following a suggested format (`xor(address(this), purpose name)` where purpose name
    /// is a string giving context to the id (Permit2, quoteForSwap, etc)
    /// @param purpose A string describing the purpose associated with the id
    /// @param target The target which will use the metadata
    /// @return id The resulting ID.
    function getId(string memory purpose, address target) internal pure returns (bytes4) {
        return bytes4(bytes20(target) ^ bytes20(keccak256(bytes(purpose))));
    }

    /// @notice Slice bytes from a start index to an end index.
    /// @param data The bytes array to slice
    /// @param start The start index to slice at.
    /// @param end The end index to slice at.
    /// @param slicedBytes The sliced array.
    function _sliceBytes(
        bytes memory data,
        uint256 start,
        uint256 end
    )
        private
        pure
        returns (bytes memory slicedBytes)
    {
        assembly {
            let length := sub(end, start)

            // Allocate memory at the freemem(add 0x20 to include the length)
            slicedBytes := mload(0x40)
            mstore(0x40, add(add(slicedBytes, length), 0x20))

            // Store the length (first element)
            mstore(slicedBytes, length)

            // compute the actual data first offset only once
            let startBytes := add(add(data, 0x20), start)

            // same for the out array
            let sliceBytesStartOfData := add(slicedBytes, 0x20)

            // store dem data
            for { let i := 0 } lt(i, end) { i := add(i, 0x20) } {
                mstore(add(sliceBytesStartOfData, i), mload(add(startBytes, i)))
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {JBRuleset} from "./../structs/JBRuleset.sol";
import {JBRulesetMetadata} from "./../structs/JBRulesetMetadata.sol";

library JBRulesetMetadataResolver {
    function reservedPercent(JBRuleset memory ruleset) internal pure returns (uint16) {
        return uint16(ruleset.metadata >> 4);
    }

    function cashOutTaxRate(JBRuleset memory ruleset) internal pure returns (uint16) {
        // Cash out tax rate is a number 0-10000.
        return uint16(ruleset.metadata >> 20);
    }

    function baseCurrency(JBRuleset memory ruleset) internal pure returns (uint32) {
        // Currency is a number 0-4294967296.
        return uint32(ruleset.metadata >> 36);
    }

    function pausePay(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 68) & 1) == 1;
    }

    function pauseCreditTransfers(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 69) & 1) == 1;
    }

    function allowOwnerMinting(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 70) & 1) == 1;
    }

    function allowSetCustomToken(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 71) & 1) == 1;
    }

    function allowTerminalMigration(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 72) & 1) == 1;
    }

    function allowSetTerminals(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 73) & 1) == 1;
    }

    function allowSetController(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 74) & 1) == 1;
    }

    function allowAddAccountingContext(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 75) & 1) == 1;
    }

    function allowAddPriceFeed(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 76) & 1) == 1;
    }

    function ownerMustSendPayouts(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 77) & 1) == 1;
    }

    function holdFees(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 78) & 1) == 1;
    }

    function useTotalSurplusForCashOuts(JBRuleset memory ruleset) internal pure returns (bool) {
        return ((ruleset.metadata >> 79) & 1) == 1;
    }

    function useDataHookForPay(JBRuleset memory ruleset) internal pure returns (bool) {
        return (ruleset.metadata >> 80) & 1 == 1;
    }

    function useDataHookForCashOut(JBRuleset memory ruleset) internal pure returns (bool) {
        return (ruleset.metadata >> 81) & 1 == 1;
    }

    function dataHook(JBRuleset memory ruleset) internal pure returns (address) {
        return address(uint160(ruleset.metadata >> 82));
    }

    function metadata(JBRuleset memory ruleset) internal pure returns (uint16) {
        return uint16(ruleset.metadata >> 242);
    }

    /// @notice Pack the funding cycle metadata.
    /// @param rulesetMetadata The ruleset metadata to validate and pack.
    /// @return packed The packed uint256 of all metadata params. The first 8 bits specify the version.
    function packRulesetMetadata(JBRulesetMetadata memory rulesetMetadata) internal pure returns (uint256 packed) {
        // version 1 in the bits 0-3 (4 bits).
        packed = 1;
        // reserved percent in bits 4-19 (16 bits).
        packed |= uint256(rulesetMetadata.reservedPercent) << 4;
        // cash out tax rate in bits 20-35 (16 bits).
        // cash out tax rate is a number 0-10000.
        packed |= uint256(rulesetMetadata.cashOutTaxRate) << 20;
        // base currency in bits 36-67 (32 bits).
        // base currency is a number 0-16777215.
        packed |= uint256(rulesetMetadata.baseCurrency) << 36;
        // pause pay in bit 68.
        if (rulesetMetadata.pausePay) packed |= 1 << 68;
        // pause credit transfers in bit 69.
        if (rulesetMetadata.pauseCreditTransfers) packed |= 1 << 69;
        // allow discretionary minting in bit 70.
        if (rulesetMetadata.allowOwnerMinting) packed |= 1 << 70;
        // allow a custom token to be set in bit 71.
        if (rulesetMetadata.allowSetCustomToken) packed |= 1 << 71;
        // allow terminal migration in bit 72.
        if (rulesetMetadata.allowTerminalMigration) packed |= 1 << 72;
        // allow set terminals in bit 73.
        if (rulesetMetadata.allowSetTerminals) packed |= 1 << 73;
        // allow set controller in bit 74.
        if (rulesetMetadata.allowSetController) packed |= 1 << 74;
        // allow add accounting context in bit 75.
        if (rulesetMetadata.allowAddAccountingContext) packed |= 1 << 75;
        // allow add price feed in bit 76.
        if (rulesetMetadata.allowAddPriceFeed) packed |= 1 << 76;
        // allow controller migration in bit 77.
        if (rulesetMetadata.ownerMustSendPayouts) packed |= 1 << 77;
        // hold fees in bit 78.
        if (rulesetMetadata.holdFees) packed |= 1 << 78;
        // useTotalSurplusForCashOuts in bit 79.
        if (rulesetMetadata.useTotalSurplusForCashOuts) packed |= 1 << 79;
        // use pay data source in bit 80.
        if (rulesetMetadata.useDataHookForPay) packed |= 1 << 80;
        // use cash out data source in bit 81.
        if (rulesetMetadata.useDataHookForCashOut) packed |= 1 << 81;
        // data source address in bits 82-241.
        packed |= uint256(uint160(address(rulesetMetadata.dataHook))) << 82;
        // metadata in bits 242-255 (14 bits).
        packed |= (uint256(rulesetMetadata.metadata) & 0x3FFF) << 242;
    }

    /// @notice Expand the funding cycle metadata.
    /// @param ruleset The funding cycle having its metadata expanded.
    /// @return rulesetMetadata The ruleset's metadata object.
    function expandMetadata(JBRuleset memory ruleset) internal pure returns (JBRulesetMetadata memory) {
        return JBRulesetMetadata(
            reservedPercent(ruleset),
            cashOutTaxRate(ruleset),
            baseCurrency(ruleset),
            pausePay(ruleset),
            pauseCreditTransfers(ruleset),
            allowOwnerMinting(ruleset),
            allowSetCustomToken(ruleset),
            allowTerminalMigration(ruleset),
            allowSetTerminals(ruleset),
            allowSetController(ruleset),
            allowAddAccountingContext(ruleset),
            allowAddPriceFeed(ruleset),
            ownerMustSendPayouts(ruleset),
            holdFees(ruleset),
            useTotalSurplusForCashOuts(ruleset),
            useDataHookForPay(ruleset),
            useDataHookForCashOut(ruleset),
            dataHook(ruleset),
            metadata(ruleset)
        );
    }
}

File 45 of 139 : JBSurplus.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {IJBTerminal} from "../interfaces/IJBTerminal.sol";
import {JBAccountingContext} from "../structs/JBAccountingContext.sol";

/// @notice Surplus calculations.
library JBSurplus {
    /// @notice Gets the total current surplus amount across all of a project's terminals.
    /// @dev This amount changes as the value of the balances changes in relation to the currency being used to measure
    /// the project's payout limits.
    /// @param projectId The ID of the project to get the total surplus for.
    /// @param terminals The terminals to look for surplus within.
    /// @param accountingContexts The accounting contexts to use to calculate the surplus.
    /// @param decimals The number of decimals that the fixed point surplus result should include.
    /// @param currency The currency that the surplus result should be in terms of.
    /// @return surplus The total surplus of a project's funds in terms of `currency`, as a fixed point number with the
    /// specified number of decimals.
    function currentSurplusOf(
        uint256 projectId,
        IJBTerminal[] memory terminals,
        JBAccountingContext[] memory accountingContexts,
        uint256 decimals,
        uint256 currency
    )
        internal
        view
        returns (uint256 surplus)
    {
        // Keep a reference to the number of termainls.
        uint256 numberOfTerminals = terminals.length;

        // Add the current surplus for each terminal.
        for (uint256 i; i < numberOfTerminals; i++) {
            surplus += terminals[i].currentSurplusOf({
                projectId: projectId,
                accountingContexts: accountingContexts,
                decimals: decimals,
                currency: currency
            });
        }
    }
}

File 46 of 139 : JBAccountingContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member token The address of the token that accounting is being done with.
/// @custom:member decimals The number of decimals expected in that token's fixed point accounting.
/// @custom:member currency The currency that the token is priced in terms of. By convention, this is
/// `uint32(uint160(tokenAddress))` for tokens, or a constant ID from e.g. `JBCurrencyIds` for other currencies.
struct JBAccountingContext {
    address token;
    uint8 decimals;
    uint32 currency;
}

File 47 of 139 : JBAfterCashOutRecordedContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBTokenAmount} from "./JBTokenAmount.sol";

/// @custom:member holder The holder of the tokens being cashed out.
/// @custom:member projectId The ID of the project being cashed out from.
/// @custom:member rulesetId The ID of the ruleset the cash out is being made during.
/// @custom:member cashOutCount The number of project tokens being cashed out.
/// @custom:member cashOutTaxRate The current ruleset's cash out tax rate.
/// @custom:member reclaimedAmount The token amount being reclaimed from the project's terminal balance. Includes the
/// token being
/// reclaimed, the value, the number of decimals included, and the currency of the amount.
/// @custom:member forwardedAmount The token amount being forwarded to the cash out hook. Includes the token
/// being forwarded, the value, the number of decimals included, and the currency of the amount.
/// @custom:member beneficiary The address the reclaimed amount will be sent to.
/// @custom:member hookMetadata Extra data specified by the data hook, which is sent to the cash out hook.
/// @custom:member cashOutMetadata Extra data specified by the account cashing out, which is sent to the cash out hook.
struct JBAfterCashOutRecordedContext {
    address holder;
    uint256 projectId;
    uint256 rulesetId;
    uint256 cashOutCount;
    JBTokenAmount reclaimedAmount;
    JBTokenAmount forwardedAmount;
    uint256 cashOutTaxRate;
    address payable beneficiary;
    bytes hookMetadata;
    bytes cashOutMetadata;
}

File 48 of 139 : JBAfterPayRecordedContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBTokenAmount} from "./JBTokenAmount.sol";

/// @custom:member payer The address the payment originated from.
/// @custom:member projectId The ID of the project being paid.
/// @custom:member rulesetId The ID of the ruleset the payment is being made during.
/// @custom:member amount The payment's token amount. Includes the token being paid, the value, the number of decimals
/// included, and the currency of the amount.
/// @custom:member forwardedAmount The token amount being forwarded to the pay hook. Includes the token
/// being paid, the value, the number of decimals included, and the currency of the amount.
/// @custom:member weight The current ruleset's weight (used to determine how many tokens should be minted).
/// @custom:member newlyIssuedTokenCount The number of project tokens minted for the beneficiary.
/// @custom:member beneficiary The address which receives any tokens this payment yields.
/// @custom:member hookMetadata Extra data specified by the data hook, which is sent to the pay hook.
/// @custom:member payerMetadata Extra data specified by the payer, which is sent to the pay hook.
struct JBAfterPayRecordedContext {
    address payer;
    uint256 projectId;
    uint256 rulesetId;
    JBTokenAmount amount;
    JBTokenAmount forwardedAmount;
    uint256 weight;
    uint256 newlyIssuedTokenCount;
    address beneficiary;
    bytes hookMetadata;
    bytes payerMetadata;
}

File 49 of 139 : JBBeforeCashOutRecordedContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBTokenAmount} from "./JBTokenAmount.sol";

/// @notice Context sent from the terminal to the ruleset's data hook upon cash out.
/// @custom:member terminal The terminal that is facilitating the cash out.
/// @custom:member holder The holder of the tokens being cashed out.
/// @custom:member projectId The ID of the project whose tokens are being cashed out.
/// @custom:member rulesetId The ID of the ruleset the cash out is being made during.
/// @custom:member cashOutCount The number of tokens being cashed out, as a fixed point number with 18 decimals.
/// @custom:member totalSupply The total token supply being used for the calculation, as a fixed point number with 18
/// decimals.
/// @custom:member surplus The surplus amount used for the calculation, as a fixed point number with 18 decimals.
/// Includes the token of the surplus, the surplus value, the number of decimals
/// included, and the currency of the surplus.
/// @custom:member useTotalSurplus If surplus across all of a project's terminals is being used when making cash outs.
/// @custom:member cashOutTaxRate The cash out tax rate of the ruleset the cash out is being made during.
/// @custom:member metadata Extra data provided by the casher.
struct JBBeforeCashOutRecordedContext {
    address terminal;
    address holder;
    uint256 projectId;
    uint256 rulesetId;
    uint256 cashOutCount;
    uint256 totalSupply;
    JBTokenAmount surplus;
    bool useTotalSurplus;
    uint256 cashOutTaxRate;
    bytes metadata;
}

File 50 of 139 : JBBeforePayRecordedContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBTokenAmount} from "./JBTokenAmount.sol";

/// @notice Context sent from the terminal to the ruleset's data hook upon payment.
/// @custom:member terminal The terminal that is facilitating the payment.
/// @custom:member payer The address that the payment originated from.
/// @custom:member amount The payment's token amount, including the token being paid, the value, the number of decimals
/// included, and the currency of the amount.
/// @custom:member projectId The ID of the project being paid.
/// @custom:member rulesetId The ID of the ruleset the payment is being made during.
/// @custom:member beneficiary The specified address that should be the beneficiary of anything that this payment
/// yields.
/// @custom:member weight The weight of the ruleset during which the payment is being made.
/// @custom:member reservedPercent The reserved percent of the ruleset the payment is being made during.
/// @custom:member metadata Extra data specified by the payer.
struct JBBeforePayRecordedContext {
    address terminal;
    address payer;
    JBTokenAmount amount;
    uint256 projectId;
    uint256 rulesetId;
    address beneficiary;
    uint256 weight;
    uint256 reservedPercent;
    bytes metadata;
}

File 51 of 139 : JBCashOutHookSpecification.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBCashOutHook} from "../interfaces/IJBCashOutHook.sol";

/// @notice A cash out hook specification sent from the ruleset's data hook back to the terminal. This specification is
/// fulfilled by the terminal.
/// @custom:member hook The cash out hook to use when fulfilling this specification.
/// @custom:member amount The amount to send to the hook.
/// @custom:member metadata Metadata to pass to the hook.
struct JBCashOutHookSpecification {
    IJBCashOutHook hook;
    uint256 amount;
    bytes metadata;
}

File 52 of 139 : JBCurrencyAmount.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member amount The amount of the currency.
/// @custom:member currency The currency. By convention, this is `uint32(uint160(tokenAddress))` for tokens, or a
/// constant ID from e.g. `JBCurrencyIds` for other currencies.
struct JBCurrencyAmount {
    uint224 amount;
    uint32 currency;
}

File 53 of 139 : JBFundAccessLimitGroup.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBCurrencyAmount} from "./JBCurrencyAmount.sol";

/// @dev Payout limit example: if the `amount` is 5, the `currency` is 1 (USD), and the terminal's token is ETH, then
/// the project can pay out 5 USD worth of ETH during a ruleset.
/// @dev Surplus allowance example: if the `amount` is 5, the `currency` is 1 (USD), and the terminal's token is ETH,
/// then the project can pay out 5 USD worth of ETH from its surplus during a ruleset. A project's surplus is its
/// balance minus its current combined payout limit.
/// @dev If a project has multiple payout limits or surplus allowances, they are all available. They can all be used
/// during a single ruleset.
/// @dev The payout limits' and surplus allowances' fixed point amounts have the same number of decimals as the
/// terminal.
/// @custom:member terminal The terminal that the payout limits and surplus allowances apply to.
/// @custom:member token The token that the payout limits and surplus allowances apply to within the `terminal`.
/// @custom:member payoutLimits An array of payout limits. The payout limits cumulatively dictate the maximum value of
/// `token`s a project can pay out from its balance in a terminal during a ruleset. Each payout limit can have a unique
/// currency and amount.
/// @custom:member surplusAllowances An array of surplus allowances. The surplus allowances cumulatively dictates the
/// maximum value of `token`s a project can pay out from its surplus (balance less payouts) in a terminal during a
/// ruleset. Each surplus allowance can have a unique currency and amount.
struct JBFundAccessLimitGroup {
    address terminal;
    address token;
    JBCurrencyAmount[] payoutLimits;
    JBCurrencyAmount[] surplusAllowances;
}

File 54 of 139 : JBPayHookSpecification.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPayHook} from "../interfaces/IJBPayHook.sol";

/// @notice A pay hook specification sent from the ruleset's data hook back to the terminal. This specification is
/// fulfilled by the terminal.
/// @custom:member hook The pay hook to use when fulfilling this specification.
/// @custom:member amount The amount to send to the hook.
/// @custom:member metadata Metadata to pass the hook.
struct JBPayHookSpecification {
    IJBPayHook hook;
    uint256 amount;
    bytes metadata;
}

File 55 of 139 : JBPermissionsData.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member operator The address that permissions are being given to.
/// @custom:member projectId The ID of the project the operator is being given permissions for. Operators only have
/// permissions under this project's scope. An ID of 0 is a wildcard, which gives an operator permissions across all
/// projects.
/// @custom:member permissionIds The IDs of the permissions being given. See the `JBPermissionIds` library.
struct JBPermissionsData {
    address operator;
    uint64 projectId;
    uint8[] permissionIds;
}

File 56 of 139 : JBRuleset.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBRulesetApprovalHook} from "./../interfaces/IJBRulesetApprovalHook.sol";

/// @dev `JBRuleset` timestamps are unix timestamps (seconds since 00:00 January 1st, 1970 UTC).
/// @custom:member cycleNumber The ruleset's cycle number. Each ruleset's `cycleNumber` is the previous ruleset's
/// `cycleNumber` plus one. Each project's first ruleset has a `cycleNumber` of 1.
/// @custom:member id The ruleset's ID, which is a timestamp of when this ruleset's rules were initialized. The
/// `rulesetId` stays the same for rulesets that automatically cycle over from a manually queued ruleset.
/// @custom:member basedOnId The `rulesetId` of the ruleset which was active when this ruleset was created.
/// @custom:member start The timestamp from which this ruleset is considered active.
/// @custom:member duration The number of seconds the ruleset lasts for. After this duration, a new ruleset will start.
/// The project owner can queue new rulesets at any time, which will take effect once the current ruleset's duration is
/// over. If the `duration` is 0, newly queued rulesets will take effect immediately. If a ruleset ends and there are no
/// new rulesets queued, the current ruleset cycles over to another one with the same properties but a new `start`
/// timestamp and a `weight` reduced by the ruleset's `weightCutPercent`.
/// @custom:member weight A fixed point number with 18 decimals which is typically used by payment terminals to
/// determine how many tokens should be minted when a payment is received. This can be used by other contracts for
/// arbitrary calculations.
/// @custom:member weightCutPercent The percentage by which to reduce the `weight` each time a new ruleset starts.
/// `weight`
/// is
/// a percentage out of `JBConstants.MAX_WEIGHT_CUT_PERCENT`. If it's 0, the next ruleset will have the same `weight` by
/// default. If it's 90%, the next ruleset's `weight` will be 10% smaller. If a ruleset explicitly sets a new `weight`,
/// the `weightCutPercent` doesn't apply.
/// @custom:member approvalHook An address of a contract that says whether a queued ruleset should be approved or
/// rejected. If a
/// ruleset is rejected, it won't go into effect. An approval hook can be used to create rules which dictate how a
/// project owner can change their ruleset over time.
/// @custom:member metadata Extra data associated with a ruleset which can be used by other contracts.
struct JBRuleset {
    uint48 cycleNumber;
    uint48 id;
    uint48 basedOnId;
    uint48 start;
    uint32 duration;
    uint112 weight;
    uint32 weightCutPercent;
    IJBRulesetApprovalHook approvalHook;
    uint256 metadata;
}

File 57 of 139 : JBRulesetConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBRulesetApprovalHook} from "../interfaces/IJBRulesetApprovalHook.sol";
import {JBFundAccessLimitGroup} from "./JBFundAccessLimitGroup.sol";
import {JBRulesetMetadata} from "./JBRulesetMetadata.sol";
import {JBSplitGroup} from "./JBSplitGroup.sol";

/// @custom:member mustStartAtOrAfter The earliest time the ruleset can start.
/// @custom:member duration The number of seconds the ruleset lasts for, after which a new ruleset will start. A
/// duration of 0 means that the ruleset will stay active until the project owner explicitly issues a reconfiguration,
/// at which point a new ruleset will immediately start with the updated properties. If the duration is greater than 0,
/// a project owner cannot make changes to a ruleset's parameters while it is active – any proposed changes will apply
/// to the subsequent ruleset. If no changes are proposed, a ruleset rolls over to another one with the same properties
/// but new `start` timestamp and a cut `weight`.
/// @custom:member weight A fixed point number with 18 decimals that contracts can use to base arbitrary calculations
/// on. For example, payment terminals can use this to determine how many tokens should be minted when a payment is
/// received.
/// @custom:member weightCutPercent A percent by how much the `weight` of the subsequent ruleset should be reduced, if
/// the
/// project owner hasn't queued the subsequent ruleset with an explicit `weight`. If it's 0, each ruleset will have
/// equal weight. If the number is 90%, the next ruleset will have a 10% smaller weight. This weight is out of
/// `JBConstants.MAX_WEIGHT_CUT_PERCENT`.
/// @custom:member approvalHook An address of a contract that says whether a proposed ruleset should be accepted or
/// rejected. It
/// can be used to create rules around how a project owner can change ruleset parameters over time.
/// @custom:member metadata Metadata specifying the controller-specific parameters that a ruleset can have. These
/// properties cannot change until the next ruleset starts.
/// @custom:member splitGroups An array of splits to use for any number of groups while the ruleset is active.
/// @custom:member fundAccessLimitGroups An array of structs which dictate the amount of funds a project can access from
/// its balance in each payment terminal while the ruleset is active. Amounts are fixed point numbers using the same
/// number of decimals as the corresponding terminal. The `_payoutLimit` and `_surplusAllowance` parameters must fit in
/// a `uint232`.
struct JBRulesetConfig {
    uint48 mustStartAtOrAfter;
    uint32 duration;
    uint112 weight;
    uint32 weightCutPercent;
    IJBRulesetApprovalHook approvalHook;
    JBRulesetMetadata metadata;
    JBSplitGroup[] splitGroups;
    JBFundAccessLimitGroup[] fundAccessLimitGroups;
}

File 58 of 139 : JBRulesetMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member reservedPercent The reserved percent of the ruleset. This number is a percentage calculated out of
/// `JBConstants.MAX_RESERVED_PERCENT`.
/// @custom:member cashOutTaxRate The cash out tax rate of the ruleset. This number is a percentage calculated out of
/// `JBConstants.MAX_CASH_OUT_TAX_RATE`.
/// @custom:member baseCurrency The currency on which to base the ruleset's weight. By convention, this is
/// `uint32(uint160(tokenAddress))` for tokens, or a constant ID from e.g. `JBCurrencyIds` for other currencies.
/// @custom:member pausePay A flag indicating if the pay functionality should be paused during the ruleset.
/// @custom:member pauseCreditTransfers A flag indicating if the project token transfer functionality should be paused
/// during the funding cycle.
/// @custom:member allowOwnerMinting A flag indicating if the project owner or an operator with the `MINT_TOKENS`
/// permission from the owner should be allowed to mint project tokens on demand during this ruleset.
/// @custom:member allowTerminalMigration A flag indicating if migrating terminals should be allowed during this
/// ruleset.
/// @custom:member allowSetTerminals A flag indicating if a project's terminals can be added or removed.
/// @custom:member allowSetController A flag indicating if a project's controller can be changed.
/// @custom:member allowAddAccountingContext A flag indicating if a project can add new accounting contexts for its
/// terminals to use.
/// @custom:member allowAddPriceFeed A flag indicating if a project can add new price feeds to calculate exchange rates
/// between its tokens.
/// @custom:member ownerMustSendPayouts A flag indicating if privileged payout distribution should be
/// enforced, otherwise payouts can be distributed by anyone.
/// @custom:member holdFees A flag indicating if fees should be held during this ruleset.
/// @custom:member useTotalSurplusForCashOuts A flag indicating if cash outs should use the project's balance held
/// in all terminals instead of the project's local terminal balance from which the cash out is being fulfilled.
/// @custom:member useDataHookForPay A flag indicating if the data hook should be used for pay transactions during this
/// ruleset.
/// @custom:member useDataHookForCashOut A flag indicating if the data hook should be used for cash out transactions
/// during
/// this ruleset.
/// @custom:member dataHook The data hook to use during this ruleset.
/// @custom:member metadata Metadata of the metadata, only the 14 least significant bits can be used, the 2 most
/// significant bits are disregarded.
struct JBRulesetMetadata {
    uint16 reservedPercent;
    uint16 cashOutTaxRate;
    uint32 baseCurrency;
    bool pausePay;
    bool pauseCreditTransfers;
    bool allowOwnerMinting;
    bool allowSetCustomToken;
    bool allowTerminalMigration;
    bool allowSetTerminals;
    bool allowSetController;
    bool allowAddAccountingContext;
    bool allowAddPriceFeed;
    bool ownerMustSendPayouts;
    bool holdFees;
    bool useTotalSurplusForCashOuts;
    bool useDataHookForPay;
    bool useDataHookForCashOut;
    address dataHook;
    uint16 metadata;
}

File 59 of 139 : JBRulesetWithMetadata.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBRuleset} from "./JBRuleset.sol";
import {JBRulesetMetadata} from "./JBRulesetMetadata.sol";

/// @custom:member ruleset The ruleset.
/// @custom:member metadata The ruleset's metadata.
struct JBRulesetWithMetadata {
    JBRuleset ruleset;
    JBRulesetMetadata metadata;
}

File 60 of 139 : JBSingleAllowance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member sigDeadline Deadline on the permit signature.
/// @custom:member amount The maximum amount allowed to spend.
/// @custom:member expiration Timestamp at which a spender's token allowances become invalid.
/// @custom:member nonce An incrementing value indexed per owner,token,and spender for each signature.
/// @custom:member signature The signature over the permit data. Supports EOA signatures, compact signatures defined by
/// EIP-2098, and contract signatures defined by EIP-1271.
struct JBSingleAllowance {
    uint256 sigDeadline;
    uint160 amount;
    uint48 expiration;
    uint48 nonce;
    bytes signature;
}

File 61 of 139 : JBSplit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBSplitHook} from "./../interfaces/IJBSplitHook.sol";

/// @notice Splits are used to send a percentage of a total token amount to a specific contract, project, or address.
/// Splits are used to send payouts and reserved tokens.
/// @dev 1. If a non-zero split hook contract is specified, this split's tokens are sent there along with this split's
/// properties.
/// @dev 2. Otherwise, if a non-zero project ID is specified, this split's tokens are used to `pay` it through its
/// terminal if possible, or sent to the project's owner if not. If this payment yields tokens, those go to the split's
/// `beneficiary`.
/// @dev 3. Otherwise, this split's tokens are sent directly to the `beneficiary`.
/// @dev To summarize, this split's tokens are sent according to the following priority: `split hook` > `projectId` >
/// `beneficiary`.
/// @custom:member percent The percent of the total token amount that this split sends. This number is out of
/// `JBConstants.SPLITS_TOTAL_PERCENT`.
/// @custom:member projectId The ID of a project to `pay`, if applicable. Resulting tokens will be routed to the
/// `beneficiary`.
/// @custom:member beneficiary Receives this split's tokens if the `hook` and `projectId` are zero. If the `projectId`
/// is specified, the `beneficiary` receives any project tokens minted by this split.
/// @custom:member preferAddToBalance If this split were to `pay` a project through its terminal, this flag indicates
/// whether it should prefer using the terminal's `addToBalance` function instead.
/// @custom:member lockedUntil The split cannot be changed until this timestamp. The `lockedUntil` timestamp can be
/// increased while a split is locked. If `lockedUntil` is zero, this split can be changed at any time.
/// @custom:member hook A contract which will receive this split's tokens and properties, and can define custom
/// behavior.
struct JBSplit {
    uint32 percent;
    uint64 projectId;
    address payable beneficiary;
    bool preferAddToBalance;
    uint48 lockedUntil;
    IJBSplitHook hook;
}

File 62 of 139 : JBSplitGroup.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBSplit} from "./JBSplit.sol";

/// @custom:member groupId An identifier for the group. By convention, this ID is `uint256(uint160(tokenAddress))` for
/// payouts and `1` for reserved tokens.
/// @custom:member splits The splits in the group.
struct JBSplitGroup {
    uint256 groupId;
    JBSplit[] splits;
}

File 63 of 139 : JBSplitHookContext.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBSplit} from "./JBSplit.sol";

/// @custom:member token The token being sent to the split hook.
/// @custom:member amount The amount being sent to the split hook, as a fixed point number.
/// @custom:member decimals The number of decimals in the amount.
/// @custom:member projectId The project the split belongs to.
/// @custom:member groupId The group the split belongs to. By convention, this ID is `uint256(uint160(tokenAddress))`
/// for payouts and `1` for reserved tokens.
/// @custom:member split The split which specified the hook.
struct JBSplitHookContext {
    address token;
    uint256 amount;
    uint256 decimals;
    uint256 projectId;
    uint256 groupId;
    JBSplit split;
}

File 64 of 139 : JBTerminalConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBAccountingContext} from "./JBAccountingContext.sol";
import {IJBTerminal} from "./../interfaces/IJBTerminal.sol";

/// @custom:member terminal The terminal to configure.
/// @custom:member accountingContextsToAccept The accounting contexts to accept from the terminal.
struct JBTerminalConfig {
    IJBTerminal terminal;
    JBAccountingContext[] accountingContextsToAccept;
}

File 65 of 139 : JBTokenAmount.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member token The token the payment was made in.
/// @custom:member decimals The number of decimals included in the value fixed point number.
/// @custom:member currency The currency. By convention, this is `uint32(uint160(tokenAddress))` for tokens, or a
/// constant ID from e.g. `JBCurrencyIds` for other currencies.
/// @custom:member value The amount of tokens that was paid, as a fixed point number.
struct JBTokenAmount {
    address token;
    uint8 decimals;
    uint32 currency;
    uint256 value;
}

// SPDX-License-Identifier: MIT
// Juicebox variation on OpenZeppelin Ownable
pragma solidity ^0.8.23;

import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {IJBPermissions} from "@bananapus/core/src/interfaces/IJBPermissions.sol";

import {JBOwnableOverrides} from "./JBOwnableOverrides.sol";

/// @notice A function restricted by `JBOwnable` can only be called by a Juicebox project's owner, a specified owner
/// address (if set), or addresses with permission from the owner.
/// @dev A function with the `onlyOwner` modifier from `JBOwnable` can only be called by addresses with owner access
/// based on a `JBOwner` struct:
/// 1. If `JBOwner.projectId` isn't zero, the address holding the `JBProjects` NFT with the `JBOwner.projectId` ID is
/// the owner.
/// 2. If `JBOwner.projectId` is set to `0`, the `JBOwner.owner` address is the owner.
/// 3. The owner can give other addresses access with `JBPermissions.setPermissionsFor(...)`, using the
/// `JBOwner.permissionId` permission.
/// @dev To use `onlyOwner`, inherit this contract and apply the modifier to a function.
contract JBOwnable is JBOwnableOverrides {
    //*********************************************************************//
    // -------------------------- constructor ---------------------------- //
    //*********************************************************************//

    /// @dev To make a Juicebox project's owner this contract's owner, pass that project's ID as the
    /// `initialProjectIdOwner`.
    /// @dev To make a specific address the owner, pass that address as the `initialOwner` and `0` as the
    /// `initialProjectIdOwner`.
    /// @dev The owner can give other addresses owner access through the `permissions` contract.
    /// @param permissions A contract storing permissions.
    /// @param projects Mints ERC-721s that represent project ownership and transfers.
    /// @param initialOwner An address with owner access (until ownership is transferred).
    /// @param initialProjectIdOwner The ID of the Juicebox project whose owner has owner access (until ownership is
    /// transferred).
    constructor(
        IJBPermissions permissions,
        IJBProjects projects,
        address initialOwner,
        uint88 initialProjectIdOwner
    )
        JBOwnableOverrides(permissions, projects, initialOwner, initialProjectIdOwner)
    {}

    //*********************************************************************//
    // --------------------------- modifiers ----------------------------- //
    //*********************************************************************//

    /// @notice Reverts if called by an address without owner access.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }

    //*********************************************************************//
    // ------------------------ internal functions ----------------------- //
    //*********************************************************************//

    /// @notice Either `newOwner` or `newProjectId` is non-zero or both are zero. But they can never both be non-zero.
    /// @dev This function exists because some contracts will try to deploy contracts for a project before
    function _emitTransferEvent(
        address previousOwner,
        address newOwner,
        uint88 newProjectId
    )
        internal
        virtual
        override
    {
        emit OwnershipTransferred({
            previousOwner: previousOwner,
            newOwner: newProjectId == 0 ? newOwner : PROJECTS.ownerOf(newProjectId),
            caller: msg.sender
        });
    }
}

// SPDX-License-Identifier: MIT
// Juicebox variation on OpenZeppelin Ownable
pragma solidity ^0.8.23;

import {JBPermissioned} from "@bananapus/core/src/abstract/JBPermissioned.sol";
import {IJBPermissions} from "@bananapus/core/src/interfaces/IJBPermissions.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

import {IJBOwnable} from "./interfaces/IJBOwnable.sol";
import {JBOwner} from "./structs/JBOwner.sol";

/// @notice An abstract base for `JBOwnable`, which restricts functions so they can only be called by a Juicebox
/// project's owner or a specific owner address. The owner can give access permission to other addresses with
/// `JBPermissions`.
abstract contract JBOwnableOverrides is Context, JBPermissioned, IJBOwnable {
    //*********************************************************************//
    // --------------------------- custom errors --------------------------//
    //*********************************************************************//b

    error JBOwnableOverrides_InvalidNewOwner();

    //*********************************************************************//
    // ---------------- public immutable stored properties --------------- //
    //*********************************************************************//

    /// @notice Mints ERC-721s that represent project ownership and transfers.
    IJBProjects public immutable override PROJECTS;

    //*********************************************************************//
    // --------------------- public stored properties -------------------- //
    //*********************************************************************//

    /// @notice This contract's owner information.
    JBOwner public override jbOwner;

    //*********************************************************************//
    // -------------------------- constructor ---------------------------- //
    //*********************************************************************//

    /// @dev To restrict access to a Juicebox project's owner, pass that project's ID as the `initialProjectIdOwner` and
    /// the zero address as the `initialOwner`.
    /// To restrict access to a specific address, pass that address as the `initialOwner` and `0` as the
    /// `initialProjectIdOwner`.
    /// @dev The owner can give owner access to other addresses through the `permissions` contract.
    /// @param permissions A contract storing permissions.
    /// @param projects Mints ERC-721s that represent project ownership and transfers.
    /// @param initialOwner The owner if the `intialProjectIdOwner` is 0 (until ownership is transferred).
    /// @param initialProjectIdOwner The ID of the Juicebox project whose owner is this contract's owner (until
    /// ownership is transferred).
    constructor(
        IJBPermissions permissions,
        IJBProjects projects,
        address initialOwner,
        uint88 initialProjectIdOwner
    )
        JBPermissioned(permissions)
    {
        PROJECTS = projects;

        // We force the inheriting contract to set an owner, as there is a low chance someone will use `JBOwnable` to
        // create an unowned contract.
        // It's more likely both were accidentally set to `0`. If you really want an unowned contract, set the owner to
        // an address and call `renounceOwnership()` in the constructor body.
        if (initialProjectIdOwner == 0 && initialOwner == address(0)) {
            revert JBOwnableOverrides_InvalidNewOwner();
        }

        _transferOwnership(initialOwner, initialProjectIdOwner);
    }

    //*********************************************************************//
    // -------------------------- public views --------------------------- //
    //*********************************************************************//

    /// @notice Returns the owner's address based on this contract's `JBOwner`.
    function owner() public view virtual returns (address) {
        JBOwner memory ownerInfo = jbOwner;

        if (ownerInfo.projectId == 0) {
            return ownerInfo.owner;
        }

        return PROJECTS.ownerOf(ownerInfo.projectId);
    }

    //*********************************************************************//
    // -------------------------- internal views ------------------------- //
    //*********************************************************************//

    /// @notice Reverts if the sender is not the owner.
    function _checkOwner() internal view virtual {
        JBOwner memory ownerInfo = jbOwner;

        _requirePermissionFrom({
            account: ownerInfo.projectId == 0 ? ownerInfo.owner : PROJECTS.ownerOf(ownerInfo.projectId),
            projectId: ownerInfo.projectId,
            permissionId: ownerInfo.permissionId
        });
    }

    //*********************************************************************//
    // ---------------------- public transactions ------------------------ //
    //*********************************************************************//

    /// @notice Gives up ownership of this contract, making it impossible to call `onlyOwner` and `_checkOwner`
    /// functions.
    /// @notice This can only be called by the current owner.
    function renounceOwnership() public virtual override {
        _checkOwner();
        _transferOwnership(address(0), 0);
    }

    /// @notice Sets the permission ID the owner can use to give other addresses owner access.
    /// @notice This can only be called by the current owner.
    /// @param permissionId The permission ID to use for `onlyOwner`.
    function setPermissionId(uint8 permissionId) public virtual override {
        _checkOwner();
        _setPermissionId(permissionId);
    }

    /// @notice Transfers ownership of this contract to a new address (the `newOwner`). Can only be called by the
    /// current owner.
    /// @notice This can only be called by the current owner.
    /// @param newOwner The address to transfer ownership to.
    function transferOwnership(address newOwner) public virtual override {
        _checkOwner();
        if (newOwner == address(0)) {
            revert JBOwnableOverrides_InvalidNewOwner();
        }

        _transferOwnership(newOwner, 0);
    }

    /// @notice Transfer ownership of this contract to a new Juicebox project.
    /// @notice This can only be called by the current owner.
    /// @dev The `projectId` must fit within a `uint88`.
    /// @param projectId The ID of the project to transfer ownership to.
    function transferOwnershipToProject(uint256 projectId) public virtual override {
        _checkOwner();
        if (projectId == 0 || projectId > type(uint88).max) {
            revert JBOwnableOverrides_InvalidNewOwner();
        }

        _transferOwnership(address(0), uint88(projectId));
    }

    //*********************************************************************//
    // ------------------------ internal functions ----------------------- //
    //*********************************************************************//

    /// @notice Either `newOwner` or `newProjectId` is non-zero or both are zero. But they can never both be non-zero.
    /// @dev This function exists because some contracts will try to deploy contracts for a project before
    function _emitTransferEvent(address previousOwner, address newOwner, uint88 newProjectId) internal virtual;

    /// @notice Sets the permission ID the owner can use to give other addresses owner access.
    /// @dev Internal function without access restriction.
    /// @param permissionId The permission ID to use for `onlyOwner`.
    function _setPermissionId(uint8 permissionId) internal virtual {
        jbOwner.permissionId = permissionId;
        emit PermissionIdChanged({newId: permissionId, caller: msg.sender});
    }

    /// @notice Helper to allow for drop-in replacement of OpenZeppelin `Ownable`.
    /// @param newOwner The address that should receive ownership of this contract.
    function _transferOwnership(address newOwner) internal virtual {
        _transferOwnership(newOwner, 0);
    }

    /// @notice Transfers this contract's ownership to an address (`newOwner`) OR a Juicebox project (`projectId`).
    /// @dev Updates this contract's `JBOwner` owner information and resets the `JBOwner.permissionId`.
    /// @dev If both `newOwner` and `projectId` are set, this will revert.
    /// @dev Internal function without access restriction.
    /// @param newOwner The address that should become this contract's owner.
    /// @param projectId The ID of the project whose owner should become this contract's owner.
    function _transferOwnership(address newOwner, uint88 projectId) internal virtual {
        // Can't set both a new owner and a new project ID.
        if (projectId != 0 && newOwner != address(0)) {
            revert JBOwnableOverrides_InvalidNewOwner();
        }
        // Load the owner information from storage.
        JBOwner memory ownerInfo = jbOwner;
        // Get the address of the old owner.
        address oldOwner = ownerInfo.projectId == 0 ? ownerInfo.owner : PROJECTS.ownerOf(ownerInfo.projectId);
        // Update the stored owner information to the new owner and reset the `permissionId`.
        // This is to prevent permissions clashes for the new user/owner.
        jbOwner = JBOwner({owner: newOwner, projectId: projectId, permissionId: 0});
        // Emit a transfer event with the new owner's address.
        _emitTransferEvent(oldOwner, newOwner, projectId);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";

interface IJBOwnable {
    event PermissionIdChanged(uint8 newId, address caller);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner, address caller);

    function PROJECTS() external view returns (IJBProjects);
    function jbOwner() external view returns (address owner, uint88 projectOwner, uint8 permissionId);
    function owner() external view returns (address);

    function renounceOwnership() external;
    function setPermissionId(uint8 permissionId) external;
    function transferOwnership(address newOwner) external;
    function transferOwnershipToProject(uint256 projectId) external;
}

File 69 of 139 : JBOwner.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Owner information for a given instance of `JBOwnableOverrides`.
/// @custom:member owner If `projectId` is 0, this address has owner access.
/// @custom:member projectId The owner of the `JBProjects` ERC-721 with this ID has owner access. If this is 0, the
/// `owner` address has owner access.
/// @custom:member permissionId The permission ID which corresponds to owner access. See `JBPermissions` in `nana-core`
/// and `nana-permission-ids`.
struct JBOwner {
    address owner;
    uint88 projectId;
    uint8 permissionId;
}

File 70 of 139 : JBPermissionIds.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Permission IDs for `JBPermissions`, used throughout the Bananapus ecosystem. See
/// [`JBPermissions`](https://github.com/Bananapus/nana-core/blob/main/src/JBPermissions.sol)
/// @dev `JBPermissions` allows one address to grant another address permission to call functions in Juicebox contracts
/// on their behalf. Each ID in `JBPermissionIds` grants access to a specific set of these functions.
library JBPermissionIds {
    uint8 internal constant ROOT = 1; // All permissions across every contract. Very dangerous. BE CAREFUL!

    /* Used by `nana-core`: https://github.com/Bananapus/nana-core */
    uint8 internal constant QUEUE_RULESETS = 2; // Permission to call `JBController.queueRulesetsOf` and
        // `JBController.launchRulesetsFor`.
    uint8 internal constant CASH_OUT_TOKENS = 3; // Permission to call `JBMultiTerminal.cashOutTokensOf`.
    uint8 internal constant SEND_PAYOUTS = 4; // Permission to call `JBMultiTerminal.sendPayoutsOf`.
    uint8 internal constant MIGRATE_TERMINAL = 5; // Permission to call `JBMultiTerminal.migrateBalanceOf`.
    uint8 internal constant SET_PROJECT_URI = 6; // Permission to call `JBController.setUriOf`.
    uint8 internal constant DEPLOY_ERC20 = 7; // Permission to call `JBController.deployERC20For`.
    uint8 internal constant SET_TOKEN = 8; // Permission to call `JBController.setTokenFor`.
    uint8 internal constant MINT_TOKENS = 9; // Permission to call `JBController.mintTokensOf`.
    uint8 internal constant BURN_TOKENS = 10; // Permission to call `JBController.burnTokensOf`.
    uint8 internal constant CLAIM_TOKENS = 11; // Permission to call `JBController.claimTokensFor`.
    uint8 internal constant TRANSFER_CREDITS = 12; // Permission to call `JBController.transferCreditsFrom`.
    uint8 internal constant SET_CONTROLLER = 13; // Permission to call `JBDirectory.setControllerOf`.
    uint8 internal constant SET_TERMINALS = 14; // Permission to call `JBDirectory.setTerminalsOf`.
    // Be careful - `SET_TERMINALS` can be used to remove the primary terminal.
    uint8 internal constant SET_PRIMARY_TERMINAL = 15; // Permission to call `JBDirectory.setPrimaryTerminalOf`.
    uint8 internal constant USE_ALLOWANCE = 16; // Permission to call `JBMultiTerminal.useAllowanceOf`.
    uint8 internal constant SET_SPLIT_GROUPS = 17; // Permission to call `JBController.setSplitGroupsOf`.
    uint8 internal constant ADD_PRICE_FEED = 18; // Permission to call `JBPrices.addPriceFeedFor`.
    uint8 internal constant ADD_ACCOUNTING_CONTEXTS = 19; // Permission to call
        // `JBMultiTerminal.addAccountingContextsFor`.

    /* Used by `nana-721-hook`: https://github.com/Bananapus/nana-721-hook */
    uint8 internal constant ADJUST_721_TIERS = 20; // Permission to call `JB721TiersHook.adjustTiers`.
    uint8 internal constant SET_721_METADATA = 21; // Permission to call `JB721TiersHook.setMetadata`.
    uint8 internal constant MINT_721 = 22; // Permission to call `JB721TiersHook.mintFor`.
    uint8 internal constant SET_721_DISCOUNT_PERCENT = 23; // Permission to call `JB721TiersHook.setDiscountPercentOf`.

    /* Used by `nana-buyback-hook`: https://github.com/Bananapus/nana-buyback-hook */
    uint8 internal constant SET_BUYBACK_TWAP = 24; // Permission to call `JBBuybackHook.setTwapWindowOf` and
        // `JBBuybackHook.setTwapSlippageToleranceOf`.
    uint8 internal constant SET_BUYBACK_POOL = 25; // Permission to call `JBBuybackHook.setPoolFor`.

    /* Used by `nana-swap-terminal`: https://github.com/Bananapus/nana-swap-terminal */
    uint8 internal constant ADD_SWAP_TERMINAL_POOL = 26; // Permission to call `JBSwapTerminal.addDefaultPool`.
    uint8 internal constant ADD_SWAP_TERMINAL_TWAP_PARAMS = 27; // Permission to call
        // `JBSwapTerminal.addTwapParamsFor`.

    /* Used by `nana-suckers`: https://github.com/Bananapus/nana-suckers */
    uint8 internal constant MAP_SUCKER_TOKEN = 28; // Permission to call `BPSucker.mapToken`.
    uint8 internal constant DEPLOY_SUCKERS = 29; // Permission to call `BPSuckerRegistry.deploySuckersFor`.
    uint8 internal constant SUCKER_SAFETY = 30; // Permission to call `BPSucker.enableEmergencyHatchFor` and
        // `BPSucker.setDeprecation`.
}

File 71 of 139 : JBAddToBalanceMode.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Options for how a `JBSucker`'s `amountToAddToBalance` gets added to its project's balance.
/// @custom:element MANUAL The amount gets added to the project's balance manually by calling
/// `addOutstandingAmountToBalance`.
/// @custom:element ON_CLAIM The amount gets added to the project's balance automatically when `claim` is called.
enum JBAddToBalanceMode {
    MANUAL,
    ON_CLAIM
}

File 72 of 139 : JBSuckerState.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Options for the deprecation state of a `JBSucker`.
/// @custom:member ENABLED The `JBSucker` is not deprecated.
/// @custom:member DEPRECATION_PENDING The `JBSucker` has a deprecation set, but it is still fully functional.
/// @custom:member SENDING_DISABLED The `JBSucker` is deprecated and sending to the pair sucker is disabled.
/// @custom:member DEPRECATED The `JBSucker` is deprecated, but it continues to let users claim their funds.
enum JBSuckerState {
    ENABLED,
    DEPRECATION_PENDING,
    SENDING_DISABLED,
    DEPRECATED
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core/src/interfaces/IJBTokens.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {JBAddToBalanceMode} from "../enums/JBAddToBalanceMode.sol";
import {JBSuckerState} from "../enums/JBSuckerState.sol";
import {JBClaim} from "../structs/JBClaim.sol";
import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
import {JBOutboxTree} from "../structs/JBOutboxTree.sol";
import {JBRemoteToken} from "../structs/JBRemoteToken.sol";
import {JBTokenMapping} from "../structs/JBTokenMapping.sol";
import {JBMessageRoot} from "../structs/JBMessageRoot.sol";

// @notice The minimal interface for a sucker contract.
interface IJBSucker is IERC165 {
    event Claimed(
        address beneficiary,
        address token,
        uint256 projectTokenCount,
        uint256 terminalTokenAmount,
        uint256 index,
        bool autoAddedToBalance,
        address caller
    );
    event InsertToOutboxTree(
        address indexed beneficiary,
        address indexed token,
        bytes32 hashed,
        uint256 index,
        bytes32 root,
        uint256 projectTokenCount,
        uint256 terminalTokenAmount,
        address caller
    );
    event NewInboxTreeRoot(address indexed token, uint64 nonce, bytes32 root, address caller);
    event RootToRemote(bytes32 indexed root, address indexed token, uint256 index, uint64 nonce, address caller);

    function MESSENGER_BASE_GAS_LIMIT() external view returns (uint32);
    function MESSENGER_ERC20_MIN_GAS_LIMIT() external view returns (uint32);

    function ADD_TO_BALANCE_MODE() external view returns (JBAddToBalanceMode);
    function DEPLOYER() external view returns (address);
    function DIRECTORY() external view returns (IJBDirectory);
    function TOKENS() external view returns (IJBTokens);

    function peer() external view returns (address);
    function projectId() external view returns (uint256);

    function amountToAddToBalanceOf(address token) external view returns (uint256 amount);
    function inboxOf(address token) external view returns (JBInboxTreeRoot memory);
    function isMapped(address token) external view returns (bool);
    function outboxOf(address token) external view returns (JBOutboxTree memory);
    function peerChainId() external view returns (uint256 chainId);
    function remoteTokenFor(address token) external view returns (JBRemoteToken memory);
    function state() external view returns (JBSuckerState);

    function addOutstandingAmountToBalance(address token) external;
    function claim(JBClaim[] calldata claims) external;
    function claim(JBClaim calldata claimData) external;
    function mapToken(JBTokenMapping calldata map) external payable;
    function mapTokens(JBTokenMapping[] calldata maps) external payable;
    function prepare(
        uint256 projectTokenAmount,
        address beneficiary,
        uint256 minTokensReclaimed,
        address token
    )
        external;
    function toRemote(address token) external payable;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core/src/interfaces/IJBTokens.sol";

import {IJBSucker} from "./IJBSucker.sol";

interface IJBSuckerDeployer {
    error JBSuckerDeployer_AlreadyConfigured();
    error JBSuckerDeployer_DeployerIsNotConfigured();
    error JBSuckerDeployer_InvalidLayerSpecificConfiguration();
    error JBSuckerDeployer_LayerSpecificNotConfigured();
    error JBSuckerDeployer_Unauthorized(address caller, address expected);
    error JBSuckerDeployer_ZeroConfiguratorAddress();

    function DIRECTORY() external view returns (IJBDirectory);
    function TOKENS() external view returns (IJBTokens);
    function LAYER_SPECIFIC_CONFIGURATOR() external view returns (address);

    function isSucker(address sucker) external view returns (bool);

    function createForSender(uint256 localProjectId, bytes32 salt) external returns (IJBSucker sucker);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {JBSuckerDeployerConfig} from "../structs/JBSuckerDeployerConfig.sol";
import {JBSuckersPair} from "../structs/JBSuckersPair.sol";

interface IJBSuckerRegistry {
    event SuckerDeployedFor(uint256 projectId, address sucker, JBSuckerDeployerConfig configuration, address caller);
    event SuckerDeployerAllowed(address deployer, address caller);
    event SuckerDeployerRemoved(address deployer, address caller);
    event SuckerDeprecated(uint256 projectId, address sucker, address caller);

    function DIRECTORY() external view returns (IJBDirectory);
    function PROJECTS() external view returns (IJBProjects);

    function isSuckerOf(uint256 projectId, address addr) external view returns (bool);
    function suckerDeployerIsAllowed(address deployer) external view returns (bool);
    function suckerPairsOf(uint256 projectId) external view returns (JBSuckersPair[] memory pairs);
    function suckersOf(uint256 projectId) external view returns (address[] memory);

    function allowSuckerDeployer(address deployer) external;
    function allowSuckerDeployers(address[] calldata deployers) external;
    function deploySuckersFor(
        uint256 projectId,
        bytes32 salt,
        JBSuckerDeployerConfig[] memory configurations
    )
        external
        returns (address[] memory suckers);
    function removeDeprecatedSucker(uint256 projectId, address sucker) external;
    function removeSuckerDeployer(address deployer) external;
}

File 76 of 139 : JBClaim.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBLeaf} from "./JBLeaf.sol";

/// @custom:member token The token to claim.
/// @custom:member leaf The leaf to claim from.
/// @custom:member proof The proof to claim with. Must be of length `JBSucker._TREE_DEPTH`.
struct JBClaim {
    address token;
    JBLeaf leaf;
    bytes32[32] proof;
}

File 77 of 139 : JBInboxTreeRoot.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice The root of an inbox tree for a given token in a `JBSucker`.
/// @dev Inbox trees are used to receive from the remote chain to the local chain. Tokens can be `claim`ed from the
/// inbox tree.
/// @custom:member nonce Tracks the nonce of the tree. The nonce cannot decrease.
/// @custom:member root The root of the tree.
struct JBInboxTreeRoot {
    uint64 nonce;
    bytes32 root;
}

File 78 of 139 : JBLeaf.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice A leaf in the inbox or outbox tree of a `JBSucker`. Used to `claim` tokens from the inbox tree.
/// @custom:member index The index of the leaf.
/// @custom:member beneficiary The beneficiary of the leaf.
/// @custom:member projectTokenCount The number of project tokens to claim.
/// @custom:member terminalTokenAmount The amount of terminal tokens to claim.
struct JBLeaf {
    uint256 index;
    address beneficiary;
    uint256 projectTokenCount;
    uint256 terminalTokenAmount;
}

File 79 of 139 : JBMessageRoot.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBInboxTreeRoot} from "./JBInboxTreeRoot.sol";

/// @notice Information about the remote (inbox) tree's root, passed in a message from the remote chain.
/// @custom:member token The address of the terminal token that the tree tracks.
/// @custom:member amount The amount of tokens being sent.
/// @custom:member remoteRoot The root of the merkle tree.
struct JBMessageRoot {
    address token;
    uint256 amount;
    JBInboxTreeRoot remoteRoot;
}

File 80 of 139 : JBOutboxTree.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {MerkleLib} from "../utils/MerkleLib.sol";

/// @notice A merkle tree used to track the outbox for a given token in a `JBSucker`.
/// @dev The outbox is used to send from the local chain to the remote chain.
/// @custom:member nonce The nonce of the outbox.
/// @custom:member balance The balance of the outbox.
/// @custom:member tree The merkle tree.
/// @custom:member numberOfClaimsSent the number of claims that have been sent to the peer. Used to determine which
/// claims have been sent.
struct JBOutboxTree {
    uint64 nonce;
    uint256 balance;
    MerkleLib.Tree tree;
    uint256 numberOfClaimsSent;
}

File 81 of 139 : JBRemoteToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice A struct that represents a token on the remote chain.
/// @dev Invarient: If the `emergencyHatch` is true then the `enabled` is always false.
/// @custom:member enabled Whether the token is enabled.
/// @custom:member emergencyHatchOpened Whether the emergency hatch is opened.
/// @custom:member minGas The minimum gas to use when bridging.
/// @custom:member addr The address of the token on the remote chain.
/// @custom:member minBridgeAmount The minimum amount to bridge.
struct JBRemoteToken {
    bool enabled;
    bool emergencyHatch;
    uint32 minGas;
    address addr;
    uint256 minBridgeAmount;
}

File 82 of 139 : JBSuckerDeployerConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBSuckerDeployer} from "../interfaces/IJBSuckerDeployer.sol";
import {JBTokenMapping} from "./JBTokenMapping.sol";

/// @custom:member deployer The deployer to use.
/// @custom:member mappings The token mappings to use.
struct JBSuckerDeployerConfig {
    IJBSuckerDeployer deployer;
    JBTokenMapping[] mappings;
}

File 83 of 139 : JBSuckersPair.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/// @custom:member local The local address.
/// @custom:member remote The remote address.
/// @custom:member remoteChainId The chain ID of the remote address.
struct JBSuckersPair {
    address local;
    address remote;
    uint256 remoteChainId;
}

File 84 of 139 : JBTokenMapping.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member localToken The local token address.
/// @custom:member minGas The minimum gas amount to bridge.
/// @custom:member remoteToken The remote token address.
/// @custom:member minBridgeAmount The minimum bridge amount.
struct JBTokenMapping {
    address localToken;
    uint32 minGas;
    address remoteToken;
    uint256 minBridgeAmount;
}

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.23;

/**
 * @title MerkleLib
 * @author Illusory Systems Inc.
 * @notice An incremental merkle tree modeled on the eth2 deposit contract.
 *
 */
library MerkleLib {
    // ========== Custom Errors ===========

    error MerkleLib__insert_treeIsFull();

    // ============ Constants =============

    uint256 internal constant TREE_DEPTH = 32;
    uint256 internal constant MAX_LEAVES = 2 ** TREE_DEPTH - 1;

    /**
     * @dev Z_i represent the hash values at different heights for a binary tree with leaf values equal to `0`.
     * (e.g. Z_1 is the keccak256 hash of (0x0, 0x0), Z_2 is the keccak256 hash of (Z_1, Z_1), etc...)
     * Z_0 is the bottom of the 33-layer tree, Z_32 is the top (i.e. root).
     * Used to shortcut calculation in root calculation methods below.
     */
    bytes32 internal constant Z_0 = hex"0000000000000000000000000000000000000000000000000000000000000000";
    bytes32 internal constant Z_1 = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5";
    bytes32 internal constant Z_2 = hex"b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30";
    bytes32 internal constant Z_3 = hex"21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85";
    bytes32 internal constant Z_4 = hex"e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344";
    bytes32 internal constant Z_5 = hex"0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d";
    bytes32 internal constant Z_6 = hex"887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968";
    bytes32 internal constant Z_7 = hex"ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83";
    bytes32 internal constant Z_8 = hex"9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af";
    bytes32 internal constant Z_9 = hex"cefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0";
    bytes32 internal constant Z_10 = hex"f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5";
    bytes32 internal constant Z_11 = hex"f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892";
    bytes32 internal constant Z_12 = hex"3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c";
    bytes32 internal constant Z_13 = hex"c1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb";
    bytes32 internal constant Z_14 = hex"5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc";
    bytes32 internal constant Z_15 = hex"da7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2";
    bytes32 internal constant Z_16 = hex"2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f";
    bytes32 internal constant Z_17 = hex"e1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a";
    bytes32 internal constant Z_18 = hex"5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0";
    bytes32 internal constant Z_19 = hex"b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0";
    bytes32 internal constant Z_20 = hex"c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2";
    bytes32 internal constant Z_21 = hex"f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9";
    bytes32 internal constant Z_22 = hex"5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377";
    bytes32 internal constant Z_23 = hex"4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652";
    bytes32 internal constant Z_24 = hex"cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef";
    bytes32 internal constant Z_25 = hex"0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d";
    bytes32 internal constant Z_26 = hex"b8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0";
    bytes32 internal constant Z_27 = hex"838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e";
    bytes32 internal constant Z_28 = hex"662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e";
    bytes32 internal constant Z_29 = hex"388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322";
    bytes32 internal constant Z_30 = hex"93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735";
    bytes32 internal constant Z_31 = hex"8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9";
    bytes32 internal constant Z_32 = hex"27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757";

    // ============= Structs ==============

    /**
     * @notice Struct representing incremental merkle tree. Contains current
     * branch and the number of inserted leaves in the tree.
     *
     */
    struct Tree {
        bytes32[TREE_DEPTH] branch;
        uint256 count;
    }

    // ========= Write Methods =========

    /**
     * @notice Inserts a given node (leaf) into merkle tree. Operates on an in-memory tree and
     * returns an updated version of that tree.
     * @dev Reverts if the tree is already full.
     * @param node Element to insert into tree.
     * @return Tree Updated tree.
     *
     */
    function insert(Tree memory tree, bytes32 node) internal pure returns (Tree memory) {
        // Update tree.count to increase the current count by 1 since we'll be including a new node.
        uint256 size = ++tree.count;
        if (size > MAX_LEAVES) revert MerkleLib__insert_treeIsFull();

        // Loop starting at 0, ending when we've finished inserting the node (i.e. hashing it) into
        // the active branch. Each loop we cut size in half, hashing the inserted node up the active
        // branch along the way.
        for (uint256 i; i < TREE_DEPTH;) {
            // Check if the current size is odd; if so, we set this index in the branch to be the node.
            if ((size & 1) == 1) {
                // If i > 0, then this node will be a hash of the original node with every layer up
                // until layer `i`.
                tree.branch[i] = node;
                return tree;
            }
            // If the size is not yet odd, we hash the current index in the tree branch with the node.
            node = keccak256(abi.encodePacked(tree.branch[i], node));
            size >>= 1; // Cut size in half (statement equivalent to: `size /= 2`).

            unchecked {
                ++i;
            }
        }
        // As the loop should always end prematurely with the `return` statement, this code should
        // be unreachable. We revert here just to be safe.
        revert MerkleLib__insert_treeIsFull();
    }

    // ========= Read Methods =========

    /**
     * @notice Calculates and returns tree's current root.
     * @return _current bytes32 root.
     *
     */
    function root(Tree storage tree) internal view returns (bytes32 _current) {
        uint256 _index = tree.count;

        if (_index == 0) {
            return Z_32;
        }

        uint256 i;
        assembly {
            let TREE_SLOT := tree.slot

            for {} true {} {
                for {} true {} {
                    if and(_index, 1) {
                        mstore(0, sload(TREE_SLOT))
                        mstore(0x20, Z_0)
                        _current := keccak256(0, 0x40)
                        break
                    }

                    if and(_index, shl(1, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 1)))
                        mstore(0x20, Z_1)
                        _current := keccak256(0, 0x40)
                        i := 1
                        break
                    }

                    if and(_index, shl(2, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 2)))
                        mstore(0x20, Z_2)
                        _current := keccak256(0, 0x40)
                        i := 2
                        break
                    }

                    if and(_index, shl(3, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 3)))
                        mstore(0x20, Z_3)
                        _current := keccak256(0, 0x40)
                        i := 3
                        break
                    }

                    if and(_index, shl(4, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 4)))
                        mstore(0x20, Z_4)
                        _current := keccak256(0, 0x40)
                        i := 4
                        break
                    }

                    if and(_index, shl(5, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 5)))
                        mstore(0x20, Z_5)
                        _current := keccak256(0, 0x40)
                        i := 5
                        break
                    }

                    if and(_index, shl(6, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 6)))
                        mstore(0x20, Z_6)
                        _current := keccak256(0, 0x40)
                        i := 6
                        break
                    }

                    if and(_index, shl(7, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 7)))
                        mstore(0x20, Z_7)
                        _current := keccak256(0, 0x40)
                        i := 7
                        break
                    }

                    if and(_index, shl(8, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 8)))
                        mstore(0x20, Z_8)
                        _current := keccak256(0, 0x40)
                        i := 8
                        break
                    }

                    if and(_index, shl(9, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 9)))
                        mstore(0x20, Z_9)
                        _current := keccak256(0, 0x40)
                        i := 9
                        break
                    }

                    if and(_index, shl(10, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 10)))
                        mstore(0x20, Z_10)
                        _current := keccak256(0, 0x40)
                        i := 10
                        break
                    }

                    if and(_index, shl(11, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 11)))
                        mstore(0x20, Z_11)
                        _current := keccak256(0, 0x40)
                        i := 11
                        break
                    }

                    if and(_index, shl(12, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 12)))
                        mstore(0x20, Z_12)
                        _current := keccak256(0, 0x40)
                        i := 12
                        break
                    }

                    if and(_index, shl(13, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 13)))
                        mstore(0x20, Z_13)
                        _current := keccak256(0, 0x40)
                        i := 13
                        break
                    }

                    if and(_index, shl(14, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 14)))
                        mstore(0x20, Z_14)
                        _current := keccak256(0, 0x40)
                        i := 14
                        break
                    }

                    if and(_index, shl(15, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 15)))
                        mstore(0x20, Z_15)
                        _current := keccak256(0, 0x40)
                        i := 15
                        break
                    }

                    if and(_index, shl(16, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 16)))
                        mstore(0x20, Z_16)
                        _current := keccak256(0, 0x40)
                        i := 16
                        break
                    }

                    if and(_index, shl(17, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 17)))
                        mstore(0x20, Z_17)
                        _current := keccak256(0, 0x40)
                        i := 17
                        break
                    }

                    if and(_index, shl(18, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 18)))
                        mstore(0x20, Z_18)
                        _current := keccak256(0, 0x40)
                        i := 18
                        break
                    }

                    if and(_index, shl(19, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 19)))
                        mstore(0x20, Z_19)
                        _current := keccak256(0, 0x40)
                        i := 19
                        break
                    }

                    if and(_index, shl(20, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 20)))
                        mstore(0x20, Z_20)
                        _current := keccak256(0, 0x40)
                        i := 20
                        break
                    }

                    if and(_index, shl(21, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 21)))
                        mstore(0x20, Z_21)
                        _current := keccak256(0, 0x40)
                        i := 21
                        break
                    }

                    if and(_index, shl(22, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 22)))
                        mstore(0x20, Z_22)
                        _current := keccak256(0, 0x40)
                        i := 22
                        break
                    }

                    if and(_index, shl(23, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 23)))
                        mstore(0x20, Z_23)
                        _current := keccak256(0, 0x40)
                        i := 23
                        break
                    }

                    if and(_index, shl(24, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 24)))
                        mstore(0x20, Z_24)
                        _current := keccak256(0, 0x40)
                        i := 24
                        break
                    }

                    if and(_index, shl(25, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 25)))
                        mstore(0x20, Z_25)
                        _current := keccak256(0, 0x40)
                        i := 25
                        break
                    }

                    if and(_index, shl(26, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 26)))
                        mstore(0x20, Z_26)
                        _current := keccak256(0, 0x40)
                        i := 26
                        break
                    }

                    if and(_index, shl(27, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 27)))
                        mstore(0x20, Z_27)
                        _current := keccak256(0, 0x40)
                        i := 27
                        break
                    }

                    if and(_index, shl(28, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 28)))
                        mstore(0x20, Z_28)
                        _current := keccak256(0, 0x40)
                        i := 28
                        break
                    }

                    if and(_index, shl(29, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 29)))
                        mstore(0x20, Z_29)
                        _current := keccak256(0, 0x40)
                        i := 29
                        break
                    }

                    if and(_index, shl(30, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 30)))
                        mstore(0x20, Z_30)
                        _current := keccak256(0, 0x40)
                        i := 30
                        break
                    }

                    if and(_index, shl(31, 1)) {
                        mstore(0, sload(add(TREE_SLOT, 31)))
                        mstore(0x20, Z_31)
                        _current := keccak256(0, 0x40)
                        // slither-disable-next-line write-after-write
                        i := 31
                        break
                    }

                    // slither-disable-next-line write-after-write
                    _current := Z_32
                    i := 32
                    break
                }

                if gt(i, 30) { break }

                {
                    if lt(i, 1) {
                        switch and(_index, shl(1, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_1)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 1)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 2) {
                        switch and(_index, shl(2, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_2)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 2)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 3) {
                        switch and(_index, shl(3, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_3)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 3)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 4) {
                        switch and(_index, shl(4, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_4)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 4)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 5) {
                        switch and(_index, shl(5, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_5)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 5)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 6) {
                        switch and(_index, shl(6, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_6)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 6)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 7) {
                        switch and(_index, shl(7, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_7)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 7)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 8) {
                        switch and(_index, shl(8, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_8)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 8)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 9) {
                        switch and(_index, shl(9, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_9)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 9)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 10) {
                        switch and(_index, shl(10, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_10)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 10)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 11) {
                        switch and(_index, shl(11, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_11)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 11)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 12) {
                        switch and(_index, shl(12, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_12)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 12)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 13) {
                        switch and(_index, shl(13, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_13)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 13)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 14) {
                        switch and(_index, shl(14, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_14)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 14)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 15) {
                        switch and(_index, shl(15, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_15)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 15)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 16) {
                        switch and(_index, shl(16, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_16)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 16)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 17) {
                        switch and(_index, shl(17, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_17)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 17)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 18) {
                        switch and(_index, shl(18, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_18)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 18)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 19) {
                        switch and(_index, shl(19, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_19)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 19)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 20) {
                        switch and(_index, shl(20, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_20)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 20)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 21) {
                        switch and(_index, shl(21, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_21)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 21)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 22) {
                        switch and(_index, shl(22, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_22)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 22)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 23) {
                        switch and(_index, shl(23, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_23)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 23)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 24) {
                        switch and(_index, shl(24, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_24)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 24)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 25) {
                        switch and(_index, shl(25, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_25)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 25)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 26) {
                        switch and(_index, shl(26, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_26)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 26)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 27) {
                        switch and(_index, shl(27, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_27)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 27)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 28) {
                        switch and(_index, shl(28, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_28)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 28)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 29) {
                        switch and(_index, shl(29, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_29)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 29)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 30) {
                        switch and(_index, shl(30, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_30)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 30)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }

                    if lt(i, 31) {
                        switch and(_index, shl(31, 1))
                        case 0 {
                            mstore(0, _current)
                            mstore(0x20, Z_31)
                        }
                        default {
                            mstore(0, sload(add(TREE_SLOT, 31)))
                            mstore(0x20, _current)
                        }

                        _current := keccak256(0, 0x40)
                    }
                }

                break
            }
        }
    }

    /**
     * @notice Calculates and returns the merkle root for the given leaf `_item`,
     * a merkle branch, and the index of `_item` in the tree.
     * @param _item Merkle leaf
     * @param _branch Merkle proof
     * @param _index Index of `_item` in tree
     * @return _current Calculated merkle root
     *
     */
    function branchRoot(
        bytes32 _item,
        bytes32[TREE_DEPTH] memory _branch,
        uint256 _index
    )
        internal
        pure
        returns (bytes32 _current)
    {
        assembly {
            _current := _item
            let BRANCH_DATA_OFFSET := _branch
            let f

            f := shl(5, and(_index, 1))
            mstore(f, _current)
            mstore(sub(0x20, f), mload(BRANCH_DATA_OFFSET))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(1, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 1))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(2, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 2))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(3, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 3))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(4, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 4))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(5, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 5))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(6, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 6))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(7, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 7))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(8, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 8))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(9, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 9))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(10, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 10))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(11, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 11))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(12, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 12))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(13, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 13))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(14, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 14))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(15, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 15))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(16, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 16))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(17, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 17))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(18, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 18))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(19, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 19))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(20, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 20))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(21, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 21))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(22, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 22))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(23, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 23))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(24, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 24))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(25, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 25))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(26, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 26))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(27, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 27))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(28, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 28))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(29, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 29))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(30, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 30))))
            _current := keccak256(0, 0x40)

            f := shl(5, iszero(and(_index, shl(31, 1))))
            mstore(sub(0x20, f), _current)
            mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 31))))
            _current := keccak256(0, 0x40)
        }
    }
}

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

import {IJB721TiersHook} from "@bananapus/721-hook/src/interfaces/IJB721TiersHook.sol";
import {JB721Tier} from "@bananapus/721-hook/src/structs/JB721Tier.sol";
import {JB721TierConfig} from "@bananapus/721-hook/src/structs/JB721TierConfig.sol";
import {JBPermissioned} from "@bananapus/core/src/abstract/JBPermissioned.sol";
import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBPermissions} from "@bananapus/core/src/interfaces/IJBPermissions.sol";
import {IJBTerminal} from "@bananapus/core/src/interfaces/IJBTerminal.sol";
import {JBConstants} from "@bananapus/core/src/libraries/JBConstants.sol";
import {JBMetadataResolver} from "@bananapus/core/src/libraries/JBMetadataResolver.sol";
import {JBOwnable} from "@bananapus/ownable/src/JBOwnable.sol";
import {JBPermissionIds} from "@bananapus/permission-ids/src/JBPermissionIds.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

import {ICTPublisher} from "./interfaces/ICTPublisher.sol";
import {CTAllowedPost} from "./structs/CTAllowedPost.sol";
import {CTPost} from "./structs/CTPost.sol";

/// @notice A contract that facilitates the permissioned publishing of NFT posts to a Juicebox project.
contract CTPublisher is JBPermissioned, ERC2771Context, ICTPublisher {
    //*********************************************************************//
    // --------------------------- custom errors ------------------------- //
    //*********************************************************************//

    error CTPublisher_EmptyEncodedIPFSUri();
    error CTPublisher_InsufficientEthSent(uint256 expected, uint256 sent);
    error CTPublisher_MaxTotalSupplyLessThanMin(uint256 min, uint256 max);
    error CTPublisher_NotInAllowList(address addr, address[] allowedAddresses);
    error CTPublisher_PriceTooSmall(uint256 price, uint256 minimumPrice);
    error CTPublisher_TotalSupplyTooBig(uint256 totalSupply, uint256 maximumTotalSupply);
    error CTPublisher_TotalSupplyTooSmall(uint256 totalSupply, uint256 minimumTotalSupply);
    error CTPublisher_UnauthorizedToPostInCategory();
    error CTPublisher_ZeroTotalSupply();

    //*********************************************************************//
    // ------------------------- public constants ------------------------ //
    //*********************************************************************//

    /// @notice The divisor that describes the fee that should be taken.
    /// @dev This is equal to 100 divided by the fee percent.
    uint256 public constant override FEE_DIVISOR = 20;

    //*********************************************************************//
    // ---------------- public immutable stored properties --------------- //
    //*********************************************************************//

    /// @notice The controller that directs the projects being posted to.
    IJBController public immutable override CONTROLLER;

    /// @notice The ID of the project to which fees will be routed.
    uint256 public immutable override FEE_PROJECT_ID;

    //*********************************************************************//
    // --------------------- public stored properties -------------------- //
    //*********************************************************************//

    /// @notice The ID of the tier that an IPFS metadata has been saved to.
    /// @custom:param hook The hook for which the tier ID applies.
    /// @custom:param encodedIPFSUri The IPFS URI.
    mapping(address hook => mapping(bytes32 encodedIPFSUri => uint256)) public override tierIdForEncodedIPFSUriOf;

    //*********************************************************************//
    // --------------------- internal stored properties ------------------ //
    //*********************************************************************//

    /// @notice Stores addresses that are allowed to post onto a hook category.
    /// @custom:param hook The hook for which this allowance applies.
    /// @custom:param category The category for which the allowance applies.
    /// @custom:param address The address to check an allowance for.
    mapping(address hook => mapping(uint256 category => address[])) internal _allowedAddresses;

    /// @notice Packed values that determine the allowance of posts.
    /// @custom:param hook The hook for which this allowance applies.
    /// @custom:param category The category for which the allowance applies
    mapping(address hook => mapping(uint256 category => uint256)) internal _packedAllowanceFor;

    //*********************************************************************//
    // -------------------------- constructor ---------------------------- //
    //*********************************************************************//

    /// @param controller The controller that directs the projects being posted to.
    /// @param permissions A contract storing permissions.
    /// @param feeProjectId The ID of the project to which fees will be routed.
    /// @param trustedForwarder The trusted forwarder for the ERC2771Context.
    constructor(
        IJBController controller,
        IJBPermissions permissions,
        uint256 feeProjectId,
        address trustedForwarder
    )
        JBPermissioned(permissions)
        ERC2771Context(trustedForwarder)
    {
        CONTROLLER = controller;
        FEE_PROJECT_ID = feeProjectId;
    }

    //*********************************************************************//
    // ------------------------- external views -------------------------- //
    //*********************************************************************//

    /// @notice Get the tiers for the provided encoded IPFS URIs.
    /// @param hook The hook from which to get tiers.
    /// @param encodedIPFSUris The URIs to get tiers of.
    /// @return tiers The tiers that correspond to the provided encoded IPFS URIs. If there's no tier yet, an empty tier
    /// is returned.
    function tiersFor(
        address hook,
        bytes32[] memory encodedIPFSUris
    )
        external
        view
        override
        returns (JB721Tier[] memory tiers)
    {
        uint256 numberOfEncodedIPFSUris = encodedIPFSUris.length;

        // Initialize the tier array being returned.
        tiers = new JB721Tier[](numberOfEncodedIPFSUris);

        // Get the tier for each provided encoded IPFS URI.
        for (uint256 i; i < numberOfEncodedIPFSUris; i++) {
            // Check if there's a tier ID stored for the encoded IPFS URI.
            uint256 tierId = tierIdForEncodedIPFSUriOf[hook][encodedIPFSUris[i]];

            // If there's a tier ID stored, resolve it.
            if (tierId != 0) {
                // slither-disable-next-line calls-loop
                tiers[i] = IJB721TiersHook(hook).STORE().tierOf(hook, tierId, false);
            }
        }
    }

    //*********************************************************************//
    // -------------------------- public views --------------------------- //
    //*********************************************************************//

    /// @notice Post allowances for a particular category on a particular hook.
    /// @param hook The hook contract for which this allowance applies.
    /// @param category The category for which this allowance applies.
    /// @return minimumPrice The minimum price that a poster must pay to record a new NFT.
    /// @return minimumTotalSupply The minimum total number of available tokens that a minter must set to record a new
    /// NFT.
    /// @return maximumTotalSupply The max total supply of NFTs that can be made available when minting. Leave as 0 for
    /// max.
    /// @return allowedAddresses The addresses allowed to post. Returns empty if all addresses are allowed.
    function allowanceFor(
        address hook,
        uint256 category
    )
        public
        view
        override
        returns (
            uint256 minimumPrice,
            uint256 minimumTotalSupply,
            uint256 maximumTotalSupply,
            address[] memory allowedAddresses
        )
    {
        // Get a reference to the packed values.
        uint256 packed = _packedAllowanceFor[hook][category];

        // minimum price in bits 0-103 (104 bits).
        minimumPrice = uint256(uint104(packed));
        // minimum supply in bits 104-135 (32 bits).
        minimumTotalSupply = uint256(uint32(packed >> 104));
        // minimum supply in bits 136-67 (32 bits).
        maximumTotalSupply = uint256(uint32(packed >> 136));

        allowedAddresses = _allowedAddresses[hook][category];
    }

    //*********************************************************************//
    // -------------------------- internal views ------------------------- //
    //*********************************************************************//

    /// @dev ERC-2771 specifies the context as being a single address (20 bytes).
    function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
        return super._contextSuffixLength();
    }

    /// @notice Check if an address is included in an allow list.
    /// @param addrs The candidate address.
    /// @param addresses An array of allowed addresses.
    function _isAllowed(address addrs, address[] memory addresses) internal pure returns (bool) {
        // Keep a reference to the number of address to check against.
        uint256 numberOfAddresses = addresses.length;

        // Check if the address is included
        for (uint256 i; i < numberOfAddresses; i++) {
            if (addrs == addresses[i]) return true;
        }

        return false;
    }

    /// @notice Returns the calldata, prefered to use over `msg.data`
    /// @return calldata the `msg.data` of this call
    function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
        return ERC2771Context._msgData();
    }

    /// @notice Returns the sender, prefered to use over `msg.sender`
    /// @return sender the sender address of this call.
    function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
        return ERC2771Context._msgSender();
    }

    //*********************************************************************//
    // ---------------------- external transactions ---------------------- //
    //*********************************************************************//

    /// @notice Collection owners can set the allowed criteria for publishing a new NFT to their project.
    /// @param allowedPosts An array of criteria for allowed posts.
    function configurePostingCriteriaFor(CTAllowedPost[] memory allowedPosts) external override {
        // Keep a reference to the number of post criteria.
        uint256 numberOfAllowedPosts = allowedPosts.length;

        // For each post criteria, save the specifications.
        for (uint256 i; i < numberOfAllowedPosts; i++) {
            // Set the post criteria being iterated on.
            CTAllowedPost memory allowedPost = allowedPosts[i];

            emit ConfigurePostingCriteria({hook: allowedPost.hook, allowedPost: allowedPost, caller: _msgSender()});

            // Enforce permissions.
            // slither-disable-next-line reentrancy-events,calls-loop
            _requirePermissionFrom({
                account: JBOwnable(allowedPost.hook).owner(),
                projectId: IJB721TiersHook(allowedPost.hook).PROJECT_ID(),
                permissionId: JBPermissionIds.ADJUST_721_TIERS
            });

            // Make sure there is a minimum supply.
            if (allowedPost.minimumTotalSupply == 0) {
                revert CTPublisher_ZeroTotalSupply();
            }

            // Make sure the minimum supply does not surpass the maximum supply.
            if (allowedPost.minimumTotalSupply > allowedPost.maximumTotalSupply) {
                revert CTPublisher_MaxTotalSupplyLessThanMin(
                    allowedPost.minimumTotalSupply, allowedPost.maximumTotalSupply
                );
            }

            uint256 packed;
            // minimum price in bits 0-103 (104 bits).
            packed |= uint256(allowedPost.minimumPrice);
            // minimum total supply in bits 104-135 (32 bits).
            packed |= uint256(allowedPost.minimumTotalSupply) << 104;
            // maximum total supply in bits 136-167 (32 bits).
            packed |= uint256(allowedPost.maximumTotalSupply) << 136;
            // Store the packed value.
            _packedAllowanceFor[allowedPost.hook][allowedPost.category] = packed;

            // Store the allow list.
            uint256 numberOfAddresses = allowedPost.allowedAddresses.length;
            // Reset the addresses.
            delete _allowedAddresses[allowedPost.hook][allowedPost.category];
            // Add the number allowed addresses.
            if (numberOfAddresses != 0) {
                // Keep a reference to the storage of the allowed addresses.
                for (uint256 j = 0; j < numberOfAddresses; j++) {
                    _allowedAddresses[allowedPost.hook][allowedPost.category].push(allowedPost.allowedAddresses[j]);
                }
            }
        }
    }

    /// @notice Publish an NFT to become mintable, and mint a first copy.
    /// @dev A fee is taken into the appropriate treasury.
    /// @param hook The hook to mint from.
    /// @param posts An array of posts that should be published as NFTs to the specified project.
    /// @param nftBeneficiary The beneficiary of the NFT mints.
    /// @param feeBeneficiary The beneficiary of the fee project's token.
    /// @param additionalPayMetadata Metadata bytes that should be included in the pay function's metadata. This
    /// prepends the
    /// payload needed for NFT creation.
    /// @param feeMetadata The metadata to send alongside the fee payment.
    function mintFrom(
        IJB721TiersHook hook,
        CTPost[] calldata posts,
        address nftBeneficiary,
        address feeBeneficiary,
        bytes calldata additionalPayMetadata,
        bytes calldata feeMetadata
    )
        external
        payable
        override
    {
        // Keep a reference to the amount being paid, which is msg.value minus the fee.
        uint256 payValue = msg.value;

        // Keep a reference to the mint metadata.
        bytes memory mintMetadata;

        // Keep a reference to the project's ID.
        uint256 projectId = hook.PROJECT_ID();

        {
            // Setup the posts.
            (JB721TierConfig[] memory tiersToAdd, uint256[] memory tierIdsToMint, uint256 totalPrice) =
                _setupPosts(hook, posts);

            if (projectId != FEE_PROJECT_ID) {
                // Keep a reference to the fee that will be paid.
                payValue -= totalPrice / FEE_DIVISOR;
            }

            // Make sure the amount sent to this function is at least the specified price of the tier plus the fee.
            if (totalPrice > payValue) {
                revert CTPublisher_InsufficientEthSent(totalPrice, msg.value);
            }

            // Add the new tiers.
            // slither-disable-next-line reentrancy-events
            hook.adjustTiers(tiersToAdd, new uint256[](0));

            // Keep a reference to the metadata ID target.
            address metadataIdTarget = hook.METADATA_ID_TARGET();

            // Create the metadata for the payment to specify the tier IDs that should be minted. We create manually the
            // original metadata, following
            // the specifications from the JBMetadataResolver library.
            mintMetadata = JBMetadataResolver.addToMetadata({
                originalMetadata: additionalPayMetadata,
                idToAdd: JBMetadataResolver.getId("pay", metadataIdTarget),
                dataToAdd: abi.encode(true, tierIdsToMint)
            });

            // Store the referal id in the first 32 bytes of the metadata (push to stack for immutable in assembly)
            uint256 feeProjectId = FEE_PROJECT_ID;

            assembly {
                mstore(add(mintMetadata, 32), feeProjectId)
            }
        }

        emit Mint({
            projectId: projectId,
            hook: hook,
            nftBeneficiary: nftBeneficiary,
            feeBeneficiary: feeBeneficiary,
            posts: posts,
            postValue: payValue,
            txValue: msg.value,
            caller: _msgSender()
        });

        {
            // Get a reference to the project's current ETH payment terminal.
            IJBTerminal projectTerminal = CONTROLLER.DIRECTORY().primaryTerminalOf(projectId, JBConstants.NATIVE_TOKEN);

            // Make the payment.
            // slither-disable-next-line unused-return
            projectTerminal.pay{value: payValue}({
                projectId: projectId,
                token: JBConstants.NATIVE_TOKEN,
                amount: payValue,
                beneficiary: nftBeneficiary,
                minReturnedTokens: 0,
                memo: "Minted from Croptop",
                metadata: mintMetadata
            });
        }

        // Pay a fee if there are funds left.
        if (address(this).balance != 0) {
            // Get a reference to the fee project's current ETH payment terminal.
            IJBTerminal feeTerminal = CONTROLLER.DIRECTORY().primaryTerminalOf(FEE_PROJECT_ID, JBConstants.NATIVE_TOKEN);

            // Make the fee payment.
            // slither-disable-next-line unused-return
            feeTerminal.pay{value: address(this).balance}({
                projectId: FEE_PROJECT_ID,
                amount: address(this).balance,
                token: JBConstants.NATIVE_TOKEN,
                beneficiary: feeBeneficiary,
                minReturnedTokens: 0,
                memo: "",
                metadata: feeMetadata
            });
        }
    }

    //*********************************************************************//
    // ------------------------ internal functions ----------------------- //
    //*********************************************************************//

    /// @notice Setup the posts.
    /// @param hook The NFT hook on which the posts will apply.
    /// @param posts An array of posts that should be published as NFTs to the specified project.
    /// @return tiersToAdd The tiers that will be created to represent the posts.
    /// @return tierIdsToMint The tier IDs of the posts that should be minted once published.
    /// @return totalPrice The total price being paid.
    function _setupPosts(
        IJB721TiersHook hook,
        CTPost[] memory posts
    )
        internal
        returns (JB721TierConfig[] memory tiersToAdd, uint256[] memory tierIdsToMint, uint256 totalPrice)
    {
        // Set the max size of the tier data that will be added.
        tiersToAdd = new JB721TierConfig[](posts.length);

        // Set the size of the tier IDs of the posts that should be minted once published.
        tierIdsToMint = new uint256[](posts.length);

        // The tier ID that will be created, and the first one that should be minted from, is one more than the current
        // max.
        uint256 startingTierId = hook.STORE().maxTierIdOf(address(hook)) + 1;

        // Keep a reference to the total number of tiers being added.
        uint256 numberOfTiersBeingAdded;

        // For each post, create tiers after validating to make sure they fulfill the allowance specified by the
        // project's owner.
        for (uint256 i; i < posts.length; i++) {
            // Get the current post being iterated on.
            CTPost memory post = posts[i];

            // Make sure the post includes an encodedIPFSUri.
            if (post.encodedIPFSUri == bytes32("")) {
                revert CTPublisher_EmptyEncodedIPFSUri();
            }

            // Scoped section to prevent stack too deep.
            {
                // Check if there's an ID of a tier already minted for this encodedIPFSUri.
                uint256 tierId = tierIdForEncodedIPFSUriOf[address(hook)][post.encodedIPFSUri];

                if (tierId != 0) tierIdsToMint[i] = tierId;
            }

            // If no tier already exists, post the tier.
            if (tierIdsToMint[i] == 0) {
                // Scoped error handling section to prevent Stack Too Deep.
                {
                    // Get references to the allowance.
                    (
                        uint256 minimumPrice,
                        uint256 minimumTotalSupply,
                        uint256 maximumTotalSupply,
                        address[] memory addresses
                    ) = allowanceFor(address(hook), post.category);

                    // Make sure the category being posted to allows publishing.
                    if (minimumTotalSupply == 0) {
                        revert CTPublisher_UnauthorizedToPostInCategory();
                    }

                    // Make sure the price being paid for the post is at least the allowed minimum price.
                    if (post.price < minimumPrice) {
                        revert CTPublisher_PriceTooSmall(post.price, minimumPrice);
                    }

                    // Make sure the total supply being made available for the post is at least the allowed minimum
                    // total supply.
                    if (post.totalSupply < minimumTotalSupply) {
                        revert CTPublisher_TotalSupplyTooSmall(post.totalSupply, minimumTotalSupply);
                    }

                    // Make sure the total supply being made available for the post is at most the allowed maximum total
                    // supply.
                    if (post.totalSupply > maximumTotalSupply) {
                        revert CTPublisher_TotalSupplyTooBig(post.totalSupply, maximumTotalSupply);
                    }

                    // Make sure the address is allowed to post.
                    if (addresses.length != 0 && !_isAllowed(_msgSender(), addresses)) {
                        revert CTPublisher_NotInAllowList(_msgSender(), addresses);
                    }
                }

                // Set the tier.
                tiersToAdd[numberOfTiersBeingAdded] = JB721TierConfig({
                    price: post.price,
                    initialSupply: post.totalSupply,
                    votingUnits: 0,
                    reserveFrequency: 0,
                    reserveBeneficiary: address(0),
                    encodedIPFSUri: post.encodedIPFSUri,
                    category: post.category,
                    discountPercent: 0,
                    allowOwnerMint: false,
                    useReserveBeneficiaryAsDefault: false,
                    transfersPausable: false,
                    useVotingUnits: true,
                    cannotBeRemoved: false,
                    cannotIncreaseDiscountPercent: false
                });

                // Set the ID of the tier to mint.
                tierIdsToMint[i] = startingTierId + numberOfTiersBeingAdded++;

                // Save the encodedIPFSUri as minted.
                tierIdForEncodedIPFSUriOf[address(hook)][post.encodedIPFSUri] = tierIdsToMint[i];
            }

            // Increment the total price.
            totalPrice += post.price;
        }

        // Resize the array if there's a mismatch in length.
        if (numberOfTiersBeingAdded != posts.length) {
            assembly ("memory-safe") {
                mstore(tiersToAdd, numberOfTiersBeingAdded)
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJB721TiersHook} from "@bananapus/721-hook/src/interfaces/IJB721TiersHook.sol";
import {JB721Tier} from "@bananapus/721-hook/src/structs/JB721Tier.sol";
import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";

import {CTAllowedPost} from "../structs/CTAllowedPost.sol";
import {CTPost} from "../structs/CTPost.sol";

interface ICTPublisher {
    event ConfigurePostingCriteria(address indexed hook, CTAllowedPost allowedPost, address caller);
    event Mint(
        uint256 indexed projectId,
        IJB721TiersHook indexed hook,
        address indexed nftBeneficiary,
        address feeBeneficiary,
        CTPost[] posts,
        uint256 postValue,
        uint256 txValue,
        address caller
    );

    function FEE_DIVISOR() external view returns (uint256);

    function CONTROLLER() external view returns (IJBController);

    function FEE_PROJECT_ID() external view returns (uint256);

    function tierIdForEncodedIPFSUriOf(address hook, bytes32 encodedIPFSUri) external view returns (uint256);

    function allowanceFor(
        address hook,
        uint256 category
    )
        external
        view
        returns (
            uint256 minimumPrice,
            uint256 minimumTotalSupply,
            uint256 maximumTotalSupply,
            address[] memory allowedAddresses
        );

    function tiersFor(
        address hook,
        bytes32[] memory encodedIPFSUris
    )
        external
        view
        returns (JB721Tier[] memory tiers);

    function configurePostingCriteriaFor(CTAllowedPost[] memory allowedPosts) external;

    function mintFrom(
        IJB721TiersHook hook,
        CTPost[] memory posts,
        address nftBeneficiary,
        address feeBeneficiary,
        bytes calldata additionalPayMetadata,
        bytes calldata feeMetadata
    )
        external
        payable;
}

File 88 of 139 : CTAllowedPost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Criteria for allowed posts.
/// @custom:member hook The hook to which this allowance applies.
/// @custom:member category A category that should allow posts.
/// @custom:member minimumPrice The minimum price that a post to the specified category should cost.
/// @custom:member minimumTotalSupply The minimum total supply of NFTs that can be made available when minting.
/// @custom:member maxTotalSupply The max total supply of NFTs that can be made available when minting. Leave as 0 for
/// max.
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
struct CTAllowedPost {
    address hook;
    uint24 category;
    uint104 minimumPrice;
    uint32 minimumTotalSupply;
    uint32 maximumTotalSupply;
    address[] allowedAddresses;
}

File 89 of 139 : CTPost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice A post to be published.
/// @custom:member encodedIPFSUri The encoded IPFS URI of the post that is being published.
/// @custom:member totalSupply The number of NFTs that should be made available, including the 1 that will be minted
/// alongside this transaction.
/// @custom:member price The price being paid for buying the post that is being published.
/// @custom:member category The category that the post should be published in.
struct CTPost {
    bytes32 encodedIPFSUri;
    uint32 totalSupply;
    uint104 price;
    uint24 category;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 92 of 139 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 93 of 139 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Context variant with ERC-2771 support.
 *
 * WARNING: Avoid using this pattern in contracts that rely in a specific calldata length as they'll
 * be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC-2771
 * specification adding the address size in bytes (20) to the calldata size. An example of an unexpected
 * behavior could be an unintended fallback (or another function) invocation while trying to invoke the `receive`
 * function only accessible if `msg.data.length == 0`.
 *
 * WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
 * Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
 * recovery.
 */
abstract contract ERC2771Context is Context {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /**
     * @dev Initializes the contract with a trusted forwarder, which will be able to
     * invoke functions on this contract on behalf of other accounts.
     *
     * NOTE: The trusted forwarder can be replaced by overriding {trustedForwarder}.
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder_) {
        _trustedForwarder = trustedForwarder_;
    }

    /**
     * @dev Returns the address of the trusted forwarder.
     */
    function trustedForwarder() public view virtual returns (address) {
        return _trustedForwarder;
    }

    /**
     * @dev Indicates whether any particular address is the trusted forwarder.
     */
    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == trustedForwarder();
    }

    /**
     * @dev Override for `msg.sender`. Defaults to the original `msg.sender` whenever
     * a call is not performed by the trusted forwarder or the calldata length is less than
     * 20 bytes (an address length).
     */
    function _msgSender() internal view virtual override returns (address) {
        uint256 calldataLength = msg.data.length;
        uint256 contextSuffixLength = _contextSuffixLength();
        if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
            return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
        } else {
            return super._msgSender();
        }
    }

    /**
     * @dev Override for `msg.data`. Defaults to the original `msg.data` whenever
     * a call is not performed by the trusted forwarder or the calldata length is less than
     * 20 bytes (an address length).
     */
    function _msgData() internal view virtual override returns (bytes calldata) {
        uint256 calldataLength = msg.data.length;
        uint256 contextSuffixLength = _contextSuffixLength();
        if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
            return msg.data[:calldataLength - contextSuffixLength];
        } else {
            return super._msgData();
        }
    }

    /**
     * @dev ERC-2771 specifies the context as being a single address (20 bytes).
     */
    function _contextSuffixLength() internal view virtual override returns (uint256) {
        return 20;
    }
}

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "./IERC721.sol";
import {IERC721Metadata} from "./extensions/IERC721Metadata.sol";
import {ERC721Utils} from "./utils/ERC721Utils.sol";
import {Context} from "../../utils/Context.sol";
import {Strings} from "../../utils/Strings.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC721Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC-721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors {
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    mapping(uint256 tokenId => address) private _owners;

    mapping(address owner => uint256) private _balances;

    mapping(uint256 tokenId => address) private _tokenApprovals;

    mapping(address owner => mapping(address operator => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual returns (uint256) {
        if (owner == address(0)) {
            revert ERC721InvalidOwner(address(0));
        }
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual returns (address) {
        return _requireOwned(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual returns (string memory) {
        _requireOwned(tokenId);

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overridden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual {
        _approve(to, tokenId, _msgSender());
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual returns (address) {
        _requireOwned(tokenId);

        return _getApproved(tokenId);
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists
        // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here.
        address previousOwner = _update(to, tokenId, _msgSender());
        if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual {
        transferFrom(from, to, tokenId);
        ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
    }

    /**
     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
     *
     * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the
     * core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances
     * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by
     * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`.
     */
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _owners[tokenId];
    }

    /**
     * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted.
     */
    function _getApproved(uint256 tokenId) internal view virtual returns (address) {
        return _tokenApprovals[tokenId];
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in
     * particular (ignoring whether it is owned by `owner`).
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) {
        return
            spender != address(0) &&
            (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender);
    }

    /**
     * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner.
     * Reverts if:
     * - `spender` does not have approval from `owner` for `tokenId`.
     * - `spender` does not have approval to manage all of `owner`'s assets.
     *
     * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this
     * assumption.
     */
    function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
        if (!_isAuthorized(owner, spender, tokenId)) {
            if (owner == address(0)) {
                revert ERC721NonexistentToken(tokenId);
            } else {
                revert ERC721InsufficientApproval(spender, tokenId);
            }
        }
    }

    /**
     * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
     *
     * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that
     * a uint256 would ever overflow from increments when these increments are bounded to uint128 values.
     *
     * WARNING: Increasing an account's balance using this function tends to be paired with an override of the
     * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership
     * remain consistent with one another.
     */
    function _increaseBalance(address account, uint128 value) internal virtual {
        unchecked {
            _balances[account] += value;
        }
    }

    /**
     * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner
     * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update.
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that
     * `auth` is either the owner of the token, or approved to operate on the token (by the owner).
     *
     * Emits a {Transfer} event.
     *
     * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}.
     */
    function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) {
        address from = _ownerOf(tokenId);

        // Perform (optional) operator check
        if (auth != address(0)) {
            _checkAuthorized(from, auth, tokenId);
        }

        // Execute the update
        if (from != address(0)) {
            // Clear approval. No need to re-authorize or emit the Approval event
            _approve(address(0), tokenId, address(0), false);

            unchecked {
                _balances[from] -= 1;
            }
        }

        if (to != address(0)) {
            unchecked {
                _balances[to] += 1;
            }
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        return from;
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner != address(0)) {
            revert ERC721InvalidSender(address(0));
        }
    }

    /**
     * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
        _mint(to, tokenId);
        ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     * This is an internal function that does not check if the sender is authorized to operate on the token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal {
        address previousOwner = _update(address(0), tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(address from, address to, uint256 tokenId) internal {
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }
        address previousOwner = _update(to, tokenId, address(0));
        if (previousOwner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        } else if (previousOwner != from) {
            revert ERC721IncorrectOwner(from, tokenId, previousOwner);
        }
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients
     * are aware of the ERC-721 standard to prevent tokens from being forever locked.
     *
     * `data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is like {safeTransferFrom} in the sense that it invokes
     * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `tokenId` token must exist and be owned by `from`.
     * - `to` cannot be the zero address.
     * - `from` cannot be the zero address.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(address from, address to, uint256 tokenId) internal {
        _safeTransfer(from, to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
        _transfer(from, to, tokenId);
        ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is
     * either the owner of the token, or approved to operate on all tokens held by this owner.
     *
     * Emits an {Approval} event.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address to, uint256 tokenId, address auth) internal {
        _approve(to, tokenId, auth, true);
    }

    /**
     * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not
     * emitted in the context of transfers.
     */
    function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual {
        // Avoid reading the owner unless necessary
        if (emitEvent || auth != address(0)) {
            address owner = _requireOwned(tokenId);

            // We do not use _isAuthorized because single-token approvals should not be able to call approve
            if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) {
                revert ERC721InvalidApprover(auth);
            }

            if (emitEvent) {
                emit Approval(owner, to, tokenId);
            }
        }

        _tokenApprovals[tokenId] = to;
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Requirements:
     * - operator can't be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
        if (operator == address(0)) {
            revert ERC721InvalidOperator(operator);
        }
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned).
     * Returns the owner.
     *
     * Overrides to ownership logic should be done to {_ownerOf}.
     */
    function _requireOwned(uint256 tokenId) internal view returns (address) {
        address owner = _ownerOf(tokenId);
        if (owner == address(0)) {
            revert ERC721NonexistentToken(tokenId);
        }
        return owner;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../utils/introspection/IERC165.sol";

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

File 100 of 139 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.20;

import {IERC721} from "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

File 102 of 139 : ERC721Utils.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/utils/ERC721Utils.sol)

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../IERC721Receiver.sol";
import {IERC721Errors} from "../../../interfaces/draft-IERC6093.sol";

/**
 * @dev Library that provide common ERC-721 utility functions.
 *
 * See https://eips.ethereum.org/EIPS/eip-721[ERC-721].
 *
 * _Available since v5.1._
 */
library ERC721Utils {
    /**
     * @dev Performs an acceptance check for the provided `operator` by calling {IERC721-onERC721Received}
     * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`).
     *
     * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA).
     * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept
     * the transfer.
     */
    function checkOnERC721Received(
        address operator,
        address from,
        address to,
        uint256 tokenId,
        bytes memory data
    ) internal {
        if (to.code.length > 0) {
            try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) {
                if (retval != IERC721Receiver.onERC721Received.selector) {
                    // Token rejected
                    revert IERC721Errors.ERC721InvalidReceiver(to);
                }
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    // non-IERC721Receiver implementer
                    revert IERC721Errors.ERC721InvalidReceiver(to);
                } else {
                    assembly ("memory-safe") {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)

pragma solidity ^0.8.20;

import {Errors} from "./Errors.sol";

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert Errors.InsufficientBalance(address(this).balance, amount);
        }

        (bool success, bytes memory returndata) = recipient.call{value: amount}("");
        if (!success) {
            _revert(returndata);
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            assembly ("memory-safe") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 105 of 139 : Errors.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

File 106 of 139 : Panic.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    using SafeCast for *;

    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev The string being parsed contains characters that are not in scope of the given base.
     */
    error StringsInvalidChar();

    /**
     * @dev The string being parsed is not a properly formatted address.
     */
    error StringsInvalidAddressFormat();

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            assembly ("memory-safe") {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                assembly ("memory-safe") {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
     * representation, according to EIP-55.
     */
    function toChecksumHexString(address addr) internal pure returns (string memory) {
        bytes memory buffer = bytes(toHexString(addr));

        // hash the hex part of buffer (skip length + 2 bytes, length 40)
        uint256 hashValue;
        assembly ("memory-safe") {
            hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
        }

        for (uint256 i = 41; i > 1; --i) {
            // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
            if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
                // case shift by xoring with 0x20
                buffer[i] ^= 0x20;
            }
            hashValue >>= 4;
        }
        return string(buffer);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }

    /**
     * @dev Parse a decimal string and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input) internal pure returns (uint256) {
        return parseUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[0-9]*`
     * - The result must fit into an `uint256` type
     */
    function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseUint} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        uint256 result = 0;
        for (uint256 i = begin; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 9) return (false, 0);
            result *= 10;
            result += chr;
        }
        return (true, result);
    }

    /**
     * @dev Parse a decimal string and returns the value as a `int256`.
     *
     * Requirements:
     * - The string must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input) internal pure returns (int256) {
        return parseInt(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `[-+]?[0-9]*`
     * - The result must fit in an `int256` type.
     */
    function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
        (bool success, int256 value) = tryParseInt(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
     * the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
        return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
    }

    uint256 private constant ABS_MIN_INT256 = 2 ** 255;

    /**
     * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
     * character or if the result does not fit in a `int256`.
     *
     * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
     */
    function tryParseInt(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, int256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseIntUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseInt} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseIntUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, int256 value) {
        bytes memory buffer = bytes(input);

        // Check presence of a negative sign.
        bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        bool positiveSign = sign == bytes1("+");
        bool negativeSign = sign == bytes1("-");
        uint256 offset = (positiveSign || negativeSign).toUint();

        (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);

        if (absSuccess && absValue < ABS_MIN_INT256) {
            return (true, negativeSign ? -int256(absValue) : int256(absValue));
        } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
            return (true, type(int256).min);
        } else return (false, 0);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input) internal pure returns (uint256) {
        return parseHexUint(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
     * - The result must fit in an `uint256` type.
     */
    function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
        (bool success, uint256 value) = tryParseHexUint(input, begin, end);
        if (!success) revert StringsInvalidChar();
        return value;
    }

    /**
     * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
        return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
     * invalid character.
     *
     * NOTE: This function will revert if the result does not fit in a `uint256`.
     */
    function tryParseHexUint(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, uint256 value) {
        if (end > bytes(input).length || begin > end) return (false, 0);
        return _tryParseHexUintUncheckedBounds(input, begin, end);
    }

    /**
     * @dev Implementation of {tryParseHexUint} that does not check bounds. Caller should make sure that
     * `begin <= end <= input.length`. Other inputs would result in undefined behavior.
     */
    function _tryParseHexUintUncheckedBounds(
        string memory input,
        uint256 begin,
        uint256 end
    ) private pure returns (bool success, uint256 value) {
        bytes memory buffer = bytes(input);

        // skip 0x prefix if present
        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 offset = hasPrefix.toUint() * 2;

        uint256 result = 0;
        for (uint256 i = begin + offset; i < end; ++i) {
            uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
            if (chr > 15) return (false, 0);
            result *= 16;
            unchecked {
                // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
                // This guaratees that adding a value < 16 will not cause an overflow, hence the unchecked.
                result += chr;
            }
        }
        return (true, result);
    }

    /**
     * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
     *
     * Requirements:
     * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input) internal pure returns (address) {
        return parseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress} that parses a substring of `input` located between position `begin` (included) and
     * `end` (excluded).
     *
     * Requirements:
     * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
     */
    function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
        (bool success, address value) = tryParseAddress(input, begin, end);
        if (!success) revert StringsInvalidAddressFormat();
        return value;
    }

    /**
     * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
     * formatted address. See {parseAddress} requirements.
     */
    function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
        return tryParseAddress(input, 0, bytes(input).length);
    }

    /**
     * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
     * formatted address. See {parseAddress} requirements.
     */
    function tryParseAddress(
        string memory input,
        uint256 begin,
        uint256 end
    ) internal pure returns (bool success, address value) {
        if (end > bytes(input).length || begin > end) return (false, address(0));

        bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
        uint256 expectedLength = 40 + hasPrefix.toUint() * 2;

        // check that input is the correct length
        if (end - begin == expectedLength) {
            // length guarantees that this does not overflow, and value is at most type(uint160).max
            (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
            return (s, address(uint160(v)));
        } else {
            return (false, address(0));
        }
    }

    function _tryParseChr(bytes1 chr) private pure returns (uint8) {
        uint8 value = uint8(chr);

        // Try to parse `chr`:
        // - Case 1: [0-9]
        // - Case 2: [a-f]
        // - Case 3: [A-F]
        // - otherwise not supported
        unchecked {
            if (value > 47 && value < 58) value -= 48;
            else if (value > 96 && value < 103) value -= 87;
            else if (value > 64 && value < 71) value -= 55;
            else return type(uint8).max;
        }

        return value;
    }

    /**
     * @dev Reads a bytes32 from a bytes array without bounds checking.
     *
     * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
     * assembly block as such would prevent some optimizations.
     */
    function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
        // This is not memory safe in the general case, but all calls to this private function are within bounds.
        assembly ("memory-safe") {
            value := mload(add(buffer, add(0x20, offset)))
        }
    }
}

File 108 of 139 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "./IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

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

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

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

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

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

    /**
     * @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
    }

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

    /**
     * @dev Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 exp;
        unchecked {
            exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
            value >>= exp;
            result += exp;

            exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
            value >>= exp;
            result += exp;

            exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
            value >>= exp;
            result += exp;

            exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
            value >>= exp;
            result += exp;

            exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
            value >>= exp;
            result += exp;

            exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
            value >>= exp;
            result += exp;

            exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
            value >>= exp;
            result += exp;

            result += SafeCast.toUint(value > 1);
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 isGt;
        unchecked {
            isGt = SafeCast.toUint(value > (1 << 128) - 1);
            value >>= isGt * 128;
            result += isGt * 16;

            isGt = SafeCast.toUint(value > (1 << 64) - 1);
            value >>= isGt * 64;
            result += isGt * 8;

            isGt = SafeCast.toUint(value > (1 << 32) - 1);
            value >>= isGt * 32;
            result += isGt * 4;

            isGt = SafeCast.toUint(value > (1 << 16) - 1);
            value >>= isGt * 16;
            result += isGt * 2;

            result += SafeCast.toUint(value > (1 << 8) - 1);
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 111 of 139 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * int256(SafeCast.toUint(condition)));
        }
    }

    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a > b, a, b);
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return ternary(a < b, a, b);
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
            // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
            // taking advantage of the most significant (or "sign" bit) in two's complement representation.
            // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
            // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
            int256 mask = n >> 255;

            // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
            return uint256((n + mask) ^ mask);
        }
    }
}

File 113 of 139 : 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 maximum value a uint64 number can have.
uint64 constant MAX_UINT64 = type(uint64).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;
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IEIP712} from "./IEIP712.sol";

/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
    /// @notice Thrown when an allowance on a token has expired.
    /// @param deadline The timestamp at which the allowed amount is no longer valid
    error AllowanceExpired(uint256 deadline);

    /// @notice Thrown when an allowance on a token has been depleted.
    /// @param amount The maximum amount allowed
    error InsufficientAllowance(uint256 amount);

    /// @notice Thrown when too many nonces are invalidated.
    error ExcessiveInvalidation();

    /// @notice Emits an event when the owner successfully invalidates an ordered nonce.
    event NonceInvalidation(
        address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
    );

    /// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
    event Approval(
        address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
    );

    /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
    event Permit(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration,
        uint48 nonce
    );

    /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
    event Lockdown(address indexed owner, address token, address spender);

    /// @notice The permit data for a token
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice The permit message signed for a single token allowance
    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The permit message signed for multiple token allowances
    struct PermitBatch {
        // the permit data for multiple token allowances
        PermitDetails[] details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The saved permissions
    /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    struct PackedAllowance {
        // amount allowed
        uint160 amount;
        // permission expiry
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each signature
        uint48 nonce;
    }

    /// @notice A token spender pair.
    struct TokenSpenderPair {
        // the token the spender is approved
        address token;
        // the spender address
        address spender;
    }

    /// @notice Details for a token transfer.
    struct AllowanceTransferDetails {
        // the owner of the token
        address from;
        // the recipient of the token
        address to;
        // the amount of the token
        uint160 amount;
        // the token to be transferred
        address token;
    }

    /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
    /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
    /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
    function allowance(address user, address token, address spender)
        external
        view
        returns (uint160 amount, uint48 expiration, uint48 nonce);

    /// @notice Approves the spender to use up to amount of the specified token up until the expiration
    /// @param token The token to approve
    /// @param spender The spender address to approve
    /// @param amount The approved amount of the token
    /// @param expiration The timestamp at which the approval is no longer valid
    /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    function approve(address token, address spender, uint160 amount, uint48 expiration) external;

    /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitSingle Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;

    /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitBatch Data signed over by the owner specifying the terms of approval
    /// @param signature The owner's signature over the permit data
    function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;

    /// @notice Transfer approved tokens from one address to another
    /// @param from The address to transfer from
    /// @param to The address of the recipient
    /// @param amount The amount of the token to transfer
    /// @param token The token address to transfer
    /// @dev Requires the from address to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(address from, address to, uint160 amount, address token) external;

    /// @notice Transfer approved tokens in a batch
    /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
    /// @dev Requires the from addresses to have approved at least the desired amount
    /// of tokens to msg.sender.
    function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;

    /// @notice Enables performing a "lockdown" of the sender's Permit2 identity
    /// by batch revoking approvals
    /// @param approvals Array of approvals to revoke.
    function lockdown(TokenSpenderPair[] calldata approvals) external;

    /// @notice Invalidate nonces for a given (token, spender) pair
    /// @param token The token to invalidate nonces for
    /// @param spender The spender to invalidate nonces for
    /// @param newNonce The new nonce to set. Invalidates all nonces less than it.
    /// @dev Can't invalidate more than 2**16 nonces per transaction.
    function invalidateNonces(address token, address spender, uint48 newNonce) external;
}

File 115 of 139 : IEIP712.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IEIP712 {
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 116 of 139 : IPermit2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";

/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IEIP712} from "./IEIP712.sol";

/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
    /// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
    /// @param maxAmount The maximum amount a spender can request to transfer
    error InvalidAmount(uint256 maxAmount);

    /// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
    /// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
    error LengthMismatch();

    /// @notice Emits an event when the owner successfully invalidates an unordered nonce.
    event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);

    /// @notice The token and amount details for a transfer signed in the permit transfer signature
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /// @notice The signed permit message for a single token transfer
    struct PermitTransferFrom {
        TokenPermissions permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice Specifies the recipient address and amount for batched transfers.
    /// @dev Recipients and amounts correspond to the index of the signed token permissions array.
    /// @dev Reverts if the requested amount is greater than the permitted signed amount.
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /// @notice Used to reconstruct the signed permit message for multiple token transfers
    /// @dev Do not need to pass in spender address as it is required that it is msg.sender
    /// @dev Note that a user still signs over a spender address
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
    /// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
    /// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
    /// @dev It returns a uint256 bitmap
    /// @dev The index, or wordPosition is capped at type(uint248).max
    function nonceBitmap(address, uint256) external view returns (uint256);

    /// @notice Transfers a token using a signed permit message
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers a token using a signed permit message
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @dev Reverts if the requested amount is greater than the permitted signed amount
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails The spender's requested transfer details for the permitted token
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitTransferFrom memory permit,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param signature The signature to verify
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;

    /// @notice Transfers multiple tokens using a signed permit message
    /// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
    /// @notice Includes extra data provided by the caller to verify signature over
    /// @param permit The permit data signed over by the owner
    /// @param owner The owner of the tokens to transfer
    /// @param transferDetails Specifies the recipient and requested amount for the token transfer
    /// @param witness Extra data to include when checking the user signature
    /// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
    /// @param signature The signature to verify
    function permitWitnessTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes32 witness,
        string calldata witnessTypeString,
        bytes calldata signature
    ) external;

    /// @notice Invalidates the bits specified in mask for the bitmap at the word position
    /// @dev The wordPos is maxed at type(uint248).max
    /// @param wordPos A number to index the nonceBitmap at
    /// @param mask A bitmap masked against msg.sender's current bitmap at the word position
    function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}

File 118 of 139 : IUniswapV3Pool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IUniswapV3PoolImmutables} from './pool/IUniswapV3PoolImmutables.sol';
import {IUniswapV3PoolState} from './pool/IUniswapV3PoolState.sol';
import {IUniswapV3PoolDerivedState} from './pool/IUniswapV3PoolDerivedState.sol';
import {IUniswapV3PoolActions} from './pool/IUniswapV3PoolActions.sol';
import {IUniswapV3PoolOwnerActions} from './pool/IUniswapV3PoolOwnerActions.sol';
import {IUniswapV3PoolErrors} from './pool/IUniswapV3PoolErrors.sol';
import {IUniswapV3PoolEvents} from './pool/IUniswapV3PoolEvents.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
    IUniswapV3PoolImmutables,
    IUniswapV3PoolState,
    IUniswapV3PoolDerivedState,
    IUniswapV3PoolActions,
    IUniswapV3PoolOwnerActions,
    IUniswapV3PoolErrors,
    IUniswapV3PoolEvents
{

}

File 119 of 139 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
    /// @notice Sets the initial price for the pool
    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
    function initialize(uint160 sqrtPriceX96) external;

    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
    /// @param recipient The address for which the liquidity will be created
    /// @param tickLower The lower tick of the position in which to add liquidity
    /// @param tickUpper The upper tick of the position in which to add liquidity
    /// @param amount The amount of liquidity to mint
    /// @param data Any data that should be passed through to the callback
    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount,
        bytes calldata data
    ) external returns (uint256 amount0, uint256 amount1);

    /// @notice Collects tokens owed to a position
    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
    /// @param recipient The address which should receive the fees collected
    /// @param tickLower The lower tick of the position for which to collect fees
    /// @param tickUpper The upper tick of the position for which to collect fees
    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
    /// @dev Fees must be collected separately via a call to #collect
    /// @param tickLower The lower tick of the position for which to burn liquidity
    /// @param tickUpper The upper tick of the position for which to burn liquidity
    /// @param amount How much liquidity to burn
    /// @return amount0 The amount of token0 sent to the recipient
    /// @return amount1 The amount of token1 sent to the recipient
    function burn(
        int24 tickLower,
        int24 tickUpper,
        uint128 amount
    ) external returns (uint256 amount0, uint256 amount1);

    /// @notice Swap token0 for token1, or token1 for token0
    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
    /// with 0 amount{0,1} and sending the donation amount(s) from the callback
    /// @param recipient The address which will receive the token0 and token1 amounts
    /// @param amount0 The amount of token0 to send
    /// @param amount1 The amount of token1 to send
    /// @param data Any data to be passed through to the callback
    function flash(
        address recipient,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external;

    /// @notice Increase the maximum number of price and liquidity observations that this pool will store
    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
    /// the input observationCardinalityNext.
    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
    /// you must call it with secondsAgos = [3600, 0].
    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
    /// timestamp
    function observe(uint32[] calldata secondsAgos)
        external
        view
        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
    /// snapshot is taken and the second snapshot is taken.
    /// @param tickLower The lower tick of the range
    /// @param tickUpper The upper tick of the range
    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
    /// @return secondsInside The snapshot of seconds per liquidity for the range
    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
        external
        view
        returns (
            int56 tickCumulativeInside,
            uint160 secondsPerLiquidityInsideX128,
            uint32 secondsInside
        );
}

File 122 of 139 : IUniswapV3PoolErrors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Errors emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolErrors {
    error LOK();
    error TLU();
    error TLM();
    error TUM();
    error AI();
    error M0();
    error M1();
    error AS();
    error IIA();
    error L();
    error F0();
    error F1();
}

File 123 of 139 : IUniswapV3PoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolEvents {
    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
    event Initialize(uint160 sqrtPriceX96, int24 tick);

    /// @notice Emitted when liquidity is minted for a given position
    /// @param sender The address that minted the liquidity
    /// @param owner The owner of the position and recipient of any minted liquidity
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity minted to the position range
    /// @param amount0 How much token0 was required for the minted liquidity
    /// @param amount1 How much token1 was required for the minted liquidity
    event Mint(
        address sender,
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    /// @notice Emitted when fees are collected by the owner of a position
    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
    /// @param owner The owner of the position for which fees are collected
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount0 The amount of token0 fees collected
    /// @param amount1 The amount of token1 fees collected
    event Collect(
        address indexed owner,
        address recipient,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount0,
        uint128 amount1
    );

    /// @notice Emitted when a position's liquidity is removed
    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
    /// @param owner The owner of the position for which liquidity is removed
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity to remove
    /// @param amount0 The amount of token0 withdrawn
    /// @param amount1 The amount of token1 withdrawn
    event Burn(
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint256 amount0,
        uint256 amount1
    );

    /// @notice Emitted by the pool for any swaps between token0 and token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the output of the swap
    /// @param amount0 The delta of the token0 balance of the pool
    /// @param amount1 The delta of the token1 balance of the pool
    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
    /// @param liquidity The liquidity of the pool after the swap
    /// @param tick The log base 1.0001 of price of the pool after the swap
    event Swap(
        address indexed sender,
        address indexed recipient,
        int256 amount0,
        int256 amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick
    );

    /// @notice Emitted by the pool for any flashes of token0/token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the tokens from flash
    /// @param amount0 The amount of token0 that was flashed
    /// @param amount1 The amount of token1 that was flashed
    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
    event Flash(
        address indexed sender,
        address indexed recipient,
        uint256 amount0,
        uint256 amount1,
        uint256 paid0,
        uint256 paid1
    );

    /// @notice Emitted by the pool for increases to the number of observations that can be stored
    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
    /// just before a mint/swap/burn.
    /// @param observationCardinalityNextOld The previous value of the next observation cardinality
    /// @param observationCardinalityNextNew The updated value of the next observation cardinality
    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld,
        uint16 observationCardinalityNextNew
    );

    /// @notice Emitted when the protocol fee is changed by the pool
    /// @param feeProtocol0Old The previous value of the token0 protocol fee
    /// @param feeProtocol1Old The previous value of the token1 protocol fee
    /// @param feeProtocol0New The updated value of the token0 protocol fee
    /// @param feeProtocol1New The updated value of the token1 protocol fee
    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);

    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
    /// @param sender The address that collects the protocol fees
    /// @param recipient The address that receives the collected protocol fees
    /// @param amount0 The amount of token0 protocol fees that is withdrawn
    /// @param amount0 The amount of token1 protocol fees that is withdrawn
    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface IUniswapV3PoolOwnerActions {
    /// @notice Set the denominator of the protocol's % share of the fees
    /// @param feeProtocol0 new protocol fee for token0 of the pool
    /// @param feeProtocol1 new protocol fee for token1 of the pool
    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;

    /// @notice Collect the protocol fee accrued to the pool
    /// @param recipient The address to which collected protocol fees should be sent
    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
    /// @return amount0 The protocol fee collected in token0
    /// @return amount1 The protocol fee collected in token1
    function collectProtocol(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// @return tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// @return observationIndex The index of the last oracle observation that was written,
    /// @return observationCardinality The current maximum number of observations stored in the pool,
    /// @return observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// @return feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint256);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint256);

    /// @notice The amounts of token0 and token1 that are owed to the protocol
    /// @dev Protocol fees will never exceed uint128 max in either token
    function protocolFees() external view returns (uint128 token0, uint128 token1);

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all ticks
    /// @return The liquidity at the current price of the pool
    function liquidity() external view returns (uint128);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
    /// tick upper
    /// @return liquidityNet how much liquidity changes when the pool price crosses the tick,
    /// @return feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
    /// @return feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
    /// @return tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
    /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
    /// @return secondsOutside the seconds spent on the other side of the tick from the current tick,
    /// @return initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
    /// a specific position.
    function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint256);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
    /// @return liquidity The amount of liquidity in the position,
    /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
    /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
    /// @return tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
    /// @return tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
    function positions(bytes32 key)
        external
        view
        returns (
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// @return tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
    /// @return secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
    /// @return initialized whether the observation has been initialized and the values are safe to use
    function observations(uint256 index)
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJB721TiersHook} from "@bananapus/721-hook/src/interfaces/IJB721TiersHook.sol";
import {IJB721TiersHookDeployer} from "@bananapus/721-hook/src/interfaces/IJB721TiersHookDeployer.sol";
import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core/src/interfaces/IJBPermissions.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {IJBRulesetDataHook} from "@bananapus/core/src/interfaces/IJBRulesetDataHook.sol";
import {JBRulesetConfig} from "@bananapus/core/src/structs/JBRulesetConfig.sol";
import {JBTerminalConfig} from "@bananapus/core/src/structs/JBTerminalConfig.sol";
import {IJBSuckerRegistry} from "@bananapus/suckers/src/interfaces/IJBSuckerRegistry.sol";
import {CTPublisher} from "@croptop/core/src/CTPublisher.sol";

import {REVBuybackHookConfig} from "../structs/REVBuybackHookConfig.sol";
import {REVConfig} from "../structs/REVConfig.sol";
import {REVCroptopAllowedPost} from "../structs/REVCroptopAllowedPost.sol";
import {REVDeploy721TiersHookConfig} from "../structs/REVDeploy721TiersHookConfig.sol";
import {REVSuckerDeploymentConfig} from "../structs/REVSuckerDeploymentConfig.sol";

interface IREVDeployer {
    event ReplaceSplitOperator(uint256 indexed revnetId, address indexed newSplitOperator, address caller);
    event DeploySuckers(
        uint256 indexed revnetId,
        bytes32 encodedConfigurationHash,
        REVSuckerDeploymentConfig suckerDeploymentConfiguration,
        address caller
    );

    event DeployRevnet(
        uint256 indexed revnetId,
        REVConfig configuration,
        JBTerminalConfig[] terminalConfigurations,
        REVBuybackHookConfig buybackHookConfiguration,
        REVSuckerDeploymentConfig suckerDeploymentConfiguration,
        JBRulesetConfig[] rulesetConfigurations,
        bytes32 encodedConfigurationHash,
        address caller
    );

    event SetCashOutDelay(uint256 indexed revnetId, uint256 cashOutDelay, address caller);

    event AutoIssue(
        uint256 indexed revnetId, uint256 indexed stageId, address indexed beneficiary, uint256 count, address caller
    );

    event StoreAutoIssuanceAmount(
        uint256 indexed revnetId, uint256 indexed stageId, address indexed beneficiary, uint256 count, address caller
    );

    event SetAdditionalOperator(uint256 revnetId, address additionalOperator, uint256[] permissionIds, address caller);

    function CASH_OUT_DELAY() external view returns (uint256);
    function CONTROLLER() external view returns (IJBController);
    function DIRECTORY() external view returns (IJBDirectory);
    function PROJECTS() external view returns (IJBProjects);
    function PERMISSIONS() external view returns (IJBPermissions);
    function FEE() external view returns (uint256);
    function SUCKER_REGISTRY() external view returns (IJBSuckerRegistry);
    function FEE_REVNET_ID() external view returns (uint256);
    function PUBLISHER() external view returns (CTPublisher);
    function HOOK_DEPLOYER() external view returns (IJB721TiersHookDeployer);

    function amountToAutoIssue(
        uint256 revnetId,
        uint256 stageId,
        address beneficiary
    )
        external
        view
        returns (uint256);
    function buybackHookOf(uint256 revnetId) external view returns (IJBRulesetDataHook);
    function cashOutDelayOf(uint256 revnetId) external view returns (uint256);
    function deploySuckersFor(
        uint256 revnetId,
        REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
    )
        external
        returns (address[] memory suckers);
    function hashedEncodedConfigurationOf(uint256 revnetId) external view returns (bytes32);
    function isSplitOperatorOf(uint256 revnetId, address addr) external view returns (bool);
    function loansOf(uint256 revnetId) external view returns (address);
    function tiered721HookOf(uint256 revnetId) external view returns (IJB721TiersHook);

    function autoIssueFor(uint256 revnetId, uint256 stageId, address beneficiary) external;
    function deployFor(
        uint256 revnetId,
        REVConfig memory configuration,
        JBTerminalConfig[] memory terminalConfigurations,
        REVBuybackHookConfig memory buybackHookConfiguration,
        REVSuckerDeploymentConfig memory suckerDeploymentConfiguration
    )
        external
        returns (uint256);

    function deployWith721sFor(
        uint256 revnetId,
        REVConfig calldata configuration,
        JBTerminalConfig[] memory terminalConfigurations,
        REVBuybackHookConfig memory buybackHookConfiguration,
        REVSuckerDeploymentConfig memory suckerDeploymentConfiguration,
        REVDeploy721TiersHookConfig memory tiered721HookConfiguration,
        REVCroptopAllowedPost[] memory allowedPosts
    )
        external
        returns (uint256, IJB721TiersHook hook);

    function setSplitOperatorOf(uint256 revnetId, address newSplitOperator) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPermit2} from "@uniswap/permit2/src/interfaces/IPermit2.sol";
import {IJBController} from "@bananapus/core/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core/src/interfaces/IJBDirectory.sol";
import {IJBPayoutTerminal} from "@bananapus/core/src/interfaces/IJBPayoutTerminal.sol";
import {IJBPrices} from "@bananapus/core/src/interfaces/IJBPrices.sol";
import {IJBProjects} from "@bananapus/core/src/interfaces/IJBProjects.sol";
import {IJBTokenUriResolver} from "@bananapus/core/src/interfaces/IJBTokenUriResolver.sol";
import {JBSingleAllowance} from "@bananapus/core/src/structs/JBSingleAllowance.sol";

import {IREVDeployer} from "./IREVDeployer.sol";
import {REVLoan} from "./../structs/REVLoan.sol";
import {REVLoanSource} from "./../structs/REVLoanSource.sol";

interface IREVLoans {
    event Borrow(
        uint256 indexed loanId,
        uint256 indexed revnetId,
        REVLoan loan,
        REVLoanSource source,
        uint256 borrowAmount,
        uint256 collateralCount,
        uint256 sourceFeeAmount,
        address payable beneficiary,
        address caller
    );
    event Liquidate(uint256 indexed loanId, uint256 indexed revnetId, REVLoan loan, address caller);
    event RepayLoan(
        uint256 indexed loanId,
        uint256 indexed revnetId,
        uint256 indexed paidOffLoanId,
        REVLoan loan,
        REVLoan paidOffLoan,
        uint256 repayBorrowAmount,
        uint256 sourceFeeAmount,
        uint256 collateralCountToReturn,
        address payable beneficiary,
        address caller
    );
    event ReallocateCollateral(
        uint256 indexed loanId,
        uint256 indexed revnetId,
        uint256 indexed reallocatedLoanId,
        REVLoan reallocatedLoan,
        uint256 removedcollateralCount,
        address caller
    );
    event SetTokenUriResolver(IJBTokenUriResolver indexed resolver, address caller);

    function LOAN_LIQUIDATION_DURATION() external view returns (uint256);
    function PERMIT2() external view returns (IPermit2);
    function CONTROLLER() external view returns (IJBController);
    function REVNETS() external view returns (IREVDeployer);
    function DIRECTORY() external view returns (IJBDirectory);
    function PRICES() external view returns (IJBPrices);
    function PROJECTS() external view returns (IJBProjects);
    function REV_ID() external view returns (uint256);
    function REV_PREPAID_FEE_PERCENT() external view returns (uint256);
    function MIN_PREPAID_FEE_PERCENT() external view returns (uint256);
    function MAX_PREPAID_FEE_PERCENT() external view returns (uint256);

    function borrowableAmountFrom(
        uint256 revnetId,
        uint256 collateral,
        uint256 decimals,
        uint256 currency
    )
        external
        view
        returns (uint256);
    function determineSourceFeeAmount(
        REVLoan memory loan,
        uint256 amount
    )
        external
        view
        returns (uint256 sourceFeeAmount);
    function isLoanSourceOf(uint256 revnetId, IJBPayoutTerminal terminal, address token) external view returns (bool);
    function loanOf(uint256 loanId) external view returns (REVLoan memory);
    function loanSourcesOf(uint256 revnetId) external view returns (REVLoanSource[] memory);
    function numberOfLoansFor(uint256 revnetId) external view returns (uint256);
    function revnetIdOfLoanWith(uint256 loanId) external view returns (uint256);
    function tokenUriResolver() external view returns (IJBTokenUriResolver);
    function totalBorrowedFrom(
        uint256 revnetId,
        IJBPayoutTerminal terminal,
        address token
    )
        external
        view
        returns (uint256);
    function totalCollateralOf(uint256 revnetId) external view returns (uint256);

    function borrowFrom(
        uint256 revnetId,
        REVLoanSource calldata source,
        uint256 minBorrowAmount,
        uint256 collateral,
        address payable beneficiary,
        uint256 prepaidFeePercent
    )
        external
        returns (uint256 loanId, REVLoan memory loan);
    function liquidateExpiredLoansFrom(uint256 revnetId, uint256 startingLoanId, uint256 count) external;
    function repayLoan(
        uint256 loanId,
        uint256 maxRepayBorrowAmount,
        uint256 newCollateral,
        address payable beneficiary,
        JBSingleAllowance calldata allowance
    )
        external
        payable
        returns (uint256 paidOffLoanId, REVLoan memory loan);
    function reallocateCollateralFromLoan(
        uint256 loanId,
        uint256 collateralToTransfer,
        REVLoanSource calldata source,
        uint256 minBorrowAmount,
        uint256 collateralToAdd,
        address payable beneficiary,
        uint256 prepaidFeePercent
    )
        external
        payable
        returns (uint256 reallocatedLoanId, uint256 newLoanId, REVLoan memory reallocatedLoan, REVLoan memory newLoan);
    function setTokenUriResolver(IJBTokenUriResolver resolver) external;
}

File 129 of 139 : REVAutoIssuance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member chainId The ID of the chain on which the mint should be honored.
/// @custom:member count The number of tokens that should be minted.
/// @custom:member beneficiary The address that will receive the minted tokens.
struct REVAutoIssuance {
    uint32 chainId;
    uint104 count;
    address beneficiary;
}

File 130 of 139 : REVBuybackHookConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBBuybackHook} from "@bananapus/buyback-hook/src/interfaces/IJBBuybackHook.sol";

import {REVBuybackPoolConfig} from "./REVBuybackPoolConfig.sol";

/// @custom:member hook The buyback hook to use.
/// @custom:member poolConfigurations The pools to setup on the given buyback contract.
struct REVBuybackHookConfig {
    IJBBuybackHook hook;
    REVBuybackPoolConfig[] poolConfigurations;
}

File 131 of 139 : REVBuybackPoolConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member token The token to setup a pool for.
/// @custom:member poolFee The fee of the pool in which swaps occur when seeking the best price for a new participant.
/// This incentivizes liquidity providers. Out of 1_000_000. A common value is 1%, or 10_000. Other passible values are
/// 0.3% and 0.1%.
/// @custom:member twapWindow The time window to take into account when quoting a price based on TWAP.
/// @custom:member twapSlippageTolerance The pricetolerance to accept when quoting a price based on TWAP.
struct REVBuybackPoolConfig {
    address token;
    uint24 fee;
    uint32 twapWindow;
    uint32 twapSlippageTolerance;
}

File 132 of 139 : REVConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IREVLoans} from "../interfaces/IREVLoans.sol";
import {REVDescription} from "./REVDescription.sol";
import {REVLoanSource} from "./REVLoanSource.sol";
import {REVStageConfig} from "./REVStageConfig.sol";

/// @custom:member description The description of the revnet.
/// @custom:member baseCurrency The currency that the issuance is based on.
/// @custom:member premintTokenAmount The number of tokens that should be preminted to the initial operator.
/// @custom:member premintChainId The ID of the chain on which the premint should be honored.
/// @custom:member premintStage The stage during which the premint should be honored.
/// @custom:member splitOperator The address that will receive the token premint and initial production split,
/// and who is allowed to change who the operator is. Only the operator can replace itself after deployment.
/// @custom:member stageConfigurations The periods of changing constraints.
/// @custom:member loanSources The sources for loans.
/// @custom:member loans The loans contract, which can mint the revnet's tokens and use the revnet's balance.
struct REVConfig {
    REVDescription description;
    uint32 baseCurrency;
    address splitOperator;
    REVStageConfig[] stageConfigurations;
    REVLoanSource[] loanSources;
    address loans;
}

File 133 of 139 : REVCroptopAllowedPost.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Criteria for allowed posts.
/// @custom:member category A category that should allow posts.
/// @custom:member minimumPrice The minimum price that a post to the specified category should cost.
/// @custom:member minimumTotalSupply The minimum total supply of NFTs that can be made available when minting.
/// @custom:member maxTotalSupply The max total supply of NFTs that can be made available when minting. Leave as 0 for
/// max.
/// @custom:member allowedAddresses A list of addresses that are allowed to post on the category through Croptop.
struct REVCroptopAllowedPost {
    uint24 category;
    uint104 minimumPrice;
    uint32 minimumTotalSupply;
    uint32 maximumTotalSupply;
    address[] allowedAddresses;
}

File 134 of 139 : REVDeploy721TiersHookConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBDeploy721TiersHookConfig} from "@bananapus/721-hook/src/structs/JBDeploy721TiersHookConfig.sol";

/// @custom:member baseline721HookConfiguration The baseline config.
/// @custom:member salt The salt to base the collection's address on.
/// @custom:member splitOperatorCanAdjustTiers A flag indicating if the revnet's split operator can add tiers and remove
/// tiers if
/// the tier is allowed to be removed
/// @custom:member splitOperatorCanUpdateMetadata A flag indicating if the revnet's split operator can update the 721's
/// metadata.
/// @custom:member splitOperatorCanMint A flag indicating if the revnet's split operator can mint 721's from tiers that
/// allow it.
/// @custom:member splitOperatorCanIncreaseDiscountPercent A flag indicating if the revnet's split operator can increase
/// the
/// discount of a tier.
struct REVDeploy721TiersHookConfig {
    JBDeploy721TiersHookConfig baseline721HookConfiguration;
    bytes32 salt;
    bool splitOperatorCanAdjustTiers;
    bool splitOperatorCanUpdateMetadata;
    bool splitOperatorCanMint;
    bool splitOperatorCanIncreaseDiscountPercent;
}

File 135 of 139 : REVDescription.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:member name The name of the ERC-20 token being create for the revnet.
/// @custom:member ticker The ticker of the ERC-20 token being created for the revnet.
/// @custom:member uri The metadata URI containing revnet's info.
/// @custom:member salt Revnets deployed across chains by the same address with the same salt will have the same
/// address.
struct REVDescription {
    string name;
    string ticker;
    string uri;
    bytes32 salt;
}

File 136 of 139 : REVLoan.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {REVLoanSource} from "./REVLoanSource.sol";

/// @custom:member borrowedAmount The amount that is being borrowed.
/// @custom:member collateralTokenCount The number of collateral tokens currently accounted for.
/// @custom:member createdAt The timestamp when the loan was created.
/// @custom:member prepaidFeePercent The percentage of the loan's fees that were prepaid.
/// @custom:member prepaidDuration The duration that the loan was prepaid for.
/// @custom:member source The source of the loan.
struct REVLoan {
    uint112 amount;
    uint112 collateral;
    uint48 createdAt;
    uint16 prepaidFeePercent;
    uint32 prepaidDuration;
    REVLoanSource source;
}

File 137 of 139 : REVLoanSource.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IJBPayoutTerminal} from "@bananapus/core/src/interfaces/IJBPayoutTerminal.sol";

/// @custom:member token The token that is being loaned.
/// @custom:member terminal The terminal that the loan is being made from.
struct REVLoanSource {
    address token;
    IJBPayoutTerminal terminal;
}

File 138 of 139 : REVStageConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBSplit} from "@bananapus/core/src/structs/JBSplit.sol";

import {REVAutoIssuance} from "./REVAutoIssuance.sol";

/// @custom:member startsAtOrAfter The timestamp to start a stage at the given rate at or after.
/// @custom:member autoIssuances The configurations of mints during this stage.
/// @custom:member splitPercent The percentage of newly issued tokens that should be split with the operator, out
/// of 10_000 (JBConstants.MAX_RESERVED_PERCENT).
/// @custom:member splits The splits for the revnet.
/// @custom:member initialIssuance The number of revnet tokens that one unit of the revnet's base currency will buy, as
/// a fixed point number
/// with 18 decimals.
/// @custom:member issuanceCutFrequency The number of seconds between applied issuance decreases. This
/// should be at least 24 hours.
/// @custom:member issuanceCutPercent The percent that issuance should decrease over time. This percentage is out
/// of 1_000_000_000 (JBConstants.MAX_CUT_PERCENT). 0% corresponds to no issuance increase.
/// @custom:member cashOutTaxRate The factor determining how much each token can cash out from the revnet once
/// cashed out. This rate is out of 10_000 (JBConstants.MAX_CASH_OUT_TAX_RATE). 0% corresponds to no tax when cashing
/// out.
/// @custom:member extraMetadata Extra info to attach set into this stage that may affect hooks.
struct REVStageConfig {
    uint48 startsAtOrAfter;
    REVAutoIssuance[] autoIssuances;
    uint16 splitPercent;
    JBSplit[] splits;
    uint112 initialIssuance;
    uint32 issuanceCutFrequency;
    uint32 issuanceCutPercent;
    uint16 cashOutTaxRate;
    uint16 extraMetadata;
}

File 139 of 139 : REVSuckerDeploymentConfig.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {JBSuckerDeployerConfig} from "@bananapus/suckers/src/structs/JBSuckerDeployerConfig.sol";

/// @custom:member deployerConfigurations The information for how to suck tokens to other chains.
/// @custom:member salt The salt to use for creating suckers so that they use the same address across chains.
struct REVSuckerDeploymentConfig {
    JBSuckerDeployerConfig[] deployerConfigurations;
    bytes32 salt;
}

Settings
{
  "evmVersion": "shanghai",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "ipfs",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 550
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "@sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry/",
    "@arbitrum/=node_modules/@arbitrum/",
    "@bananapus/=node_modules/@bananapus/",
    "@chainlink/=node_modules/@chainlink/",
    "@croptop/=node_modules/@croptop/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "@exhausted-pigeon/=node_modules/@exhausted-pigeon/",
    "@offchainlabs/=node_modules/@offchainlabs/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@prb/=node_modules/@prb/",
    "@rev-net/=node_modules/@rev-net/",
    "@scroll-tech/=node_modules/@scroll-tech/",
    "@uniswap/=node_modules/@uniswap/",
    "@zksync/=node_modules/@zksync/",
    "base64-sol/=node_modules/base64-sol/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "hardhat/=node_modules/hardhat/",
    "solady/=node_modules/solady/",
    "solmate/=node_modules/solmate/",
    "sphinx/=lib/sphinx/packages/contracts/contracts/forge-std/src/"
  ],
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IREVDeployer","name":"revnets","type":"address"},{"internalType":"uint256","name":"revId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"contract IPermit2","name":"permit2","type":"address"},{"internalType":"address","name":"trustedForwarder","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721IncorrectOwner","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721InsufficientApproval","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC721InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"ERC721InvalidOperator","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ERC721InvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC721InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC721InvalidSender","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721NonexistentToken","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","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":"uint256","name":"collateralToReturn","type":"uint256"},{"internalType":"uint256","name":"loanCollateral","type":"uint256"}],"name":"REVLoans_CollateralExceedsLoan","type":"error"},{"inputs":[{"internalType":"uint256","name":"prepaidFeePercent","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"REVLoans_InvalidPrepaidFeePercent","type":"error"},{"inputs":[{"internalType":"uint256","name":"timeSinceLoanCreated","type":"uint256"},{"internalType":"uint256","name":"loanLiquidationDuration","type":"uint256"}],"name":"REVLoans_LoanExpired","type":"error"},{"inputs":[{"internalType":"uint256","name":"newBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"loanAmount","type":"uint256"}],"name":"REVLoans_NewBorrowAmountGreaterThanLoanAmount","type":"error"},{"inputs":[],"name":"REVLoans_NoMsgValueAllowed","type":"error"},{"inputs":[],"name":"REVLoans_NotEnoughCollateral","type":"error"},{"inputs":[{"internalType":"uint256","name":"maxRepayBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"repayBorrowAmount","type":"uint256"}],"name":"REVLoans_OverMaxRepayBorrowAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"}],"name":"REVLoans_OverflowAlert","type":"error"},{"inputs":[{"internalType":"uint256","name":"allowanceAmount","type":"uint256"},{"internalType":"uint256","name":"requiredAmount","type":"uint256"}],"name":"REVLoans_PermitAllowanceNotEnough","type":"error"},{"inputs":[{"internalType":"uint256","name":"newBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"loanAmount","type":"uint256"}],"name":"REVLoans_ReallocatingMoreCollateralThanBorrowedAmountAllows","type":"error"},{"inputs":[{"internalType":"address","name":"revnetOwner","type":"address"},{"internalType":"address","name":"revnets","type":"address"}],"name":"REVLoans_RevnetsMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"REVLoans_Unauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"minBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"REVLoans_UnderMinBorrowAmount","type":"error"},{"inputs":[],"name":"REVLoans_ZeroCollateralLoanIsInvalid","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"revnetId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"indexed":false,"internalType":"struct REVLoan","name":"loan","type":"tuple"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"indexed":false,"internalType":"struct REVLoanSource","name":"source","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralCount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sourceFeeAmount","type":"uint256"},{"indexed":false,"internalType":"address payable","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"revnetId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"indexed":false,"internalType":"struct REVLoan","name":"loan","type":"tuple"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"revnetId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"reallocatedLoanId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"indexed":false,"internalType":"struct REVLoan","name":"reallocatedLoan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"removedcollateralCount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"ReallocateCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"loanId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"revnetId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"paidOffLoanId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"indexed":false,"internalType":"struct REVLoan","name":"loan","type":"tuple"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"indexed":false,"internalType":"struct REVLoan","name":"paidOffLoan","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"repayBorrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sourceFeeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralCountToReturn","type":"uint256"},{"indexed":false,"internalType":"address payable","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RepayLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IJBTokenUriResolver","name":"resolver","type":"address"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"SetTokenUriResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"CONTROLLER","outputs":[{"internalType":"contract IJBController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DIRECTORY","outputs":[{"internalType":"contract IJBDirectory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOAN_LIQUIDATION_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PREPAID_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_PREPAID_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IPermit2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICES","outputs":[{"internalType":"contract IJBPrices","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROJECTS","outputs":[{"internalType":"contract IJBProjects","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REVNETS","outputs":[{"internalType":"contract IREVDeployer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REV_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REV_PREPAID_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"},{"internalType":"uint256","name":"minBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"collateralCount","type":"uint256"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"prepaidFeePercent","type":"uint256"}],"name":"borrowFrom","outputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"},{"internalType":"uint256","name":"collateralCount","type":"uint256"},{"internalType":"uint256","name":"decimals","type":"uint256"},{"internalType":"uint256","name":"currency","type":"uint256"}],"name":"borrowableAmountFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"loan","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"determineSourceFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"isLoanSourceOf","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"},{"internalType":"uint256","name":"startingLoanId","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"liquidateExpiredLoansFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"loanOf","outputs":[{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"}],"name":"loanSourcesOf","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"}],"name":"numberOfLoansFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"collateralCountToTransfer","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"},{"internalType":"uint256","name":"minBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"collateralCountToAdd","type":"uint256"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"prepaidFeePercent","type":"uint256"}],"name":"reallocateCollateralFromLoan","outputs":[{"internalType":"uint256","name":"reallocatedLoanId","type":"uint256"},{"internalType":"uint256","name":"newLoanId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"reallocatedLoan","type":"tuple"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"newLoan","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"},{"internalType":"uint256","name":"maxRepayBorrowAmount","type":"uint256"},{"internalType":"uint256","name":"collateralCountToReturn","type":"uint256"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"components":[{"internalType":"uint256","name":"sigDeadline","type":"uint256"},{"internalType":"uint160","name":"amount","type":"uint160"},{"internalType":"uint48","name":"expiration","type":"uint48"},{"internalType":"uint48","name":"nonce","type":"uint48"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct JBSingleAllowance","name":"allowance","type":"tuple"}],"name":"repayLoan","outputs":[{"internalType":"uint256","name":"paidOffLoanId","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"collateral","type":"uint112"},{"internalType":"uint48","name":"createdAt","type":"uint48"},{"internalType":"uint16","name":"prepaidFeePercent","type":"uint16"},{"internalType":"uint32","name":"prepaidDuration","type":"uint32"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"}],"internalType":"struct REVLoanSource","name":"source","type":"tuple"}],"internalType":"struct REVLoan","name":"paidOffloan","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"revnetIdOfLoanWith","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IJBTokenUriResolver","name":"resolver","type":"address"}],"name":"setTokenUriResolver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenUriResolver","outputs":[{"internalType":"contract IJBTokenUriResolver","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"},{"internalType":"contract IJBPayoutTerminal","name":"terminal","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"totalBorrowedFrom","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"revnetId","type":"uint256"}],"name":"totalCollateralOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

61018060405234801562000011575f80fd5b5060405162005b4038038062005b408339810160408190526200003491620003b1565b828160405180604001604052806009815260200168524556204c6f616e7360b81b81525060405180604001604052806008815260200167122922ab2627a0a760c11b815250815f9081620000899190620004bf565b506001620000988282620004bf565b5050506001600160a01b039081166080528116620000cf57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b620000da8162000348565b506001600160a01b03851660e08190526040805163ee0fc12160e01b8152905163ee0fc121916004808201926020929091908290030181865afa15801562000124573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200014a91906200058b565b6001600160a01b031660c0816001600160a01b031681525050846001600160a01b03166388bc2ef36040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001a0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001c691906200058b565b6001600160a01b0316610100816001600160a01b031681525050846001600160a01b031663ee0fc1216040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200021d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200024391906200058b565b6001600160a01b0316631eabcd346040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200027f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002a591906200058b565b6001600160a01b0316610120816001600160a01b031681525050846001600160a01b031663293c49996040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002fc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200032291906200058b565b6001600160a01b0390811661014052610160949094525090911660a05250620005b09050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b6001600160a01b0381168114620003ae575f80fd5b50565b5f805f805f60a08688031215620003c6575f80fd5b8551620003d38162000399565b602087015160408801519196509450620003ed8162000399565b6060870151909350620004008162000399565b6080870151909250620004138162000399565b809150509295509295909350565b634e487b7160e01b5f52604160045260245ffd5b600181811c908216806200044a57607f821691505b6020821081036200046957634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620004ba57805f5260205f20601f840160051c81016020851015620004965750805b601f840160051c820191505b81811015620004b7575f8155600101620004a2565b50505b505050565b81516001600160401b03811115620004db57620004db62000421565b620004f381620004ec845462000435565b846200046f565b602080601f83116001811462000529575f8415620005115750858301515b5f19600386901b1c1916600185901b17855562000583565b5f85815260208120601f198616915b82811015620005595788860151825594840194600190910190840162000538565b50858210156200057757878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b5f602082840312156200059c575f80fd5b8151620005a98162000399565b9392505050565b60805160a05160c05160e051610100516101205161014051610160516154af620006915f395f81816104750152818161296201528181612c25015281816137ab015261394901525f818161042d01526111ec01525f81816103bc0152613e1001525f818161068f0152818161181301528181611b95015261299a01525f81816109270152818161126101526112b701525f81816108b601528181612e6601528181612f5c015281816139fe0152613b4601525f81816105d801528181611e0201526123ea01525f81816105390152818161063b015261314601526154af5ff3fe608060405260043610610286575f3560e01c806370a0823111610151578063b88d4fde116100bc578063e4ba13c711610083578063f2fde38b11610060578063f2fde38b146108d8578063f8231e39146108f7578063fe91edd61461091657005b8063e4ba13c714610832578063e985e9c51461085e578063ee0fc121146108a557005b8063b88d4fde1461076b578063c4d8ea3c1461078a578063c87b56dd146107b5578063ddad5271146107d4578063e131fc0c1461081357005b80638da5cb5b11610118578063a22cb465116100f5578063a22cb4651461070e578063a33853d11461072d578063a4a0481a1461074c57005b80638da5cb5b146106b157806395d89b41146106ce578063a21cd6f8146106e257005b806370a08231146105fa578063715018a6146106195780637da0a8771461062d57806383b4cf2f1461065f57806388bc2ef31461067e57005b80632cc2cb63116101f157806350115869116101b8578063667079891161019557806366707989146105885780636a57728f146105b35780636afdd850146105c757005b806350115869146104e1578063572b6c051461051d5780636352211e1461056957005b80632cc2cb631461044f57806331011c5414610464578063325eefd9146104975780633e73ec33146104ab57806342842e0e146104c257005b806309c586211161024d57806323b872dd1161022a57806323b872dd146103de5780632407497e146103fd578063293c49991461041c57005b806309c58621146103675780630ba8827d146103885780631eabcd34146103ab57005b806301ffc9a71461028f5780630582928e146102c357806306fdde03146102f0578063081812fc14610311578063095ea7b31461034857005b3661028d57005b005b34801561029a575f80fd5b506102ae6102a93660046141ee565b610949565b60405190151581526020015b60405180910390f35b3480156102ce575f80fd5b506102e26102dd366004614209565b61099a565b6040519081526020016102ba565b3480156102fb575f80fd5b506103046109aa565b6040516102ba919061426d565b34801561031c575f80fd5b5061033061032b366004614209565b610a39565b6040516001600160a01b0390911681526020016102ba565b348015610353575f80fd5b5061028d610362366004614293565b610a60565b61037a6103753660046142bd565b610a76565b6040516102ba92919061439e565b61039b6103963660046143c9565b610d8f565b6040516102ba949392919061442d565b3480156103b6575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b3480156103e9575f80fd5b5061028d6103f8366004614457565b611011565b348015610408575f80fd5b5061028d610417366004614495565b6110a1565b348015610427575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b34801561045a575f80fd5b506102e26101f481565b34801561046f575f80fd5b506102e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156104a2575f80fd5b506102e2601981565b3480156104b6575f80fd5b506102e26312cc030081565b3480156104cd575f80fd5b5061028d6104dc366004614457565b61110e565b3480156104ec575f80fd5b506102e26104fb3660046144b0565b600a60209081525f938452604080852082529284528284209052825290205481565b348015610528575f80fd5b506102ae610537366004614495565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b348015610574575f80fd5b50610330610583366004614209565b61112d565b348015610593575f80fd5b506102e26105a2366004614209565b600b6020525f908152604090205481565b3480156105be575f80fd5b506102e2600a81565b3480156105d2575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b348015610605575f80fd5b506102e2610614366004614495565b611137565b348015610624575f80fd5b5061028d61117c565b348015610638575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610330565b34801561066a575f80fd5b5061037a6106793660046144ef565b61118f565b348015610689575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b3480156106bc575f80fd5b506006546001600160a01b0316610330565b3480156106d9575f80fd5b5061030461153b565b3480156106ed575f80fd5b506107016106fc366004614209565b61154a565b6040516102ba9190614548565b348015610719575f80fd5b5061028d6107283660046145b7565b6115d1565b348015610738575f80fd5b5061028d6107473660046145ee565b6115e3565b348015610757575f80fd5b506102e2610766366004614617565b611809565b348015610776575f80fd5b5061028d610785366004614798565b6118b0565b348015610795575f80fd5b506102e26107a4366004614209565b60086020525f908152604090205481565b3480156107c0575f80fd5b506103046107cf366004614209565b6118cf565b3480156107df575f80fd5b506102ae6107ee3660046144b0565b600760209081525f938452604080852082529284528284209052825290205460ff1681565b34801561081e575f80fd5b50600954610330906001600160a01b031681565b34801561083d575f80fd5b5061085161084c366004614209565b611968565b6040516102ba9190614800565b348015610869575f80fd5b506102ae61087836600461480e565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b3480156108b0575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b3480156108e3575f80fd5b5061028d6108f2366004614495565b611a4b565b348015610902575f80fd5b506102e2610911366004614881565b611a88565b348015610921575f80fd5b506103307f000000000000000000000000000000000000000000000000000000000000000081565b5f6001600160e01b031982166380ac58cd60e01b148061097957506001600160e01b03198216635b5e139f60e01b145b8061099457506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f61099464e8d4a510008361497e565b60605f80546109b89061499d565b80601f01602080910402602001604051908101604052809291908181526020018280546109e49061499d565b8015610a2f5780601f10610a0657610100808354040283529160200191610a2f565b820191905f5260205f20905b815481529060010190602001808311610a1257829003601f168201915b5050505050905090565b5f610a4382611a93565b505f828152600460205260409020546001600160a01b0316610994565b610a728282610a6d611acb565b611ad9565b5050565b5f610abb6040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610ac3611acb565b6001600160a01b0316610aea885f908152600260205260409020546001600160a01b031690565b6001600160a01b031614610b4957610b00611acb565b5f888152600260205260409020546001600160a01b03165b6040516318d514bd60e21b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b5f878152600d602052604090208054600160701b90046001600160701b0316861115610ba357805460405163b9c5041760e01b815260048101889052600160701b9091046001600160701b03166024820152604401610b40565b5f610bad8961099a565b82549091505f90610bdc9084908490610bd7908c90600160701b90046001600160701b03166149cf565b611ae6565b83549091506001600160701b0316811115610c1f5782546040516395e361d560e01b8152600481018390526001600160701b039091166024820152604401610b40565b82545f90610c379083906001600160701b03166149cf565b6040805160c08101825286546001600160701b038082168352600160701b90910416602080830191909152600188015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028801546001600160a01b0390811684526003890154169083015260a08101919091529091505f90610cd19083611c2a565b9050610cdd81836149e2565b6002860154909250610d01906001600160a01b03168c610cfc8b6149f5565b611d12565b9a508a821115610d2e5760405163c4d4ea0d60e01b8152600481018c905260248101839052604401610b40565b610d3d8c868685858f8f611eee565b9097509550818b1115610d8057610d8030610d56611acb565b876002015f015f9054906101000a90046001600160a01b0316858f610d7b91906149cf565b6122aa565b50505050509550959350505050565b5f80610dd56040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610e196040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610e21611acb565b6001600160a01b0316610e488c5f908152600260205260409020546001600160a01b031690565b6001600160a01b031614610e7a57610e5e611acb565b5f8c8152600260205260409020546001600160a01b0316610b18565b5f610e848c61099a565b9050610e918c828d612447565b806040518060c00160405290815f82015f9054906101000a90046001600160701b03166001600160701b03166001600160701b031681526020015f8201600e9054906101000a90046001600160701b03166001600160701b03166001600160701b03168152602001600182015f9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160069054906101000a900461ffff1661ffff1661ffff1681526020016001820160089054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600282016040518060400160405290815f82015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600182015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b0316815250508152505090508094508196505050610ffb818b8b8b8f610ff491906149e2565b8b8b61118f565b959d909c50929a50939850909650505050505050565b6001600160a01b03821661103a57604051633250574960e11b81525f6004820152602401610b40565b5f61104d8383611048611acb565b6126a3565b9050836001600160a01b0316816001600160a01b03161461109b576040516364283d7b60e01b81526001600160a01b0380861660048301526024820184905282166044820152606401610b40565b50505050565b6110a9612795565b600980546001600160a01b0319166001600160a01b0383169081179091557fe7784d93cfbfa4408e19577e6cc0436f4dbb51214b70e100905dfce9def88c166110f0611acb565b6040516001600160a01b03909116815260200160405180910390a250565b61112883838360405180602001604052805f8152506118b0565b505050565b5f61099482611a93565b5f6001600160a01b038216611161576040516322718ad960e21b81525f6004820152602401610b40565b506001600160a01b03165f9081526003602052604090205490565b611184612795565b61118d5f6127f3565b565b5f6111d46040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b6040516331a9108f60e11b8152600481018990525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636352211e90602401602060405180830381865afa158015611239573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125d9190614aaf565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316146112e457604051630128886560e71b81526001600160a01b0380831660048301527f0000000000000000000000000000000000000000000000000000000000000000166024820152604401610b40565b855f0361130457604051631a39845d60e21b815260040160405180910390fd5b601984108061131457506101f484115b1561134457604051633a54873960e21b815260048101859052601960248201526101f46044820152606401610b40565b5f898152600860205260408120805461136d928c929161136390614aca565b9182905550612844565b5f818152600d60205260409020909350886002820161138c8282614ae2565b505060018101805461ffff8716600160301b0267ffffffffffffffff1990911664ffffffffff4216171790556113c9856312cc03006101f461285f565b8160010160086101000a81548163ffffffff021916908363ffffffff1602179055505f6113f7828c8a611ae6565b905088811015611424576040516304aefef560e51b8152600481018a905260248101829052604401610b40565b5f61142f828861292e565b905061143f838d848c858d61293c565b61145061144a611acb565b87612d0d565b8b867f90ac858b09db4f44113a984e09c297b5c0529be26c65a23a1b8967d4124ca5e4858e868e878f611481611acb565b6040516114949796959493929190614ba8565b60405180910390a350506040805160c08101825282546001600160701b038082168352600160701b90910416602080830191909152600184015465ffffffffffff81168385015261ffff600160301b820416606084015263ffffffff600160401b909104166080830152825180840190935260028401546001600160a01b0390811684526003909401549093169282019290925260a0820152915050965096945050505050565b6060600180546109b89061499d565b6060600c5f8381526020019081526020015f20805480602002602001604051908101604052809291908181526020015f905b828210156115c6575f848152602090819020604080518082019091526002850290910180546001600160a01b0390811683526001918201541682840152908352909201910161157c565b505050509050919050565b610a726115dc611acb565b8383612d6e565b5f5b8181101561109b575f611601856115fc84876149e2565b612844565b5f818152600d60209081526040808320815160c08101835281546001600160701b038082168352600160701b9091041681850152600182015465ffffffffffff808216838601908152600160301b830461ffff166060850152600160401b90920463ffffffff166080840152845180860190955260028401546001600160a01b0390811686526003909401549093169484019490945260a08101929092529151939450921690036116b357505061109b565b5f828152600260205260409020546001600160a01b03168015806116f257506312cc0300826040015165ffffffffffff166116ee91906149e2565b4211155b156116ff57505050611801565b61170883612e0c565b60208201516001600160701b03161561174e5781602001516001600160701b0316600b5f8981526020019081526020015f205f82825461174891906149cf565b90915550505b81516001600160701b0316156117bb5781515f888152600a6020908152604080832060a0870180518401516001600160a01b0390811686529184528285209051519091168452909152812080546001600160701b03909316929091906117b59084906149cf565b90915550505b86837f2710408375e429523d39f7c37d7d73c1b816756d511f9cb07cd54215ead59f80846117e7611acb565b6040516117f5929190614c1f565b60405180910390a35050505b6001016115e5565b5f6118a5858585857f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d17541538b6040518263ffffffff1660e01b815260040161185f91815260200190565b5f60405180830381865afa158015611879573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526118a09190810190614c44565b612e44565b90505b949350505050565b6118bb848484611011565b61109b6118c6611acb565b8585858561300f565b6009546060906001600160a01b0316806118f857505060408051602081019091525f8152919050565b604051636d02a25560e11b8152600481018490526001600160a01b0382169063da0544aa906024015f60405180830381865afa15801561193a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119619190810190614cf1565b9392505050565b6119ac6040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b505f908152600d6020908152604091829020825160c08101845281546001600160701b038082168352600160701b9091041681840152600182015465ffffffffffff811682860152600160301b810461ffff166060830152600160401b900463ffffffff166080820152835180850190945260028201546001600160a01b0390811685526003909201549091169183019190915260a081019190915290565b611a53612795565b6001600160a01b038116611a7c57604051631e4fbdf760e01b81525f6004820152602401610b40565b611a85816127f3565b50565b5f6119618383611c2a565b5f818152600260205260408120546001600160a01b03168061099457604051637e27328960e01b815260048101849052602401610b40565b5f611ad4613137565b905090565b61112883838360016131aa565b5f815f03611af557505f611961565b60038401546002850154604051633a01714f60e01b8152600481018690526001600160a01b0391821660248201525f929190911690633a01714f90604401606060405180830381865afa158015611b4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b729190614d65565b60405163d175415360e01b8152600481018690529091505f906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063d1754153906024015f60405180830381865afa158015611bd9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611c009190810190614c44565b9050611c208585846020015160ff16856040015163ffffffff1685612e44565b9695505050505050565b5f80836040015165ffffffffffff1642611c4491906149cf565b9050836080015163ffffffff16811115611d0b576312cc0300811115611c8a5760405163045be49160e01b8152600481018290526312cc03006024820152604401610b40565b5f611ca9855f01516001600160701b0316866060015161ffff1661292e565b90505f611cdd82875f01516001600160701b0316611cc791906149cf565b611cd8856103e86312cc030061285f565b61292e565b90505f81875f01516001600160701b0316611cf891906149e2565b9050611d0582878361285f565b94505050505b5092915050565b5f61eeed196001600160a01b03851601611d2d575034611961565b3415611d4c576040516314ac44cf60e21b815260040160405180910390fd5b60208201516001600160a01b031615611eb2578282602001516001600160a01b03161015611da55760208201516040516335c2b2a360e21b81526001600160a01b03909116600482015260248101849052604401610b40565b6040805160e0810182526001600160a01b038087166060808401918252602087810151841660808601528786015165ffffffffffff90811660a08701529188015190911660c08501529083523090830152845192820192909252907f000000000000000000000000000000000000000000000000000000000000000016632b67b570611e2f611acb565b8386608001516040518463ffffffff1660e01b8152600401611e5393929190614dda565b5f604051808303815f87803b158015611e6a575f80fd5b505af1925050508015611e7b575060015b611eb0573d808015611ea8576040519150601f19603f3d011682016040523d82523d5f602084013e611ead565b606091505b50505b505b5f611ebc856132cc565b9050611ed1611ec9611acb565b3087876122aa565b80611edb866132cc565b611ee591906149cf565b95945050505050565b5f611f336040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b611f3c89612e0c565b8754600160701b90046001600160701b0316840361204357611f6288885f80898861293c565b88878a7f791f716b9ed5747949be0a1d9657c61802806973871cbd7ffb8dbaff12b8b2c68b8c8b8b8b8b611f94611acb565b604051611fa79796959493929190614e4e565b60405180910390a450506040805160c08101825287546001600160701b038082168352600160701b90910416602080830191909152600189015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028901546001600160a01b03908116845260038a0154169083015260a0810191909152879061229e565b5f8781526008602052604081208054612063918a91849061136390614aca565b5f818152600d602052604090208a5481546001600160701b039182166dffffffffffffffffffffffffffff198216811784558d546001600160e01b031990921617600160701b91829004909216021781556001808c018054918301805465ffffffffffff90931665ffffffffffff19841681178255825467ffffffffffffffff1990941617600160301b9384900461ffff169093029290921780835590546bffffffff000000000000000019909116600160401b9182900463ffffffff169091021790556002808c015490820180546001600160a01b039283166001600160a01b0319918216179091556003808e015490840180549190931691161790559091506121ac818a6121738a8c6149cf565b845461218891906001600160701b03166149cf565b84546121a5908b90600160701b90046001600160701b03166149cf565b8b8a61293c565b6121bd6121b7611acb565b83612d0d565b81898c7f791f716b9ed5747949be0a1d9657c61802806973871cbd7ffb8dbaff12b8b2c68d858d8d8d8d6121ef611acb565b6040516122029796959493929190614e4e565b60405180910390a46040805160c08101825282546001600160701b038082168352600160701b90910416602080830191909152600184015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028401546001600160a01b0390811684526003909401549093169282019290925260a082015290925090505b97509795505050505050565b306001600160a01b038516036122f05761eeed196001600160a01b038316016122dc576122d78382613351565b61109b565b6122d76001600160a01b03831684836133dd565b604051636eb1769f60e11b81526001600160a01b03858116600483015230602483015282919084169063dd62ed3e90604401602060405180830381865afa15801561233d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123619190614ea4565b1061237b576122d76001600160a01b03831685858461343c565b6001600160a01b038111156123b357604051633459303960e01b8152600481018290526001600160a01b036024820152604401610b40565b604051631b63c28b60e11b81526001600160a01b0385811660048301528481166024830152828116604483015283811660648301527f000000000000000000000000000000000000000000000000000000000000000016906336c78516906084015f604051808303815f87803b15801561242b575f80fd5b505af115801561243d573d5f803e3d5ffd5b5050505050505050565b5f8061245285612e0c565b5f858152600d602052604090208054600160701b90046001600160701b03168411156124915760405163c459ec8360e01b815260040160405180910390fd5b80545f906124b0908690600160701b90046001600160701b03166149cf565b90505f6124be838884611ae6565b83549091506001600160701b0316811015612501578254604051631b5de0b760e31b8152600481018390526001600160701b039091166024820152604401610b40565b5f8781526008602052604081208054612520928a929161136390614aca565b5f818152600d60205260408120855481546dffffffffffffffffffffffffffff1981166001600160701b0392831690811784558854600160701b908190048416026001600160e01b0319909216171780835560018089018054918501805465ffffffffffff19811665ffffffffffff9094169384178255825461ffff600160301b91829004160267ffffffffffffffff1990911690931792909217808355905463ffffffff600160401b9182900416026bffffffff00000000000000001990911617905560028089015490840180546001600160a01b03199081166001600160a01b03938416179091556003808b0154908601805490921692169190911790559398509096506126429287928b929190911690869061263d611acb565b61293c565b61265361264d611acb565b86612d0d565b8487897f7d3a0daefe69f98b6718ad00d0a9cf2dd50902a8db1b484d4238a86fea6be7c6878a612681611acb565b60405161269093929190614ebb565b60405180910390a4505050935093915050565b5f828152600260205260408120546001600160a01b03908116908316156126cf576126cf818486613475565b6001600160a01b03811615612709576126ea5f855f806131aa565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615612737576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b61279d611acb565b6001600160a01b03166127b86006546001600160a01b031690565b6001600160a01b03161461118d576127ce611acb565b60405163118cdaa760e01b81526001600160a01b039091166004820152602401610b40565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f8161285564e8d4a5100085614ee8565b61196191906149e2565b5f80805f19858709858702925082811083820303915050805f036128965783828161288c5761288c614956565b0492505050611961565b8381106128c757604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610b40565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f61196183836103e861285f565b85546001600160701b0316841115612ab1576002860154604051630862026560e41b81527f000000000000000000000000000000000000000000000000000000000000000060048201526001600160a01b0391821660248201525f917f00000000000000000000000000000000000000000000000000000000000000001690638620265090604401602060405180830381865afa1580156129df573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a039190614aaf565b6040805160c08101825289546001600160701b03808216808452600160701b9092041660208084019190915260018c015465ffffffffffff811684860152600160301b810461ffff166060850152600160401b900463ffffffff166080840152835180850190945260028c01546001600160a01b03908116855260038d0154169084015260a0820192909252919250612aab91908890612aa390896149cf565b8685876134d9565b50612b65565b85546001600160701b0316841015612b65576040805160c08101825287546001600160701b03808216808452600160701b9092041660208084019190915260018a015465ffffffffffff811684860152600160301b810461ffff166060850152600160401b900463ffffffff166080840152835180850190945260028a01546001600160a01b03908116855260038b0154169084015260a0820192909252612b65918790612b609088906149cf565b6138bc565b8554600160701b90046001600160701b0316831115612baa578554612ba5908690612ba090600160701b90046001600160701b0316866149cf565b6139d2565b612bec565b8554600160701b90046001600160701b0316831015612bec578554612bec908690612be6908690600160701b90046001600160701b03166149cf565b83613abd565b8115612ce057600386015460028701545f91612c15916001600160a01b03918216911685613bb0565b60038801546002890154604080517f000000000000000000000000000000000000000000000000000000000000000060208201529394506001600160a01b039283169363fef432579386938c93911691899189915f91016040516020818303038152906040526040518863ffffffff1660e01b8152600401612c9c96959493929190614eff565b60206040518083038185885af1158015612cb8573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190612cdd9190614ea4565b50505b505083546001600160701b03918216600160701b026001600160e01b031990911691909216171790915550565b6001600160a01b038216612d3657604051633250574960e11b81525f6004820152602401610b40565b5f612d4283835f6126a3565b90506001600160a01b03811615611128576040516339e3563760e11b81525f6004820152602401610b40565b6001600160a01b038216612da057604051630b61174360e31b81526001600160a01b0383166004820152602401610b40565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b5f612e185f835f6126a3565b90506001600160a01b038116610a7257604051637e27328960e01b815260048101839052602401610b40565b6040516320c94b1360e11b8152600481018690525f9081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063419296269060240161038060405180830381865afa158015612eac573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ed09190615114565b5090505f612f2b888583604051908082528060200260200182016040528015612f2357816020015b604080516060810182525f80825260208083018290529282015282525f19909201910181612ef85790505b508989613be8565b90505f612f39898888613c9f565b604051634da781a960e01b8152600481018b90529091505f906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690634da781a990602401602060405180830381865afa158015612fa1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fc59190614ea4565b5f8b8152600b6020526040902054909150613000612fe384866149e2565b8b612fee84866149e2565b61010089015160141c61ffff16613f05565b9b9a5050505050505050505050565b6001600160a01b0383163b1561313057604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906130519088908890879087906004016151e8565b6020604051808303815f875af192505050801561308b575060408051601f3d908101601f1916820190925261308891810190615219565b60015b6130f2573d8080156130b8576040519150601f19603f3d011682016040523d82523d5f602084013e6130bd565b606091505b5080515f036130ea57604051633250574960e11b81526001600160a01b0385166004820152602401610b40565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b1461312e57604051633250574960e11b81526001600160a01b0385166004820152602401610b40565b505b5050505050565b5f366014336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156131735750808210155b156131a2575f3661318483856149cf565b61318f928290615234565b6131989161525b565b60601c9250505090565b339250505090565b80806131be57506001600160a01b03821615155b1561329d575f6131cd84611a93565b90506001600160a01b038316158015906131f95750826001600160a01b0316816001600160a01b031614155b801561322a57506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b156132535760405163a9fbf51f60e01b81526001600160a01b0384166004820152602401610b40565b811561329b5783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b03821661eeee1461334a576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015613321573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133459190614ea4565b610994565b4792915050565b8047101561337b5760405163cf47918160e01b815247600482015260248101829052604401610b40565b5f80836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146133c5576040519150601f19603f3d011682016040523d82523d5f602084013e6133ca565b606091505b50915091508161109b5761109b81613f6e565b6040516001600160a01b0383811660248301526044820183905261112891859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f97565b6040516001600160a01b03848116602483015283811660448301526064820183905261109b9186918216906323b872dd9060840161340a565b613480838383614003565b611128576001600160a01b0383166134ae57604051637e27328960e01b815260048101829052602401610b40565b60405163177e802f60e01b81526001600160a01b038316600482015260248101829052604401610b40565b5f85815260076020908152604080832060a08a0180518401516001600160a01b039081168652918452828520905151909116845290915290205460ff166135c4575f85815260076020908152604080832060a08a0180518401516001600160a01b039081168652918452828520815151831686528452828520805460ff191660019081179091558a8652600c85528386208451808601909552825151841685529151850151831684860190815282548083018455928752949095209251600290910290920180549282166001600160a01b031993841617815592519290930180549290931691161790555b5f858152600a6020908152604080832060a08a0180518401516001600160a01b03908116865291845282852090515190911684529091528120805486929061360d9084906149e2565b909155505060a086015160208101519051604051633a01714f60e01b8152600481018890526001600160a01b0391821660248201525f9283921690633a01714f90604401606060405180830381865afa15801561366c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136909190614d65565b60a0890151602081015190516040838101519051631d23a08760e21b8152600481018c90526001600160a01b039283166024820152604481018b905263ffffffff90911660648201525f60848201523060a482015286821660c482015261010060e482015260196101048201527f4c656e64696e67206f757420746f206120626f72726f77657200000000000000610124820152929350169063748e821c90610144016020604051808303815f875af115801561374f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137739190614ea4565b9150505f61378286600a61292e565b90505f613797858a60a001515f015184613bb0565b9050846001600160a01b031663fef43257827f00000000000000000000000000000000000000000000000000000000000000008c60a001515f015186895f8f6040516020016137e891815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161381896959493929190614eff565b60206040518083038185885af193505050508015613853575060408051601f3d908101601f1916820190925261385091810190614ea4565b60015b61388c573d808015613880576040519150601f19603f3d011682016040523d82523d5f602084013e613885565b606091505b505061388e565b505b60a0890151516138b19030908690896138a787896149cf565b610d7b91906149cf565b505050505050505050565b5f828152600a6020908152604080832060a0870180518401516001600160a01b0390811686529184528285209051519091168452909152812080548392906139059084906149cf565b909155505060a0830151602081015190515f916139229184613bb0565b90508360a00151602001516001600160a01b0316639e6eec0582858760a001515f0151865f7f000000000000000000000000000000000000000000000000000000000000000060405160200161397a91815260200190565b6040516020818303038152906040526040518763ffffffff1660e01b81526004016139a9959493929190615290565b5f604051808303818588803b1580156139c0575f80fd5b505af11580156138b1573d5f803e3d5ffd5b5f828152600b6020526040812080548392906139ef9084906149e2565b90915550506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001663a2d532e6613a2b611acb565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044810184905260806064820152601960848201527f416464696e6720636f6c6c61746572616c20746f206c6f616e0000000000000060a482015260c4015f604051808303815f87803b158015613aab575f80fd5b505af115801561312e573d5f803e3d5ffd5b5f838152600b602052604081208054849290613ada9084906149cf565b90915550506040516363fdc96f60e11b815260048101849052602481018390526001600160a01b03828116604483015260a06064830152601d60a48301527f52656d6f76696e6720636f6c6c61746572616c2066726f6d206c6f616e00000060c48301525f60848301527f0000000000000000000000000000000000000000000000000000000000000000169063c7fb92de9060e4016020604051808303815f875af1158015613b8c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061109b9190614ea4565b5f61eeed196001600160a01b03841601613bcb575080611961565b613bdf6001600160a01b0384168584614083565b505f9392505050565b83515f90815b81811015613c9457868181518110613c0857613c086152fa565b60200260200101516001600160a01b031663edb40950898888886040518563ffffffff1660e01b8152600401613c41949392919061530e565b602060405180830381865afa158015613c5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c809190614ea4565b613c8a90846149e2565b9250600101613bee565b505095945050505050565b5f838152600c6020908152604080832080548251818502810185019093528083528493849084015b82821015613d11575f848152602090819020604080518082019091526002850290910180546001600160a01b03908116835260019182015416828401529083529092019101613cc7565b5050505090505f5b8151811015613efc575f828281518110613d3557613d356152fa565b602002602001015190505f81602001516001600160a01b0316633a01714f89845f01516040518363ffffffff1660e01b8152600401613d879291909182526001600160a01b0316602082015260400190565b606060405180830381865afa158015613da2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613dc69190614d65565b90505f86826040015163ffffffff1614613e86576040828101519051631df139fd60e11b8152600481018b905263ffffffff909116602482015260448101889052606481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690633be273fa90608401602060405180830381865afa158015613e5d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e819190614ea4565b613e91565b613e9188600a61546e565b5f8a8152600a60208181526040808420888301516001600160a01b0390811686529083528185208951909116855290915290912054919250613ee0908290613eda908c9061546e565b8461285f565b613eea90886149e2565b96505060019093019250613d19915050565b50509392505050565b5f61270f198201613f1757505f6118a8565b828410613f255750836118a8565b5f613f3186868661285f565b9050825f03613f415790506118a8565b611c2081613f5085888861285f565b613f5c866127106149cf565b613f6691906149e2565b61271061285f565b805115613f7e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f8060205f8451602086015f885af180613fb6576040513d5f823e3d81fd5b50505f513d91508115613fcd578060011415613fda565b6001600160a01b0384163b155b1561109b57604051635274afe760e01b81526001600160a01b0385166004820152602401610b40565b5f6001600160a01b038316158015906118a85750826001600160a01b0316846001600160a01b0316148061405b57506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806118a85750505f908152600460205260409020546001600160a01b03908116911614919050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa1580156140d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140f49190614ea4565b905061109b848461410585856149e2565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526141568482614194565b61109b576040516001600160a01b0384811660248301525f604483015261418a91869182169063095ea7b39060640161340a565b61109b8482613f97565b5f805f8060205f8651602088015f8a5af192503d91505f519050828015611c20575081156141c55780600114611c20565b50505050506001600160a01b03163b151590565b6001600160e01b031981168114611a85575f80fd5b5f602082840312156141fe575f80fd5b8135611961816141d9565b5f60208284031215614219575f80fd5b5035919050565b5f5b8381101561423a578181015183820152602001614222565b50505f910152565b5f8151808452614259816020860160208601614220565b601f01601f19169290920160200192915050565b602081525f6119616020830184614242565b6001600160a01b0381168114611a85575f80fd5b5f80604083850312156142a4575f80fd5b82356142af8161427f565b946020939093013593505050565b5f805f805f60a086880312156142d1575f80fd5b85359450602086013593506040860135925060608601356142f18161427f565b9150608086013567ffffffffffffffff81111561430c575f80fd5b860160a0818903121561431d575f80fd5b809150509295509295909350565b6001600160701b038082511683528060208301511660208401525065ffffffffffff604082015116604083015261ffff606082015116606083015263ffffffff608082015116608083015260a081015161112860a084018280516001600160a01b03908116835260209182015116910152565b8281526101008101611961602083018461432b565b5f604082840312156143c3575f80fd5b50919050565b5f805f805f805f610100888a0312156143e0575f80fd5b87359650602088013595506143f88960408a016143b3565b94506080880135935060a0880135925060c08801356144168161427f565b8092505060e0880135905092959891949750929550565b848152602081018490526102008101614449604083018561432b565b611ee561012083018461432b565b5f805f60608486031215614469575f80fd5b83356144748161427f565b925060208401356144848161427f565b929592945050506040919091013590565b5f602082840312156144a5575f80fd5b81356119618161427f565b5f805f606084860312156144c2575f80fd5b8335925060208401356144d48161427f565b915060408401356144e48161427f565b809150509250925092565b5f805f805f8060e08789031215614504575f80fd5b8635955061451588602089016143b3565b9450606087013593506080870135925060a08701356145338161427f565b8092505060c087013590509295509295509295565b602080825282518282018190525f919060409081850190868401855b8281101561459d5761458d84835180516001600160a01b03908116835260209182015116910152565b9284019290850190600101614564565b5091979650505050505050565b8015158114611a85575f80fd5b5f80604083850312156145c8575f80fd5b82356145d38161427f565b915060208301356145e3816145aa565b809150509250929050565b5f805f60608486031215614600575f80fd5b505081359360208301359350604090920135919050565b5f805f806080858703121561462a575f80fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b5f52604160045260245ffd5b60405160c0810167ffffffffffffffff8111828210171561467d5761467d614646565b60405290565b6040805190810167ffffffffffffffff8111828210171561467d5761467d614646565b604051610260810167ffffffffffffffff8111828210171561467d5761467d614646565b604051610120810167ffffffffffffffff8111828210171561467d5761467d614646565b604051601f8201601f1916810167ffffffffffffffff8111828210171561471757614717614646565b604052919050565b5f67ffffffffffffffff82111561473857614738614646565b50601f01601f191660200190565b5f82601f830112614755575f80fd5b81356147686147638261471f565b6146ee565b81815284602083860101111561477c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f80608085870312156147ab575f80fd5b84356147b68161427f565b935060208501356147c68161427f565b925060408501359150606085013567ffffffffffffffff8111156147e8575f80fd5b6147f487828801614746565b91505092959194509250565b60e08101610994828461432b565b5f806040838503121561481f575f80fd5b823561482a8161427f565b915060208301356145e38161427f565b6001600160701b0381168114611a85575f80fd5b65ffffffffffff81168114611a85575f80fd5b61ffff81168114611a85575f80fd5b63ffffffff81168114611a85575f80fd5b5f80828403610100811215614894575f80fd5b60e08112156148a1575f80fd5b6148a961465a565b84356148b48161483a565b815260208501356148c48161483a565b602082015260408501356148d78161484e565b604082015260608501356148ea81614861565b606082015260808501356148fd81614870565b60808201526040609f1983011215614913575f80fd5b61491b614683565b915060a085013561492b8161427f565b825260c085013561493b8161427f565b602083015260a08101919091529460e0939093013593505050565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8261499857634e487b7160e01b5f52601260045260245ffd5b500490565b600181811c908216806149b157607f821691505b6020821081036143c357634e487b7160e01b5f52602260045260245ffd5b818103818111156109945761099461496a565b808201808211156109945761099461496a565b5f60a08236031215614a05575f80fd5b60405160a0810167ffffffffffffffff8282108183111715614a2957614a29614646565b816040528435835260208501359150614a418261427f565b81602084015260408501359150614a578261484e565b81604084015260608501359150614a6d8261484e565b8160608401526080850135915080821115614a86575f80fd5b50614a9336828601614746565b60808301525092915050565b8051614aaa8161427f565b919050565b5f60208284031215614abf575f80fd5b81516119618161427f565b5f60018201614adb57614adb61496a565b5060010190565b8135614aed8161427f565b81546001600160a01b0319166001600160a01b038216178255506020820135614b158161427f565b6001820180546001600160a01b0319166001600160a01b038316179055505050565b80546001600160701b038082168452808260701c1660208501525050600181015465ffffffffffff8116604084015261ffff8160301c16606084015263ffffffff8160401c166080840152506001600160a01b038060028301541660a08401528060038301541660c0840152505050565b6101c08101614bb7828a614b37565b8735614bc28161427f565b6001600160a01b0390811660e0840152602089013590614be18261427f565b8082166101008501528861012085015287610140850152866101608501528086166101808501528085166101a0850152505098975050505050505050565b6101008101614c2e828561432b565b6001600160a01b03831660e08301529392505050565b5f6020808385031215614c55575f80fd5b825167ffffffffffffffff80821115614c6c575f80fd5b818501915085601f830112614c7f575f80fd5b815181811115614c9157614c91614646565b8060051b9150614ca28483016146ee565b8181529183018401918481019088841115614cbb575f80fd5b938501935b83851015614ce55784519250614cd58361427f565b8282529385019390850190614cc0565b98975050505050505050565b5f60208284031215614d01575f80fd5b815167ffffffffffffffff811115614d17575f80fd5b8201601f81018413614d27575f80fd5b8051614d356147638261471f565b818152856020838501011115614d49575f80fd5b611ee5826020830160208601614220565b8051614aaa81614870565b5f60608284031215614d75575f80fd5b6040516060810181811067ffffffffffffffff82111715614d9857614d98614646565b6040528251614da68161427f565b8152602083015160ff81168114614dbb575f80fd5b60208201526040830151614dce81614870565b60408201529392505050565b5f6101006001600160a01b0380871684528551818151166020860152816020820151166040860152604081015165ffffffffffff80821660608801528060608401511660808801525050508060208701511660a085015250604085015160c08401528060e0840152611c2081840185614242565b6102608101614e5d828a614b37565b614e6a60e0830189614b37565b6101c08201969096526101e08101949094526102008401929092526001600160a01b03908116610220840152166102409091015292915050565b5f60208284031215614eb4575f80fd5b5051919050565b6101208101614eca8286614b37565b8360e08301526001600160a01b038316610100830152949350505050565b80820281158282048414176109945761099461496a565b8681525f6001600160a01b03808816602084015286604084015280861660608401525083608083015260e060a0830152600d60e08301527f4665652066726f6d206c6f616e000000000000000000000000000000000000006101008301526101208060c0840152614f7281840185614242565b9998505050505050505050565b8051614aaa8161484e565b8051614aaa8161483a565b8051614aaa81614861565b8051614aaa816145aa565b5f6102608284031215614fbc575f80fd5b614fc46146a6565b9050614fcf82614f95565b8152614fdd60208301614f95565b6020820152614fee60408301614d5a565b6040820152614fff60608301614fa0565b606082015261501060808301614fa0565b608082015261502160a08301614fa0565b60a082015261503260c08301614fa0565b60c082015261504360e08301614fa0565b60e0820152610100615056818401614fa0565b90820152610120615068838201614fa0565b9082015261014061507a838201614fa0565b9082015261016061508c838201614fa0565b9082015261018061509e838201614fa0565b908201526101a06150b0838201614fa0565b908201526101c06150c2838201614fa0565b908201526101e06150d4838201614fa0565b908201526102006150e6838201614fa0565b908201526102206150f8838201614a9f565b9082015261024061510a838201614f95565b9082015292915050565b5f80828403610380811215615127575f80fd5b61012080821215615136575f80fd5b61513e6146ca565b915061514985614f7f565b825261515760208601614f7f565b602083015261516860408601614f7f565b604083015261517960608601614f7f565b606083015261518a60808601614d5a565b608083015261519b60a08601614f8a565b60a08301526151ac60c08601614d5a565b60c08301526151bd60e08601614a9f565b60e08301526101008086015181840152508193506151dd86828701614fab565b925050509250929050565b5f6001600160a01b03808716835280861660208401525083604083015260806060830152611c206080830184614242565b5f60208284031215615229575f80fd5b8151611961816141d9565b5f8085851115615242575f80fd5b8386111561524e575f80fd5b5050820193919092039150565b6bffffffffffffffffffffffff1981358181169160148510156152885780818660140360031b1b83161692505b505092915050565b8581526001600160a01b0385166020820152836040820152821515606082015260c06080820152600f60c08201527f506179696e67206f6666206c6f616e000000000000000000000000000000000060e08201525f6101008060a0840152614ce581840185614242565b634e487b7160e01b5f52603260045260245ffd5b5f6080820186835260206080602085015281875180845260a0860191506020890193505f5b8181101561537757845180516001600160a01b031684528481015160ff168585015260409081015163ffffffff169084015293830193606090920191600101615333565b505060408501969096525050506060015292915050565b600181815b808511156153c857815f19048211156153ae576153ae61496a565b808516156153bb57918102915b93841c9390800290615393565b509250929050565b5f826153de57506001610994565b816153ea57505f610994565b8160018114615400576002811461540a57615426565b6001915050610994565b60ff84111561541b5761541b61496a565b50506001821b610994565b5060208310610133831016604e8410600b8410161715615449575081810a610994565b615453838361538e565b805f19048211156154665761546661496a565b029392505050565b5f61196183836153d056fea2646970667358221220e145ee1192a76a4f7b508484f382df6004b5f8b563b102bd4069d7a00406d12964736f6c63430008170033000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc00000000000000000000000000000000000000000000000000000000000000030000000000000000000000006b92c73682f0e1fac35a18ab17efa5e77dde9fe1000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f887

Deployed Bytecode

0x608060405260043610610286575f3560e01c806370a0823111610151578063b88d4fde116100bc578063e4ba13c711610083578063f2fde38b11610060578063f2fde38b146108d8578063f8231e39146108f7578063fe91edd61461091657005b8063e4ba13c714610832578063e985e9c51461085e578063ee0fc121146108a557005b8063b88d4fde1461076b578063c4d8ea3c1461078a578063c87b56dd146107b5578063ddad5271146107d4578063e131fc0c1461081357005b80638da5cb5b11610118578063a22cb465116100f5578063a22cb4651461070e578063a33853d11461072d578063a4a0481a1461074c57005b80638da5cb5b146106b157806395d89b41146106ce578063a21cd6f8146106e257005b806370a08231146105fa578063715018a6146106195780637da0a8771461062d57806383b4cf2f1461065f57806388bc2ef31461067e57005b80632cc2cb63116101f157806350115869116101b8578063667079891161019557806366707989146105885780636a57728f146105b35780636afdd850146105c757005b806350115869146104e1578063572b6c051461051d5780636352211e1461056957005b80632cc2cb631461044f57806331011c5414610464578063325eefd9146104975780633e73ec33146104ab57806342842e0e146104c257005b806309c586211161024d57806323b872dd1161022a57806323b872dd146103de5780632407497e146103fd578063293c49991461041c57005b806309c58621146103675780630ba8827d146103885780631eabcd34146103ab57005b806301ffc9a71461028f5780630582928e146102c357806306fdde03146102f0578063081812fc14610311578063095ea7b31461034857005b3661028d57005b005b34801561029a575f80fd5b506102ae6102a93660046141ee565b610949565b60405190151581526020015b60405180910390f35b3480156102ce575f80fd5b506102e26102dd366004614209565b61099a565b6040519081526020016102ba565b3480156102fb575f80fd5b506103046109aa565b6040516102ba919061426d565b34801561031c575f80fd5b5061033061032b366004614209565b610a39565b6040516001600160a01b0390911681526020016102ba565b348015610353575f80fd5b5061028d610362366004614293565b610a60565b61037a6103753660046142bd565b610a76565b6040516102ba92919061439e565b61039b6103963660046143c9565b610d8f565b6040516102ba949392919061442d565b3480156103b6575f80fd5b506103307f000000000000000000000000e712d14b04f1a1fe464be930e3ea72b9b0a141d781565b3480156103e9575f80fd5b5061028d6103f8366004614457565b611011565b348015610408575f80fd5b5061028d610417366004614495565b6110a1565b348015610427575f80fd5b506103307f0000000000000000000000000b538a02610d7d3cc91ce2870f423e0a34d646ad81565b34801561045a575f80fd5b506102e26101f481565b34801561046f575f80fd5b506102e27f000000000000000000000000000000000000000000000000000000000000000381565b3480156104a2575f80fd5b506102e2601981565b3480156104b6575f80fd5b506102e26312cc030081565b3480156104cd575f80fd5b5061028d6104dc366004614457565b61110e565b3480156104ec575f80fd5b506102e26104fb3660046144b0565b600a60209081525f938452604080852082529284528284209052825290205481565b348015610528575f80fd5b506102ae610537366004614495565b7f0000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f8876001600160a01b0390811691161490565b348015610574575f80fd5b50610330610583366004614209565b61112d565b348015610593575f80fd5b506102e26105a2366004614209565b600b6020525f908152604090205481565b3480156105be575f80fd5b506102e2600a81565b3480156105d2575f80fd5b506103307f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b348015610605575f80fd5b506102e2610614366004614495565b611137565b348015610624575f80fd5b5061028d61117c565b348015610638575f80fd5b507f0000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f887610330565b34801561066a575f80fd5b5061037a6106793660046144ef565b61118f565b348015610689575f80fd5b506103307f0000000000000000000000000bc9f153dee4d3d474ce0903775b9b2aaae9aa4181565b3480156106bc575f80fd5b506006546001600160a01b0316610330565b3480156106d9575f80fd5b5061030461153b565b3480156106ed575f80fd5b506107016106fc366004614209565b61154a565b6040516102ba9190614548565b348015610719575f80fd5b5061028d6107283660046145b7565b6115d1565b348015610738575f80fd5b5061028d6107473660046145ee565b6115e3565b348015610757575f80fd5b506102e2610766366004614617565b611809565b348015610776575f80fd5b5061028d610785366004614798565b6118b0565b348015610795575f80fd5b506102e26107a4366004614209565b60086020525f908152604090205481565b3480156107c0575f80fd5b506103046107cf366004614209565b6118cf565b3480156107df575f80fd5b506102ae6107ee3660046144b0565b600760209081525f938452604080852082529284528284209052825290205460ff1681565b34801561081e575f80fd5b50600954610330906001600160a01b031681565b34801561083d575f80fd5b5061085161084c366004614209565b611968565b6040516102ba9190614800565b348015610869575f80fd5b506102ae61087836600461480e565b6001600160a01b039182165f90815260056020908152604080832093909416825291909152205460ff1690565b3480156108b0575f80fd5b506103307f000000000000000000000000b291844f213047eb9e1621ae555b1eae6700d55381565b3480156108e3575f80fd5b5061028d6108f2366004614495565b611a4b565b348015610902575f80fd5b506102e2610911366004614881565b611a88565b348015610921575f80fd5b506103307f000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc81565b5f6001600160e01b031982166380ac58cd60e01b148061097957506001600160e01b03198216635b5e139f60e01b145b8061099457506301ffc9a760e01b6001600160e01b03198316145b92915050565b5f61099464e8d4a510008361497e565b60605f80546109b89061499d565b80601f01602080910402602001604051908101604052809291908181526020018280546109e49061499d565b8015610a2f5780601f10610a0657610100808354040283529160200191610a2f565b820191905f5260205f20905b815481529060010190602001808311610a1257829003601f168201915b5050505050905090565b5f610a4382611a93565b505f828152600460205260409020546001600160a01b0316610994565b610a728282610a6d611acb565b611ad9565b5050565b5f610abb6040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610ac3611acb565b6001600160a01b0316610aea885f908152600260205260409020546001600160a01b031690565b6001600160a01b031614610b4957610b00611acb565b5f888152600260205260409020546001600160a01b03165b6040516318d514bd60e21b81526001600160a01b039283166004820152911660248201526044015b60405180910390fd5b5f878152600d602052604090208054600160701b90046001600160701b0316861115610ba357805460405163b9c5041760e01b815260048101889052600160701b9091046001600160701b03166024820152604401610b40565b5f610bad8961099a565b82549091505f90610bdc9084908490610bd7908c90600160701b90046001600160701b03166149cf565b611ae6565b83549091506001600160701b0316811115610c1f5782546040516395e361d560e01b8152600481018390526001600160701b039091166024820152604401610b40565b82545f90610c379083906001600160701b03166149cf565b6040805160c08101825286546001600160701b038082168352600160701b90910416602080830191909152600188015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028801546001600160a01b0390811684526003890154169083015260a08101919091529091505f90610cd19083611c2a565b9050610cdd81836149e2565b6002860154909250610d01906001600160a01b03168c610cfc8b6149f5565b611d12565b9a508a821115610d2e5760405163c4d4ea0d60e01b8152600481018c905260248101839052604401610b40565b610d3d8c868685858f8f611eee565b9097509550818b1115610d8057610d8030610d56611acb565b876002015f015f9054906101000a90046001600160a01b0316858f610d7b91906149cf565b6122aa565b50505050509550959350505050565b5f80610dd56040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610e196040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b610e21611acb565b6001600160a01b0316610e488c5f908152600260205260409020546001600160a01b031690565b6001600160a01b031614610e7a57610e5e611acb565b5f8c8152600260205260409020546001600160a01b0316610b18565b5f610e848c61099a565b9050610e918c828d612447565b806040518060c00160405290815f82015f9054906101000a90046001600160701b03166001600160701b03166001600160701b031681526020015f8201600e9054906101000a90046001600160701b03166001600160701b03166001600160701b03168152602001600182015f9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016001820160069054906101000a900461ffff1661ffff1661ffff1681526020016001820160089054906101000a900463ffffffff1663ffffffff1663ffffffff168152602001600282016040518060400160405290815f82015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b03168152602001600182015f9054906101000a90046001600160a01b03166001600160a01b03166001600160a01b0316815250508152505090508094508196505050610ffb818b8b8b8f610ff491906149e2565b8b8b61118f565b959d909c50929a50939850909650505050505050565b6001600160a01b03821661103a57604051633250574960e11b81525f6004820152602401610b40565b5f61104d8383611048611acb565b6126a3565b9050836001600160a01b0316816001600160a01b03161461109b576040516364283d7b60e01b81526001600160a01b0380861660048301526024820184905282166044820152606401610b40565b50505050565b6110a9612795565b600980546001600160a01b0319166001600160a01b0383169081179091557fe7784d93cfbfa4408e19577e6cc0436f4dbb51214b70e100905dfce9def88c166110f0611acb565b6040516001600160a01b03909116815260200160405180910390a250565b61112883838360405180602001604052805f8152506118b0565b505050565b5f61099482611a93565b5f6001600160a01b038216611161576040516322718ad960e21b81525f6004820152602401610b40565b506001600160a01b03165f9081526003602052604090205490565b611184612795565b61118d5f6127f3565b565b5f6111d46040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b6040516331a9108f60e11b8152600481018990525f907f0000000000000000000000000b538a02610d7d3cc91ce2870f423e0a34d646ad6001600160a01b031690636352211e90602401602060405180830381865afa158015611239573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125d9190614aaf565b90507f000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc6001600160a01b0316816001600160a01b0316146112e457604051630128886560e71b81526001600160a01b0380831660048301527f000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc166024820152604401610b40565b855f0361130457604051631a39845d60e21b815260040160405180910390fd5b601984108061131457506101f484115b1561134457604051633a54873960e21b815260048101859052601960248201526101f46044820152606401610b40565b5f898152600860205260408120805461136d928c929161136390614aca565b9182905550612844565b5f818152600d60205260409020909350886002820161138c8282614ae2565b505060018101805461ffff8716600160301b0267ffffffffffffffff1990911664ffffffffff4216171790556113c9856312cc03006101f461285f565b8160010160086101000a81548163ffffffff021916908363ffffffff1602179055505f6113f7828c8a611ae6565b905088811015611424576040516304aefef560e51b8152600481018a905260248101829052604401610b40565b5f61142f828861292e565b905061143f838d848c858d61293c565b61145061144a611acb565b87612d0d565b8b867f90ac858b09db4f44113a984e09c297b5c0529be26c65a23a1b8967d4124ca5e4858e868e878f611481611acb565b6040516114949796959493929190614ba8565b60405180910390a350506040805160c08101825282546001600160701b038082168352600160701b90910416602080830191909152600184015465ffffffffffff81168385015261ffff600160301b820416606084015263ffffffff600160401b909104166080830152825180840190935260028401546001600160a01b0390811684526003909401549093169282019290925260a0820152915050965096945050505050565b6060600180546109b89061499d565b6060600c5f8381526020019081526020015f20805480602002602001604051908101604052809291908181526020015f905b828210156115c6575f848152602090819020604080518082019091526002850290910180546001600160a01b0390811683526001918201541682840152908352909201910161157c565b505050509050919050565b610a726115dc611acb565b8383612d6e565b5f5b8181101561109b575f611601856115fc84876149e2565b612844565b5f818152600d60209081526040808320815160c08101835281546001600160701b038082168352600160701b9091041681850152600182015465ffffffffffff808216838601908152600160301b830461ffff166060850152600160401b90920463ffffffff166080840152845180860190955260028401546001600160a01b0390811686526003909401549093169484019490945260a08101929092529151939450921690036116b357505061109b565b5f828152600260205260409020546001600160a01b03168015806116f257506312cc0300826040015165ffffffffffff166116ee91906149e2565b4211155b156116ff57505050611801565b61170883612e0c565b60208201516001600160701b03161561174e5781602001516001600160701b0316600b5f8981526020019081526020015f205f82825461174891906149cf565b90915550505b81516001600160701b0316156117bb5781515f888152600a6020908152604080832060a0870180518401516001600160a01b0390811686529184528285209051519091168452909152812080546001600160701b03909316929091906117b59084906149cf565b90915550505b86837f2710408375e429523d39f7c37d7d73c1b816756d511f9cb07cd54215ead59f80846117e7611acb565b6040516117f5929190614c1f565b60405180910390a35050505b6001016115e5565b5f6118a5858585857f0000000000000000000000000bc9f153dee4d3d474ce0903775b9b2aaae9aa416001600160a01b031663d17541538b6040518263ffffffff1660e01b815260040161185f91815260200190565b5f60405180830381865afa158015611879573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526118a09190810190614c44565b612e44565b90505b949350505050565b6118bb848484611011565b61109b6118c6611acb565b8585858561300f565b6009546060906001600160a01b0316806118f857505060408051602081019091525f8152919050565b604051636d02a25560e11b8152600481018490526001600160a01b0382169063da0544aa906024015f60405180830381865afa15801561193a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119619190810190614cf1565b9392505050565b6119ac6040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b505f908152600d6020908152604091829020825160c08101845281546001600160701b038082168352600160701b9091041681840152600182015465ffffffffffff811682860152600160301b810461ffff166060830152600160401b900463ffffffff166080820152835180850190945260028201546001600160a01b0390811685526003909201549091169183019190915260a081019190915290565b611a53612795565b6001600160a01b038116611a7c57604051631e4fbdf760e01b81525f6004820152602401610b40565b611a85816127f3565b50565b5f6119618383611c2a565b5f818152600260205260408120546001600160a01b03168061099457604051637e27328960e01b815260048101849052602401610b40565b5f611ad4613137565b905090565b61112883838360016131aa565b5f815f03611af557505f611961565b60038401546002850154604051633a01714f60e01b8152600481018690526001600160a01b0391821660248201525f929190911690633a01714f90604401606060405180830381865afa158015611b4e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b729190614d65565b60405163d175415360e01b8152600481018690529091505f906001600160a01b037f0000000000000000000000000bc9f153dee4d3d474ce0903775b9b2aaae9aa41169063d1754153906024015f60405180830381865afa158015611bd9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611c009190810190614c44565b9050611c208585846020015160ff16856040015163ffffffff1685612e44565b9695505050505050565b5f80836040015165ffffffffffff1642611c4491906149cf565b9050836080015163ffffffff16811115611d0b576312cc0300811115611c8a5760405163045be49160e01b8152600481018290526312cc03006024820152604401610b40565b5f611ca9855f01516001600160701b0316866060015161ffff1661292e565b90505f611cdd82875f01516001600160701b0316611cc791906149cf565b611cd8856103e86312cc030061285f565b61292e565b90505f81875f01516001600160701b0316611cf891906149e2565b9050611d0582878361285f565b94505050505b5092915050565b5f61eeed196001600160a01b03851601611d2d575034611961565b3415611d4c576040516314ac44cf60e21b815260040160405180910390fd5b60208201516001600160a01b031615611eb2578282602001516001600160a01b03161015611da55760208201516040516335c2b2a360e21b81526001600160a01b03909116600482015260248101849052604401610b40565b6040805160e0810182526001600160a01b038087166060808401918252602087810151841660808601528786015165ffffffffffff90811660a08701529188015190911660c08501529083523090830152845192820192909252907f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba316632b67b570611e2f611acb565b8386608001516040518463ffffffff1660e01b8152600401611e5393929190614dda565b5f604051808303815f87803b158015611e6a575f80fd5b505af1925050508015611e7b575060015b611eb0573d808015611ea8576040519150601f19603f3d011682016040523d82523d5f602084013e611ead565b606091505b50505b505b5f611ebc856132cc565b9050611ed1611ec9611acb565b3087876122aa565b80611edb866132cc565b611ee591906149cf565b95945050505050565b5f611f336040805160c0810182525f8082526020808301829052828401829052606083018290526080830182905283518085019094528184528301529060a082015290565b611f3c89612e0c565b8754600160701b90046001600160701b0316840361204357611f6288885f80898861293c565b88878a7f791f716b9ed5747949be0a1d9657c61802806973871cbd7ffb8dbaff12b8b2c68b8c8b8b8b8b611f94611acb565b604051611fa79796959493929190614e4e565b60405180910390a450506040805160c08101825287546001600160701b038082168352600160701b90910416602080830191909152600189015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028901546001600160a01b03908116845260038a0154169083015260a0810191909152879061229e565b5f8781526008602052604081208054612063918a91849061136390614aca565b5f818152600d602052604090208a5481546001600160701b039182166dffffffffffffffffffffffffffff198216811784558d546001600160e01b031990921617600160701b91829004909216021781556001808c018054918301805465ffffffffffff90931665ffffffffffff19841681178255825467ffffffffffffffff1990941617600160301b9384900461ffff169093029290921780835590546bffffffff000000000000000019909116600160401b9182900463ffffffff169091021790556002808c015490820180546001600160a01b039283166001600160a01b0319918216179091556003808e015490840180549190931691161790559091506121ac818a6121738a8c6149cf565b845461218891906001600160701b03166149cf565b84546121a5908b90600160701b90046001600160701b03166149cf565b8b8a61293c565b6121bd6121b7611acb565b83612d0d565b81898c7f791f716b9ed5747949be0a1d9657c61802806973871cbd7ffb8dbaff12b8b2c68d858d8d8d8d6121ef611acb565b6040516122029796959493929190614e4e565b60405180910390a46040805160c08101825282546001600160701b038082168352600160701b90910416602080830191909152600184015465ffffffffffff811683850152600160301b810461ffff166060840152600160401b900463ffffffff166080830152825180840190935260028401546001600160a01b0390811684526003909401549093169282019290925260a082015290925090505b97509795505050505050565b306001600160a01b038516036122f05761eeed196001600160a01b038316016122dc576122d78382613351565b61109b565b6122d76001600160a01b03831684836133dd565b604051636eb1769f60e11b81526001600160a01b03858116600483015230602483015282919084169063dd62ed3e90604401602060405180830381865afa15801561233d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123619190614ea4565b1061237b576122d76001600160a01b03831685858461343c565b6001600160a01b038111156123b357604051633459303960e01b8152600481018290526001600160a01b036024820152604401610b40565b604051631b63c28b60e11b81526001600160a01b0385811660048301528481166024830152828116604483015283811660648301527f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba316906336c78516906084015f604051808303815f87803b15801561242b575f80fd5b505af115801561243d573d5f803e3d5ffd5b5050505050505050565b5f8061245285612e0c565b5f858152600d602052604090208054600160701b90046001600160701b03168411156124915760405163c459ec8360e01b815260040160405180910390fd5b80545f906124b0908690600160701b90046001600160701b03166149cf565b90505f6124be838884611ae6565b83549091506001600160701b0316811015612501578254604051631b5de0b760e31b8152600481018390526001600160701b039091166024820152604401610b40565b5f8781526008602052604081208054612520928a929161136390614aca565b5f818152600d60205260408120855481546dffffffffffffffffffffffffffff1981166001600160701b0392831690811784558854600160701b908190048416026001600160e01b0319909216171780835560018089018054918501805465ffffffffffff19811665ffffffffffff9094169384178255825461ffff600160301b91829004160267ffffffffffffffff1990911690931792909217808355905463ffffffff600160401b9182900416026bffffffff00000000000000001990911617905560028089015490840180546001600160a01b03199081166001600160a01b03938416179091556003808b0154908601805490921692169190911790559398509096506126429287928b929190911690869061263d611acb565b61293c565b61265361264d611acb565b86612d0d565b8487897f7d3a0daefe69f98b6718ad00d0a9cf2dd50902a8db1b484d4238a86fea6be7c6878a612681611acb565b60405161269093929190614ebb565b60405180910390a4505050935093915050565b5f828152600260205260408120546001600160a01b03908116908316156126cf576126cf818486613475565b6001600160a01b03811615612709576126ea5f855f806131aa565b6001600160a01b0381165f90815260036020526040902080545f190190555b6001600160a01b03851615612737576001600160a01b0385165f908152600360205260409020805460010190555b5f8481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b61279d611acb565b6001600160a01b03166127b86006546001600160a01b031690565b6001600160a01b03161461118d576127ce611acb565b60405163118cdaa760e01b81526001600160a01b039091166004820152602401610b40565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f8161285564e8d4a5100085614ee8565b61196191906149e2565b5f80805f19858709858702925082811083820303915050805f036128965783828161288c5761288c614956565b0492505050611961565b8381106128c757604051630c740aef60e31b8152600481018790526024810186905260448101859052606401610b40565b5f8486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091025f889003889004909101858311909403939093029303949094049190911702949350505050565b5f61196183836103e861285f565b85546001600160701b0316841115612ab1576002860154604051630862026560e41b81527f000000000000000000000000000000000000000000000000000000000000000360048201526001600160a01b0391821660248201525f917f0000000000000000000000000bc9f153dee4d3d474ce0903775b9b2aaae9aa411690638620265090604401602060405180830381865afa1580156129df573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a039190614aaf565b6040805160c08101825289546001600160701b03808216808452600160701b9092041660208084019190915260018c015465ffffffffffff811684860152600160301b810461ffff166060850152600160401b900463ffffffff166080840152835180850190945260028c01546001600160a01b03908116855260038d0154169084015260a0820192909252919250612aab91908890612aa390896149cf565b8685876134d9565b50612b65565b85546001600160701b0316841015612b65576040805160c08101825287546001600160701b03808216808452600160701b9092041660208084019190915260018a015465ffffffffffff811684860152600160301b810461ffff166060850152600160401b900463ffffffff166080840152835180850190945260028a01546001600160a01b03908116855260038b0154169084015260a0820192909252612b65918790612b609088906149cf565b6138bc565b8554600160701b90046001600160701b0316831115612baa578554612ba5908690612ba090600160701b90046001600160701b0316866149cf565b6139d2565b612bec565b8554600160701b90046001600160701b0316831015612bec578554612bec908690612be6908690600160701b90046001600160701b03166149cf565b83613abd565b8115612ce057600386015460028701545f91612c15916001600160a01b03918216911685613bb0565b60038801546002890154604080517f000000000000000000000000000000000000000000000000000000000000000360208201529394506001600160a01b039283169363fef432579386938c93911691899189915f91016040516020818303038152906040526040518863ffffffff1660e01b8152600401612c9c96959493929190614eff565b60206040518083038185885af1158015612cb8573d5f803e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190612cdd9190614ea4565b50505b505083546001600160701b03918216600160701b026001600160e01b031990911691909216171790915550565b6001600160a01b038216612d3657604051633250574960e11b81525f6004820152602401610b40565b5f612d4283835f6126a3565b90506001600160a01b03811615611128576040516339e3563760e11b81525f6004820152602401610b40565b6001600160a01b038216612da057604051630b61174360e31b81526001600160a01b0383166004820152602401610b40565b6001600160a01b038381165f81815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b5f612e185f835f6126a3565b90506001600160a01b038116610a7257604051637e27328960e01b815260048101839052602401610b40565b6040516320c94b1360e11b8152600481018690525f9081906001600160a01b037f000000000000000000000000b291844f213047eb9e1621ae555b1eae6700d553169063419296269060240161038060405180830381865afa158015612eac573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ed09190615114565b5090505f612f2b888583604051908082528060200260200182016040528015612f2357816020015b604080516060810182525f80825260208083018290529282015282525f19909201910181612ef85790505b508989613be8565b90505f612f39898888613c9f565b604051634da781a960e01b8152600481018b90529091505f906001600160a01b037f000000000000000000000000b291844f213047eb9e1621ae555b1eae6700d5531690634da781a990602401602060405180830381865afa158015612fa1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fc59190614ea4565b5f8b8152600b6020526040902054909150613000612fe384866149e2565b8b612fee84866149e2565b61010089015160141c61ffff16613f05565b9b9a5050505050505050505050565b6001600160a01b0383163b1561313057604051630a85bd0160e11b81526001600160a01b0384169063150b7a02906130519088908890879087906004016151e8565b6020604051808303815f875af192505050801561308b575060408051601f3d908101601f1916820190925261308891810190615219565b60015b6130f2573d8080156130b8576040519150601f19603f3d011682016040523d82523d5f602084013e6130bd565b606091505b5080515f036130ea57604051633250574960e11b81526001600160a01b0385166004820152602401610b40565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b1461312e57604051633250574960e11b81526001600160a01b0385166004820152602401610b40565b505b5050505050565b5f366014336001600160a01b037f0000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f887161480156131735750808210155b156131a2575f3661318483856149cf565b61318f928290615234565b6131989161525b565b60601c9250505090565b339250505090565b80806131be57506001600160a01b03821615155b1561329d575f6131cd84611a93565b90506001600160a01b038316158015906131f95750826001600160a01b0316816001600160a01b031614155b801561322a57506001600160a01b038082165f9081526005602090815260408083209387168352929052205460ff16155b156132535760405163a9fbf51f60e01b81526001600160a01b0384166004820152602401610b40565b811561329b5783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b50505f90815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b5f6001600160a01b03821661eeee1461334a576040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015613321573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133459190614ea4565b610994565b4792915050565b8047101561337b5760405163cf47918160e01b815247600482015260248101829052604401610b40565b5f80836001600160a01b0316836040515f6040518083038185875af1925050503d805f81146133c5576040519150601f19603f3d011682016040523d82523d5f602084013e6133ca565b606091505b50915091508161109b5761109b81613f6e565b6040516001600160a01b0383811660248301526044820183905261112891859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613f97565b6040516001600160a01b03848116602483015283811660448301526064820183905261109b9186918216906323b872dd9060840161340a565b613480838383614003565b611128576001600160a01b0383166134ae57604051637e27328960e01b815260048101829052602401610b40565b60405163177e802f60e01b81526001600160a01b038316600482015260248101829052604401610b40565b5f85815260076020908152604080832060a08a0180518401516001600160a01b039081168652918452828520905151909116845290915290205460ff166135c4575f85815260076020908152604080832060a08a0180518401516001600160a01b039081168652918452828520815151831686528452828520805460ff191660019081179091558a8652600c85528386208451808601909552825151841685529151850151831684860190815282548083018455928752949095209251600290910290920180549282166001600160a01b031993841617815592519290930180549290931691161790555b5f858152600a6020908152604080832060a08a0180518401516001600160a01b03908116865291845282852090515190911684529091528120805486929061360d9084906149e2565b909155505060a086015160208101519051604051633a01714f60e01b8152600481018890526001600160a01b0391821660248201525f9283921690633a01714f90604401606060405180830381865afa15801561366c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136909190614d65565b60a0890151602081015190516040838101519051631d23a08760e21b8152600481018c90526001600160a01b039283166024820152604481018b905263ffffffff90911660648201525f60848201523060a482015286821660c482015261010060e482015260196101048201527f4c656e64696e67206f757420746f206120626f72726f77657200000000000000610124820152929350169063748e821c90610144016020604051808303815f875af115801561374f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137739190614ea4565b9150505f61378286600a61292e565b90505f613797858a60a001515f015184613bb0565b9050846001600160a01b031663fef43257827f00000000000000000000000000000000000000000000000000000000000000038c60a001515f015186895f8f6040516020016137e891815260200190565b6040516020818303038152906040526040518863ffffffff1660e01b815260040161381896959493929190614eff565b60206040518083038185885af193505050508015613853575060408051601f3d908101601f1916820190925261385091810190614ea4565b60015b61388c573d808015613880576040519150601f19603f3d011682016040523d82523d5f602084013e613885565b606091505b505061388e565b505b60a0890151516138b19030908690896138a787896149cf565b610d7b91906149cf565b505050505050505050565b5f828152600a6020908152604080832060a0870180518401516001600160a01b0390811686529184528285209051519091168452909152812080548392906139059084906149cf565b909155505060a0830151602081015190515f916139229184613bb0565b90508360a00151602001516001600160a01b0316639e6eec0582858760a001515f0151865f7f000000000000000000000000000000000000000000000000000000000000000360405160200161397a91815260200190565b6040516020818303038152906040526040518763ffffffff1660e01b81526004016139a9959493929190615290565b5f604051808303818588803b1580156139c0575f80fd5b505af11580156138b1573d5f803e3d5ffd5b5f828152600b6020526040812080548392906139ef9084906149e2565b90915550506001600160a01b037f000000000000000000000000b291844f213047eb9e1621ae555b1eae6700d5531663a2d532e6613a2b611acb565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018590526044810184905260806064820152601960848201527f416464696e6720636f6c6c61746572616c20746f206c6f616e0000000000000060a482015260c4015f604051808303815f87803b158015613aab575f80fd5b505af115801561312e573d5f803e3d5ffd5b5f838152600b602052604081208054849290613ada9084906149cf565b90915550506040516363fdc96f60e11b815260048101849052602481018390526001600160a01b03828116604483015260a06064830152601d60a48301527f52656d6f76696e6720636f6c6c61746572616c2066726f6d206c6f616e00000060c48301525f60848301527f000000000000000000000000b291844f213047eb9e1621ae555b1eae6700d553169063c7fb92de9060e4016020604051808303815f875af1158015613b8c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061109b9190614ea4565b5f61eeed196001600160a01b03841601613bcb575080611961565b613bdf6001600160a01b0384168584614083565b505f9392505050565b83515f90815b81811015613c9457868181518110613c0857613c086152fa565b60200260200101516001600160a01b031663edb40950898888886040518563ffffffff1660e01b8152600401613c41949392919061530e565b602060405180830381865afa158015613c5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c809190614ea4565b613c8a90846149e2565b9250600101613bee565b505095945050505050565b5f838152600c6020908152604080832080548251818502810185019093528083528493849084015b82821015613d11575f848152602090819020604080518082019091526002850290910180546001600160a01b03908116835260019182015416828401529083529092019101613cc7565b5050505090505f5b8151811015613efc575f828281518110613d3557613d356152fa565b602002602001015190505f81602001516001600160a01b0316633a01714f89845f01516040518363ffffffff1660e01b8152600401613d879291909182526001600160a01b0316602082015260400190565b606060405180830381865afa158015613da2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613dc69190614d65565b90505f86826040015163ffffffff1614613e86576040828101519051631df139fd60e11b8152600481018b905263ffffffff909116602482015260448101889052606481018990527f000000000000000000000000e712d14b04f1a1fe464be930e3ea72b9b0a141d76001600160a01b031690633be273fa90608401602060405180830381865afa158015613e5d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e819190614ea4565b613e91565b613e9188600a61546e565b5f8a8152600a60208181526040808420888301516001600160a01b0390811686529083528185208951909116855290915290912054919250613ee0908290613eda908c9061546e565b8461285f565b613eea90886149e2565b96505060019093019250613d19915050565b50509392505050565b5f61270f198201613f1757505f6118a8565b828410613f255750836118a8565b5f613f3186868661285f565b9050825f03613f415790506118a8565b611c2081613f5085888861285f565b613f5c866127106149cf565b613f6691906149e2565b61271061285f565b805115613f7e5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f8060205f8451602086015f885af180613fb6576040513d5f823e3d81fd5b50505f513d91508115613fcd578060011415613fda565b6001600160a01b0384163b155b1561109b57604051635274afe760e01b81526001600160a01b0385166004820152602401610b40565b5f6001600160a01b038316158015906118a85750826001600160a01b0316846001600160a01b0316148061405b57506001600160a01b038085165f9081526005602090815260408083209387168352929052205460ff165b806118a85750505f908152600460205260409020546001600160a01b03908116911614919050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa1580156140d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906140f49190614ea4565b905061109b848461410585856149e2565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526141568482614194565b61109b576040516001600160a01b0384811660248301525f604483015261418a91869182169063095ea7b39060640161340a565b61109b8482613f97565b5f805f8060205f8651602088015f8a5af192503d91505f519050828015611c20575081156141c55780600114611c20565b50505050506001600160a01b03163b151590565b6001600160e01b031981168114611a85575f80fd5b5f602082840312156141fe575f80fd5b8135611961816141d9565b5f60208284031215614219575f80fd5b5035919050565b5f5b8381101561423a578181015183820152602001614222565b50505f910152565b5f8151808452614259816020860160208601614220565b601f01601f19169290920160200192915050565b602081525f6119616020830184614242565b6001600160a01b0381168114611a85575f80fd5b5f80604083850312156142a4575f80fd5b82356142af8161427f565b946020939093013593505050565b5f805f805f60a086880312156142d1575f80fd5b85359450602086013593506040860135925060608601356142f18161427f565b9150608086013567ffffffffffffffff81111561430c575f80fd5b860160a0818903121561431d575f80fd5b809150509295509295909350565b6001600160701b038082511683528060208301511660208401525065ffffffffffff604082015116604083015261ffff606082015116606083015263ffffffff608082015116608083015260a081015161112860a084018280516001600160a01b03908116835260209182015116910152565b8281526101008101611961602083018461432b565b5f604082840312156143c3575f80fd5b50919050565b5f805f805f805f610100888a0312156143e0575f80fd5b87359650602088013595506143f88960408a016143b3565b94506080880135935060a0880135925060c08801356144168161427f565b8092505060e0880135905092959891949750929550565b848152602081018490526102008101614449604083018561432b565b611ee561012083018461432b565b5f805f60608486031215614469575f80fd5b83356144748161427f565b925060208401356144848161427f565b929592945050506040919091013590565b5f602082840312156144a5575f80fd5b81356119618161427f565b5f805f606084860312156144c2575f80fd5b8335925060208401356144d48161427f565b915060408401356144e48161427f565b809150509250925092565b5f805f805f8060e08789031215614504575f80fd5b8635955061451588602089016143b3565b9450606087013593506080870135925060a08701356145338161427f565b8092505060c087013590509295509295509295565b602080825282518282018190525f919060409081850190868401855b8281101561459d5761458d84835180516001600160a01b03908116835260209182015116910152565b9284019290850190600101614564565b5091979650505050505050565b8015158114611a85575f80fd5b5f80604083850312156145c8575f80fd5b82356145d38161427f565b915060208301356145e3816145aa565b809150509250929050565b5f805f60608486031215614600575f80fd5b505081359360208301359350604090920135919050565b5f805f806080858703121561462a575f80fd5b5050823594602084013594506040840135936060013592509050565b634e487b7160e01b5f52604160045260245ffd5b60405160c0810167ffffffffffffffff8111828210171561467d5761467d614646565b60405290565b6040805190810167ffffffffffffffff8111828210171561467d5761467d614646565b604051610260810167ffffffffffffffff8111828210171561467d5761467d614646565b604051610120810167ffffffffffffffff8111828210171561467d5761467d614646565b604051601f8201601f1916810167ffffffffffffffff8111828210171561471757614717614646565b604052919050565b5f67ffffffffffffffff82111561473857614738614646565b50601f01601f191660200190565b5f82601f830112614755575f80fd5b81356147686147638261471f565b6146ee565b81815284602083860101111561477c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f805f80608085870312156147ab575f80fd5b84356147b68161427f565b935060208501356147c68161427f565b925060408501359150606085013567ffffffffffffffff8111156147e8575f80fd5b6147f487828801614746565b91505092959194509250565b60e08101610994828461432b565b5f806040838503121561481f575f80fd5b823561482a8161427f565b915060208301356145e38161427f565b6001600160701b0381168114611a85575f80fd5b65ffffffffffff81168114611a85575f80fd5b61ffff81168114611a85575f80fd5b63ffffffff81168114611a85575f80fd5b5f80828403610100811215614894575f80fd5b60e08112156148a1575f80fd5b6148a961465a565b84356148b48161483a565b815260208501356148c48161483a565b602082015260408501356148d78161484e565b604082015260608501356148ea81614861565b606082015260808501356148fd81614870565b60808201526040609f1983011215614913575f80fd5b61491b614683565b915060a085013561492b8161427f565b825260c085013561493b8161427f565b602083015260a08101919091529460e0939093013593505050565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f8261499857634e487b7160e01b5f52601260045260245ffd5b500490565b600181811c908216806149b157607f821691505b6020821081036143c357634e487b7160e01b5f52602260045260245ffd5b818103818111156109945761099461496a565b808201808211156109945761099461496a565b5f60a08236031215614a05575f80fd5b60405160a0810167ffffffffffffffff8282108183111715614a2957614a29614646565b816040528435835260208501359150614a418261427f565b81602084015260408501359150614a578261484e565b81604084015260608501359150614a6d8261484e565b8160608401526080850135915080821115614a86575f80fd5b50614a9336828601614746565b60808301525092915050565b8051614aaa8161427f565b919050565b5f60208284031215614abf575f80fd5b81516119618161427f565b5f60018201614adb57614adb61496a565b5060010190565b8135614aed8161427f565b81546001600160a01b0319166001600160a01b038216178255506020820135614b158161427f565b6001820180546001600160a01b0319166001600160a01b038316179055505050565b80546001600160701b038082168452808260701c1660208501525050600181015465ffffffffffff8116604084015261ffff8160301c16606084015263ffffffff8160401c166080840152506001600160a01b038060028301541660a08401528060038301541660c0840152505050565b6101c08101614bb7828a614b37565b8735614bc28161427f565b6001600160a01b0390811660e0840152602089013590614be18261427f565b8082166101008501528861012085015287610140850152866101608501528086166101808501528085166101a0850152505098975050505050505050565b6101008101614c2e828561432b565b6001600160a01b03831660e08301529392505050565b5f6020808385031215614c55575f80fd5b825167ffffffffffffffff80821115614c6c575f80fd5b818501915085601f830112614c7f575f80fd5b815181811115614c9157614c91614646565b8060051b9150614ca28483016146ee565b8181529183018401918481019088841115614cbb575f80fd5b938501935b83851015614ce55784519250614cd58361427f565b8282529385019390850190614cc0565b98975050505050505050565b5f60208284031215614d01575f80fd5b815167ffffffffffffffff811115614d17575f80fd5b8201601f81018413614d27575f80fd5b8051614d356147638261471f565b818152856020838501011115614d49575f80fd5b611ee5826020830160208601614220565b8051614aaa81614870565b5f60608284031215614d75575f80fd5b6040516060810181811067ffffffffffffffff82111715614d9857614d98614646565b6040528251614da68161427f565b8152602083015160ff81168114614dbb575f80fd5b60208201526040830151614dce81614870565b60408201529392505050565b5f6101006001600160a01b0380871684528551818151166020860152816020820151166040860152604081015165ffffffffffff80821660608801528060608401511660808801525050508060208701511660a085015250604085015160c08401528060e0840152611c2081840185614242565b6102608101614e5d828a614b37565b614e6a60e0830189614b37565b6101c08201969096526101e08101949094526102008401929092526001600160a01b03908116610220840152166102409091015292915050565b5f60208284031215614eb4575f80fd5b5051919050565b6101208101614eca8286614b37565b8360e08301526001600160a01b038316610100830152949350505050565b80820281158282048414176109945761099461496a565b8681525f6001600160a01b03808816602084015286604084015280861660608401525083608083015260e060a0830152600d60e08301527f4665652066726f6d206c6f616e000000000000000000000000000000000000006101008301526101208060c0840152614f7281840185614242565b9998505050505050505050565b8051614aaa8161484e565b8051614aaa8161483a565b8051614aaa81614861565b8051614aaa816145aa565b5f6102608284031215614fbc575f80fd5b614fc46146a6565b9050614fcf82614f95565b8152614fdd60208301614f95565b6020820152614fee60408301614d5a565b6040820152614fff60608301614fa0565b606082015261501060808301614fa0565b608082015261502160a08301614fa0565b60a082015261503260c08301614fa0565b60c082015261504360e08301614fa0565b60e0820152610100615056818401614fa0565b90820152610120615068838201614fa0565b9082015261014061507a838201614fa0565b9082015261016061508c838201614fa0565b9082015261018061509e838201614fa0565b908201526101a06150b0838201614fa0565b908201526101c06150c2838201614fa0565b908201526101e06150d4838201614fa0565b908201526102006150e6838201614fa0565b908201526102206150f8838201614a9f565b9082015261024061510a838201614f95565b9082015292915050565b5f80828403610380811215615127575f80fd5b61012080821215615136575f80fd5b61513e6146ca565b915061514985614f7f565b825261515760208601614f7f565b602083015261516860408601614f7f565b604083015261517960608601614f7f565b606083015261518a60808601614d5a565b608083015261519b60a08601614f8a565b60a08301526151ac60c08601614d5a565b60c08301526151bd60e08601614a9f565b60e08301526101008086015181840152508193506151dd86828701614fab565b925050509250929050565b5f6001600160a01b03808716835280861660208401525083604083015260806060830152611c206080830184614242565b5f60208284031215615229575f80fd5b8151611961816141d9565b5f8085851115615242575f80fd5b8386111561524e575f80fd5b5050820193919092039150565b6bffffffffffffffffffffffff1981358181169160148510156152885780818660140360031b1b83161692505b505092915050565b8581526001600160a01b0385166020820152836040820152821515606082015260c06080820152600f60c08201527f506179696e67206f6666206c6f616e000000000000000000000000000000000060e08201525f6101008060a0840152614ce581840185614242565b634e487b7160e01b5f52603260045260245ffd5b5f6080820186835260206080602085015281875180845260a0860191506020890193505f5b8181101561537757845180516001600160a01b031684528481015160ff168585015260409081015163ffffffff169084015293830193606090920191600101615333565b505060408501969096525050506060015292915050565b600181815b808511156153c857815f19048211156153ae576153ae61496a565b808516156153bb57918102915b93841c9390800290615393565b509250929050565b5f826153de57506001610994565b816153ea57505f610994565b8160018114615400576002811461540a57615426565b6001915050610994565b60ff84111561541b5761541b61496a565b50506001821b610994565b5060208310610133831016604e8410600b8410161715615449575081810a610994565b615453838361538e565b805f19048211156154665761546661496a565b029392505050565b5f61196183836153d056fea2646970667358221220e145ee1192a76a4f7b508484f382df6004b5f8b563b102bd4069d7a00406d12964736f6c63430008170033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc00000000000000000000000000000000000000000000000000000000000000030000000000000000000000006b92c73682f0e1fac35a18ab17efa5e77dde9fe1000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f887

-----Decoded View---------------
Arg [0] : revnets (address): 0x027F1684c6D31066c3f2468117f2508e8134Fdfc
Arg [1] : revId (uint256): 3
Arg [2] : owner (address): 0x6b92c73682f0e1fac35A18ab17efa5e77DDE9fE1
Arg [3] : permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
Arg [4] : trustedForwarder (address): 0x8A5ba591Ed2bed5691a378C65611eD492500f887

-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000027f1684c6d31066c3f2468117f2508e8134fdfc
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [2] : 0000000000000000000000006b92c73682f0e1fac35a18ab17efa5e77dde9fe1
Arg [3] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Arg [4] : 0000000000000000000000008a5ba591ed2bed5691a378c65611ed492500f887


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

OVERVIEW

Juicebox is a programmable crypto fundraising platform for web3.

Loading...
Loading
[ Download: CSV Export  ]
[ 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.