Overview
MON Balance
MON Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 35732753 | 72 days ago | Contract Creation | 0 MON |
Loading...
Loading
Contract Name:
PriceFeedStore
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 1000 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {Ownable, Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IPriceFeed, IUpdatablePriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {PriceFeedValidationTrait} from "@gearbox-protocol/core-v3/contracts/traits/PriceFeedValidationTrait.sol";
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
import {IPriceFeedStore, PriceUpdate} from "../interfaces/IPriceFeedStore.sol";
import {Call, ConnectedPriceFeed, PriceFeedInfo} from "../interfaces/Types.sol";
import {
AP_BYTECODE_REPOSITORY,
AP_INSTANCE_MANAGER_PROXY,
AP_PRICE_FEED_STORE,
AP_ZERO_PRICE_FEED,
NO_VERSION_CONTROL
} from "../libraries/ContractLiterals.sol";
import {NestedPriceFeeds} from "../libraries/NestedPriceFeeds.sol";
import {DeployerTrait} from "../traits/DeployerTrait.sol";
import {ImmutableOwnableTrait} from "../traits/ImmutableOwnableTrait.sol";
/// @title Price feed store
contract PriceFeedStore is
DeployerTrait,
ImmutableOwnableTrait,
PriceFeedValidationTrait,
SanityCheckTrait,
IPriceFeedStore
{
using Address for address;
using EnumerableSet for EnumerableSet.AddressSet;
using NestedPriceFeeds for IPriceFeed;
/// @notice Contract version
uint256 public constant override version = 3_10;
/// @notice Contract type
bytes32 public constant override contractType = AP_PRICE_FEED_STORE;
/// @notice Zero price feed address
address public immutable override zeroPriceFeed;
/// @dev Set of all known price feeds
EnumerableSet.AddressSet internal _knownPriceFeeds;
/// @dev Set of all known tokens
EnumerableSet.AddressSet internal _knownTokens;
/// @dev Set of all updatable price feeds
EnumerableSet.AddressSet internal _updatablePriceFeeds;
/// @dev Mapping from `token` to its set of allowed price feeds
mapping(address token => EnumerableSet.AddressSet) internal _allowedPriceFeeds;
/// @dev Mapping from a `(token, priceFeed)` pair to a timestamp when `priceFeed` was allowed for `token`
mapping(address token => mapping(address priceFeed => uint256)) internal _allowanceTimestamps;
/// @dev Mapping from `priceFeed` to its info
mapping(address priceFeed => PriceFeedInfo) internal _priceFeedInfo;
/// @notice Constructor
/// @param addressProvider_ Address provider contract address
constructor(address addressProvider_)
DeployerTrait(addressProvider_)
ImmutableOwnableTrait(
IAddressProvider(addressProvider_).getAddressOrRevert(AP_INSTANCE_MANAGER_PROXY, NO_VERSION_CONTROL)
)
{
zeroPriceFeed = _deploy(AP_ZERO_PRICE_FEED, 3_10, "", bytes32(0));
}
// ------- //
// GETTERS //
// ------- //
/// @notice Returns the list of price feeds allowed for `token`
function getPriceFeeds(address token) public view override returns (address[] memory) {
return _allowedPriceFeeds[token].values();
}
/// @notice Returns whether `priceFeed` is allowed for `token`
function isAllowedPriceFeed(address token, address priceFeed) external view override returns (bool) {
return _allowedPriceFeeds[token].contains(priceFeed);
}
/// @notice Returns the staleness period of `priceFeed`
/// @dev Reverts if `priceFeed` is not known
function getStalenessPeriod(address priceFeed) external view override returns (uint32) {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
return _priceFeedInfo[priceFeed].stalenessPeriod;
}
/// @notice Returns the timestamp when `priceFeed` was allowed for `token`
/// @dev Reverts if `priceFeed` is not allowed for `token`
function getAllowanceTimestamp(address token, address priceFeed) external view override returns (uint256) {
if (!_allowedPriceFeeds[token].contains(priceFeed)) revert PriceFeedIsNotAllowedException(token, priceFeed);
return _allowanceTimestamps[token][priceFeed];
}
/// @notice Returns whether `token` is known
function isKnownToken(address token) external view override returns (bool) {
return _knownTokens.contains(token);
}
/// @notice Returns the list of known tokens
function getKnownTokens() external view override returns (address[] memory) {
return _knownTokens.values();
}
/// @notice Returns the list of tokens with their allowed price feeds
function getTokenPriceFeedsMap() external view override returns (ConnectedPriceFeed[] memory connectedPriceFeeds) {
address[] memory tokens = _knownTokens.values();
uint256 len = tokens.length;
connectedPriceFeeds = new ConnectedPriceFeed[](len);
for (uint256 i; i < len; ++i) {
connectedPriceFeeds[i].token = tokens[i];
connectedPriceFeeds[i].priceFeeds = getPriceFeeds(tokens[i]);
}
}
/// @notice Returns whether `priceFeed` is known
function isKnownPriceFeed(address priceFeed) external view override returns (bool) {
return _knownPriceFeeds.contains(priceFeed);
}
/// @notice Returns the list of known price feeds
function getKnownPriceFeeds() external view override returns (address[] memory) {
return _knownPriceFeeds.values();
}
/// @notice Returns the info for `priceFeed`
function priceFeedInfo(address priceFeed) external view override returns (PriceFeedInfo memory) {
return _priceFeedInfo[priceFeed];
}
// ------------- //
// CONFIGURATION //
// ------------- //
/// @notice Adds a new price feed to the store
/// @param priceFeed The address of the new price feed
/// @param stalenessPeriod Staleness period of the new price feed
/// @param name Name of the new price feed
/// @dev Reverts if caller is not owner
/// @dev Reverts if `priceFeed` is zero address or is already added
/// @dev Validates `priceFeed`'s tree and adds all updatable price feeds from it to the store.
function addPriceFeed(address priceFeed, uint32 stalenessPeriod, string calldata name)
external
override
onlyOwner
nonZeroAddress(priceFeed)
{
if (!_knownPriceFeeds.add(priceFeed)) revert PriceFeedIsAlreadyAddedException(priceFeed);
_validatePriceFeed(priceFeed, stalenessPeriod);
bool isExternal = _validatePriceFeedTree(priceFeed);
_priceFeedInfo[priceFeed] = PriceFeedInfo({
stalenessPeriod: stalenessPeriod,
priceFeedType: isExternal ? bytes32("PRICE_FEED::EXTERNAL") : IPriceFeed(priceFeed).contractType(),
version: isExternal ? 0 : IPriceFeed(priceFeed).version(),
name: name
});
emit AddPriceFeed(priceFeed, stalenessPeriod, name);
}
/// @notice Forbids `priceFeed` for all tokens and removes it from the store
/// @dev Reverts if caller is not owner
/// @dev Reverts if `priceFeed` is not known
function removePriceFeed(address priceFeed) external override onlyOwner {
if (!_knownPriceFeeds.remove(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
delete _priceFeedInfo[priceFeed];
uint256 numTokens = _knownTokens.length();
for (uint256 i; i < numTokens; ++i) {
address token = _knownTokens.at(i);
if (_allowedPriceFeeds[token].remove(priceFeed)) {
_allowanceTimestamps[token][priceFeed] = 0;
emit ForbidPriceFeed(token, priceFeed);
}
}
emit RemovePriceFeed(priceFeed);
}
/// @notice Sets `priceFeed`'s staleness period to `stalenessPeriod`
/// @dev Reverts if caller is not owner
/// @dev Reverts if `priceFeed` is not known
function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod) external override onlyOwner {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
if (_priceFeedInfo[priceFeed].stalenessPeriod == stalenessPeriod) return;
_validatePriceFeed(priceFeed, stalenessPeriod);
_priceFeedInfo[priceFeed].stalenessPeriod = stalenessPeriod;
emit SetStalenessPeriod(priceFeed, stalenessPeriod);
}
/// @notice Allows `priceFeed` for `token`
/// @dev Reverts if caller is not owner
/// @dev Reverts if `token` is zero address
/// @dev Reverts if `priceFeed` is not known or is already allowed for `token`
function allowPriceFeed(address token, address priceFeed) external override onlyOwner nonZeroAddress(token) {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
if (!_allowedPriceFeeds[token].add(priceFeed)) revert PriceFeedIsAlreadyAllowedException(token, priceFeed);
_allowanceTimestamps[token][priceFeed] = block.timestamp;
_knownTokens.add(token);
emit AllowPriceFeed(token, priceFeed);
}
/// @notice Forbids `priceFeed` for `token`
/// @dev Reverts if caller is not owner
/// @dev Reverts if `priceFeed` is not known or is not allowed for `token`
function forbidPriceFeed(address token, address priceFeed) external override onlyOwner {
if (!_knownPriceFeeds.contains(priceFeed)) revert PriceFeedIsNotKnownException(priceFeed);
if (!_allowedPriceFeeds[token].remove(priceFeed)) revert PriceFeedIsNotAllowedException(token, priceFeed);
_allowanceTimestamps[token][priceFeed] = 0;
emit ForbidPriceFeed(token, priceFeed);
}
/// @notice Executes price feed configuration `calls` with owner privileges
/// @dev Reverts if caller is not owner
/// @dev Reverts if any of call targets is not a known price feed
/// @dev Reverts if any of calls renounces ownership over price feed
function configurePriceFeeds(Call[] calldata calls) external override onlyOwner {
uint256 numCalls = calls.length;
for (uint256 i; i < numCalls; ++i) {
if (!_knownPriceFeeds.contains(calls[i].target)) revert PriceFeedIsNotKnownException(calls[i].target);
bytes4 selector = bytes4(calls[i].callData);
if (selector == Ownable.renounceOwnership.selector) {
revert ForbiddenConfigurationMethodException(selector);
}
calls[i].target.functionCall(calls[i].callData);
}
}
// ------------- //
// PRICE UPDATES //
// ------------- //
/// @notice Returns the list of updatable price feeds
function getUpdatablePriceFeeds() external view override returns (address[] memory) {
return _updatablePriceFeeds.values();
}
/// @notice Performs on-demand price feed updates
/// @dev Reverts if any of the price feeds is not added to the updatable price feeds set
function updatePrices(PriceUpdate[] calldata updates) external override {
uint256 numUpdates = updates.length;
for (uint256 i; i < numUpdates; ++i) {
if (!_updatablePriceFeeds.contains(updates[i].priceFeed)) {
revert PriceFeedIsNotUpdatableException(updates[i].priceFeed);
}
IUpdatablePriceFeed(updates[i].priceFeed).updatePrice(updates[i].data);
}
}
// --------- //
// INTERNALS //
// --------- //
/// @dev Validates `priceFeed`'s tree and adds all updatable price feeds from it to the store.
/// Returns whether `priceFeed` is deployed externally or via BCR.
/// Externally deployed price feeds are assumed to be non-updatable leaves of the tree.
function _validatePriceFeedTree(address priceFeed) internal returns (bool) {
if (_validatePriceFeedDeployment(priceFeed)) return true;
if (_isUpdatable(priceFeed) && _updatablePriceFeeds.add(priceFeed)) emit AddUpdatablePriceFeed(priceFeed);
address[] memory underlyingFeeds = IPriceFeed(priceFeed).getUnderlyingFeeds();
uint256 numFeeds = underlyingFeeds.length;
for (uint256 i; i < numFeeds; ++i) {
_validatePriceFeedTree(underlyingFeeds[i]);
}
return false;
}
/// @dev Returns whether `priceFeed` is deployed externally or via BCR.
/// For latter case, also ensures that price feed is owned by the store.
function _validatePriceFeedDeployment(address priceFeed) internal returns (bool) {
if (!IBytecodeRepository(bytecodeRepository).isDeployedFromRepository(priceFeed)) return true;
try Ownable2Step(priceFeed).acceptOwnership() {} catch {}
try Ownable(priceFeed).owner() returns (address owner_) {
if (owner_ != address(this)) revert PriceFeedIsNotOwnedByStore(priceFeed);
} catch {}
return false;
}
/// @dev Returns whether `priceFeed` is updatable
function _isUpdatable(address priceFeed) internal view returns (bool) {
try IUpdatablePriceFeed(priceFeed).updatable() returns (bool updatable) {
return updatable;
} catch {
return false;
}
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
struct AddressProviderEntry {
bytes32 key;
uint256 ver;
address value;
}
struct AuditReport {
address auditor;
string reportUrl;
bytes signature;
}
struct Bytecode {
bytes32 contractType;
uint256 version;
bytes initCode;
address author;
string source;
bytes authorSignature;
}
struct BytecodePointer {
bytes32 contractType;
uint256 version;
address[] initCodePointers;
address author;
string source;
bytes authorSignature;
}
struct Call {
address target;
bytes callData;
}
struct ConnectedPriceFeed {
address token;
address[] priceFeeds;
}
struct CrossChainCall {
uint256 chainId; // 0 means to be executed on all chains
address target;
bytes callData;
}
struct DeployParams {
bytes32 postfix;
bytes32 salt;
bytes constructorParams;
}
struct DeployResult {
address newContract;
Call[] onInstallOps;
}
struct MarketFactories {
address poolFactory;
address priceOracleFactory;
address interestRateModelFactory;
address rateKeeperFactory;
address lossPolicyFactory;
}
struct PriceFeedInfo {
string name;
uint32 stalenessPeriod;
bytes32 priceFeedType;
uint256 version;
}
struct SignedBatch {
string name;
bytes32 prevHash;
CrossChainCall[] calls;
bytes[] signatures;
}
struct SignedRecoveryModeMessage {
uint256 chainId;
bytes32 startingBatchHash;
bytes[] signatures;
}
struct Split {
bool initialized;
address[] receivers;
uint16[] proportions;
}
struct TwoAdminProposal {
bytes callData;
bool confirmedByAdmin;
bool confirmedByTreasuryProxy;
}// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {LibString} from "@solady/utils/LibString.sol";
library Domain {
using LibString for string;
using LibString for bytes32;
uint128 internal constant UNDERSCORE = 1 << 95;
function getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) {
if (postfix == 0) return domain;
return string.concat(domain.fromSmallString(), "::", postfix.fromSmallString()).toSmallString();
}
function extractDomain(bytes32 contractType) internal pure returns (bytes32) {
string memory str = contractType.fromSmallString();
uint256 separatorIndex = str.indexOf("::");
// If no separator found, treat the whole type as domain
if (separatorIndex == LibString.NOT_FOUND) return str.toSmallString();
return str.slice(0, separatorIndex).toSmallString();
}
function extractPostfix(bytes32 contractType) internal pure returns (bytes32) {
string memory str = contractType.fromSmallString();
uint256 separatorIndex = str.indexOf("::");
// if no separator found, return empty postfix
if (separatorIndex == LibString.NOT_FOUND) return bytes32(0);
return str.slice(separatorIndex + 2).toSmallString();
}
function isValidContractType(bytes32 contractType) internal pure returns (bool) {
bytes32 domain = extractDomain(contractType);
if (!isValidDomain(domain)) return false;
bytes32 postfix = extractPostfix(contractType);
if (!isValidPostfix(postfix)) return false;
// avoid the "DOMAIN::" case
return contractType == getContractType(domain, postfix);
}
function isValidDomain(bytes32 domain) internal pure returns (bool) {
return domain != 0 && _isValidString(domain.fromSmallString());
}
function isValidPostfix(bytes32 postfix) internal pure returns (bool) {
return _isValidString(postfix.fromSmallString());
}
function _isValidString(string memory str) internal pure returns (bool) {
return str.is7BitASCII(LibString.ALPHANUMERIC_7_BIT_ASCII | UNDERSCORE);
}
}// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {LibString} from "@solady/utils/LibString.sol";
import {IAddressProvider} from "../interfaces/IAddressProvider.sol";
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
import {IDeployerTrait} from "../interfaces/base/IDeployerTrait.sol";
import {AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol";
import {Domain} from "../libraries/Domain.sol";
abstract contract DeployerTrait is IDeployerTrait {
using LibString for string;
using LibString for bytes32;
address public immutable override addressProvider;
address public immutable override bytecodeRepository;
constructor(address addressProvider_) {
addressProvider = addressProvider_;
bytecodeRepository = _getAddressOrRevert(AP_BYTECODE_REPOSITORY, NO_VERSION_CONTROL);
}
function _getAddress(bytes32 key, uint256 version) internal view returns (address) {
return IAddressProvider(addressProvider).getAddress(key, version);
}
function _getAddressOrRevert(bytes32 key, uint256 version) internal view returns (address) {
return IAddressProvider(addressProvider).getAddressOrRevert(key, version);
}
function _getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) {
return Domain.getContractType(domain, postfix);
}
function _deploy(bytes32 contractType, uint256 version, bytes memory constructorParams, bytes32 salt)
internal
returns (address)
{
return IBytecodeRepository(bytecodeRepository).deploy(contractType, version, constructorParams, salt);
}
function _computeAddress(
bytes32 contractType,
uint256 version,
bytes memory constructorParams,
bytes32 salt,
address deployer
) internal view returns (address) {
return IBytecodeRepository(bytecodeRepository).computeAddress(
contractType, version, constructorParams, salt, deployer
);
}
function _deployLatestPatch(
bytes32 contractType,
uint256 minorVersion,
bytes memory constructorParams,
bytes32 salt
) internal returns (address) {
// NOTE: it's best to add a check that deployed contract's version matches the expected one in the governor
return _deploy(contractType, _getLatestPatchVersion(contractType, minorVersion), constructorParams, salt);
}
function _computeAddressLatestPatch(
bytes32 contractType,
uint256 minorVersion,
bytes memory constructorParams,
bytes32 salt,
address deployer
) internal view returns (address) {
return _computeAddress(
contractType, _getLatestPatchVersion(contractType, minorVersion), constructorParams, salt, deployer
);
}
function _getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) internal view returns (uint256) {
return IBytecodeRepository(bytecodeRepository).getLatestPatchVersion(contractType, minorVersion);
}
function _getTokenSpecificPostfix(address token) internal view returns (bytes32) {
return IBytecodeRepository(bytecodeRepository).getTokenSpecificPostfix(token);
}
}// 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)))
}
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {
IPriceFeedStore as IPriceFeedStoreBase,
PriceUpdate
} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeedStore.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IDeployerTrait} from "./base/IDeployerTrait.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {Call, ConnectedPriceFeed, PriceFeedInfo} from "./Types.sol";
/// @title Price feed store interface
interface IPriceFeedStore is IPriceFeedStoreBase, IVersion, IDeployerTrait, IImmutableOwnableTrait {
// ------ //
// ERRORS //
// ------ //
/// @notice Thrown when attempting to use a price feed that is not known by the price feed store
error PriceFeedIsNotKnownException(address priceFeed);
/// @notice Thrown when attempting to add a price feed that is already known by the price feed store
error PriceFeedIsAlreadyAddedException(address priceFeed);
/// @notice Thrown when attempting to forbid a price feed that is not allowed for a token
error PriceFeedIsNotAllowedException(address token, address priceFeed);
/// @notice Thrown when attempting to allow a price feed that is already allowed for a token
error PriceFeedIsAlreadyAllowedException(address token, address priceFeed);
/// @notice Thrown when attempting to add a price feed that is not owned by the store
error PriceFeedIsNotOwnedByStore(address priceFeed);
/// @notice Thrown when attempting to update a price feed that is not added to the updatable price feeds set
error PriceFeedIsNotUpdatableException(address priceFeed);
/// @notice Thrown when attempting to call a forbidden configuration method
error ForbiddenConfigurationMethodException(bytes4 selector);
// ------ //
// EVENTS //
// ------ //
/// @notice Emitted when a new price feed is added to the store
event AddPriceFeed(address indexed priceFeed, uint32 stalenessPeriod, string name);
/// @notice Emitted when a price feed is removed from the store
event RemovePriceFeed(address indexed priceFeed);
/// @notice Emitted when the staleness period is set for a price feed
event SetStalenessPeriod(address indexed priceFeed, uint32 stalenessPeriod);
/// @notice Emitted when a price feed is allowed for a token
event AllowPriceFeed(address indexed token, address indexed priceFeed);
/// @notice Emitted when a price feed is forbidden for a token
event ForbidPriceFeed(address indexed token, address indexed priceFeed);
/// @notice Emitted when a price feed is added to the updatable price feeds set
event AddUpdatablePriceFeed(address indexed priceFeed);
// ------- //
// GETTERS //
// ------- //
function zeroPriceFeed() external view returns (address);
function getPriceFeeds(address token) external view returns (address[] memory);
function isAllowedPriceFeed(address token, address priceFeed) external view returns (bool);
function getStalenessPeriod(address priceFeed) external view override returns (uint32);
function getAllowanceTimestamp(address token, address priceFeed) external view returns (uint256);
function getTokenPriceFeedsMap() external view returns (ConnectedPriceFeed[] memory);
function getKnownTokens() external view returns (address[] memory);
function isKnownToken(address token) external view returns (bool);
function getKnownPriceFeeds() external view returns (address[] memory);
function isKnownPriceFeed(address priceFeed) external view returns (bool);
function priceFeedInfo(address priceFeed) external view returns (PriceFeedInfo memory);
// ------------- //
// CONFIGURATION //
// ------------- //
function addPriceFeed(address priceFeed, uint32 stalenessPeriod, string calldata name) external;
function removePriceFeed(address priceFeed) external;
function setStalenessPeriod(address priceFeed, uint32 stalenessPeriod) external;
function allowPriceFeed(address token, address priceFeed) external;
function forbidPriceFeed(address token, address priceFeed) external;
function configurePriceFeeds(Call[] calldata calls) external;
// ------------- //
// PRICE UPDATES //
// ------------- //
function getUpdatablePriceFeeds() external view returns (address[] memory);
function updatePrices(PriceUpdate[] calldata updates) external override;
}// SPDX-License-Identifier: BUSL-1.1 // Gearbox Protocol. Generalized leverage for DeFi protocols // (c) Gearbox Foundation, 2025. pragma solidity ^0.8.23; uint256 constant NO_VERSION_CONTROL = 0; // Contract types and prefixes bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT"; bytes32 constant AP_ACL = "ACL"; bytes32 constant AP_ADDRESS_PROVIDER = "ADDRESS_PROVIDER"; bytes32 constant AP_BOT_LIST = "BOT_LIST"; bytes32 constant AP_BYTECODE_REPOSITORY = "BYTECODE_REPOSITORY"; bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER"; bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR"; bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE"; bytes32 constant AP_CREDIT_FACTORY = "CREDIT_FACTORY"; bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER"; bytes32 constant AP_CROSS_CHAIN_GOVERNANCE = "CROSS_CHAIN_GOVERNANCE"; bytes32 constant AP_CROSS_CHAIN_GOVERNANCE_PROXY = "CROSS_CHAIN_GOVERNANCE_PROXY"; bytes32 constant AP_CROSS_CHAIN_MULTISIG = "CROSS_CHAIN_MULTISIG"; bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING"; bytes32 constant AP_GEAR_TOKEN = "GLOBAL::GEAR_TOKEN"; bytes32 constant AP_GOVERNOR = "GOVERNOR"; bytes32 constant AP_INSTANCE_MANAGER = "INSTANCE_MANAGER"; bytes32 constant AP_INSTANCE_MANAGER_PROXY = "INSTANCE_MANAGER_PROXY"; bytes32 constant AP_INTEREST_RATE_MODEL_DEFAULT = "IRM::DEFAULT"; bytes32 constant AP_INTEREST_RATE_MODEL_FACTORY = "INTEREST_RATE_MODEL_FACTORY"; bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR"; bytes32 constant AP_LOSS_POLICY_ALIASED = "LOSS_POLICY::ALIASED"; bytes32 constant AP_LOSS_POLICY_DEFAULT = "LOSS_POLICY::DEFAULT"; bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY"; bytes32 constant AP_MARKET_CONFIGURATOR = "MARKET_CONFIGURATOR"; bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY"; bytes32 constant AP_MARKET_CONFIGURATOR_LEGACY = "MARKET_CONFIGURATOR::LEGACY"; bytes32 constant AP_POOL = "POOL"; bytes32 constant AP_POOL_FACTORY = "POOL_FACTORY"; bytes32 constant AP_POOL_QUOTA_KEEPER = "POOL_QUOTA_KEEPER"; bytes32 constant AP_PRICE_FEED_STORE = "PRICE_FEED_STORE"; bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE"; bytes32 constant AP_PRICE_ORACLE_FACTORY = "PRICE_ORACLE_FACTORY"; bytes32 constant AP_RATE_KEEPER_FACTORY = "RATE_KEEPER_FACTORY"; bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE"; bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER"; bytes32 constant AP_TREASURY = "TREASURY"; bytes32 constant AP_TREASURY_PROXY = "TREASURY_PROXY"; bytes32 constant AP_TREASURY_SPLITTER = "TREASURY_SPLITTER"; bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN"; bytes32 constant AP_ZERO_PRICE_FEED = "PRICE_FEED::ZERO"; // Common domains bytes32 constant DOMAIN_ACCOUNT_FACTORY = "ACCOUNT_FACTORY"; bytes32 constant DOMAIN_ADAPTER = "ADAPTER"; bytes32 constant DOMAIN_BOT = "BOT"; bytes32 constant DOMAIN_CREDIT_MANAGER = "CREDIT_MANAGER"; bytes32 constant DOMAIN_DEGEN_NFT = "DEGEN_NFT"; bytes32 constant DOMAIN_IRM = "IRM"; bytes32 constant DOMAIN_LOSS_POLICY = "LOSS_POLICY"; bytes32 constant DOMAIN_POOL = "POOL"; bytes32 constant DOMAIN_PRICE_FEED = "PRICE_FEED"; bytes32 constant DOMAIN_RATE_KEEPER = "RATE_KEEPER"; bytes32 constant DOMAIN_ZAPPER = "ZAPPER"; // Roles bytes32 constant ROLE_EMERGENCY_LIQUIDATOR = "EMERGENCY_LIQUIDATOR"; bytes32 constant ROLE_PAUSABLE_ADMIN = "PAUSABLE_ADMIN"; bytes32 constant ROLE_UNPAUSABLE_ADMIN = "UNPAUSABLE_ADMIN";
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IPriceFeed} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IPriceFeed.sol";
import {OptionalCall} from "@gearbox-protocol/core-v3/contracts/libraries/OptionalCall.sol";
interface NestedPriceFeedWithSingleUnderlying is IPriceFeed {
function priceFeed() external view returns (address);
}
interface NestedPriceFeedWithMultipleUnderlyings is IPriceFeed {
function priceFeed0() external view returns (address);
function priceFeed1() external view returns (address);
function priceFeed2() external view returns (address);
function priceFeed3() external view returns (address);
function priceFeed4() external view returns (address);
function priceFeed5() external view returns (address);
function priceFeed6() external view returns (address);
function priceFeed7() external view returns (address);
}
library NestedPriceFeeds {
using OptionalCall for address;
uint256 constant MAX_UNDERLYING_PRICE_FEEDS = 8;
enum NestingType {
NO_NESTING,
SINGLE_UNDERLYING,
MULTIPLE_UNDERLYING
}
function getUnderlyingFeeds(IPriceFeed priceFeed) internal view returns (address[] memory feeds) {
NestingType nestingType = getNestingType(priceFeed);
if (nestingType == NestingType.SINGLE_UNDERLYING) {
feeds = getSingleUnderlyingFeed(NestedPriceFeedWithSingleUnderlying(address(priceFeed)));
} else if (nestingType == NestingType.MULTIPLE_UNDERLYING) {
feeds = getMultipleUnderlyingFeeds(NestedPriceFeedWithMultipleUnderlyings(address(priceFeed)));
}
}
function getNestingType(IPriceFeed priceFeed) internal view returns (NestingType) {
(bool success,) = address(priceFeed).staticCallOptionalSafe(abi.encodeWithSignature("priceFeed()"), 10000);
if (success) return NestingType.SINGLE_UNDERLYING;
(success,) = address(priceFeed).staticCallOptionalSafe(abi.encodeWithSignature("priceFeed0()"), 10000);
if (success) return NestingType.MULTIPLE_UNDERLYING;
return NestingType.NO_NESTING;
}
function getSingleUnderlyingFeed(NestedPriceFeedWithSingleUnderlying priceFeed)
internal
view
returns (address[] memory feeds)
{
feeds = new address[](1);
feeds[0] = priceFeed.priceFeed();
}
function getMultipleUnderlyingFeeds(NestedPriceFeedWithMultipleUnderlyings priceFeed)
internal
view
returns (address[] memory feeds)
{
feeds = new address[](MAX_UNDERLYING_PRICE_FEEDS);
for (uint256 i; i < MAX_UNDERLYING_PRICE_FEEDS; ++i) {
feeds[i] = _getPriceFeedByIndex(priceFeed, i);
if (feeds[i] == address(0)) {
assembly {
mstore(feeds, i)
}
break;
}
}
}
function _getPriceFeedByIndex(NestedPriceFeedWithMultipleUnderlyings priceFeed, uint256 index)
private
view
returns (address)
{
bytes4 selector;
if (index == 0) {
selector = priceFeed.priceFeed0.selector;
} else if (index == 1) {
selector = priceFeed.priceFeed1.selector;
} else if (index == 2) {
selector = priceFeed.priceFeed2.selector;
} else if (index == 3) {
selector = priceFeed.priceFeed3.selector;
} else if (index == 4) {
selector = priceFeed.priceFeed4.selector;
} else if (index == 5) {
selector = priceFeed.priceFeed5.selector;
} else if (index == 6) {
selector = priceFeed.priceFeed6.selector;
} else if (index == 7) {
selector = priceFeed.priceFeed7.selector;
}
(bool success, bytes memory result) =
address(priceFeed).staticCallOptionalSafe(abi.encodePacked(selector), 10000);
if (!success || result.length == 0) return address(0);
return abi.decode(result, (address));
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IAddressProvider as IAddressProviderBase} from
"@gearbox-protocol/core-v3/contracts/interfaces/base/IAddressProvider.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {AddressProviderEntry} from "./Types.sol";
/// @title Address provider interface
interface IAddressProvider is IAddressProviderBase, IVersion, IImmutableOwnableTrait {
// ------ //
// EVENTS //
// ------ //
event SetAddress(bytes32 indexed key, uint256 indexed ver, address indexed value);
// ------ //
// ERRORS //
// ------ //
error AddressNotFoundException(bytes32 key, uint256 ver);
error InvalidVersionException(bytes32 key, uint256 ver);
error VersionNotFoundException(bytes32 key);
error ZeroAddressException(bytes32 key);
// ------- //
// GETTERS //
// ------- //
function getAddress(bytes32 key, uint256 ver) external view returns (address);
function getAddressOrRevert(bytes32 key, uint256 ver) external view override returns (address);
function getKeys() external view returns (bytes32[] memory);
function getVersions(bytes32 key) external view returns (uint256[] memory);
function getAllEntries() external view returns (AddressProviderEntry[] memory);
function getLatestVersion(bytes32 key) external view returns (uint256);
function getLatestMinorVersion(bytes32 key, uint256 majorVersion) external view returns (uint256);
function getLatestPatchVersion(bytes32 key, uint256 minorVersion) external view returns (uint256);
// ------------- //
// CONFIGURATION //
// ------------- //
function setAddress(bytes32 key, address value, bool saveVersion) external;
}// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IImmutableOwnableTrait} from "../interfaces/base/IImmutableOwnableTrait.sol";
abstract contract ImmutableOwnableTrait is IImmutableOwnableTrait {
address public immutable override owner;
modifier onlyOwner() {
if (msg.sender != owner) revert CallerIsNotOwnerException(msg.sender);
_;
}
constructor(address owner_) {
owner = owner_;
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {AuditReport, Bytecode} from "./Types.sol";
/// @title Bytecode repository interface
interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
// ------ //
// EVENTS //
// ------ //
event AddAuditor(address indexed auditor, string name);
event AddPublicDomain(bytes32 indexed domain);
event AddSystemDomain(bytes32 indexed domain);
event AllowContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event AuditBytecode(bytes32 indexed bytecodeHash, address indexed auditor, string reportUrl, bytes signature);
event DeployContract(
bytes32 indexed bytecodeHash,
bytes32 indexed contractType,
uint256 indexed version,
address contractAddress,
bytes constructorParams
);
event ForbidContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event ForbidInitCode(bytes32 indexed initCodeHash);
event RemoveAuditor(address indexed auditor);
event RemoveContractTypeOwner(bytes32 indexed contractType);
event SetContractTypeOwner(bytes32 indexed contractType, address indexed owner);
event SetTokenSpecificPostfix(address indexed token, bytes32 indexed postfix);
event UploadBytecode(
bytes32 indexed bytecodeHash,
bytes32 indexed contractType,
uint256 indexed version,
address author,
string source,
bytes signature
);
// ------ //
// ERRORS //
// ------ //
error AuditorIsNotApprovedException(address auditor);
error AuthorIsNotContractTypeOwnerException(bytes32 contractType, address author);
error BytecodeIsAlreadyAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsAlreadySignedByAuditorException(bytes32 bytecodeHash, address auditor);
error BytecodeIsNotAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsNotAuditedException(bytes32 bytecodeHash);
error BytecodeIsNotUploadedException(bytes32 bytecodeHash);
error CallerIsNotBytecodeAuthorException(address caller);
error ContractIsAlreadyDeployedException(address deployedContract);
error ContractTypeIsNotInPublicDomainException(bytes32 contractType);
error DomainIsAlreadyMarkedAsPublicException(bytes32 domain);
error DomainIsAlreadyMarkedAsSystemException(bytes32 domain);
error InitCodeIsEmptyException();
error InitCodeIsForbiddenException(bytes32 initCodeHash);
error InvalidAuditorSignatureException(address auditor);
error InvalidAuthorSignatureException(address author);
error InvalidBytecodeException(bytes32 bytecodeHash);
error InvalidContractTypeException(bytes32 contractType);
error InvalidDomainException(bytes32 domain);
error InvalidPostfixException(bytes32 postfix);
error InvalidVersionException(bytes32 contractType, uint256 version);
error VersionNotFoundException(bytes32 contractType);
// --------------- //
// EIP-712 GETTERS //
// --------------- //
function BYTECODE_TYPEHASH() external view returns (bytes32);
function AUDIT_REPORT_TYPEHASH() external view returns (bytes32);
function domainSeparatorV4() external view returns (bytes32);
function computeBytecodeHash(Bytecode calldata bytecode) external view returns (bytes32);
function computeAuditReportHash(bytes32 bytecodeHash, AuditReport calldata report)
external
view
returns (bytes32);
// ------------------- //
// DEPLOYING CONTRACTS //
// ------------------- //
function isDeployedFromRepository(address deployedContract) external view returns (bool);
function getDeployedContractBytecodeHash(address deployedContract) external view returns (bytes32);
function computeAddress(
bytes32 contractType,
uint256 version,
bytes calldata constructorParams,
bytes32 salt,
address deployer
) external view returns (address);
function deploy(bytes32 contractType, uint256 version, bytes calldata constructorParams, bytes32 salt)
external
returns (address);
// ------------------ //
// UPLOADING BYTECODE //
// ------------------ //
function getBytecode(bytes32 bytecodeHash) external view returns (Bytecode memory);
function isBytecodeUploaded(bytes32 bytecodeHash) external view returns (bool);
function uploadBytecode(Bytecode calldata bytecode) external;
// ----------------- //
// AUDITING BYTECODE //
// ----------------- //
function isBytecodeAudited(bytes32 bytecodeHash) external view returns (bool);
function getAuditReports(bytes32 bytecodeHash) external view returns (AuditReport[] memory);
function getAuditReport(bytes32 bytecodeHash, uint256 index) external view returns (AuditReport memory);
function getNumAuditReports(bytes32 bytecodeHash) external view returns (uint256);
function submitAuditReport(bytes32 bytecodeHash, AuditReport calldata auditReport) external;
// ----------------- //
// ALLOWING BYTECODE //
// ----------------- //
function getAllowedBytecodeHash(bytes32 contractType, uint256 version) external view returns (bytes32);
function getContractTypeOwner(bytes32 contractType) external view returns (address);
function allowSystemContract(bytes32 bytecodeHash) external;
function allowPublicContract(bytes32 bytecodeHash) external;
function removePublicContractType(bytes32 contractType) external;
// ------------------ //
// DOMAINS MANAGEMENT //
// ------------------ //
function isSystemDomain(bytes32 domain) external view returns (bool);
function getSystemDomains() external view returns (bytes32[] memory);
function isPublicDomain(bytes32 domain) external view returns (bool);
function getPublicDomains() external view returns (bytes32[] memory);
function addPublicDomain(bytes32 domain) external;
// ------------------- //
// AUDITORS MANAGEMENT //
// ------------------- //
function isAuditor(address auditor) external view returns (bool);
function getAuditors() external view returns (address[] memory);
function getAuditorName(address auditor) external view returns (string memory);
function addAuditor(address auditor, string calldata name) external;
function removeAuditor(address auditor) external;
// -------------------- //
// FORBIDDING INIT CODE //
// -------------------- //
function isInitCodeForbidden(bytes32 initCodeHash) external view returns (bool);
function forbidInitCode(bytes32 initCodeHash) external;
// ------------------------ //
// TOKENS WITH CUSTOM LOGIC //
// ------------------------ //
function getTokenSpecificPostfix(address token) external view returns (bytes32);
function setTokenSpecificPostfix(address token, bytes32 postfix) external;
// --------------- //
// VERSION CONTROL //
// --------------- //
function getVersions(bytes32 contractType) external view returns (uint256[] memory);
function getLatestVersion(bytes32 contractType) external view returns (uint256);
function getLatestMinorVersion(bytes32 contractType, uint256 majorVersion) external view returns (uint256);
function getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
/// @title Deployer trait interface
interface IDeployerTrait {
function addressProvider() external view returns (address);
function bytecodeRepository() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
/// @title Immutable ownable trait interface
/// @notice Interface for contracts with immutable owner functionality
interface IImmutableOwnableTrait {
error CallerIsNotOwnerException(address caller);
/// @notice Returns the immutable owner address
function owner() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT // Gearbox Protocol. Generalized leverage for DeFi protocols // (c) Gearbox Foundation, 2024. pragma solidity ^0.8.17; // ------- // // GENERAL // // ------- // /// @notice Thrown on attempting to set an important address to zero address error ZeroAddressException(); /// @notice Thrown when attempting to pass a zero amount to a funding-related operation error AmountCantBeZeroException(); /// @notice Thrown on incorrect input parameter error IncorrectParameterException(); /// @notice Thrown when balance is insufficient to perform an operation error InsufficientBalanceException(); /// @notice Thrown if parameter is out of range error ValueOutOfRangeException(); /// @notice Thrown when trying to send ETH to a contract that is not allowed to receive ETH directly error ReceiveIsNotAllowedException(); /// @notice Thrown on attempting to set an EOA as an important contract in the system error AddressIsNotContractException(address); /// @notice Thrown on attempting to receive a token that is not a collateral token or was forbidden error TokenNotAllowedException(); /// @notice Thrown on attempting to add a token that is already in a collateral list error TokenAlreadyAddedException(); /// @notice Thrown when attempting to use quota-related logic for a token that is not quoted in quota keeper error TokenIsNotQuotedException(); /// @notice Thrown on attempting to interact with an address that is not a valid target contract error TargetContractNotAllowedException(); /// @notice Thrown if function is not implemented error NotImplementedException(); // ------------------ // // CONTRACTS REGISTER // // ------------------ // /// @notice Thrown when an address is expected to be a registered credit manager, but is not error RegisteredCreditManagerOnlyException(); /// @notice Thrown when an address is expected to be a registered pool, but is not error RegisteredPoolOnlyException(); // ---------------- // // ADDRESS PROVIDER // // ---------------- // /// @notice Reverts if address key isn't found in address provider error AddressNotFoundException(); // ----------------- // // POOL, PQK, GAUGES // // ----------------- // /// @notice Thrown by pool-adjacent contracts when a credit manager being connected has a wrong pool address error IncompatibleCreditManagerException(); /// @notice Thrown when attempting to set an incompatible successor staking contract error IncompatibleSuccessorException(); /// @notice Thrown when attempting to vote in a non-approved contract error VotingContractNotAllowedException(); /// @notice Thrown when attempting to unvote more votes than there are error InsufficientVotesException(); /// @notice Thrown when attempting to borrow more than the second point on a two-point curve error BorrowingMoreThanU2ForbiddenException(); /// @notice Thrown when a credit manager attempts to borrow more than its limit in the current block, or in general error CreditManagerCantBorrowException(); /// @notice Thrown when attempting to connect a quota keeper to an incompatible pool error IncompatiblePoolQuotaKeeperException(); /// @notice Thrown when attempting to connect a gauge to an incompatible pool quota keeper error IncompatibleGaugeException(); /// @notice Thrown when the quota is outside of min/max bounds error QuotaIsOutOfBoundsException(); // -------------- // // CREDIT MANAGER // // -------------- // /// @notice Thrown on failing a full collateral check after multicall error NotEnoughCollateralException(); /// @notice Thrown if an attempt to approve a collateral token to adapter's target contract fails error AllowanceFailedException(); /// @notice Thrown on attempting to perform an action for a credit account that does not exist error CreditAccountDoesNotExistException(); /// @notice Thrown on configurator attempting to add more than 255 collateral tokens error TooManyTokensException(); /// @notice Thrown if more than the maximum number of tokens were enabled on a credit account error TooManyEnabledTokensException(); /// @notice Thrown when attempting to execute a protocol interaction without active credit account set error ActiveCreditAccountNotSetException(); /// @notice Thrown when trying to update credit account's debt more than once in the same block error DebtUpdatedTwiceInOneBlockException(); /// @notice Thrown when trying to repay all debt while having active quotas error DebtToZeroWithActiveQuotasException(); /// @notice Thrown when a zero-debt account attempts to update quota error UpdateQuotaOnZeroDebtAccountException(); /// @notice Thrown when attempting to close an account with non-zero debt error CloseAccountWithNonZeroDebtException(); /// @notice Thrown when value of funds remaining on the account after liquidation is insufficient error InsufficientRemainingFundsException(); /// @notice Thrown when Credit Facade tries to write over a non-zero active Credit Account error ActiveCreditAccountOverridenException(); // ------------------- // // CREDIT CONFIGURATOR // // ------------------- // /// @notice Thrown on attempting to use a non-ERC20 contract or an EOA as a token error IncorrectTokenContractException(); /// @notice Thrown if the newly set LT if zero or greater than the underlying's LT error IncorrectLiquidationThresholdException(); /// @notice Thrown if borrowing limits are incorrect: minLimit > maxLimit or maxLimit > blockLimit error IncorrectLimitsException(); /// @notice Thrown if the new expiration date is less than the current expiration date or current timestamp error IncorrectExpirationDateException(); /// @notice Thrown if a contract returns a wrong credit manager or reverts when trying to retrieve it error IncompatibleContractException(); /// @notice Thrown if attempting to forbid an adapter that is not registered in the credit manager error AdapterIsNotRegisteredException(); /// @notice Thrown if new credit configurator's set of allowed adapters differs from the current one error IncorrectAdaptersSetException(); /// @notice Thrown if attempting to schedule a token's LT ramping that is too short in duration error RampDurationTooShortException(); /// @notice Thrown if attempting to set liquidation fees such that the sum of premium and fee changes error InconsistentLiquidationFeesException(); /// @notice Thrown if attempting to set expired liquidation fees such that the sum of premium and fee changes error InconsistentExpiredLiquidationFeesException(); // ------------- // // CREDIT FACADE // // ------------- // /// @notice Thrown when attempting to perform an action that is forbidden in whitelisted mode error ForbiddenInWhitelistedModeException(); /// @notice Thrown if credit facade is not expirable, and attempted aciton requires expirability error NotAllowedWhenNotExpirableException(); /// @notice Thrown if a selector that doesn't match any allowed function is passed to the credit facade in a multicall error UnknownMethodException(bytes4 selector); /// @notice Thrown if a liquidator tries to liquidate an account with a health factor above 1 error CreditAccountNotLiquidatableException(); /// @notice Thrown if a liquidator tries to liquidate an account with loss but violates the loss policy error CreditAccountNotLiquidatableWithLossException(); /// @notice Thrown if too much new debt was taken within a single block error BorrowedBlockLimitException(); /// @notice Thrown if the new debt principal for a credit account falls outside of borrowing limits error BorrowAmountOutOfLimitsException(); /// @notice Thrown if a user attempts to open an account via an expired credit facade error NotAllowedAfterExpirationException(); /// @notice Thrown if expected balances are attempted to be set twice without performing a slippage check error ExpectedBalancesAlreadySetException(); /// @notice Thrown if attempting to perform a slippage check when excepted balances are not set error ExpectedBalancesNotSetException(); /// @notice Thrown if balance of at least one token is less than expected during a slippage check error BalanceLessThanExpectedException(address token); /// @notice Thrown when trying to perform an action that is forbidden when credit account has enabled forbidden tokens error ForbiddenTokensException(uint256 forbiddenTokensMask); /// @notice Thrown when forbidden token quota is increased during the multicall error ForbiddenTokenQuotaIncreasedException(address token); /// @notice Thrown when enabled forbidden token balance is increased during the multicall error ForbiddenTokenBalanceIncreasedException(address token); /// @notice Thrown when the remaining token balance is increased during the liquidation error RemainingTokenBalanceIncreasedException(address token); /// @notice Thrown if `botMulticall` is called by an address that is not approved by account owner or is forbidden error NotApprovedBotException(address bot); /// @notice Thrown when attempting to perform a multicall action with no permission for it error NoPermissionException(uint256 permission); /// @notice Thrown when attempting to give a bot unexpected permissions error UnexpectedPermissionsException(uint256 permissions); /// @notice Thrown when a custom HF parameter lower than 10000 is passed into the full collateral check error CustomHealthFactorTooLowException(); /// @notice Thrown when submitted collateral hint is not a valid token mask error InvalidCollateralHintException(uint256 mask); /// @notice Thrown when trying to seize underlying token during partial liquidation error UnderlyingIsNotLiquidatableException(); /// @notice Thrown when amount of collateral seized during partial liquidation is less than required error SeizedLessThanRequiredException(uint256 seizedAmount); // ------ // // ACCESS // // ------ // /// @notice Thrown on attempting to call an access restricted function not as credit account owner error CallerNotCreditAccountOwnerException(); /// @notice Thrown on attempting to call an access restricted function not as configurator error CallerNotConfiguratorException(); /// @notice Thrown on attempting to call an access-restructed function not as account factory error CallerNotAccountFactoryException(); /// @notice Thrown on attempting to call an access restricted function not as credit manager error CallerNotCreditManagerException(); /// @notice Thrown on attempting to call an access restricted function not as credit facade error CallerNotCreditFacadeException(); /// @notice Thrown on attempting to pause a contract without pausable admin rights error CallerNotPausableAdminException(); /// @notice Thrown on attempting to unpause a contract without unpausable admin rights error CallerNotUnpausableAdminException(); /// @notice Thrown on attempting to call an access restricted function not as gauge error CallerNotGaugeException(); /// @notice Thrown on attempting to call an access restricted function not as quota keeper error CallerNotPoolQuotaKeeperException(); /// @notice Thrown on attempting to call an access restricted function not as voter error CallerNotVoterException(); /// @notice Thrown on attempting to call an access restricted function not as allowed adapter error CallerNotAdapterException(); /// @notice Thrown on attempting to call an access restricted function not as migrator error CallerNotMigratorException(); /// @notice Thrown when an address that is not the designated executor attempts to execute a transaction error CallerNotExecutorException(); /// @notice Thrown on attempting to call an access restricted function not as veto admin error CallerNotVetoAdminException(); // -------- // // BOT LIST // // -------- // /// @notice Thrown when attempting to set non-zero permissions for a forbidden bot error InvalidBotException(); /// @notice Thrown when attempting to set permissions for a bot that don't meet its requirements error IncorrectBotPermissionsException(); /// @notice Thrown when attempting to set non-zero permissions for too many bots error TooManyActiveBotsException(); // --------------- // // ACCOUNT FACTORY // // --------------- // /// @notice Thrown when trying to deploy second master credit account for a credit manager error MasterCreditAccountAlreadyDeployedException(); /// @notice Thrown when trying to rescue funds from a credit account that is currently in use error CreditAccountIsInUseException(); // ------------ // // PRICE ORACLE // // ------------ // /// @notice Thrown on attempting to set a token price feed to an address that is not a correct price feed error IncorrectPriceFeedException(); /// @notice Thrown on attempting to interact with a price feed for a token not added to the price oracle error PriceFeedDoesNotExistException(); /// @notice Thrown when trying to apply an on-demand price update to a non-updatable price feed error PriceFeedIsNotUpdatableException(); /// @notice Thrown when price feed returns incorrect price for a token error IncorrectPriceException(); /// @notice Thrown when token's price feed becomes stale error StalePriceException();
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
/// @title Optional call library
/// @notice Implements a function that calls a contract that may not have an expected selector.
/// Handles the case where the contract has a fallback function that may or may not change state.
library OptionalCall {
function staticCallOptionalSafe(address target, bytes memory data, uint256 gasAllowance)
internal
view
returns (bool, bytes memory)
{
(bool success, bytes memory returnData) = target.staticcall{gas: gasAllowance}(data);
return returnData.length > 0 ? (success, returnData) : (false, returnData);
}
}// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {ZeroAddressException} from "../interfaces/IExceptions.sol";
/// @title Sanity check trait
abstract contract SanityCheckTrait {
/// @dev Ensures that passed address is non-zero
modifier nonZeroAddress(address addr) {
_revertIfZeroAddress(addr);
_;
}
/// @dev Reverts if address is zero
function _revertIfZeroAddress(address addr) private pure {
if (addr == address(0)) revert ZeroAddressException();
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
/// @title Version interface
/// @notice Defines contract version and type
interface IVersion {
/// @notice Contract version
function version() external view returns (uint256);
/// @notice Contract type
function contractType() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {IVersion} from "./IVersion.sol";
import {IStateSerializer} from "./IStateSerializer.sol";
/// @title Price feed interface
/// @notice Interface for Chainlink-like price feeds that can be plugged into Gearbox's price oracle
/// @dev Price feeds must have type `PRICE_FEED::{POSTFIX}`
interface IPriceFeed is IVersion, IStateSerializer {
/// @notice Whether price feed implements its own staleness and sanity checks
function skipPriceCheck() external view returns (bool);
/// @notice Scale decimals of price feed answers
function decimals() external view returns (uint8);
/// @notice Price feed description
function description() external view returns (string memory);
/// @notice Price feed answer in standard Chainlink format, only `answer` and `updatedAt` fields are used
function latestRoundData() external view returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80);
}
/// @title Updatable price feed interface
/// @notice Extended version of `IPriceFeed` for pull oracles that allow on-demand updates
interface IUpdatablePriceFeed is IPriceFeed {
/// @notice Emitted when price is updated
event UpdatePrice(uint256 price);
/// @notice Whether price feed is updatable
function updatable() external view returns (bool);
/// @notice Performs on-demand price update
function updatePrice(bytes calldata data) external;
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {IVersion} from "./IVersion.sol";
struct PriceUpdate {
address priceFeed;
bytes data;
}
interface IPriceFeedStore {
function getStalenessPeriod(address priceFeed) external view returns (uint32);
function updatePrices(PriceUpdate[] calldata updates) external;
}// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OptionalCall} from "../libraries/OptionalCall.sol";
import {
AddressIsNotContractException,
IncorrectParameterException,
IncorrectPriceException,
IncorrectPriceFeedException,
PriceFeedDoesNotExistException,
StalePriceException
} from "../interfaces/IExceptions.sol";
import {IPriceFeed, IUpdatablePriceFeed} from "../interfaces/base/IPriceFeed.sol";
/// @title Price feed validation trait
abstract contract PriceFeedValidationTrait {
using Address for address;
/// @dev Ensures that price feed's answer is positive and not stale.
/// If `skipCheck` is true, only checks that price is non-negative to allow zero price feed to be used.
/// @custom:tests U:[PO-9]
function _checkAnswer(int256 price, uint256 updatedAt, uint32 stalenessPeriod, bool skipCheck) internal view {
if (price < 0 || !skipCheck && price == 0) revert IncorrectPriceException();
if (!skipCheck && block.timestamp >= updatedAt + stalenessPeriod) revert StalePriceException();
}
/// @dev Validates that `priceFeed` is a contract that adheres to Chainlink interface
/// @dev Reverts if `priceFeed` does not have exactly 8 decimals
/// @dev Reverts if `stalenessPeriod` is inconsistent with `priceFeed`'s `skipPriceCheck()` flag
/// (which is considered to be false if `priceFeed` does not have this function)
/// @custom:tests U:[PO-8], U:[PO-10]
function _validatePriceFeedMetadata(address priceFeed, uint32 stalenessPeriod)
internal
view
returns (bool skipCheck)
{
if (!priceFeed.isContract()) revert AddressIsNotContractException(priceFeed);
try IPriceFeed(priceFeed).decimals() returns (uint8 _decimals) {
if (_decimals != 8) revert IncorrectPriceFeedException();
} catch {
revert IncorrectPriceFeedException();
}
// NOTE: Some external price feeds without `skipPriceCheck` may have a fallback function that changes state,
// which can cause a `THROW` that burns all gas, or does not change state and instead returns empty data.
// To handle these cases, we use a special call construction with a strict gas limit.
(bool success, bytes memory returnData) = OptionalCall.staticCallOptionalSafe({
target: priceFeed,
data: abi.encodeWithSelector(IPriceFeed.skipPriceCheck.selector),
gasAllowance: 10_000
});
if (success) skipCheck = abi.decode(returnData, (bool));
if (skipCheck && stalenessPeriod != 0 || !skipCheck && stalenessPeriod == 0) {
revert IncorrectParameterException();
}
}
/// @dev Validates that `priceFeed` is a contract that adheres to Chainlink interface and returns valid answer
/// @custom:tests U:[PO-8], U:[PO-10]
function _validatePriceFeed(address priceFeed, uint32 stalenessPeriod) internal view returns (bool skipCheck) {
skipCheck = _validatePriceFeedMetadata(priceFeed, stalenessPeriod);
try IPriceFeed(priceFeed).latestRoundData() returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80)
{
_checkAnswer(answer, updatedAt, stalenessPeriod, skipCheck);
} catch {
revert IncorrectPriceFeedException();
}
}
/// @dev Returns answer from a price feed with optional sanity and staleness checks
/// @custom:tests U:[PO-9]
function _getValidatedPrice(address priceFeed, uint32 stalenessPeriod, bool skipCheck)
internal
view
returns (int256 answer)
{
uint256 updatedAt;
(, answer,, updatedAt,) = IPriceFeed(priceFeed).latestRoundData();
_checkAnswer(answer, updatedAt, stalenessPeriod, skipCheck);
}
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
interface IAddressProvider {
function getAddressOrRevert(bytes32 key, uint256 version) external view returns (address);
}// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
/// @title State serializer interface
/// @notice Generic interface for a contract that can serialize its state into a bytes array
interface IStateSerializer {
/// @notice Serializes the state of the contract into a bytes array `serializedData`
function serialize() external view returns (bytes memory serializedData);
}{
"remappings": [
"@1inch/=lib/@1inch/",
"@gearbox-protocol/=lib/@gearbox-protocol/",
"@openzeppelin/=lib/@openzeppelin/",
"@redstone-finance/=node_modules/@redstone-finance/",
"@solady/=lib/@solady/src/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/@openzeppelin/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin/=lib/@openzeppelin/contracts/"
],
"optimizer": {
"runs": 1000,
"enabled": true
},
"metadata": {
"bytecodeHash": "none",
"useLiteralContent": true
},
"evmVersion": "shanghai",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"addressProvider_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"AddressIsNotContractException","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotOwnerException","type":"error"},{"inputs":[{"internalType":"bytes4","name":"selector","type":"bytes4"}],"name":"ForbiddenConfigurationMethodException","type":"error"},{"inputs":[],"name":"IncorrectParameterException","type":"error"},{"inputs":[],"name":"IncorrectPriceException","type":"error"},{"inputs":[],"name":"IncorrectPriceFeedException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsAlreadyAddedException","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsAlreadyAllowedException","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotAllowedException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotKnownException","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotOwnedByStore","type":"error"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"PriceFeedIsNotUpdatableException","type":"error"},{"inputs":[],"name":"StalePriceException","type":"error"},{"inputs":[],"name":"ZeroAddressException","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"},{"indexed":false,"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"indexed":false,"internalType":"string","name":"name","type":"string"}],"name":"AddPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"AddUpdatablePriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"AllowPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"ForbidPriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"}],"name":"RemovePriceFeed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"priceFeed","type":"address"},{"indexed":false,"internalType":"uint32","name":"stalenessPeriod","type":"uint32"}],"name":"SetStalenessPeriod","type":"event"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"internalType":"string","name":"name","type":"string"}],"name":"addPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"addressProvider","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"allowPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bytecodeRepository","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"}],"internalType":"struct Call[]","name":"calls","type":"tuple[]"}],"name":"configurePriceFeeds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractType","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"forbidPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getAllowanceTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKnownPriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKnownTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getPriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"getStalenessPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenPriceFeedsMap","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address[]","name":"priceFeeds","type":"address[]"}],"internalType":"struct ConnectedPriceFeed[]","name":"connectedPriceFeeds","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUpdatablePriceFeeds","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"}],"name":"isAllowedPriceFeed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"isKnownPriceFeed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isKnownToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"priceFeedInfo","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"},{"internalType":"bytes32","name":"priceFeedType","type":"bytes32"},{"internalType":"uint256","name":"version","type":"uint256"}],"internalType":"struct PriceFeedInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"}],"name":"removePriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"uint32","name":"stalenessPeriod","type":"uint32"}],"name":"setStalenessPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct PriceUpdate[]","name":"updates","type":"tuple[]"}],"name":"updatePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"zeroPriceFeed","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61010060405234801562000011575f80fd5b5060405162002cf338038062002cf383398101604081905262000034916200025a565b604051632bdad0e360e11b81527f494e5354414e43455f4d414e414745525f50524f58590000000000000000000060048201525f60248201526001600160a01b038216906357b5a1c690604401602060405180830381865afa1580156200009d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620000c391906200025a565b6001600160a01b03821660805281620000fd7f42595445434f44455f5245504f5349544f5259000000000000000000000000005f6200015a565b6001600160a01b0390811660a0529190911660c0525060408051602081019091525f80825262000147916f50524943455f464545443a3a5a45524f60801b916101369190620001d6565b6001600160a01b031660e05250620002e6565b608051604051632bdad0e360e11b815260048101849052602481018390525f916001600160a01b0316906357b5a1c690604401602060405180830381865afa158015620001a9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620001cf91906200025a565b9392505050565b60a051604051635ce8938f60e01b81525f916001600160a01b031690635ce8938f906200020e90889088908890889060040162000282565b6020604051808303815f875af11580156200022b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200025191906200025a565b95945050505050565b5f602082840312156200026b575f80fd5b81516001600160a01b0381168114620001cf575f80fd5b8481525f60208560208401526080604084015284518060808501525f5b81811015620002bd5786810183015185820160a0015282016200029f565b505f60a0828601015260a0601f19601f8301168501019250505082606083015295945050505050565b60805160a05160c05160e0516129a86200034b5f395f61030201525f8181610329015281816104110152818161064d0152818161094101528181610ab501528181610bd701526111bc01525f818161028701526118e701525f6101f301526129a85ff3fe608060405234801561000f575f80fd5b506004361061018f575f3560e01c806371346b42116100dd578063ba0f681e11610088578063ce37a61e11610063578063ce37a61e146103b8578063e647e5cc146103e0578063fceb0024146103f3575f80fd5b8063ba0f681e1461036b578063c7a22fe01461037e578063cb2ef6f714610391575f80fd5b806389e402f1116100b857806389e402f1146102fd5780638da5cb5b14610324578063a9ff6c0f1461034b575f80fd5b806371346b42146102c45780637199e2c9146102d75780637cc6ce52146102ea575f80fd5b80634026ebd61161013d57806360e93cfd1161011857806360e93cfd14610282578063614eb733146102a95780636f898fb4146102b1575f80fd5b80634026ebd61461025057806345af83961461025857806354fd4d501461026b575f80fd5b806326ec6c301161016d57806326ec6c30146101d95780632954018c146101ee57806330d1f9fe1461022d575f80fd5b8063149d919114610193578063161455ad146101a857806316f5a392146101c6575b5f80fd5b6101a66101a13660046122c2565b610406565b005b6101b0610526565b6040516101bd91906122f5565b60405180910390f35b6101a66101d43660046123a2565b610642565b6101e16108ef565b6040516101bd919061242b565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101bd565b61024061023b366004612477565b610900565b60405190151581526020016101bd565b6101e161092a565b6101a6610266366004612477565b610936565b61027461013681565b6040519081526020016101bd565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6101e1610a9f565b6101a66102bf366004612477565b610aaa565b6101a66102d23660046124f6565b610bcc565b6101a66102e53660046124f6565b610e22565b6101e16102f8366004612535565b610fa5565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b6102157f000000000000000000000000000000000000000000000000000000000000000081565b61035e610359366004612535565b610fc8565b6040516101bd919061259d565b610274610379366004612477565b6110c7565b61024061038c366004612535565b611143565b6102747f50524943455f464545445f53544f52450000000000000000000000000000000081565b6103cb6103c6366004612535565b61114e565b60405163ffffffff90911681526020016101bd565b6102406103ee366004612535565b6111a5565b6101a6610401366004612535565b6111b1565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461045657604051630cc479f960e01b81523360048201526024015b60405180910390fd5b6104605f8361134e565b61048857604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0382165f9081526008602052604090206001015463ffffffff828116911614610522576104bc828261136f565b506001600160a01b0382165f81815260086020908152604091829020600101805463ffffffff191663ffffffff861690811790915591519182527fd85fed169ea0f7852e9ec4becfec49eca5fe689c65e95486e97ab1f4f02623b9910160405180910390a25b5050565b60605f610533600261140a565b80519091508067ffffffffffffffff811115610551576105516125e8565b60405190808252806020026020018201604052801561059657816020015b604080518082019091525f81526060602082015281526020019060019003908161056f5790505b5092505f5b8181101561063c578281815181106105b5576105b56125fc565b60200260200101518482815181106105cf576105cf6125fc565b60200260200101515f01906001600160a01b031690816001600160a01b031681525050610614838281518110610607576106076125fc565b6020026020010151610fa5565b848281518110610626576106266125fc565b602090810291909101810151015260010161059b565b50505090565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461068d57604051630cc479f960e01b815233600482015260240161044d565b836106978161141d565b6106a15f86611460565b6106e2576040517f8d8d2d3a0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260240161044d565b6106ec858561136f565b505f6106f786611474565b6040805160a06020601f880181900402820181019092526080810186815292935091829187908790819085018382808284375f9201919091525050509082525063ffffffff87166020820152604001826107b057876001600160a01b031663cb2ef6f76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610787573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ab9190612610565b6107d2565b7f50524943455f464545443a3a45585445524e414c0000000000000000000000005b81526020018261084157876001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa158015610818573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612610565b610843565b5f5b90526001600160a01b0387165f9081526008602052604090208151819061086a90826126a5565b50602082015160018201805463ffffffff191663ffffffff9092169190911790556040808301516002830155606090920151600390910155516001600160a01b038716907f18c05f0511be33b4b3636da69473fd2c8b44012e11eabd7729a54611b6085932906108df9088908890889061278d565b60405180910390a2505050505050565b60606108fb600461140a565b905090565b6001600160a01b0382165f908152600660205260408120610921908361134e565b90505b92915050565b60606108fb600261140a565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461098157604051630cc479f960e01b815233600482015260240161044d565b8161098b8161141d565b6109955f8361134e565b6109bd57604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0383165f9081526006602052604090206109de9083611460565b610a27576040517f3e64cd550000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260440161044d565b6001600160a01b038084165f908152600760209081526040808320938616835292905220429055610a59600284611460565b50816001600160a01b0316836001600160a01b03167f73720c9031fae32be0c4f58f3b3934399946cfa29d2b13019ead89d904aa950e60405160405180910390a3505050565b60606108fb5f61140a565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610af557604051630cc479f960e01b815233600482015260240161044d565b610aff5f8261134e565b610b2757604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0382165f908152600660205260409020610b489082611536565b610b7857604051638896d35b60e01b81526001600160a01b0380841660048301528216602482015260440161044d565b6001600160a01b038083165f81815260076020908152604080832094861680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610c1757604051630cc479f960e01b815233600482015260240161044d565b805f5b81811015610e1c57610c5e848483818110610c3757610c376125fc565b9050602002810190610c4991906127b5565b610c57906020810190612535565b5f9061134e565b610cb957838382818110610c7457610c746125fc565b9050602002810190610c8691906127b5565b610c94906020810190612535565b604051630aab1bd160e01b81526001600160a01b03909116600482015260240161044d565b5f848483818110610ccc57610ccc6125fc565b9050602002810190610cde91906127b5565b610cec9060208101906127d3565b610cf591612816565b90507f8eafe75a000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610d66576040517f242ccae90000000000000000000000000000000000000000000000000000000081526001600160e01b03198216600482015260240161044d565b610e12858584818110610d7b57610d7b6125fc565b9050602002810190610d8d91906127b5565b610d9b9060208101906127d3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150869050818110610de357610de36125fc565b9050602002810190610df591906127b5565b610e03906020810190612535565b6001600160a01b03169061154a565b5050600101610c1a565b50505050565b805f5b81811015610e1c57610e6a848483818110610e4257610e426125fc565b9050602002810190610e5491906127b5565b610e62906020810190612535565b60049061134e565b610ede57838382818110610e8057610e806125fc565b9050602002810190610e9291906127b5565b610ea0906020810190612535565b6040517f253c07ed0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161044d565b838382818110610ef057610ef06125fc565b9050602002810190610f0291906127b5565b610f10906020810190612535565b6001600160a01b0316638736ec47858584818110610f3057610f306125fc565b9050602002810190610f4291906127b5565b610f509060208101906127d3565b6040518363ffffffff1660e01b8152600401610f6d929190612844565b5f604051808303815f87803b158015610f84575f80fd5b505af1158015610f96573d5f803e3d5ffd5b50505050806001019050610e25565b6001600160a01b0381165f9081526006602052604090206060906109249061140a565b6040805160808101825260608082525f602083018190529282018390528101919091526001600160a01b0382165f908152600860205260409081902081516080810190925280548290829061101c90612627565b80601f016020809104026020016040519081016040528092919081815260200182805461104890612627565b80156110935780601f1061106a57610100808354040283529160200191611093565b820191905f5260205f20905b81548152906001019060200180831161107657829003601f168201915b5050509183525050600182015463ffffffff1660208201526002820154604082015260039091015460609091015292915050565b6001600160a01b0382165f9081526006602052604081206110e8908361134e565b61111857604051638896d35b60e01b81526001600160a01b0380851660048301528316602482015260440161044d565b506001600160a01b039182165f90815260076020908152604080832093909416825291909152205490565b5f610924818361134e565b5f611159818361134e565b61118157604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b506001600160a01b03165f9081526008602052604090206001015463ffffffff1690565b5f61092460028361134e565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111fc57604051630cc479f960e01b815233600482015260240161044d565b6112065f82611536565b61122e57604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0381165f908152600860205260408120906112508282612251565b5060018101805463ffffffff191690555f600280830182905560039092018190559061127b9061158d565b90505f5b81811015611316575f611293600283611596565b6001600160a01b0381165f9081526006602052604090209091506112b79085611536565b1561130d576001600160a01b038082165f81815260076020908152604080832094891680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35b5060010161127f565b506040516001600160a01b038316907f0d34c8e3483f57054e96fce18e1b58c102738aca38e401f22ce095d181dc7dba905f90a25050565b6001600160a01b0381165f9081526001830160205260408120541515610921565b5f61137a83836115a1565b9050826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa9250505080156113d6575060408051601f3d908101601f191682019092526113d391810190612870565b60015b6113f3576040516367a7cd4360e01b815260040160405180910390fd5b6113ff84838989611768565b505050505092915050565b60605f61141683611808565b9392505050565b6001600160a01b03811661145d576040517fb2335f2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f610921836001600160a01b038416611861565b5f61147e826118ad565b1561148b57506001919050565b61149482611a5d565b80156114a657506114a6600483611460565b156114df576040516001600160a01b038316907f126881a22591f9c505916524e1e627cee9847bd538cc439d374e156d965fd5ec905f90a25b5f6114f2836001600160a01b0316611ac8565b80519091505f5b8181101561152c57611523838281518110611516576115166125fc565b6020026020010151611474565b506001016114f9565b505f949350505050565b5f610921836001600160a01b038416611b2a565b606061092183835f6040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c65640000815250611c0d565b5f610924825490565b5f6109218383611cfd565b5f6001600160a01b0383163b6115ee576040517fdf4c572d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611648575060408051601f3d908101601f19168201909252611645918101906128bc565b60015b611665576040516367a7cd4360e01b815260040160405180910390fd5b8060ff16600814611689576040516367a7cd4360e01b815260040160405180910390fd5b506040805160048152602481019091526020810180516001600160e01b03167fd62ada11000000000000000000000000000000000000000000000000000000001790525f9081906116de908690612710611d23565b9150915081156116ff57808060200190518101906116fc91906128dc565b92505b828015611711575063ffffffff841615155b80611729575082158015611729575063ffffffff8416155b15611760576040517f47fbaa9700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b5f84128061177d57508015801561177d575083155b156117b4576040517f53b798e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156117d157506117cd63ffffffff83168461290f565b4210155b15610e1c576040517f16dd0ffb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060815f0180548060200260200160405190810160405280929190818152602001828054801561185557602002820191905f5260205f20905b815481526020019060010190808311611841575b50505050509050919050565b5f8181526001830160205260408120546118a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610924565b505f610924565b6040517f55b8fcfd0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906355b8fcfd90602401602060405180830381865afa15801561192e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195291906128dc565b61195e57506001919050565b816001600160a01b03166379ba50976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611996575f80fd5b505af19250505080156119a7575060015b50816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611a02575060408051601f3d908101601f191682019092526119ff91810190612922565b60015b15611a56576001600160a01b0381163014611a54576040517fa717374f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b505b505f919050565b5f816001600160a01b031663e75aeec86040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611ab8575060408051601f3d908101601f19168201909252611ab5918101906128dc565b60015b61092457505f919050565b919050565b60605f611ad483611da4565b90506001816002811115611aea57611aea61293d565b03611aff57611af883611e6d565b9150611b24565b6002816002811115611b1357611b1361293d565b03611b2457611b2183611f26565b91505b50919050565b5f8181526001830160205260408120548015611c04575f611b4c600183612951565b85549091505f90611b5f90600190612951565b9050818114611bbe575f865f018281548110611b7d57611b7d6125fc565b905f5260205f200154905080875f018481548110611b9d57611b9d6125fc565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611bcf57611bcf612964565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610924565b5f915050610924565b606082471015611c855760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161044d565b5f80866001600160a01b03168587604051611ca09190612978565b5f6040518083038185875af1925050503d805f8114611cda576040519150601f19603f3d011682016040523d82523d5f602084013e611cdf565b606091505b5091509150611cf087838387611fd4565b925050505b949350505050565b5f825f018281548110611d1257611d126125fc565b905f5260205f200154905092915050565b5f60605f80866001600160a01b03168587604051611d419190612978565b5f604051808303818686fa925050503d805f8114611d7a576040519150601f19603f3d011682016040523d82523d5f602084013e611d7f565b606091505b50915091505f815111611d93575f81611d96565b81815b935093505050935093915050565b6040805160048152602481019091526020810180516001600160e01b03167f741bef1a000000000000000000000000000000000000000000000000000000001790525f908190611e01906001600160a01b03851690612710611d23565b5090508015611e135750600192915050565b6040805160048152602481019091526020810180516001600160e01b031663385aee1b60e01b179052611e53906001600160a01b03851690612710611d23565b5090508015611e655750600292915050565b505f92915050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ecb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eef9190612922565b815f81518110611f0157611f016125fc565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b604080516008808252610120820190925260609160208201610100803683370190505090505f5b6008811015611b2457611f60838261204c565b828281518110611f7257611f726125fc565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6001600160a01b0316828281518110611fae57611fae6125fc565b60200260200101516001600160a01b031603611fcc57808252611b24565b600101611f4d565b606083156120425782515f0361203b576001600160a01b0385163b61203b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161044d565b5081611cf5565b611cf58383612227565b5f80825f03612063575063385aee1b60e01b6121a8565b8260010361209257507fab0ca0e1000000000000000000000000000000000000000000000000000000006121a8565b826002036120c157507fe5693f41000000000000000000000000000000000000000000000000000000006121a8565b826003036120f057507f427cb6fe000000000000000000000000000000000000000000000000000000006121a8565b8260040361211f57507f7aac1c48000000000000000000000000000000000000000000000000000000006121a8565b8260050361214e57507f7ee446ad000000000000000000000000000000000000000000000000000000006121a8565b8260060361217d57507fe5d9e964000000000000000000000000000000000000000000000000000000006121a8565b826007036121a857507f5d776bd7000000000000000000000000000000000000000000000000000000005b6040516001600160e01b0319821660208201525f9081906121e99060240160408051601f198184030181529190526001600160a01b03881690612710611d23565b915091508115806121f957508051155b15612209575f9350505050610924565b8080602001905181019061221d9190612922565b9695505050505050565b8151156122375781518083602001fd5b8060405162461bcd60e51b815260040161044d9190612989565b50805461225d90612627565b5f825580601f1061226c575050565b601f0160209004905f5260205f209081019061145d91905b80821115612297575f8155600101612284565b5090565b6001600160a01b038116811461145d575f80fd5b803563ffffffff81168114611ac3575f80fd5b5f80604083850312156122d3575f80fd5b82356122de8161229b565b91506122ec602084016122af565b90509250929050565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561239457888303603f19018552815180516001600160a01b039081168552908801518885018890528051888601819052908901915f9160608701905b8084101561237e57845183168252938b019360019390930192908b019061235c565b50978a019795505050918701915060010161231c565b509098975050505050505050565b5f805f80606085870312156123b5575f80fd5b84356123c08161229b565b93506123ce602086016122af565b9250604085013567ffffffffffffffff808211156123ea575f80fd5b818701915087601f8301126123fd575f80fd5b81358181111561240b575f80fd5b88602082850101111561241c575f80fd5b95989497505060200194505050565b602080825282518282018190525f9190848201906040850190845b8181101561246b5783516001600160a01b031683529284019291840191600101612446565b50909695505050505050565b5f8060408385031215612488575f80fd5b82356124938161229b565b915060208301356124a38161229b565b809150509250929050565b5f8083601f8401126124be575f80fd5b50813567ffffffffffffffff8111156124d5575f80fd5b6020830191508360208260051b85010111156124ef575f80fd5b9250929050565b5f8060208385031215612507575f80fd5b823567ffffffffffffffff81111561251d575f80fd5b612529858286016124ae565b90969095509350505050565b5f60208284031215612545575f80fd5b81356114168161229b565b5f5b8381101561256a578181015183820152602001612552565b50505f910152565b5f8151808452612589816020860160208601612550565b601f01601f19169290920160200192915050565b602081525f8251608060208401526125b860a0840182612572565b905063ffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612620575f80fd5b5051919050565b600181811c9082168061263b57607f821691505b602082108103611b2457634e487b7160e01b5f52602260045260245ffd5b601f8211156126a057805f5260205f20601f840160051c8101602085101561267e5750805b601f840160051c820191505b8181101561269d575f815560010161268a565b50505b505050565b815167ffffffffffffffff8111156126bf576126bf6125e8565b6126d3816126cd8454612627565b84612659565b602080601f831160018114612706575f84156126ef5750858301515b5f19600386901b1c1916600185901b17855561275d565b5f85815260208120601f198616915b8281101561273457888601518255948401946001909101908401612715565b508582101561275157878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b63ffffffff84168152604060208201525f6127ac604083018486612765565b95945050505050565b5f8235603e198336030181126127c9575f80fd5b9190910192915050565b5f808335601e198436030181126127e8575f80fd5b83018035915067ffffffffffffffff821115612802575f80fd5b6020019150368190038213156124ef575f80fd5b6001600160e01b031981358181169160048510156117605760049490940360031b84901b1690921692915050565b602081525f611cf5602083018486612765565b805169ffffffffffffffffffff81168114611ac3575f80fd5b5f805f805f60a08688031215612884575f80fd5b61288d86612857565b94506020860151935060408601519250606086015191506128b060808701612857565b90509295509295909350565b5f602082840312156128cc575f80fd5b815160ff81168114611416575f80fd5b5f602082840312156128ec575f80fd5b81518015158114611416575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610924576109246128fb565b5f60208284031215612932575f80fd5b81516114168161229b565b634e487b7160e01b5f52602160045260245ffd5b81810381811115610924576109246128fb565b634e487b7160e01b5f52603160045260245ffd5b5f82516127c9818460208701612550565b602081525f610921602083018461257256fea164736f6c6343000817000a000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061018f575f3560e01c806371346b42116100dd578063ba0f681e11610088578063ce37a61e11610063578063ce37a61e146103b8578063e647e5cc146103e0578063fceb0024146103f3575f80fd5b8063ba0f681e1461036b578063c7a22fe01461037e578063cb2ef6f714610391575f80fd5b806389e402f1116100b857806389e402f1146102fd5780638da5cb5b14610324578063a9ff6c0f1461034b575f80fd5b806371346b42146102c45780637199e2c9146102d75780637cc6ce52146102ea575f80fd5b80634026ebd61161013d57806360e93cfd1161011857806360e93cfd14610282578063614eb733146102a95780636f898fb4146102b1575f80fd5b80634026ebd61461025057806345af83961461025857806354fd4d501461026b575f80fd5b806326ec6c301161016d57806326ec6c30146101d95780632954018c146101ee57806330d1f9fe1461022d575f80fd5b8063149d919114610193578063161455ad146101a857806316f5a392146101c6575b5f80fd5b6101a66101a13660046122c2565b610406565b005b6101b0610526565b6040516101bd91906122f5565b60405180910390f35b6101a66101d43660046123a2565b610642565b6101e16108ef565b6040516101bd919061242b565b6102157f000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e3881565b6040516001600160a01b0390911681526020016101bd565b61024061023b366004612477565b610900565b60405190151581526020016101bd565b6101e161092a565b6101a6610266366004612477565b610936565b61027461013681565b6040519081526020016101bd565b6102157f0000000000000000000000001ce2b1be96a082b1b1539f80d5d8f82ec06a0f9a81565b6101e1610a9f565b6101a66102bf366004612477565b610aaa565b6101a66102d23660046124f6565b610bcc565b6101a66102e53660046124f6565b610e22565b6101e16102f8366004612535565b610fa5565b6102157f0000000000000000000000005dc92ec92cd5423ef95aebe8ac96619bcc9ffc8881565b6102157f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a81565b61035e610359366004612535565b610fc8565b6040516101bd919061259d565b610274610379366004612477565b6110c7565b61024061038c366004612535565b611143565b6102747f50524943455f464545445f53544f52450000000000000000000000000000000081565b6103cb6103c6366004612535565b61114e565b60405163ffffffff90911681526020016101bd565b6102406103ee366004612535565b6111a5565b6101a6610401366004612535565b6111b1565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461045657604051630cc479f960e01b81523360048201526024015b60405180910390fd5b6104605f8361134e565b61048857604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0382165f9081526008602052604090206001015463ffffffff828116911614610522576104bc828261136f565b506001600160a01b0382165f81815260086020908152604091829020600101805463ffffffff191663ffffffff861690811790915591519182527fd85fed169ea0f7852e9ec4becfec49eca5fe689c65e95486e97ab1f4f02623b9910160405180910390a25b5050565b60605f610533600261140a565b80519091508067ffffffffffffffff811115610551576105516125e8565b60405190808252806020026020018201604052801561059657816020015b604080518082019091525f81526060602082015281526020019060019003908161056f5790505b5092505f5b8181101561063c578281815181106105b5576105b56125fc565b60200260200101518482815181106105cf576105cf6125fc565b60200260200101515f01906001600160a01b031690816001600160a01b031681525050610614838281518110610607576106076125fc565b6020026020010151610fa5565b848281518110610626576106266125fc565b602090810291909101810151015260010161059b565b50505090565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461068d57604051630cc479f960e01b815233600482015260240161044d565b836106978161141d565b6106a15f86611460565b6106e2576040517f8d8d2d3a0000000000000000000000000000000000000000000000000000000081526001600160a01b038616600482015260240161044d565b6106ec858561136f565b505f6106f786611474565b6040805160a06020601f880181900402820181019092526080810186815292935091829187908790819085018382808284375f9201919091525050509082525063ffffffff87166020820152604001826107b057876001600160a01b031663cb2ef6f76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610787573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107ab9190612610565b6107d2565b7f50524943455f464545443a3a45585445524e414c0000000000000000000000005b81526020018261084157876001600160a01b03166354fd4d506040518163ffffffff1660e01b8152600401602060405180830381865afa158015610818573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061083c9190612610565b610843565b5f5b90526001600160a01b0387165f9081526008602052604090208151819061086a90826126a5565b50602082015160018201805463ffffffff191663ffffffff9092169190911790556040808301516002830155606090920151600390910155516001600160a01b038716907f18c05f0511be33b4b3636da69473fd2c8b44012e11eabd7729a54611b6085932906108df9088908890889061278d565b60405180910390a2505050505050565b60606108fb600461140a565b905090565b6001600160a01b0382165f908152600660205260408120610921908361134e565b90505b92915050565b60606108fb600261140a565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a161461098157604051630cc479f960e01b815233600482015260240161044d565b8161098b8161141d565b6109955f8361134e565b6109bd57604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b6001600160a01b0383165f9081526006602052604090206109de9083611460565b610a27576040517f3e64cd550000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260440161044d565b6001600160a01b038084165f908152600760209081526040808320938616835292905220429055610a59600284611460565b50816001600160a01b0316836001600160a01b03167f73720c9031fae32be0c4f58f3b3934399946cfa29d2b13019ead89d904aa950e60405160405180910390a3505050565b60606108fb5f61140a565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a1614610af557604051630cc479f960e01b815233600482015260240161044d565b610aff5f8261134e565b610b2757604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0382165f908152600660205260409020610b489082611536565b610b7857604051638896d35b60e01b81526001600160a01b0380841660048301528216602482015260440161044d565b6001600160a01b038083165f81815260076020908152604080832094861680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35050565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a1614610c1757604051630cc479f960e01b815233600482015260240161044d565b805f5b81811015610e1c57610c5e848483818110610c3757610c376125fc565b9050602002810190610c4991906127b5565b610c57906020810190612535565b5f9061134e565b610cb957838382818110610c7457610c746125fc565b9050602002810190610c8691906127b5565b610c94906020810190612535565b604051630aab1bd160e01b81526001600160a01b03909116600482015260240161044d565b5f848483818110610ccc57610ccc6125fc565b9050602002810190610cde91906127b5565b610cec9060208101906127d3565b610cf591612816565b90507f8eafe75a000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610d66576040517f242ccae90000000000000000000000000000000000000000000000000000000081526001600160e01b03198216600482015260240161044d565b610e12858584818110610d7b57610d7b6125fc565b9050602002810190610d8d91906127b5565b610d9b9060208101906127d3565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250899250889150869050818110610de357610de36125fc565b9050602002810190610df591906127b5565b610e03906020810190612535565b6001600160a01b03169061154a565b5050600101610c1a565b50505050565b805f5b81811015610e1c57610e6a848483818110610e4257610e426125fc565b9050602002810190610e5491906127b5565b610e62906020810190612535565b60049061134e565b610ede57838382818110610e8057610e806125fc565b9050602002810190610e9291906127b5565b610ea0906020810190612535565b6040517f253c07ed0000000000000000000000000000000000000000000000000000000081526001600160a01b03909116600482015260240161044d565b838382818110610ef057610ef06125fc565b9050602002810190610f0291906127b5565b610f10906020810190612535565b6001600160a01b0316638736ec47858584818110610f3057610f306125fc565b9050602002810190610f4291906127b5565b610f509060208101906127d3565b6040518363ffffffff1660e01b8152600401610f6d929190612844565b5f604051808303815f87803b158015610f84575f80fd5b505af1158015610f96573d5f803e3d5ffd5b50505050806001019050610e25565b6001600160a01b0381165f9081526006602052604090206060906109249061140a565b6040805160808101825260608082525f602083018190529282018390528101919091526001600160a01b0382165f908152600860205260409081902081516080810190925280548290829061101c90612627565b80601f016020809104026020016040519081016040528092919081815260200182805461104890612627565b80156110935780601f1061106a57610100808354040283529160200191611093565b820191905f5260205f20905b81548152906001019060200180831161107657829003601f168201915b5050509183525050600182015463ffffffff1660208201526002820154604082015260039091015460609091015292915050565b6001600160a01b0382165f9081526006602052604081206110e8908361134e565b61111857604051638896d35b60e01b81526001600160a01b0380851660048301528316602482015260440161044d565b506001600160a01b039182165f90815260076020908152604080832093909416825291909152205490565b5f610924818361134e565b5f611159818361134e565b61118157604051630aab1bd160e01b81526001600160a01b038316600482015260240161044d565b506001600160a01b03165f9081526008602052604090206001015463ffffffff1690565b5f61092460028361134e565b336001600160a01b037f000000000000000000000000bcd875f0d62b9aa22481c81975f9ae1753fc559a16146111fc57604051630cc479f960e01b815233600482015260240161044d565b6112065f82611536565b61122e57604051630aab1bd160e01b81526001600160a01b038216600482015260240161044d565b6001600160a01b0381165f908152600860205260408120906112508282612251565b5060018101805463ffffffff191690555f600280830182905560039092018190559061127b9061158d565b90505f5b81811015611316575f611293600283611596565b6001600160a01b0381165f9081526006602052604090209091506112b79085611536565b1561130d576001600160a01b038082165f81815260076020908152604080832094891680845294909152808220829055517f6b88c312dae29edcabbccaba2c987c9b3f61480dd7ed03bd7f932f717dccc54f9190a35b5060010161127f565b506040516001600160a01b038316907f0d34c8e3483f57054e96fce18e1b58c102738aca38e401f22ce095d181dc7dba905f90a25050565b6001600160a01b0381165f9081526001830160205260408120541515610921565b5f61137a83836115a1565b9050826001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa9250505080156113d6575060408051601f3d908101601f191682019092526113d391810190612870565b60015b6113f3576040516367a7cd4360e01b815260040160405180910390fd5b6113ff84838989611768565b505050505092915050565b60605f61141683611808565b9392505050565b6001600160a01b03811661145d576040517fb2335f2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b5f610921836001600160a01b038416611861565b5f61147e826118ad565b1561148b57506001919050565b61149482611a5d565b80156114a657506114a6600483611460565b156114df576040516001600160a01b038316907f126881a22591f9c505916524e1e627cee9847bd538cc439d374e156d965fd5ec905f90a25b5f6114f2836001600160a01b0316611ac8565b80519091505f5b8181101561152c57611523838281518110611516576115166125fc565b6020026020010151611474565b506001016114f9565b505f949350505050565b5f610921836001600160a01b038416611b2a565b606061092183835f6040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c65640000815250611c0d565b5f610924825490565b5f6109218383611cfd565b5f6001600160a01b0383163b6115ee576040517fdf4c572d0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611648575060408051601f3d908101601f19168201909252611645918101906128bc565b60015b611665576040516367a7cd4360e01b815260040160405180910390fd5b8060ff16600814611689576040516367a7cd4360e01b815260040160405180910390fd5b506040805160048152602481019091526020810180516001600160e01b03167fd62ada11000000000000000000000000000000000000000000000000000000001790525f9081906116de908690612710611d23565b9150915081156116ff57808060200190518101906116fc91906128dc565b92505b828015611711575063ffffffff841615155b80611729575082158015611729575063ffffffff8416155b15611760576040517f47fbaa9700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b5f84128061177d57508015801561177d575083155b156117b4576040517f53b798e200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b801580156117d157506117cd63ffffffff83168461290f565b4210155b15610e1c576040517f16dd0ffb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060815f0180548060200260200160405190810160405280929190818152602001828054801561185557602002820191905f5260205f20905b815481526020019060010190808311611841575b50505050509050919050565b5f8181526001830160205260408120546118a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610924565b505f610924565b6040517f55b8fcfd0000000000000000000000000000000000000000000000000000000081526001600160a01b0382811660048301525f917f0000000000000000000000001ce2b1be96a082b1b1539f80d5d8f82ec06a0f9a909116906355b8fcfd90602401602060405180830381865afa15801561192e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061195291906128dc565b61195e57506001919050565b816001600160a01b03166379ba50976040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611996575f80fd5b505af19250505080156119a7575060015b50816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611a02575060408051601f3d908101601f191682019092526119ff91810190612922565b60015b15611a56576001600160a01b0381163014611a54576040517fa717374f0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015260240161044d565b505b505f919050565b5f816001600160a01b031663e75aeec86040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611ab8575060408051601f3d908101601f19168201909252611ab5918101906128dc565b60015b61092457505f919050565b919050565b60605f611ad483611da4565b90506001816002811115611aea57611aea61293d565b03611aff57611af883611e6d565b9150611b24565b6002816002811115611b1357611b1361293d565b03611b2457611b2183611f26565b91505b50919050565b5f8181526001830160205260408120548015611c04575f611b4c600183612951565b85549091505f90611b5f90600190612951565b9050818114611bbe575f865f018281548110611b7d57611b7d6125fc565b905f5260205f200154905080875f018481548110611b9d57611b9d6125fc565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611bcf57611bcf612964565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610924565b5f915050610924565b606082471015611c855760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161044d565b5f80866001600160a01b03168587604051611ca09190612978565b5f6040518083038185875af1925050503d805f8114611cda576040519150601f19603f3d011682016040523d82523d5f602084013e611cdf565b606091505b5091509150611cf087838387611fd4565b925050505b949350505050565b5f825f018281548110611d1257611d126125fc565b905f5260205f200154905092915050565b5f60605f80866001600160a01b03168587604051611d419190612978565b5f604051808303818686fa925050503d805f8114611d7a576040519150601f19603f3d011682016040523d82523d5f602084013e611d7f565b606091505b50915091505f815111611d93575f81611d96565b81815b935093505050935093915050565b6040805160048152602481019091526020810180516001600160e01b03167f741bef1a000000000000000000000000000000000000000000000000000000001790525f908190611e01906001600160a01b03851690612710611d23565b5090508015611e135750600192915050565b6040805160048152602481019091526020810180516001600160e01b031663385aee1b60e01b179052611e53906001600160a01b03851690612710611d23565b5090508015611e655750600292915050565b505f92915050565b60408051600180825281830190925260609160208083019080368337019050509050816001600160a01b031663741bef1a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ecb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611eef9190612922565b815f81518110611f0157611f016125fc565b60200260200101906001600160a01b031690816001600160a01b031681525050919050565b604080516008808252610120820190925260609160208201610100803683370190505090505f5b6008811015611b2457611f60838261204c565b828281518110611f7257611f726125fc565b60200260200101906001600160a01b031690816001600160a01b0316815250505f6001600160a01b0316828281518110611fae57611fae6125fc565b60200260200101516001600160a01b031603611fcc57808252611b24565b600101611f4d565b606083156120425782515f0361203b576001600160a01b0385163b61203b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161044d565b5081611cf5565b611cf58383612227565b5f80825f03612063575063385aee1b60e01b6121a8565b8260010361209257507fab0ca0e1000000000000000000000000000000000000000000000000000000006121a8565b826002036120c157507fe5693f41000000000000000000000000000000000000000000000000000000006121a8565b826003036120f057507f427cb6fe000000000000000000000000000000000000000000000000000000006121a8565b8260040361211f57507f7aac1c48000000000000000000000000000000000000000000000000000000006121a8565b8260050361214e57507f7ee446ad000000000000000000000000000000000000000000000000000000006121a8565b8260060361217d57507fe5d9e964000000000000000000000000000000000000000000000000000000006121a8565b826007036121a857507f5d776bd7000000000000000000000000000000000000000000000000000000005b6040516001600160e01b0319821660208201525f9081906121e99060240160408051601f198184030181529190526001600160a01b03881690612710611d23565b915091508115806121f957508051155b15612209575f9350505050610924565b8080602001905181019061221d9190612922565b9695505050505050565b8151156122375781518083602001fd5b8060405162461bcd60e51b815260040161044d9190612989565b50805461225d90612627565b5f825580601f1061226c575050565b601f0160209004905f5260205f209081019061145d91905b80821115612297575f8155600101612284565b5090565b6001600160a01b038116811461145d575f80fd5b803563ffffffff81168114611ac3575f80fd5b5f80604083850312156122d3575f80fd5b82356122de8161229b565b91506122ec602084016122af565b90509250929050565b5f60208083018184528085518083526040925060408601915060408160051b8701018488015f5b8381101561239457888303603f19018552815180516001600160a01b039081168552908801518885018890528051888601819052908901915f9160608701905b8084101561237e57845183168252938b019360019390930192908b019061235c565b50978a019795505050918701915060010161231c565b509098975050505050505050565b5f805f80606085870312156123b5575f80fd5b84356123c08161229b565b93506123ce602086016122af565b9250604085013567ffffffffffffffff808211156123ea575f80fd5b818701915087601f8301126123fd575f80fd5b81358181111561240b575f80fd5b88602082850101111561241c575f80fd5b95989497505060200194505050565b602080825282518282018190525f9190848201906040850190845b8181101561246b5783516001600160a01b031683529284019291840191600101612446565b50909695505050505050565b5f8060408385031215612488575f80fd5b82356124938161229b565b915060208301356124a38161229b565b809150509250929050565b5f8083601f8401126124be575f80fd5b50813567ffffffffffffffff8111156124d5575f80fd5b6020830191508360208260051b85010111156124ef575f80fd5b9250929050565b5f8060208385031215612507575f80fd5b823567ffffffffffffffff81111561251d575f80fd5b612529858286016124ae565b90969095509350505050565b5f60208284031215612545575f80fd5b81356114168161229b565b5f5b8381101561256a578181015183820152602001612552565b50505f910152565b5f8151808452612589816020860160208601612550565b601f01601f19169290920160200192915050565b602081525f8251608060208401526125b860a0840182612572565b905063ffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215612620575f80fd5b5051919050565b600181811c9082168061263b57607f821691505b602082108103611b2457634e487b7160e01b5f52602260045260245ffd5b601f8211156126a057805f5260205f20601f840160051c8101602085101561267e5750805b601f840160051c820191505b8181101561269d575f815560010161268a565b50505b505050565b815167ffffffffffffffff8111156126bf576126bf6125e8565b6126d3816126cd8454612627565b84612659565b602080601f831160018114612706575f84156126ef5750858301515b5f19600386901b1c1916600185901b17855561275d565b5f85815260208120601f198616915b8281101561273457888601518255948401946001909101908401612715565b508582101561275157878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b63ffffffff84168152604060208201525f6127ac604083018486612765565b95945050505050565b5f8235603e198336030181126127c9575f80fd5b9190910192915050565b5f808335601e198436030181126127e8575f80fd5b83018035915067ffffffffffffffff821115612802575f80fd5b6020019150368190038213156124ef575f80fd5b6001600160e01b031981358181169160048510156117605760049490940360031b84901b1690921692915050565b602081525f611cf5602083018486612765565b805169ffffffffffffffffffff81168114611ac3575f80fd5b5f805f805f60a08688031215612884575f80fd5b61288d86612857565b94506020860151935060408601519250606086015191506128b060808701612857565b90509295509295909350565b5f602082840312156128cc575f80fd5b815160ff81168114611416575f80fd5b5f602082840312156128ec575f80fd5b81518015158114611416575f80fd5b634e487b7160e01b5f52601160045260245ffd5b80820180821115610924576109246128fb565b5f60208284031215612932575f80fd5b81516114168161229b565b634e487b7160e01b5f52602160045260245ffd5b81810381811115610924576109246128fb565b634e487b7160e01b5f52603160045260245ffd5b5f82516127c9818460208701612550565b602081525f610921602083018461257256fea164736f6c6343000817000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38
-----Decoded View---------------
Arg [0] : addressProvider_ (address): 0xF7f0a609BfAb9a0A98786951ef10e5FE26cC1E38
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000f7f0a609bfab9a0a98786951ef10e5fe26cc1e38
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.