MON Price: $0.018961 (+0.87%)

Contract

0x74A868AC479EE145029bB80827BB77F7B7c441cB

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
357327532025-11-15 9:46:0872 days ago1763199968  Contract Creation0 MON
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PriceFeedStore

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 1000 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IPriceFeed, IUpdatablePriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";

import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
import {IPriceFeedStore, PriceUpdate} from "../interfaces/IPriceFeedStore.sol";
import {Call, ConnectedPriceFeed, PriceFeedInfo} from "../interfaces/Types.sol";

import {
    AP_BYTECODE_REPOSITORY,
    AP_INSTANCE_MANAGER_PROXY,
    AP_PRICE_FEED_STORE,
    AP_ZERO_PRICE_FEED,
    NO_VERSION_CONTROL
} from "../libraries/ContractLiterals.sol";
import {NestedPriceFeeds} from "../libraries/NestedPriceFeeds.sol";

import {DeployerTrait} from "../traits/DeployerTrait.sol";
import {ImmutableOwnableTrait} from "../traits/ImmutableOwnableTrait.sol";

/// @title Price feed store
contract PriceFeedStore is
    DeployerTrait,
    ImmutableOwnableTrait,
    PriceFeedValidationTrait,
    SanityCheckTrait,
    IPriceFeedStore
{
    using Address for address;
    using EnumerableSet for EnumerableSet.AddressSet;
    using NestedPriceFeeds for IPriceFeed;

    /// @notice Contract version
    uint256 public constant override version = 3_10;

    /// @notice Contract type
    bytes32 public constant override contractType = AP_PRICE_FEED_STORE;

    /// @notice Zero price feed address
    address public immutable override zeroPriceFeed;

    /// @dev Set of all known price feeds
    EnumerableSet.AddressSet internal _knownPriceFeeds;

    /// @dev Set of all known tokens
    EnumerableSet.AddressSet internal _knownTokens;

    /// @dev Set of all updatable price feeds
    EnumerableSet.AddressSet internal _updatablePriceFeeds;

    /// @dev Mapping from `token` to its set of allowed price feeds
    mapping(address token => EnumerableSet.AddressSet) internal _allowedPriceFeeds;

    /// @dev Mapping from a `(token, priceFeed)` pair to a timestamp when `priceFeed` was allowed for `token`
    mapping(address token => mapping(address priceFeed => uint256)) internal _allowanceTimestamps;

    /// @dev Mapping from `priceFeed` to its info
    mapping(address priceFeed => PriceFeedInfo) internal _priceFeedInfo;

    /// @notice Constructor
    /// @param addressProvider_ Address provider contract address
    constructor(address addressProvider_)
        DeployerTrait(addressProvider_)
        ImmutableOwnableTrait(
            IAddressProvider(addressProvider_).getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL)
        )
    {
        zeroPriceFeed = _deploy(AP_ZERO_PRICE_FEED, 3_10, "", bytes32(0));
    }

    // ------- //
    // GETTERS //
    // ------- //

    /// @notice Returns the list of price feeds allowed for `token`
    function getPriceFeeds(address token) public view override returns (address[] memory) {
        return _allowedPriceFeeds[token].values();
    }

    /// @notice Returns whether `priceFeed` is allowed for `token`
    function isAllowedPriceFeed(address token, address priceFeed) external view override returns (bool) {
        return _allowedPriceFeeds[token].contains(priceFeed);
    }

    /// @notice Returns the staleness period of `priceFeed`
    /// @dev Reverts if `priceFeed` is not known
    function getStalenessPeriod(address priceFeed) external view override returns (uint32) {
        if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
        return _priceFeedInfo[priceFeed].stalenessPeriod;
    }

    /// @notice Returns the timestamp when `priceFeed` was allowed for `token`
    /// @dev Reverts if `priceFeed` is not allowed for `token`
    function getAllowanceTimestamp(address token, address priceFeed) external view override returns (uint256) {
        if (!_allowedPriceFeeds[token].contains(priceFeed)) revert PriceFeedIsNotAllowedException(token, priceFeed);
        return _allowanceTimestamps[token][priceFeed];
    }

    /// @notice Returns whether `token` is known
    function isKnownToken(address token) external view override returns (bool) {
        return _knownTokens.contains(token);
    }

    /// @notice Returns the list of known tokens
    function getKnownTokens() external view override returns (address[] memory) {
        return _knownTokens.values();
    }

    /// @notice Returns the list of tokens with their allowed price feeds
    function getTokenPriceFeedsMap() external view override returns (ConnectedPriceFeed[] memory connectedPriceFeeds) {
        address[] memory tokens = _knownTokens.values();
        uint256 len = tokens.length;

        connectedPriceFeeds = new ConnectedPriceFeed[](len);
        for (uint256 i; i < len; ++i) {
            connectedPriceFeeds[i].token = tokens[i];
            connectedPriceFeeds[i].priceFeeds = getPriceFeeds(tokens[i]);
        }
    }

    /// @notice Returns whether `priceFeed` is known
    function isKnownPriceFeed(address priceFeed) external view override returns (bool) {
        return _knownPriceFeeds.contains(priceFeed);
    }

    /// @notice Returns the list of known price feeds
    function getKnownPriceFeeds() external view override returns (address[] memory) {
        return _knownPriceFeeds.values();
    }

    /// @notice Returns the info for `priceFeed`
    function priceFeedInfo(address priceFeed) external view override returns (PriceFeedInfo memory) {
        return _priceFeedInfo[priceFeed];
    }

    // ------------- //
    // CONFIGURATION //
    // ------------- //

    /// @notice Adds a new price feed to the store
    /// @param priceFeed The address of the new price feed
    /// @param stalenessPeriod Staleness period of the new price feed
    /// @param name Name of the new price feed
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if `priceFeed` is zero address or is already added
    /// @dev Validates `priceFeed`'s tree and adds all updatable price feeds from it to the store.
    function addPriceFeed(address priceFeed, uint32 stalenessPeriod, string calldata name)
        external
        override
        onlyOwner
        nonZeroAddress(priceFeed)
    {
        if (!_knownPriceFeeds.add(priceFeed)) revert PriceFeedIsAlreadyAddedException(priceFeed);

        _validatePriceFeed(priceFeed, stalenessPeriod);
        bool isExternal = _validatePriceFeedTree(priceFeed);

        _priceFeedInfo[priceFeed] = PriceFeedInfo({
            stalenessPeriod: stalenessPeriod,
            priceFeedType: isExternal ? bytes32("PRICE_FEED::EXTERNAL") : IPriceFeed(priceFeed).contractType(),
            version: isExternal ? 0 : IPriceFeed(priceFeed).version(),
            name: name
        });

        emit AddPriceFeed(priceFeed, stalenessPeriod, name);
    }

    /// @notice Forbids `priceFeed` for all tokens and removes it from the store
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if `priceFeed` is not known
    function removePriceFeed(address priceFeed) external override onlyOwner {
        if (!_knownPriceFeeds.remove(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
        delete _priceFeedInfo[priceFeed];

        uint256 numTokens = _knownTokens.length();
        for (uint256 i; i < numTokens; ++i) {
            address token = _knownTokens.at(i);
            if (_allowedPriceFeeds[token].remove(priceFeed)) {
                _allowanceTimestamps[token][priceFeed] = 0;
                emit ForbidPriceFeed(token, priceFeed);
            }
        }

        emit RemovePriceFeed(priceFeed);
    }

    /// @notice Sets `priceFeed`'s staleness period to `stalenessPeriod`
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if `priceFeed` is not known
    function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod) external override onlyOwner {
        if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
        if (_priceFeedInfo[priceFeed].stalenessPeriod == stalenessPeriod) return;

        _validatePriceFeed(priceFeed, stalenessPeriod);
        _priceFeedInfo[priceFeed].stalenessPeriod = stalenessPeriod;

        emit SetStalenessPeriod(priceFeed, stalenessPeriod);
    }

    /// @notice Allows `priceFeed` for `token`
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if `token` is zero address
    /// @dev Reverts if `priceFeed` is not known or is already allowed for `token`
    function allowPriceFeed(address token, address priceFeed) external override onlyOwner nonZeroAddress(token) {
        if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
        if (!_allowedPriceFeeds[token].add(priceFeed)) revert PriceFeedIsAlreadyAllowedException(token, priceFeed);

        _allowanceTimestamps[token][priceFeed] = block.timestamp;
        _knownTokens.add(token);

        emit AllowPriceFeed(token, priceFeed);
    }

    /// @notice Forbids `priceFeed` for `token`
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if `priceFeed` is not known or is not allowed for `token`
    function forbidPriceFeed(address token, address priceFeed) external override onlyOwner {
        if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
        if (!_allowedPriceFeeds[token].remove(priceFeed)) revert PriceFeedIsNotAllowedException(token, priceFeed);

        _allowanceTimestamps[token][priceFeed] = 0;

        emit ForbidPriceFeed(token, priceFeed);
    }

    /// @notice Executes price feed configuration `calls` with owner privileges
    /// @dev Reverts if caller is not owner
    /// @dev Reverts if any of call targets is not a known price feed
    /// @dev Reverts if any of calls renounces ownership over price feed
    function configurePriceFeeds(Call[] calldata calls) external override onlyOwner {
        uint256 numCalls = calls.length;
        for (uint256 i; i < numCalls; ++i) {
            if (!_knownPriceFeeds.contains(calls[i].target)) revert PriceFeedIsNotKnownException(calls[i].target);
            bytes4 selector = bytes4(calls[i].callData);
            if (selector == Ownable.renounceOwnership.selector) {
                revert ForbiddenConfigurationMethodException(selector);
            }
            calls[i].target.functionCall(calls[i].callData);
        }
    }

    // ------------- //
    // PRICE UPDATES //
    // ------------- //

    /// @notice Returns the list of updatable price feeds
    function getUpdatablePriceFeeds() external view override returns (address[] memory) {
        return _updatablePriceFeeds.values();
    }

    /// @notice Performs on-demand price feed updates
    /// @dev Reverts if any of the price feeds is not added to the updatable price feeds set
    function updatePrices(PriceUpdate[] calldata updates) external override {
        uint256 numUpdates = updates.length;
        for (uint256 i; i < numUpdates; ++i) {
            if (!_updatablePriceFeeds.contains(updates[i].priceFeed)) {
                revert PriceFeedIsNotUpdatableException(updates[i].priceFeed);
            }
            IUpdatablePriceFeed(updates[i].priceFeed).updatePrice(updates[i].data);
        }
    }

    // --------- //
    // INTERNALS //
    // --------- //

    /// @dev Validates `priceFeed`'s tree and adds all updatable price feeds from it to the store.
    ///      Returns whether `priceFeed` is deployed externally or via BCR.
    ///      Externally deployed price feeds are assumed to be non-updatable leaves of the tree.
    function _validatePriceFeedTree(address priceFeed) internal returns (bool) {
        if (_validatePriceFeedDeployment(priceFeed)) return true;

        if (_isUpdatable(priceFeed) && _updatablePriceFeeds.add(priceFeed)) emit AddUpdatablePriceFeed(priceFeed);
        address[] memory underlyingFeeds = IPriceFeed(priceFeed).getUnderlyingFeeds();
        uint256 numFeeds = underlyingFeeds.length;
        for (uint256 i; i < numFeeds; ++i) {
            _validatePriceFeedTree(underlyingFeeds[i]);
        }

        return false;
    }

    /// @dev Returns whether `priceFeed` is deployed externally or via BCR.
    ///      For latter case, also ensures that price feed is owned by the store.
    function _validatePriceFeedDeployment(address priceFeed) internal returns (bool) {
        if (!IBytecodeRepository(bytecodeRepository).isDeployedFromRepository(priceFeed)) return true;

        try Ownable2Step(priceFeed).acceptOwnership() {} catch {}
        try Ownable(priceFeed).owner() returns (address owner_) {
            if (owner_ != address(this)) revert PriceFeedIsNotOwnedByStore(priceFeed);
        } catch {}

        return false;
    }

    /// @dev Returns whether `priceFeed` is updatable
    function _isUpdatable(address priceFeed) internal view returns (bool) {
        try IUpdatablePriceFeed(priceFeed).updatable() returns (bool updatable) {
            return updatable;
        } catch {
            return false;
        }
    }
}

File 2 of 27 : Types.sol
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

struct AddressProviderEntry {
    bytes32 key;
    uint256 ver;
    address value;
}

struct AuditReport {
    address auditor;
    string reportUrl;
    bytes signature;
}

struct Bytecode {
    bytes32 contractType;
    uint256 version;
    bytes initCode;
    address author;
    string source;
    bytes authorSignature;
}

struct BytecodePointer {
    bytes32 contractType;
    uint256 version;
    address[] initCodePointers;
    address author;
    string source;
    bytes authorSignature;
}

struct Call {
    address target;
    bytes callData;
}

struct ConnectedPriceFeed {
    address token;
    address[] priceFeeds;
}

struct CrossChainCall {
    uint256 chainId; // 0 means to be executed on all chains
    address target;
    bytes callData;
}

struct DeployParams {
    bytes32 postfix;
    bytes32 salt;
    bytes constructorParams;
}

struct DeployResult {
    address newContract;
    Call[] onInstallOps;
}

struct MarketFactories {
    address poolFactory;
    address priceOracleFactory;
    address interestRateModelFactory;
    address rateKeeperFactory;
    address lossPolicyFactory;
}

struct PriceFeedInfo {
    string name;
    uint32 stalenessPeriod;
    bytes32 priceFeedType;
    uint256 version;
}

struct SignedBatch {
    string name;
    bytes32 prevHash;
    CrossChainCall[] calls;
    bytes[] signatures;
}

struct SignedRecoveryModeMessage {
    uint256 chainId;
    bytes32 startingBatchHash;
    bytes[] signatures;
}

struct Split {
    bool initialized;
    address[] receivers;
    uint16[] proportions;
}

struct TwoAdminProposal {
    bytes callData;
    bool confirmedByAdmin;
    bool confirmedByTreasuryProxy;
}

// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {LibString} from "@solady/utils/LibString.sol";

library Domain {
    using LibString for string;
    using LibString for bytes32;

    uint128 internal constant UNDERSCORE = 1 << 95;

    function getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) {
        if (postfix == 0) return domain;
        return string.concat(domain.fromSmallString(), "::", postfix.fromSmallString()).toSmallString();
    }

    function extractDomain(bytes32 contractType) internal pure returns (bytes32) {
        string memory str = contractType.fromSmallString();
        uint256 separatorIndex = str.indexOf("::");

        // If no separator found, treat the whole type as domain
        if (separatorIndex == LibString.NOT_FOUND) return str.toSmallString();

        return str.slice(0, separatorIndex).toSmallString();
    }

    function extractPostfix(bytes32 contractType) internal pure returns (bytes32) {
        string memory str = contractType.fromSmallString();
        uint256 separatorIndex = str.indexOf("::");

        // if no separator found, return empty postfix
        if (separatorIndex == LibString.NOT_FOUND) return bytes32(0);

        return str.slice(separatorIndex + 2).toSmallString();
    }

    function isValidContractType(bytes32 contractType) internal pure returns (bool) {
        bytes32 domain = extractDomain(contractType);
        if (!isValidDomain(domain)) return false;

        bytes32 postfix = extractPostfix(contractType);
        if (!isValidPostfix(postfix)) return false;

        // avoid the "DOMAIN::" case
        return contractType == getContractType(domain, postfix);
    }

    function isValidDomain(bytes32 domain) internal pure returns (bool) {
        return domain != 0 && _isValidString(domain.fromSmallString());
    }

    function isValidPostfix(bytes32 postfix) internal pure returns (bool) {
        return _isValidString(postfix.fromSmallString());
    }

    function _isValidString(string memory str) internal pure returns (bool) {
        return str.is7BitASCII(LibString.ALPHANUMERIC_7_BIT_ASCII | UNDERSCORE);
    }
}

// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {LibString} from "@solady/utils/LibString.sol";

import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
import {IDeployerTrait} from "../interfaces/base/IDeployerTrait.sol";

import {AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol";
import {Domain} from "../libraries/Domain.sol";

abstract contract DeployerTrait is IDeployerTrait {
    using LibString for string;
    using LibString for bytes32;

    address public immutable override addressProvider;
    address public immutable override bytecodeRepository;

    constructor(address addressProvider_) {
        addressProvider = addressProvider_;
        bytecodeRepository = _getAddressOrRevert(AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL);
    }

    function _getAddress(bytes32 key, uint256 version) internal view returns (address) {
        return IAddressProvider(addressProvider).getAddress(key, version);
    }

    function _getAddressOrRevert(bytes32 key, uint256 version) internal view returns (address) {
        return IAddressProvider(addressProvider).getAddressOrRevert(key, version);
    }

    function _getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) {
        return Domain.getContractType(domain, postfix);
    }

    function _deploy(bytes32 contractType, uint256 version, bytes memory constructorParams, bytes32 salt)
        internal
        returns (address)
    {
        return IBytecodeRepository(bytecodeRepository).deploy(contractType, version, constructorParams, salt);
    }

    function _computeAddress(
        bytes32 contractType,
        uint256 version,
        bytes memory constructorParams,
        bytes32 salt,
        address deployer
    ) internal view returns (address) {
        return IBytecodeRepository(bytecodeRepository).computeAddress(
            contractType, version, constructorParams, salt, deployer
        );
    }

    function _deployLatestPatch(
        bytes32 contractType,
        uint256 minorVersion,
        bytes memory constructorParams,
        bytes32 salt
    ) internal returns (address) {
        // NOTE: it's best to add a check that deployed contract's version matches the expected one in the governor
        return _deploy(contractType, _getLatestPatchVersion(contractType, minorVersion), constructorParams, salt);
    }

    function _computeAddressLatestPatch(
        bytes32 contractType,
        uint256 minorVersion,
        bytes memory constructorParams,
        bytes32 salt,
        address deployer
    ) internal view returns (address) {
        return _computeAddress(
            contractType, _getLatestPatchVersion(contractType, minorVersion), constructorParams, salt, deployer
        );
    }

    function _getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) internal view returns (uint256) {
        return IBytecodeRepository(bytecodeRepository).getLatestPatchVersion(contractType, minorVersion);
    }

    function _getTokenSpecificPostfix(address token) internal view returns (bytes32) {
        return IBytecodeRepository(bytecodeRepository).getTokenSpecificPostfix(token);
    }
}

File 5 of 27 : LibString.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The length of the output is too small to contain all the hex digits.
    error HexLengthInsufficient();

    /// @dev The length of the string is more than 32 bytes.
    error TooBigForSmallString();

    /// @dev The input string must be a 7-bit ASCII.
    error StringNot7BitASCII();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The constant returned when the `search` is not found in the string.
    uint256 internal constant NOT_FOUND = type(uint256).max;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;

    /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
    uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;

    /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
    uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;

    /// @dev Lookup for '0123456789'.
    uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;

    /// @dev Lookup for '0123456789abcdefABCDEF'.
    uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;

    /// @dev Lookup for '01234567'.
    uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;

    /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
    uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;

    /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
    uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;

    /// @dev Lookup for ' \t\n\r\x0b\x0c'.
    uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;

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

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(~uint256(value) + 1);
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }

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

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2 + 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value, length);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`,
    /// left-padded to an input length of `length` bytes.
    /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
    /// giving a total length of `length * 2` bytes.
    /// Reverts if `length` is too small for the output to contain all the digits.
    function toHexStringNoPrefix(uint256 value, uint256 length)
        internal
        pure
        returns (string memory str)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
            // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
            // We add 0x20 to the total and round down to a multiple of 0x20.
            // (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
            str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let start := sub(str, add(length, length))
            let w := not(1) // Tsk.
            let temp := value
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for {} 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(xor(str, start)) { break }
            }

            if temp {
                mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
                revert(0x1c, 0x04)
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2 + 2` bytes.
    function toHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x".
    /// The output excludes leading "0" from the `toHexString` output.
    /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
    function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
            str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output excludes leading "0" from the `toHexStringNoPrefix` output.
    /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
    function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
            let strLength := mload(str) // Get the length.
            str := add(str, o) // Move the pointer, accounting for leading zero.
            mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    /// As address are 20 bytes long, the output will left-padded to have
    /// a length of `20 * 2` bytes.
    function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x40 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
            str := add(mload(0x40), 0x80)
            // Allocate the memory.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end to calculate the length later.
            let end := str
            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let w := not(1) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 2)`.
                mstore8(add(str, 1), mload(and(temp, 15)))
                mstore8(str, mload(and(shr(4, temp), 15)))
                temp := shr(8, temp)
                if iszero(temp) { break }
            }

            // Compute the string's length.
            let strLength := sub(end, str)
            // Move the pointer and write the length.
            str := sub(str, 0x20)
            mstore(str, strLength)
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
    /// and the alphabets are capitalized conditionally according to
    /// https://eips.ethereum.org/EIPS/eip-55
    function toHexStringChecksummed(address value) internal pure returns (string memory str) {
        str = toHexString(value);
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
            let o := add(str, 0x22)
            let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
            let t := shl(240, 136) // `0b10001000 << 240`
            for { let i := 0 } 1 {} {
                mstore(add(i, i), mul(t, byte(i, hashed)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
            mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
            o := add(o, 0x20)
            mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
    function toHexString(address value) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(value);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hexadecimal representation of `value`.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            str := mload(0x40)

            // Allocate the memory.
            // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
            // 0x02 bytes for the prefix, and 0x28 bytes for the digits.
            // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
            mstore(0x40, add(str, 0x80))

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            str := add(str, 2)
            mstore(str, 40)

            let o := add(str, 0x20)
            mstore(add(o, 40), 0)

            value := shl(96, value)

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let i := 0 } 1 {} {
                let p := add(o, add(i, i))
                let temp := byte(i, value)
                mstore8(add(p, 1), mload(and(temp, 15)))
                mstore8(p, mload(shr(4, temp)))
                i := add(i, 1)
                if eq(i, 20) { break }
            }
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexString(bytes memory raw) internal pure returns (string memory str) {
        str = toHexStringNoPrefix(raw);
        /// @solidity memory-safe-assembly
        assembly {
            let strLength := add(mload(str), 2) // Compute the length.
            mstore(str, 0x3078) // Write the "0x" prefix.
            str := sub(str, 2) // Move the pointer.
            mstore(str, strLength) // Write the length.
        }
    }

    /// @dev Returns the hex encoded string from the raw bytes.
    /// The output is encoded using 2 hexadecimal digits per byte.
    function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(raw)
            str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
            mstore(str, add(length, length)) // Store the length of the output.

            // Store "0123456789abcdef" in scratch space.
            mstore(0x0f, 0x30313233343536373839616263646566)

            let o := add(str, 0x20)
            let end := add(raw, length)

            for {} iszero(eq(raw, end)) {} {
                raw := add(raw, 1)
                mstore8(add(o, 1), mload(and(mload(raw), 15)))
                mstore8(o, mload(and(shr(4, mload(raw)), 15)))
                o := add(o, 2)
            }
            mstore(o, 0) // Zeroize the slot after the string.
            mstore(0x40, add(o, 0x20)) // Allocate the memory.
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RUNE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the number of UTF characters in the string.
    function runeCount(string memory s) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                mstore(0x00, div(not(0), 255))
                mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for { result := 1 } 1 { result := add(result, 1) } {
                    o := add(o, byte(0, mload(shr(250, mload(o)))))
                    if iszero(lt(o, end)) { break }
                }
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string.
    /// (i.e. all characters codes are in [0..127])
    function is7BitASCII(string memory s) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            let mask := shl(7, div(not(0), 255))
            result := 1
            let n := mload(s)
            if n {
                let o := add(s, 0x20)
                let end := add(o, n)
                let last := mload(end)
                mstore(end, 0)
                for {} 1 {} {
                    if and(mask, mload(o)) {
                        result := 0
                        break
                    }
                    o := add(o, 0x20)
                    if iszero(lt(o, end)) { break }
                }
                mstore(end, last)
            }
        }
    }

    /// @dev Returns if this string is a 7-bit ASCII string,
    /// AND all characters are in the `allowed` lookup.
    /// Note: If `s` is empty, returns true regardless of `allowed`.
    function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := 1
            if mload(s) {
                let allowed_ := shr(128, shl(128, allowed))
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := and(result, shr(byte(0, mload(o)), allowed_))
                    o := add(o, 1)
                    if iszero(and(result, lt(o, end))) { break }
                }
            }
        }
    }

    /// @dev Converts the bytes in the 7-bit ASCII string `s` to
    /// an allowed lookup for use in `is7BitASCII(s, allowed)`.
    /// To save runtime gas, you can cache the result in an immutable variable.
    function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if mload(s) {
                let o := add(s, 0x20)
                let end := add(o, mload(s))
                for {} 1 {} {
                    result := or(result, shl(byte(0, mload(o)), 1))
                    o := add(o, 1)
                    if iszero(lt(o, end)) { break }
                }
                if shr(128, result) {
                    mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   BYTE STRING OPERATIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // For performance and bytecode compactness, byte string operations are restricted
    // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
    // Usage of byte string operations on charsets with runes spanning two or more bytes
    // can lead to undefined behavior.

    /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
    function replace(string memory subject, string memory search, string memory replacement)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)
            let replacementLength := mload(replacement)

            subject := add(subject, 0x20)
            search := add(search, 0x20)
            replacement := add(replacement, 0x20)
            result := add(mload(0x40), 0x20)

            let subjectEnd := add(subject, subjectLength)
            if iszero(gt(searchLength, subjectLength)) {
                let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                mstore(result, t)
                                result := add(result, 1)
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Copy the `replacement` one word at a time.
                        for { let o := 0 } 1 {} {
                            mstore(add(result, o), mload(add(replacement, o)))
                            o := add(o, 0x20)
                            if iszero(lt(o, replacementLength)) { break }
                        }
                        result := add(result, replacementLength)
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    mstore(result, t)
                    result := add(result, 1)
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
            }

            let resultRemainder := result
            result := add(mload(0x40), 0x20)
            let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
            // Copy the rest of the string one word at a time.
            for {} lt(subject, subjectEnd) {} {
                mstore(resultRemainder, mload(subject))
                resultRemainder := add(resultRemainder, 0x20)
                subject := add(subject, 0x20)
            }
            result := sub(result, 0x20)
            let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
            mstore(last, 0)
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
            mstore(result, k) // Store the length.
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for { let subjectLength := mload(subject) } 1 {} {
                if iszero(mload(search)) {
                    if iszero(gt(from, subjectLength)) {
                        result := from
                        break
                    }
                    result := subjectLength
                    break
                }
                let searchLength := mload(search)
                let subjectStart := add(subject, 0x20)

                result := not(0) // Initialize to `NOT_FOUND`.

                subject := add(subjectStart, from)
                let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)

                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(add(search, 0x20))

                if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }

                if iszero(lt(searchLength, 0x20)) {
                    for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                        if iszero(shr(m, xor(mload(subject), s))) {
                            if eq(keccak256(subject, searchLength), h) {
                                result := sub(subject, subjectStart)
                                break
                            }
                        }
                        subject := add(subject, 1)
                        if iszero(lt(subject, end)) { break }
                    }
                    break
                }
                for {} 1 {} {
                    if iszero(shr(m, xor(mload(subject), s))) {
                        result := sub(subject, subjectStart)
                        break
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from left to right.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function indexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = indexOf(subject, search, 0);
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left, starting from `from`.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search, uint256 from)
        internal
        pure
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                result := not(0) // Initialize to `NOT_FOUND`.
                let searchLength := mload(search)
                if gt(searchLength, mload(subject)) { break }
                let w := result

                let fromMax := sub(mload(subject), searchLength)
                if iszero(gt(fromMax, from)) { from := fromMax }

                let end := add(add(subject, 0x20), w)
                subject := add(add(subject, 0x20), from)
                if iszero(gt(subject, end)) { break }
                // As this function is not too often used,
                // we shall simply use keccak256 for smaller bytecode size.
                for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
                    if eq(keccak256(subject, searchLength), h) {
                        result := sub(subject, add(end, 1))
                        break
                    }
                    subject := add(subject, w) // `sub(subject, 1)`.
                    if iszero(gt(subject, end)) { break }
                }
                break
            }
        }
    }

    /// @dev Returns the byte index of the first location of `search` in `subject`,
    /// searching from right to left.
    /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
    function lastIndexOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256 result)
    {
        result = lastIndexOf(subject, search, uint256(int256(-1)));
    }

    /// @dev Returns true if `search` is found in `subject`, false otherwise.
    function contains(string memory subject, string memory search) internal pure returns (bool) {
        return indexOf(subject, search) != NOT_FOUND;
    }

    /// @dev Returns whether `subject` starts with `search`.
    function startsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                iszero(gt(searchLength, mload(subject))),
                eq(
                    keccak256(add(subject, 0x20), searchLength),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns whether `subject` ends with `search`.
    function endsWith(string memory subject, string memory search)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let searchLength := mload(search)
            let subjectLength := mload(subject)
            // Whether `search` is not longer than `subject`.
            let withinRange := iszero(gt(searchLength, subjectLength))
            // Just using keccak256 directly is actually cheaper.
            // forgefmt: disable-next-item
            result := and(
                withinRange,
                eq(
                    keccak256(
                        // `subject + 0x20 + max(subjectLength - searchLength, 0)`.
                        add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
                        searchLength
                    ),
                    keccak256(add(search, 0x20), searchLength)
                )
            )
        }
    }

    /// @dev Returns `subject` repeated `times`.
    function repeat(string memory subject, uint256 times)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(or(iszero(times), iszero(subjectLength))) {
                subject := add(subject, 0x20)
                result := mload(0x40)
                let output := add(result, 0x20)
                for {} 1 {} {
                    // Copy the `subject` one word at a time.
                    for { let o := 0 } 1 {} {
                        mstore(add(output, o), mload(add(subject, o)))
                        o := add(o, 0x20)
                        if iszero(lt(o, subjectLength)) { break }
                    }
                    output := add(output, subjectLength)
                    times := sub(times, 1)
                    if iszero(times) { break }
                }
                mstore(output, 0) // Zeroize the slot after the string.
                let resultLength := sub(output, add(result, 0x20))
                mstore(result, resultLength) // Store the length.
                // Allocate the memory.
                mstore(0x40, add(result, add(resultLength, 0x20)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
    /// `start` and `end` are byte offsets.
    function slice(string memory subject, uint256 start, uint256 end)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            if iszero(gt(subjectLength, end)) { end := subjectLength }
            if iszero(gt(subjectLength, start)) { start := subjectLength }
            if lt(start, end) {
                result := mload(0x40)
                let resultLength := sub(end, start)
                mstore(result, resultLength)
                subject := add(subject, start)
                let w := not(0x1f)
                // Copy the `subject` one word at a time, backwards.
                for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
                    mstore(add(result, o), mload(add(subject, o)))
                    o := add(o, w) // `sub(o, 0x20)`.
                    if iszero(o) { break }
                }
                // Zeroize the slot after the string.
                mstore(add(add(result, 0x20), resultLength), 0)
                // Allocate memory for the length and the bytes,
                // rounded up to a multiple of 32.
                mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
            }
        }
    }

    /// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
    /// `start` is a byte offset.
    function slice(string memory subject, uint256 start)
        internal
        pure
        returns (string memory result)
    {
        result = slice(subject, start, uint256(int256(-1)));
    }

    /// @dev Returns all the indices of `search` in `subject`.
    /// The indices are byte offsets.
    function indicesOf(string memory subject, string memory search)
        internal
        pure
        returns (uint256[] memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let subjectLength := mload(subject)
            let searchLength := mload(search)

            if iszero(gt(searchLength, subjectLength)) {
                subject := add(subject, 0x20)
                search := add(search, 0x20)
                result := add(mload(0x40), 0x20)

                let subjectStart := subject
                let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
                let h := 0
                if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
                let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
                let s := mload(search)
                for {} 1 {} {
                    let t := mload(subject)
                    // Whether the first `searchLength % 32` bytes of
                    // `subject` and `search` matches.
                    if iszero(shr(m, xor(t, s))) {
                        if h {
                            if iszero(eq(keccak256(subject, searchLength), h)) {
                                subject := add(subject, 1)
                                if iszero(lt(subject, subjectSearchEnd)) { break }
                                continue
                            }
                        }
                        // Append to `result`.
                        mstore(result, sub(subject, subjectStart))
                        result := add(result, 0x20)
                        // Advance `subject` by `searchLength`.
                        subject := add(subject, searchLength)
                        if searchLength {
                            if iszero(lt(subject, subjectSearchEnd)) { break }
                            continue
                        }
                    }
                    subject := add(subject, 1)
                    if iszero(lt(subject, subjectSearchEnd)) { break }
                }
                let resultEnd := result
                // Assign `result` to the free memory pointer.
                result := mload(0x40)
                // Store the length of `result`.
                mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
                // Allocate memory for result.
                // We allocate one more word, so this array can be recycled for {split}.
                mstore(0x40, add(resultEnd, 0x20))
            }
        }
    }

    /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
    function split(string memory subject, string memory delimiter)
        internal
        pure
        returns (string[] memory result)
    {
        uint256[] memory indices = indicesOf(subject, delimiter);
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            let indexPtr := add(indices, 0x20)
            let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
            mstore(add(indicesEnd, w), mload(subject))
            mstore(indices, add(mload(indices), 1))
            let prevIndex := 0
            for {} 1 {} {
                let index := mload(indexPtr)
                mstore(indexPtr, 0x60)
                if iszero(eq(index, prevIndex)) {
                    let element := mload(0x40)
                    let elementLength := sub(index, prevIndex)
                    mstore(element, elementLength)
                    // Copy the `subject` one word at a time, backwards.
                    for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
                        mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
                        o := add(o, w) // `sub(o, 0x20)`.
                        if iszero(o) { break }
                    }
                    // Zeroize the slot after the string.
                    mstore(add(add(element, 0x20), elementLength), 0)
                    // Allocate memory for the length and the bytes,
                    // rounded up to a multiple of 32.
                    mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
                    // Store the `element` into the array.
                    mstore(indexPtr, element)
                }
                prevIndex := add(index, mload(delimiter))
                indexPtr := add(indexPtr, 0x20)
                if iszero(lt(indexPtr, indicesEnd)) { break }
            }
            result := indices
            if iszero(mload(delimiter)) {
                result := add(indices, 0x20)
                mstore(result, sub(mload(indices), 2))
            }
        }
    }

    /// @dev Returns a concatenated string of `a` and `b`.
    /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    function concat(string memory a, string memory b)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let w := not(0x1f)
            result := mload(0x40)
            let aLength := mload(a)
            // Copy `a` one word at a time, backwards.
            for { let o := and(add(aLength, 0x20), w) } 1 {} {
                mstore(add(result, o), mload(add(a, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let bLength := mload(b)
            let output := add(result, aLength)
            // Copy `b` one word at a time, backwards.
            for { let o := and(add(bLength, 0x20), w) } 1 {} {
                mstore(add(output, o), mload(add(b, o)))
                o := add(o, w) // `sub(o, 0x20)`.
                if iszero(o) { break }
            }
            let totalLength := add(aLength, bLength)
            let last := add(add(result, 0x20), totalLength)
            // Zeroize the slot after the string.
            mstore(last, 0)
            // Stores the length.
            mstore(result, totalLength)
            // Allocate memory for the length and the bytes,
            // rounded up to a multiple of 32.
            mstore(0x40, and(add(last, 0x1f), w))
        }
    }

    /// @dev Returns a copy of the string in either lowercase or UPPERCASE.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function toCase(string memory subject, bool toUpper)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let length := mload(subject)
            if length {
                result := add(mload(0x40), 0x20)
                subject := add(subject, 1)
                let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
                let w := not(0)
                for { let o := length } 1 {} {
                    o := add(o, w)
                    let b := and(0xff, mload(add(subject, o)))
                    mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
                    if iszero(o) { break }
                }
                result := mload(0x40)
                mstore(result, length) // Store the length.
                let last := add(add(result, 0x20), length)
                mstore(last, 0) // Zeroize the slot after the string.
                mstore(0x40, add(last, 0x20)) // Allocate the memory.
            }
        }
    }

    /// @dev Returns a string from a small bytes32 string.
    /// `s` must be null-terminated, or behavior will be undefined.
    function fromSmallString(bytes32 s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let n := 0
            for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
            mstore(result, n)
            let o := add(result, 0x20)
            mstore(o, s)
            mstore(add(o, n), 0)
            mstore(0x40, add(result, 0x40))
        }
    }

    /// @dev Returns the small string, with all bytes after the first null byte zeroized.
    function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
            mstore(0x00, s)
            mstore(result, 0x00)
            result := mload(0x00)
        }
    }

    /// @dev Returns the string as a normalized null-terminated small string.
    function toSmallString(string memory s) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(s)
            if iszero(lt(result, 33)) {
                mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
                revert(0x1c, 0x04)
            }
            result := shl(shl(3, sub(32, result)), mload(add(s, result)))
        }
    }

    /// @dev Returns a lowercased copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function lower(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, false);
    }

    /// @dev Returns an UPPERCASED copy of the string.
    /// WARNING! This function is only compatible with 7-bit ASCII strings.
    function upper(string memory subject) internal pure returns (string memory result) {
        result = toCase(subject, true);
    }

    /// @dev Escapes the string to be used within HTML tags.
    function escapeHTML(string memory s) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            // Store the bytes of the packed offsets and strides into the scratch space.
            // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
            mstore(0x1f, 0x900094)
            mstore(0x08, 0xc0000000a6ab)
            // Store "&quot;&amp;&#39;&lt;&gt;" into the scratch space.
            mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                // Not in `["\"","'","&","<",">"]`.
                if iszero(and(shl(c, 1), 0x500000c400000000)) {
                    mstore8(result, c)
                    result := add(result, 1)
                    continue
                }
                let t := shr(248, mload(c))
                mstore(result, mload(and(t, 0x1f)))
                result := add(result, shr(5, t))
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
    function escapeJSON(string memory s, bool addDoubleQuotes)
        internal
        pure
        returns (string memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let end := add(s, mload(s))
            result := add(mload(0x40), 0x20)
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            // Store "\\u0000" in scratch space.
            // Store "0123456789abcdef" in scratch space.
            // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
            // into the scratch space.
            mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
            // Bitmask for detecting `["\"","\\"]`.
            let e := or(shl(0x22, 1), shl(0x5c, 1))
            for {} iszero(eq(s, end)) {} {
                s := add(s, 1)
                let c := and(mload(s), 0xff)
                if iszero(lt(c, 0x20)) {
                    if iszero(and(shl(c, 1), e)) {
                        // Not in `["\"","\\"]`.
                        mstore8(result, c)
                        result := add(result, 1)
                        continue
                    }
                    mstore8(result, 0x5c) // "\\".
                    mstore8(add(result, 1), c)
                    result := add(result, 2)
                    continue
                }
                if iszero(and(shl(c, 1), 0x3700)) {
                    // Not in `["\b","\t","\n","\f","\d"]`.
                    mstore8(0x1d, mload(shr(4, c))) // Hex value.
                    mstore8(0x1e, mload(and(c, 15))) // Hex value.
                    mstore(result, mload(0x19)) // "\\u00XX".
                    result := add(result, 6)
                    continue
                }
                mstore8(result, 0x5c) // "\\".
                mstore8(add(result, 1), mload(add(c, 8)))
                result := add(result, 2)
            }
            if addDoubleQuotes {
                mstore8(result, 34)
                result := add(1, result)
            }
            let last := result
            mstore(last, 0) // Zeroize the slot after the string.
            result := mload(0x40)
            mstore(result, sub(last, add(result, 0x20))) // Store the length.
            mstore(0x40, add(last, 0x20)) // Allocate the memory.
        }
    }

    /// @dev Escapes the string to be used within double-quotes in a JSON.
    function escapeJSON(string memory s) internal pure returns (string memory result) {
        result = escapeJSON(s, false);
    }

    /// @dev Returns whether `a` equals `b`.
    function eq(string memory a, string memory b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
        }
    }

    /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
    function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // These should be evaluated on compile time, as far as possible.
            let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
            let x := not(or(m, or(b, add(m, and(b, m)))))
            let r := shl(7, iszero(iszero(shr(128, x))))
            r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
                xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
        }
    }

    /// @dev Packs a single string with its length into a single word.
    /// Returns `bytes32(0)` if the length is zero or greater than 31.
    function packOne(string memory a) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // We don't need to zero right pad the string,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes.
                    mload(add(a, 0x1f)),
                    // `length != 0 && length < 32`. Abuses underflow.
                    // Assumes that the length is valid and within the block gas limit.
                    lt(sub(mload(a), 1), 0x1f)
                )
        }
    }

    /// @dev Unpacks a string packed using {packOne}.
    /// Returns the empty string if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packOne}, the output behavior is undefined.
    function unpackOne(bytes32 packed) internal pure returns (string memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            result := mload(0x40)
            // Allocate 2 words (1 for the length, 1 for the bytes).
            mstore(0x40, add(result, 0x40))
            // Zeroize the length slot.
            mstore(result, 0)
            // Store the length and bytes.
            mstore(add(result, 0x1f), packed)
            // Right pad with zeroes.
            mstore(add(add(result, 0x20), mload(result)), 0)
        }
    }

    /// @dev Packs two strings with their lengths into a single word.
    /// Returns `bytes32(0)` if combined length is zero or greater than 30.
    function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
        /// @solidity memory-safe-assembly
        assembly {
            let aLength := mload(a)
            // We don't need to zero right pad the strings,
            // since this is our own custom non-standard packing scheme.
            result :=
                mul(
                    // Load the length and the bytes of `a` and `b`.
                    or(
                        shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
                        mload(sub(add(b, 0x1e), aLength))
                    ),
                    // `totalLength != 0 && totalLength < 31`. Abuses underflow.
                    // Assumes that the lengths are valid and within the block gas limit.
                    lt(sub(add(aLength, mload(b)), 1), 0x1e)
                )
        }
    }

    /// @dev Unpacks strings packed using {packTwo}.
    /// Returns the empty strings if `packed` is `bytes32(0)`.
    /// If `packed` is not an output of {packTwo}, the output behavior is undefined.
    function unpackTwo(bytes32 packed)
        internal
        pure
        returns (string memory resultA, string memory resultB)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Grab the free memory pointer.
            resultA := mload(0x40)
            resultB := add(resultA, 0x40)
            // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
            mstore(0x40, add(resultB, 0x40))
            // Zeroize the length slots.
            mstore(resultA, 0)
            mstore(resultB, 0)
            // Store the lengths and bytes.
            mstore(add(resultA, 0x1f), packed)
            mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
            // Right pad with zeroes.
            mstore(add(add(resultA, 0x20), mload(resultA)), 0)
            mstore(add(add(resultB, 0x20), mload(resultB)), 0)
        }
    }

    /// @dev Directly returns `a` without copying.
    function directReturn(string memory a) internal pure {
        assembly {
            // Assumes that the string does not start from the scratch space.
            let retStart := sub(a, 0x20)
            let retUnpaddedSize := add(mload(a), 0x40)
            // Right pad with zeroes. Just in case the string is produced
            // by a method that doesn't zero right pad.
            mstore(add(retStart, retUnpaddedSize), 0)
            // Store the return offset.
            mstore(retStart, 0x20)
            // End the transaction, returning the string.
            return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
        }
    }
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {
    IPriceFeedStore as IPriceFeedStoreBase,
    PriceUpdate
} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeedStore.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IDeployerTrait} from "./base/IDeployerTrait.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {Call, ConnectedPriceFeed, PriceFeedInfo} from "./Types.sol";

/// @title Price feed store interface
interface IPriceFeedStore is IPriceFeedStoreBase, IVersion, IDeployerTrait, IImmutableOwnableTrait {
    // ------ //
    // ERRORS //
    // ------ //

    /// @notice Thrown when attempting to use a price feed that is not known by the price feed store
    error PriceFeedIsNotKnownException(address priceFeed);

    /// @notice Thrown when attempting to add a price feed that is already known by the price feed store
    error PriceFeedIsAlreadyAddedException(address priceFeed);

    /// @notice Thrown when attempting to forbid a price feed that is not allowed for a token
    error PriceFeedIsNotAllowedException(address token, address priceFeed);

    /// @notice Thrown when attempting to allow a price feed that is already allowed for a token
    error PriceFeedIsAlreadyAllowedException(address token, address priceFeed);

    /// @notice Thrown when attempting to add a price feed that is not owned by the store
    error PriceFeedIsNotOwnedByStore(address priceFeed);

    /// @notice Thrown when attempting to update a price feed that is not added to the updatable price feeds set
    error PriceFeedIsNotUpdatableException(address priceFeed);

    /// @notice Thrown when attempting to call a forbidden configuration method
    error ForbiddenConfigurationMethodException(bytes4 selector);

    // ------ //
    // EVENTS //
    // ------ //

    /// @notice Emitted when a new price feed is added to the store
    event AddPriceFeed(address indexed priceFeed, uint32 stalenessPeriod, string name);

    /// @notice Emitted when a price feed is removed from the store
    event RemovePriceFeed(address indexed priceFeed);

    /// @notice Emitted when the staleness period is set for a price feed
    event SetStalenessPeriod(address indexed priceFeed, uint32 stalenessPeriod);

    /// @notice Emitted when a price feed is allowed for a token
    event AllowPriceFeed(address indexed token, address indexed priceFeed);

    /// @notice Emitted when a price feed is forbidden for a token
    event ForbidPriceFeed(address indexed token, address indexed priceFeed);

    /// @notice Emitted when a price feed is added to the updatable price feeds set
    event AddUpdatablePriceFeed(address indexed priceFeed);

    // ------- //
    // GETTERS //
    // ------- //

    function zeroPriceFeed() external view returns (address);
    function getPriceFeeds(address token) external view returns (address[] memory);
    function isAllowedPriceFeed(address token, address priceFeed) external view returns (bool);
    function getStalenessPeriod(address priceFeed) external view override returns (uint32);
    function getAllowanceTimestamp(address token, address priceFeed) external view returns (uint256);
    function getTokenPriceFeedsMap() external view returns (ConnectedPriceFeed[] memory);
    function getKnownTokens() external view returns (address[] memory);
    function isKnownToken(address token) external view returns (bool);
    function getKnownPriceFeeds() external view returns (address[] memory);
    function isKnownPriceFeed(address priceFeed) external view returns (bool);
    function priceFeedInfo(address priceFeed) external view returns (PriceFeedInfo memory);

    // ------------- //
    // CONFIGURATION //
    // ------------- //

    function addPriceFeed(address priceFeed, uint32 stalenessPeriod, string calldata name) external;
    function removePriceFeed(address priceFeed) external;
    function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod) external;
    function allowPriceFeed(address token, address priceFeed) external;
    function forbidPriceFeed(address token, address priceFeed) external;
    function configurePriceFeeds(Call[] calldata calls) external;

    // ------------- //
    // PRICE UPDATES //
    // ------------- //

    function getUpdatablePriceFeeds() external view returns (address[] memory);
    function updatePrices(PriceUpdate[] calldata updates) external override;
}

File 7 of 27 : ContractLiterals.sol
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

uint256 constant NO_VERSION_CONTROL = 0;

// Contract types and prefixes
bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT";
bytes32 constant AP_ACL = "ACL";
bytes32 constant AP_ADDRESS_PROVIDER = "ADDRESS_PROVIDER";
bytes32 constant AP_BOT_LIST = "BOT_LIST";
bytes32 constant AP_BYTECODE_REPOSITORY = "BYTECODE_REPOSITORY";
bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER";
bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR";
bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE";
bytes32 constant AP_CREDIT_FACTORY = "CREDIT_FACTORY";
bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant AP_CROSS_CHAIN_GOVERNANCE = "CROSS_CHAIN_GOVERNANCE";
bytes32 constant AP_CROSS_CHAIN_GOVERNANCE_PROXY = "CROSS_CHAIN_GOVERNANCE_PROXY";
bytes32 constant AP_CROSS_CHAIN_MULTISIG = "CROSS_CHAIN_MULTISIG";
bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING";
bytes32 constant AP_GEAR_TOKEN = "GLOBAL::GEAR_TOKEN";
bytes32 constant AP_GOVERNOR = "GOVERNOR";
bytes32 constant AP_INSTANCE_MANAGER = "INSTANCE_MANAGER";
bytes32 constant AP_INSTANCE_MANAGER_PROXY = "INSTANCE_MANAGER_PROXY";
bytes32 constant AP_INTEREST_RATE_MODEL_DEFAULT = "IRM::DEFAULT";
bytes32 constant AP_INTEREST_RATE_MODEL_FACTORY = "INTEREST_RATE_MODEL_FACTORY";
bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR";
bytes32 constant AP_LOSS_POLICY_ALIASED = "LOSS_POLICY::ALIASED";
bytes32 constant AP_LOSS_POLICY_DEFAULT = "LOSS_POLICY::DEFAULT";
bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY";
bytes32 constant AP_MARKET_CONFIGURATOR = "MARKET_CONFIGURATOR";
bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY";
bytes32 constant AP_MARKET_CONFIGURATOR_LEGACY = "MARKET_CONFIGURATOR::LEGACY";
bytes32 constant AP_POOL = "POOL";
bytes32 constant AP_POOL_FACTORY = "POOL_FACTORY";
bytes32 constant AP_POOL_QUOTA_KEEPER = "POOL_QUOTA_KEEPER";
bytes32 constant AP_PRICE_FEED_STORE = "PRICE_FEED_STORE";
bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE";
bytes32 constant AP_PRICE_ORACLE_FACTORY = "PRICE_ORACLE_FACTORY";
bytes32 constant AP_RATE_KEEPER_FACTORY = "RATE_KEEPER_FACTORY";
bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE";
bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER";
bytes32 constant AP_TREASURY = "TREASURY";
bytes32 constant AP_TREASURY_PROXY = "TREASURY_PROXY";
bytes32 constant AP_TREASURY_SPLITTER = "TREASURY_SPLITTER";
bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN";
bytes32 constant AP_ZERO_PRICE_FEED = "PRICE_FEED::ZERO";

// Common domains
bytes32 constant DOMAIN_ACCOUNT_FACTORY = "ACCOUNT_FACTORY";
bytes32 constant DOMAIN_ADAPTER = "ADAPTER";
bytes32 constant DOMAIN_BOT = "BOT";
bytes32 constant DOMAIN_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant DOMAIN_DEGEN_NFT = "DEGEN_NFT";
bytes32 constant DOMAIN_IRM = "IRM";
bytes32 constant DOMAIN_LOSS_POLICY = "LOSS_POLICY";
bytes32 constant DOMAIN_POOL = "POOL";
bytes32 constant DOMAIN_PRICE_FEED = "PRICE_FEED";
bytes32 constant DOMAIN_RATE_KEEPER = "RATE_KEEPER";
bytes32 constant DOMAIN_ZAPPER = "ZAPPER";

// Roles
bytes32 constant ROLE_EMERGENCY_LIQUIDATOR = "EMERGENCY_LIQUIDATOR";
bytes32 constant ROLE_PAUSABLE_ADMIN = "PAUSABLE_ADMIN";
bytes32 constant ROLE_UNPAUSABLE_ADMIN = "UNPAUSABLE_ADMIN";

// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {IPriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {OptionalCall} from "@gearbox-protocol/core-v3/contracts/libraries/OptionalCall.sol";

interface NestedPriceFeedWithSingleUnderlying is IPriceFeed {
    function priceFeed() external view returns (address);
}

interface NestedPriceFeedWithMultipleUnderlyings is IPriceFeed {
    function priceFeed0() external view returns (address);
    function priceFeed1() external view returns (address);
    function priceFeed2() external view returns (address);
    function priceFeed3() external view returns (address);
    function priceFeed4() external view returns (address);
    function priceFeed5() external view returns (address);
    function priceFeed6() external view returns (address);
    function priceFeed7() external view returns (address);
}

library NestedPriceFeeds {
    using OptionalCall for address;

    uint256 constant MAX_UNDERLYING_PRICE_FEEDS = 8;

    enum NestingType {
        NO_NESTING,
        SINGLE_UNDERLYING,
        MULTIPLE_UNDERLYING
    }

    function getUnderlyingFeeds(IPriceFeed priceFeed) internal view returns (address[] memory feeds) {
        NestingType nestingType = getNestingType(priceFeed);
        if (nestingType == NestingType.SINGLE_UNDERLYING) {
            feeds = getSingleUnderlyingFeed(NestedPriceFeedWithSingleUnderlying(address(priceFeed)));
        } else if (nestingType == NestingType.MULTIPLE_UNDERLYING) {
            feeds = getMultipleUnderlyingFeeds(NestedPriceFeedWithMultipleUnderlyings(address(priceFeed)));
        }
    }

    function getNestingType(IPriceFeed priceFeed) internal view returns (NestingType) {
        (bool success,) = address(priceFeed).staticCallOptionalSafe(abi.encodeWithSignature("priceFeed()"), 10000);
        if (success) return NestingType.SINGLE_UNDERLYING;

        (success,) = address(priceFeed).staticCallOptionalSafe(abi.encodeWithSignature("priceFeed0()"), 10000);
        if (success) return NestingType.MULTIPLE_UNDERLYING;

        return NestingType.NO_NESTING;
    }

    function getSingleUnderlyingFeed(NestedPriceFeedWithSingleUnderlying priceFeed)
        internal
        view
        returns (address[] memory feeds)
    {
        feeds = new address[](1);
        feeds[0] = priceFeed.priceFeed();
    }

    function getMultipleUnderlyingFeeds(NestedPriceFeedWithMultipleUnderlyings priceFeed)
        internal
        view
        returns (address[] memory feeds)
    {
        feeds = new address[](MAX_UNDERLYING_PRICE_FEEDS);
        for (uint256 i; i < MAX_UNDERLYING_PRICE_FEEDS; ++i) {
            feeds[i] = _getPriceFeedByIndex(priceFeed, i);
            if (feeds[i] == address(0)) {
                assembly {
                    mstore(feeds, i)
                }
                break;
            }
        }
    }

    function _getPriceFeedByIndex(NestedPriceFeedWithMultipleUnderlyings priceFeed, uint256 index)
        private
        view
        returns (address)
    {
        bytes4 selector;
        if (index == 0) {
            selector = priceFeed.priceFeed0.selector;
        } else if (index == 1) {
            selector = priceFeed.priceFeed1.selector;
        } else if (index == 2) {
            selector = priceFeed.priceFeed2.selector;
        } else if (index == 3) {
            selector = priceFeed.priceFeed3.selector;
        } else if (index == 4) {
            selector = priceFeed.priceFeed4.selector;
        } else if (index == 5) {
            selector = priceFeed.priceFeed5.selector;
        } else if (index == 6) {
            selector = priceFeed.priceFeed6.selector;
        } else if (index == 7) {
            selector = priceFeed.priceFeed7.selector;
        }
        (bool success, bytes memory result) =
            address(priceFeed).staticCallOptionalSafe(abi.encodePacked(selector), 10000);
        if (!success || result.length == 0) return address(0);
        return abi.decode(result, (address));
    }
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {IAddressProvider as IAddressProviderBase} from
    "@gearbox-protocol/core-v3/contracts/interfaces/base/IAddressProvider.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {AddressProviderEntry} from "./Types.sol";

/// @title Address provider interface
interface IAddressProvider is IAddressProviderBase, IVersion, IImmutableOwnableTrait {
    // ------ //
    // EVENTS //
    // ------ //

    event SetAddress(bytes32 indexed key, uint256 indexed ver, address indexed value);

    // ------ //
    // ERRORS //
    // ------ //

    error AddressNotFoundException(bytes32 key, uint256 ver);
    error InvalidVersionException(bytes32 key, uint256 ver);
    error VersionNotFoundException(bytes32 key);
    error ZeroAddressException(bytes32 key);

    // ------- //
    // GETTERS //
    // ------- //

    function getAddress(bytes32 key, uint256 ver) external view returns (address);
    function getAddressOrRevert(bytes32 key, uint256 ver) external view override returns (address);
    function getKeys() external view returns (bytes32[] memory);
    function getVersions(bytes32 key) external view returns (uint256[] memory);
    function getAllEntries() external view returns (AddressProviderEntry[] memory);
    function getLatestVersion(bytes32 key) external view returns (uint256);
    function getLatestMinorVersion(bytes32 key, uint256 majorVersion) external view returns (uint256);
    function getLatestPatchVersion(bytes32 key, uint256 minorVersion) external view returns (uint256);

    // ------------- //
    // CONFIGURATION //
    // ------------- //

    function setAddress(bytes32 key, address value, bool saveVersion) external;
}

File 10 of 27 : ImmutableOwnableTrait.sol
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {IImmutableOwnableTrait} from "../interfaces/base/IImmutableOwnableTrait.sol";

abstract contract ImmutableOwnableTrait is IImmutableOwnableTrait {
    address public immutable override owner;

    modifier onlyOwner() {
        if (msg.sender != owner) revert CallerIsNotOwnerException(msg.sender);
        _;
    }

    constructor(address owner_) {
        owner = owner_;
    }
}

File 11 of 27 : IBytecodeRepository.sol
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {AuditReport, Bytecode} from "./Types.sol";

/// @title Bytecode repository interface
interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
    // ------ //
    // EVENTS //
    // ------ //

    event AddAuditor(address indexed auditor, string name);
    event AddPublicDomain(bytes32 indexed domain);
    event AddSystemDomain(bytes32 indexed domain);
    event AllowContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
    event AuditBytecode(bytes32 indexed bytecodeHash, address indexed auditor, string reportUrl, bytes signature);
    event DeployContract(
        bytes32 indexed bytecodeHash,
        bytes32 indexed contractType,
        uint256 indexed version,
        address contractAddress,
        bytes constructorParams
    );
    event ForbidContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
    event ForbidInitCode(bytes32 indexed initCodeHash);
    event RemoveAuditor(address indexed auditor);
    event RemoveContractTypeOwner(bytes32 indexed contractType);
    event SetContractTypeOwner(bytes32 indexed contractType, address indexed owner);
    event SetTokenSpecificPostfix(address indexed token, bytes32 indexed postfix);
    event UploadBytecode(
        bytes32 indexed bytecodeHash,
        bytes32 indexed contractType,
        uint256 indexed version,
        address author,
        string source,
        bytes signature
    );

    // ------ //
    // ERRORS //
    // ------ //

    error AuditorIsNotApprovedException(address auditor);
    error AuthorIsNotContractTypeOwnerException(bytes32 contractType, address author);
    error BytecodeIsAlreadyAllowedException(bytes32 contractType, uint256 version);
    error BytecodeIsAlreadySignedByAuditorException(bytes32 bytecodeHash, address auditor);
    error BytecodeIsNotAllowedException(bytes32 contractType, uint256 version);
    error BytecodeIsNotAuditedException(bytes32 bytecodeHash);
    error BytecodeIsNotUploadedException(bytes32 bytecodeHash);
    error CallerIsNotBytecodeAuthorException(address caller);
    error ContractIsAlreadyDeployedException(address deployedContract);
    error ContractTypeIsNotInPublicDomainException(bytes32 contractType);
    error DomainIsAlreadyMarkedAsPublicException(bytes32 domain);
    error DomainIsAlreadyMarkedAsSystemException(bytes32 domain);
    error InitCodeIsEmptyException();
    error InitCodeIsForbiddenException(bytes32 initCodeHash);
    error InvalidAuditorSignatureException(address auditor);
    error InvalidAuthorSignatureException(address author);
    error InvalidBytecodeException(bytes32 bytecodeHash);
    error InvalidContractTypeException(bytes32 contractType);
    error InvalidDomainException(bytes32 domain);
    error InvalidPostfixException(bytes32 postfix);
    error InvalidVersionException(bytes32 contractType, uint256 version);
    error VersionNotFoundException(bytes32 contractType);

    // --------------- //
    // EIP-712 GETTERS //
    // --------------- //

    function BYTECODE_TYPEHASH() external view returns (bytes32);
    function AUDIT_REPORT_TYPEHASH() external view returns (bytes32);
    function domainSeparatorV4() external view returns (bytes32);
    function computeBytecodeHash(Bytecode calldata bytecode) external view returns (bytes32);
    function computeAuditReportHash(bytes32 bytecodeHash, AuditReport calldata report)
        external
        view
        returns (bytes32);

    // ------------------- //
    // DEPLOYING CONTRACTS //
    // ------------------- //

    function isDeployedFromRepository(address deployedContract) external view returns (bool);
    function getDeployedContractBytecodeHash(address deployedContract) external view returns (bytes32);
    function computeAddress(
        bytes32 contractType,
        uint256 version,
        bytes calldata constructorParams,
        bytes32 salt,
        address deployer
    ) external view returns (address);
    function deploy(bytes32 contractType, uint256 version, bytes calldata constructorParams, bytes32 salt)
        external
        returns (address);

    // ------------------ //
    // UPLOADING BYTECODE //
    // ------------------ //

    function getBytecode(bytes32 bytecodeHash) external view returns (Bytecode memory);
    function isBytecodeUploaded(bytes32 bytecodeHash) external view returns (bool);
    function uploadBytecode(Bytecode calldata bytecode) external;

    // ----------------- //
    // AUDITING BYTECODE //
    // ----------------- //

    function isBytecodeAudited(bytes32 bytecodeHash) external view returns (bool);
    function getAuditReports(bytes32 bytecodeHash) external view returns (AuditReport[] memory);
    function getAuditReport(bytes32 bytecodeHash, uint256 index) external view returns (AuditReport memory);
    function getNumAuditReports(bytes32 bytecodeHash) external view returns (uint256);
    function submitAuditReport(bytes32 bytecodeHash, AuditReport calldata auditReport) external;

    // ----------------- //
    // ALLOWING BYTECODE //
    // ----------------- //

    function getAllowedBytecodeHash(bytes32 contractType, uint256 version) external view returns (bytes32);
    function getContractTypeOwner(bytes32 contractType) external view returns (address);
    function allowSystemContract(bytes32 bytecodeHash) external;
    function allowPublicContract(bytes32 bytecodeHash) external;
    function removePublicContractType(bytes32 contractType) external;

    // ------------------ //
    // DOMAINS MANAGEMENT //
    // ------------------ //

    function isSystemDomain(bytes32 domain) external view returns (bool);
    function getSystemDomains() external view returns (bytes32[] memory);
    function isPublicDomain(bytes32 domain) external view returns (bool);
    function getPublicDomains() external view returns (bytes32[] memory);
    function addPublicDomain(bytes32 domain) external;

    // ------------------- //
    // AUDITORS MANAGEMENT //
    // ------------------- //

    function isAuditor(address auditor) external view returns (bool);
    function getAuditors() external view returns (address[] memory);
    function getAuditorName(address auditor) external view returns (string memory);
    function addAuditor(address auditor, string calldata name) external;
    function removeAuditor(address auditor) external;

    // -------------------- //
    // FORBIDDING INIT CODE //
    // -------------------- //

    function isInitCodeForbidden(bytes32 initCodeHash) external view returns (bool);
    function forbidInitCode(bytes32 initCodeHash) external;

    // ------------------------ //
    // TOKENS WITH CUSTOM LOGIC //
    // ------------------------ //

    function getTokenSpecificPostfix(address token) external view returns (bytes32);
    function setTokenSpecificPostfix(address token, bytes32 postfix) external;

    // --------------- //
    // VERSION CONTROL //
    // --------------- //

    function getVersions(bytes32 contractType) external view returns (uint256[] memory);
    function getLatestVersion(bytes32 contractType) external view returns (uint256);
    function getLatestMinorVersion(bytes32 contractType, uint256 majorVersion) external view returns (uint256);
    function getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

/// @title Deployer trait interface
interface IDeployerTrait {
    function addressProvider() external view returns (address);

    function bytecodeRepository() external view returns (address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

pragma solidity ^0.8.0;

import "../utils/Context.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }
}

File 17 of 27 : IImmutableOwnableTrait.sol
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;

/// @title Immutable ownable trait interface
/// @notice Interface for contracts with immutable owner functionality
interface IImmutableOwnableTrait {
    error CallerIsNotOwnerException(address caller);

    /// @notice Returns the immutable owner address
    function owner() external view returns (address);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.0;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

File 19 of 27 : IExceptions.sol
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

// ------- //
// GENERAL //
// ------- //

/// @notice Thrown on attempting to set an important address to zero address
error ZeroAddressException();

/// @notice Thrown when attempting to pass a zero amount to a funding-related operation
error AmountCantBeZeroException();

/// @notice Thrown on incorrect input parameter
error IncorrectParameterException();

/// @notice Thrown when balance is insufficient to perform an operation
error InsufficientBalanceException();

/// @notice Thrown if parameter is out of range
error ValueOutOfRangeException();

/// @notice Thrown when trying to send ETH to a contract that is not allowed to receive ETH directly
error ReceiveIsNotAllowedException();

/// @notice Thrown on attempting to set an EOA as an important contract in the system
error AddressIsNotContractException(address);

/// @notice Thrown on attempting to receive a token that is not a collateral token or was forbidden
error TokenNotAllowedException();

/// @notice Thrown on attempting to add a token that is already in a collateral list
error TokenAlreadyAddedException();

/// @notice Thrown when attempting to use quota-related logic for a token that is not quoted in quota keeper
error TokenIsNotQuotedException();

/// @notice Thrown on attempting to interact with an address that is not a valid target contract
error TargetContractNotAllowedException();

/// @notice Thrown if function is not implemented
error NotImplementedException();

// ------------------ //
// CONTRACTS REGISTER //
// ------------------ //

/// @notice Thrown when an address is expected to be a registered credit manager, but is not
error RegisteredCreditManagerOnlyException();

/// @notice Thrown when an address is expected to be a registered pool, but is not
error RegisteredPoolOnlyException();

// ---------------- //
// ADDRESS PROVIDER //
// ---------------- //

/// @notice Reverts if address key isn't found in address provider
error AddressNotFoundException();

// ----------------- //
// POOL, PQK, GAUGES //
// ----------------- //

/// @notice Thrown by pool-adjacent contracts when a credit manager being connected has a wrong pool address
error IncompatibleCreditManagerException();

/// @notice Thrown when attempting to set an incompatible successor staking contract
error IncompatibleSuccessorException();

/// @notice Thrown when attempting to vote in a non-approved contract
error VotingContractNotAllowedException();

/// @notice Thrown when attempting to unvote more votes than there are
error InsufficientVotesException();

/// @notice Thrown when attempting to borrow more than the second point on a two-point curve
error BorrowingMoreThanU2ForbiddenException();

/// @notice Thrown when a credit manager attempts to borrow more than its limit in the current block, or in general
error CreditManagerCantBorrowException();

/// @notice Thrown when attempting to connect a quota keeper to an incompatible pool
error IncompatiblePoolQuotaKeeperException();

/// @notice Thrown when attempting to connect a gauge to an incompatible pool quota keeper
error IncompatibleGaugeException();

/// @notice Thrown when the quota is outside of min/max bounds
error QuotaIsOutOfBoundsException();

// -------------- //
// CREDIT MANAGER //
// -------------- //

/// @notice Thrown on failing a full collateral check after multicall
error NotEnoughCollateralException();

/// @notice Thrown if an attempt to approve a collateral token to adapter's target contract fails
error AllowanceFailedException();

/// @notice Thrown on attempting to perform an action for a credit account that does not exist
error CreditAccountDoesNotExistException();

/// @notice Thrown on configurator attempting to add more than 255 collateral tokens
error TooManyTokensException();

/// @notice Thrown if more than the maximum number of tokens were enabled on a credit account
error TooManyEnabledTokensException();

/// @notice Thrown when attempting to execute a protocol interaction without active credit account set
error ActiveCreditAccountNotSetException();

/// @notice Thrown when trying to update credit account's debt more than once in the same block
error DebtUpdatedTwiceInOneBlockException();

/// @notice Thrown when trying to repay all debt while having active quotas
error DebtToZeroWithActiveQuotasException();

/// @notice Thrown when a zero-debt account attempts to update quota
error UpdateQuotaOnZeroDebtAccountException();

/// @notice Thrown when attempting to close an account with non-zero debt
error CloseAccountWithNonZeroDebtException();

/// @notice Thrown when value of funds remaining on the account after liquidation is insufficient
error InsufficientRemainingFundsException();

/// @notice Thrown when Credit Facade tries to write over a non-zero active Credit Account
error ActiveCreditAccountOverridenException();

// ------------------- //
// CREDIT CONFIGURATOR //
// ------------------- //

/// @notice Thrown on attempting to use a non-ERC20 contract or an EOA as a token
error IncorrectTokenContractException();

/// @notice Thrown if the newly set LT if zero or greater than the underlying's LT
error IncorrectLiquidationThresholdException();

/// @notice Thrown if borrowing limits are incorrect: minLimit > maxLimit or maxLimit > blockLimit
error IncorrectLimitsException();

/// @notice Thrown if the new expiration date is less than the current expiration date or current timestamp
error IncorrectExpirationDateException();

/// @notice Thrown if a contract returns a wrong credit manager or reverts when trying to retrieve it
error IncompatibleContractException();

/// @notice Thrown if attempting to forbid an adapter that is not registered in the credit manager
error AdapterIsNotRegisteredException();

/// @notice Thrown if new credit configurator's set of allowed adapters differs from the current one
error IncorrectAdaptersSetException();

/// @notice Thrown if attempting to schedule a token's LT ramping that is too short in duration
error RampDurationTooShortException();

/// @notice Thrown if attempting to set liquidation fees such that the sum of premium and fee changes
error InconsistentLiquidationFeesException();

/// @notice Thrown if attempting to set expired liquidation fees such that the sum of premium and fee changes
error InconsistentExpiredLiquidationFeesException();

// ------------- //
// CREDIT FACADE //
// ------------- //

/// @notice Thrown when attempting to perform an action that is forbidden in whitelisted mode
error ForbiddenInWhitelistedModeException();

/// @notice Thrown if credit facade is not expirable, and attempted aciton requires expirability
error NotAllowedWhenNotExpirableException();

/// @notice Thrown if a selector that doesn't match any allowed function is passed to the credit facade in a multicall
error UnknownMethodException(bytes4 selector);

/// @notice Thrown if a liquidator tries to liquidate an account with a health factor above 1
error CreditAccountNotLiquidatableException();

/// @notice Thrown if a liquidator tries to liquidate an account with loss but violates the loss policy
error CreditAccountNotLiquidatableWithLossException();

/// @notice Thrown if too much new debt was taken within a single block
error BorrowedBlockLimitException();

/// @notice Thrown if the new debt principal for a credit account falls outside of borrowing limits
error BorrowAmountOutOfLimitsException();

/// @notice Thrown if a user attempts to open an account via an expired credit facade
error NotAllowedAfterExpirationException();

/// @notice Thrown if expected balances are attempted to be set twice without performing a slippage check
error ExpectedBalancesAlreadySetException();

/// @notice Thrown if attempting to perform a slippage check when excepted balances are not set
error ExpectedBalancesNotSetException();

/// @notice Thrown if balance of at least one token is less than expected during a slippage check
error BalanceLessThanExpectedException(address token);

/// @notice Thrown when trying to perform an action that is forbidden when credit account has enabled forbidden tokens
error ForbiddenTokensException(uint256 forbiddenTokensMask);

/// @notice Thrown when forbidden token quota is increased during the multicall
error ForbiddenTokenQuotaIncreasedException(address token);

/// @notice Thrown when enabled forbidden token balance is increased during the multicall
error ForbiddenTokenBalanceIncreasedException(address token);

/// @notice Thrown when the remaining token balance is increased during the liquidation
error RemainingTokenBalanceIncreasedException(address token);

/// @notice Thrown if `botMulticall` is called by an address that is not approved by account owner or is forbidden
error NotApprovedBotException(address bot);

/// @notice Thrown when attempting to perform a multicall action with no permission for it
error NoPermissionException(uint256 permission);

/// @notice Thrown when attempting to give a bot unexpected permissions
error UnexpectedPermissionsException(uint256 permissions);

/// @notice Thrown when a custom HF parameter lower than 10000 is passed into the full collateral check
error CustomHealthFactorTooLowException();

/// @notice Thrown when submitted collateral hint is not a valid token mask
error InvalidCollateralHintException(uint256 mask);

/// @notice Thrown when trying to seize underlying token during partial liquidation
error UnderlyingIsNotLiquidatableException();

/// @notice Thrown when amount of collateral seized during partial liquidation is less than required
error SeizedLessThanRequiredException(uint256 seizedAmount);

// ------ //
// ACCESS //
// ------ //

/// @notice Thrown on attempting to call an access restricted function not as credit account owner
error CallerNotCreditAccountOwnerException();

/// @notice Thrown on attempting to call an access restricted function not as configurator
error CallerNotConfiguratorException();

/// @notice Thrown on attempting to call an access-restructed function not as account factory
error CallerNotAccountFactoryException();

/// @notice Thrown on attempting to call an access restricted function not as credit manager
error CallerNotCreditManagerException();

/// @notice Thrown on attempting to call an access restricted function not as credit facade
error CallerNotCreditFacadeException();

/// @notice Thrown on attempting to pause a contract without pausable admin rights
error CallerNotPausableAdminException();

/// @notice Thrown on attempting to unpause a contract without unpausable admin rights
error CallerNotUnpausableAdminException();

/// @notice Thrown on attempting to call an access restricted function not as gauge
error CallerNotGaugeException();

/// @notice Thrown on attempting to call an access restricted function not as quota keeper
error CallerNotPoolQuotaKeeperException();

/// @notice Thrown on attempting to call an access restricted function not as voter
error CallerNotVoterException();

/// @notice Thrown on attempting to call an access restricted function not as allowed adapter
error CallerNotAdapterException();

/// @notice Thrown on attempting to call an access restricted function not as migrator
error CallerNotMigratorException();

/// @notice Thrown when an address that is not the designated executor attempts to execute a transaction
error CallerNotExecutorException();

/// @notice Thrown on attempting to call an access restricted function not as veto admin
error CallerNotVetoAdminException();

// -------- //
// BOT LIST //
// -------- //

/// @notice Thrown when attempting to set non-zero permissions for a forbidden bot
error InvalidBotException();

/// @notice Thrown when attempting to set permissions for a bot that don't meet its requirements
error IncorrectBotPermissionsException();

/// @notice Thrown when attempting to set non-zero permissions for too many bots
error TooManyActiveBotsException();

// --------------- //
// ACCOUNT FACTORY //
// --------------- //

/// @notice Thrown when trying to deploy second master credit account for a credit manager
error MasterCreditAccountAlreadyDeployedException();

/// @notice Thrown when trying to rescue funds from a credit account that is currently in use
error CreditAccountIsInUseException();

// ------------ //
// PRICE ORACLE //
// ------------ //

/// @notice Thrown on attempting to set a token price feed to an address that is not a correct price feed
error IncorrectPriceFeedException();

/// @notice Thrown on attempting to interact with a price feed for a token not added to the price oracle
error PriceFeedDoesNotExistException();

/// @notice Thrown when trying to apply an on-demand price update to a non-updatable price feed
error PriceFeedIsNotUpdatableException();

/// @notice Thrown when price feed returns incorrect price for a token
error IncorrectPriceException();

/// @notice Thrown when token's price feed becomes stale
error StalePriceException();

// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

/// @title Optional call library
/// @notice Implements a function that calls a contract that may not have an expected selector.
///         Handles the case where the contract has a fallback function that may or may not change state.
library OptionalCall {
    function staticCallOptionalSafe(address target, bytes memory data, uint256 gasAllowance)
        internal
        view
        returns (bool, bytes memory)
    {
        (bool success, bytes memory returnData) = target.staticcall{gas: gasAllowance}(data);
        return returnData.length > 0 ? (success, returnData) : (false, returnData);
    }
}

File 21 of 27 : SanityCheckTrait.sol
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

import {ZeroAddressException} from "../interfaces/IExceptions.sol";

/// @title Sanity check trait
abstract contract SanityCheckTrait {
    /// @dev Ensures that passed address is non-zero
    modifier nonZeroAddress(address addr) {
        _revertIfZeroAddress(addr);
        _;
    }

    /// @dev Reverts if address is zero
    function _revertIfZeroAddress(address addr) private pure {
        if (addr == address(0)) revert ZeroAddressException();
    }
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

/// @title Version interface
/// @notice Defines contract version and type
interface IVersion {
    /// @notice Contract version
    function version() external view returns (uint256);

    /// @notice Contract type
    function contractType() external view returns (bytes32);
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

import {IVersion} from "./IVersion.sol";
import {IStateSerializer} from "./IStateSerializer.sol";

/// @title Price feed interface
/// @notice Interface for Chainlink-like price feeds that can be plugged into Gearbox's price oracle
/// @dev Price feeds must have type `PRICE_FEED::{POSTFIX}`
interface IPriceFeed is IVersion, IStateSerializer {
    /// @notice Whether price feed implements its own staleness and sanity checks
    function skipPriceCheck() external view returns (bool);

    /// @notice Scale decimals of price feed answers
    function decimals() external view returns (uint8);

    /// @notice Price feed description
    function description() external view returns (string memory);

    /// @notice Price feed answer in standard Chainlink format, only `answer` and `updatedAt` fields are used
    function latestRoundData() external view returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80);
}

/// @title Updatable price feed interface
/// @notice Extended version of `IPriceFeed` for pull oracles that allow on-demand updates
interface IUpdatablePriceFeed is IPriceFeed {
    /// @notice Emitted when price is updated
    event UpdatePrice(uint256 price);

    /// @notice Whether price feed is updatable
    function updatable() external view returns (bool);

    /// @notice Performs on-demand price update
    function updatePrice(bytes calldata data) external;
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

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

struct PriceUpdate {
    address priceFeed;
    bytes data;
}

interface IPriceFeedStore {
    function getStalenessPeriod(address priceFeed) external view returns (uint32);
    function updatePrices(PriceUpdate[] calldata updates) external;
}

// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OptionalCall} from "../libraries/OptionalCall.sol";

import {
    AddressIsNotContractException,
    IncorrectParameterException,
    IncorrectPriceException,
    IncorrectPriceFeedException,
    PriceFeedDoesNotExistException,
    StalePriceException
} from "../interfaces/IExceptions.sol";
import {IPriceFeed, IUpdatablePriceFeed} from "../interfaces/base/IPriceFeed.sol";

/// @title Price feed validation trait
abstract contract PriceFeedValidationTrait {
    using Address for address;

    /// @dev Ensures that price feed's answer is positive and not stale.
    ///      If `skipCheck` is true, only checks that price is non-negative to allow zero price feed to be used.
    /// @custom:tests U:[PO-9]
    function _checkAnswer(int256 price, uint256 updatedAt, uint32 stalenessPeriod, bool skipCheck) internal view {
        if (price < 0 || !skipCheck && price == 0) revert IncorrectPriceException();
        if (!skipCheck && block.timestamp >= updatedAt + stalenessPeriod) revert StalePriceException();
    }

    /// @dev Validates that `priceFeed` is a contract that adheres to Chainlink interface
    /// @dev Reverts if `priceFeed` does not have exactly 8 decimals
    /// @dev Reverts if `stalenessPeriod` is inconsistent with `priceFeed`'s `skipPriceCheck()` flag
    ///      (which is considered to be false if `priceFeed` does not have this function)
    /// @custom:tests U:[PO-8], U:[PO-10]
    function _validatePriceFeedMetadata(address priceFeed, uint32 stalenessPeriod)
        internal
        view
        returns (bool skipCheck)
    {
        if (!priceFeed.isContract()) revert AddressIsNotContractException(priceFeed);

        try IPriceFeed(priceFeed).decimals() returns (uint8 _decimals) {
            if (_decimals != 8) revert IncorrectPriceFeedException();
        } catch {
            revert IncorrectPriceFeedException();
        }

        // NOTE: Some external price feeds without `skipPriceCheck` may have a fallback function that changes state,
        // which can cause a `THROW` that burns all gas, or does not change state and instead returns empty data.
        // To handle these cases, we use a special call construction with a strict gas limit.
        (bool success, bytes memory returnData) = OptionalCall.staticCallOptionalSafe({
            target: priceFeed,
            data: abi.encodeWithSelector(IPriceFeed.skipPriceCheck.selector),
            gasAllowance: 10_000
        });
        if (success) skipCheck = abi.decode(returnData, (bool));
        if (skipCheck && stalenessPeriod != 0 || !skipCheck && stalenessPeriod == 0) {
            revert IncorrectParameterException();
        }
    }

    /// @dev Validates that `priceFeed` is a contract that adheres to Chainlink interface and returns valid answer
    /// @custom:tests U:[PO-8], U:[PO-10]
    function _validatePriceFeed(address priceFeed, uint32 stalenessPeriod) internal view returns (bool skipCheck) {
        skipCheck = _validatePriceFeedMetadata(priceFeed, stalenessPeriod);

        try IPriceFeed(priceFeed).latestRoundData() returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80)
        {
            _checkAnswer(answer, updatedAt, stalenessPeriod, skipCheck);
        } catch {
            revert IncorrectPriceFeedException();
        }
    }

    /// @dev Returns answer from a price feed with optional sanity and staleness checks
    /// @custom:tests U:[PO-9]
    function _getValidatedPrice(address priceFeed, uint32 stalenessPeriod, bool skipCheck)
        internal
        view
        returns (int256 answer)
    {
        uint256 updatedAt;
        (, answer,, updatedAt,) = IPriceFeed(priceFeed).latestRoundData();
        _checkAnswer(answer, updatedAt, stalenessPeriod, skipCheck);
    }
}

// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

interface IAddressProvider {
    function getAddressOrRevert(bytes32 key, uint256 version) external view returns (address);
}

File 27 of 27 : IStateSerializer.sol
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;

/// @title State serializer interface
/// @notice Generic interface for a contract that can serialize its state into a bytes array
interface IStateSerializer {
    /// @notice Serializes the state of the contract into a bytes array `serializedData`
    function serialize() external view returns (bytes memory serializedData);
}

Settings
{
  "remappings": [
    "@1inch/=lib/@1inch/",
    "@gearbox-protocol/=lib/@gearbox-protocol/",
    "@openzeppelin/=lib/@openzeppelin/",
    "@redstone-finance/=node_modules/@redstone-finance/",
    "@solady/=lib/@solady/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/@openzeppelin/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin/=lib/@openzeppelin/contracts/"
  ],
  "optimizer": {
    "runs": 1000,
    "enabled": true
  },
  "metadata": {
    "bytecodeHash": "none",
    "useLiteralContent": true
  },
  "evmVersion": "shanghai",
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"addressProvider_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"AddressIsNotContractException","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotOwnerException","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"ForbiddenConfigurationMethodException","type":"error"},{"inputs":[],"name":"IncorrectParameterException","type":"error"},{"inputs":[],"name":"IncorrectPriceException","type":"error"},{"inputs":[],"name":"IncorrectPriceFeedException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsAlreadyAddedException","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsAlreadyAllowedException","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotAllowedException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotKnownException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotOwnedByStore","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotUpdatableException","type":"error"},{"inputs":[],"name":"StalePriceException","type":"error"},{"inputs":[],"name":"ZeroAddressException","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"},{"indexed":false,"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"AddPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"AddUpdatablePriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"AllowPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"ForbidPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"RemovePriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"},{"indexed":false,"internalType":"uint32","name":"stalenessPeriod","type":"uint32"}],"name":"SetStalenessPeriod","type":"event"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"internalType":"string","name":"name","type":"string"}],"name":"addPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"allowPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bytecodeRepository","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"configurePriceFeeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractType","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"forbidPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getAllowanceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKnownPriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKnownTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getPriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getStalenessPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenPriceFeedsMap","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address[]","name":"priceFeeds","type":"address[]"}],"internalType":"struct ConnectedPriceFeed[]","name":"connectedPriceFeeds","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUpdatablePriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"isAllowedPriceFeed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"isKnownPriceFeed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isKnownToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"priceFeedInfo","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"internalType":"bytes32","name":"priceFeedType","type":"bytes32"},{"internalType":"uint256","name":"version","type":"uint256"}],"internalType":"struct PriceFeedInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"removePriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"}],"name":"setStalenessPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct PriceUpdate[]","name":"updates","type":"tuple[]"}],"name":"updatePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zeroPriceFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

61010060405234801562000011575f80fd5b5060405162002cf338038062002cf383398101604081905262000034916200025a565b604051632bdad0e360e11b81527f494e5354414e43455f4d414e414745525f50524f58590000000000000000000060048201525f60248201526001600160a01b038216906357b5a1c690604401602060405180830381865afa1580156200009d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620000c391906200025a565b6001600160a01b03821660805281620000fd7f42595445434f44455f5245504f5349544f5259000000000000000000000000005f6200015a565b6001600160a01b0390811660a0529190911660c0525060408051602081019091525f80825262000147916f50524943455f464545443a3a5a45524f60801b916101369190620001d6565b6001600160a01b031660e05250620002e6565b608051604051632bdad0e360e11b815260048101849052602481018390525f916001600160a01b0316906357b5a1c690604401602060405180830381865afa158015620001a9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001cf91906200025a565b9392505050565b60a051604051635ce8938f60e01b81525f916001600160a01b031690635ce8938f906200020e90889088908890889060040162000282565b6020604051808303815f875af11580156200022b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200025191906200025a565b95945050505050565b5f602082840312156200026b575f80fd5b81516001600160a01b0381168114620001cf575f80fd5b8481525f60208560208401526080604084015284518060808501525f5b81811015620002bd5786810183015185820160a0015282016200029f565b505f60a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60805160a05160c05160e0516129a86200034b5f395f61030201525f8181610329015281816104110152818161064d0152818161094101528181610ab501528181610bd701526111bc01525f818161028701526118e701525f6101f301526129a85ff3fe608060405234801561000f575f80fd5b506004361061018f575f3560e01c806371346b42116100dd578063ba0f681e11610088578063ce37a61e11610063578063ce37a61e146103b8578063e647e5cc146103e0578063fceb0024146103f3575f80fd5b8063ba0f681e1461036b578063c7a22fe01461037e578063cb2ef6f714610391575f80fd5b806389e402f1116100b857806389e402f1146102fd5780638da5cb5b14610324578063a9ff6c0f1461034b575f80fd5b806371346b42146102c45780637199e2c9146102d75780637cc6ce52146102ea575f80fd5b80634026ebd61161013d57806360e93cfd1161011857806360e93cfd14610282578063614eb733146102a95780636f898fb4146102b1575f80fd5b80634026ebd61461025057806345af83961461025857806354fd4d501461026b575f80fd5b806326ec6c301161016d57806326ec6c30146101d95780632954018c146101ee57806330d1f9fe1461022d575f80fd5b8063149d919114610193578063161455ad146101a857806316f5a392146101c6575b5f80fd5b6101a66101a13660046122c2565b610406565b005b6101b0610526565b6040516101bd91906122f5565b60405180910390f35b6101a66101d43660046123a2565b610642565b6101e16108ef565b6040516101bd919061242b565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101bd565b61024061023b366004612477565b610900565b60405190151581526020016101bd565b6101e161092a565b6101a6610266366004612477565b610936565b61027461013681565b6040519081526020016101bd565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6101e1610a9f565b6101a66102bf366004612477565b610aaa565b6101a66102d23660046124f6565b610bcc565b6101a66102e53660046124f6565b610e22565b6101e16102f8366004612535565b610fa5565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b61035e610359366004612535565b610fc8565b6040516101bd919061259d565b610274610379366004612477565b6110c7565b61024061038c366004612535565b611143565b6102747f50524943455f464545445f53544f52450000000000000000000000000000000081565b6103cb6103c6366004612535565b61114e565b60405163ffffffff90911681526020016101bd565b6102406103ee366004612535565b6111a5565b6101a6610401366004612535565b6111b1565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461045657604051630cc479f960e01b81523360048201526024015b60405180910390fd5b6104605f8361134e565b61048857604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0382165f9081526008602052604090206001015463ffffffff828116911614610522576104bc828261136f565b506001600160a01b0382165f81815260086020908152604091829020600101805463ffffffff191663ffffffff861690811790915591519182527fd85fed169ea0f7852e9ec4becfec49eca5fe689c65e95486e97ab1f4f02623b9910160405180910390a25b5050565b60605f610533600261140a565b80519091508067ffffffffffffffff811115610551576105516125e8565b60405190808252806020026020018201604052801561059657816020015b604080518082019091525f81526060602082015281526020019060019003908161056f5790505b5092505f5b8181101561063c578281815181106105b5576105b56125fc565b60200260200101518482815181106105cf576105cf6125fc565b60200260200101515f01906001600160a01b031690816001600160a01b031681525050610614838281518110610607576106076125fc565b6020026020010151610fa5565b848281518110610626576106266125fc565b602090810291909101810151015260010161059b565b50505090565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461068d57604051630cc479f960e01b815233600482015260240161044d565b836106978161141d565b6106a15f86611460565b6106e2576040517f8d8d2d3a0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260240161044d565b6106ec858561136f565b505f6106f786611474565b6040805160a06020601f880181900402820181019092526080810186815292935091829187908790819085018382808284375f9201919091525050509082525063ffffffff87166020820152604001826107b057876001600160a01b031663cb2ef6f76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610787573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ab9190612610565b6107d2565b7f50524943455f464545443a3a45585445524e414c0000000000000000000000005b81526020018261084157876001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa158015610818573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612610565b610843565b5f5b90526001600160a01b0387165f9081526008602052604090208151819061086a90826126a5565b50602082015160018201805463ffffffff191663ffffffff9092169190911790556040808301516002830155606090920151600390910155516001600160a01b038716907f18c05f0511be33b4b3636da69473fd2c8b44012e11eabd7729a54611b6085932906108df9088908890889061278d565b60405180910390a2505050505050565b60606108fb600461140a565b905090565b6001600160a01b0382165f908152600660205260408120610921908361134e565b90505b92915050565b60606108fb600261140a565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461098157604051630cc479f960e01b815233600482015260240161044d565b8161098b8161141d565b6109955f8361134e565b6109bd57604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0383165f9081526006602052604090206109de9083611460565b610a27576040517f3e64cd550000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260440161044d565b6001600160a01b038084165f908152600760209081526040808320938616835292905220429055610a59600284611460565b50816001600160a01b0316836001600160a01b03167f73720c9031fae32be0c4f58f3b3934399946cfa29d2b13019ead89d904aa950e60405160405180910390a3505050565b60606108fb5f61140a565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610af557604051630cc479f960e01b815233600482015260240161044d565b610aff5f8261134e565b610b2757604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0382165f908152600660205260409020610b489082611536565b610b7857604051638896d35b60e01b81526001600160a01b0380841660048301528216602482015260440161044d565b6001600160a01b038083165f81815260076020908152604080832094861680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c1757604051630cc479f960e01b815233600482015260240161044d565b805f5b81811015610e1c57610c5e848483818110610c3757610c376125fc565b9050602002810190610c4991906127b5565b610c57906020810190612535565b5f9061134e565b610cb957838382818110610c7457610c746125fc565b9050602002810190610c8691906127b5565b610c94906020810190612535565b604051630aab1bd160e01b81526001600160a01b03909116600482015260240161044d565b5f848483818110610ccc57610ccc6125fc565b9050602002810190610cde91906127b5565b610cec9060208101906127d3565b610cf591612816565b90507f8eafe75a000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610d66576040517f242ccae90000000000000000000000000000000000000000000000000000000081526001600160e01b03198216600482015260240161044d565b610e12858584818110610d7b57610d7b6125fc565b9050602002810190610d8d91906127b5565b610d9b9060208101906127d3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150869050818110610de357610de36125fc565b9050602002810190610df591906127b5565b610e03906020810190612535565b6001600160a01b03169061154a565b5050600101610c1a565b50505050565b805f5b81811015610e1c57610e6a848483818110610e4257610e426125fc565b9050602002810190610e5491906127b5565b610e62906020810190612535565b60049061134e565b610ede57838382818110610e8057610e806125fc565b9050602002810190610e9291906127b5565b610ea0906020810190612535565b6040517f253c07ed0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161044d565b838382818110610ef057610ef06125fc565b9050602002810190610f0291906127b5565b610f10906020810190612535565b6001600160a01b0316638736ec47858584818110610f3057610f306125fc565b9050602002810190610f4291906127b5565b610f509060208101906127d3565b6040518363ffffffff1660e01b8152600401610f6d929190612844565b5f604051808303815f87803b158015610f84575f80fd5b505af1158015610f96573d5f803e3d5ffd5b50505050806001019050610e25565b6001600160a01b0381165f9081526006602052604090206060906109249061140a565b6040805160808101825260608082525f602083018190529282018390528101919091526001600160a01b0382165f908152600860205260409081902081516080810190925280548290829061101c90612627565b80601f016020809104026020016040519081016040528092919081815260200182805461104890612627565b80156110935780601f1061106a57610100808354040283529160200191611093565b820191905f5260205f20905b81548152906001019060200180831161107657829003601f168201915b5050509183525050600182015463ffffffff1660208201526002820154604082015260039091015460609091015292915050565b6001600160a01b0382165f9081526006602052604081206110e8908361134e565b61111857604051638896d35b60e01b81526001600160a01b0380851660048301528316602482015260440161044d565b506001600160a01b039182165f90815260076020908152604080832093909416825291909152205490565b5f610924818361134e565b5f611159818361134e565b61118157604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b506001600160a01b03165f9081526008602052604090206001015463ffffffff1690565b5f61092460028361134e565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111fc57604051630cc479f960e01b815233600482015260240161044d565b6112065f82611536565b61122e57604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0381165f908152600860205260408120906112508282612251565b5060018101805463ffffffff191690555f600280830182905560039092018190559061127b9061158d565b90505f5b81811015611316575f611293600283611596565b6001600160a01b0381165f9081526006602052604090209091506112b79085611536565b1561130d576001600160a01b038082165f81815260076020908152604080832094891680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35b5060010161127f565b506040516001600160a01b038316907f0d34c8e3483f57054e96fce18e1b58c102738aca38e401f22ce095d181dc7dba905f90a25050565b6001600160a01b0381165f9081526001830160205260408120541515610921565b5f61137a83836115a1565b9050826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa9250505080156113d6575060408051601f3d908101601f191682019092526113d391810190612870565b60015b6113f3576040516367a7cd4360e01b815260040160405180910390fd5b6113ff84838989611768565b505050505092915050565b60605f61141683611808565b9392505050565b6001600160a01b03811661145d576040517fb2335f2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f610921836001600160a01b038416611861565b5f61147e826118ad565b1561148b57506001919050565b61149482611a5d565b80156114a657506114a6600483611460565b156114df576040516001600160a01b038316907f126881a22591f9c505916524e1e627cee9847bd538cc439d374e156d965fd5ec905f90a25b5f6114f2836001600160a01b0316611ac8565b80519091505f5b8181101561152c57611523838281518110611516576115166125fc565b6020026020010151611474565b506001016114f9565b505f949350505050565b5f610921836001600160a01b038416611b2a565b606061092183835f6040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c65640000815250611c0d565b5f610924825490565b5f6109218383611cfd565b5f6001600160a01b0383163b6115ee576040517fdf4c572d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611648575060408051601f3d908101601f19168201909252611645918101906128bc565b60015b611665576040516367a7cd4360e01b815260040160405180910390fd5b8060ff16600814611689576040516367a7cd4360e01b815260040160405180910390fd5b506040805160048152602481019091526020810180516001600160e01b03167fd62ada11000000000000000000000000000000000000000000000000000000001790525f9081906116de908690612710611d23565b9150915081156116ff57808060200190518101906116fc91906128dc565b92505b828015611711575063ffffffff841615155b80611729575082158015611729575063ffffffff8416155b15611760576040517f47fbaa9700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b5f84128061177d57508015801561177d575083155b156117b4576040517f53b798e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156117d157506117cd63ffffffff83168461290f565b4210155b15610e1c576040517f16dd0ffb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060815f0180548060200260200160405190810160405280929190818152602001828054801561185557602002820191905f5260205f20905b815481526020019060010190808311611841575b50505050509050919050565b5f8181526001830160205260408120546118a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610924565b505f610924565b6040517f55b8fcfd0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906355b8fcfd90602401602060405180830381865afa15801561192e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195291906128dc565b61195e57506001919050565b816001600160a01b03166379ba50976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611996575f80fd5b505af19250505080156119a7575060015b50816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611a02575060408051601f3d908101601f191682019092526119ff91810190612922565b60015b15611a56576001600160a01b0381163014611a54576040517fa717374f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b505b505f919050565b5f816001600160a01b031663e75aeec86040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611ab8575060408051601f3d908101601f19168201909252611ab5918101906128dc565b60015b61092457505f919050565b919050565b60605f611ad483611da4565b90506001816002811115611aea57611aea61293d565b03611aff57611af883611e6d565b9150611b24565b6002816002811115611b1357611b1361293d565b03611b2457611b2183611f26565b91505b50919050565b5f8181526001830160205260408120548015611c04575f611b4c600183612951565b85549091505f90611b5f90600190612951565b9050818114611bbe575f865f018281548110611b7d57611b7d6125fc565b905f5260205f200154905080875f018481548110611b9d57611b9d6125fc565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611bcf57611bcf612964565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610924565b5f915050610924565b606082471015611c855760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161044d565b5f80866001600160a01b03168587604051611ca09190612978565b5f6040518083038185875af1925050503d805f8114611cda576040519150601f19603f3d011682016040523d82523d5f602084013e611cdf565b606091505b5091509150611cf087838387611fd4565b925050505b949350505050565b5f825f018281548110611d1257611d126125fc565b905f5260205f200154905092915050565b5f60605f80866001600160a01b03168587604051611d419190612978565b5f604051808303818686fa925050503d805f8114611d7a576040519150601f19603f3d011682016040523d82523d5f602084013e611d7f565b606091505b50915091505f815111611d93575f81611d96565b81815b935093505050935093915050565b6040805160048152602481019091526020810180516001600160e01b03167f741bef1a000000000000000000000000000000000000000000000000000000001790525f908190611e01906001600160a01b03851690612710611d23565b5090508015611e135750600192915050565b6040805160048152602481019091526020810180516001600160e01b031663385aee1b60e01b179052611e53906001600160a01b03851690612710611d23565b5090508015611e655750600292915050565b505f92915050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ecb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eef9190612922565b815f81518110611f0157611f016125fc565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b604080516008808252610120820190925260609160208201610100803683370190505090505f5b6008811015611b2457611f60838261204c565b828281518110611f7257611f726125fc565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6001600160a01b0316828281518110611fae57611fae6125fc565b60200260200101516001600160a01b031603611fcc57808252611b24565b600101611f4d565b606083156120425782515f0361203b576001600160a01b0385163b61203b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161044d565b5081611cf5565b611cf58383612227565b5f80825f03612063575063385aee1b60e01b6121a8565b8260010361209257507fab0ca0e1000000000000000000000000000000000000000000000000000000006121a8565b826002036120c157507fe5693f41000000000000000000000000000000000000000000000000000000006121a8565b826003036120f057507f427cb6fe000000000000000000000000000000000000000000000000000000006121a8565b8260040361211f57507f7aac1c48000000000000000000000000000000000000000000000000000000006121a8565b8260050361214e57507f7ee446ad000000000000000000000000000000000000000000000000000000006121a8565b8260060361217d57507fe5d9e964000000000000000000000000000000000000000000000000000000006121a8565b826007036121a857507f5d776bd7000000000000000000000000000000000000000000000000000000005b6040516001600160e01b0319821660208201525f9081906121e99060240160408051601f198184030181529190526001600160a01b03881690612710611d23565b915091508115806121f957508051155b15612209575f9350505050610924565b8080602001905181019061221d9190612922565b9695505050505050565b8151156122375781518083602001fd5b8060405162461bcd60e51b815260040161044d9190612989565b50805461225d90612627565b5f825580601f1061226c575050565b601f0160209004905f5260205f209081019061145d91905b80821115612297575f8155600101612284565b5090565b6001600160a01b038116811461145d575f80fd5b803563ffffffff81168114611ac3575f80fd5b5f80604083850312156122d3575f80fd5b82356122de8161229b565b91506122ec602084016122af565b90509250929050565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561239457888303603f19018552815180516001600160a01b039081168552908801518885018890528051888601819052908901915f9160608701905b8084101561237e57845183168252938b019360019390930192908b019061235c565b50978a019795505050918701915060010161231c565b509098975050505050505050565b5f805f80606085870312156123b5575f80fd5b84356123c08161229b565b93506123ce602086016122af565b9250604085013567ffffffffffffffff808211156123ea575f80fd5b818701915087601f8301126123fd575f80fd5b81358181111561240b575f80fd5b88602082850101111561241c575f80fd5b95989497505060200194505050565b602080825282518282018190525f9190848201906040850190845b8181101561246b5783516001600160a01b031683529284019291840191600101612446565b50909695505050505050565b5f8060408385031215612488575f80fd5b82356124938161229b565b915060208301356124a38161229b565b809150509250929050565b5f8083601f8401126124be575f80fd5b50813567ffffffffffffffff8111156124d5575f80fd5b6020830191508360208260051b85010111156124ef575f80fd5b9250929050565b5f8060208385031215612507575f80fd5b823567ffffffffffffffff81111561251d575f80fd5b612529858286016124ae565b90969095509350505050565b5f60208284031215612545575f80fd5b81356114168161229b565b5f5b8381101561256a578181015183820152602001612552565b50505f910152565b5f8151808452612589816020860160208601612550565b601f01601f19169290920160200192915050565b602081525f8251608060208401526125b860a0840182612572565b905063ffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612620575f80fd5b5051919050565b600181811c9082168061263b57607f821691505b602082108103611b2457634e487b7160e01b5f52602260045260245ffd5b601f8211156126a057805f5260205f20601f840160051c8101602085101561267e5750805b601f840160051c820191505b8181101561269d575f815560010161268a565b50505b505050565b815167ffffffffffffffff8111156126bf576126bf6125e8565b6126d3816126cd8454612627565b84612659565b602080601f831160018114612706575f84156126ef5750858301515b5f19600386901b1c1916600185901b17855561275d565b5f85815260208120601f198616915b8281101561273457888601518255948401946001909101908401612715565b508582101561275157878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b63ffffffff84168152604060208201525f6127ac604083018486612765565b95945050505050565b5f8235603e198336030181126127c9575f80fd5b9190910192915050565b5f808335601e198436030181126127e8575f80fd5b83018035915067ffffffffffffffff821115612802575f80fd5b6020019150368190038213156124ef575f80fd5b6001600160e01b031981358181169160048510156117605760049490940360031b84901b1690921692915050565b602081525f611cf5602083018486612765565b805169ffffffffffffffffffff81168114611ac3575f80fd5b5f805f805f60a08688031215612884575f80fd5b61288d86612857565b94506020860151935060408601519250606086015191506128b060808701612857565b90509295509295909350565b5f602082840312156128cc575f80fd5b815160ff81168114611416575f80fd5b5f602082840312156128ec575f80fd5b81518015158114611416575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610924576109246128fb565b5f60208284031215612932575f80fd5b81516114168161229b565b634e487b7160e01b5f52602160045260245ffd5b81810381811115610924576109246128fb565b634e487b7160e01b5f52603160045260245ffd5b5f82516127c9818460208701612550565b602081525f610921602083018461257256fea164736f6c6343000817000a000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061018f575f3560e01c806371346b42116100dd578063ba0f681e11610088578063ce37a61e11610063578063ce37a61e146103b8578063e647e5cc146103e0578063fceb0024146103f3575f80fd5b8063ba0f681e1461036b578063c7a22fe01461037e578063cb2ef6f714610391575f80fd5b806389e402f1116100b857806389e402f1146102fd5780638da5cb5b14610324578063a9ff6c0f1461034b575f80fd5b806371346b42146102c45780637199e2c9146102d75780637cc6ce52146102ea575f80fd5b80634026ebd61161013d57806360e93cfd1161011857806360e93cfd14610282578063614eb733146102a95780636f898fb4146102b1575f80fd5b80634026ebd61461025057806345af83961461025857806354fd4d501461026b575f80fd5b806326ec6c301161016d57806326ec6c30146101d95780632954018c146101ee57806330d1f9fe1461022d575f80fd5b8063149d919114610193578063161455ad146101a857806316f5a392146101c6575b5f80fd5b6101a66101a13660046122c2565b610406565b005b6101b0610526565b6040516101bd91906122f5565b60405180910390f35b6101a66101d43660046123a2565b610642565b6101e16108ef565b6040516101bd919061242b565b6102157f000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e3881565b6040516001600160a01b0390911681526020016101bd565b61024061023b366004612477565b610900565b60405190151581526020016101bd565b6101e161092a565b6101a6610266366004612477565b610936565b61027461013681565b6040519081526020016101bd565b6102157f0000000000000000000000001ce2b1be96a082b1b1539f80d5d8f82ec06a0f9a81565b6101e1610a9f565b6101a66102bf366004612477565b610aaa565b6101a66102d23660046124f6565b610bcc565b6101a66102e53660046124f6565b610e22565b6101e16102f8366004612535565b610fa5565b6102157f0000000000000000000000005dc92ec92cd5423ef95aebe8ac96619bcc9ffc8881565b6102157f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a81565b61035e610359366004612535565b610fc8565b6040516101bd919061259d565b610274610379366004612477565b6110c7565b61024061038c366004612535565b611143565b6102747f50524943455f464545445f53544f52450000000000000000000000000000000081565b6103cb6103c6366004612535565b61114e565b60405163ffffffff90911681526020016101bd565b6102406103ee366004612535565b6111a5565b6101a6610401366004612535565b6111b1565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461045657604051630cc479f960e01b81523360048201526024015b60405180910390fd5b6104605f8361134e565b61048857604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0382165f9081526008602052604090206001015463ffffffff828116911614610522576104bc828261136f565b506001600160a01b0382165f81815260086020908152604091829020600101805463ffffffff191663ffffffff861690811790915591519182527fd85fed169ea0f7852e9ec4becfec49eca5fe689c65e95486e97ab1f4f02623b9910160405180910390a25b5050565b60605f610533600261140a565b80519091508067ffffffffffffffff811115610551576105516125e8565b60405190808252806020026020018201604052801561059657816020015b604080518082019091525f81526060602082015281526020019060019003908161056f5790505b5092505f5b8181101561063c578281815181106105b5576105b56125fc565b60200260200101518482815181106105cf576105cf6125fc565b60200260200101515f01906001600160a01b031690816001600160a01b031681525050610614838281518110610607576106076125fc565b6020026020010151610fa5565b848281518110610626576106266125fc565b602090810291909101810151015260010161059b565b50505090565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461068d57604051630cc479f960e01b815233600482015260240161044d565b836106978161141d565b6106a15f86611460565b6106e2576040517f8d8d2d3a0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260240161044d565b6106ec858561136f565b505f6106f786611474565b6040805160a06020601f880181900402820181019092526080810186815292935091829187908790819085018382808284375f9201919091525050509082525063ffffffff87166020820152604001826107b057876001600160a01b031663cb2ef6f76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610787573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ab9190612610565b6107d2565b7f50524943455f464545443a3a45585445524e414c0000000000000000000000005b81526020018261084157876001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa158015610818573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612610565b610843565b5f5b90526001600160a01b0387165f9081526008602052604090208151819061086a90826126a5565b50602082015160018201805463ffffffff191663ffffffff9092169190911790556040808301516002830155606090920151600390910155516001600160a01b038716907f18c05f0511be33b4b3636da69473fd2c8b44012e11eabd7729a54611b6085932906108df9088908890889061278d565b60405180910390a2505050505050565b60606108fb600461140a565b905090565b6001600160a01b0382165f908152600660205260408120610921908361134e565b90505b92915050565b60606108fb600261140a565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461098157604051630cc479f960e01b815233600482015260240161044d565b8161098b8161141d565b6109955f8361134e565b6109bd57604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0383165f9081526006602052604090206109de9083611460565b610a27576040517f3e64cd550000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260440161044d565b6001600160a01b038084165f908152600760209081526040808320938616835292905220429055610a59600284611460565b50816001600160a01b0316836001600160a01b03167f73720c9031fae32be0c4f58f3b3934399946cfa29d2b13019ead89d904aa950e60405160405180910390a3505050565b60606108fb5f61140a565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a1614610af557604051630cc479f960e01b815233600482015260240161044d565b610aff5f8261134e565b610b2757604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0382165f908152600660205260409020610b489082611536565b610b7857604051638896d35b60e01b81526001600160a01b0380841660048301528216602482015260440161044d565b6001600160a01b038083165f81815260076020908152604080832094861680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35050565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a1614610c1757604051630cc479f960e01b815233600482015260240161044d565b805f5b81811015610e1c57610c5e848483818110610c3757610c376125fc565b9050602002810190610c4991906127b5565b610c57906020810190612535565b5f9061134e565b610cb957838382818110610c7457610c746125fc565b9050602002810190610c8691906127b5565b610c94906020810190612535565b604051630aab1bd160e01b81526001600160a01b03909116600482015260240161044d565b5f848483818110610ccc57610ccc6125fc565b9050602002810190610cde91906127b5565b610cec9060208101906127d3565b610cf591612816565b90507f8eafe75a000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610d66576040517f242ccae90000000000000000000000000000000000000000000000000000000081526001600160e01b03198216600482015260240161044d565b610e12858584818110610d7b57610d7b6125fc565b9050602002810190610d8d91906127b5565b610d9b9060208101906127d3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150869050818110610de357610de36125fc565b9050602002810190610df591906127b5565b610e03906020810190612535565b6001600160a01b03169061154a565b5050600101610c1a565b50505050565b805f5b81811015610e1c57610e6a848483818110610e4257610e426125fc565b9050602002810190610e5491906127b5565b610e62906020810190612535565b60049061134e565b610ede57838382818110610e8057610e806125fc565b9050602002810190610e9291906127b5565b610ea0906020810190612535565b6040517f253c07ed0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161044d565b838382818110610ef057610ef06125fc565b9050602002810190610f0291906127b5565b610f10906020810190612535565b6001600160a01b0316638736ec47858584818110610f3057610f306125fc565b9050602002810190610f4291906127b5565b610f509060208101906127d3565b6040518363ffffffff1660e01b8152600401610f6d929190612844565b5f604051808303815f87803b158015610f84575f80fd5b505af1158015610f96573d5f803e3d5ffd5b50505050806001019050610e25565b6001600160a01b0381165f9081526006602052604090206060906109249061140a565b6040805160808101825260608082525f602083018190529282018390528101919091526001600160a01b0382165f908152600860205260409081902081516080810190925280548290829061101c90612627565b80601f016020809104026020016040519081016040528092919081815260200182805461104890612627565b80156110935780601f1061106a57610100808354040283529160200191611093565b820191905f5260205f20905b81548152906001019060200180831161107657829003601f168201915b5050509183525050600182015463ffffffff1660208201526002820154604082015260039091015460609091015292915050565b6001600160a01b0382165f9081526006602052604081206110e8908361134e565b61111857604051638896d35b60e01b81526001600160a01b0380851660048301528316602482015260440161044d565b506001600160a01b039182165f90815260076020908152604080832093909416825291909152205490565b5f610924818361134e565b5f611159818361134e565b61118157604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b506001600160a01b03165f9081526008602052604090206001015463ffffffff1690565b5f61092460028361134e565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a16146111fc57604051630cc479f960e01b815233600482015260240161044d565b6112065f82611536565b61122e57604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0381165f908152600860205260408120906112508282612251565b5060018101805463ffffffff191690555f600280830182905560039092018190559061127b9061158d565b90505f5b81811015611316575f611293600283611596565b6001600160a01b0381165f9081526006602052604090209091506112b79085611536565b1561130d576001600160a01b038082165f81815260076020908152604080832094891680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35b5060010161127f565b506040516001600160a01b038316907f0d34c8e3483f57054e96fce18e1b58c102738aca38e401f22ce095d181dc7dba905f90a25050565b6001600160a01b0381165f9081526001830160205260408120541515610921565b5f61137a83836115a1565b9050826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa9250505080156113d6575060408051601f3d908101601f191682019092526113d391810190612870565b60015b6113f3576040516367a7cd4360e01b815260040160405180910390fd5b6113ff84838989611768565b505050505092915050565b60605f61141683611808565b9392505050565b6001600160a01b03811661145d576040517fb2335f2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f610921836001600160a01b038416611861565b5f61147e826118ad565b1561148b57506001919050565b61149482611a5d565b80156114a657506114a6600483611460565b156114df576040516001600160a01b038316907f126881a22591f9c505916524e1e627cee9847bd538cc439d374e156d965fd5ec905f90a25b5f6114f2836001600160a01b0316611ac8565b80519091505f5b8181101561152c57611523838281518110611516576115166125fc565b6020026020010151611474565b506001016114f9565b505f949350505050565b5f610921836001600160a01b038416611b2a565b606061092183835f6040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c65640000815250611c0d565b5f610924825490565b5f6109218383611cfd565b5f6001600160a01b0383163b6115ee576040517fdf4c572d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611648575060408051601f3d908101601f19168201909252611645918101906128bc565b60015b611665576040516367a7cd4360e01b815260040160405180910390fd5b8060ff16600814611689576040516367a7cd4360e01b815260040160405180910390fd5b506040805160048152602481019091526020810180516001600160e01b03167fd62ada11000000000000000000000000000000000000000000000000000000001790525f9081906116de908690612710611d23565b9150915081156116ff57808060200190518101906116fc91906128dc565b92505b828015611711575063ffffffff841615155b80611729575082158015611729575063ffffffff8416155b15611760576040517f47fbaa9700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b5f84128061177d57508015801561177d575083155b156117b4576040517f53b798e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156117d157506117cd63ffffffff83168461290f565b4210155b15610e1c576040517f16dd0ffb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060815f0180548060200260200160405190810160405280929190818152602001828054801561185557602002820191905f5260205f20905b815481526020019060010190808311611841575b50505050509050919050565b5f8181526001830160205260408120546118a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610924565b505f610924565b6040517f55b8fcfd0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301525f917f0000000000000000000000001ce2b1be96a082b1b1539f80d5d8f82ec06a0f9a909116906355b8fcfd90602401602060405180830381865afa15801561192e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195291906128dc565b61195e57506001919050565b816001600160a01b03166379ba50976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611996575f80fd5b505af19250505080156119a7575060015b50816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611a02575060408051601f3d908101601f191682019092526119ff91810190612922565b60015b15611a56576001600160a01b0381163014611a54576040517fa717374f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b505b505f919050565b5f816001600160a01b031663e75aeec86040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611ab8575060408051601f3d908101601f19168201909252611ab5918101906128dc565b60015b61092457505f919050565b919050565b60605f611ad483611da4565b90506001816002811115611aea57611aea61293d565b03611aff57611af883611e6d565b9150611b24565b6002816002811115611b1357611b1361293d565b03611b2457611b2183611f26565b91505b50919050565b5f8181526001830160205260408120548015611c04575f611b4c600183612951565b85549091505f90611b5f90600190612951565b9050818114611bbe575f865f018281548110611b7d57611b7d6125fc565b905f5260205f200154905080875f018481548110611b9d57611b9d6125fc565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611bcf57611bcf612964565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610924565b5f915050610924565b606082471015611c855760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161044d565b5f80866001600160a01b03168587604051611ca09190612978565b5f6040518083038185875af1925050503d805f8114611cda576040519150601f19603f3d011682016040523d82523d5f602084013e611cdf565b606091505b5091509150611cf087838387611fd4565b925050505b949350505050565b5f825f018281548110611d1257611d126125fc565b905f5260205f200154905092915050565b5f60605f80866001600160a01b03168587604051611d419190612978565b5f604051808303818686fa925050503d805f8114611d7a576040519150601f19603f3d011682016040523d82523d5f602084013e611d7f565b606091505b50915091505f815111611d93575f81611d96565b81815b935093505050935093915050565b6040805160048152602481019091526020810180516001600160e01b03167f741bef1a000000000000000000000000000000000000000000000000000000001790525f908190611e01906001600160a01b03851690612710611d23565b5090508015611e135750600192915050565b6040805160048152602481019091526020810180516001600160e01b031663385aee1b60e01b179052611e53906001600160a01b03851690612710611d23565b5090508015611e655750600292915050565b505f92915050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ecb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eef9190612922565b815f81518110611f0157611f016125fc565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b604080516008808252610120820190925260609160208201610100803683370190505090505f5b6008811015611b2457611f60838261204c565b828281518110611f7257611f726125fc565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6001600160a01b0316828281518110611fae57611fae6125fc565b60200260200101516001600160a01b031603611fcc57808252611b24565b600101611f4d565b606083156120425782515f0361203b576001600160a01b0385163b61203b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161044d565b5081611cf5565b611cf58383612227565b5f80825f03612063575063385aee1b60e01b6121a8565b8260010361209257507fab0ca0e1000000000000000000000000000000000000000000000000000000006121a8565b826002036120c157507fe5693f41000000000000000000000000000000000000000000000000000000006121a8565b826003036120f057507f427cb6fe000000000000000000000000000000000000000000000000000000006121a8565b8260040361211f57507f7aac1c48000000000000000000000000000000000000000000000000000000006121a8565b8260050361214e57507f7ee446ad000000000000000000000000000000000000000000000000000000006121a8565b8260060361217d57507fe5d9e964000000000000000000000000000000000000000000000000000000006121a8565b826007036121a857507f5d776bd7000000000000000000000000000000000000000000000000000000005b6040516001600160e01b0319821660208201525f9081906121e99060240160408051601f198184030181529190526001600160a01b03881690612710611d23565b915091508115806121f957508051155b15612209575f9350505050610924565b8080602001905181019061221d9190612922565b9695505050505050565b8151156122375781518083602001fd5b8060405162461bcd60e51b815260040161044d9190612989565b50805461225d90612627565b5f825580601f1061226c575050565b601f0160209004905f5260205f209081019061145d91905b80821115612297575f8155600101612284565b5090565b6001600160a01b038116811461145d575f80fd5b803563ffffffff81168114611ac3575f80fd5b5f80604083850312156122d3575f80fd5b82356122de8161229b565b91506122ec602084016122af565b90509250929050565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561239457888303603f19018552815180516001600160a01b039081168552908801518885018890528051888601819052908901915f9160608701905b8084101561237e57845183168252938b019360019390930192908b019061235c565b50978a019795505050918701915060010161231c565b509098975050505050505050565b5f805f80606085870312156123b5575f80fd5b84356123c08161229b565b93506123ce602086016122af565b9250604085013567ffffffffffffffff808211156123ea575f80fd5b818701915087601f8301126123fd575f80fd5b81358181111561240b575f80fd5b88602082850101111561241c575f80fd5b95989497505060200194505050565b602080825282518282018190525f9190848201906040850190845b8181101561246b5783516001600160a01b031683529284019291840191600101612446565b50909695505050505050565b5f8060408385031215612488575f80fd5b82356124938161229b565b915060208301356124a38161229b565b809150509250929050565b5f8083601f8401126124be575f80fd5b50813567ffffffffffffffff8111156124d5575f80fd5b6020830191508360208260051b85010111156124ef575f80fd5b9250929050565b5f8060208385031215612507575f80fd5b823567ffffffffffffffff81111561251d575f80fd5b612529858286016124ae565b90969095509350505050565b5f60208284031215612545575f80fd5b81356114168161229b565b5f5b8381101561256a578181015183820152602001612552565b50505f910152565b5f8151808452612589816020860160208601612550565b601f01601f19169290920160200192915050565b602081525f8251608060208401526125b860a0840182612572565b905063ffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612620575f80fd5b5051919050565b600181811c9082168061263b57607f821691505b602082108103611b2457634e487b7160e01b5f52602260045260245ffd5b601f8211156126a057805f5260205f20601f840160051c8101602085101561267e5750805b601f840160051c820191505b8181101561269d575f815560010161268a565b50505b505050565b815167ffffffffffffffff8111156126bf576126bf6125e8565b6126d3816126cd8454612627565b84612659565b602080601f831160018114612706575f84156126ef5750858301515b5f19600386901b1c1916600185901b17855561275d565b5f85815260208120601f198616915b8281101561273457888601518255948401946001909101908401612715565b508582101561275157878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b63ffffffff84168152604060208201525f6127ac604083018486612765565b95945050505050565b5f8235603e198336030181126127c9575f80fd5b9190910192915050565b5f808335601e198436030181126127e8575f80fd5b83018035915067ffffffffffffffff821115612802575f80fd5b6020019150368190038213156124ef575f80fd5b6001600160e01b031981358181169160048510156117605760049490940360031b84901b1690921692915050565b602081525f611cf5602083018486612765565b805169ffffffffffffffffffff81168114611ac3575f80fd5b5f805f805f60a08688031215612884575f80fd5b61288d86612857565b94506020860151935060408601519250606086015191506128b060808701612857565b90509295509295909350565b5f602082840312156128cc575f80fd5b815160ff81168114611416575f80fd5b5f602082840312156128ec575f80fd5b81518015158114611416575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610924576109246128fb565b5f60208284031215612932575f80fd5b81516114168161229b565b634e487b7160e01b5f52602160045260245ffd5b81810381811115610924576109246128fb565b634e487b7160e01b5f52603160045260245ffd5b5f82516127c9818460208701612550565b602081525f610921602083018461257256fea164736f6c6343000817000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38

-----Decoded View---------------
Arg [0] : addressProvider_ (address): 0xF7f0a609BfAb9a0A98786951ef10e5FE26cC1E38

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

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

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