Blockchain / Global / Address

Contract

0x7D60CfaF7c2cec210638A5e46E4000894830C034

Balance
0 SOMI
Transactions sent
1
Type
Contract · 4,071 bytes of code

MarketConfiguratorFactory Verified

Compiler
solc 0.8.23+commit.f704f362
Language
Solidity
Optimization
Yes, 1000 runs
EVM version
shanghai
License
—
Verified
2026-10-12

contracts/instance/MarketConfiguratorFactory.sol

and 17 more files

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

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

import {IContractsRegister} from "../interfaces/IContractsRegister.sol";
import {IMarketConfigurator} from "../interfaces/IMarketConfigurator.sol";
import {IMarketConfiguratorFactory} from "../interfaces/IMarketConfiguratorFactory.sol";

import {
    AP_CROSS_CHAIN_GOVERNANCE,
    AP_MARKET_CONFIGURATOR,
    AP_MARKET_CONFIGURATOR_FACTORY,
    NO_VERSION_CONTROL
} from "../libraries/ContractLiterals.sol";

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

/// @title Market configurator factory
contract MarketConfiguratorFactory is DeployerTrait, IMarketConfiguratorFactory {
    using EnumerableSet for EnumerableSet.AddressSet;

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

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

    /// @dev Set of registered market configurators
    EnumerableSet.AddressSet internal _registeredMarketConfiguratorsSet;

    /// @dev Set of shutdown market configurators
    EnumerableSet.AddressSet internal _shutdownMarketConfiguratorsSet;

    /// @dev Reverts if caller is not cross-chain governance
    modifier onlyCrossChainGovernance() {
        if (msg.sender != _getAddressOrRevert(AP_CROSS_CHAIN_GOVERNANCE, NO_VERSION_CONTROL)) {
            revert CallerIsNotCrossChainGovernanceException(msg.sender);
        }
        _;
    }

    /// @dev Reverts if `msg.sender` is not the admin of `marketConfigurator`
    modifier onlyMarketConfiguratorAdmin(address marketConfigurator) {
        if (msg.sender != IMarketConfigurator(marketConfigurator).admin()) {
            revert CallerIsNotMarketConfiguratorAdminException(msg.sender);
        }
        _;
    }

    /// @notice Constructor
    /// @param addressProvider_ Address provider contract address
    constructor(address addressProvider_) DeployerTrait(addressProvider_) {}

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

    /// @notice Returns whether `account` is a registered market configurator
    function isMarketConfigurator(address account) external view override returns (bool) {
        return _registeredMarketConfiguratorsSet.contains(account);
    }

    /// @notice Returns all registered market configurators
    function getMarketConfigurators() external view override returns (address[] memory) {
        return _registeredMarketConfiguratorsSet.values();
    }

    /// @notice Returns the market configurator at `index`
    function getMarketConfigurator(uint256 index) external view override returns (address) {
        return _registeredMarketConfiguratorsSet.at(index);
    }

    /// @notice Returns the number of registered market configurators
    function getNumMarketConfigurators() external view override returns (uint256) {
        return _registeredMarketConfiguratorsSet.length();
    }

    /// @notice Returns all shutdown market configurators
    function getShutdownMarketConfigurators() external view override returns (address[] memory) {
        return _shutdownMarketConfiguratorsSet.values();
    }

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

    /// @notice Allows anyone to permissionlessly deploy a new market configurator
    /// @param emergencyAdmin Address to set as emergency admin
    /// @param adminFeeTreasury Address to set as the second admin of the fee splitter, with the first one being the
    ///        DAO treasury. If `address(0)`, the splitter is not deployed and all fees will be sent to the treasury.
    /// @param curatorName Risk curator name
    /// @param deployGovernor If true, a governor contract is deployed and set as market configurator's admin.
    ///        `msg.sender` is set as its owner, queue and execution admin, while `emergencyAdmin` is set as veto admin.
    ///        Otherwise, `msg.sender` is set as the admin of the market configurator.
    /// @return marketConfigurator Address of the newly deployed market configurator
    function createMarketConfigurator(
        address emergencyAdmin,
        address adminFeeTreasury,
        string calldata curatorName,
        bool deployGovernor
    ) external override returns (address marketConfigurator) {
        marketConfigurator = _deployLatestPatch({
            contractType: AP_MARKET_CONFIGURATOR,
            minorVersion: 3_10,
            constructorParams: abi.encode(
                addressProvider, msg.sender, emergencyAdmin, adminFeeTreasury, curatorName, deployGovernor
            ),
            salt: bytes32(bytes20(msg.sender))
        });

        _registeredMarketConfiguratorsSet.add(marketConfigurator);
        emit CreateMarketConfigurator(marketConfigurator, curatorName);
    }

    /// @notice Allows the admin of `marketConfigurator` to shut it down
    /// @dev Reverts if caller is not the admin of `marketConfigurator`
    /// @dev Reverts if `marketConfigurator` is not registered or already shutdown
    /// @dev Reverts if `marketConfigurator` has non-shutdown pools
    function shutdownMarketConfigurator(address marketConfigurator)
        external
        override
        onlyMarketConfiguratorAdmin(marketConfigurator)
    {
        if (!_shutdownMarketConfiguratorsSet.add(marketConfigurator)) {
            revert MarketConfiguratorIsAlreadyShutdownException(marketConfigurator);
        }
        if (!_registeredMarketConfiguratorsSet.remove(marketConfigurator)) {
            revert MarketConfiguratorIsNotRegisteredException(marketConfigurator);
        }
        address contractsRegister = IMarketConfigurator(marketConfigurator).contractsRegister();
        if (IContractsRegister(contractsRegister).getPools().length != 0) {
            revert CantShutdownMarketConfiguratorException(marketConfigurator);
        }
        emit ShutdownMarketConfigurator(marketConfigurator);
    }

    /// @notice Allows cross-chain governance to register an externally deployed legacy market configurator
    /// @dev Reverts if caller is not cross-chain governance
    /// @dev Reverts if `marketConfigurator` is already registered or shutdown
    function addMarketConfigurator(address marketConfigurator) external override onlyCrossChainGovernance {
        if (!_registeredMarketConfiguratorsSet.add(marketConfigurator)) {
            revert MarketConfiguratorIsAlreadyAddedException(marketConfigurator);
        }
        if (_shutdownMarketConfiguratorsSet.contains(marketConfigurator)) {
            revert MarketConfiguratorIsAlreadyShutdownException(marketConfigurator);
        }
        emit CreateMarketConfigurator(marketConfigurator, IMarketConfigurator(marketConfigurator).curatorName());
    }
}

Other files

  • lib/@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol
    // 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);
    }
    
  • lib/@openzeppelin/contracts/utils/structs/EnumerableSet.sol
    // 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;
        }
    }
    
  • lib/@solady/src/utils/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)))
            }
        }
    }
    
  • contracts/interfaces/IContractsRegister.sol
    // SPDX-License-Identifier: MIT
    // Gearbox Protocol. Generalized leverage for DeFi protocols
    // (c) Gearbox Foundation, 2025.
    pragma solidity ^0.8.23;
    
    import {IACLTrait} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IACLTrait.sol";
    import {IContractsRegister as IContractsRegisterBase} from
        "@gearbox-protocol/core-v3/contracts/interfaces/base/IContractsRegister.sol";
    import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
    
    interface IContractsRegister is IContractsRegisterBase, IVersion, IACLTrait {
        // ------ //
        // EVENTS //
        // ------ //
    
        event RegisterMarket(address indexed pool, address indexed priceOracle, address indexed lossPolicy);
        event ShutdownMarket(address indexed pool);
        event RegisterCreditSuite(address indexed pool, address indexed creditManager);
        event ShutdownCreditSuite(address indexed pool, address indexed creditManager);
        event SetPriceOracle(address indexed pool, address indexed priceOracle);
        event SetLossPolicy(address indexed pool, address indexed lossPolicy);
    
        // ------ //
        // ERRORS //
        // ------ //
    
        error MarketNotRegisteredException(address pool);
        error MarketShutDownException(address pool);
        error MarketNotEmptyException(address pool);
        error CreditSuiteNotRegisteredException(address creditManager);
        error CreditSuiteShutDownException(address creditManager);
    
        // ------- //
        // MARKETS //
        // ------- //
    
        function getShutdownPools() external view returns (address[] memory);
        function getPriceOracle(address pool) external view returns (address);
        function getLossPolicy(address pool) external view returns (address);
    
        function registerMarket(address pool, address priceOracle, address lossPolicy) external;
        function shutdownMarket(address pool) external;
        function setPriceOracle(address pool, address priceOracle) external;
        function setLossPolicy(address pool, address lossPolicy) external;
    
        // ------------- //
        // CREDIT SUITES //
        // ------------- //
    
        function getCreditManagers(address pool) external view returns (address[] memory);
        function getShutdownCreditManagers() external view returns (address[] memory);
        function getShutdownCreditManagers(address pool) external view returns (address[] memory);
    
        function registerCreditSuite(address creditManager) external;
        function shutdownCreditSuite(address creditManager) external;
    }
    
  • contracts/interfaces/IMarketConfigurator.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 {IDeployerTrait} from "./base/IDeployerTrait.sol";
    import {Call, DeployParams, MarketFactories} from "./Types.sol";
    
    interface IMarketConfigurator is IVersion, IDeployerTrait {
        // ------ //
        // EVENTS //
        // ------ //
    
        event SetEmergencyAdmin(address indexed newEmergencyAdmin);
    
        event GrantRole(bytes32 indexed role, address indexed account);
    
        event RevokeRole(bytes32 indexed role, address indexed account);
    
        event EmergencyRevokeRole(bytes32 indexed role, address indexed account);
    
        event CreateMarket(
            address indexed pool,
            address priceOracle,
            address interestRateModel,
            address rateKeeper,
            address lossPolicy,
            MarketFactories factories
        );
    
        event ShutdownMarket(address indexed pool);
    
        event AddToken(address indexed pool, address indexed token);
    
        event ConfigurePool(address indexed pool, bytes data);
    
        event EmergencyConfigurePool(address indexed pool, bytes data);
    
        event CreateCreditSuite(address indexed creditManager, address factory);
    
        event ShutdownCreditSuite(address indexed creditManager);
    
        event ConfigureCreditSuite(address indexed creditManager, bytes data);
    
        event EmergencyConfigureCreditSuite(address indexed creditManager, bytes data);
    
        event UpdatePriceOracle(address indexed pool, address priceOracle);
    
        event ConfigurePriceOracle(address indexed pool, bytes data);
    
        event EmergencyConfigurePriceOracle(address indexed pool, bytes data);
    
        event UpdateInterestRateModel(address indexed pool, address interestRateModel);
    
        event ConfigureInterestRateModel(address indexed pool, bytes data);
    
        event EmergencyConfigureInterestRateModel(address indexed pool, bytes data);
    
        event UpdateRateKeeper(address indexed pool, address rateKeeper);
    
        event ConfigureRateKeeper(address indexed pool, bytes data);
    
        event EmergencyConfigureRateKeeper(address indexed pool, bytes data);
    
        event UpdateLossPolicy(address indexed pool, address lossPolicy);
    
        event ConfigureLossPolicy(address indexed pool, bytes data);
    
        event EmergencyConfigureLossPolicy(address indexed pool, bytes data);
    
        event AddPeripheryContract(bytes32 indexed domain, address indexed peripheryContract);
    
        event RemovePeripheryContract(bytes32 indexed domain, address indexed peripheryContract);
    
        event AuthorizeFactory(address indexed factory, address indexed suite, address indexed target);
    
        event UnauthorizeFactory(address indexed factory, address indexed suite, address indexed target);
    
        event UpgradePoolFactory(address indexed pool, address factory);
    
        event UpgradePriceOracleFactory(address indexed pool, address factory);
    
        event UpgradeInterestRateModelFactory(address indexed pool, address factory);
    
        event UpgradeRateKeeperFactory(address indexed pool, address factory);
    
        event UpgradeLossPolicyFactory(address indexed pool, address factory);
    
        event UpgradeCreditFactory(address indexed creditManager, address factory);
    
        event ExecuteHook(address indexed target, bytes callData);
    
        // ------ //
        // ERRORS //
        // ------ //
    
        error CallerIsNotAdminException(address caller);
    
        error CallerIsNotEmergencyAdminException(address caller);
    
        error CallerIsNotSelfException(address caller);
    
        error CreditSuiteNotRegisteredException(address creditManager);
    
        error IncorrectMinorVersionException(uint256 version);
    
        error IncorrectPeripheryContractException(address peripheryContract);
    
        error MarketNotRegisteredException(address pool);
    
        error UnauthorizedFactoryException(address factory, address target);
    
        // --------------- //
        // STATE VARIABLES //
        // --------------- //
    
        function admin() external view returns (address);
        function emergencyAdmin() external view returns (address);
        function curatorName() external view returns (string memory);
    
        function acl() external view returns (address);
        function contractsRegister() external view returns (address);
        function treasury() external view returns (address);
    
        // ---------------- //
        // ROLES MANAGEMENT //
        // ---------------- //
    
        function setEmergencyAdmin(address newEmergencyAdmin) external;
    
        function grantRole(bytes32 role, address account) external;
    
        function revokeRole(bytes32 role, address account) external;
    
        function emergencyRevokeRole(bytes32 role, address account) external;
    
        // ----------------- //
        // MARKET MANAGEMENT //
        // ----------------- //
    
        function previewCreateMarket(uint256 minorVersion, address underlying, string calldata name, string calldata symbol)
            external
            view
            returns (address pool);
    
        function createMarket(
            uint256 minorVersion,
            address underlying,
            string calldata name,
            string calldata symbol,
            DeployParams calldata interestRateModelParams,
            DeployParams calldata rateKeeperParams,
            DeployParams calldata lossPolicyParams,
            address underlyingPriceFeed
        ) external returns (address pool);
    
        function shutdownMarket(address pool) external;
    
        function addToken(address pool, address token, address priceFeed) external;
    
        function configurePool(address pool, bytes calldata data) external;
    
        function emergencyConfigurePool(address pool, bytes calldata data) external;
    
        // ----------------------- //
        // CREDIT SUITE MANAGEMENT //
        // ----------------------- //
    
        function previewCreateCreditSuite(uint256 minorVersion, address pool, bytes calldata encodedParams)
            external
            view
            returns (address creditManager);
    
        function previewCreateCreditSuite(
            uint256 marketMinorVersion,
            uint256 creditSuiteMinorVersion,
            address underlying,
            string calldata name,
            string calldata symbol,
            bytes calldata encodedParams
        ) external view returns (address creditManager);
    
        function createCreditSuite(uint256 minorVersion, address pool, bytes calldata encdodedParams)
            external
            returns (address creditManager);
    
        function shutdownCreditSuite(address creditManager) external;
    
        function configureCreditSuite(address creditManager, bytes calldata data) external;
    
        function emergencyConfigureCreditSuite(address creditManager, bytes calldata data) external;
    
        // ----------------------- //
        // PRICE ORACLE MANAGEMENT //
        // ----------------------- //
    
        function updatePriceOracle(address pool) external returns (address priceOracle);
    
        function configurePriceOracle(address pool, bytes calldata data) external;
    
        function emergencyConfigurePriceOracle(address pool, bytes calldata data) external;
    
        // -------------- //
        // IRM MANAGEMENT //
        // -------------- //
    
        function updateInterestRateModel(address pool, DeployParams calldata params) external returns (address irm);
    
        function configureInterestRateModel(address pool, bytes calldata data) external;
    
        function emergencyConfigureInterestRateModel(address pool, bytes calldata data) external;
    
        // ---------------------- //
        // RATE KEEPER MANAGEMENT //
        // ---------------------- //
    
        function updateRateKeeper(address pool, DeployParams calldata params) external returns (address rateKeeper);
    
        function configureRateKeeper(address pool, bytes calldata data) external;
    
        function emergencyConfigureRateKeeper(address pool, bytes calldata data) external;
    
        // -–-------------------- //
        // LOSS POLICY MANAGEMENT //
        // -–-------------------- //
    
        function updateLossPolicy(address pool, DeployParams calldata params) external returns (address lossPolicy);
    
        function configureLossPolicy(address pool, bytes calldata data) external;
    
        function emergencyConfigureLossPolicy(address pool, bytes calldata data) external;
    
        // --------- //
        // PERIPHERY //
        // --------- //
    
        function getPeripheryContracts(bytes32 domain) external view returns (address[] memory);
    
        function isPeripheryContract(bytes32 domain, address peripheryContract) external view returns (bool);
    
        function addPeripheryContract(address peripheryContract) external;
    
        function removePeripheryContract(address peripheryContract) external;
    
        // --------- //
        // FACTORIES //
        // --------- //
    
        function getMarketFactories(address pool) external view returns (MarketFactories memory);
    
        function getCreditFactory(address creditManager) external view returns (address);
    
        function getAuthorizedFactory(address target) external view returns (address);
    
        function getFactoryTargets(address factory, address suite) external view returns (address[] memory);
    
        function authorizeFactory(address factory, address suite, address target) external;
    
        function unauthorizeFactory(address factory, address suite, address target) external;
    
        function upgradePoolFactory(address pool) external;
    
        function upgradePriceOracleFactory(address pool) external;
    
        function upgradeInterestRateModelFactory(address pool) external;
    
        function upgradeRateKeeperFactory(address pool) external;
    
        function upgradeLossPolicyFactory(address pool) external;
    
        function upgradeCreditFactory(address creditManager) external;
    }
    
  • lib/@gearbox-protocol/core-v3/contracts/interfaces/base/IAddressProvider.sol
    // 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);
    }
    
  • lib/@gearbox-protocol/core-v3/contracts/interfaces/base/IContractsRegister.sol
    // SPDX-License-Identifier: MIT
    // Gearbox Protocol. Generalized leverage for DeFi protocols
    // (c) Gearbox Foundation, 2024.
    pragma solidity ^0.8.17;
    
    interface IContractsRegister {
        function isPool(address) external view returns (bool);
        function getPools() external view returns (address[] memory);
        function isCreditManager(address) external view returns (bool);
        function getCreditManagers() external view returns (address[] memory);
    }
    
  • contracts/interfaces/IAddressProvider.sol
    // 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;
    }
    
  • contracts/interfaces/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);
    }
    
  • contracts/interfaces/IMarketConfiguratorFactory.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 {IDeployerTrait} from "./base/IDeployerTrait.sol";
    
    /// @title Market configurator factory interface
    interface IMarketConfiguratorFactory is IVersion, IDeployerTrait {
        // ------ //
        // EVENTS //
        // ------ //
    
        event CreateMarketConfigurator(address indexed marketConfigurator, string name);
        event ShutdownMarketConfigurator(address indexed marketConfigurator);
    
        // ------ //
        // ERRORS //
        // ------ //
    
        error CallerIsNotCrossChainGovernanceException(address caller);
        error CallerIsNotMarketConfiguratorAdminException(address caller);
        error CantShutdownMarketConfiguratorException(address marketConfigurator);
        error MarketConfiguratorIsAlreadyAddedException(address marketConfigurator);
        error MarketConfiguratorIsAlreadyShutdownException(address marketConfigruator);
        error MarketConfiguratorIsNotRegisteredException(address marketConfigurator);
    
        // ------- //
        // GETTERS //
        // ------- //
    
        function isMarketConfigurator(address account) external view returns (bool);
        function getMarketConfigurators() external view returns (address[] memory);
        function getMarketConfigurator(uint256 index) external view returns (address);
        function getNumMarketConfigurators() external view returns (uint256);
        function getShutdownMarketConfigurators() external view returns (address[] memory);
    
        // ------------- //
        // CONFIGURATION //
        // ------------- //
    
        function createMarketConfigurator(
            address emergencyAdmin,
            address adminFeeTreasury,
            string calldata curatorName,
            bool deployGovernor
        ) external returns (address marketConfigurator);
        function shutdownMarketConfigurator(address marketConfigurator) external;
        function addMarketConfigurator(address marketConfigurator) external;
    }
    
  • contracts/interfaces/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;
    }
    
  • contracts/interfaces/base/IDeployerTrait.sol
    // 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);
    }
    
  • contracts/interfaces/base/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);
    }
    
  • contracts/libraries/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";
    
  • contracts/libraries/Domain.sol
    // 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);
        }
    }
    
  • contracts/traits/DeployerTrait.sol
    // 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);
        }
    }
    
  • lib/@gearbox-protocol/core-v3/contracts/interfaces/base/IACLTrait.sol
    // SPDX-License-Identifier: MIT
    // Gearbox Protocol. Generalized leverage for DeFi protocols
    // (c) Gearbox Foundation, 2024.
    pragma solidity ^0.8.17;
    
    interface IACLTrait {
        function acl() external view returns (address);
    }
    
ABI · 21 entries
json
[
  {
    "type": "constructor",
    "inputs": [
      {
        "name": "addressProvider_",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "CallerIsNotCrossChainGovernanceException",
    "type": "error",
    "inputs": [
      {
        "name": "caller",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "CallerIsNotMarketConfiguratorAdminException",
    "type": "error",
    "inputs": [
      {
        "name": "caller",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "CantShutdownMarketConfiguratorException",
    "type": "error",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "MarketConfiguratorIsAlreadyAddedException",
    "type": "error",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "MarketConfiguratorIsAlreadyShutdownException",
    "type": "error",
    "inputs": [
      {
        "name": "marketConfigruator",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "MarketConfiguratorIsNotRegisteredException",
    "type": "error",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ]
  },
  {
    "name": "CreateMarketConfigurator",
    "type": "event",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "name",
        "type": "string",
        "indexed": false,
        "internalType": "string"
      }
    ],
    "anonymous": false
  },
  {
    "name": "ShutdownMarketConfigurator",
    "type": "event",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      }
    ],
    "anonymous": false
  },
  {
    "name": "addMarketConfigurator",
    "type": "function",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "addressProvider",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "bytecodeRepository",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "contractType",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "bytes32",
        "internalType": "bytes32"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "createMarketConfigurator",
    "type": "function",
    "inputs": [
      {
        "name": "emergencyAdmin",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "adminFeeTreasury",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "curatorName",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "deployGovernor",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "outputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "nonpayable"
  },
  {
    "name": "getMarketConfigurator",
    "type": "function",
    "inputs": [
      {
        "name": "index",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getMarketConfigurators",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getNumMarketConfigurators",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "getShutdownMarketConfigurators",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "isMarketConfigurator",
    "type": "function",
    "inputs": [
      {
        "name": "account",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "bool",
        "internalType": "bool"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "shutdownMarketConfigurator",
    "type": "function",
    "inputs": [
      {
        "name": "marketConfigurator",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "version",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  }
]