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 ""&'<>" 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"
}
]