Contract Name:
ControllerTimelockV3
Contract Source Code:
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {PolicyManagerV3} from "./PolicyManagerV3.sol";
import {IControllerTimelockV3, QueuedTransactionData} from "../interfaces/IControllerTimelockV3.sol";
import {ICreditManagerV3} from "../interfaces/ICreditManagerV3.sol";
import {ICreditFacadeV3} from "../interfaces/ICreditFacadeV3.sol";
import {IPoolV3} from "../interfaces/IPoolV3.sol";
import {IPoolQuotaKeeperV3} from "../interfaces/IPoolQuotaKeeperV3.sol";
import {IGaugeV3} from "../interfaces/IGaugeV3.sol";
import {ILPPriceFeedV2} from "@gearbox-protocol/core-v2/contracts/interfaces/ILPPriceFeedV2.sol";
import "../interfaces/IExceptions.sol";
/// @title Controller timelock V3
/// @notice Controller timelock is a governance contract that allows special actors less trusted than Gearbox Governance
/// to modify system parameters within set boundaries. This is mostly related to risk parameters that should be
/// adjusted frequently or periodic tasks (e.g., updating price feed limiters) that are too trivial to employ
/// the full governance for.
/// @dev The contract uses `PolicyManager` as its underlying engine to set parameter change boundaries and conditions.
/// In order to schedule a change for a particular contract / function combination, a policy needs to be defined
/// for it. The policy also determines the address that can change a particular parameter.
contract ControllerTimelockV3 is PolicyManagerV3, IControllerTimelockV3 {
/// @notice Contract version
uint256 public constant override version = 3_00;
/// @dev Minimum liquidation threshold ramp duration
uint256 constant MIN_LT_RAMP_DURATION = 7 days;
/// @notice Period before a mature transaction becomes stale
uint256 public constant override GRACE_PERIOD = 14 days;
/// @notice Admin address that can cancel transactions
address public override vetoAdmin;
/// @notice Mapping from transaction hashes to their data
mapping(bytes32 => QueuedTransactionData) public override queuedTransactions;
/// @notice Constructor
/// @param _addressProvider Address of the address provider
/// @param _vetoAdmin Admin that can cancel transactions
constructor(address _addressProvider, address _vetoAdmin) PolicyManagerV3(_addressProvider) {
vetoAdmin = _vetoAdmin;
}
/// @dev Ensures that function caller is the veto admin
modifier vetoAdminOnly() {
_revertIfCallerIsNotVetoAdmin();
_;
}
/// @dev Reverts if `msg.sender` is not the veto admin
function _revertIfCallerIsNotVetoAdmin() internal view {
if (msg.sender != vetoAdmin) {
revert CallerNotVetoAdminException();
}
}
// -------- //
// QUEUEING //
// -------- //
/// @notice Queues a transaction to set a new expiration date in the Credit Facade
/// @dev Requires the policy for keccak(group(creditManager), "EXPIRATION_DATE") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the expiration date for
/// @param expirationDate The new expiration date
function setExpirationDate(address creditManager, uint40 expirationDate) external override {
address creditConfigurator = ICreditManagerV3(creditManager).creditConfigurator();
IPoolV3 pool = IPoolV3(ICreditManagerV3(creditManager).pool());
address creditFacade = ICreditManagerV3(creditManager).creditFacade();
uint40 oldExpirationDate = getExpirationDate(creditFacade);
if (!_checkPolicy(creditManager, "EXPIRATION_DATE", uint256(oldExpirationDate), uint256(expirationDate))) {
revert ParameterChecksFailedException(); // U:[CT-1]
}
uint256 totalBorrowed = pool.creditManagerBorrowed(address(creditManager));
if (totalBorrowed != 0) {
revert ParameterChecksFailedException(); // U:[CT-1]
}
_queueTransaction({
target: creditConfigurator,
signature: "setExpirationDate(uint40)",
data: abi.encode(expirationDate),
delay: _getPolicyDelay(creditManager, "EXPIRATION_DATE"),
sanityCheckValue: oldExpirationDate,
sanityCheckCallData: abi.encodeCall(this.getExpirationDate, (creditFacade))
}); // U:[CT-1]
}
/// @dev Retrieves current expiration date for a credit manager
function getExpirationDate(address creditFacade) public view returns (uint40) {
return ICreditFacadeV3(creditFacade).expirationDate();
}
/// @notice Queues a transaction to set a new limiter value in a price feed
/// @dev Requires the policy for keccak(group(priceFeed), "LP_PRICE_FEED_LIMITER") to be enabled,
/// otherwise auto-fails the check
/// @param priceFeed The price feed to update the limiter in
/// @param lowerBound The new limiter lower bound value
function setLPPriceFeedLimiter(address priceFeed, uint256 lowerBound) external override {
uint256 currentLowerBound = getPriceFeedLowerBound(priceFeed);
if (!_checkPolicy(priceFeed, "LP_PRICE_FEED_LIMITER", currentLowerBound, lowerBound)) {
revert ParameterChecksFailedException(); // U:[CT-2]
}
_queueTransaction({
target: priceFeed,
signature: "setLimiter(uint256)",
data: abi.encode(lowerBound),
delay: _getPolicyDelay(priceFeed, "LP_PRICE_FEED_LIMITER"),
sanityCheckValue: currentLowerBound,
sanityCheckCallData: abi.encodeCall(this.getPriceFeedLowerBound, (priceFeed))
}); // U:[CT-2]
}
/// @dev Retrieves current lower bound for a price feed
function getPriceFeedLowerBound(address priceFeed) public view returns (uint256) {
return ILPPriceFeedV2(priceFeed).lowerBound();
}
/// @notice Queues a transaction to set a new max debt per block multiplier
/// @dev Requires the policy for keccak(group(creditManager), "MAX_DEBT_PER_BLOCK_MULTIPLIER") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the multiplier for
/// @param multiplier The new multiplier value
function setMaxDebtPerBlockMultiplier(address creditManager, uint8 multiplier) external override {
address creditConfigurator = ICreditManagerV3(creditManager).creditConfigurator();
address creditFacade = ICreditManagerV3(creditManager).creditFacade();
uint8 currentMultiplier = getMaxDebtPerBlockMultiplier(creditFacade);
if (
!_checkPolicy(
creditManager, "MAX_DEBT_PER_BLOCK_MULTIPLIER", uint256(currentMultiplier), uint256(multiplier)
)
) {
revert ParameterChecksFailedException(); // U:[CT-3]
}
_queueTransaction({
target: creditConfigurator,
signature: "setMaxDebtPerBlockMultiplier(uint8)",
data: abi.encode(multiplier),
delay: _getPolicyDelay(creditManager, "MAX_DEBT_PER_BLOCK_MULTIPLIER"),
sanityCheckValue: currentMultiplier,
sanityCheckCallData: abi.encodeCall(this.getMaxDebtPerBlockMultiplier, (creditFacade))
}); // U:[CT-3]
}
/// @dev Retrieves current max debt per block multiplier for a Credit Facade
function getMaxDebtPerBlockMultiplier(address creditFacade) public view returns (uint8) {
return ICreditFacadeV3(creditFacade).maxDebtPerBlockMultiplier();
}
/// @notice Queues a transaction to set a new min debt per account
/// @dev Requires the policy for keccak(group(creditManager), "MIN_DEBT") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the limits for
/// @param minDebt The new minimal debt amount
function setMinDebtLimit(address creditManager, uint128 minDebt) external override {
address creditConfigurator = ICreditManagerV3(creditManager).creditConfigurator();
address creditFacade = ICreditManagerV3(creditManager).creditFacade();
uint128 minDebtCurrent = getMinDebtLimit(creditFacade);
if (!_checkPolicy(creditManager, "MIN_DEBT", uint256(minDebtCurrent), uint256(minDebt))) {
revert ParameterChecksFailedException(); // U:[CT-4A]
}
_queueTransaction({
target: creditConfigurator,
signature: "setMinDebtLimit(uint128)",
data: abi.encode(minDebt),
delay: _getPolicyDelay(creditManager, "MIN_DEBT"),
sanityCheckValue: minDebtCurrent,
sanityCheckCallData: abi.encodeCall(this.getMinDebtLimit, (creditFacade))
}); // U:[CT-4A]
}
/// @dev Retrieves the current min debt limit for a Credit Manager
function getMinDebtLimit(address creditFacade) public view returns (uint128) {
(uint128 minDebtCurrent,) = ICreditFacadeV3(creditFacade).debtLimits();
return minDebtCurrent;
}
/// @notice Queues a transaction to set a new max debt per account
/// @dev Requires the policy for keccak(group(creditManager), "MAX_DEBT") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the limits for
/// @param maxDebt The new maximal debt amount
function setMaxDebtLimit(address creditManager, uint128 maxDebt) external override {
address creditConfigurator = ICreditManagerV3(creditManager).creditConfigurator();
address creditFacade = ICreditManagerV3(creditManager).creditFacade();
uint128 maxDebtCurrent = getMaxDebtLimit(creditFacade);
if (!_checkPolicy(creditManager, "MAX_DEBT", uint256(maxDebtCurrent), uint256(maxDebt))) {
revert ParameterChecksFailedException(); // U:[CT-4B]
}
_queueTransaction({
target: creditConfigurator,
signature: "setMaxDebtLimit(uint128)",
data: abi.encode(maxDebt),
delay: _getPolicyDelay(creditManager, "MAX_DEBT"),
sanityCheckValue: maxDebtCurrent,
sanityCheckCallData: abi.encodeCall(this.getMaxDebtLimit, (creditFacade))
}); // U:[CT-4B]
}
/// @dev Retrieves the current max debt limit for a Credit Manager
function getMaxDebtLimit(address creditFacade) public view returns (uint128) {
(, uint128 maxDebtCurrent) = ICreditFacadeV3(creditFacade).debtLimits();
return maxDebtCurrent;
}
/// @notice Queues a transaction to set a new debt limit for a Credit Manager
/// @dev Requires the policy for keccak(group(creditManager), "CREDIT_MANAGER_DEBT_LIMIT") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the debt limit for
/// @param debtLimit The new debt limit
function setCreditManagerDebtLimit(address creditManager, uint256 debtLimit) external override {
IPoolV3 pool = IPoolV3(ICreditManagerV3(creditManager).pool());
uint256 debtLimitCurrent = getCreditManagerDebtLimit(address(pool), creditManager);
if (!_checkPolicy(creditManager, "CREDIT_MANAGER_DEBT_LIMIT", uint256(debtLimitCurrent), uint256(debtLimit))) {
revert ParameterChecksFailedException(); // U:[CT-5]
}
_queueTransaction({
target: address(pool),
signature: "setCreditManagerDebtLimit(address,uint256)",
data: abi.encode(address(creditManager), debtLimit),
delay: _getPolicyDelay(creditManager, "CREDIT_MANAGER_DEBT_LIMIT"),
sanityCheckValue: debtLimitCurrent,
sanityCheckCallData: abi.encodeCall(this.getCreditManagerDebtLimit, (address(pool), creditManager))
}); // U:[CT-5]
}
/// @dev Retrieves the current total debt limit for Credit Manager from its pool
function getCreditManagerDebtLimit(address pool, address creditManager) public view returns (uint256) {
return IPoolV3(pool).creditManagerDebtLimit(creditManager);
}
/// @notice Queues a transaction to start a liquidation threshold ramp
/// @dev Requires the policy for keccak(group(creditManager), group(token), "TOKEN_LT") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to update the LT for
/// @param token Token to ramp the LT for
/// @param liquidationThresholdFinal The liquidation threshold value after the ramp
/// @param rampDuration Duration of the ramp
function rampLiquidationThreshold(
address creditManager,
address token,
uint16 liquidationThresholdFinal,
uint40 rampStart,
uint24 rampDuration
) external override {
bytes32 policyHash = keccak256(abi.encode(_group[creditManager], _group[token], "TOKEN_LT"));
uint256 ltCurrent = ICreditManagerV3(creditManager).liquidationThresholds(token);
uint256 delay = _getPolicyDelay(policyHash);
if (
!_checkPolicy(policyHash, uint256(ltCurrent), uint256(liquidationThresholdFinal))
|| rampDuration < MIN_LT_RAMP_DURATION || rampStart < block.timestamp + delay
) {
revert ParameterChecksFailedException(); // U: [CT-6]
}
_queueTransaction({
target: ICreditManagerV3(creditManager).creditConfigurator(),
signature: "rampLiquidationThreshold(address,uint16,uint40,uint24)",
data: abi.encode(token, liquidationThresholdFinal, rampStart, rampDuration),
delay: delay,
sanityCheckValue: uint256(getLTRampParamsHash(creditManager, token)),
sanityCheckCallData: abi.encodeCall(this.getLTRampParamsHash, (creditManager, token))
}); // U: [CT-6]
}
/// @dev Retrives the keccak of liquidation threshold params for a token
function getLTRampParamsHash(address creditManager, address token) public view returns (bytes32) {
(uint16 ltInitial, uint16 ltFinal, uint40 timestampRampStart, uint24 rampDuration) =
ICreditManagerV3(creditManager).ltParams(token);
return keccak256(abi.encode(ltInitial, ltFinal, timestampRampStart, rampDuration));
}
/// @notice Queues a transaction to forbid a third party contract adapter
/// @dev Requires the policy for keccak(group(creditManager), "FORBID_ADAPTER") to be enabled,
/// otherwise auto-fails the check
/// @param creditManager Adress of CM to forbid an adapter for
/// @param adapter Address of adapter to forbid
function forbidAdapter(address creditManager, address adapter) external override {
bytes32 policyHash = keccak256(abi.encode(_group[creditManager], "FORBID_ADAPTER"));
address creditConfigurator = ICreditManagerV3(creditManager).creditConfigurator();
// For `forbidAdapter`, there is no value to modify
// A policy check simply verifies that this controller has access to the function in a given group
if (!_checkPolicy(policyHash, 0, 0)) {
revert ParameterChecksFailedException(); // U: [CT-10]
}
_queueTransaction({
target: creditConfigurator,
signature: "forbidAdapter(address)",
data: abi.encode(adapter),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: 0,
sanityCheckCallData: ""
}); // U: [CT-10]
}
/// @notice Queues a transaction to set a new limit on quotas for particular pool and token
/// @dev Requires the policy for keccak(group(pool), group(token), "TOKEN_LIMIT") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param token Token to update the limit for
/// @param limit The new value of the limit
function setTokenLimit(address pool, address token, uint96 limit) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], _group[token], "TOKEN_LIMIT"));
address poolQuotaKeeper = IPoolV3(pool).poolQuotaKeeper();
uint96 oldLimit = getTokenLimit(poolQuotaKeeper, token);
if (!_checkPolicy(policyHash, uint256(oldLimit), uint256(limit))) {
revert ParameterChecksFailedException(); // U: [CT-11]
}
_queueTransaction({
target: poolQuotaKeeper,
signature: "setTokenLimit(address,uint96)",
data: abi.encode(token, limit),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: oldLimit,
sanityCheckCallData: abi.encodeCall(this.getTokenLimit, (poolQuotaKeeper, token))
}); // U: [CT-11]
}
/// @dev Retrieves the per-token quota limit from pool quota keeper
function getTokenLimit(address poolQuotaKeeper, address token) public view returns (uint96) {
(,,,, uint96 oldLimit,) = IPoolQuotaKeeperV3(poolQuotaKeeper).getTokenQuotaParams(token);
return oldLimit;
}
/// @notice Queues a transaction to set a new quota increase (trading) fee for a particular pool and token
/// @dev Requires the policy for keccak(group(pool), group(token), "TOKEN_QUOTA_INCREASE_FEE") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param token Token to update the limit for
/// @param quotaIncreaseFee The new value of the fee in bp
function setTokenQuotaIncreaseFee(address pool, address token, uint16 quotaIncreaseFee) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], _group[token], "TOKEN_QUOTA_INCREASE_FEE"));
address poolQuotaKeeper = IPoolV3(pool).poolQuotaKeeper();
uint16 quotaIncreaseFeeOld = getTokenQuotaIncreaseFee(poolQuotaKeeper, token);
if (!_checkPolicy(policyHash, uint256(quotaIncreaseFeeOld), uint256(quotaIncreaseFee))) {
revert ParameterChecksFailedException(); // U: [CT-12]
}
_queueTransaction({
target: poolQuotaKeeper,
signature: "setTokenQuotaIncreaseFee(address,uint16)",
data: abi.encode(token, quotaIncreaseFee),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: quotaIncreaseFeeOld,
sanityCheckCallData: abi.encodeCall(this.getTokenQuotaIncreaseFee, (poolQuotaKeeper, token))
}); // U: [CT-12]
}
/// @dev Retrieves the quota increase fee for a token
function getTokenQuotaIncreaseFee(address poolQuotaKeeper, address token) public view returns (uint16) {
(,, uint16 quotaIncreaseFeeOld,,,) = IPoolQuotaKeeperV3(poolQuotaKeeper).getTokenQuotaParams(token);
return quotaIncreaseFeeOld;
}
/// @notice Queues a transaction to set a new total debt limit for the entire pool
/// @dev Requires the policy for keccak(group(pool), "TOTAL_DEBT_LIMIT") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param newLimit The new value of the limit
function setTotalDebtLimit(address pool, uint256 newLimit) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], "TOTAL_DEBT_LIMIT"));
uint256 totalDebtLimitOld = getTotalDebtLimit(pool);
if (!_checkPolicy(policyHash, uint256(totalDebtLimitOld), uint256(newLimit))) {
revert ParameterChecksFailedException(); // U: [CT-13]
}
_queueTransaction({
target: pool,
signature: "setTotalDebtLimit(uint256)",
data: abi.encode(newLimit),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: totalDebtLimitOld,
sanityCheckCallData: abi.encodeCall(this.getTotalDebtLimit, (pool))
}); // U: [CT-13]
}
/// @dev Retrieves the total debt limit for a pool
function getTotalDebtLimit(address pool) public view returns (uint256) {
return IPoolV3(pool).totalDebtLimit();
}
/// @notice Queues a transaction to set a new withdrawal fee in a pool
/// @dev Requires the policy for keccak(group(pool), "WITHDRAW_FEE") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param newFee The new value of the fee in bp
function setWithdrawFee(address pool, uint256 newFee) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], "WITHDRAW_FEE"));
uint256 withdrawFeeOld = IPoolV3(pool).withdrawFee();
if (!_checkPolicy(policyHash, withdrawFeeOld, newFee)) {
revert ParameterChecksFailedException(); // U: [CT-14]
}
_queueTransaction({
target: pool,
signature: "setWithdrawFee(uint256)",
data: abi.encode(newFee),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: withdrawFeeOld,
sanityCheckCallData: abi.encodeCall(this.getWithdrawFee, (pool))
}); // U: [CT-14]
}
/// @dev Retrieves the withdrawal fee for a pool
function getWithdrawFee(address pool) public view returns (uint256) {
return IPoolV3(pool).withdrawFee();
}
/// @notice Queues a transaction to set a new minimal quota interest rate for particular pool and token
/// @dev Requires the policy for keccak(group(pool), group(token), "TOKEN_QUOTA_MIN_RATE") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param token Token to set the new fee for
/// @param rate The new minimal rate
function setMinQuotaRate(address pool, address token, uint16 rate) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], _group[token], "TOKEN_QUOTA_MIN_RATE"));
address poolQuotaKeeper = IPoolV3(pool).poolQuotaKeeper();
address gauge = IPoolQuotaKeeperV3(poolQuotaKeeper).gauge();
uint16 minRateCurrent = getMinQuotaRate(gauge, token);
if (!_checkPolicy(policyHash, uint256(minRateCurrent), uint256(rate))) {
revert ParameterChecksFailedException(); // U: [CT-15A]
}
_queueTransaction({
target: gauge,
signature: "changeQuotaMinRate(address,uint16)",
data: abi.encode(token, rate),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: minRateCurrent,
sanityCheckCallData: abi.encodeCall(this.getMinQuotaRate, (gauge, token))
}); // U: [CT-15A]
}
/// @dev Retrieves the current minimal quota rate for a token in a gauge
function getMinQuotaRate(address gauge, address token) public view returns (uint16) {
(uint16 minRateCurrent,,,) = IGaugeV3(gauge).quotaRateParams(token);
return minRateCurrent;
}
/// @notice Queues a transaction to set a new maximal quota interest rate for particular pool and token
/// @dev Requires the policy for keccak(group(pool), group(token), "TOKEN_QUOTA_MAX_RATE") to be enabled,
/// otherwise auto-fails the check
/// @param pool Pool to update the limit for
/// @param token Token to set the new fee for
/// @param rate The new maximal rate
function setMaxQuotaRate(address pool, address token, uint16 rate) external override {
bytes32 policyHash = keccak256(abi.encode(_group[pool], _group[token], "TOKEN_QUOTA_MAX_RATE"));
address poolQuotaKeeper = IPoolV3(pool).poolQuotaKeeper();
address gauge = IPoolQuotaKeeperV3(poolQuotaKeeper).gauge();
uint16 maxRateCurrent = getMaxQuotaRate(gauge, token);
if (!_checkPolicy(policyHash, uint256(maxRateCurrent), uint256(rate))) {
revert ParameterChecksFailedException(); // U: [CT-15B]
}
_queueTransaction({
target: gauge,
signature: "changeQuotaMaxRate(address,uint16)",
data: abi.encode(token, rate),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: maxRateCurrent,
sanityCheckCallData: abi.encodeCall(this.getMaxQuotaRate, (gauge, token))
}); // U: [CT-15B]
}
/// @dev Retrieves the current maximal quota rate for a token in a gauge
function getMaxQuotaRate(address gauge, address token) public view returns (uint16) {
(, uint16 maxRateCurrent,,) = IGaugeV3(gauge).quotaRateParams(token);
return maxRateCurrent;
}
/// @notice Queues a transaction to activate or deactivate reserve price feed for a token in price oracle
/// @dev Requires the policy for keccak(group(priceOracle), group(token), "RESERVE_PRICE_FEED_STATUS")
/// to be enabled, otherwise auto-fails the check
/// @param priceOracle Price oracle to change reserve price feed status for
/// @param token Token to change reserve price feed status for
/// @param active New reserve price feed status (`true` to activate, `false` to deactivate)
function setReservePriceFeedStatus(address priceOracle, address token, bool active) external override {
bytes32 policyHash = keccak256(abi.encode(_group[priceOracle], _group[token], "RESERVE_PRICE_FEED_STATUS"));
if (!_checkPolicy(policyHash, 0, 0)) {
revert ParameterChecksFailedException(); // U:[CT-16]
}
_queueTransaction({
target: priceOracle,
signature: "setReservePriceFeedStatus(address,bool)",
data: abi.encode(token, active),
delay: _getPolicyDelay(policyHash),
sanityCheckValue: 0,
sanityCheckCallData: ""
}); // U:[CT-16]
}
/// @notice Queues a transaction to forbid permissionless bounds update in an LP price feed
/// @dev Requires the policy for keccak(group(priceFeed), "UPDATE_BOUNDS_ALLOWED") to be enabled,
/// otherwise auto-fails the check
/// @param priceFeed The price feed to forbid bounds update for
function forbidBoundsUpdate(address priceFeed) external override {
if (!_checkPolicy(priceFeed, "UPDATE_BOUNDS_ALLOWED", 0, 0)) {
revert ParameterChecksFailedException(); // U:[CT-17]
}
_queueTransaction({
target: priceFeed,
signature: "forbidBoundsUpdate()",
data: "",
delay: _getPolicyDelay(priceFeed, "UPDATE_BOUNDS_ALLOWED"),
sanityCheckValue: 0,
sanityCheckCallData: ""
}); // U:[CT-17]
}
/// @dev Internal function that stores the transaction in the queued tx map
/// @param target The contract to call
/// @param signature The signature of the called function
/// @param data The call data
/// @return Hash of the queued transaction
function _queueTransaction(
address target,
string memory signature,
bytes memory data,
uint256 delay,
uint256 sanityCheckValue,
bytes memory sanityCheckCallData
) internal returns (bytes32) {
uint256 eta = block.timestamp + delay;
bytes32 txHash = keccak256(abi.encode(msg.sender, target, signature, data, eta));
queuedTransactions[txHash] = QueuedTransactionData({
queued: true,
executor: msg.sender,
target: target,
eta: uint40(eta),
signature: signature,
data: data,
sanityCheckValue: sanityCheckValue,
sanityCheckCallData: sanityCheckCallData
});
emit QueueTransaction({
txHash: txHash,
executor: msg.sender,
target: target,
signature: signature,
data: data,
eta: uint40(eta)
});
return txHash;
}
// --------- //
// EXECUTION //
// --------- //
/// @notice Sets the transaction's queued status as false, effectively cancelling it
/// @param txHash Hash of the transaction to be cancelled
function cancelTransaction(bytes32 txHash)
external
override
vetoAdminOnly // U: [CT-7]
{
queuedTransactions[txHash].queued = false;
emit CancelTransaction(txHash);
}
/// @notice Executes a queued transaction
/// @param txHash Hash of the transaction to be executed
function executeTransaction(bytes32 txHash) external override {
QueuedTransactionData memory qtd = queuedTransactions[txHash];
if (!qtd.queued) {
revert TxNotQueuedException(); // U: [CT-7]
}
if (msg.sender != qtd.executor) {
revert CallerNotExecutorException(); // U: [CT-9]
}
address target = qtd.target;
uint40 eta = qtd.eta;
string memory signature = qtd.signature;
bytes memory data = qtd.data;
if (block.timestamp < eta || block.timestamp > eta + GRACE_PERIOD) {
revert TxExecutedOutsideTimeWindowException(); // U: [CT-9]
}
// In order to ensure that we do not accidentally override a change
// made by configurator or another admin, the current value of the parameter
// is compared to the value at the moment of tx being queued
if (qtd.sanityCheckCallData.length != 0) {
(, bytes memory returndata) = address(this).staticcall(qtd.sanityCheckCallData);
if (abi.decode(returndata, (uint256)) != qtd.sanityCheckValue) {
revert ParameterChangedAfterQueuedTxException();
}
}
queuedTransactions[txHash].queued = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
(bool success,) = target.call(callData);
if (!success) {
revert TxExecutionRevertedException(); // U: [CT-9]
}
emit ExecuteTransaction(txHash); // U: [CT-9]
}
// ------------- //
// CONFIGURATION //
// ------------- //
/// @notice Sets a new veto admin address
function setVetoAdmin(address newAdmin)
external
override
configuratorOnly // U: [CT-8]
{
if (vetoAdmin != newAdmin) {
vetoAdmin = newAdmin; // U: [CT-8]
emit SetVetoAdmin(newAdmin); // U: [CT-8]
}
}
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {ACLNonReentrantTrait} from "../traits/ACLNonReentrantTrait.sol";
import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/Constants.sol";
/// @notice Policy that determines checks performed on a parameter
/// Each policy is defined for a contract group, which is a string
/// identifier for a set of contracts
/// @param enabled Determines whether the policy is enabled. A disabled policy will auto-fail the policy check.
/// @param admin The admin that can change the parameter under the given policy
/// @param delay The delay before the transaction can be triggered under a given policy
/// @param flags Bitmask of flags that determine which policy checks to apply on parameter change:
/// * 0 - check exact value
/// * 1 - check min value
/// * 2 - check max value
/// * 3 - check min change
/// * 4 - check max change
/// * 5 - check min pct change
/// * 6 - check max pct change
/// @param exactValue Exact value to check the incoming parameter value against, if applies
/// @param minValue Min value to check the incoming parameter value against, if applies
/// @param maxValue Max value to check the incoming parameter value against, if applies
/// @param referencePoint A reference value of a parameter to check change magnitudes against;
/// When the reference update period has elapsed since the last reference point update,
/// the reference point is updated to the 'current' value on the next parameter change
/// NB: Should not be changed manually in most cases
/// @param referencePointUpdatePeriod The minimal time period after which the RP can be updated
/// @param referencePointTimestampLU Last timestamp at which the reference point was updated
/// NB: Should not be changed manually in most cases
/// @param minPctChangeDown Min percentage decrease for new values, relative to reference point
/// @param minPctChangeUp Min percentage increase for new values, relative to reference point
/// @param maxPctChangeDown Max percentage decrease for new values, relative to reference point
/// @param maxPctChangeUp Max percentage increase for new values, relative to reference point
/// @param minChange Min absolute changes for new values, relative to reference point
/// @param maxChange Max absolute changes for new values, relative to reference point
struct Policy {
bool enabled;
address admin;
uint40 delay;
uint8 flags;
uint256 exactValue;
uint256 minValue;
uint256 maxValue;
uint256 referencePoint;
uint40 referencePointUpdatePeriod;
uint40 referencePointTimestampLU;
uint16 minPctChangeDown;
uint16 minPctChangeUp;
uint16 maxPctChangeDown;
uint16 maxPctChangeUp;
uint256 minChange;
uint256 maxChange;
}
/// @title Policy manager V3
/// @dev A contract for managing bounds and conditions for mission-critical protocol params
abstract contract PolicyManagerV3 is ACLNonReentrantTrait {
uint256 internal constant CHECK_EXACT_VALUE_FLAG = 1;
uint256 internal constant CHECK_MIN_VALUE_FLAG = 1 << 1;
uint256 internal constant CHECK_MAX_VALUE_FLAG = 1 << 2;
uint256 internal constant CHECK_MIN_CHANGE_FLAG = 1 << 3;
uint256 internal constant CHECK_MAX_CHANGE_FLAG = 1 << 4;
uint256 internal constant CHECK_MIN_PCT_CHANGE_FLAG = 1 << 5;
uint256 internal constant CHECK_MAX_PCT_CHANGE_FLAG = 1 << 6;
/// @dev Mapping from parameter hashes to metaparameters
mapping(bytes32 => Policy) internal _policies;
/// @dev Mapping from a contract address to its group
mapping(address => string) internal _group;
/// @notice Emitted when new policy is set
event SetPolicy(bytes32 indexed policyHash, bool enabled);
/// @notice Emitted when new policy group of the address is set
event SetGroup(address indexed contractAddress, string indexed group);
constructor(address _addressProvider) ACLNonReentrantTrait(_addressProvider) {}
/// @notice Sets the params for a new or existing policy, using policy UID as key
/// @param policyHash A unique identifier for a policy, generally, should be a hash of (GROUP_NAME, PARAMETER_NAME)
/// @param policyParams Policy parameters
function setPolicy(bytes32 policyHash, Policy memory policyParams)
external
configuratorOnly // U:[PM-1]
{
policyParams.enabled = true; // U:[PM-1]
_policies[policyHash] = policyParams; // U:[PM-1]
emit SetPolicy({policyHash: policyHash, enabled: true}); // U:[PM-1]
}
/// @notice Disables the policy which makes all requested checks for the passed policy hash to auto-fail
/// @param policyHash A unique identifier for a policy
function disablePolicy(bytes32 policyHash)
public
configuratorOnly // U:[PM-2]
{
_policies[policyHash].enabled = false; // U:[PM-2]
emit SetPolicy({policyHash: policyHash, enabled: false}); // U:[PM-2]
}
/// @notice Retrieves policy from policy UID
function getPolicy(bytes32 policyHash) external view returns (Policy memory) {
return _policies[policyHash]; // U:[PM-1]
}
/// @notice Sets the policy group of the address
function setGroup(address contractAddress, string calldata group) external configuratorOnly {
_group[contractAddress] = group; // U:[PM-1]
emit SetGroup(contractAddress, group); // U:[PM-1]
}
/// @notice Retrieves the group associated with a contract
function getGroup(address contractAddress) external view returns (string memory) {
return _group[contractAddress]; // U:[PM-1]
}
/// @dev Returns policy transaction delay, with policy retrieved based on contract and parameter name
function _getPolicyDelay(address contractAddress, string memory paramName) internal view returns (uint256) {
bytes32 policyHash = keccak256(abi.encode(_group[contractAddress], paramName));
return _policies[policyHash].delay;
}
/// @dev Returns policy transaction delay, with policy retrieved based on policy UID
function _getPolicyDelay(bytes32 policyHash) internal view returns (uint256) {
return _policies[policyHash].delay;
}
/// @dev Performs parameter checks, with policy retrieved based on contract and parameter name
function _checkPolicy(address contractAddress, string memory paramName, uint256 oldValue, uint256 newValue)
internal
returns (bool)
{
bytes32 policyHash = keccak256(abi.encode(_group[contractAddress], paramName));
return _checkPolicy(policyHash, oldValue, newValue);
}
/// @dev Performs parameter checks, with policy retrieved based on policy UID
function _checkPolicy(bytes32 policyHash, uint256 oldValue, uint256 newValue) internal returns (bool) {
Policy storage policy = _policies[policyHash];
if (!policy.enabled) return false; // U:[PM-2]
if (policy.admin != msg.sender) return false;
uint8 flags = policy.flags;
if (flags & CHECK_EXACT_VALUE_FLAG != 0) {
if (newValue != policy.exactValue) return false; // U:[PM-3]
}
if (flags & CHECK_MIN_VALUE_FLAG != 0) {
if (newValue < policy.minValue) return false; // U:[PM-4]
}
if (flags & CHECK_MAX_VALUE_FLAG != 0) {
if (newValue > policy.maxValue) return false; // U:[PM-5]
}
uint256 referencePoint;
// The policy uses a reference point to gauge relative parameter changes. A reference point
// is a value that is set to current value on updating a parameter. All future values for a period
// will be rubber-banded to the reference point, until the refresh period elapses and it is updated again.
if (
flags
& (CHECK_MIN_CHANGE_FLAG | CHECK_MAX_CHANGE_FLAG | CHECK_MIN_PCT_CHANGE_FLAG | CHECK_MAX_PCT_CHANGE_FLAG)
!= 0
) {
if (block.timestamp > policy.referencePointTimestampLU + policy.referencePointUpdatePeriod) {
referencePoint = oldValue;
policy.referencePoint = referencePoint; // U:[PM-6]
policy.referencePointTimestampLU = uint40(block.timestamp); // U:[PM-6]
} else {
referencePoint = policy.referencePoint;
}
(uint256 diff, bool isIncrease) = calcDiff(newValue, referencePoint);
if (flags & CHECK_MIN_CHANGE_FLAG != 0) {
if (diff < policy.minChange) return false; // U:[PM-7]
}
if (flags & CHECK_MAX_CHANGE_FLAG != 0) {
if (diff > policy.maxChange) return false; // U:[PM-8]
}
if (flags & (CHECK_MIN_PCT_CHANGE_FLAG | CHECK_MAX_PCT_CHANGE_FLAG) != 0) {
uint256 pctDiff = diff * PERCENTAGE_FACTOR / referencePoint;
if (
flags & CHECK_MIN_PCT_CHANGE_FLAG != 0
&& pctDiff < (isIncrease ? policy.minPctChangeUp : policy.minPctChangeDown)
) return false; // U:[PM-9]
if (
flags & CHECK_MAX_PCT_CHANGE_FLAG != 0
&& pctDiff > (isIncrease ? policy.maxPctChangeUp : policy.maxPctChangeDown)
) return false; // U:[PM-10]
}
}
return true;
}
/// @dev Returns the absolute difference between two numbers and the flag whether the first one is greater
function calcDiff(uint256 a, uint256 b) internal pure returns (uint256, bool) {
return a > b ? (a - b, true) : (b - a, false);
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
struct QueuedTransactionData {
bool queued;
address executor;
address target;
uint40 eta;
string signature;
bytes data;
uint256 sanityCheckValue;
bytes sanityCheckCallData;
}
interface IControllerTimelockV3Events {
/// @notice Emitted when the veto admin of the controller is updated
event SetVetoAdmin(address indexed newAdmin);
/// @notice Emitted when a transaction is queued
event QueueTransaction(
bytes32 indexed txHash, address indexed executor, address target, string signature, bytes data, uint40 eta
);
/// @notice Emitted when a transaction is executed
event ExecuteTransaction(bytes32 indexed txHash);
/// @notice Emitted when a transaction is cancelled
event CancelTransaction(bytes32 indexed txHash);
}
/// @title Controller timelock V3 interface
interface IControllerTimelockV3 is IControllerTimelockV3Events, IVersion {
// -------- //
// QUEUEING //
// -------- //
function setExpirationDate(address creditManager, uint40 expirationDate) external;
function setMaxDebtPerBlockMultiplier(address creditManager, uint8 multiplier) external;
function setMinDebtLimit(address creditManager, uint128 minDebt) external;
function setMaxDebtLimit(address creditManager, uint128 maxDebt) external;
function setCreditManagerDebtLimit(address creditManager, uint256 debtLimit) external;
function rampLiquidationThreshold(
address creditManager,
address token,
uint16 liquidationThresholdFinal,
uint40 rampStart,
uint24 rampDuration
) external;
function forbidAdapter(address creditManager, address adapter) external;
function setTotalDebtLimit(address pool, uint256 newLimit) external;
function setTokenLimit(address pool, address token, uint96 limit) external;
function setTokenQuotaIncreaseFee(address pool, address token, uint16 quotaIncreaseFee) external;
function setMinQuotaRate(address pool, address token, uint16 rate) external;
function setMaxQuotaRate(address pool, address token, uint16 rate) external;
function setWithdrawFee(address pool, uint256 newFee) external;
function setLPPriceFeedLimiter(address priceFeed, uint256 lowerBound) external;
function setReservePriceFeedStatus(address priceOracle, address token, bool active) external;
function forbidBoundsUpdate(address priceFeed) external;
// --------- //
// EXECUTION //
// --------- //
function GRACE_PERIOD() external view returns (uint256);
function queuedTransactions(bytes32 txHash)
external
view
returns (
bool queued,
address executor,
address target,
uint40 eta,
string memory signature,
bytes memory data,
uint256 sanityCheckValue,
bytes memory sanityCheckCallData
);
function executeTransaction(bytes32 txHash) external;
function cancelTransaction(bytes32 txHash) external;
// ------------- //
// CONFIGURATION //
// ------------- //
function vetoAdmin() external view returns (address);
function setVetoAdmin(address newAdmin) external;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
uint8 constant BOT_PERMISSIONS_SET_FLAG = 1;
uint8 constant DEFAULT_MAX_ENABLED_TOKENS = 4;
address constant INACTIVE_CREDIT_ACCOUNT_ADDRESS = address(1);
/// @notice Debt management type
/// - `INCREASE_DEBT` borrows additional funds from the pool, updates account's debt and cumulative interest index
/// - `DECREASE_DEBT` repays debt components (quota interest and fees -> base interest and fees -> debt principal)
/// and updates all corresponding state varibles (base interest index, quota interest and fees, debt).
/// When repaying all the debt, ensures that account has no enabled quotas.
enum ManageDebtAction {
INCREASE_DEBT,
DECREASE_DEBT
}
/// @notice Collateral/debt calculation mode
/// - `GENERIC_PARAMS` returns generic data like account debt and cumulative indexes
/// - `DEBT_ONLY` is same as `GENERIC_PARAMS` but includes more detailed debt info, like accrued base/quota
/// interest and fees
/// - `FULL_COLLATERAL_CHECK_LAZY` checks whether account is sufficiently collateralized in a lazy fashion,
/// i.e. it stops iterating over collateral tokens once TWV reaches the desired target.
/// Since it may return underestimated TWV, it's only available for internal use.
/// - `DEBT_COLLATERAL` is same as `DEBT_ONLY` but also returns total value and total LT-weighted value of
/// account's tokens, this mode is used during account liquidation
/// - `DEBT_COLLATERAL_SAFE_PRICES` is same as `DEBT_COLLATERAL` but uses safe prices from price oracle
enum CollateralCalcTask {
GENERIC_PARAMS,
DEBT_ONLY,
FULL_COLLATERAL_CHECK_LAZY,
DEBT_COLLATERAL,
DEBT_COLLATERAL_SAFE_PRICES
}
struct CreditAccountInfo {
uint256 debt;
uint256 cumulativeIndexLastUpdate;
uint128 cumulativeQuotaInterest;
uint128 quotaFees;
uint256 enabledTokensMask;
uint16 flags;
uint64 lastDebtUpdate;
address borrower;
}
struct CollateralDebtData {
uint256 debt;
uint256 cumulativeIndexNow;
uint256 cumulativeIndexLastUpdate;
uint128 cumulativeQuotaInterest;
uint256 accruedInterest;
uint256 accruedFees;
uint256 totalDebtUSD;
uint256 totalValue;
uint256 totalValueUSD;
uint256 twvUSD;
uint256 enabledTokensMask;
uint256 quotedTokensMask;
address[] quotedTokens;
address _poolQuotaKeeper;
}
struct CollateralTokenData {
address token;
uint16 ltInitial;
uint16 ltFinal;
uint40 timestampRampStart;
uint24 rampDuration;
}
struct RevocationPair {
address spender;
address token;
}
interface ICreditManagerV3Events {
/// @notice Emitted when new credit configurator is set
event SetCreditConfigurator(address indexed newConfigurator);
}
/// @title Credit manager V3 interface
interface ICreditManagerV3 is IVersion, ICreditManagerV3Events {
function pool() external view returns (address);
function underlying() external view returns (address);
function creditFacade() external view returns (address);
function creditConfigurator() external view returns (address);
function addressProvider() external view returns (address);
function accountFactory() external view returns (address);
function name() external view returns (string memory);
// ------------------ //
// ACCOUNT MANAGEMENT //
// ------------------ //
function openCreditAccount(address onBehalfOf) external returns (address);
function closeCreditAccount(address creditAccount) external;
function liquidateCreditAccount(
address creditAccount,
CollateralDebtData calldata collateralDebtData,
address to,
bool isExpired
) external returns (uint256 remainingFunds, uint256 loss);
function manageDebt(address creditAccount, uint256 amount, uint256 enabledTokensMask, ManageDebtAction action)
external
returns (uint256 newDebt, uint256 tokensToEnable, uint256 tokensToDisable);
function addCollateral(address payer, address creditAccount, address token, uint256 amount)
external
returns (uint256 tokensToEnable);
function withdrawCollateral(address creditAccount, address token, uint256 amount, address to)
external
returns (uint256 tokensToDisable);
function externalCall(address creditAccount, address target, bytes calldata callData)
external
returns (bytes memory result);
function approveToken(address creditAccount, address token, address spender, uint256 amount) external;
function revokeAdapterAllowances(address creditAccount, RevocationPair[] calldata revocations) external;
// -------- //
// ADAPTERS //
// -------- //
function adapterToContract(address adapter) external view returns (address targetContract);
function contractToAdapter(address targetContract) external view returns (address adapter);
function execute(bytes calldata data) external returns (bytes memory result);
function approveCreditAccount(address token, uint256 amount) external;
function setActiveCreditAccount(address creditAccount) external;
function getActiveCreditAccountOrRevert() external view returns (address creditAccount);
// ----------------- //
// COLLATERAL CHECKS //
// ----------------- //
function priceOracle() external view returns (address);
function fullCollateralCheck(
address creditAccount,
uint256 enabledTokensMask,
uint256[] calldata collateralHints,
uint16 minHealthFactor,
bool useSafePrices
) external returns (uint256 enabledTokensMaskAfter);
function isLiquidatable(address creditAccount, uint16 minHealthFactor) external view returns (bool);
function calcDebtAndCollateral(address creditAccount, CollateralCalcTask task)
external
view
returns (CollateralDebtData memory cdd);
// ------ //
// QUOTAS //
// ------ //
function poolQuotaKeeper() external view returns (address);
function quotedTokensMask() external view returns (uint256);
function updateQuota(address creditAccount, address token, int96 quotaChange, uint96 minQuota, uint96 maxQuota)
external
returns (uint256 tokensToEnable, uint256 tokensToDisable);
// --------------------- //
// CREDIT MANAGER PARAMS //
// --------------------- //
function maxEnabledTokens() external view returns (uint8);
function fees()
external
view
returns (
uint16 feeInterest,
uint16 feeLiquidation,
uint16 liquidationDiscount,
uint16 feeLiquidationExpired,
uint16 liquidationDiscountExpired
);
function collateralTokensCount() external view returns (uint8);
function getTokenMaskOrRevert(address token) external view returns (uint256 tokenMask);
function getTokenByMask(uint256 tokenMask) external view returns (address token);
function liquidationThresholds(address token) external view returns (uint16 lt);
function ltParams(address token)
external
view
returns (uint16 ltInitial, uint16 ltFinal, uint40 timestampRampStart, uint24 rampDuration);
function collateralTokenByMask(uint256 tokenMask)
external
view
returns (address token, uint16 liquidationThreshold);
// ------------ //
// ACCOUNT INFO //
// ------------ //
function creditAccountInfo(address creditAccount)
external
view
returns (
uint256 debt,
uint256 cumulativeIndexLastUpdate,
uint128 cumulativeQuotaInterest,
uint128 quotaFees,
uint256 enabledTokensMask,
uint16 flags,
uint64 lastDebtUpdate,
address borrower
);
function getBorrowerOrRevert(address creditAccount) external view returns (address borrower);
function flagsOf(address creditAccount) external view returns (uint16);
function setFlagFor(address creditAccount, uint16 flag, bool value) external;
function enabledTokensMaskOf(address creditAccount) external view returns (uint256);
function creditAccounts() external view returns (address[] memory);
function creditAccounts(uint256 offset, uint256 limit) external view returns (address[] memory);
function creditAccountsLen() external view returns (uint256);
// ------------- //
// CONFIGURATION //
// ------------- //
function addToken(address token) external;
function setCollateralTokenData(
address token,
uint16 ltInitial,
uint16 ltFinal,
uint40 timestampRampStart,
uint24 rampDuration
) external;
function setFees(
uint16 feeInterest,
uint16 feeLiquidation,
uint16 liquidationDiscount,
uint16 feeLiquidationExpired,
uint16 liquidationDiscountExpired
) external;
function setQuotedMask(uint256 quotedTokensMask) external;
function setMaxEnabledTokens(uint8 maxEnabledTokens) external;
function setContractAllowance(address adapter, address targetContract) external;
function setCreditFacade(address creditFacade) external;
function setPriceOracle(address priceOracle) external;
function setCreditConfigurator(address creditConfigurator) external;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {MultiCall} from "@gearbox-protocol/core-v2/contracts/libraries/MultiCall.sol";
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
import "./ICreditFacadeV3Multicall.sol";
import {AllowanceAction} from "../interfaces/ICreditConfiguratorV3.sol";
/// @notice Debt limits packed into a single slot
/// @param minDebt Minimum debt amount per credit account
/// @param maxDebt Maximum debt amount per credit account
struct DebtLimits {
uint128 minDebt;
uint128 maxDebt;
}
/// @notice Info on bad debt liquidation losses packed into a single slot
/// @param currentCumulativeLoss Current cumulative loss from bad debt liquidations
/// @param maxCumulativeLoss Max cumulative loss incurred before the facade gets paused
struct CumulativeLossParams {
uint128 currentCumulativeLoss;
uint128 maxCumulativeLoss;
}
/// @notice Collateral check params
/// @param collateralHints Optional array of token masks to check first to reduce the amount of computation
/// when known subset of account's collateral tokens covers all the debt
/// @param minHealthFactor Min account's health factor in bps in order not to revert
/// @param enabledTokensMaskAfter Bitmask of account's enabled collateral tokens after the multicall
/// @param revertOnForbiddenTokens Whether to revert on enabled forbidden tokens after the multicall
/// @param useSafePrices Whether to use safe pricing (min of main and reserve feeds) when evaluating collateral
struct FullCheckParams {
uint256[] collateralHints;
uint16 minHealthFactor;
uint256 enabledTokensMaskAfter;
bool revertOnForbiddenTokens;
bool useSafePrices;
}
interface ICreditFacadeV3Events {
/// @notice Emitted when a new credit account is opened
event OpenCreditAccount(
address indexed creditAccount, address indexed onBehalfOf, address indexed caller, uint256 referralCode
);
/// @notice Emitted when account is closed
event CloseCreditAccount(address indexed creditAccount, address indexed borrower);
/// @notice Emitted when account is liquidated
event LiquidateCreditAccount(
address indexed creditAccount, address indexed liquidator, address to, uint256 remainingFunds
);
/// @notice Emitted when account's debt is increased
event IncreaseDebt(address indexed creditAccount, uint256 amount);
/// @notice Emitted when account's debt is decreased
event DecreaseDebt(address indexed creditAccount, uint256 amount);
/// @notice Emitted when collateral is added to account
event AddCollateral(address indexed creditAccount, address indexed token, uint256 amount);
/// @notice Emitted when collateral is withdrawn from account
event WithdrawCollateral(address indexed creditAccount, address indexed token, uint256 amount, address to);
/// @notice Emitted when a multicall is started
event StartMultiCall(address indexed creditAccount, address indexed caller);
/// @notice Emitted when a call from account to an external contract is made during a multicall
event Execute(address indexed creditAccount, address indexed targetContract);
/// @notice Emitted when a multicall is finished
event FinishMultiCall();
}
/// @title Credit facade V3 interface
interface ICreditFacadeV3 is IVersion, ICreditFacadeV3Events {
function creditManager() external view returns (address);
function degenNFT() external view returns (address);
function weth() external view returns (address);
function botList() external view returns (address);
function maxDebtPerBlockMultiplier() external view returns (uint8);
function maxQuotaMultiplier() external view returns (uint256);
function expirable() external view returns (bool);
function expirationDate() external view returns (uint40);
function debtLimits() external view returns (uint128 minDebt, uint128 maxDebt);
function lossParams() external view returns (uint128 currentCumulativeLoss, uint128 maxCumulativeLoss);
function forbiddenTokenMask() external view returns (uint256);
function canLiquidateWhilePaused(address) external view returns (bool);
// ------------------ //
// ACCOUNT MANAGEMENT //
// ------------------ //
function openCreditAccount(address onBehalfOf, MultiCall[] calldata calls, uint256 referralCode)
external
payable
returns (address creditAccount);
function closeCreditAccount(address creditAccount, MultiCall[] calldata calls) external payable;
function liquidateCreditAccount(address creditAccount, address to, MultiCall[] calldata calls) external;
function multicall(address creditAccount, MultiCall[] calldata calls) external payable;
function botMulticall(address creditAccount, MultiCall[] calldata calls) external;
function setBotPermissions(address creditAccount, address bot, uint192 permissions) external;
// ------------- //
// CONFIGURATION //
// ------------- //
function setExpirationDate(uint40 newExpirationDate) external;
function setDebtLimits(uint128 newMinDebt, uint128 newMaxDebt, uint8 newMaxDebtPerBlockMultiplier) external;
function setBotList(address newBotList) external;
function setCumulativeLossParams(uint128 newMaxCumulativeLoss, bool resetCumulativeLoss) external;
function setTokenAllowance(address token, AllowanceAction allowance) external;
function setEmergencyLiquidator(address liquidator, AllowanceAction allowance) external;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
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 {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/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, IPoolV3Events, IERC4626, IERC20Permit {
function addressProvider() external view returns (address);
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;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/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 {
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;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
import {IVotingContractV3} from "./IVotingContractV3.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 IGaugeV3Events, IVotingContractV3, IVersion {
function pool() external view returns (address);
function voter() external view returns (address);
function updateEpoch() external;
function epochLastUpdate() external view returns (uint16);
function getRates(address[] calldata tokens) external view returns (uint16[] memory rates);
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 isTokenAdded(address token) external view returns (bool);
function addQuotaToken(address token, uint16 minRate, uint16 maxRate) external;
function changeQuotaMinRate(address token, uint16 minRate) external;
function changeQuotaMaxRate(address token, uint16 maxRate) external;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Holdings, 2022
pragma solidity ^0.8.10;
import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
interface ILPPriceFeedV2Events {
/// @dev Emits on updating the virtual price bounds
event NewLimiterParams(uint256 lowerBound, uint256 upperBound);
}
interface ILPPriceFeedV2Exceptions {
/// @dev Thrown on returning a value that violates the current bounds
error ValueOutOfRangeException();
/// @dev Thrown on failing sanity checks when setting new bounds
error IncorrectLimitsException();
}
/// @title Interface for LP PriceFeeds with limiter
interface ILPPriceFeedV2 is
AggregatorV3Interface,
ILPPriceFeedV2Events,
ILPPriceFeedV2Exceptions
{
/// @dev Sets the lower and upper bounds for virtual price.
/// @param _lowerBound The new lower bound
/// @notice The upper bound is computed automatically
function setLimiter(uint256 _lowerBound) external;
/// @dev Returns the lower bound
function lowerBound() external view returns (uint256);
/// @dev Returns the upper bound
function upperBound() external view returns (uint256);
/// @dev Returns the pre-defined window between the lower and upper bounds
/// @notice In bp format
function delta() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
// ------- //
// GENERAL //
// ------- //
/// @notice Thrown on attempting to set an important address to zero address
error ZeroAddressException();
/// @notice Thrown when attempting to pass a zero amount to a funding-related operation
error AmountCantBeZeroException();
/// @notice Thrown on incorrect input parameter
error IncorrectParameterException();
/// @notice Thrown when balance is insufficient to perform an operation
error InsufficientBalanceException();
/// @notice Thrown if parameter is out of range
error ValueOutOfRangeException();
/// @notice Thrown when trying to send ETH to a contract that is not allowed to receive ETH directly
error ReceiveIsNotAllowedException();
/// @notice Thrown on attempting to set an EOA as an important contract in the system
error AddressIsNotContractException(address);
/// @notice Thrown on attempting to receive a token that is not a collateral token or was forbidden
error TokenNotAllowedException();
/// @notice Thrown on attempting to add a token that is already in a collateral list
error TokenAlreadyAddedException();
/// @notice Thrown when attempting to use quota-related logic for a token that is not quoted in quota keeper
error TokenIsNotQuotedException();
/// @notice Thrown on attempting to interact with an address that is not a valid target contract
error TargetContractNotAllowedException();
/// @notice Thrown if function is not implemented
error NotImplementedException();
// ------------------ //
// CONTRACTS REGISTER //
// ------------------ //
/// @notice Thrown when an address is expected to be a registered credit manager, but is not
error RegisteredCreditManagerOnlyException();
/// @notice Thrown when an address is expected to be a registered pool, but is not
error RegisteredPoolOnlyException();
// ---------------- //
// ADDRESS PROVIDER //
// ---------------- //
/// @notice Reverts if address key isn't found in address provider
error AddressNotFoundException();
// ----------------- //
// POOL, PQK, GAUGES //
// ----------------- //
/// @notice Thrown by pool-adjacent contracts when a credit manager being connected has a wrong pool address
error IncompatibleCreditManagerException();
/// @notice Thrown when attempting to set an incompatible successor staking contract
error IncompatibleSuccessorException();
/// @notice Thrown when attempting to vote in a non-approved contract
error VotingContractNotAllowedException();
/// @notice Thrown when attempting to unvote more votes than there are
error InsufficientVotesException();
/// @notice Thrown when attempting to borrow more than the second point on a two-point curve
error BorrowingMoreThanU2ForbiddenException();
/// @notice Thrown when a credit manager attempts to borrow more than its limit in the current block, or in general
error CreditManagerCantBorrowException();
/// @notice Thrown when attempting to connect a quota keeper to an incompatible pool
error IncompatiblePoolQuotaKeeperException();
/// @notice Thrown when the quota is outside of min/max bounds
error QuotaIsOutOfBoundsException();
// -------------- //
// CREDIT MANAGER //
// -------------- //
/// @notice Thrown on failing a full collateral check after multicall
error NotEnoughCollateralException();
/// @notice Thrown if an attempt to approve a collateral token to adapter's target contract fails
error AllowanceFailedException();
/// @notice Thrown on attempting to perform an action for a credit account that does not exist
error CreditAccountDoesNotExistException();
/// @notice Thrown on configurator attempting to add more than 255 collateral tokens
error TooManyTokensException();
/// @notice Thrown if more than the maximum number of tokens were enabled on a credit account
error TooManyEnabledTokensException();
/// @notice Thrown when attempting to execute a protocol interaction without active credit account set
error ActiveCreditAccountNotSetException();
/// @notice Thrown when trying to update credit account's debt more than once in the same block
error DebtUpdatedTwiceInOneBlockException();
/// @notice Thrown when trying to repay all debt while having active quotas
error DebtToZeroWithActiveQuotasException();
/// @notice Thrown when a zero-debt account attempts to update quota
error UpdateQuotaOnZeroDebtAccountException();
/// @notice Thrown when attempting to close an account with non-zero debt
error CloseAccountWithNonZeroDebtException();
/// @notice Thrown when value of funds remaining on the account after liquidation is insufficient
error InsufficientRemainingFundsException();
/// @notice Thrown when Credit Facade tries to write over a non-zero active Credit Account
error ActiveCreditAccountOverridenException();
// ------------------- //
// CREDIT CONFIGURATOR //
// ------------------- //
/// @notice Thrown on attempting to use a non-ERC20 contract or an EOA as a token
error IncorrectTokenContractException();
/// @notice Thrown if the newly set LT if zero or greater than the underlying's LT
error IncorrectLiquidationThresholdException();
/// @notice Thrown if borrowing limits are incorrect: minLimit > maxLimit or maxLimit > blockLimit
error IncorrectLimitsException();
/// @notice Thrown if the new expiration date is less than the current expiration date or current timestamp
error IncorrectExpirationDateException();
/// @notice Thrown if a contract returns a wrong credit manager or reverts when trying to retrieve it
error IncompatibleContractException();
/// @notice Thrown if attempting to forbid an adapter that is not registered in the credit manager
error AdapterIsNotRegisteredException();
// ------------- //
// CREDIT FACADE //
// ------------- //
/// @notice Thrown when attempting to perform an action that is forbidden in whitelisted mode
error ForbiddenInWhitelistedModeException();
/// @notice Thrown if credit facade is not expirable, and attempted aciton requires expirability
error NotAllowedWhenNotExpirableException();
/// @notice Thrown if a selector that doesn't match any allowed function is passed to the credit facade in a multicall
error UnknownMethodException();
/// @notice Thrown when trying to close an account with enabled tokens
error CloseAccountWithEnabledTokensException();
/// @notice Thrown if a liquidator tries to liquidate an account with a health factor above 1
error CreditAccountNotLiquidatableException();
/// @notice Thrown if too much new debt was taken within a single block
error BorrowedBlockLimitException();
/// @notice Thrown if the new debt principal for a credit account falls outside of borrowing limits
error BorrowAmountOutOfLimitsException();
/// @notice Thrown if a user attempts to open an account via an expired credit facade
error NotAllowedAfterExpirationException();
/// @notice Thrown if expected balances are attempted to be set twice without performing a slippage check
error ExpectedBalancesAlreadySetException();
/// @notice Thrown if attempting to perform a slippage check when excepted balances are not set
error ExpectedBalancesNotSetException();
/// @notice Thrown if balance of at least one token is less than expected during a slippage check
error BalanceLessThanExpectedException();
/// @notice Thrown when trying to perform an action that is forbidden when credit account has enabled forbidden tokens
error ForbiddenTokensException();
/// @notice Thrown when new forbidden tokens are enabled during the multicall
error ForbiddenTokenEnabledException();
/// @notice Thrown when enabled forbidden token balance is increased during the multicall
error ForbiddenTokenBalanceIncreasedException();
/// @notice Thrown when the remaining token balance is increased during the liquidation
error RemainingTokenBalanceIncreasedException();
/// @notice Thrown if `botMulticall` is called by an address that is not approved by account owner or is forbidden
error NotApprovedBotException();
/// @notice Thrown when attempting to perform a multicall action with no permission for it
error NoPermissionException(uint256 permission);
/// @notice Thrown when attempting to give a bot unexpected permissions
error UnexpectedPermissionsException();
/// @notice Thrown when a custom HF parameter lower than 10000 is passed into the full collateral check
error CustomHealthFactorTooLowException();
/// @notice Thrown when submitted collateral hint is not a valid token mask
error InvalidCollateralHintException();
// ------ //
// ACCESS //
// ------ //
/// @notice Thrown on attempting to call an access restricted function not as credit account owner
error CallerNotCreditAccountOwnerException();
/// @notice Thrown on attempting to call an access restricted function not as configurator
error CallerNotConfiguratorException();
/// @notice Thrown on attempting to call an access-restructed function not as account factory
error CallerNotAccountFactoryException();
/// @notice Thrown on attempting to call an access restricted function not as credit manager
error CallerNotCreditManagerException();
/// @notice Thrown on attempting to call an access restricted function not as credit facade
error CallerNotCreditFacadeException();
/// @notice Thrown on attempting to call an access restricted function not as controller or configurator
error CallerNotControllerException();
/// @notice Thrown on attempting to pause a contract without pausable admin rights
error CallerNotPausableAdminException();
/// @notice Thrown on attempting to unpause a contract without unpausable admin rights
error CallerNotUnpausableAdminException();
/// @notice Thrown on attempting to call an access restricted function not as gauge
error CallerNotGaugeException();
/// @notice Thrown on attempting to call an access restricted function not as quota keeper
error CallerNotPoolQuotaKeeperException();
/// @notice Thrown on attempting to call an access restricted function not as voter
error CallerNotVoterException();
/// @notice Thrown on attempting to call an access restricted function not as allowed adapter
error CallerNotAdapterException();
/// @notice Thrown on attempting to call an access restricted function not as migrator
error CallerNotMigratorException();
/// @notice Thrown when an address that is not the designated executor attempts to execute a transaction
error CallerNotExecutorException();
/// @notice Thrown on attempting to call an access restricted function not as veto admin
error CallerNotVetoAdminException();
// ------------------- //
// CONTROLLER TIMELOCK //
// ------------------- //
/// @notice Thrown when the new parameter values do not satisfy required conditions
error ParameterChecksFailedException();
/// @notice Thrown when attempting to execute a non-queued transaction
error TxNotQueuedException();
/// @notice Thrown when attempting to execute a transaction that is either immature or stale
error TxExecutedOutsideTimeWindowException();
/// @notice Thrown when execution of a transaction fails
error TxExecutionRevertedException();
/// @notice Thrown when the value of a parameter on execution is different from the value on queue
error ParameterChangedAfterQueuedTxException();
// -------- //
// BOT LIST //
// -------- //
/// @notice Thrown when attempting to set non-zero permissions for a forbidden or special bot
error InvalidBotException();
// --------------- //
// ACCOUNT FACTORY //
// --------------- //
/// @notice Thrown when trying to deploy second master credit account for a credit manager
error MasterCreditAccountAlreadyDeployedException();
/// @notice Thrown when trying to rescue funds from a credit account that is currently in use
error CreditAccountIsInUseException();
// ------------ //
// PRICE ORACLE //
// ------------ //
/// @notice Thrown on attempting to set a token price feed to an address that is not a correct price feed
error IncorrectPriceFeedException();
/// @notice Thrown on attempting to interact with a price feed for a token not added to the price oracle
error PriceFeedDoesNotExistException();
/// @notice Thrown when price feed returns incorrect price for a token
error IncorrectPriceException();
/// @notice Thrown when token's price feed becomes stale
error StalePriceException();
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {IACL} from "@gearbox-protocol/core-v2/contracts/interfaces/IACL.sol";
import {
CallerNotControllerException,
CallerNotPausableAdminException,
CallerNotUnpausableAdminException
} from "../interfaces/IExceptions.sol";
import {ACLTrait} from "./ACLTrait.sol";
import {ReentrancyGuardTrait} from "./ReentrancyGuardTrait.sol";
/// @title ACL non-reentrant trait
/// @notice Extended version of `ACLTrait` that implements pausable functionality,
/// reentrancy protection and external controller role
abstract contract ACLNonReentrantTrait is ACLTrait, Pausable, ReentrancyGuardTrait {
/// @notice Emitted when new external controller is set
event NewController(address indexed newController);
/// @notice External controller address
address public controller;
/// @dev Ensures that function caller is external controller or configurator
modifier controllerOnly() {
_ensureCallerIsControllerOrConfigurator();
_;
}
/// @dev Reverts if the caller is not controller or configurator
/// @dev Used to cut contract size on modifiers
function _ensureCallerIsControllerOrConfigurator() internal view {
if (msg.sender != controller && !_isConfigurator({account: msg.sender})) {
revert CallerNotControllerException();
}
}
/// @dev Ensures that function caller has pausable admin role
modifier pausableAdminsOnly() {
_ensureCallerIsPausableAdmin();
_;
}
/// @dev Reverts if the caller is not pausable admin
/// @dev Used to cut contract size on modifiers
function _ensureCallerIsPausableAdmin() internal view {
if (!_isPausableAdmin({account: msg.sender})) {
revert CallerNotPausableAdminException();
}
}
/// @dev Ensures that function caller has unpausable admin role
modifier unpausableAdminsOnly() {
_ensureCallerIsUnpausableAdmin();
_;
}
/// @dev Reverts if the caller is not unpausable admin
/// @dev Used to cut contract size on modifiers
function _ensureCallerIsUnpausableAdmin() internal view {
if (!_isUnpausableAdmin({account: msg.sender})) {
revert CallerNotUnpausableAdminException();
}
}
/// @notice Constructor
/// @param addressProvider Address provider contract address
constructor(address addressProvider) ACLTrait(addressProvider) {
controller = IACL(acl).owner();
}
/// @notice Pauses contract, can only be called by an account with pausable admin role
function pause() external virtual pausableAdminsOnly {
_pause();
}
/// @notice Unpauses contract, can only be called by an account with unpausable admin role
function unpause() external virtual unpausableAdminsOnly {
_unpause();
}
/// @notice Sets new external controller, can only be called by configurator
function setController(address newController) external configuratorOnly {
if (controller == newController) return;
controller = newController;
emit NewController(newController);
}
/// @dev Checks whether given account has pausable admin role
function _isPausableAdmin(address account) internal view returns (bool) {
return IACL(acl).isPausableAdmin(account);
}
/// @dev Checks whether given account has unpausable admin role
function _isUnpausableAdmin(address account) internal view returns (bool) {
return IACL(acl).isUnpausableAdmin(account);
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Holdings, 2022
pragma solidity ^0.8.10;
// Denominations
uint256 constant WAD = 1e18;
uint256 constant RAY = 1e27;
uint16 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals
// 25% of type(uint256).max
uint256 constant ALLOWANCE_THRESHOLD = type(uint96).max >> 3;
// FEE = 50%
uint16 constant DEFAULT_FEE_INTEREST = 50_00; // 50%
// LIQUIDATION_FEE 1.5%
uint16 constant DEFAULT_FEE_LIQUIDATION = 1_50; // 1.5%
// LIQUIDATION PREMIUM 4%
uint16 constant DEFAULT_LIQUIDATION_PREMIUM = 4_00; // 4%
// LIQUIDATION_FEE_EXPIRED 2%
uint16 constant DEFAULT_FEE_LIQUIDATION_EXPIRED = 1_00; // 2%
// LIQUIDATION PREMIUM EXPIRED 2%
uint16 constant DEFAULT_LIQUIDATION_PREMIUM_EXPIRED = 2_00; // 2%
// DEFAULT PROPORTION OF MAX BORROWED PER BLOCK TO MAX BORROWED PER ACCOUNT
uint16 constant DEFAULT_LIMIT_PER_BLOCK_MULTIPLIER = 2;
// Seconds in a year
uint256 constant SECONDS_PER_YEAR = 365 days;
uint256 constant SECONDS_PER_ONE_AND_HALF_YEAR = (SECONDS_PER_YEAR * 3) / 2;
// OPERATIONS
// Leverage decimals - 100 is equal to 2x leverage (100% * collateral amount + 100% * borrowed amount)
uint8 constant LEVERAGE_DECIMALS = 100;
// Maximum withdraw fee for pool in PERCENTAGE_FACTOR format
uint8 constant MAX_WITHDRAW_FEE = 100;
uint256 constant EXACT_INPUT = 1;
uint256 constant EXACT_OUTPUT = 2;
address constant UNIVERSAL_CONTRACT = 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC;
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Holdings, 2022
pragma solidity ^0.8.10;
/// @title Version interface
/// @notice Defines contract version
interface IVersion {
/// @notice Contract version
function version() external view returns (uint256);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
struct MultiCall {
address target;
bytes callData;
}
library MultiCallOps {
function copyMulticall(MultiCall memory call)
internal
pure
returns (MultiCall memory)
{
return MultiCall({ target: call.target, callData: call.callData });
}
function trim(MultiCall[] memory calls)
internal
pure
returns (MultiCall[] memory trimmed)
{
uint256 len = calls.length;
if (len == 0) return calls;
uint256 foundLen;
while (calls[foundLen].target != address(0)) {
unchecked {
++foundLen;
if (foundLen == len) return calls;
}
}
if (foundLen > 0) return copy(calls, foundLen);
}
function copy(MultiCall[] memory calls, uint256 len)
internal
pure
returns (MultiCall[] memory res)
{
res = new MultiCall[](len);
for (uint256 i; i < len; ) {
res[i] = copyMulticall(calls[i]);
unchecked {
++i;
}
}
}
function clone(MultiCall[] memory calls)
internal
pure
returns (MultiCall[] memory res)
{
return copy(calls, calls.length);
}
function append(MultiCall[] memory calls, MultiCall memory newCall)
internal
pure
returns (MultiCall[] memory res)
{
uint256 len = calls.length;
res = new MultiCall[](len + 1);
for (uint256 i; i < len; ) {
res[i] = copyMulticall(calls[i]);
unchecked {
++i;
}
}
res[len] = copyMulticall(newCall);
}
function prepend(MultiCall[] memory calls, MultiCall memory newCall)
internal
pure
returns (MultiCall[] memory res)
{
uint256 len = calls.length;
res = new MultiCall[](len + 1);
res[0] = copyMulticall(newCall);
for (uint256 i = 1; i < len + 1; ) {
res[i] = copyMulticall(calls[i]);
unchecked {
++i;
}
}
}
function concat(MultiCall[] memory calls1, MultiCall[] memory calls2)
internal
pure
returns (MultiCall[] memory res)
{
uint256 len1 = calls1.length;
uint256 lenTotal = len1 + calls2.length;
if (lenTotal == calls1.length) return clone(calls1);
if (lenTotal == calls2.length) return clone(calls2);
res = new MultiCall[](lenTotal);
for (uint256 i; i < lenTotal; ) {
res[i] = (i < len1)
? copyMulticall(calls1[i])
: copyMulticall(calls2[i - len1]);
unchecked {
++i;
}
}
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {BalanceDelta} from "../libraries/BalancesLogic.sol";
import {RevocationPair} from "./ICreditManagerV3.sol";
// ----------- //
// PERMISSIONS //
// ----------- //
uint192 constant ADD_COLLATERAL_PERMISSION = 1;
uint192 constant INCREASE_DEBT_PERMISSION = 1 << 1;
uint192 constant DECREASE_DEBT_PERMISSION = 1 << 2;
uint192 constant ENABLE_TOKEN_PERMISSION = 1 << 3;
uint192 constant DISABLE_TOKEN_PERMISSION = 1 << 4;
uint192 constant WITHDRAW_COLLATERAL_PERMISSION = 1 << 5;
uint192 constant UPDATE_QUOTA_PERMISSION = 1 << 6;
uint192 constant REVOKE_ALLOWANCES_PERMISSION = 1 << 7;
uint192 constant EXTERNAL_CALLS_PERMISSION = 1 << 16;
uint256 constant ALL_CREDIT_FACADE_CALLS_PERMISSION = ADD_COLLATERAL_PERMISSION | WITHDRAW_COLLATERAL_PERMISSION
| INCREASE_DEBT_PERMISSION | DECREASE_DEBT_PERMISSION | ENABLE_TOKEN_PERMISSION | DISABLE_TOKEN_PERMISSION
| UPDATE_QUOTA_PERMISSION | REVOKE_ALLOWANCES_PERMISSION;
uint256 constant ALL_PERMISSIONS = ALL_CREDIT_FACADE_CALLS_PERMISSION | EXTERNAL_CALLS_PERMISSION;
// ----- //
// FLAGS //
// ----- //
/// @dev Indicates that there are enabled forbidden tokens on the account before multicall
uint256 constant FORBIDDEN_TOKENS_BEFORE_CALLS = 1 << 192;
/// @dev Indicates that external calls from credit account to adapters were made during multicall,
/// set to true on the first call to the adapter
uint256 constant EXTERNAL_CONTRACT_WAS_CALLED = 1 << 193;
/// @title Credit facade V3 multicall interface
/// @dev Unless specified otherwise, all these methods are only available in `openCreditAccount`,
/// `closeCreditAccount`, `multicall`, and, with account owner's permission, `botMulticall`
interface ICreditFacadeV3Multicall {
/// @notice Updates the price for a token with on-demand updatable price feed
/// @param token Token to push the price update for
/// @param reserve Whether to update reserve price feed or main price feed
/// @param data Data to call `updatePrice` with
/// @dev Calls of this type must be placed before all other calls in the multicall not to revert
/// @dev This method is available in all kinds of multicalls
function onDemandPriceUpdate(address token, bool reserve, bytes calldata data) external;
/// @notice Stores expected token balances (current balance + delta) after operations for a slippage check.
/// Normally, a check is performed automatically at the end of the multicall, but more fine-grained
/// behavior can be achieved by placing `storeExpectedBalances` and `compareBalances` where needed.
/// @param balanceDeltas Array of (token, minBalanceDelta) pairs, deltas are allowed to be negative
/// @dev Reverts if expected balances are already set
/// @dev This method is available in all kinds of multicalls
function storeExpectedBalances(BalanceDelta[] calldata balanceDeltas) external;
/// @notice Performs a slippage check ensuring that current token balances are greater than saved expected ones
/// @dev Resets stored expected balances
/// @dev Reverts if expected balances are not stored
/// @dev This method is available in all kinds of multicalls
function compareBalances() external;
/// @notice Adds collateral to account
/// @param token Token to add
/// @param amount Amount to add
/// @dev Requires token approval from caller to the credit manager
/// @dev This method can also be called during liquidation
function addCollateral(address token, uint256 amount) external;
/// @notice Adds collateral to account using signed EIP-2612 permit message
/// @param token Token to add
/// @param amount Amount to add
/// @param deadline Permit deadline
/// @dev `v`, `r`, `s` must be a valid signature of the permit message from caller to the credit manager
/// @dev This method can also be called during liquidation
function addCollateralWithPermit(address token, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
external;
/// @notice Increases account's debt
/// @param amount Underlying amount to borrow
/// @dev Increasing debt is prohibited when closing an account
/// @dev Increasing debt is prohibited if it was previously updated in the same block
/// @dev The resulting debt amount must be within allowed range
/// @dev Increasing debt is prohibited if there are forbidden tokens enabled as collateral on the account
/// @dev After debt increase, total amount borrowed by the credit manager in the current block must not exceed
/// the limit defined in the facade
function increaseDebt(uint256 amount) external;
/// @notice Decreases account's debt
/// @param amount Underlying amount to repay, value above account's total debt indicates full repayment
/// @dev Decreasing debt is prohibited when opening an account
/// @dev Decreasing debt is prohibited if it was previously updated in the same block
/// @dev The resulting debt amount must be within allowed range or zero
/// @dev Full repayment brings account into a special mode that skips collateral checks and thus requires
/// an account to have no potential debt sources, e.g., all quotas must be disabled
function decreaseDebt(uint256 amount) external;
/// @notice Updates account's quota for a token
/// @param token Token to update the quota for
/// @param quotaChange Desired quota change in underlying token units (`type(int96).min` to disable quota)
/// @param minQuota Minimum resulting account's quota for token required not to revert
/// @dev Enables token as collateral if quota is increased from zero, disables if decreased to zero
/// @dev Quota increase is prohibited if there are forbidden tokens enabled as collateral on the account
/// @dev Quota update is prohibited if account has zero debt
/// @dev Resulting account's quota for token must not exceed the limit defined in the facade
function updateQuota(address token, int96 quotaChange, uint96 minQuota) external;
/// @notice Withdraws collateral from account
/// @param token Token to withdraw
/// @param amount Amount to withdraw, `type(uint256).max` to withdraw all balance
/// @param to Token recipient
/// @dev This method can also be called during liquidation
/// @dev Withdrawals are prohibited in multicalls if there are forbidden tokens enabled as collateral on the account
/// @dev Withdrawals activate safe pricing (min of main and reserve feeds) in collateral check
function withdrawCollateral(address token, uint256 amount, address to) external;
/// @notice Sets advanced collateral check parameters
/// @param collateralHints Optional array of token masks to check first to reduce the amount of computation
/// when known subset of account's collateral tokens covers all the debt
/// @param minHealthFactor Min account's health factor in bps in order not to revert, must be at least 10000
function setFullCheckParams(uint256[] calldata collateralHints, uint16 minHealthFactor) external;
/// @notice Enables token as account's collateral, which makes it count towards account's total value
/// @param token Token to enable as collateral
/// @dev Enabling forbidden tokens is prohibited
/// @dev Quoted tokens can only be enabled via `updateQuota`, this method is no-op for them
function enableToken(address token) external;
/// @notice Disables token as account's collateral
/// @param token Token to disable as collateral
/// @dev Quoted tokens can only be disabled via `updateQuota`, this method is no-op for them
function disableToken(address token) external;
/// @notice Revokes account's allowances for specified spender/token pairs
/// @param revocations Array of spender/token pairs
/// @dev Exists primarily to allow users to revoke allowances on accounts from old account factory on mainnet
function revokeAdapterAllowances(RevocationPair[] calldata revocations) external;
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
enum AllowanceAction {
FORBID,
ALLOW
}
/// @notice Struct with credit manager configuration parameters
/// @param minDebt Minimum debt amount per account
/// @param maxDebt Maximum debt amount per account
/// @param degenNFT Whether to apply Degen NFT whitelist logic
/// @param expirable Whether facade must be expirable
/// @param name Credit manager name
struct CreditManagerOpts {
uint128 minDebt;
uint128 maxDebt;
address degenNFT;
bool expirable;
string name;
}
interface ICreditConfiguratorV3Events {
// ------ //
// TOKENS //
// ------ //
/// @notice Emitted when a token is made recognizable as collateral in the credit manager
event AddCollateralToken(address indexed token);
/// @notice Emitted when a new collateral token liquidation threshold is set
event SetTokenLiquidationThreshold(address indexed token, uint16 liquidationThreshold);
/// @notice Emitted when a collateral token liquidation threshold ramping is scheduled
event ScheduleTokenLiquidationThresholdRamp(
address indexed token,
uint16 liquidationThresholdInitial,
uint16 liquidationThresholdFinal,
uint40 timestampRampStart,
uint40 timestampRampEnd
);
/// @notice Emitted when a collateral token is forbidden
event ForbidToken(address indexed token);
/// @notice Emitted when a previously forbidden collateral token is allowed
event AllowToken(address indexed token);
/// @notice Emitted when a token is made quoted
event QuoteToken(address indexed token);
// -------- //
// ADAPTERS //
// -------- //
/// @notice Emitted when a new adapter and its target contract are allowed in the credit manager
event AllowAdapter(address indexed targetContract, address indexed adapter);
/// @notice Emitted when adapter and its target contract are forbidden in the credit manager
event ForbidAdapter(address indexed targetContract, address indexed adapter);
// -------------- //
// CREDIT MANAGER //
// -------------- //
/// @notice Emitted when a new maximum number of enabled tokens is set in the credit manager
event SetMaxEnabledTokens(uint8 maxEnabledTokens);
/// @notice Emitted when new fee parameters are set in the credit manager
event UpdateFees(
uint16 feeInterest,
uint16 feeLiquidation,
uint16 liquidationPremium,
uint16 feeLiquidationExpired,
uint16 liquidationPremiumExpired
);
// -------- //
// UPGRADES //
// -------- //
/// @notice Emitted when a new price oracle is set in the credit manager
event SetPriceOracle(address indexed priceOracle);
/// @notice Emitted when a new bot list is set in the credit facade
event SetBotList(address indexed botList);
/// @notice Emitted when a new facade is connected to the credit manager
event SetCreditFacade(address indexed creditFacade);
/// @notice Emitted when credit manager's configurator contract is upgraded
event CreditConfiguratorUpgraded(address indexed creditConfigurator);
// ------------- //
// CREDIT FACADE //
// ------------- //
/// @notice Emitted when new debt principal limits are set
event SetBorrowingLimits(uint256 minDebt, uint256 maxDebt);
/// @notice Emitted when a new max debt per block multiplier is set
event SetMaxDebtPerBlockMultiplier(uint8 maxDebtPerBlockMultiplier);
/// @notice Emitted when a new max cumulative loss is set
event SetMaxCumulativeLoss(uint128 maxCumulativeLoss);
/// @notice Emitted when cumulative loss is reset to zero in the credit facade
event ResetCumulativeLoss();
/// @notice Emitted when a new expiration timestamp is set in the credit facade
event SetExpirationDate(uint40 expirationDate);
/// @notice Emitted when an address is added to the list of emergency liquidators
event AddEmergencyLiquidator(address indexed liquidator);
/// @notice Emitted when an address is removed from the list of emergency liquidators
event RemoveEmergencyLiquidator(address indexed liquidator);
}
/// @title Credit configurator V3 interface
interface ICreditConfiguratorV3 is IVersion, ICreditConfiguratorV3Events {
function addressProvider() external view returns (address);
function creditManager() external view returns (address);
function creditFacade() external view returns (address);
function underlying() external view returns (address);
// ------ //
// TOKENS //
// ------ //
function addCollateralToken(address token, uint16 liquidationThreshold) external;
function setLiquidationThreshold(address token, uint16 liquidationThreshold) external;
function rampLiquidationThreshold(
address token,
uint16 liquidationThresholdFinal,
uint40 rampStart,
uint24 rampDuration
) external;
function forbidToken(address token) external;
function allowToken(address token) external;
function makeTokenQuoted(address token) external;
// -------- //
// ADAPTERS //
// -------- //
function allowedAdapters() external view returns (address[] memory);
function allowAdapter(address adapter) external;
function forbidAdapter(address adapter) external;
// -------------- //
// CREDIT MANAGER //
// -------------- //
function setFees(
uint16 feeInterest,
uint16 feeLiquidation,
uint16 liquidationPremium,
uint16 feeLiquidationExpired,
uint16 liquidationPremiumExpired
) external;
function setMaxEnabledTokens(uint8 newMaxEnabledTokens) external;
// -------- //
// UPGRADES //
// -------- //
function setPriceOracle(uint256 newVersion) external;
function setBotList(uint256 newVersion) external;
function setCreditFacade(address newCreditFacade, bool migrateParams) external;
function upgradeCreditConfigurator(address newCreditConfigurator) external;
// ------------- //
// CREDIT FACADE //
// ------------- //
function setMinDebtLimit(uint128 newMinDebt) external;
function setMaxDebtLimit(uint128 newMaxDebt) external;
function setMaxDebtPerBlockMultiplier(uint8 newMaxDebtLimitPerBlockMultiplier) external;
function forbidBorrowing() external;
function setMaxCumulativeLoss(uint128 newMaxCumulativeLoss) external;
function resetCumulativeLoss() external;
function setExpirationDate(uint40 newExpirationDate) external;
function emergencyLiquidators() external view returns (address[] memory);
function addEmergencyLiquidator(address liquidator) external;
function removeEmergencyLiquidator(address liquidator) external;
}
// 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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
*/
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].
*/
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);
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
interface IVotingContractV3 {
function vote(address user, uint96 votes, bytes calldata extraData) external;
function unvote(address user, uint96 votes, bytes calldata extraData) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Holdings, 2022
pragma solidity ^0.8.10;
import { IVersion } from "./IVersion.sol";
interface IACLExceptions {
/// @dev Thrown when attempting to delete an address from a set that is not a pausable admin
error AddressNotPausableAdminException(address addr);
/// @dev Thrown when attempting to delete an address from a set that is not a unpausable admin
error AddressNotUnpausableAdminException(address addr);
}
interface IACLEvents {
/// @dev Emits when a new admin is added that can pause contracts
event PausableAdminAdded(address indexed newAdmin);
/// @dev Emits when a Pausable admin is removed
event PausableAdminRemoved(address indexed admin);
/// @dev Emits when a new admin is added that can unpause contracts
event UnpausableAdminAdded(address indexed newAdmin);
/// @dev Emits when an Unpausable admin is removed
event UnpausableAdminRemoved(address indexed admin);
}
/// @title ACL interface
interface IACL is IACLEvents, IACLExceptions, IVersion {
/// @dev Returns true if the address is a pausable admin and false if not
/// @param addr Address to check
function isPausableAdmin(address addr) external view returns (bool);
/// @dev Returns true if the address is unpausable admin and false if not
/// @param addr Address to check
function isUnpausableAdmin(address addr) external view returns (bool);
/// @dev Returns true if an address has configurator rights
/// @param account Address to check
function isConfigurator(address account) external view returns (bool);
/// @dev Returns address of configurator
function owner() external view returns (address);
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IACL} from "@gearbox-protocol/core-v2/contracts/interfaces/IACL.sol";
import {AP_ACL, IAddressProviderV3, NO_VERSION_CONTROL} from "../interfaces/IAddressProviderV3.sol";
import {CallerNotConfiguratorException} from "../interfaces/IExceptions.sol";
import {SanityCheckTrait} from "./SanityCheckTrait.sol";
/// @title ACL trait
/// @notice Utility class for ACL (access-control list) consumers
abstract contract ACLTrait is SanityCheckTrait {
/// @notice ACL contract address
address public immutable acl;
/// @notice Constructor
/// @param addressProvider Address provider contract address
constructor(address addressProvider) nonZeroAddress(addressProvider) {
acl = IAddressProviderV3(addressProvider).getAddressOrRevert(AP_ACL, NO_VERSION_CONTROL);
}
/// @dev Ensures that function caller has configurator role
modifier configuratorOnly() {
_ensureCallerIsConfigurator();
_;
}
/// @dev Reverts if the caller is not the configurator
/// @dev Used to cut contract size on modifiers
function _ensureCallerIsConfigurator() internal view {
if (!_isConfigurator({account: msg.sender})) {
revert CallerNotConfiguratorException();
}
}
/// @dev Checks whether given account has configurator role
function _isConfigurator(address account) internal view returns (bool) {
return IACL(acl).isConfigurator(account);
}
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
uint8 constant NOT_ENTERED = 1;
uint8 constant ENTERED = 2;
/// @title Reentrancy guard trait
/// @notice Same as OpenZeppelin's `ReentrancyGuard` but only uses 1 byte of storage instead of 32
abstract contract ReentrancyGuardTrait {
uint8 internal _reentrancyStatus = NOT_ENTERED;
/// @dev Prevents a contract from calling itself, directly or indirectly.
/// Calling a `nonReentrant` function from another `nonReentrant`
/// function is not supported. It is possible to prevent this from happening
/// by making the `nonReentrant` function external, and making it call a
/// `private` function that does the actual work.
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
_ensureNotEntered();
// Any calls to nonReentrant after this point will fail
_reentrancyStatus = ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_reentrancyStatus = NOT_ENTERED;
}
/// @dev Reverts if the contract is currently entered
/// @dev Used to cut contract size on modifiers
function _ensureNotEntered() internal view {
require(_reentrancyStatus != ENTERED, "ReentrancyGuard: reentrant call");
}
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {SafeERC20} from "@1inch/solidity-utils/contracts/libraries/SafeERC20.sol";
import {BitMask} from "./BitMask.sol";
import {Balance} from "@gearbox-protocol/core-v2/contracts/libraries/Balances.sol";
struct BalanceWithMask {
address token;
uint256 tokenMask;
uint256 balance;
}
struct BalanceDelta {
address token;
int256 amount;
}
enum Comparison {
GREATER,
LESS
}
/// @title Balances logic library
/// @notice Implements functions for before-and-after balance comparisons
library BalancesLogic {
using BitMask for uint256;
using SafeCast for int256;
using SafeCast for uint256;
using SafeERC20 for IERC20;
/// @dev Compares current `token` balance with `value`
/// @param token Token to check balance for
/// @param value Value to compare current token balance with
/// @param comparison Whether current balance must be greater/less than or equal to `value`
function checkBalance(address creditAccount, address token, uint256 value, Comparison comparison)
internal
view
returns (bool)
{
uint256 current = IERC20(token).safeBalanceOf(creditAccount);
return (comparison == Comparison.GREATER && current >= value)
|| (comparison == Comparison.LESS && current <= value); // U:[BLL-1]
}
/// @dev Returns an array of expected token balances after operations
/// @param creditAccount Credit account to compute balances for
/// @param deltas Array of expected token balance changes
function storeBalances(address creditAccount, BalanceDelta[] memory deltas)
internal
view
returns (Balance[] memory balances)
{
uint256 len = deltas.length;
balances = new Balance[](len); // U:[BLL-2]
for (uint256 i = 0; i < len;) {
int256 balance = IERC20(deltas[i].token).safeBalanceOf(creditAccount).toInt256();
balances[i] = Balance({token: deltas[i].token, balance: (balance + deltas[i].amount).toUint256()}); // U:[BLL-2]
unchecked {
++i;
}
}
}
/// @dev Compares current balances with the previously stored ones
/// @param creditAccount Credit account to compare balances for
/// @param balances Array of previously stored balances
/// @param comparison Whether current balances must be greater/less than or equal to stored ones
/// @return success True if condition specified by `comparison` holds for all tokens, false otherwise
function compareBalances(address creditAccount, Balance[] memory balances, Comparison comparison)
internal
view
returns (bool success)
{
uint256 len = balances.length;
unchecked {
for (uint256 i = 0; i < len; ++i) {
if (!BalancesLogic.checkBalance(creditAccount, balances[i].token, balances[i].balance, comparison)) {
return false; // U:[BLL-3]
}
}
}
return true; // U:[BLL-3]
}
/// @dev Returns balances of specified tokens on the credit account
/// @param creditAccount Credit account to compute balances for
/// @param tokensMask Bit mask of tokens to compute balances for
/// @param getTokenByMaskFn Function that returns token's address by its mask
function storeBalances(
address creditAccount,
uint256 tokensMask,
function (uint256) view returns (address) getTokenByMaskFn
) internal view returns (BalanceWithMask[] memory balances) {
if (tokensMask == 0) return balances;
balances = new BalanceWithMask[](tokensMask.calcEnabledTokens()); // U:[BLL-4]
unchecked {
uint256 i;
while (tokensMask != 0) {
uint256 tokenMask = tokensMask & uint256(-int256(tokensMask));
tokensMask ^= tokenMask;
address token = getTokenByMaskFn(tokenMask);
balances[i] = BalanceWithMask({
token: token,
tokenMask: tokenMask,
balance: IERC20(token).safeBalanceOf(creditAccount)
}); // U:[BLL-4]
++i;
}
}
}
/// @dev Compares current balances of specified tokens with the previously stored ones
/// @param creditAccount Credit account to compare balances for
/// @param tokensMask Bit mask of tokens to compare balances for
/// @param balances Array of previously stored balances
/// @param comparison Whether current balances must be greater/less than or equal to stored ones
/// @return success True if condition specified by `comparison` holds for all tokens, false otherwise
function compareBalances(
address creditAccount,
uint256 tokensMask,
BalanceWithMask[] memory balances,
Comparison comparison
) internal view returns (bool) {
if (tokensMask == 0) return true;
unchecked {
uint256 len = balances.length;
for (uint256 i; i < len; ++i) {
if (tokensMask & balances[i].tokenMask != 0) {
if (!BalancesLogic.checkBalance(creditAccount, balances[i].token, balances[i].balance, comparison))
{
return false; // U:[BLL-5]
}
}
}
}
return true; // U:[BLL-5]
}
}
// 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);
}
// 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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (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;
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IVersion} from "@gearbox-protocol/core-v2/contracts/interfaces/IVersion.sol";
uint256 constant NO_VERSION_CONTROL = 0;
bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER";
bytes32 constant AP_ACL = "ACL";
bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE";
bytes32 constant AP_ACCOUNT_FACTORY = "ACCOUNT_FACTORY";
bytes32 constant AP_DATA_COMPRESSOR = "DATA_COMPRESSOR";
bytes32 constant AP_TREASURY = "TREASURY";
bytes32 constant AP_GEAR_TOKEN = "GEAR_TOKEN";
bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN";
bytes32 constant AP_WETH_GATEWAY = "WETH_GATEWAY";
bytes32 constant AP_ROUTER = "ROUTER";
bytes32 constant AP_BOT_LIST = "BOT_LIST";
bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING";
bytes32 constant AP_ZAPPER_REGISTER = "ZAPPER_REGISTER";
interface IAddressProviderV3Events {
/// @notice Emitted when an address is set for a contract key
event SetAddress(bytes32 indexed key, address indexed value, uint256 indexed version);
}
/// @title Address provider V3 interface
interface IAddressProviderV3 is IAddressProviderV3Events, IVersion {
function addresses(bytes32 key, uint256 _version) external view returns (address);
function getAddressOrRevert(bytes32 key, uint256 _version) external view returns (address result);
function setAddress(bytes32 key, address value, bool saveVersion) external;
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {ZeroAddressException} from "../interfaces/IExceptions.sol";
/// @title Sanity check trait
abstract contract SanityCheckTrait {
/// @dev Ensures that passed address is non-zero
modifier nonZeroAddress(address addr) {
_revertIfZeroAddress(addr);
_;
}
/// @dev Reverts if address is zero
function _revertIfZeroAddress(address addr) private pure {
if (addr == address(0)) revert ZeroAddressException();
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "../interfaces/IDaiLikePermit.sol";
import "../interfaces/IPermit2.sol";
import "../interfaces/IWETH.sol";
import "../libraries/RevertReasonForwarder.sol";
/// @title Implements efficient safe methods for ERC20 interface.
library SafeERC20 {
error SafeTransferFailed();
error SafeTransferFromFailed();
error ForceApproveFailed();
error SafeIncreaseAllowanceFailed();
error SafeDecreaseAllowanceFailed();
error SafePermitBadLength();
error Permit2TransferAmountTooHigh();
address private constant _PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
bytes4 private constant _PERMIT_LENGTH_ERROR = 0x68275857; // SafePermitBadLength.selector
uint256 private constant _RAW_CALL_GAS_LIMIT = 5000;
function safeBalanceOf(
IERC20 token,
address account
) internal view returns(uint256 tokenBalance) {
bytes4 selector = IERC20.balanceOf.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0x00, selector)
mstore(0x04, account)
let success := staticcall(gas(), token, 0x00, 0x24, 0x00, 0x20)
tokenBalance := mload(0)
if or(iszero(success), lt(returndatasize(), 0x20)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
/// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
function safeTransferFromUniversal(
IERC20 token,
address from,
address to,
uint256 amount,
bool permit2
) internal {
if (permit2) {
safeTransferFromPermit2(token, from, to, amount);
} else {
safeTransferFrom(token, from, to, amount);
}
}
/// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
bytes4 selector = token.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
success := call(gas(), token, 0, data, 100, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
if (!success) revert SafeTransferFromFailed();
}
/// @dev Permit2 version of safeTransferFrom above.
function safeTransferFromPermit2(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
if (amount > type(uint160).max) revert Permit2TransferAmountTooHigh();
bytes4 selector = IPermit2.transferFrom.selector;
bool success;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), from)
mstore(add(data, 0x24), to)
mstore(add(data, 0x44), amount)
mstore(add(data, 0x64), token)
success := call(gas(), _PERMIT2, 0, data, 0x84, 0x0, 0x0)
if success {
success := gt(extcodesize(_PERMIT2), 0)
}
}
if (!success) revert SafeTransferFromFailed();
}
/// @dev Ensures method do not revert or return boolean `true`, admits call to non-smart-contract.
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
if (!_makeCall(token, token.transfer.selector, to, value)) {
revert SafeTransferFailed();
}
}
/// @dev If `approve(from, to, amount)` fails, try to `approve(from, to, 0)` before retry.
function forceApprove(
IERC20 token,
address spender,
uint256 value
) internal {
if (!_makeCall(token, token.approve.selector, spender, value)) {
if (
!_makeCall(token, token.approve.selector, spender, 0) ||
!_makeCall(token, token.approve.selector, spender, value)
) {
revert ForceApproveFailed();
}
}
}
/// @dev Allowance increase with safe math check.
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > type(uint256).max - allowance) revert SafeIncreaseAllowanceFailed();
forceApprove(token, spender, allowance + value);
}
/// @dev Allowance decrease with safe math check.
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 allowance = token.allowance(address(this), spender);
if (value > allowance) revert SafeDecreaseAllowanceFailed();
forceApprove(token, spender, allowance - value);
}
function safePermit(IERC20 token, bytes calldata permit) internal {
if (!tryPermit(token, msg.sender, address(this), permit)) RevertReasonForwarder.reRevert();
}
function safePermit(IERC20 token, address owner, address spender, bytes calldata permit) internal {
if (!tryPermit(token, owner, spender, permit)) RevertReasonForwarder.reRevert();
}
function tryPermit(IERC20 token, bytes calldata permit) internal returns(bool success) {
return tryPermit(token, msg.sender, address(this), permit);
}
function tryPermit(IERC20 token, address owner, address spender, bytes calldata permit) internal returns(bool success) {
bytes4 permitSelector = IERC20Permit.permit.selector;
bytes4 daiPermitSelector = IDaiLikePermit.permit.selector;
bytes4 permit2Selector = IPermit2.permit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
switch permit.length
case 100 {
mstore(ptr, permitSelector)
mstore(add(ptr, 0x04), owner)
mstore(add(ptr, 0x24), spender)
// Compact IERC20Permit.permit(uint256 value, uint32 deadline, uint256 r, uint256 vs)
{ // stack too deep
let deadline := shr(224, calldataload(add(permit.offset, 0x20)))
let vs := calldataload(add(permit.offset, 0x44))
calldatacopy(add(ptr, 0x44), permit.offset, 0x20) // value
mstore(add(ptr, 0x64), sub(deadline, 1))
mstore(add(ptr, 0x84), add(27, shr(255, vs)))
calldatacopy(add(ptr, 0xa4), add(permit.offset, 0x24), 0x20) // r
mstore(add(ptr, 0xc4), shr(1, shl(1, vs)))
}
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
case 72 {
mstore(ptr, daiPermitSelector)
mstore(add(ptr, 0x04), owner)
mstore(add(ptr, 0x24), spender)
// Compact IDaiLikePermit.permit(uint32 nonce, uint32 expiry, uint256 r, uint256 vs)
{ // stack too deep
let expiry := shr(224, calldataload(add(permit.offset, 0x04)))
let vs := calldataload(add(permit.offset, 0x28))
mstore(add(ptr, 0x44), shr(224, calldataload(permit.offset)))
mstore(add(ptr, 0x64), sub(expiry, 1))
mstore(add(ptr, 0x84), true)
mstore(add(ptr, 0xa4), add(27, shr(255, vs)))
calldatacopy(add(ptr, 0xc4), add(permit.offset, 0x08), 0x20) // r
mstore(add(ptr, 0xe4), shr(1, shl(1, vs)))
}
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
case 224 {
mstore(ptr, permitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length)
// IERC20Permit.permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0xe4, 0, 0)
}
case 256 {
mstore(ptr, daiPermitSelector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length)
// IDaiLikePermit.permit(address holder, address spender, uint256 nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s)
success := call(gas(), token, 0, ptr, 0x104, 0, 0)
}
case 96 {
// Compact IPermit2.permit(uint160 amount, uint32 expiration, uint32 nonce, uint32 sigDeadline, uint256 r, uint256 vs)
mstore(ptr, permit2Selector)
mstore(add(ptr, 0x04), owner)
mstore(add(ptr, 0x24), token)
calldatacopy(add(ptr, 0x50), permit.offset, 0x14) // amount
mstore(add(ptr, 0x64), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x14))), 1))) // expiration
mstore(add(ptr, 0x84), shr(224, calldataload(add(permit.offset, 0x18)))) // nonce
mstore(add(ptr, 0xa4), spender)
mstore(add(ptr, 0xc4), and(0xffffffffffff, sub(shr(224, calldataload(add(permit.offset, 0x1c))), 1))) // sigDeadline
mstore(add(ptr, 0xe4), 0x100)
mstore(add(ptr, 0x104), 0x40)
calldatacopy(add(ptr, 0x124), add(permit.offset, 0x20), 0x20) // r
calldatacopy(add(ptr, 0x144), add(permit.offset, 0x40), 0x20) // vs
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
case 352 {
mstore(ptr, permit2Selector)
calldatacopy(add(ptr, 0x04), permit.offset, permit.length)
// IPermit2.permit(address owner, PermitSingle calldata permitSingle, bytes calldata signature)
success := call(gas(), _PERMIT2, 0, ptr, 0x164, 0, 0)
}
default {
mstore(ptr, _PERMIT_LENGTH_ERROR)
revert(ptr, 4)
}
}
}
function _makeCall(
IERC20 token,
bytes4 selector,
address to,
uint256 amount
) private returns (bool success) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let data := mload(0x40)
mstore(data, selector)
mstore(add(data, 0x04), to)
mstore(add(data, 0x24), amount)
success := call(gas(), token, 0, data, 0x44, 0x0, 0x20)
if success {
switch returndatasize()
case 0 {
success := gt(extcodesize(token), 0)
}
default {
success := and(gt(returndatasize(), 31), eq(mload(0), 1))
}
}
}
}
function safeDeposit(IWETH weth, uint256 amount) internal {
if (amount > 0) {
bytes4 selector = IWETH.deposit.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
if iszero(call(gas(), weth, amount, 0, 4, 0, 0)) {
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
}
function safeWithdraw(IWETH weth, uint256 amount) internal {
bytes4 selector = IWETH.withdraw.selector;
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
mstore(0, selector)
mstore(4, amount)
if iszero(call(gas(), weth, 0, 0, 0x24, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
function safeWithdrawTo(IWETH weth, uint256 amount, address to) internal {
safeWithdraw(weth, amount);
if (to != address(this)) {
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
if iszero(call(_RAW_CALL_GAS_LIMIT, to, amount, 0, 0, 0, 0)) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}
}
}
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2023.
pragma solidity ^0.8.17;
import {IncorrectParameterException} from "../interfaces/IExceptions.sol";
uint256 constant UNDERLYING_TOKEN_MASK = 1;
/// @title Bit mask library
/// @notice Implements functions that manipulate bit masks
/// Bit masks are utilized extensively by Gearbox to efficiently store token sets (enabled tokens on accounts
/// or forbidden tokens) and check for set inclusion. A mask is a uint256 number that has its i-th bit set to
/// 1 if i-th item is included into the set. For example, each token has a mask equal to 2**i, so set inclusion
/// can be checked by checking tokenMask & setMask != 0.
library BitMask {
/// @dev Calculates an index of an item based on its mask (using a binary search)
/// @dev The input should always have only 1 bit set, otherwise the result may be unpredictable
function calcIndex(uint256 mask) internal pure returns (uint8 index) {
if (mask == 0) revert IncorrectParameterException(); // U:[BM-1]
uint16 lb = 0; // U:[BM-2]
uint16 ub = 256; // U:[BM-2]
uint16 mid = 128; // U:[BM-2]
unchecked {
while (true) {
uint256 newMask = 1 << mid;
if (newMask & mask != 0) return uint8(mid); // U:[BM-2]
if (newMask > mask) ub = mid; // U:[BM-2]
else lb = mid; // U:[BM-2]
mid = (lb + ub) >> 1; // U:[BM-2]
}
}
}
/// @dev Calculates the number of `1` bits
/// @param enabledTokensMask Bit mask to compute the number of `1` bits in
function calcEnabledTokens(uint256 enabledTokensMask) internal pure returns (uint256 totalTokensEnabled) {
unchecked {
while (enabledTokensMask > 0) {
enabledTokensMask &= enabledTokensMask - 1; // U:[BM-3]
++totalTokensEnabled; // U:[BM-3]
}
}
}
/// @dev Enables bits from the second mask in the first mask
/// @param enabledTokenMask The initial mask
/// @param bitsToEnable Mask of bits to enable
function enable(uint256 enabledTokenMask, uint256 bitsToEnable) internal pure returns (uint256) {
return enabledTokenMask | bitsToEnable; // U:[BM-4]
}
/// @dev Disables bits from the second mask in the first mask
/// @param enabledTokenMask The initial mask
/// @param bitsToDisable Mask of bits to disable
function disable(uint256 enabledTokenMask, uint256 bitsToDisable) internal pure returns (uint256) {
return enabledTokenMask & ~bitsToDisable; // U:[BM-4]
}
/// @dev Computes a new mask with sets of new enabled and disabled bits
/// @dev bitsToEnable and bitsToDisable are applied sequentially to original mask
/// @param enabledTokensMask The initial mask
/// @param bitsToEnable Mask with bits to enable
/// @param bitsToDisable Mask with bits to disable
function enableDisable(uint256 enabledTokensMask, uint256 bitsToEnable, uint256 bitsToDisable)
internal
pure
returns (uint256)
{
return (enabledTokensMask | bitsToEnable) & (~bitsToDisable); // U:[BM-5]
}
/// @dev Enables bits from the second mask in the first mask, skipping specified bits
/// @param enabledTokenMask The initial mask
/// @param bitsToEnable Mask with bits to enable
/// @param invertedSkipMask An inversion of mask of immutable bits
function enable(uint256 enabledTokenMask, uint256 bitsToEnable, uint256 invertedSkipMask)
internal
pure
returns (uint256)
{
return enabledTokenMask | (bitsToEnable & invertedSkipMask); // U:[BM-6]
}
/// @dev Disables bits from the second mask in the first mask, skipping specified bits
/// @param enabledTokenMask The initial mask
/// @param bitsToDisable Mask with bits to disable
/// @param invertedSkipMask An inversion of mask of immutable bits
function disable(uint256 enabledTokenMask, uint256 bitsToDisable, uint256 invertedSkipMask)
internal
pure
returns (uint256)
{
return enabledTokenMask & (~(bitsToDisable & invertedSkipMask)); // U:[BM-6]
}
/// @dev Computes a new mask with sets of new enabled and disabled bits, skipping some bits
/// @dev bitsToEnable and bitsToDisable are applied sequentially to original mask. Skipmask is applied in both cases.
/// @param enabledTokensMask The initial mask
/// @param bitsToEnable Mask with bits to enable
/// @param bitsToDisable Mask with bits to disable
/// @param invertedSkipMask An inversion of mask of immutable bits
function enableDisable(
uint256 enabledTokensMask,
uint256 bitsToEnable,
uint256 bitsToDisable,
uint256 invertedSkipMask
) internal pure returns (uint256) {
return (enabledTokensMask | (bitsToEnable & invertedSkipMask)) & (~(bitsToDisable & invertedSkipMask)); // U:[BM-7]
}
}
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Holdings, 2022
pragma solidity ^0.8.10;
struct Balance {
address token;
uint256 balance;
}
library BalanceOps {
error UnknownToken(address);
function copyBalance(Balance memory b)
internal
pure
returns (Balance memory)
{
return Balance({ token: b.token, balance: b.balance });
}
function addBalance(
Balance[] memory b,
address token,
uint256 amount
) internal pure {
b[getIndex(b, token)].balance += amount;
}
function subBalance(
Balance[] memory b,
address token,
uint256 amount
) internal pure {
b[getIndex(b, token)].balance -= amount;
}
function getBalance(Balance[] memory b, address token)
internal
pure
returns (uint256 amount)
{
return b[getIndex(b, token)].balance;
}
function setBalance(
Balance[] memory b,
address token,
uint256 amount
) internal pure {
b[getIndex(b, token)].balance = amount;
}
function getIndex(Balance[] memory b, address token)
internal
pure
returns (uint256 index)
{
for (uint256 i; i < b.length; ) {
if (b[i].token == token) {
return i;
}
unchecked {
++i;
}
}
revert UnknownToken(token);
}
function copy(Balance[] memory b, uint256 len)
internal
pure
returns (Balance[] memory res)
{
res = new Balance[](len);
for (uint256 i; i < len; ) {
res[i] = copyBalance(b[i]);
unchecked {
++i;
}
}
}
function clone(Balance[] memory b)
internal
pure
returns (Balance[] memory)
{
return copy(b, b.length);
}
function getModifiedAfterSwap(
Balance[] memory b,
address tokenFrom,
uint256 amountFrom,
address tokenTo,
uint256 amountTo
) internal pure returns (Balance[] memory res) {
res = copy(b, b.length);
setBalance(res, tokenFrom, getBalance(b, tokenFrom) - amountFrom);
setBalance(res, tokenTo, getBalance(b, tokenTo) + amountTo);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
// EIP-2612 is Final as of 2022-11-01. This file is deprecated.
import "./IERC20Permit.sol";
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPermit2 {
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allownce
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice Packed allowance
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
function transferFrom(address user, address spender, uint160 amount, address token) external;
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
function allowance(address user, address token, address spender) external view returns (PackedAllowance memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title Revert reason forwarder.
library RevertReasonForwarder {
/// @dev Forwards latest externall call revert.
function reRevert() internal pure {
// bubble up revert reason from latest external call
assembly ("memory-safe") { // solhint-disable-line no-inline-assembly
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
}
}