Contract Name:
RateKeeperFactory
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IRateKeeper} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IRateKeeper.sol";
import {IGaugeV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IGaugeV3.sol";
import {IFactory} from "../interfaces/factories/IFactory.sol";
import {IMarketFactory} from "../interfaces/factories/IMarketFactory.sol";
import {IRateKeeperFactory} from "../interfaces/factories/IRateKeeperFactory.sol";
import {Call, DeployParams, DeployResult} from "../interfaces/Types.sol";
import {CallBuilder} from "../libraries/CallBuilder.sol";
import {
DOMAIN_RATE_KEEPER,
AP_RATE_KEEPER_FACTORY,
NO_VERSION_CONTROL,
AP_GEAR_STAKING
} from "../libraries/ContractLiterals.sol";
import {AbstractFactory} from "./AbstractFactory.sol";
import {AbstractMarketFactory} from "./AbstractMarketFactory.sol";
contract RateKeeperFactory is AbstractMarketFactory, IRateKeeperFactory {
using CallBuilder for Call[];
uint256 public constant override version = 3_10;
bytes32 public constant override contractType = AP_RATE_KEEPER_FACTORY;
address public immutable gearStaking;
constructor(address addressProvider_) AbstractFactory(addressProvider_) {
gearStaking = _getAddressOrRevert(AP_GEAR_STAKING, NO_VERSION_CONTROL);
}
// ---------- //
// DEPLOYMENT //
// ---------- //
function deployRateKeeper(address pool, DeployParams calldata params)
external
override
onlyMarketConfigurators
returns (DeployResult memory)
{
if (params.postfix == "GAUGE") {
(address decodedPool, address decodedGearStaking) = abi.decode(params.constructorParams, (address, address));
if (decodedPool != pool || decodedGearStaking != gearStaking) revert InvalidConstructorParamsException();
} else if (params.postfix == "TUMBLER") {
(address decodedPool,) = abi.decode(params.constructorParams, (address, uint256));
if (decodedPool != pool) revert InvalidConstructorParamsException();
} else {
_validateDefaultConstructorParams(pool, params.constructorParams);
}
address rateKeeper = _deployLatestPatch({
contractType: _getContractType(DOMAIN_RATE_KEEPER, params.postfix),
minorVersion: version,
constructorParams: params.constructorParams,
salt: keccak256(abi.encode(params.salt, msg.sender))
});
return DeployResult({
newContract: rateKeeper,
onInstallOps: CallBuilder.build(_authorizeFactory(msg.sender, pool, rateKeeper))
});
}
// ------------ //
// MARKET HOOKS //
// ------------ //
function onCreateMarket(address, address, address, address rateKeeper, address, address)
external
view
override(AbstractMarketFactory, IMarketFactory)
returns (Call[] memory)
{
if (_getRateKeeperType(rateKeeper) == "RATE_KEEPER::GAUGE") {
return CallBuilder.build(_setFrozenEpoch(rateKeeper, false));
}
return CallBuilder.build();
}
function onShutdownMarket(address pool)
external
view
override(AbstractMarketFactory, IMarketFactory)
returns (Call[] memory)
{
address rateKeeper = _rateKeeper(_quotaKeeper(pool));
if (_getRateKeeperType(rateKeeper) == "RATE_KEEPER::GAUGE") {
return CallBuilder.build(_setFrozenEpoch(rateKeeper, true));
}
return CallBuilder.build();
}
function onUpdateRateKeeper(address pool, address newRateKeeper, address oldRateKeeper)
external
view
override(AbstractMarketFactory, IMarketFactory)
returns (Call[] memory calls)
{
address[] memory tokens = _quotedTokens(_quotaKeeper(pool));
uint256 numTokens = tokens.length;
calls = new Call[](numTokens);
bytes32 type_ = _getRateKeeperType(newRateKeeper);
for (uint256 i; i < numTokens; ++i) {
calls[i] = _addToken(newRateKeeper, tokens[i], type_);
}
if (_getRateKeeperType(oldRateKeeper) == "RATE_KEEPER::GAUGE") {
calls = calls.append(_setFrozenEpoch(oldRateKeeper, true));
}
if (type_ == "RATE_KEEPER::GAUGE") {
calls = calls.append(_setFrozenEpoch(newRateKeeper, false));
}
calls = calls.append(_unauthorizeFactory(msg.sender, pool, oldRateKeeper));
}
function onAddToken(address pool, address token, address)
external
view
override(AbstractMarketFactory, IMarketFactory)
returns (Call[] memory)
{
address rateKeeper = _rateKeeper(_quotaKeeper(pool));
return CallBuilder.build(_addToken(rateKeeper, token, _getRateKeeperType(rateKeeper)));
}
// ------------- //
// CONFIGURATION //
// ------------- //
function configure(address pool, bytes calldata callData)
external
view
override(AbstractFactory, IFactory)
returns (Call[] memory)
{
address rateKeeper = _rateKeeper(_quotaKeeper(pool));
bytes4 selector = bytes4(callData);
if (_isForbiddenConfigurationCall(rateKeeper, selector)) revert ForbiddenConfigurationCallException(selector);
return CallBuilder.build(Call(rateKeeper, callData));
}
// --------- //
// INTERNALS //
// --------- //
function _getRateKeeperType(address rateKeeper) internal view returns (bytes32) {
try IRateKeeper(rateKeeper).contractType() returns (bytes32 type_) {
return type_;
} catch {
return "RATE_KEEPER::GAUGE";
}
}
function _isForbiddenConfigurationCall(address rateKeeper, bytes4 selector) internal view returns (bool) {
if (_getRateKeeperType(rateKeeper) == "RATE_KEEPER::GAUGE") {
return selector == IRateKeeper.addToken.selector || selector == IGaugeV3.addQuotaToken.selector
|| selector == IGaugeV3.setFrozenEpoch.selector || selector == bytes4(keccak256("setController(address)"));
}
return selector == IRateKeeper.addToken.selector;
}
function _addToken(address rateKeeper, address token, bytes32 type_) internal pure returns (Call memory) {
return Call(
rateKeeper,
type_ == "RATE_KEEPER::GAUGE"
? abi.encodeCall(IGaugeV3.addQuotaToken, (token, 1, 1))
: abi.encodeCall(IRateKeeper.addToken, token)
);
}
function _setFrozenEpoch(address gauge, bool status) internal pure returns (Call memory) {
return Call(gauge, abi.encodeCall(IGaugeV3.setFrozenEpoch, status));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {IStateSerializer} from "./IStateSerializer.sol";
import {IVersion} from "./IVersion.sol";
/// @title Rate keeper interface
/// @notice Generic interface for a contract that can provide rates to the quota keeper
/// @dev Bots must have type `RATE_KEEPER::{POSTFIX}`
interface IRateKeeper is IVersion, IStateSerializer {
/// @notice Pool rates are provided for
function pool() external view returns (address);
/// @notice Adds token to the rate keeper
/// @dev Must add token to the quota keeper in case it's not already there
function addToken(address token) external;
/// @notice Whether token is added to the rate keeper
function isTokenAdded(address token) external view returns (bool);
/// @notice Returns quota rates for a list of tokens, must return non-zero rates for added tokens
/// and revert if some tokens are not recognized
function getRates(address[] calldata tokens) external view returns (uint16[] memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {IACLTrait} from "./base/IACLTrait.sol";
import {IRateKeeper} from "./base/IRateKeeper.sol";
import {IVotingContract} from "./base/IVotingContract.sol";
struct QuotaRateParams {
uint16 minRate;
uint16 maxRate;
uint96 totalVotesLpSide;
uint96 totalVotesCaSide;
}
struct UserVotes {
uint96 votesLpSide;
uint96 votesCaSide;
}
interface IGaugeV3Events {
/// @notice Emitted when epoch is updated
event UpdateEpoch(uint16 epochNow);
/// @notice Emitted when a user submits a vote
event Vote(address indexed user, address indexed token, uint96 votes, bool lpSide);
/// @notice Emitted when a user removes a vote
event Unvote(address indexed user, address indexed token, uint96 votes, bool lpSide);
/// @notice Emitted when a new quota token is added in the PoolQuotaKeeper
event AddQuotaToken(address indexed token, uint16 minRate, uint16 maxRate);
/// @notice Emitted when quota interest rate parameters are changed
event SetQuotaTokenParams(address indexed token, uint16 minRate, uint16 maxRate);
/// @notice Emitted when the frozen epoch status changes
event SetFrozenEpoch(bool status);
}
/// @title Gauge V3 interface
interface IGaugeV3 is IVotingContract, IRateKeeper, IACLTrait, IGaugeV3Events {
function updateEpoch() external;
function epochLastUpdate() external view returns (uint16);
function getRates(address[] calldata tokens) external view override returns (uint16[] memory);
function vote(address user, uint96 votes, bytes calldata extraData) external override;
function unvote(address user, uint96 votes, bytes calldata extraData) external override;
function userTokenVotes(address user, address token)
external
view
returns (uint96 votesLpSide, uint96 votesCaSide);
function quotaRateParams(address token)
external
view
returns (uint16 minRate, uint16 maxRate, uint96 totalVotesLpSide, uint96 totalVotesCaSide);
// ------------- //
// CONFIGURATION //
// ------------- //
function epochFrozen() external view returns (bool);
function setFrozenEpoch(bool status) external;
function addQuotaToken(address token, uint16 minRate, uint16 maxRate) external;
function changeQuotaMinRate(address token, uint16 minRate) external;
function changeQuotaMaxRate(address token, uint16 maxRate) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IDeployerTrait} from "../base/IDeployerTrait.sol";
import {Call} from "../Types.sol";
interface IFactory is IVersion, IDeployerTrait {
// ------ //
// ERRORS //
// ------ //
error CallerIsNotMarketConfiguratorException(address caller);
error ForbiddenConfigurationCallException(bytes4 selector);
error ForbiddenEmergencyConfigurationCallException(bytes4 selector);
error InvalidConstructorParamsException();
// --------------- //
// STATE VARIABLES //
// --------------- //
function marketConfiguratorFactory() external view returns (address);
// ------------- //
// CONFIGURATION //
// ------------- //
function configure(address target, bytes calldata callData) external returns (Call[] memory calls);
function emergencyConfigure(address target, bytes calldata callData) external returns (Call[] memory calls);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {Call} from "../Types.sol";
import {IFactory} from "./IFactory.sol";
interface IMarketFactory is IFactory {
// ------------ //
// MARKET HOOKS //
// ------------ //
function onCreateMarket(
address pool,
address priceOracle,
address interestRateModel,
address rateKeeper,
address lossPolicy,
address underlyingPriceFeed
) external returns (Call[] memory calls);
function onShutdownMarket(address pool) external returns (Call[] memory calls);
function onCreateCreditSuite(address creditManager) external returns (Call[] memory calls);
function onShutdownCreditSuite(address creditManager) external returns (Call[] memory calls);
function onUpdatePriceOracle(address pool, address newPriceOracle, address oldPriceOracle)
external
returns (Call[] memory calls);
function onUpdateInterestRateModel(address pool, address newInterestRateModel, address oldInterestRateModel)
external
returns (Call[] memory calls);
function onUpdateRateKeeper(address pool, address newRateKeeper, address oldRateKeeper)
external
returns (Call[] memory calls);
function onUpdateLossPolicy(address pool, address newLossPolicy, address oldLossPolicy)
external
returns (Call[] memory calls);
function onAddToken(address pool, address token, address priceFeed) external returns (Call[] memory calls);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {DeployParams, DeployResult} from "../Types.sol";
import {IMarketFactory} from "./IMarketFactory.sol";
interface IRateKeeperFactory is IMarketFactory {
function deployRateKeeper(address pool, DeployParams calldata params) external returns (DeployResult memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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 initCodePointer;
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;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {Call} from "../interfaces/Types.sol";
library CallBuilder {
function build() internal pure returns (Call[] memory calls) {}
function build(Call memory call1) internal pure returns (Call[] memory calls) {
calls = new Call[](1);
calls[0] = call1;
}
function build(Call memory call1, Call memory call2) internal pure returns (Call[] memory calls) {
calls = new Call[](2);
calls[0] = call1;
calls[1] = call2;
}
function build(Call memory call1, Call memory call2, Call memory call3)
internal
pure
returns (Call[] memory calls)
{
calls = new Call[](3);
calls[0] = call1;
calls[1] = call2;
calls[2] = call3;
}
function build(Call memory call1, Call memory call2, Call memory call3, Call memory call4)
internal
pure
returns (Call[] memory calls)
{
calls = new Call[](4);
calls[0] = call1;
calls[1] = call2;
calls[2] = call3;
calls[3] = call4;
}
function build(Call memory call1, Call memory call2, Call memory call3, Call memory call4, Call memory call5)
internal
pure
returns (Call[] memory calls)
{
calls = new Call[](5);
calls[0] = call1;
calls[1] = call2;
calls[2] = call3;
calls[3] = call4;
calls[4] = call5;
}
function build(
Call memory call1,
Call memory call2,
Call memory call3,
Call memory call4,
Call memory call5,
Call memory call6
) internal pure returns (Call[] memory calls) {
calls = new Call[](6);
calls[0] = call1;
calls[1] = call2;
calls[2] = call3;
calls[3] = call4;
calls[4] = call5;
calls[5] = call6;
}
function append(Call[] memory calls, Call memory call) internal pure returns (Call[] memory newCalls) {
uint256 numCalls = calls.length;
newCalls = new Call[](numCalls + 1);
for (uint256 i; i < numCalls; ++i) {
newCalls[i] = calls[i];
}
newCalls[numCalls] = call;
}
function extend(Call[] memory calls1, Call[] memory calls2) internal pure returns (Call[] memory newCalls) {
uint256 num1 = calls1.length;
uint256 num2 = calls2.length;
newCalls = new Call[](num1 + num2);
for (uint256 i; i < num1; ++i) {
newCalls[i] = calls1[i];
}
for (uint256 i; i < num2; ++i) {
newCalls[num1 + i] = calls2[i];
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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 = "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";
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IFactory} from "../interfaces/factories/IFactory.sol";
import {IMarketConfigurator} from "../interfaces/IMarketConfigurator.sol";
import {IMarketConfiguratorFactory} from "../interfaces/IMarketConfiguratorFactory.sol";
import {Call} from "../interfaces/Types.sol";
import {AP_MARKET_CONFIGURATOR_FACTORY, NO_VERSION_CONTROL} from "../libraries/ContractLiterals.sol";
import {DeployerTrait} from "../traits/DeployerTrait.sol";
abstract contract AbstractFactory is DeployerTrait, IFactory {
address public immutable override marketConfiguratorFactory;
modifier onlyMarketConfigurators() {
_ensureCallerIsMarketConfigurator();
_;
}
constructor(address addressProvider_) DeployerTrait(addressProvider_) {
marketConfiguratorFactory = _getAddressOrRevert(AP_MARKET_CONFIGURATOR_FACTORY, NO_VERSION_CONTROL);
}
// ------------- //
// CONFIGURATION //
// ------------- //
function configure(address, bytes calldata callData) external virtual override returns (Call[] memory) {
revert ForbiddenConfigurationCallException(bytes4(callData));
}
function emergencyConfigure(address, bytes calldata callData) external virtual override returns (Call[] memory) {
revert ForbiddenEmergencyConfigurationCallException(bytes4(callData));
}
// --------- //
// INTERNALS //
// --------- //
function _ensureCallerIsMarketConfigurator() internal view {
if (!IMarketConfiguratorFactory(marketConfiguratorFactory).isMarketConfigurator(msg.sender)) {
revert CallerIsNotMarketConfiguratorException(msg.sender);
}
}
function _authorizeFactory(address marketConfigurator, address suite, address target)
internal
view
returns (Call memory)
{
return Call({
target: marketConfigurator,
callData: abi.encodeCall(IMarketConfigurator.authorizeFactory, (address(this), suite, target))
});
}
function _unauthorizeFactory(address marketConfigurator, address suite, address target)
internal
view
returns (Call memory)
{
return Call({
target: marketConfigurator,
callData: abi.encodeCall(IMarketConfigurator.unauthorizeFactory, (address(this), suite, target))
});
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IPoolQuotaKeeperV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolQuotaKeeperV3.sol";
import {IPoolV3} from "@gearbox-protocol/core-v3/contracts/interfaces/IPoolV3.sol";
import {IMarketFactory} from "../interfaces/factories/IMarketFactory.sol";
import {IContractsRegister} from "../interfaces/IContractsRegister.sol";
import {IMarketConfigurator} from "../interfaces/IMarketConfigurator.sol";
import {Call} from "../interfaces/Types.sol";
import {AbstractFactory} from "./AbstractFactory.sol";
abstract contract AbstractMarketFactory is AbstractFactory, IMarketFactory {
// ------------ //
// MARKET HOOKS //
// ------------ //
function onCreateMarket(address, address, address, address, address, address)
external
virtual
override
returns (Call[] memory)
{}
function onShutdownMarket(address) external virtual override returns (Call[] memory) {}
function onCreateCreditSuite(address) external virtual override returns (Call[] memory) {}
function onShutdownCreditSuite(address) external virtual override returns (Call[] memory) {}
function onUpdatePriceOracle(address, address, address) external virtual override returns (Call[] memory) {}
function onUpdateInterestRateModel(address, address, address) external virtual override returns (Call[] memory) {}
function onUpdateRateKeeper(address, address, address) external virtual override returns (Call[] memory) {}
function onUpdateLossPolicy(address, address, address) external virtual override returns (Call[] memory) {}
function onAddToken(address, address, address) external virtual override returns (Call[] memory) {}
// --------- //
// INTERNALS //
// --------- //
function _validateDefaultConstructorParams(address pool, bytes calldata constructorParams) internal view {
(address decodedPool, address decodedAddressProvider) = abi.decode(constructorParams[:64], (address, address));
if (decodedPool != pool || decodedAddressProvider != addressProvider) {
revert InvalidConstructorParamsException();
}
}
function _marketConfigurator(address pool) internal view returns (address) {
return Ownable(IPoolV3(pool).acl()).owner();
}
function _contractsRegister(address pool) internal view returns (address) {
return IMarketConfigurator(_marketConfigurator(pool)).contractsRegister();
}
function _underlying(address pool) internal view returns (address) {
return IPoolV3(pool).asset();
}
function _quotaKeeper(address pool) internal view returns (address) {
return IPoolV3(pool).poolQuotaKeeper();
}
function _interestRateModel(address pool) internal view returns (address) {
return IPoolV3(pool).interestRateModel();
}
function _priceOracle(address pool) internal view returns (address) {
return IContractsRegister(_contractsRegister(pool)).getPriceOracle(pool);
}
function _lossPolicy(address pool) internal view returns (address) {
return IContractsRegister(_contractsRegister(pool)).getLossPolicy(pool);
}
function _rateKeeper(address quotaKeeper) internal view returns (address) {
return IPoolQuotaKeeperV3(quotaKeeper).gauge();
}
function _quotedTokens(address quotaKeeper) internal view returns (address[] memory) {
return IPoolQuotaKeeperV3(quotaKeeper).quotedTokens();
}
function _isQuotedToken(address quotaKeeper, address token) internal view returns (bool) {
return IPoolQuotaKeeperV3(quotaKeeper).isQuotedToken(token);
}
function _quota(address quotaKeeper, address token) internal view returns (uint96 quota) {
(,,, quota,,) = IPoolQuotaKeeperV3(quotaKeeper).getTokenQuotaParams(token);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// 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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// 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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
interface IACLTrait {
function acl() external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
/// @title Voting contract interface
/// @notice Generic interface for a contract that can be voted for in `GearStakingV3` contract
/// @dev `vote` and `unvote` must implement votes accounting since it's not performed on the staking contract side
interface IVotingContract {
function voter() external view returns (address);
function vote(address user, uint96 votes, bytes calldata extraData) external;
function unvote(address user, uint96 votes, bytes calldata extraData) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
/// @title Deployer trait interface
interface IDeployerTrait {
function addressProvider() external view returns (address);
function bytecodeRepository() external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IDeployerTrait} from "./base/IDeployerTrait.sol";
import {Call, DeployParams, MarketFactories} from "./Types.sol";
interface IMarketConfigurator is IVersion, IDeployerTrait {
// ------ //
// EVENTS //
// ------ //
event SetEmergencyAdmin(address indexed newEmergencyAdmin);
event GrantRole(bytes32 indexed role, address indexed account);
event RevokeRole(bytes32 indexed role, address indexed account);
event EmergencyRevokeRole(bytes32 indexed role, address indexed account);
event CreateMarket(
address indexed pool,
address priceOracle,
address interestRateModel,
address rateKeeper,
address lossPolicy,
MarketFactories factories
);
event ShutdownMarket(address indexed pool);
event AddToken(address indexed pool, address indexed token);
event ConfigurePool(address indexed pool, bytes data);
event EmergencyConfigurePool(address indexed pool, bytes data);
event CreateCreditSuite(address indexed creditManager, address factory);
event ShutdownCreditSuite(address indexed creditManager);
event ConfigureCreditSuite(address indexed creditManager, bytes data);
event EmergencyConfigureCreditSuite(address indexed creditManager, bytes data);
event UpdatePriceOracle(address indexed pool, address priceOracle);
event ConfigurePriceOracle(address indexed pool, bytes data);
event EmergencyConfigurePriceOracle(address indexed pool, bytes data);
event UpdateInterestRateModel(address indexed pool, address interestRateModel);
event ConfigureInterestRateModel(address indexed pool, bytes data);
event EmergencyConfigureInterestRateModel(address indexed pool, bytes data);
event UpdateRateKeeper(address indexed pool, address rateKeeper);
event ConfigureRateKeeper(address indexed pool, bytes data);
event EmergencyConfigureRateKeeper(address indexed pool, bytes data);
event UpdateLossPolicy(address indexed pool, address lossPolicy);
event ConfigureLossPolicy(address indexed pool, bytes data);
event EmergencyConfigureLossPolicy(address indexed pool, bytes data);
event AddPeripheryContract(bytes32 indexed domain, address indexed peripheryContract);
event RemovePeripheryContract(bytes32 indexed domain, address indexed peripheryContract);
event AuthorizeFactory(address indexed factory, address indexed suite, address indexed target);
event UnauthorizeFactory(address indexed factory, address indexed suite, address indexed target);
event UpgradePoolFactory(address indexed pool, address factory);
event UpgradePriceOracleFactory(address indexed pool, address factory);
event UpgradeInterestRateModelFactory(address indexed pool, address factory);
event UpgradeRateKeeperFactory(address indexed pool, address factory);
event UpgradeLossPolicyFactory(address indexed pool, address factory);
event UpgradeCreditFactory(address indexed creditManager, address factory);
event ExecuteHook(address indexed target, bytes callData);
// ------ //
// ERRORS //
// ------ //
error CallerIsNotAdminException(address caller);
error CallerIsNotEmergencyAdminException(address caller);
error CallerIsNotSelfException(address caller);
error CreditSuiteNotRegisteredException(address creditManager);
error IncorrectMinorVersionException(uint256 version);
error IncorrectPeripheryContractException(address peripheryContract);
error MarketNotRegisteredException(address pool);
error UnauthorizedFactoryException(address factory, address target);
// --------------- //
// STATE VARIABLES //
// --------------- //
function admin() external view returns (address);
function emergencyAdmin() external view returns (address);
function curatorName() external view returns (string memory);
function acl() external view returns (address);
function contractsRegister() external view returns (address);
function treasury() external view returns (address);
// ---------------- //
// ROLES MANAGEMENT //
// ---------------- //
function setEmergencyAdmin(address newEmergencyAdmin) external;
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function emergencyRevokeRole(bytes32 role, address account) external;
// ----------------- //
// MARKET MANAGEMENT //
// ----------------- //
function previewCreateMarket(uint256 minorVersion, address underlying, string calldata name, string calldata symbol)
external
view
returns (address pool);
function createMarket(
uint256 minorVersion,
address underlying,
string calldata name,
string calldata symbol,
DeployParams calldata interestRateModelParams,
DeployParams calldata rateKeeperParams,
DeployParams calldata lossPolicyParams,
address underlyingPriceFeed
) external returns (address pool);
function shutdownMarket(address pool) external;
function addToken(address pool, address token, address priceFeed) external;
function configurePool(address pool, bytes calldata data) external;
function emergencyConfigurePool(address pool, bytes calldata data) external;
// ----------------------- //
// CREDIT SUITE MANAGEMENT //
// ----------------------- //
function previewCreateCreditSuite(uint256 minorVersion, address pool, bytes calldata encodedParams)
external
view
returns (address creditManager);
function previewCreateCreditSuite(
uint256 marketMinorVersion,
uint256 creditSuiteMinorVersion,
address underlying,
string calldata name,
string calldata symbol,
bytes calldata encodedParams
) external view returns (address creditManager);
function createCreditSuite(uint256 minorVersion, address pool, bytes calldata encdodedParams)
external
returns (address creditManager);
function shutdownCreditSuite(address creditManager) external;
function configureCreditSuite(address creditManager, bytes calldata data) external;
function emergencyConfigureCreditSuite(address creditManager, bytes calldata data) external;
// ----------------------- //
// PRICE ORACLE MANAGEMENT //
// ----------------------- //
function updatePriceOracle(address pool) external returns (address priceOracle);
function configurePriceOracle(address pool, bytes calldata data) external;
function emergencyConfigurePriceOracle(address pool, bytes calldata data) external;
// -------------- //
// IRM MANAGEMENT //
// -------------- //
function updateInterestRateModel(address pool, DeployParams calldata params) external returns (address irm);
function configureInterestRateModel(address pool, bytes calldata data) external;
function emergencyConfigureInterestRateModel(address pool, bytes calldata data) external;
// ---------------------- //
// RATE KEEPER MANAGEMENT //
// ---------------------- //
function updateRateKeeper(address pool, DeployParams calldata params) external returns (address rateKeeper);
function configureRateKeeper(address pool, bytes calldata data) external;
function emergencyConfigureRateKeeper(address pool, bytes calldata data) external;
// -–-------------------- //
// LOSS POLICY MANAGEMENT //
// -–-------------------- //
function updateLossPolicy(address pool, DeployParams calldata params) external returns (address lossPolicy);
function configureLossPolicy(address pool, bytes calldata data) external;
function emergencyConfigureLossPolicy(address pool, bytes calldata data) external;
// --------- //
// PERIPHERY //
// --------- //
function getPeripheryContracts(bytes32 domain) external view returns (address[] memory);
function isPeripheryContract(bytes32 domain, address peripheryContract) external view returns (bool);
function addPeripheryContract(address peripheryContract) external;
function removePeripheryContract(address peripheryContract) external;
// --------- //
// FACTORIES //
// --------- //
function getMarketFactories(address pool) external view returns (MarketFactories memory);
function getCreditFactory(address creditManager) external view returns (address);
function getAuthorizedFactory(address target) external view returns (address);
function getFactoryTargets(address factory, address suite) external view returns (address[] memory);
function authorizeFactory(address factory, address suite, address target) external;
function unauthorizeFactory(address factory, address suite, address target) external;
function upgradePoolFactory(address pool) external;
function upgradePriceOracleFactory(address pool) external;
function upgradeInterestRateModelFactory(address pool) external;
function upgradeRateKeeperFactory(address pool) external;
function upgradeLossPolicyFactory(address pool) external;
function upgradeCreditFactory(address creditManager) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IDeployerTrait} from "./base/IDeployerTrait.sol";
/// @title Market configurator factory interface
interface IMarketConfiguratorFactory is IVersion, IDeployerTrait {
// ------ //
// EVENTS //
// ------ //
event CreateMarketConfigurator(address indexed marketConfigurator, string name);
event ShutdownMarketConfigurator(address indexed marketConfigurator);
// ------ //
// ERRORS //
// ------ //
error CallerIsNotCrossChainGovernanceException(address caller);
error CallerIsNotMarketConfiguratorAdminException(address caller);
error CantShutdownMarketConfiguratorException(address marketConfigurator);
error MarketConfiguratorIsAlreadyAddedException(address marketConfigurator);
error MarketConfiguratorIsAlreadyShutdownException(address marketConfigruator);
error MarketConfiguratorIsNotRegisteredException(address marketConfigurator);
// ------- //
// GETTERS //
// ------- //
function isMarketConfigurator(address account) external view returns (bool);
function getMarketConfigurators() external view returns (address[] memory);
function getMarketConfigurator(uint256 index) external view returns (address);
function getNumMarketConfigurators() external view returns (uint256);
function getShutdownMarketConfigurators() external view returns (address[] memory);
// ------------- //
// CONFIGURATION //
// ------------- //
function createMarketConfigurator(
address emergencyAdmin,
address adminFeeTreasury,
string calldata curatorName,
bool deployGovernor
) external returns (address marketConfigurator);
function shutdownMarketConfigurator(address marketConfigurator) external;
function addMarketConfigurator(address marketConfigurator) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated 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);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {IACLTrait} from "./base/IACLTrait.sol";
import {IContractsRegisterTrait} from "./base/IContractsRegisterTrait.sol";
import {IVersion} from "./base/IVersion.sol";
struct TokenQuotaParams {
uint16 rate;
uint192 cumulativeIndexLU;
uint16 quotaIncreaseFee;
uint96 totalQuoted;
uint96 limit;
}
struct AccountQuota {
uint96 quota;
uint192 cumulativeIndexLU;
}
interface IPoolQuotaKeeperV3Events {
/// @notice Emitted when account's quota for a token is updated
event UpdateQuota(address indexed creditAccount, address indexed token, int96 quotaChange);
/// @notice Emitted when token's quota rate is updated
event UpdateTokenQuotaRate(address indexed token, uint16 rate);
/// @notice Emitted when the gauge is updated
event SetGauge(address indexed newGauge);
/// @notice Emitted when a new credit manager is allowed
event AddCreditManager(address indexed creditManager);
/// @notice Emitted when a new token is added as quoted
event AddQuotaToken(address indexed token);
/// @notice Emitted when a new total quota limit is set for a token
event SetTokenLimit(address indexed token, uint96 limit);
/// @notice Emitted when a new one-time quota increase fee is set for a token
event SetQuotaIncreaseFee(address indexed token, uint16 fee);
}
/// @title Pool quota keeper V3 interface
interface IPoolQuotaKeeperV3 is IPoolQuotaKeeperV3Events, IVersion, IACLTrait, IContractsRegisterTrait {
function pool() external view returns (address);
function underlying() external view returns (address);
// ----------------- //
// QUOTAS MANAGEMENT //
// ----------------- //
function updateQuota(address creditAccount, address token, int96 requestedChange, uint96 minQuota, uint96 maxQuota)
external
returns (uint128 caQuotaInterestChange, uint128 fees, bool enableToken, bool disableToken);
function removeQuotas(address creditAccount, address[] calldata tokens, bool setLimitsToZero) external;
function accrueQuotaInterest(address creditAccount, address[] calldata tokens) external;
function getQuotaRate(address) external view returns (uint16);
function cumulativeIndex(address token) external view returns (uint192);
function isQuotedToken(address token) external view returns (bool);
function getQuota(address creditAccount, address token)
external
view
returns (uint96 quota, uint192 cumulativeIndexLU);
function getTokenQuotaParams(address token)
external
view
returns (
uint16 rate,
uint192 cumulativeIndexLU,
uint16 quotaIncreaseFee,
uint96 totalQuoted,
uint96 limit,
bool isActive
);
function getQuotaAndOutstandingInterest(address creditAccount, address token)
external
view
returns (uint96 quoted, uint128 outstandingInterest);
function poolQuotaRevenue() external view returns (uint256);
function lastQuotaRateUpdate() external view returns (uint40);
// ------------- //
// CONFIGURATION //
// ------------- //
function gauge() external view returns (address);
function setGauge(address _gauge) external;
function creditManagers() external view returns (address[] memory);
function addCreditManager(address _creditManager) external;
function quotedTokens() external view returns (address[] memory);
function addQuotaToken(address token) external;
function updateRates() external;
function setTokenLimit(address token, uint96 limit) external;
function setTokenQuotaIncreaseFee(address token, uint16 fee) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
pragma abicoder v1;
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IACLTrait} from "./base/IACLTrait.sol";
import {IContractsRegisterTrait} from "./base/IContractsRegisterTrait.sol";
import {IVersion} from "./base/IVersion.sol";
interface IPoolV3Events {
/// @notice Emitted when depositing liquidity with referral code
event Refer(address indexed onBehalfOf, uint256 indexed referralCode, uint256 amount);
/// @notice Emitted when credit account borrows funds from the pool
event Borrow(address indexed creditManager, address indexed creditAccount, uint256 amount);
/// @notice Emitted when credit account's debt is repaid to the pool
event Repay(address indexed creditManager, uint256 borrowedAmount, uint256 profit, uint256 loss);
/// @notice Emitted when incurred loss can't be fully covered by burning treasury's shares
event IncurUncoveredLoss(address indexed creditManager, uint256 loss);
/// @notice Emitted when new interest rate model contract is set
event SetInterestRateModel(address indexed newInterestRateModel);
/// @notice Emitted when new pool quota keeper contract is set
event SetPoolQuotaKeeper(address indexed newPoolQuotaKeeper);
/// @notice Emitted when new total debt limit is set
event SetTotalDebtLimit(uint256 limit);
/// @notice Emitted when new credit manager is connected to the pool
event AddCreditManager(address indexed creditManager);
/// @notice Emitted when new debt limit is set for a credit manager
event SetCreditManagerDebtLimit(address indexed creditManager, uint256 newLimit);
/// @notice Emitted when new withdrawal fee is set
event SetWithdrawFee(uint256 fee);
}
/// @title Pool V3 interface
interface IPoolV3 is IVersion, IACLTrait, IContractsRegisterTrait, IPoolV3Events, IERC4626, IERC20Permit {
function underlyingToken() external view returns (address);
function treasury() external view returns (address);
function withdrawFee() external view returns (uint16);
function creditManagers() external view returns (address[] memory);
function availableLiquidity() external view returns (uint256);
function expectedLiquidity() external view returns (uint256);
function expectedLiquidityLU() external view returns (uint256);
// ---------------- //
// ERC-4626 LENDING //
// ---------------- //
function depositWithReferral(uint256 assets, address receiver, uint256 referralCode)
external
returns (uint256 shares);
function mintWithReferral(uint256 shares, address receiver, uint256 referralCode)
external
returns (uint256 assets);
// --------- //
// BORROWING //
// --------- //
function totalBorrowed() external view returns (uint256);
function totalDebtLimit() external view returns (uint256);
function creditManagerBorrowed(address creditManager) external view returns (uint256);
function creditManagerDebtLimit(address creditManager) external view returns (uint256);
function creditManagerBorrowable(address creditManager) external view returns (uint256 borrowable);
function lendCreditAccount(uint256 borrowedAmount, address creditAccount) external;
function repayCreditAccount(uint256 repaidAmount, uint256 profit, uint256 loss) external;
// ------------- //
// INTEREST RATE //
// ------------- //
function interestRateModel() external view returns (address);
function baseInterestRate() external view returns (uint256);
function supplyRate() external view returns (uint256);
function baseInterestIndex() external view returns (uint256);
function baseInterestIndexLU() external view returns (uint256);
function lastBaseInterestUpdate() external view returns (uint40);
// ------ //
// QUOTAS //
// ------ //
function poolQuotaKeeper() external view returns (address);
function quotaRevenue() external view returns (uint256);
function lastQuotaRevenueUpdate() external view returns (uint40);
function updateQuotaRevenue(int256 quotaRevenueDelta) external;
function setQuotaRevenue(uint256 newQuotaRevenue) external;
// ------------- //
// CONFIGURATION //
// ------------- //
function setInterestRateModel(address newInterestRateModel) external;
function setPoolQuotaKeeper(address newPoolQuotaKeeper) external;
function setTotalDebtLimit(uint256 newLimit) external;
function setCreditManagerDebtLimit(address creditManager, uint256 newLimit) external;
function setWithdrawFee(uint256 newWithdrawFee) external;
function pause() external;
function unpause() external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.23;
import {IACLTrait} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IACLTrait.sol";
import {IContractsRegister as IContractsRegisterBase} from
"@gearbox-protocol/core-v3/contracts/interfaces/base/IContractsRegister.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
interface IContractsRegister is IContractsRegisterBase, IVersion, IACLTrait {
// ------ //
// EVENTS //
// ------ //
event RegisterMarket(address indexed pool, address indexed priceOracle, address indexed lossPolicy);
event ShutdownMarket(address indexed pool);
event RegisterCreditSuite(address indexed pool, address indexed creditManager);
event ShutdownCreditSuite(address indexed pool, address indexed creditManager);
event SetPriceOracle(address indexed pool, address indexed priceOracle);
event SetLossPolicy(address indexed pool, address indexed lossPolicy);
// ------ //
// ERRORS //
// ------ //
error MarketNotRegisteredException(address pool);
error MarketShutDownException(address pool);
error MarketNotEmptyException(address pool);
error CreditSuiteNotRegisteredException(address creditManager);
error CreditSuiteShutDownException(address creditManager);
// ------- //
// MARKETS //
// ------- //
function getShutdownPools() external view returns (address[] memory);
function getPriceOracle(address pool) external view returns (address);
function getLossPolicy(address pool) external view returns (address);
function registerMarket(address pool, address priceOracle, address lossPolicy) external;
function shutdownMarket(address pool) external;
function setPriceOracle(address pool, address priceOracle) external;
function setLossPolicy(address pool, address lossPolicy) external;
// ------------- //
// CREDIT SUITES //
// ------------- //
function getCreditManagers(address pool) external view returns (address[] memory);
function getShutdownCreditManagers() external view returns (address[] memory);
function getShutdownCreditManagers(address pool) external view returns (address[] memory);
function registerCreditSuite(address creditManager) external;
function shutdownCreditSuite(address creditManager) external;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice 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)))
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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
);
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 DomainIsAlreadyMarketAsPublicException(bytes32 domain);
error DomainIsAlreadyMarketAsSystemException(bytes32 domain);
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 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, address auditor, string calldata reportUrl)
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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated 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;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
interface IContractsRegisterTrait {
function contractsRegister() external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
interface IContractsRegister {
function isPool(address) external view returns (bool);
function getPools() external view returns (address[] memory);
function isCreditManager(address) external view returns (bool);
function getCreditManagers() external view returns (address[] memory);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// 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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
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);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}