ETH Price: $2,014.00 (+2.98%)

Contract Diff Checker

Contract Name:
JBOptimismSuckerDeployer

Contract Source Code:

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "./IOwnable.sol";

interface IBridge {
    /// @dev This is an instruction to offchain readers to inform them where to look
    ///      for sequencer inbox batch data. This is not the type of data (eg. das, brotli encoded, or blob versioned hash)
    ///      and this enum is not used in the state transition function, rather it informs an offchain
    ///      reader where to find the data so that they can supply it to the replay binary
    enum BatchDataLocation {
        /// @notice The data can be found in the transaction call data
        TxInput,
        /// @notice The data can be found in an event emitted during the transaction
        SeparateBatchEvent,
        /// @notice This batch contains no data
        NoData,
        /// @notice The data can be found in the 4844 data blobs on this transaction
        Blob
    }

    struct TimeBounds {
        uint64 minTimestamp;
        uint64 maxTimestamp;
        uint64 minBlockNumber;
        uint64 maxBlockNumber;
    }

    event MessageDelivered(
        uint256 indexed messageIndex,
        bytes32 indexed beforeInboxAcc,
        address inbox,
        uint8 kind,
        address sender,
        bytes32 messageDataHash,
        uint256 baseFeeL1,
        uint64 timestamp
    );

    event BridgeCallTriggered(
        address indexed outbox,
        address indexed to,
        uint256 value,
        bytes data
    );

    event InboxToggle(address indexed inbox, bool enabled);

    event OutboxToggle(address indexed outbox, bool enabled);

    event SequencerInboxUpdated(address newSequencerInbox);

    event RollupUpdated(address rollup);

    function allowedDelayedInboxList(uint256) external returns (address);

    function allowedOutboxList(uint256) external returns (address);

    /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
    function delayedInboxAccs(uint256) external view returns (bytes32);

    /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
    function sequencerInboxAccs(uint256) external view returns (bytes32);

    function rollup() external view returns (IOwnable);

    function sequencerInbox() external view returns (address);

    function activeOutbox() external view returns (address);

    function allowedDelayedInboxes(address inbox) external view returns (bool);

    function allowedOutboxes(address outbox) external view returns (bool);

    function sequencerReportedSubMessageCount() external view returns (uint256);

    function executeCall(
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (bool success, bytes memory returnData);

    function delayedMessageCount() external view returns (uint256);

    function sequencerMessageCount() external view returns (uint256);

    // ---------- onlySequencerInbox functions ----------

    function enqueueSequencerMessage(
        bytes32 dataHash,
        uint256 afterDelayedMessagesRead,
        uint256 prevMessageCount,
        uint256 newMessageCount
    )
        external
        returns (
            uint256 seqMessageIndex,
            bytes32 beforeAcc,
            bytes32 delayedAcc,
            bytes32 acc
        );

    /**
     * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
     *      This is done through a separate function entrypoint instead of allowing the sequencer inbox
     *      to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
     *      every delayed inbox or every sequencer inbox call.
     */
    function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
        external
        returns (uint256 msgNum);

    // ---------- onlyRollupOrOwner functions ----------

    function setSequencerInbox(address _sequencerInbox) external;

    function setDelayedInbox(address inbox, bool enabled) external;

    function setOutbox(address inbox, bool enabled) external;

    function updateRollupAddress(IOwnable _rollup) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

interface IDelayedMessageProvider {
    /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
    event InboxMessageDelivered(uint256 indexed messageNum, bytes data);

    /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
    /// same as InboxMessageDelivered but the batch data is available in tx.input
    event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "./IBridge.sol";
import "./IInboxBase.sol";

interface IInbox is IInboxBase {
    function sendL1FundedUnsignedTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    function sendL1FundedContractTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    /**
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendL1FundedUnsignedTransactionToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        bytes calldata data
    ) external payable returns (uint256);

    /**
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendUnsignedTransactionToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    /**
     * @notice Send a message to initiate L2 withdrawal
     * @dev This method can only be called upon L1 fork and will not alias the caller
     *      This method will revert if not called from origin
     */
    function sendWithdrawEthToFork(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        uint256 value,
        address withdrawTo
    ) external returns (uint256);

    /**
     * @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract
     * @dev This does not trigger the fallback function when receiving in the L2 side.
     *      Look into retryable tickets if you are interested in this functionality.
     * @dev This function should not be called inside contract constructors
     */
    function depositEth() external payable returns (uint256);

    /**
     * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
     * @dev all msg.value will deposited to callValueRefundAddress on L2
     * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
     * @param to destination L2 contract address
     * @param l2CallValue call value for retryable L2 message
     * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
     * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
     * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
     * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param data ABI encoded data of L2 message
     * @return unique message number of the retryable transaction
     */
    function createRetryableTicket(
        address to,
        uint256 l2CallValue,
        uint256 maxSubmissionCost,
        address excessFeeRefundAddress,
        address callValueRefundAddress,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        bytes calldata data
    ) external payable returns (uint256);

    /**
     * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
     * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds
     * come from the deposit alone, rather than falling back on the user's L2 balance
     * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
     * createRetryableTicket method is the recommended standard.
     * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
     * @param to destination L2 contract address
     * @param l2CallValue call value for retryable L2 message
     * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
     * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
     * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
     * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
     * @param data ABI encoded data of L2 message
     * @return unique message number of the retryable transaction
     */
    function unsafeCreateRetryableTicket(
        address to,
        uint256 l2CallValue,
        uint256 maxSubmissionCost,
        address excessFeeRefundAddress,
        address callValueRefundAddress,
        uint256 gasLimit,
        uint256 maxFeePerGas,
        bytes calldata data
    ) external payable returns (uint256);

    // ---------- initializer ----------

    /**
     * @dev function to be called one time during the inbox upgrade process
     *      this is used to fix the storage slots
     */
    function postUpgradeInit(IBridge _bridge) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

import "./IBridge.sol";
import "./IDelayedMessageProvider.sol";
import "./ISequencerInbox.sol";

interface IInboxBase is IDelayedMessageProvider {
    function bridge() external view returns (IBridge);

    function sequencerInbox() external view returns (ISequencerInbox);

    function maxDataSize() external view returns (uint256);

    /**
     * @notice Send a generic L2 message to the chain
     * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
     * @param messageData Data of the message being sent
     */
    function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256);

    /**
     * @notice Send a generic L2 message to the chain
     * @dev This method can be used to send any type of message that doesn't require L1 validation
     * @param messageData Data of the message being sent
     */
    function sendL2Message(bytes calldata messageData) external returns (uint256);

    function sendUnsignedTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        uint256 nonce,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    function sendContractTransaction(
        uint256 gasLimit,
        uint256 maxFeePerGas,
        address to,
        uint256 value,
        bytes calldata data
    ) external returns (uint256);

    /**
     * @notice Get the L1 fee for submitting a retryable
     * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value
     * @dev This formula may change in the future, to future proof your code query this method instead of inlining!!
     * @param dataLength The length of the retryable's calldata, in bytes
     * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used
     */
    function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
        external
        view
        returns (uint256);

    // ---------- onlyRollupOrOwner functions ----------

    /// @notice pauses all inbox functionality
    function pause() external;

    /// @notice unpauses all inbox functionality
    function unpause() external;

    /// @notice add or remove users from allowList
    function setAllowList(address[] memory user, bool[] memory val) external;

    /// @notice enable or disable allowList
    function setAllowListEnabled(bool _allowListEnabled) external;

    /// @notice check if user is in allowList
    function isAllowed(address user) external view returns (bool);

    /// @notice check if allowList is enabled
    function allowListEnabled() external view returns (bool);

    function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external;

    /// @notice returns the current admin
    function getProxyAdmin() external view returns (address);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.4.21 <0.9.0;

interface IOwnable {
    function owner() external view returns (address);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
pragma experimental ABIEncoderV2;

import "../libraries/IGasRefunder.sol";
import "./IDelayedMessageProvider.sol";
import "./IBridge.sol";

interface ISequencerInbox is IDelayedMessageProvider {
    struct MaxTimeVariation {
        uint256 delayBlocks;
        uint256 futureBlocks;
        uint256 delaySeconds;
        uint256 futureSeconds;
    }

    event SequencerBatchDelivered(
        uint256 indexed batchSequenceNumber,
        bytes32 indexed beforeAcc,
        bytes32 indexed afterAcc,
        bytes32 delayedAcc,
        uint256 afterDelayedMessagesRead,
        IBridge.TimeBounds timeBounds,
        IBridge.BatchDataLocation dataLocation
    );

    event OwnerFunctionCalled(uint256 indexed id);

    /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input
    event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data);

    /// @dev a valid keyset was added
    event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes);

    /// @dev a keyset was invalidated
    event InvalidateKeyset(bytes32 indexed keysetHash);

    function totalDelayedMessagesRead() external view returns (uint256);

    function bridge() external view returns (IBridge);

    /// @dev The size of the batch header
    // solhint-disable-next-line func-name-mixedcase
    function HEADER_LENGTH() external view returns (uint256);

    /// @dev If the first batch data byte after the header has this bit set,
    ///      the sequencer inbox has authenticated the data. Currently only used for 4844 blob support.
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function DATA_AUTHENTICATED_FLAG() external view returns (bytes1);

    /// @dev If the first data byte after the header has this bit set,
    ///      then the batch data is to be found in 4844 data blobs
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function DATA_BLOB_HEADER_FLAG() external view returns (bytes1);

    /// @dev If the first data byte after the header has this bit set,
    ///      then the batch data is a das message
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);

    /// @dev If the first data byte after the header has this bit set,
    ///      then the batch data is a das message that employs a merklesization strategy
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function TREE_DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);

    /// @dev If the first data byte after the header has this bit set,
    ///      then the batch data has been brotli compressed
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function BROTLI_MESSAGE_HEADER_FLAG() external view returns (bytes1);

    /// @dev If the first data byte after the header has this bit set,
    ///      then the batch data uses a zero heavy encoding
    ///      See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
    // solhint-disable-next-line func-name-mixedcase
    function ZERO_HEAVY_MESSAGE_HEADER_FLAG() external view returns (bytes1);

    function rollup() external view returns (IOwnable);

    function isBatchPoster(address) external view returns (bool);

    function isSequencer(address) external view returns (bool);

    function maxDataSize() external view returns (uint256);

    /// @notice The batch poster manager has the ability to change the batch poster addresses
    ///         This enables the batch poster to do key rotation
    function batchPosterManager() external view returns (address);

    struct DasKeySetInfo {
        bool isValidKeyset;
        uint64 creationBlock;
    }

    /// @dev returns 4 uint256 to be compatible with older version
    function maxTimeVariation()
        external
        view
        returns (
            uint256 delayBlocks,
            uint256 futureBlocks,
            uint256 delaySeconds,
            uint256 futureSeconds
        );

    function dasKeySetInfo(bytes32) external view returns (bool, uint64);

    /// @notice Remove force inclusion delay after a L1 chainId fork
    function removeDelayAfterFork() external;

    /// @notice Force messages from the delayed inbox to be included in the chain
    ///         Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and
    ///         maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these
    ///         messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages.
    /// @param _totalDelayedMessagesRead The total number of messages to read up to
    /// @param kind The kind of the last message to be included
    /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
    /// @param baseFeeL1 The l1 gas price of the last message to be included
    /// @param sender The sender of the last message to be included
    /// @param messageDataHash The messageDataHash of the last message to be included
    function forceInclusion(
        uint256 _totalDelayedMessagesRead,
        uint8 kind,
        uint64[2] calldata l1BlockAndTime,
        uint256 baseFeeL1,
        address sender,
        bytes32 messageDataHash
    ) external;

    function inboxAccs(uint256 index) external view returns (bytes32);

    function batchCount() external view returns (uint256);

    function isValidKeysetHash(bytes32 ksHash) external view returns (bool);

    /// @notice the creation block is intended to still be available after a keyset is deleted
    function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256);

    // ---------- BatchPoster functions ----------

    function addSequencerL2BatchFromOrigin(
        uint256 sequenceNumber,
        bytes calldata data,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder
    ) external;

    function addSequencerL2BatchFromOrigin(
        uint256 sequenceNumber,
        bytes calldata data,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder,
        uint256 prevMessageCount,
        uint256 newMessageCount
    ) external;

    function addSequencerL2Batch(
        uint256 sequenceNumber,
        bytes calldata data,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder,
        uint256 prevMessageCount,
        uint256 newMessageCount
    ) external;

    function addSequencerL2BatchFromBlobs(
        uint256 sequenceNumber,
        uint256 afterDelayedMessagesRead,
        IGasRefunder gasRefunder,
        uint256 prevMessageCount,
        uint256 newMessageCount
    ) external;

    // ---------- onlyRollupOrOwner functions ----------

    /**
     * @notice Set max delay for sequencer inbox
     * @param maxTimeVariation_ the maximum time variation parameters
     */
    function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external;

    /**
     * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
     * @param addr the address
     * @param isBatchPoster_ if the specified address should be authorized as a batch poster
     */
    function setIsBatchPoster(address addr, bool isBatchPoster_) external;

    /**
     * @notice Makes Data Availability Service keyset valid
     * @param keysetBytes bytes of the serialized keyset
     */
    function setValidKeyset(bytes calldata keysetBytes) external;

    /**
     * @notice Invalidates a Data Availability Service keyset
     * @param ksHash hash of the keyset
     */
    function invalidateKeysetHash(bytes32 ksHash) external;

    /**
     * @notice Updates whether an address is authorized to be a sequencer.
     * @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer.
     * @param addr the address
     * @param isSequencer_ if the specified address should be authorized as a sequencer
     */
    function setIsSequencer(address addr, bool isSequencer_) external;

    /**
     * @notice Updates the batch poster manager, the address which has the ability to rotate batch poster keys
     * @param newBatchPosterManager The new batch poster manager to be set
     */
    function setBatchPosterManager(address newBatchPosterManager) external;

    /// @notice Allows the rollup owner to sync the rollup address
    function updateRollupAddress() external;

    // ---------- initializer ----------

    function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1

// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;

interface IGasRefunder {
    function onGasSpent(
        address payable spender,
        uint256 gasUsed,
        uint256 calldataSize
    ) external returns (bool success);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

/// @notice A terminal that can be cashed out from.
interface IJBCashOutTerminal is IJBTerminal {
    event HookAfterRecordCashOut(
        IJBCashOutHook indexed hook,
        JBAfterCashOutRecordedContext context,
        uint256 specificationAmount,
        uint256 fee,
        address caller
    );
    event CashOutTokens(
        uint256 indexed rulesetId,
        uint256 indexed rulesetCycleNumber,
        uint256 indexed projectId,
        address holder,
        address beneficiary,
        uint256 cashOutCount,
        uint256 cashOutTaxRate,
        uint256 reclaimAmount,
        bytes metadata,
        address caller
    );

    function cashOutTokensOf(
        address holder,
        uint256 projectId,
        uint256 cashOutCount,
        address tokenToReclaim,
        uint256 minTokensReclaimed,
        address payable beneficiary,
        bytes calldata metadata
    )
        external
        returns (uint256 reclaimAmount);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// 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 DeployERC20(
        uint256 indexed projectId, address indexed deployer, bytes32 salt, bytes32 saltHash, 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 OMNICHAIN_RULESET_OPERATOR() external view returns (address);

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

import {JBRuleset} from "./../structs/JBRuleset.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, JBRuleset memory ruleset) external view returns (JBApprovalStatus);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
// slither-disable-next-line solc-version
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`.
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// 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";

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// 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";

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.20;

/**
 * @dev Library for managing uint256 to bool mapping in a compact and efficient way, provided the keys are sequential.
 * Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
 *
 * BitMaps pack 256 booleans across each bit of a single 256-bit slot of `uint256` type.
 * Hence booleans corresponding to 256 _sequential_ indices would only consume a single slot,
 * unlike the regular `bool` which would consume an entire slot for a single value.
 *
 * This results in gas savings in two ways:
 *
 * - Setting a zero value to non-zero only once every 256 times
 * - Accessing the same warm slot for every 256 _sequential_ indices
 */
library BitMaps {
    struct BitMap {
        mapping(uint256 bucket => uint256) _data;
    }

    /**
     * @dev Returns whether the bit at `index` is set.
     */
    function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        return bitmap._data[bucket] & mask != 0;
    }

    /**
     * @dev Sets the bit at `index` to the boolean `value`.
     */
    function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
        if (value) {
            set(bitmap, index);
        } else {
            unset(bitmap, index);
        }
    }

    /**
     * @dev Sets the bit at `index`.
     */
    function set(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] |= mask;
    }

    /**
     * @dev Unsets the bit at `index`.
     */
    function unset(BitMap storage bitmap, uint256 index) internal {
        uint256 bucket = index >> 8;
        uint256 mask = 1 << (index & 0xff);
        bitmap._data[bucket] &= ~mask;
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

/// @notice Minimal proxy library.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the ERC1167 pattern during runtime,
/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
/// which saves 4 gas over the ERC1167 pattern during runtime, and has the smallest bytecode.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal proxy (PUSH0 variant):
/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai.
/// It is optimized first for minimal runtime gas, then for minimal bytecode.
/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as
/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai.
/// Please use with caution.
/// - Automatically verified on Etherscan.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here is does NOT append the immutable args into the calldata
/// passed into delegatecall. It is simply an ERC1167 minimal proxy with the immutable arguments
/// appended to the back of the runtime bytecode.
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I proxy:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 beacon proxy:
/// A minimal beacon proxy, intended to be upgraded with an upgradable beacon.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 beacon proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I beacon proxy:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
library LibClone {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The keccak256 of deployed code for the clone proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant CLONE_CODE_HASH =
        0x48db2cfdb2853fce0b464f1f93a1996469459df3ab6c812106074c4106a1eb1f;

    /// @dev The keccak256 of deployed code for the PUSH0 proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant PUSH0_CLONE_CODE_HASH =
        0x67bc6bde1b84d66e267c718ba44cf3928a615d29885537955cb43d44b3e789dc;

    /// @dev The keccak256 of deployed code for the ERC-1167 CWIA proxy,
    /// with the implementation set to `address(0)`.
    bytes32 internal constant CWIA_CODE_HASH =
        0x3cf92464268225a4513da40a34d967354684c32cd0edd67b5f668dfe3550e940;

    /// @dev The keccak256 of the deployed code for the ERC1967 proxy.
    bytes32 internal constant ERC1967_CODE_HASH =
        0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;

    /// @dev The keccak256 of the deployed code for the ERC1967I proxy.
    bytes32 internal constant ERC1967I_CODE_HASH =
        0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;

    /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
    bytes32 internal constant ERC1967_BEACON_PROXY_CODE_HASH =
        0x14044459af17bc4f0f5aa2f658cb692add77d1302c29fe2aebab005eea9d1162;

    /// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
    bytes32 internal constant ERC1967I_BEACON_PROXY_CODE_HASH =
        0xf8c46d2793d5aa984eb827aeaba4b63aedcab80119212fce827309788735519a;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the clone.
    error DeploymentFailed();

    /// @dev The salt must start with either the zero address or `by`.
    error SaltDoesNotStartWith();

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  MINIMAL PROXY OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a clone of `implementation`.
    function clone(address implementation) internal returns (address instance) {
        instance = clone(0, implementation);
    }

    /// @dev Deploys a clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    function clone(uint256 value, address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * --------------------------------------------------------------------------+
             * CREATION (9 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                       |
             * --------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize     | r         |                              |
             * 3d         | RETURNDATASIZE    | 0 r       |                              |
             * 81         | DUP2              | r 0 r     |                              |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                              |
             * 3d         | RETURNDATASIZE    | 0 o r 0 r |                              |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code   |
             * f3         | RETURN            |           | [0..runSize): runtime code   |
             * --------------------------------------------------------------------------|
             * RUNTIME (44 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode  | Mnemonic       | Stack                  | Memory                |
             * --------------------------------------------------------------------------|
             *                                                                           |
             * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | 0                      |                       |
             * 3d      | RETURNDATASIZE | 0 0                    |                       |
             * 3d      | RETURNDATASIZE | 0 0 0                  |                       |
             * 3d      | RETURNDATASIZE | 0 0 0 0                |                       |
             *                                                                           |
             * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            |                       |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          |                       |
             * 3d      | RETURNDATASIZE | 0 0 cds 0 0 0 0        |                       |
             * 37      | CALLDATACOPY   | 0 0 0 0                | [0..cds): calldata    |
             *                                                                           |
             * ::: delegate call to the implementation contract :::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          | [0..cds): calldata    |
             * 73 addr | PUSH20 addr    | addr 0 cds 0 0 0 0     | [0..cds): calldata    |
             * 5a      | GAS            | gas addr 0 cds 0 0 0 0 | [0..cds): calldata    |
             * f4      | DELEGATECALL   | success 0 0            | [0..cds): calldata    |
             *                                                                           |
             * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds success 0 0        | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | rds rds success 0 0    | [0..cds): calldata    |
             * 93      | SWAP4          | 0 rds success 0 rds    | [0..cds): calldata    |
             * 80      | DUP1           | 0 0 rds success 0 rds  | [0..cds): calldata    |
             * 3e      | RETURNDATACOPY | success 0 rds          | [0..rds): returndata  |
             *                                                                           |
             * 60 0x2a | PUSH1 0x2a     | 0x2a success 0 rds     | [0..rds): returndata  |
             * 57      | JUMPI          | 0 rds                  | [0..rds): returndata  |
             *                                                                           |
             * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * fd      | REVERT         |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b      | JUMPDEST       | 0 rds                  | [0..rds): returndata  |
             * f3      | RETURN         |                        | [0..rds): returndata  |
             * --------------------------------------------------------------------------+
             */
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create(value, 0x0c, 0x35)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    function cloneDeterministic(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic(0, implementation, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    /// Deposits `value` ETH during deployment.
    function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create2(value, 0x0c, 0x35, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the clone of `implementation`.
    function initCode(address implementation) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
            mstore(add(c, 0x28), implementation)
            mstore(add(c, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            mstore(c, 0x35) // Store the length.
            mstore(0x40, add(c, 0x60)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`.
    function initCodeHash(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            hash := keccak256(0x0c, 0x35)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address of the clone of `implementation`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(address implementation, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          MINIMAL PROXY OPERATIONS (PUSH0 VARIANT)          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a PUSH0 clone of `implementation`.
    function clone_PUSH0(address implementation) internal returns (address instance) {
        instance = clone_PUSH0(0, implementation);
    }

    /// @dev Deploys a PUSH0 clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    function clone_PUSH0(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * --------------------------------------------------------------------------+
             * CREATION (9 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                       |
             * --------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize     | r         |                              |
             * 5f         | PUSH0             | 0 r       |                              |
             * 81         | DUP2              | r 0 r     |                              |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                              |
             * 5f         | PUSH0             | 0 o r 0 r |                              |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code   |
             * f3         | RETURN            |           | [0..runSize): runtime code   |
             * --------------------------------------------------------------------------|
             * RUNTIME (45 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode  | Mnemonic       | Stack                  | Memory                |
             * --------------------------------------------------------------------------|
             *                                                                           |
             * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
             * 5f      | PUSH0          | 0                      |                       |
             * 5f      | PUSH0          | 0 0                    |                       |
             *                                                                           |
             * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0                |                       |
             * 5f      | PUSH0          | 0 cds 0 0              |                       |
             * 5f      | PUSH0          | 0 0 cds 0 0            |                       |
             * 37      | CALLDATACOPY   | 0 0                    | [0..cds): calldata    |
             *                                                                           |
             * ::: delegate call to the implementation contract :::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0                | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 cds 0 0              | [0..cds): calldata    |
             * 73 addr | PUSH20 addr    | addr 0 cds 0 0         | [0..cds): calldata    |
             * 5a      | GAS            | gas addr 0 cds 0 0     | [0..cds): calldata    |
             * f4      | DELEGATECALL   | success                | [0..cds): calldata    |
             *                                                                           |
             * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds success            | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 rds success          | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 0 rds success        | [0..cds): calldata    |
             * 3e      | RETURNDATACOPY | success                | [0..rds): returndata  |
             *                                                                           |
             * 60 0x29 | PUSH1 0x29     | 0x29 success           | [0..rds): returndata  |
             * 57      | JUMPI          |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds                    | [0..rds): returndata  |
             * 5f      | PUSH0          | 0 rds                  | [0..rds): returndata  |
             * fd      | REVERT         |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b      | JUMPDEST       |                        | [0..rds): returndata  |
             * 3d      | RETURNDATASIZE | rds                    | [0..rds): returndata  |
             * 5f      | PUSH0          | 0 rds                  | [0..rds): returndata  |
             * f3      | RETURN         |                        | [0..rds): returndata  |
             * --------------------------------------------------------------------------+
             */
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            instance := create(value, 0x0e, 0x36)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
    function cloneDeterministic_PUSH0(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic_PUSH0(0, implementation, salt);
    }

    /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
    /// Deposits `value` ETH during deployment.
    function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            instance := create2(value, 0x0e, 0x36, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the PUSH0 clone of `implementation`.
    function initCode_PUSH0(address implementation) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16
            mstore(add(c, 0x26), implementation) // 20
            mstore(add(c, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            mstore(c, 0x36) // Store the length.
            mstore(0x40, add(c, 0x60)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`.
    function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            hash := keccak256(0x0e, 0x36)
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address of the PUSH0 clone of `implementation`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress_PUSH0(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHash_PUSH0(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           CLONES WITH IMMUTABLE ARGS OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`.
    function clone(address implementation, bytes memory args) internal returns (address instance) {
        instance = clone(0, implementation, args);
    }

    /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`.
    /// Deposits `value` ETH during deployment.
    function clone(uint256 value, address implementation, bytes memory args)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------+
             * CREATION (10 bytes)                                                        |
             * ---------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                        |
             * ---------------------------------------------------------------------------|
             * 61 runSize | PUSH2 runSize     | r         |                               |
             * 3d         | RETURNDATASIZE    | 0 r       |                               |
             * 81         | DUP2              | r 0 r     |                               |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                               |
             * 3d         | RETURNDATASIZE    | 0 o r 0 r |                               |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code    |
             * f3         | RETURN            |           | [0..runSize): runtime code    |
             * ---------------------------------------------------------------------------|
             * RUNTIME (45 bytes + extraLength)                                           |
             * ---------------------------------------------------------------------------|
             * Opcode   | Mnemonic       | Stack                  | Memory                |
             * ---------------------------------------------------------------------------|
             *                                                                            |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::: |
             * 36       | CALLDATASIZE   | cds                    |                       |
             * 3d       | RETURNDATASIZE | 0 cds                  |                       |
             * 3d       | RETURNDATASIZE | 0 0 cds                |                       |
             * 37       | CALLDATACOPY   |                        | [0..cds): calldata    |
             *                                                                            |
             * ::: delegate call to the implementation contract ::::::::::::::::::::::::: |
             * 3d       | RETURNDATASIZE | 0                      | [0..cds): calldata    |
             * 3d       | RETURNDATASIZE | 0 0                    | [0..cds): calldata    |
             * 3d       | RETURNDATASIZE | 0 0 0                  | [0..cds): calldata    |
             * 36       | CALLDATASIZE   | cds 0 0 0              | [0..cds): calldata    |
             * 3d       | RETURNDATASIZE | 0 cds 0 0 0 0          | [0..cds): calldata    |
             * 73 addr  | PUSH20 addr    | addr 0 cds 0 0 0 0     | [0..cds): calldata    |
             * 5a       | GAS            | gas addr 0 cds 0 0 0 0 | [0..cds): calldata    |
             * f4       | DELEGATECALL   | success 0 0            | [0..cds): calldata    |
             *                                                                            |
             * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::: |
             * 3d       | RETURNDATASIZE | rds success 0          | [0..cds): calldata    |
             * 82       | DUP3           | 0 rds success 0         | [0..cds): calldata   |
             * 80       | DUP1           | 0 0 rds success 0      | [0..cds): calldata    |
             * 3e       | RETURNDATACOPY | success 0              | [0..rds): returndata  |
             * 90       | SWAP1          | 0 success              | [0..rds): returndata  |
             * 3d       | RETURNDATASIZE | rds 0 success          | [0..rds): returndata  |
             * 91       | SWAP2          | success 0 rds          | [0..rds): returndata  |
             *                                                                            |
             * 60 0x2b  | PUSH1 0x2b     | 0x2b success 0 rds     | [0..rds): returndata  |
             * 57       | JUMPI          | 0 rds                  | [0..rds): returndata  |
             *                                                                            |
             * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * fd       | REVERT         |                        | [0..rds): returndata  |
             *                                                                            |
             * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b       | JUMPDEST       | 0 rds                  | [0..rds): returndata  |
             * f3       | RETURN         |                        | [0..rds): returndata  |
             * ---------------------------------------------------------------------------+
             */
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
            mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(m, 0x14), implementation)
            mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
            instance := create(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `args` and `salt`.
    function cloneDeterministic(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic(0, implementation, args, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `args` and `salt`.
    function cloneDeterministic(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
            mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(m, 0x14), implementation)
            mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
            instance := create2(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `args` and `salt`.
    /// This method does not revert if the clone has already been deployed.
    function createDeterministicClone(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicClone(0, implementation, args, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `args` and `salt`.
    /// This method does not revert if the clone has already been deployed.
    function createDeterministicClone(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (bool alreadyDeployed, address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
            mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(m, 0x14), implementation)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
            // forgefmt: disable-next-item
            mstore(add(m, gt(n, 0xffd2)), add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(add(m, 0x0c), add(n, 0x37)))
            mstore(0x01, shl(96, address()))
            mstore(0x15, salt)
            instance := keccak256(0x00, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, add(m, 0x0c), add(n, 0x37), salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the clone of `implementation`
    /// using immutable arguments encoded in `args`.
    function initCode(address implementation, bytes memory args)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x57), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(c, 0x37), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(c, 0x28), implementation)
            mstore(add(c, 0x14), add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
            mstore(c, add(0x37, n)) // Store the length.
            mstore(add(c, add(n, 0x57)), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(c, add(n, 0x77))) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`
    /// using immutable arguments encoded in `args`.
    function initCodeHash(address implementation, bytes memory args)
        internal
        pure
        returns (bytes32 hash)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(m, 0x43), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
            mstore(add(m, 0x14), implementation)
            mstore(m, add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
            hash := keccak256(add(m, 0x0c), add(n, 0x37))
        }
    }

    /// @dev Returns the address of the clone of
    /// `implementation` using immutable arguments encoded in `args`, with `salt`, by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(
        address implementation,
        bytes memory data,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHash(implementation, data);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /// @dev Equivalent to `argsOnClone(instance, 0, 2 ** 256 - 1)`.
    function argsOnClone(address instance) internal view returns (bytes memory args) {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x2d))) // Store the length.
            extcodecopy(instance, add(args, 0x20), 0x2d, add(mload(args), 0x20))
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `argsOnClone(instance, start, 2 ** 256 - 1)`.
    function argsOnClone(address instance, uint256 start)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(instance), 0x2d))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(instance, args, add(start, 0x0d), add(l, 0x40))
            mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `instance` MUST be deployed via the clone with immutable args functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `instance` does not have any code.
    function argsOnClone(address instance, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(instance, args, add(start, 0x0d), add(d, 0x20))
            if iszero(and(0xff, mload(add(args, d)))) {
                let n := sub(extcodesize(instance), 0x2d)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(args, d) // Store the length.
            mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              MINIMAL ERC1967 PROXY OPERATIONS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: The ERC1967 proxy here is intended to be upgraded with UUPS.
    // This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.

    /// @dev Deploys a minimal ERC1967 proxy with `implementation`.
    function deployERC1967(address implementation) internal returns (address instance) {
        instance = deployERC1967(0, implementation);
    }

    /// @dev Deploys a minimal ERC1967 proxy with `implementation`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 impl    | PUSH20 impl    | impl 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos impl 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot impl 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (61 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             * 7f slot    | PUSH32 slot    | s 0 cds 0 0      | [0..calldatasize): calldata     |
             * 54         | SLOAD          | i 0 cds 0 0      | [0..calldatasize): calldata     |
             * 5a         | GAS            | g i 0 cds 0 0    | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 80         | DUP1           | 0 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x38    | PUSH1 0x38     | dest succ        | [0..returndatasize): returndata |
             * 57         | JUMPI          |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * fd         | REVERT         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [0..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * f3         | RETURN         |                  | [0..returndatasize): returndata |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            instance := create(value, 0x21, 0x5f)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    function deployDeterministicERC1967(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967(0, implementation, salt);
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            instance := create2(value, 0x21, 0x5f, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967(0, implementation, salt);
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x21, 0x5f))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x21, 0x5f, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
    function initCodeERC1967(address implementation) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x60), 0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300)
            mstore(add(c, 0x40), 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
            mstore(add(c, 0x20), or(shl(24, implementation), 0x600951))
            mstore(add(c, 0x09), 0x603d3d8160223d3973)
            mstore(c, 0x5f) // Store the length.
            mstore(0x40, add(c, 0x80)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
    function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            hash := keccak256(0x21, 0x5f)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the ERC1967 proxy of `implementation`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*    MINIMAL ERC1967 PROXY WITH IMMUTABLE ARGS OPERATIONS    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
    function deployERC1967(address implementation, bytes memory args)
        internal
        returns (address instance)
    {
        instance = deployERC1967(0, implementation, args);
    }

    /// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967(uint256 value, address implementation, bytes memory args)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
            mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x16, 0x6009)
            mstore(0x14, implementation)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
            mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            instance := create(value, m, add(n, 0x60))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
    function deployDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967(0, implementation, args, salt);
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
            mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x16, 0x6009)
            mstore(0x14, implementation)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
            mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            instance := create2(value, m, add(n, 0x60), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967(0, implementation, args, salt);
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (bool alreadyDeployed, address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
            mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x16, 0x6009)
            mstore(0x14, implementation)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
            mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(m, add(n, 0x60)))
            mstore(0x01, shl(96, address()))
            mstore(0x15, salt)
            instance := keccak256(0x00, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, m, add(n, 0x60), salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation` and `args`.
    function initCodeERC1967(address implementation, bytes memory args)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x80), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(c, 0x60), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(add(c, 0x40), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(add(c, 0x20), 0x6009)
            mstore(add(c, 0x1e), implementation)
            mstore(add(c, 0x0a), add(0x61003d3d8160233d3973, shl(56, n)))
            mstore(c, add(n, 0x60)) // Store the length.
            mstore(add(c, add(n, 0x80)), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(c, add(n, 0xa0))) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation` and `args`.
    function initCodeHashERC1967(address implementation, bytes memory args)
        internal
        pure
        returns (bytes32 hash)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(m, 0x60), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x16, 0x6009)
            mstore(0x14, implementation)
            mstore(0x00, add(0x61003d3d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            hash := keccak256(m, add(n, 0x60))
        }
    }

    /// @dev Returns the address of the ERC1967 proxy of `implementation`, `args`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967(
        address implementation,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967(implementation, args);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967(address instance) internal view returns (bytes memory args) {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x3d))) // Store the length.
            extcodecopy(instance, add(args, 0x20), 0x3d, add(mload(args), 0x20))
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967(address instance, uint256 start)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(instance), 0x3d))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(instance, args, add(start, 0x1d), add(l, 0x40))
            mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `instance` does not have any code.
    function argsOnERC1967(address instance, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(instance, args, add(start, 0x1d), add(d, 0x20))
            if iszero(and(0xff, mload(add(args, d)))) {
                let n := sub(extcodesize(instance), 0x3d)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(args, d) // Store the length.
            mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                 ERC1967I PROXY OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: This proxy has a special code path that activates if `calldatasize() == 1`.
    // This code path skips the delegatecall and directly returns the `implementation` address.
    // The returned implementation is guaranteed to be valid if the keccak256 of the
    // proxy's code is equal to `ERC1967I_CODE_HASH`.

    /// @dev Deploys a ERC1967I proxy with `implementation`.
    function deployERC1967I(address implementation) internal returns (address instance) {
        instance = deployERC1967I(0, implementation);
    }

    /// @dev Deploys a ERC1967I proxy with `implementation`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967I(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 impl    | PUSH20 impl    | impl 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos impl 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot impl 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (82 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 58         | PC             | 1 cds            |                                 |
             * 14         | EQ             | eqs              |                                 |
             * 60 0x43    | PUSH1 0x43     | dest eqs         |                                 |
             * 57         | JUMPI          |                  |                                 |
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             * 7f slot    | PUSH32 slot    | s 0 cds 0 0      | [0..calldatasize): calldata     |
             * 54         | SLOAD          | i 0 cds 0 0      | [0..calldatasize): calldata     |
             * 5a         | GAS            | g i 0 cds 0 0    | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 80         | DUP1           | 0 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x3E    | PUSH1 0x3E     | dest succ        | [0..returndatasize): returndata |
             * 57         | JUMPI          |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * fd         | REVERT         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [0..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * f3         | RETURN         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  |                                 |
             * 60 0x20    | PUSH1 0x20     | 32               |                                 |
             * 60 0x0F    | PUSH1 0x0F     | o 32             |                                 |
             * 3d         | RETURNDATASIZE | 0 o 32           |                                 |
             * 39         | CODECOPY       |                  | [0..32): implementation slot    |
             * 3d         | RETURNDATASIZE | 0                | [0..32): implementation slot    |
             * 51         | MLOAD          | slot             | [0..32): implementation slot    |
             * 54         | SLOAD          | impl             | [0..32): implementation slot    |
             * 3d         | RETURNDATASIZE | 0 impl           | [0..32): implementation slot    |
             * 52         | MSTORE         |                  | [0..32): implementation address |
             * 59         | MSIZE          | 32               | [0..32): implementation address |
             * 3d         | RETURNDATASIZE | 0 32             | [0..32): implementation address |
             * f3         | RETURN         |                  | [0..32): implementation address |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            instance := create(value, 0x0c, 0x74)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
    function deployDeterministicERC1967I(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967I(0, implementation, salt);
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            instance := create2(value, 0x0c, 0x74, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967I(0, implementation, salt);
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x0c, 0x74))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x0c, 0x74, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the ERC1967I proxy of `implementation`.
    function initCodeERC1967I(address implementation) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x74), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(c, 0x54), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(c, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(add(c, 0x1d), implementation)
            mstore(add(c, 0x09), 0x60523d8160223d3973)
            mstore(add(c, 0x94), 0)
            mstore(c, 0x74) // Store the length.
            mstore(0x40, add(c, 0xa0)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation`.
    function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            hash := keccak256(0x0c, 0x74)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the ERC1967I proxy of `implementation`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967I(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967I(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*       ERC1967I PROXY WITH IMMUTABLE ARGS OPERATIONS        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
    function deployERC1967I(address implementation, bytes memory args) internal returns (address) {
        return deployERC1967I(0, implementation, args);
    }

    /// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967I(uint256 value, address implementation, bytes memory args)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))

            mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(add(m, 0x14), implementation)
            mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))

            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            instance := create(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`.
    function deployDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967I(0, implementation, args, salt);
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967I(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))

            mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(add(m, 0x14), implementation)
            mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))

            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            instance := create2(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967I(0, implementation, args, salt);
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(
        uint256 value,
        address implementation,
        bytes memory args,
        bytes32 salt
    ) internal returns (bool alreadyDeployed, address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x75), n))
            mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x16, 0x600f)
            mstore(0x14, implementation)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            mstore(gt(n, 0xffad), add(0xfe6100523d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(m, add(n, 0x75)))
            mstore(0x01, shl(96, address()))
            mstore(0x15, salt)
            instance := keccak256(0x00, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, m, add(0x75, n), salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the ERC1967I proxy of `implementation` and `args`.
    function initCodeERC1967I(address implementation, bytes memory args)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
            }

            mstore(add(c, 0x75), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(c, 0x55), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(c, 0x35), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(add(c, 0x1e), implementation)
            mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
            mstore(add(c, add(n, 0x95)), 0)
            mstore(c, add(0x75, n)) // Store the length.
            mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation` and `args.
    function initCodeHashERC1967I(address implementation, bytes memory args)
        internal
        pure
        returns (bytes32 hash)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))

            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(m, 0x75), i), mload(add(add(args, 0x20), i)))
            }

            mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x16, 0x600f)
            mstore(0x14, implementation)
            mstore(0x00, add(0x6100523d8160233d3973, shl(56, n)))
            mstore(m, mload(0x16))
            hash := keccak256(m, add(0x75, n))
        }
    }

    /// @dev Returns the address of the ERC1967I proxy of `implementation`, `args` with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967I(
        address implementation,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967I(implementation, args);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967I(address instance) internal view returns (bytes memory args) {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
            extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967I(address instance, uint256 start)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(instance, args, add(start, 0x32), add(l, 0x40))
            mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `instance` does not have any code.
    function argsOnERC1967I(address instance, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
            if iszero(and(0xff, mload(add(args, d)))) {
                let n := sub(extcodesize(instance), 0x52)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(args, d) // Store the length.
            mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                ERC1967 BOOTSTRAP OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // A bootstrap is a minimal UUPS implementation that allows an ERC1967 proxy
    // pointing to it to be upgraded. The ERC1967 proxy can then be deployed to a
    // deterministic address independent of the implementation:
    // ```
    //     address bootstrap = LibClone.erc1967Bootstrap();
    //     address instance = LibClone.deployDeterministicERC1967(0, bootstrap, salt);
    //     LibClone.bootstrapERC1967(bootstrap, implementation);
    // ```

    /// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
    function erc1967Bootstrap() internal returns (address) {
        return erc1967Bootstrap(address(this));
    }

    /// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
    function erc1967Bootstrap(address authorizedUpgrader) internal returns (address bootstrap) {
        bytes memory c = initCodeERC1967Bootstrap(authorizedUpgrader);
        bootstrap = predictDeterministicAddress(keccak256(c), bytes32(0), address(this));
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(extcodesize(bootstrap)) {
                if iszero(create2(0, add(c, 0x20), mload(c), 0)) {
                    mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Replaces the implementation at `instance`.
    function bootstrapERC1967(address instance, address implementation) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, implementation)
            if iszero(call(gas(), instance, 0, 0x0c, 0x14, codesize(), 0x00)) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Replaces the implementation at `instance`, and then call it with `data`.
    function bootstrapERC1967AndCall(address instance, address implementation, bytes memory data)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let n := mload(data)
            mstore(data, implementation)
            if iszero(call(gas(), instance, 0, add(data, 0x0c), add(n, 0x14), codesize(), 0x00)) {
                if iszero(returndatasize()) {
                    mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                    revert(0x1c, 0x04)
                }
                returndatacopy(mload(0x40), 0x00, returndatasize())
                revert(mload(0x40), returndatasize())
            }
            mstore(data, n) // Restore the length of `data`.
        }
    }

    /// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
    function predictDeterministicAddressERC1967Bootstrap() internal view returns (address) {
        return predictDeterministicAddressERC1967Bootstrap(address(this), address(this));
    }

    /// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
    function predictDeterministicAddressERC1967Bootstrap(
        address authorizedUpgrader,
        address deployer
    ) internal pure returns (address) {
        bytes32 hash = initCodeHashERC1967Bootstrap(authorizedUpgrader);
        return predictDeterministicAddress(hash, bytes32(0), deployer);
    }

    /// @dev Returns the initialization code of the ERC1967 bootstrap.
    function initCodeERC1967Bootstrap(address authorizedUpgrader)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x80), 0x3d3560601c5af46047573d6000383e3d38fd0000000000000000000000000000)
            mstore(add(c, 0x60), 0xa920a3ca505d382bbc55601436116049575b005b363d3d373d3d601436036014)
            mstore(add(c, 0x40), 0x0338573d3560601c7f360894a13ba1a3210667c828492db98dca3e2076cc3735)
            mstore(add(c, 0x20), authorizedUpgrader)
            mstore(add(c, 0x0c), 0x606880600a3d393df3fe3373)
            mstore(c, 0x72)
            mstore(0x40, add(c, 0xa0))
        }
    }

    /// @dev Returns the initialization code hash of the ERC1967 bootstrap.
    function initCodeHashERC1967Bootstrap(address authorizedUpgrader)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(initCodeERC1967Bootstrap(authorizedUpgrader));
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          MINIMAL ERC1967 BEACON PROXY OPERATIONS           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: If you use this proxy, you MUST make sure that the beacon is a
    // valid ERC1967 beacon. This means that the beacon must always return a valid
    // address upon a staticcall to `implementation()`, given sufficient gas.
    // For performance, the deployment operations and the proxy assumes that the
    // beacon is always valid and will NOT validate it.

    /// @dev Deploys a minimal ERC1967 beacon proxy.
    function deployERC1967BeaconProxy(address beacon) internal returns (address instance) {
        instance = deployERC1967BeaconProxy(0, beacon);
    }

    /// @dev Deploys a minimal ERC1967 beacon proxy.
    /// Deposits `value` ETH during deployment.
    function deployERC1967BeaconProxy(uint256 value, address beacon)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 beac    | PUSH20 beac    | beac 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos beac 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot beac 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (82 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             *                                                                                  |
             * ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 60 0x20       | PUSH1 0x20       | 32                          |                 |
             * 36            | CALLDATASIZE     | cds 32                      |                 |
             * 60 0x04       | PUSH1 0x04       | 4 cds 32                    |                 |
             * 36            | CALLDATASIZE     | cds 4 cds 32                |                 |
             * 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32     |                 |
             * 60 0xe0       | PUSH1 0xe0       | 224 0x5c60da1b cds 4 cds 32 |                 |
             * 1b            | SHL              | sel cds 4 cds 32            |                 |
             * 36            | CALLDATASIZE     | cds sel cds 4 cds 32        |                 |
             * 52            | MSTORE           | cds 4 cds 32                | sel             |
             * 7f slot       | PUSH32 slot      | s cds 4 cds 32              | sel             |
             * 54            | SLOAD            | beac cds 4 cds 32           | sel             |
             * 5a            | GAS              | g beac cds 4 cds 32         | sel             |
             * fa            | STATICCALL       | succ                        | impl            |
             * 50            | POP              |                             | impl            |
             * 36            | CALLDATASIZE     | cds                         | impl            |
             * 51            | MLOAD            | impl                        | impl            |
             * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 5a         | GAS            | g impl 0 cds 0 0 | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 80         | DUP1           | 0 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x4d    | PUSH1 0x4d     | dest succ        | [0..returndatasize): returndata |
             * 57         | JUMPI          |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * fd         | REVERT         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [0..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * f3         | RETURN         |                  | [0..returndatasize): returndata |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
            instance := create(value, 0x0c, 0x74)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
    function deployDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967BeaconProxy(0, beacon, salt);
    }

    /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
            instance := create2(value, 0x0c, 0x74, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967BeaconProxy(0, beacon, salt);
    }

    /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x0c, 0x74))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x0c, 0x74, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
    function initCodeERC1967BeaconProxy(address beacon) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x74), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(c, 0x54), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(c, 0x34), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(c, 0x1d), beacon)
            mstore(add(c, 0x09), 0x60523d8160223d3973)
            mstore(add(c, 0x94), 0)
            mstore(c, 0x74) // Store the length.
            mstore(0x40, add(c, 0xa0)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy.
    function initCodeHashERC1967BeaconProxy(address beacon) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
            hash := keccak256(0x0c, 0x74)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the ERC1967 beacon proxy, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967BeaconProxy(
        address beacon,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967BeaconProxy(beacon);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*    ERC1967 BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
    function deployERC1967BeaconProxy(address beacon, bytes memory args)
        internal
        returns (address instance)
    {
        instance = deployERC1967BeaconProxy(0, beacon, args);
    }

    /// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967BeaconProxy(uint256 value, address beacon, bytes memory args)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
            mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
            instance := create(value, add(m, 0x16), add(n, 0x75))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
    function deployDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967BeaconProxy(0, beacon, args, salt);
    }

    /// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967BeaconProxy(
        uint256 value,
        address beacon,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
            mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
            instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967BeaconProxy(0, beacon, args, salt);
    }

    /// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967BeaconProxy(
        uint256 value,
        address beacon,
        bytes memory args,
        bytes32 salt
    ) internal returns (bool alreadyDeployed, address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
            mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(add(m, 0x16), add(n, 0x75)))
            mstore(0x01, shl(96, address()))
            mstore(0x15, salt)
            instance := keccak256(0x00, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
    function initCodeERC1967BeaconProxy(address beacon, bytes memory args)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(c, 0x75), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(c, 0x55), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(c, 0x35), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(c, 0x1e), beacon)
            mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
            mstore(c, add(n, 0x75)) // Store the length.
            mstore(add(c, add(n, 0x95)), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy with `args`.
    function initCodeHashERC1967BeaconProxy(address beacon, bytes memory args)
        internal
        pure
        returns (bytes32 hash)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(m, 0x8b), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
            mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
            mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
            mstore(add(m, 0x14), beacon)
            mstore(m, add(0x6100523d8160233d3973, shl(56, n)))
            hash := keccak256(add(m, 0x16), add(n, 0x75))
        }
    }

    /// @dev Returns the address of the ERC1967 beacon proxy with `args`, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967BeaconProxy(
        address beacon,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967BeaconProxy(beacon, args);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967BeaconProxy(address instance) internal view returns (bytes memory args) {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
            extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967BeaconProxy(address instance, uint256 start)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(instance, args, add(start, 0x32), add(l, 0x40))
            mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `instance` MUST be deployed via the ERC1967 beacon proxy with immutable args functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `instance` does not have any code.
    function argsOnERC1967BeaconProxy(address instance, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
            if iszero(and(0xff, mload(add(args, d)))) {
                let n := sub(extcodesize(instance), 0x52)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(args, d) // Store the length.
            mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              ERC1967I BEACON PROXY OPERATIONS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: This proxy has a special code path that activates if `calldatasize() == 1`.
    // This code path skips the delegatecall and directly returns the `implementation` address.
    // The returned implementation is guaranteed to be valid if the keccak256 of the
    // proxy's code is equal to `ERC1967_BEACON_PROXY_CODE_HASH`.
    //
    // If you use this proxy, you MUST make sure that the beacon is a
    // valid ERC1967 beacon. This means that the beacon must always return a valid
    // address upon a staticcall to `implementation()`, given sufficient gas.
    // For performance, the deployment operations and the proxy assumes that the
    // beacon is always valid and will NOT validate it.

    /// @dev Deploys a ERC1967I beacon proxy.
    function deployERC1967IBeaconProxy(address beacon) internal returns (address instance) {
        instance = deployERC1967IBeaconProxy(0, beacon);
    }

    /// @dev Deploys a ERC1967I beacon proxy.
    /// Deposits `value` ETH during deployment.
    function deployERC1967IBeaconProxy(uint256 value, address beacon)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 beac    | PUSH20 beac    | beac 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos beac 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot beac 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (87 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             *                                                                                  |
             * ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 60 0x20       | PUSH1 0x20       | 32                          |                 |
             * 36            | CALLDATASIZE     | cds 32                      |                 |
             * 60 0x04       | PUSH1 0x04       | 4 cds 32                    |                 |
             * 36            | CALLDATASIZE     | cds 4 cds 32                |                 |
             * 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32     |                 |
             * 60 0xe0       | PUSH1 0xe0       | 224 0x5c60da1b cds 4 cds 32 |                 |
             * 1b            | SHL              | sel cds 4 cds 32            |                 |
             * 36            | CALLDATASIZE     | cds sel cds 4 cds 32        |                 |
             * 52            | MSTORE           | cds 4 cds 32                | sel             |
             * 7f slot       | PUSH32 slot      | s cds 4 cds 32              | sel             |
             * 54            | SLOAD            | beac cds 4 cds 32           | sel             |
             * 5a            | GAS              | g beac cds 4 cds 32         | sel             |
             * fa            | STATICCALL       | succ                        | impl            |
             * ~~~~~~ check calldatasize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 36            | CALLDATASIZE     | cds succ                    |                 |
             * 14            | EQ               |                             | impl            |
             * 60 0x52       | PUSH1 0x52       |                             | impl            |
             * 57            | JUMPI            |                             | impl            |
             * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 36            | CALLDATASIZE     | cds                         | impl            |
             * 51            | MLOAD            | impl                        | impl            |
             * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
             * 5a         | GAS            | g impl 0 cds 0 0 | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 60 0x01    | PUSH1 0x01     | 1 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [1..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x52    | PUSH1 0x52     | dest succ        | [1..returndatasize): returndata |
             * 57         | JUMPI          |                  | [1..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [1..returndatasize): returndata |
             * 60 0x01    | PUSH1 0x01     | 1 rds            | [1..returndatasize): returndata |
             * fd         | REVERT         |                  | [1..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [1..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [1..returndatasize): returndata |
             * 60 0x01    | PUSH1 0x01     | 1 rds            | [1..returndatasize): returndata |
             * f3         | RETURN         |                  | [1..returndatasize): returndata |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
            instance := create(value, 0x07, 0x79)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
    function deployDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967IBeaconProxy(0, beacon, salt);
    }

    /// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
            instance := create2(value, 0x07, 0x79, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967IBeaconProxy(0, beacon, salt);
    }

    /// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x07, 0x79))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x07, 0x79, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the ERC1967I beacon proxy.
    function initCodeERC1967IBeaconProxy(address beacon) internal pure returns (bytes memory c) {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            mstore(add(c, 0x79), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(c, 0x59), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(c, 0x39), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(c, 0x1d), beacon)
            mstore(add(c, 0x09), 0x60573d8160223d3973)
            mstore(add(c, 0x99), 0)
            mstore(c, 0x79) // Store the length.
            mstore(0x40, add(c, 0xa0)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the ERC1967I beacon proxy.
    function initCodeHashERC1967IBeaconProxy(address beacon) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
            hash := keccak256(0x07, 0x79)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the ERC1967I beacon proxy, with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967IBeaconProxy(
        address beacon,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*    ERC1967I BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a ERC1967I beacon proxy with `args.
    function deployERC1967IBeaconProxy(address beacon, bytes memory args)
        internal
        returns (address instance)
    {
        instance = deployERC1967IBeaconProxy(0, beacon, args);
    }

    /// @dev Deploys a ERC1967I beacon proxy with `args.
    /// Deposits `value` ETH during deployment.
    function deployERC1967IBeaconProxy(uint256 value, address beacon, bytes memory args)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
            mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
            mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
            instance := create(value, add(m, 0x16), add(n, 0x7a))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
    function deployDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
    }

    /// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967IBeaconProxy(
        uint256 value,
        address beacon,
        bytes memory args,
        bytes32 salt
    ) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
            mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
            mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
            instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
    }

    /// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967IBeaconProxy(
        uint256 value,
        address beacon,
        bytes memory args,
        bytes32 salt
    ) internal returns (bool alreadyDeployed, address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            let n := mload(args)
            pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
            mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(m, 0x14), beacon)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
            mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, keccak256(add(m, 0x16), add(n, 0x7a)))
            mstore(0x01, shl(96, address()))
            mstore(0x15, salt)
            instance := keccak256(0x00, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the ERC1967I beacon proxy with `args`.
    function initCodeERC1967IBeaconProxy(address beacon, bytes memory args)
        internal
        pure
        returns (bytes memory c)
    {
        /// @solidity memory-safe-assembly
        assembly {
            c := mload(0x40)
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x9a), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(c, 0x7a), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(c, 0x5a), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(c, 0x3a), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(c, 0x1e), beacon)
            mstore(add(c, 0x0a), add(0x6100573d8160233d3973, shl(56, n)))
            mstore(add(c, add(n, 0x9a)), 0)
            mstore(c, add(n, 0x7a)) // Store the length.
            mstore(0x40, add(c, add(n, 0xba))) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the ERC1967I beacon proxy with `args`.
    function initCodeHashERC1967IBeaconProxy(address beacon, bytes memory args)
        internal
        pure
        returns (bytes32 hash)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let c := mload(0x40) // Cache the free memory pointer.
            let n := mload(args)
            // Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
            returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
            for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
                mstore(add(add(c, 0x90), i), mload(add(add(args, 0x20), i)))
            }
            mstore(add(c, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
            mstore(add(c, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
            mstore(add(c, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
            mstore(add(c, 0x14), beacon)
            mstore(c, add(0x6100573d8160233d3973, shl(56, n)))
            hash := keccak256(add(c, 0x16), add(n, 0x7a))
        }
    }

    /// @dev Returns the address of the ERC1967I beacon proxy, with  `args` and salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967IBeaconProxy(
        address beacon,
        bytes memory args,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon, args);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967IBeaconProxy(address instance)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x57))) // Store the length.
            extcodecopy(instance, add(args, 0x20), 0x57, add(mload(args), 0x20))
            mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
        }
    }

    /// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
    function argsOnERC1967IBeaconProxy(address instance, uint256 start)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            let n := and(0xffffffffff, sub(extcodesize(instance), 0x57))
            let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
            extcodecopy(instance, args, add(start, 0x37), add(l, 0x40))
            mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
            mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
        }
    }

    /// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
    /// `start` and `end` will be clamped to the range `[0, args.length]`.
    /// The `instance` MUST be deployed via the ERC1967I beacon proxy with immutable args functions.
    /// Otherwise, the behavior is undefined.
    /// Out-of-gas reverts if `instance` does not have any code.
    function argsOnERC1967IBeaconProxy(address instance, uint256 start, uint256 end)
        internal
        view
        returns (bytes memory args)
    {
        /// @solidity memory-safe-assembly
        assembly {
            args := mload(0x40)
            if iszero(lt(end, 0xffff)) { end := 0xffff }
            let d := mul(sub(end, start), lt(start, end))
            extcodecopy(instance, args, add(start, 0x37), add(d, 0x20))
            if iszero(and(0xff, mload(add(args, d)))) {
                let n := sub(extcodesize(instance), 0x57)
                returndatacopy(returndatasize(), returndatasize(), shr(40, n))
                d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
            }
            mstore(args, d) // Store the length.
            mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
            mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      OTHER OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `address(0)` if the implementation address cannot be determined.
    function implementationOf(address instance) internal view returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            for { extcodecopy(instance, 0x00, 0x00, 0x57) } 1 {} {
                if mload(0x2d) {
                    // ERC1967I and ERC1967IBeaconProxy detection.
                    if or(
                        eq(keccak256(0x00, 0x52), ERC1967I_CODE_HASH),
                        eq(keccak256(0x00, 0x57), ERC1967I_BEACON_PROXY_CODE_HASH)
                    ) {
                        pop(staticcall(gas(), instance, 0x00, 0x01, 0x00, 0x20))
                        result := mload(0x0c)
                        break
                    }
                }
                // 0age clone detection.
                result := mload(0x0b)
                codecopy(0x0b, codesize(), 0x14) // Zeroize the 20 bytes for the address.
                if iszero(xor(keccak256(0x00, 0x2c), CLONE_CODE_HASH)) { break }
                mstore(0x0b, result) // Restore the zeroized memory.
                // CWIA detection.
                result := mload(0x0a)
                codecopy(0x0a, codesize(), 0x14) // Zeroize the 20 bytes for the address.
                if iszero(xor(keccak256(0x00, 0x2d), CWIA_CODE_HASH)) { break }
                mstore(0x0a, result) // Restore the zeroized memory.
                // PUSH0 clone detection.
                result := mload(0x09)
                codecopy(0x09, codesize(), 0x14) // Zeroize the 20 bytes for the address.
                result := shr(xor(keccak256(0x00, 0x2d), PUSH0_CLONE_CODE_HASH), result)
                break
            }
            result := shr(96, result)
            mstore(0x37, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address when a contract with initialization code hash,
    /// `hash`, is deployed with `salt`, by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, hash)
            mstore(0x01, shl(96, deployer))
            mstore(0x15, salt)
            predicted := keccak256(0x00, 0x55)
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Requires that `salt` starts with either the zero address or `by`.
    function checkStartsWith(bytes32 salt, address by) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            // If the salt does not start with the zero address or `by`.
            if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) {
                mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns the `bytes32` at `offset` in `args`, without any bounds checks.
    /// To load an address, you can use `address(bytes20(argLoad(args, offset)))`.
    function argLoad(bytes memory args, uint256 offset) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(add(add(args, 0x20), offset))
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";

import {JBSucker} from "./JBSucker.sol";
import {JBOptimismSuckerDeployer} from "./deployers/JBOptimismSuckerDeployer.sol";
import {IJBOptimismSucker} from "./interfaces/IJBOptimismSucker.sol";
import {IJBSuckerDeployer} from "./interfaces/IJBSuckerDeployer.sol";
import {IOPMessenger} from "./interfaces/IOPMessenger.sol";
import {IOPStandardBridge} from "./interfaces/IOPStandardBridge.sol";
import {JBAddToBalanceMode} from "./enums/JBAddToBalanceMode.sol";
import {JBInboxTreeRoot} from "./structs/JBInboxTreeRoot.sol";
import {JBMessageRoot} from "./structs/JBMessageRoot.sol";
import {JBOutboxTree} from "./structs/JBOutboxTree.sol";
import {JBRemoteToken} from "./structs/JBRemoteToken.sol";
import {MerkleLib} from "./utils/MerkleLib.sol";

/// @notice A `JBSucker` implementation to suck tokens between two chains connected by an OP Bridge.
contract JBOptimismSucker is JBSucker, IJBOptimismSucker {
    using BitMaps for BitMaps.BitMap;
    using MerkleLib for MerkleLib.Tree;

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

    /// @notice The bridge used to bridge tokens between the local and remote chain.
    IOPStandardBridge public immutable override OPBRIDGE;

    /// @notice The messenger used to send messages between the local and remote sucker.
    IOPMessenger public immutable override OPMESSENGER;

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

    /// @param deployer A contract that deploys the clones for this contracts.
    /// @param directory A contract storing directories of terminals and controllers for each project.
    /// @param tokens A contract that manages token minting and burning.
    /// @param permissions A contract storing permissions.
    /// @param addToBalanceMode The mode of adding tokens to balance.
    constructor(
        JBOptimismSuckerDeployer deployer,
        IJBDirectory directory,
        IJBPermissions permissions,
        IJBTokens tokens,
        JBAddToBalanceMode addToBalanceMode,
        address trusted_forwarder
    )
        JBSucker(directory, permissions, tokens, addToBalanceMode, trusted_forwarder)
    {
        // Fetch the messenger and bridge by doing a callback to the deployer contract.
        OPBRIDGE = JBOptimismSuckerDeployer(deployer).opBridge();
        OPMESSENGER = JBOptimismSuckerDeployer(deployer).opMessenger();
    }

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

    /// @notice Returns the chain on which the peer is located.
    /// @return chainId of the peer.
    function peerChainId() external view virtual override returns (uint256) {
        uint256 chainId = block.chainid;
        if (chainId == 1) return 10;
        if (chainId == 10) return 1;
        if (chainId == 11_155_111) return 11_155_420;
        if (chainId == 11_155_420) return 11_155_111;
        return 0;
    }

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

    /// @notice Checks if the `sender` (`_msgSender()`) is a valid representative of the remote peer.
    /// @param sender The message's sender.
    function _isRemotePeer(address sender) internal override returns (bool valid) {
        return sender == address(OPMESSENGER) && OPMESSENGER.xDomainMessageSender() == peer();
    }

    /// @notice Use the `OPMESSENGER` to send the outbox tree for the `token` and the corresponding funds to the peer
    /// over the `OPBRIDGE`.
    /// @param transportPayment the amount of `msg.value` that is going to get paid for sending this message.
    /// @param token The token to bridge the outbox tree for.
    /// @param remoteToken Information about the remote token being bridged to.
    function _sendRootOverAMB(
        uint256 transportPayment,
        uint256,
        address token,
        uint256 amount,
        JBRemoteToken memory remoteToken,
        JBMessageRoot memory message
    )
        internal
        override
    {
        uint256 nativeValue;

        // Revert if there's a `msg.value`. The OP bridge does not expect to be paid.
        if (transportPayment != 0) {
            revert JBSucker_UnexpectedMsgValue(transportPayment);
        }

        // If the token is an ERC20, bridge it to the peer.
        // If the amount is `0` then we do not need to bridge any ERC20.
        if (token != JBConstants.NATIVE_TOKEN && amount != 0) {
            // Approve the tokens bing bridged.
            // slither-disable-next-line reentrancy-events
            SafeERC20.forceApprove({token: IERC20(token), spender: address(OPBRIDGE), value: amount});

            // Bridge the tokens to the peer sucker.
            // slither-disable-next-line reentrency-events,calls-loop
            OPBRIDGE.bridgeERC20To({
                localToken: token,
                remoteToken: remoteToken.addr,
                to: peer(),
                amount: amount,
                minGasLimit: remoteToken.minGas,
                extraData: bytes("")
            });
        } else {
            // Otherwise, the token is the native token, and the amount will be sent as `msg.value`.
            nativeValue = amount;
        }

        // Send the message to the peer with the reclaimed ETH.
        // slither-disable-next-line arbitrary-send-eth,reentrency-events,calls-loop
        OPMESSENGER.sendMessage{value: nativeValue}(
            peer(), abi.encodeCall(JBSucker.fromRemote, (message)), MESSENGER_BASE_GAS_LIMIT
        );
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {JBPermissioned} from "@bananapus/core-v5/src/abstract/JBPermissioned.sol";
import {IJBCashOutTerminal} from "@bananapus/core-v5/src/interfaces/IJBCashOutTerminal.sol";
import {IJBController} from "@bananapus/core-v5/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBPermissioned} from "@bananapus/core-v5/src/interfaces/IJBPermissioned.sol";
import {IJBTerminal} from "@bananapus/core-v5/src/interfaces/IJBTerminal.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
import {JBPermissionIds} from "@bananapus/permission-ids-v5/src/JBPermissionIds.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

import {JBAddToBalanceMode} from "./enums/JBAddToBalanceMode.sol";
import {IJBSucker} from "./interfaces/IJBSucker.sol";
import {IJBSuckerExtended} from "./interfaces/IJBSuckerExtended.sol";
import {IJBSuckerDeployer} from "./interfaces/IJBSuckerDeployer.sol";
import {JBClaim} from "./structs/JBClaim.sol";
import {JBInboxTreeRoot} from "./structs/JBInboxTreeRoot.sol";
import {JBMessageRoot} from "./structs/JBMessageRoot.sol";
import {JBOutboxTree} from "./structs/JBOutboxTree.sol";
import {JBRemoteToken} from "./structs/JBRemoteToken.sol";
import {JBTokenMapping} from "./structs/JBTokenMapping.sol";
import {MerkleLib} from "./utils/MerkleLib.sol";
import {JBSuckerState} from "./enums/JBSuckerState.sol";

/// @notice An abstract contract for bridging a Juicebox project's tokens and the corresponding funds to and from a
/// remote chain.
/// @dev Beneficiaries and balances are tracked on two merkle trees: the outbox tree is used to send from the local
/// chain to the remote chain, and the inbox tree is used to receive from the remote chain to the local chain.
/// @dev Throughout this contract, "terminal token" refers to any token accepted by a project's terminal.
/// @dev This contract does *NOT* support tokens that have a fee on regular transfers and rebasing tokens.
abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC165, IJBSuckerExtended {
    using BitMaps for BitMaps.BitMap;
    using MerkleLib for MerkleLib.Tree;
    using SafeERC20 for IERC20;

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

    error JBSucker_BelowMinGas(uint256 minGas, uint256 minGasLimit);
    error JBSucker_InsufficientBalance(uint256 amount, uint256 balance);
    error JBSucker_InvalidNativeRemoteAddress(address remoteToken);
    error JBSucker_InvalidProof(bytes32 root, bytes32 inboxRoot);
    error JBSucker_LeafAlreadyExecuted(address token, uint256 index);
    error JBSucker_ManualNotAllowed(JBAddToBalanceMode mode);
    error JBSucker_DeprecationTimestampTooSoon(uint256 givenTime, uint256 minimumTime);
    error JBSucker_NoTerminalForToken(uint256 projectId, address token);
    error JBSucker_NotPeer(address caller);
    error JBSucker_QueueInsufficientSize(uint256 amount, uint256 minimumAmount);
    error JBSucker_TokenNotMapped(address token);
    error JBSucker_TokenHasInvalidEmergencyHatchState(address token);
    error JBSucker_TokenAlreadyMapped(address localToken, address mappedTo);
    error JBSucker_UnexpectedMsgValue(uint256 value);
    error JBSucker_ExpectedMsgValue();
    error JBSucker_InsufficientMsgValue(uint256 received, uint256 expected);
    error JBSucker_ZeroBeneficiary();
    error JBSucker_ZeroERC20Token();
    error JBSucker_Deprecated();

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

    /// @notice A reasonable minimum gas limit for a basic cross-chain call. The minimum amount of gas required to call
    /// the `fromRemote` (successfully/safely) on the remote chain.
    uint32 public constant override MESSENGER_BASE_GAS_LIMIT = 300_000;

    /// @notice A reasonable minimum gas limit used when bridging ERC-20s. The minimum amount of gas required to
    /// (successfully/safely) perform a transfer on the remote chain.
    uint32 public constant override MESSENGER_ERC20_MIN_GAS_LIMIT = 200_000;

    //*********************************************************************//
    // ------------------------- internal constants ----------------------- //
    //*********************************************************************//

    /// @notice The depth of the merkle tree used to store the outbox and inbox.
    uint32 constant _TREE_DEPTH = 32;

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

    /// @notice Whether the `amountToAddToBalance` gets added to the project's balance automatically when `claim` is
    /// called or manually by calling `addOutstandingAmountToBalance`.
    JBAddToBalanceMode public immutable override ADD_TO_BALANCE_MODE;

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

    /// @notice The contract that manages token minting and burning.
    IJBTokens public immutable override TOKENS;

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

    /// @notice The address of this contract's deployer.
    address public override deployer;

    //*********************************************************************//
    // --------------------- private stored properties ------------------- //
    //*********************************************************************//

    /// @notice The timestamp after which the sucker is entirely deprecated.
    uint256 internal deprecatedAfter;

    /// @notice The ID of the project (on the local chain) that this sucker is associated with.
    uint256 private localProjectId;

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

    /// @notice Tracks whether individual leaves in a given token's merkle tree have been executed (to prevent
    /// double-spending).
    /// @dev A leaf is "executed" when the tokens it represents are minted for its beneficiary.
    /// @custom:param token The token to get the executed bitmap of.
    mapping(address token => BitMaps.BitMap) internal _executedFor;

    /// @notice The inbox merkle tree root for a given token.
    /// @custom:param token The local terminal token to get the inbox for.
    mapping(address token => JBInboxTreeRoot root) internal _inboxOf;

    /// @notice The outbox merkle tree for a given token.
    /// @custom:param token The local terminal token to get the outbox for.
    mapping(address token => JBOutboxTree) internal _outboxOf;

    /// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
    /// @custom:param token The local terminal token to get the remote token for.
    mapping(address token => JBRemoteToken remoteToken) internal _remoteTokenFor;

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

    /// @param directory A contract storing directories of terminals and controllers for each project.
    /// @param permissions A contract storing permissions.
    /// @param tokens A contract that manages token minting and burning.
    /// @param addToBalanceMode The mode of adding tokens to balance.
    constructor(
        IJBDirectory directory,
        IJBPermissions permissions,
        IJBTokens tokens,
        JBAddToBalanceMode addToBalanceMode,
        address trusted_forwarder
    )
        ERC2771Context(trusted_forwarder)
        JBPermissioned(permissions)
    {
        DIRECTORY = directory;
        TOKENS = tokens;
        ADD_TO_BALANCE_MODE = addToBalanceMode;

        // Make it so the singleton can't be initialized.
        _disableInitializers();

        // Sanity check: make sure the merkle lib uses the same tree depth.
        assert(MerkleLib.TREE_DEPTH == _TREE_DEPTH);
    }

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

    /// @notice The outstanding amount of tokens to be added to the project's balance by `claim` or
    /// `addOutstandingAmountToBalance`.
    /// @param token The local terminal token to get the amount to add to balance for.
    function amountToAddToBalanceOf(address token) public view override returns (uint256) {
        // Get the amount that is in this sucker to be bridged.
        return _balanceOf(token, address(this)) - _outboxOf[token].balance;
    }

    /// @notice The inbox merkle tree root for a given token.
    /// @param token The local terminal token to get the inbox for.
    function inboxOf(address token) external view returns (JBInboxTreeRoot memory) {
        return _inboxOf[token];
    }

    /// @notice Checks whether the specified token is mapped to a remote token.
    /// @param token The terminal token to check.
    /// @return A boolean which is `true` if the token is mapped to a remote token and `false` if it is not.
    function isMapped(address token) external view override returns (bool) {
        return _remoteTokenFor[token].addr != address(0);
    }

    /// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
    /// @param token The local terminal token to get the remote token for.
    function outboxOf(address token) external view returns (JBOutboxTree memory) {
        return _outboxOf[token];
    }

    /// @notice Returns the chain on which the peer is located.
    /// @return chain ID of the peer.
    function peerChainId() external view virtual returns (uint256);

    /// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
    /// @param token The local terminal token to get the remote token for.
    function remoteTokenFor(address token) external view returns (JBRemoteToken memory) {
        return _remoteTokenFor[token];
    }

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

    /// @notice The peer sucker on the remote chain.
    function peer() public view virtual returns (address) {
        /// This can be overridden by the inheriting contract to return a different address. This is fully supported by
        /// the sucker implementation and all its off-chain infrastructure, This does however break some
        /// invariants/assumptions, for revnets it would break the assumption of matching configurations on both chains,
        /// for this reason we only support a matching address.

        // The peer is at the same address on the other chain.
        return address(this);
    }

    /// @notice The ID of the project (on the local chain) that this sucker is associated with.
    function projectId() public view returns (uint256) {
        return localProjectId;
    }

    /// @notice Reports the deprecation state of the sucker.
    /// @return state The current deprecation state
    function state() public view override returns (JBSuckerState) {
        uint256 _deprecatedAfter = deprecatedAfter;

        // The sucker is fully functional, no deprecation has been set yet.
        if (_deprecatedAfter == 0) {
            return JBSuckerState.ENABLED;
        }

        // The sucker will soon be considered deprecated, this functions only as a warning to users.
        if (block.timestamp < _deprecatedAfter - _maxMessagingDelay()) {
            return JBSuckerState.DEPRECATION_PENDING;
        }

        // The sucker will no longer send new roots to the pair, but it will accept new incoming roots.
        // Additionally it will let users exit here now that we can no longer send roots/tokens.
        if (block.timestamp < _deprecatedAfter) {
            return JBSuckerState.SENDING_DISABLED;
        }

        // The sucker is now in the final state of deprecation. It will no longer allow new roots.
        return JBSuckerState.DEPRECATED;
    }

    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return interfaceId == type(IJBSuckerExtended).interfaceId || interfaceId == type(IJBSucker).interfaceId
            || interfaceId == type(IJBPermissioned).interfaceId || super.supportsInterface(interfaceId);
    }

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

    /// @notice Helper to get the `addr`'s balance for a given `token`.
    /// @param token The token to get the balance for.
    /// @param addr The address to get the `token` balance of.
    /// @return balance The address' `token` balance.
    function _balanceOf(address token, address addr) internal view returns (uint256 balance) {
        if (token == JBConstants.NATIVE_TOKEN) {
            return addr.balance;
        }

        // slither-disable-next-line calls-loop
        return IERC20(token).balanceOf(addr);
    }

    /// @notice Builds a hash as they are stored in the merkle tree.
    /// @param projectTokenCount The number of project tokens being cashed out.
    /// @param terminalTokenAmount The amount of terminal tokens being reclaimed by the cash out.
    /// @param beneficiary The beneficiary which will receive the project tokens.
    function _buildTreeHash(
        uint256 projectTokenCount,
        uint256 terminalTokenAmount,
        address beneficiary
    )
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encode(projectTokenCount, terminalTokenAmount, beneficiary));
    }

    /// @notice Allow sucker implementations to add/override mapping rules to suite their specific needs.
    function _validateTokenMapping(JBTokenMapping calldata map) internal pure virtual {
        bool isNative = map.localToken == JBConstants.NATIVE_TOKEN;

        // If the token being mapped is the native token, the `remoteToken` must also be the native token.
        // The native token can also be mapped to the 0 address, which is used to disable native token bridging.
        if (isNative && map.remoteToken != JBConstants.NATIVE_TOKEN && map.remoteToken != address(0)) {
            revert JBSucker_InvalidNativeRemoteAddress(map.remoteToken);
        }

        // Enforce a reasonable minimum gas limit for bridging. A minimum which is too low could lead to the loss of
        // funds.
        if (map.minGas < MESSENGER_ERC20_MIN_GAS_LIMIT && !isNative) {
            revert JBSucker_BelowMinGas(map.minGas, MESSENGER_ERC20_MIN_GAS_LIMIT);
        }
    }

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

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

    /// @notice Initializes the sucker with the project ID and peer address.
    /// @param _projectId The ID of the project (on the local chain) that this sucker is associated with.
    function initialize(uint256 _projectId) public initializer {
        // slither-disable-next-line missing-zero-check
        localProjectId = _projectId;
        deployer = msg.sender;
    }

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

    /// @notice Adds the reclaimed `token` balance to the projects terminal. Can only be used if `ADD_TO_BALANCE_MODE`
    /// is
    /// `MANUAL`.
    /// @param token The address of the terminal token to add to the project's balance.
    function addOutstandingAmountToBalance(address token) external override {
        if (ADD_TO_BALANCE_MODE != JBAddToBalanceMode.MANUAL) {
            revert JBSucker_ManualNotAllowed(ADD_TO_BALANCE_MODE);
        }

        // Add entire outstanding amount to the project's balance.
        _addToBalance({token: token, amount: amountToAddToBalanceOf(token)});
    }

    /// @notice Performs multiple claims.
    /// @param claims A list of claims to perform (including the terminal token, merkle tree leaf, and proof for each
    /// claim).
    function claim(JBClaim[] calldata claims) external override {
        // Claim each.
        for (uint256 i; i < claims.length; i++) {
            claim(claims[i]);
        }
    }

    /// @notice `JBClaim` project tokens which have been bridged from the remote chain for their beneficiary.
    /// @param claimData The terminal token, merkle tree leaf, and proof for the claim.
    function claim(JBClaim calldata claimData) public override {
        // Attempt to validate the proof against the inbox tree for the terminal token.
        _validate({
            projectTokenCount: claimData.leaf.projectTokenCount,
            terminalToken: claimData.token,
            terminalTokenAmount: claimData.leaf.terminalTokenAmount,
            beneficiary: claimData.leaf.beneficiary,
            index: claimData.leaf.index,
            leaves: claimData.proof
        });

        emit Claimed({
            beneficiary: claimData.leaf.beneficiary,
            token: claimData.token,
            projectTokenCount: claimData.leaf.projectTokenCount,
            terminalTokenAmount: claimData.leaf.terminalTokenAmount,
            index: claimData.leaf.index,
            autoAddedToBalance: ADD_TO_BALANCE_MODE == JBAddToBalanceMode.ON_CLAIM ? true : false,
            caller: _msgSender()
        });

        // Give the user their project tokens, send the project its funds.
        _handleClaim({
            terminalToken: claimData.token,
            terminalTokenAmount: claimData.leaf.terminalTokenAmount,
            projectTokenAmount: claimData.leaf.projectTokenCount,
            beneficiary: claimData.leaf.beneficiary
        });
    }

    /// @notice Receive a merkle root for a terminal token from the remote project.
    /// @dev This can only be called by the messenger contract on the local chain, with a message from the remote peer.
    /// @param root The merkle root, token, and amount being received.
    function fromRemote(JBMessageRoot calldata root) external payable {
        // Make sure that the message came from our peer.
        if (!_isRemotePeer(_msgSender())) {
            revert JBSucker_NotPeer(_msgSender());
        }

        // Get the inbox in storage.
        JBInboxTreeRoot storage inbox = _inboxOf[root.token];

        // If the received tree's nonce is greater than the current inbox tree's nonce, update the inbox tree.
        // We can't revert because this could be a native token transfer. If we reverted, we would lose the native
        // tokens.
        if (root.remoteRoot.nonce > inbox.nonce && state() != JBSuckerState.DEPRECATED) {
            inbox.nonce = root.remoteRoot.nonce;
            inbox.root = root.remoteRoot.root;
            emit NewInboxTreeRoot({
                token: root.token,
                nonce: root.remoteRoot.nonce,
                root: root.remoteRoot.root,
                caller: _msgSender()
            });
        }
    }

    /// @notice Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be
    /// bridged.
    /// @param map The local and remote terminal token addresses to map, and minimum amount/gas limits for bridging
    /// them.
    function mapToken(JBTokenMapping calldata map) public payable override {
        _mapToken({map: map, transportPaymentValue: msg.value});
    }

    /// @notice Map multiple ERC-20 tokens on the local chain to ERC-20 tokens on the remote chain, allowing those
    /// tokens to be bridged.
    /// @param maps A list of local and remote terminal token addresses to map, and minimum amount/gas limits for
    /// bridging them.
    function mapTokens(JBTokenMapping[] calldata maps) external payable override {
        uint256 numberToDisable;

        // Loop over the number of mappings and increase numberToDisable to correctly set transportPaymentValue.
        for (uint256 h; h < maps.length; h++) {
            JBOutboxTree storage _outbox = _outboxOf[maps[h].localToken];
            if (maps[h].remoteToken == address(0) && _outbox.numberOfClaimsSent != _outbox.tree.count) {
                numberToDisable++;
            }
        }

        // Perform each token mapping.
        for (uint256 i; i < maps.length; i++) {
            // slither-disable-next-line msg-value-loop
            _mapToken({map: maps[i], transportPaymentValue: numberToDisable > 0 ? msg.value / numberToDisable : 0});
        }
    }

    /// @notice Enables the emergency hatch for a list of tokens, allowing users to exit on the chain they deposited on.
    /// @dev For use when a token or a few tokens are no longer compatible with a bridge.
    /// @param tokens The terminal tokens to enable the emergency hatch for.
    function enableEmergencyHatchFor(address[] calldata tokens) external override {
        // The caller must be the project owner or have the `QUEUE_RULESETS` permission from them.
        // slither-disable-next-line calls-loop
        uint256 _projectId = projectId();

        _requirePermissionFrom({
            account: DIRECTORY.PROJECTS().ownerOf(_projectId),
            projectId: _projectId,
            permissionId: JBPermissionIds.SUCKER_SAFETY
        });

        // Enable the emergency hatch for each token.
        for (uint256 i; i < tokens.length; i++) {
            // We have an invariant where if emergencyHatch is true, enabled should be false.
            _remoteTokenFor[tokens[i]].enabled = false;
            _remoteTokenFor[tokens[i]].emergencyHatch = true;
        }

        emit EmergencyHatchOpened(tokens, _msgSender());
    }

    /// @notice Prepare project tokens and the cash out amount backing them to be bridged to the remote chain.
    /// @dev This adds the tokens and funds to the outbox tree for the `token`. They will be bridged by the next call to
    /// `toRemote` for the same `token`.
    /// @param projectTokenCount The number of project tokens to prepare for bridging.
    /// @param beneficiary The address of the recipient of the tokens on the remote chain.
    /// @param minTokensReclaimed The minimum amount of terminal tokens to cash out for. If the amount cashed out is
    /// less
    /// than this, the transaction will revert.
    /// @param token The address of the terminal token to cash out for.
    function prepare(
        uint256 projectTokenCount,
        address beneficiary,
        uint256 minTokensReclaimed,
        address token
    )
        external
        override
    {
        // Make sure the beneficiary is not the zero address, as this would revert when minting on the remote chain.
        if (beneficiary == address(0)) {
            revert JBSucker_ZeroBeneficiary();
        }

        // Get the project's token.
        IERC20 projectToken = IERC20(address(TOKENS.tokenOf(projectId())));
        if (address(projectToken) == address(0)) {
            revert JBSucker_ZeroERC20Token();
        }

        // Make sure that the token is mapped to a remote token.
        if (!_remoteTokenFor[token].enabled) {
            revert JBSucker_TokenNotMapped(token);
        }

        // Make sure that the sucker still allows sending new messaged.
        JBSuckerState deprecationState = state();
        if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
            revert JBSucker_Deprecated();
        }

        // Transfer the tokens to this contract.
        // slither-disable-next-line reentrancy-events,reentrancy-benign
        projectToken.safeTransferFrom({from: _msgSender(), to: address(this), value: projectTokenCount});

        // Cash out the tokens.
        // slither-disable-next-line reentrancy-events,reentrancy-benign
        uint256 terminalTokenAmount = _pullBackingAssets({
            projectToken: projectToken,
            count: projectTokenCount,
            token: token,
            minTokensReclaimed: minTokensReclaimed
        });

        // Insert the item into the outbox tree for the terminal `token`.
        _insertIntoTree({
            projectTokenCount: projectTokenCount,
            token: token,
            terminalTokenAmount: terminalTokenAmount,
            beneficiary: beneficiary
        });
    }

    /// @notice Bridge the project tokens, cashed out funds, and beneficiary information for a given `token` to the
    /// remote
    /// chain.
    /// @dev This sends the outbox root for the specified `token` to the remote chain.
    /// @param token The terminal token being bridged.
    function toRemote(address token) external payable override {
        JBRemoteToken memory remoteToken = _remoteTokenFor[token];

        // Ensure that the token does not have an emergency hatch enabled.
        if (remoteToken.emergencyHatch) {
            revert JBSucker_TokenHasInvalidEmergencyHatchState(token);
        }

        // Ensure that the amount being bridged exceeds the minimum bridge amount.
        if (_outboxOf[token].balance < remoteToken.minBridgeAmount) {
            revert JBSucker_QueueInsufficientSize(_outboxOf[token].balance, remoteToken.minBridgeAmount);
        }

        // Send the merkle root to the remote chain.
        _sendRoot({transportPayment: msg.value, token: token, remoteToken: remoteToken});
    }

    /// @notice Lets user exit on the chain they deposited in a scenario where the bridge is no longer functional.
    /// @param claimData The terminal token, merkle tree leaf, and proof for the claim
    function exitThroughEmergencyHatch(JBClaim calldata claimData) external override {
        // Does all the needed validation to ensure that the claim is valid *and* that claiming through the emergency
        // hatch is allowed.
        _validateForEmergencyExit({
            projectTokenCount: claimData.leaf.projectTokenCount,
            terminalToken: claimData.token,
            terminalTokenAmount: claimData.leaf.terminalTokenAmount,
            beneficiary: claimData.leaf.beneficiary,
            index: claimData.leaf.index,
            leaves: claimData.proof
        });

        // Decrease the outstanding balance for this token.
        _outboxOf[claimData.token].balance -= claimData.leaf.terminalTokenAmount;

        // Give the user their project tokens, send the project its funds.
        _handleClaim({
            terminalToken: claimData.token,
            terminalTokenAmount: claimData.leaf.terminalTokenAmount,
            projectTokenAmount: claimData.leaf.projectTokenCount,
            beneficiary: claimData.leaf.beneficiary
        });
    }

    /// @notice Set or remove the time after which this sucker will be deprecated, once deprecated the sucker will no
    /// longer be functional and it will let all users exit.
    /// @param timestamp The time after which the sucker will be deprecated. Or `0` to remove the upcoming deprecation.
    function setDeprecation(uint40 timestamp) external override {
        // As long as the sucker has not started letting users withdrawal, its deprecation time can be
        // extended/shortened.
        JBSuckerState deprecationState = state();
        if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
            revert JBSucker_Deprecated();
        }

        // slither-disable-next-line calls-loop
        uint256 _projectId = projectId();

        _requirePermissionFrom({
            account: DIRECTORY.PROJECTS().ownerOf(_projectId),
            projectId: _projectId,
            permissionId: JBPermissionIds.SUCKER_SAFETY
        });

        // This is the earliest time for when the sucker can be considered deprecated.
        // There is a mandatory delay to allow for remaining messages to be received.
        // This should be called on both sides of the suckers, preferably with a matching timestamp.
        uint256 nextEarliestDeprecationTime = block.timestamp + _maxMessagingDelay();

        // The deprecation can be entirely disabled *or* it has to be later than the earliest possible time.
        if (timestamp != 0 && timestamp < nextEarliestDeprecationTime) {
            revert JBSucker_DeprecationTimestampTooSoon(timestamp, nextEarliestDeprecationTime);
        }

        deprecatedAfter = timestamp;
        emit DeprecationTimeUpdated(timestamp, _msgSender());
    }

    //*********************************************************************//
    // ---------------------------- receive  ----------------------------- //
    //*********************************************************************//

    /// @notice Used to receive cashed out native tokens.
    receive() external payable {}

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

    /// @notice Adds funds to the projects balance.
    /// @param token The terminal token to add to the project's balance.
    /// @param amount The amount of terminal tokens to add to the project's balance.
    function _addToBalance(address token, uint256 amount) internal {
        // Make sure that the current `amountToAddToBalance` is greater than or equal to the amount being added.
        uint256 addableAmount = amountToAddToBalanceOf(token);
        if (amount > addableAmount) {
            revert JBSucker_InsufficientBalance(amount, addableAmount);
        }

        uint256 _projectId = projectId();

        // Get the project's primary terminal for the token.
        // slither
        // slither-disable-next-line calls-loop
        IJBTerminal terminal = DIRECTORY.primaryTerminalOf({projectId: _projectId, token: token});

        // slither-disable-next-line incorrect-equality
        if (address(terminal) == address(0)) revert JBSucker_NoTerminalForToken(_projectId, token);

        // Perform the `addToBalance`.
        if (token != JBConstants.NATIVE_TOKEN) {
            // slither-disable-next-line calls-loop
            uint256 balanceBefore = IERC20(token).balanceOf(address(this));

            SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: amount});

            // slither-disable-next-line calls-loop
            terminal.addToBalanceOf({
                projectId: _projectId,
                token: token,
                amount: amount,
                shouldReturnHeldFees: false,
                memo: "",
                metadata: ""
            });

            // Sanity check: make sure we transfer the full amount.
            // slither-disable-next-line calls-loop,incorrect-equality
            assert(IERC20(token).balanceOf(address(this)) == balanceBefore - amount);
        } else {
            // If the token is the native token, use `msg.value`.
            // slither-disable-next-line arbitrary-send-eth,calls-loop
            terminal.addToBalanceOf{value: amount}({
                projectId: _projectId,
                token: token,
                amount: amount,
                shouldReturnHeldFees: false,
                memo: "",
                metadata: ""
            });
        }
    }

    /// @notice The action(s) to perform after a user has succesfully proven their claim.
    /// @param terminalToken The terminal token being sucked.
    /// @param terminalTokenAmount The amount of terminal tokens.
    /// @param projectTokenAmount The amount of project tokens.
    /// @param beneficiary The beneficiary of the project tokens.
    function _handleClaim(
        address terminalToken,
        uint256 terminalTokenAmount,
        uint256 projectTokenAmount,
        address beneficiary
    )
        internal
    {
        // If this contract's add to balance mode is `ON_CLAIM`, add the cashed out funds to the project's balance.
        if (ADD_TO_BALANCE_MODE == JBAddToBalanceMode.ON_CLAIM && terminalTokenAmount != 0) {
            _addToBalance({token: terminalToken, amount: terminalTokenAmount});
        }

        uint256 _projectId = projectId();

        // Mint the project tokens for the beneficiary.
        // slither-disable-next-line calls-loop,unused-return
        IJBController(address(DIRECTORY.controllerOf(_projectId))).mintTokensOf({
            projectId: _projectId,
            tokenCount: projectTokenAmount,
            beneficiary: beneficiary,
            memo: "",
            useReservedPercent: false
        });
    }

    /// @notice Inserts a new leaf into the outbox merkle tree for the specified `token`.
    /// @param projectTokenCount The amount of project tokens being cashed out.
    /// @param token The terminal token being cashed out for.
    /// @param terminalTokenAmount The amount of terminal tokens reclaimed by cashing out.
    /// @param beneficiary The beneficiary of the project tokens on the remote chain.
    function _insertIntoTree(
        uint256 projectTokenCount,
        address token,
        uint256 terminalTokenAmount,
        address beneficiary
    )
        internal
    {
        // Build a hash based on the token amounts and the beneficiary.
        bytes32 hashed = _buildTreeHash({
            projectTokenCount: projectTokenCount,
            terminalTokenAmount: terminalTokenAmount,
            beneficiary: beneficiary
        });

        // Get the outbox in storage.
        JBOutboxTree storage outbox = _outboxOf[token];

        // Create a new tree based on the outbox tree for the terminal token with the hash inserted.
        MerkleLib.Tree memory tree = outbox.tree.insert(hashed);

        // Update the outbox tree and balance for the terminal token.
        outbox.tree = tree;
        outbox.balance += terminalTokenAmount;

        emit InsertToOutboxTree({
            beneficiary: beneficiary,
            token: token,
            hashed: hashed,
            index: tree.count - 1, // Subtract 1 since we want the 0-based index.
            root: outbox.tree.root(),
            projectTokenCount: projectTokenCount,
            terminalTokenAmount: terminalTokenAmount,
            caller: _msgSender()
        });
    }

    /// @notice Checks if the `sender` (`_msgSender`) is a valid representative of the remote peer.
    /// @param sender The message's sender.
    function _isRemotePeer(address sender) internal virtual returns (bool valid);

    /// @notice Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be
    /// bridged or disabled.
    /// @param map The local and remote terminal token addresses to map, and minimum amount/gas limits for bridging
    /// them.
    /// @param transportPaymentValue The amount of `msg.value` to send for the token mapping.
    function _mapToken(JBTokenMapping calldata map, uint256 transportPaymentValue) internal {
        address token = map.localToken;
        JBRemoteToken memory currentMapping = _remoteTokenFor[token];

        // Once the emergency hatch for a token is enabled it can't be disabled.
        if (currentMapping.emergencyHatch) {
            revert JBSucker_TokenHasInvalidEmergencyHatchState(token);
        }

        // Validate the token mapping according to the rules of the sucker.
        _validateTokenMapping(map);

        // Reference the project id.
        uint256 _projectId = projectId();

        // slither-disable-next-line calls-loop
        _requirePermissionFrom({
            account: DIRECTORY.PROJECTS().ownerOf(_projectId),
            projectId: _projectId,
            permissionId: JBPermissionIds.MAP_SUCKER_TOKEN
        });

        // Make sure that the token does not get remapped to another remote token.
        // As this would cause the funds for this token to be double spendable on the other side.
        // It should not be possible to cause any issues even without this check
        // a bridge *should* never accept such a request. This is mostly a sanity check.
        if (
            currentMapping.addr != address(0) && currentMapping.addr != map.remoteToken && map.remoteToken != address(0)
                && _outboxOf[token].tree.count != 0
        ) {
            revert JBSucker_TokenAlreadyMapped(token, currentMapping.addr);
        }

        // If the remote token is being set to the 0 address (which disables bridging), send any remaining outbox funds
        // to the remote chain.
        if (map.remoteToken == address(0) && _outboxOf[token].numberOfClaimsSent != _outboxOf[token].tree.count) {
            _sendRoot({transportPayment: transportPaymentValue, token: token, remoteToken: currentMapping});
        }

        // Update the token mapping.
        _remoteTokenFor[token] = JBRemoteToken({
            enabled: map.remoteToken != address(0),
            emergencyHatch: false,
            minGas: map.minGas,
            // This is done so that a token can be disabled and then enabled again
            // while ensuring the remoteToken never changes (unless it hasn't been used yet)
            addr: map.remoteToken == address(0) ? currentMapping.addr : map.remoteToken,
            minBridgeAmount: map.minBridgeAmount
        });
    }

    /// @notice Cash out project tokens for terminal tokens.
    /// @param projectToken The project token being cashed out.
    /// @param count The number of project tokens to cash out.
    /// @param token The terminal token to cash out for.
    /// @param minTokensReclaimed The minimum amount of terminal tokens to reclaim. If the amount reclaimed is less than
    /// this, the transaction will revert.
    /// @return reclaimedAmount The amount of terminal tokens reclaimed by the cash out.
    function _pullBackingAssets(
        IERC20 projectToken,
        uint256 count,
        address token,
        uint256 minTokensReclaimed
    )
        internal
        virtual
        returns (uint256 reclaimedAmount)
    {
        projectToken;

        uint256 _projectId = projectId();

        // Get the project's primary terminal for `token`. We will cash out from this terminal.
        IJBCashOutTerminal terminal =
            IJBCashOutTerminal(address(DIRECTORY.primaryTerminalOf({projectId: _projectId, token: token})));

        // If the project doesn't have a primary terminal for `token`, revert.
        if (address(terminal) == address(0)) {
            revert JBSucker_NoTerminalForToken(_projectId, token);
        }

        // Cash out the tokens.
        uint256 balanceBefore = _balanceOf(token, address(this));
        reclaimedAmount = terminal.cashOutTokensOf({
            holder: address(this),
            projectId: _projectId,
            cashOutCount: count,
            tokenToReclaim: token,
            minTokensReclaimed: minTokensReclaimed,
            beneficiary: payable(address(this)),
            metadata: bytes("")
        });

        // Sanity check to make sure we received the expected amount.
        // This prevents malicious terminals from reporting amounts other than what they send.
        // slither-disable-next-line incorrect-equality
        assert(reclaimedAmount == _balanceOf({token: token, addr: address(this)}) - balanceBefore);
    }

    /// @notice Send the outbox root for the specified token to the remote peer.
    /// @dev The call may have a `transportPayment` for bridging native tokens. Require it to be `0` if it is not
    /// needed. Make sure if a value being paid to the bridge is expected to revert if the given value is `0`.
    /// @param transportPayment the amount of `msg.value` that is going to get paid for sending this message. (usually
    /// derived from `msg.value`)
    /// @param token The terminal token to bridge the merkle tree of.
    /// @param remoteToken The remote token which the `token` is mapped to.
    function _sendRoot(uint256 transportPayment, address token, JBRemoteToken memory remoteToken) internal virtual {
        // Ensure the token is mapped to an address on the remote chain.
        if (remoteToken.addr == address(0)) revert JBSucker_TokenNotMapped(token);

        // Make sure that the sucker still allows sending new messaged.
        JBSuckerState deprecationState = state();
        if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
            revert JBSucker_Deprecated();
        }

        // Get the outbox in storage.
        JBOutboxTree storage outbox = _outboxOf[token];

        // Get the amount to send and then clear it from the outbox tree.
        uint256 amount = outbox.balance;
        delete outbox.balance;

        // Increment the outbox tree's nonce.
        uint64 nonce = ++outbox.nonce;
        bytes32 root = outbox.tree.root();

        uint256 count = outbox.tree.count;
        // Update the numberOfClaimsSent to the current count of the tree.
        // This is used as in the fallback to allow users to withdraw locally if the bridge is reverting.
        outbox.numberOfClaimsSent = count;
        uint256 index = count - 1;

        // Emit an event for the relayers to watch for.
        emit RootToRemote({root: root, token: token, index: index, nonce: nonce, caller: _msgSender()});

        // Build the message to be send.
        JBMessageRoot memory message = JBMessageRoot({
            token: remoteToken.addr,
            amount: amount,
            remoteRoot: JBInboxTreeRoot({nonce: nonce, root: root})
        });

        // Execute the chain/sucker specific logic for transferring the assets and communicating the root.
        _sendRootOverAMB(transportPayment, index, token, amount, remoteToken, message);
    }

    /// @notice Performs the logic to send a message to the peer over the AMB.
    /// @dev This is chain/sucker/bridge specific logic.
    /// @param transportPayment The amount of `msg.value` that is going to get paid for sending this message.
    /// @param index The index of the most recent message that is part of the root.
    /// @param token The terminal token being bridged.
    /// @param amount The amount of terminal tokens being bridged.
    /// @param remoteToken The remote token which the terminal token is mapped to.
    /// @param message The message/root to send to the remote chain.
    function _sendRootOverAMB(
        uint256 transportPayment,
        uint256 index,
        address token,
        uint256 amount,
        JBRemoteToken memory remoteToken,
        JBMessageRoot memory message
    )
        internal
        virtual;

    /// @notice What is the maximum time it takes for a message to be received on the other side.
    /// @dev Be sure to keep in mind if a message fails having to retry and the time it takes to retry.
    /// @return The maximum time it takes for a message to be received on the other side.
    function _maxMessagingDelay() internal pure virtual returns (uint40) {
        return 14 days;
    }

    /// @notice Validates a leaf as being in the inbox merkle tree and registers the leaf as executed (to prevent
    /// double-spending).
    /// @dev Reverts if the leaf is invalid.
    /// @param projectTokenCount The number of project tokens which were cashed out.
    /// @param terminalToken The terminal token that the project tokens were cashed out for.
    /// @param terminalTokenAmount The amount of terminal tokens reclaimed by the cash out.
    /// @param beneficiary The beneficiary which will receive the project tokens.
    /// @param index The index of the leaf being proved in the terminal token's inbox tree.
    /// @param leaves The leaves that prove that the leaf at the `index` is in the tree (i.e. the merkle branch that the
    /// leaf is on).
    function _validate(
        uint256 projectTokenCount,
        address terminalToken,
        uint256 terminalTokenAmount,
        address beneficiary,
        uint256 index,
        bytes32[_TREE_DEPTH] calldata leaves
    )
        internal
    {
        // Make sure the leaf has not already been executed.
        if (_executedFor[terminalToken].get(index)) {
            revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
        }

        // Register the leaf as executed to prevent double-spending.
        _executedFor[terminalToken].set(index);

        // Calculate the root based on the leaf, the branch, and the index.
        // Compare to the current root, Revert if they do not match.
        _validateBranchRoot(
            _inboxOf[terminalToken].root, projectTokenCount, terminalTokenAmount, beneficiary, index, leaves
        );
    }

    /// @notice Validates a leaf as being in the outbox merkle tree and not being send over the amb, and registers the
    /// leaf as executed (to prevent double-spending).
    /// @dev Reverts if the leaf is invalid.
    /// @param projectTokenCount The number of project tokens which were cashed out.
    /// @param terminalToken The terminal token that the project tokens were cashed out for.
    /// @param terminalTokenAmount The amount of terminal tokens reclaimed by the cash out.
    /// @param beneficiary The beneficiary which will receive the project tokens.
    /// @param index The index of the leaf being proved in the terminal token's inbox tree.
    /// @param leaves The leaves that prove that the leaf at the `index` is in the tree (i.e. the merkle branch that the
    /// leaf is on).
    function _validateForEmergencyExit(
        uint256 projectTokenCount,
        address terminalToken,
        uint256 terminalTokenAmount,
        address beneficiary,
        uint256 index,
        bytes32[_TREE_DEPTH] calldata leaves
    )
        internal
    {
        // Make sure that the emergencyHatch is enabled for the token.
        JBSuckerState deprecationState = state();
        if (
            deprecationState != JBSuckerState.DEPRECATED && deprecationState != JBSuckerState.SENDING_DISABLED
                && !_remoteTokenFor[terminalToken].emergencyHatch
        ) {
            revert JBSucker_TokenHasInvalidEmergencyHatchState(terminalToken);
        }

        // Check that this claim is within the bounds of who can claim.
        // If the root that this leaf is in was already send then we can not let the user claim here.
        // As it could have also been received by the peer sucker, which would then let the user claim on each side.
        // NOTE: We are comparing the *count* and the *index*, so `count - 1` is the last index that was sent.
        // A count of 0 means that no root has ever been send for this token, so everyone can claim.
        JBOutboxTree storage outboxOfToken = _outboxOf[terminalToken];
        if (outboxOfToken.numberOfClaimsSent != 0 && outboxOfToken.numberOfClaimsSent - 1 >= index) {
            revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
        }

        {
            // We re-use the same `_executedFor` mapping but we use a different slot.
            // We can not use the regular mapping, since this claim is done for tokens being send from here to the pair.
            // where the regular mapping is for tokens that were send on the pair to here. Even though these may seem
            // similar they are actually completely unrelated.
            address emergencyExitAddress = address(bytes20(keccak256(abi.encode(terminalToken))));

            // Make sure the leaf has not already been executed.
            if (_executedFor[emergencyExitAddress].get(index)) {
                revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
            }

            // Register the leaf as executed to prevent double-spending.
            _executedFor[emergencyExitAddress].set(index);
        }

        // Calculate the root based on the leaf, the branch, and the index.
        // Compare to the current root, Revert if they do not match.
        _validateBranchRoot(
            _outboxOf[terminalToken].tree.root(), projectTokenCount, terminalTokenAmount, beneficiary, index, leaves
        );
    }

    /// @notice Validates a branch root against the expected root.
    /// @dev This is a virtual function to allow a tests to override the behavior, it should never be overwritten
    /// otherwise.
    function _validateBranchRoot(
        bytes32 expectedRoot,
        uint256 projectTokenCount,
        uint256 terminalTokenAmount,
        address beneficiary,
        uint256 index,
        bytes32[_TREE_DEPTH] calldata leaves
    )
        internal
        virtual
    {
        // Calculate the root based on the leaf, the branch, and the index.
        bytes32 root = MerkleLib.branchRoot({
            _item: _buildTreeHash({
                projectTokenCount: projectTokenCount,
                terminalTokenAmount: terminalTokenAmount,
                beneficiary: beneficiary
            }),
            _branch: leaves,
            _index: index
        });

        // Compare to the current root, Revert if they do not match.
        if (root != expectedRoot) {
            revert JBSucker_InvalidProof(root, expectedRoot);
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import "./JBSuckerDeployer.sol";
import "../interfaces/IJBArbitrumSuckerDeployer.sol";

import {JBOptimismSucker} from "../JBOptimismSucker.sol";
import {JBAddToBalanceMode} from "../enums/JBAddToBalanceMode.sol";
import {IJBOpSuckerDeployer} from "./../interfaces/IJBOpSuckerDeployer.sol";
import {IJBSuckerDeployer} from "./../interfaces/IJBSuckerDeployer.sol";
import {IOPMessenger} from "../interfaces/IOPMessenger.sol";
import {IOPStandardBridge} from "../interfaces/IOPStandardBridge.sol";

/// @notice An `IJBSuckerDeployerFeeless` implementation to deploy `JBOptimismSucker` contracts.
contract JBOptimismSuckerDeployer is JBSuckerDeployer, IJBOpSuckerDeployer {
    //*********************************************************************//
    // ---------------------- public stored properties ------------------- //
    //*********************************************************************//

    /// @notice The messenger used to send messages between the local and remote sucker.
    IOPMessenger public override opMessenger;

    /// @notice The bridge used to bridge tokens between the local and remote chain.
    IOPStandardBridge public override opBridge;

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

    /// @param directory The directory of terminals and controllers for projects.
    /// @param permissions The permissions contract for the deployer.
    /// @param tokens The contract that manages token minting and burning.
    /// @param configurator The address of the configurator.
    constructor(
        IJBDirectory directory,
        IJBPermissions permissions,
        IJBTokens tokens,
        address configurator,
        address trusted_forwarder
    )
        JBSuckerDeployer(directory, permissions, tokens, configurator, trusted_forwarder)
    {}

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

    /// @notice Check if the layer specific configuration is set or not. Used as a sanity check.
    function _layerSpecificConfigurationIsSet() internal view override returns (bool) {
        return address(opMessenger) != address(0) || address(opBridge) != address(0);
    }

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

    /// @notice handles some layer specific configuration that can't be done in the constructor otherwise deployment
    /// addresses would change.
    /// @notice messenger the OPMesssenger on this layer.
    /// @notice bridge the OPStandardBridge on this layer.
    function setChainSpecificConstants(IOPMessenger messenger, IOPStandardBridge bridge) external {
        if (_layerSpecificConfigurationIsSet()) {
            revert JBSuckerDeployer_AlreadyConfigured();
        }

        if (_msgSender() != LAYER_SPECIFIC_CONFIGURATOR) {
            revert JBSuckerDeployer_Unauthorized(_msgSender(), LAYER_SPECIFIC_CONFIGURATOR);
        }

        // Configure these layer specific properties.
        // This is done in a separate call to make the deployment code chain agnostic.
        opMessenger = messenger;
        opBridge = bridge;

        // Make sure the layer specific configuration is properly configured.
        if (!_layerSpecificConfigurationIsSet()) {
            revert JBSuckerDeployer_InvalidLayerSpecificConfiguration();
        }
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {IJBSuckerDeployer} from "./../interfaces/IJBSuckerDeployer.sol";
import {IJBSucker} from "./../interfaces/IJBSucker.sol";

import {JBPermissioned} from "@bananapus/core-v5/src/abstract/JBPermissioned.sol";
import {LibClone} from "solady/src/utils/LibClone.sol";
import {JBSucker} from "../JBSucker.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";

/// @notice A base implementation for deploying suckers.
abstract contract JBSuckerDeployer is ERC2771Context, JBPermissioned, IJBSuckerDeployer {
    //*********************************************************************//
    // --------------- public immutable stored properties ---------------- //
    //*********************************************************************//

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

    /// @notice Only this address can configure this deployer, can only be used once.
    address public immutable override LAYER_SPECIFIC_CONFIGURATOR;

    /// @notice The contract that manages token minting and burning.
    IJBTokens public immutable override TOKENS;

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

    /// @notice A mapping of suckers deployed by this contract.
    mapping(address => bool) public override isSucker;

    /// @notice The singleton used to clone suckers.
    JBSucker public singleton;

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

    /// @param directory The directory of terminals and controllers for projects.
    /// @param permissions The permissions contract for the deployer.
    /// @param tokens The contract that manages token minting and burning.
    /// @param configurator The address of the configurator.
    constructor(
        IJBDirectory directory,
        IJBPermissions permissions,
        IJBTokens tokens,
        address configurator,
        address trusted_forwarder
    )
        ERC2771Context(trusted_forwarder)
        JBPermissioned(permissions)
    {
        DIRECTORY = directory;
        TOKENS = tokens;
        LAYER_SPECIFIC_CONFIGURATOR = configurator;

        // There has to be a configurator address or the layer specific configuration has to already be configured.
        if (configurator == address(0) && !_layerSpecificConfigurationIsSet()) {
            revert JBSuckerDeployer_ZeroConfiguratorAddress();
        }
    }

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

    /// @notice Check if the layer specific configuration is set or not. Used as a sanity check.
    function _layerSpecificConfigurationIsSet() internal view virtual returns (bool);

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

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

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

    /// @notice Configure the singleton instance that is used to clone suckers.
    /// @dev Can only be called *once* by the layer specific configurator.
    /// @param _singleton The address of the singleton.
    function configureSingleton(JBSucker _singleton) external {
        // Make sure only the configurator can call this function.
        if (_msgSender() != LAYER_SPECIFIC_CONFIGURATOR) {
            revert JBSuckerDeployer_Unauthorized(_msgSender(), LAYER_SPECIFIC_CONFIGURATOR);
        }

        // Ensure that the layer specific configuration is set.
        if (!_layerSpecificConfigurationIsSet()) {
            revert JBSuckerDeployer_LayerSpecificNotConfigured();
        }

        // Make sure the singleton is not already configured.
        if (address(singleton) != address(0)) revert JBSuckerDeployer_AlreadyConfigured();

        singleton = _singleton;
    }

    /// @notice Create a new `JBSucker` for a specific project.
    /// @dev Uses the sender address as the salt, which means the same sender must call this function on both chains.
    /// @param localProjectId The project's ID on the local chain.
    /// @param salt The salt to use for the `create2` address.
    /// @return sucker The address of the new sucker.
    function createForSender(
        uint256 localProjectId,
        bytes32 salt
    )
        external
        override(IJBSuckerDeployer)
        returns (IJBSucker sucker)
    {
        // Make sure that this deployer is configured properly.
        if (address(singleton) == address(0)) {
            revert JBSuckerDeployer_DeployerIsNotConfigured();
        }

        // Hash the salt with the sender address to ensure only a specific sender can create this sucker.
        salt = keccak256(abi.encodePacked(_msgSender(), salt));

        // Clone the singleton.
        sucker = IJBSucker(LibClone.cloneDeterministic(address(singleton), salt));

        // Mark it as a sucker that was deployed by this deployer.
        isSucker[address(sucker)] = true;

        // Initialize the clone.
        JBSucker(payable(address(sucker))).initialize(localProjectId);
    }
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

/// @notice Whether a `JBArbitrumSucker` is on L1 or L2.
enum JBLayer {
    L1,
    L2
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

/**
 * @title Common interface for L1 and L2 Gateway Routers
 */
interface IArbGatewayRouter {
    function defaultGateway() external view returns (address gateway);

    function getGateway(address _token) external view returns (address gateway);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {JBLayer} from "../enums/JBLayer.sol";
import {IArbGatewayRouter} from "../interfaces/IArbGatewayRouter.sol";
import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";

interface IJBArbitrumSuckerDeployer {
    function arbGatewayRouter() external view returns (IArbGatewayRouter);
    function arbInbox() external view returns (IInbox);
    function arbLayer() external view returns (JBLayer);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IOPStandardBridge} from "./IOPStandardBridge.sol";
import {IOPMessenger} from "./IOPMessenger.sol";

interface IJBOpSuckerDeployer {
    function opBridge() external view returns (IOPStandardBridge);
    function opMessenger() external view returns (IOPMessenger);
    function setChainSpecificConstants(IOPMessenger messenger, IOPStandardBridge bridge) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IOPMessenger} from "./IOPMessenger.sol";
import {IOPStandardBridge} from "./IOPStandardBridge.sol";

interface IJBOptimismSucker {
    function OPBRIDGE() external view returns (IOPStandardBridge);
    function OPMESSENGER() external view returns (IOPMessenger);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IJBController} from "@bananapus/core-v5/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core-v5/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 DIRECTORY() external view returns (IJBDirectory);
    function TOKENS() external view returns (IJBTokens);

    function deployer() external view returns (address);
    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;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core-v5/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);
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

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

// @notice Contains the IJBSucker interface and extends it with additional functions and events.
interface IJBSuckerExtended is IJBSucker {
    event EmergencyHatchOpened(address[] tokens, address caller);
    event DeprecationTimeUpdated(uint40 timestamp, address caller);

    function enableEmergencyHatchFor(address[] calldata tokens) external;
    function exitThroughEmergencyHatch(JBClaim calldata claimData) external;
    function setDeprecation(uint40 timestamp) external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

interface IOPMessenger {
    function xDomainMessageSender() external returns (address);

    function bridgeERC20To(
        address localToken,
        address remoteToken,
        address to,
        uint256 amount,
        uint32 minGasLimit,
        bytes calldata extraData
    )
        external;

    function sendMessage(address target, bytes memory message, uint32 gasLimit) external payable;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

interface IOPStandardBridge {
    /**
     * @notice Sends ERC20 tokens to a receiver's address on the other chain. Note that if the
     *         ERC20 token on the other chain does not recognize the local token as the correct
     *         pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
     *         this chain.
     *
     * @param localToken  Address of the ERC20 on this chain.
     * @param remoteToken Address of the corresponding token on the remote chain.
     * @param to          Address of the receiver.
     * @param amount      Amount of local tokens to deposit.
     * @param minGasLimit Minimum amount of gas that the bridge can be relayed with.
     * @param extraData   Extra data to be sent with the transaction. Note that the recipient will
     *                     not be triggered with this data, but it will be emitted and can be used
     *                     to identify the transaction.
     */
    function bridgeERC20To(
        address localToken,
        address remoteToken,
        address to,
        uint256 amount,
        uint32 minGasLimit,
        bytes calldata extraData
    )
        external;
}

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>

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

Please enter a contract address above to load the contract details and source code.

Context size (optional):