// SPDX-License-Identifier: MIT // Gearbox Protocol. Generalized leverage for DeFi protocols // (c) Gearbox Holdings, 2022 pragma solidity ^0.8.10; import { Balance } from "../libraries/Balances.sol"; import { MultiCall } from "../libraries/MultiCall.sol"; import { ICreditManagerV2, ICreditManagerV2Exceptions } from "./ICreditManagerV2.sol"; import { IVersion } from "./IVersion.sol"; interface ICreditFacadeExtended { /// @dev Stores expected balances (computed as current balance + passed delta) /// and compare with actual balances at the end of a multicall, reverts /// if at least one is less than expected /// @param expected Array of expected balance changes /// @notice This is an extenstion function that does not exist in the Credit Facade /// itself and can only be used within a multicall function revertIfReceivedLessThan(Balance[] memory expected) external; /// @dev Enables token in enabledTokenMask for the Credit Account of msg.sender /// @param token Address of token to enable function enableToken(address token) external; /// @dev Disables a token on the caller's Credit Account /// @param token Token to disable /// @notice This is an extenstion function that does not exist in the Credit Facade /// itself and can only be used within a multicall function disableToken(address token) external; /// @dev Adds collateral to borrower's credit account /// @param token Address of a collateral token /// @param amount Amount to add function addCollateral(address token, uint256 amount) external payable; /// @dev Increases debt for msg.sender's Credit Account /// - Borrows the requested amount from the pool /// - Updates the CA's borrowAmount / cumulativeIndexOpen /// to correctly compute interest going forward /// - Performs a full collateral check /// /// @param amount Amount to borrow function increaseDebt(uint256 amount) external; /// @dev Decrease debt /// - Decreases the debt by paying the requested amount + accrued interest + fees back to the pool /// - It's also include to this payment interest accrued at the moment and fees /// - Updates cunulativeIndex to cumulativeIndex now /// /// @param amount Amount to increase borrowed amount function decreaseDebt(uint256 amount) external; } interface ICreditFacadeEvents { /// @dev Emits when Blacklist Helper is set for the Credit Facade upon creation event BlacklistHelperSet(address indexed blacklistHelper); /// @dev Emits when a new Credit Account is opened through the /// Credit Facade event OpenCreditAccount( address indexed onBehalfOf, address indexed creditAccount, uint256 borrowAmount, uint16 referralCode ); /// @dev Emits when the account owner closes their CA normally event CloseCreditAccount(address indexed borrower, address indexed to); /// @dev Emits when a Credit Account is liquidated due to low health factor event LiquidateCreditAccount( address indexed borrower, address indexed liquidator, address indexed to, uint256 remainingFunds ); /// @dev Emits when a Credit Account is liquidated due to expiry event LiquidateExpiredCreditAccount( address indexed borrower, address indexed liquidator, address indexed to, uint256 remainingFunds ); /// @dev Emits when remaining funds in underlying currency are sent to /// the blacklist helper upon blacklisted borrower liquidation event UnderlyingSentToBlacklistHelper( address indexed borrower, uint256 amount ); /// @dev Emits when the account owner increases CA's debt event IncreaseBorrowedAmount(address indexed borrower, uint256 amount); /// @dev Emits when the account owner reduces CA's debt event DecreaseBorrowedAmount(address indexed borrower, uint256 amount); /// @dev Emits when the account owner add new collateral to a CA event AddCollateral( address indexed onBehalfOf, address indexed token, uint256 value ); /// @dev Emits when a multicall is started event MultiCallStarted(address indexed borrower); /// @dev Emits when a multicall is finished event MultiCallFinished(); /// @dev Emits when Credit Account ownership is transferred event TransferAccount(address indexed oldOwner, address indexed newOwner); /// @dev Emits when the user changes approval for account transfers to itself from another address event TransferAccountAllowed( address indexed from, address indexed to, bool state ); /// @dev Emits when the account owner enables a token on their CA event TokenEnabled(address indexed borrower, address indexed token); /// @dev Emits when the account owner disables a token on their CA event TokenDisabled(address indexed borrower, address indexed token); /// @dev Emits when pool incurs loss on account liquidation and facade forbids borrowing event IncurLossOnLiquidation(uint256 loss); } interface ICreditFacadeExceptions is ICreditManagerV2Exceptions { /// @dev Thrown if the CreditFacade is not expirable, and an aciton is attempted that /// requires expirability error NotAllowedWhenNotExpirableException(); /// @dev Thrown if whitelisted mode is enabled, and an action is attempted that is /// not allowed in whitelisted mode error NotAllowedInWhitelistedMode(); /// @dev Thrown if a user attempts to transfer a CA to an address that didn't allow it error AccountTransferNotAllowedException(); /// @dev Thrown if a liquidator tries to liquidate an account with a health factor above 1 error CantLiquidateWithSuchHealthFactorException(); /// @dev Thrown if a liquidator tries to liquidate an account by expiry while a Credit Facade is not expired error CantLiquidateNonExpiredException(); /// @dev Thrown if call data passed to a multicall is too short error IncorrectCallDataException(); /// @dev Thrown inside account closure multicall if the borrower attempts an action that is forbidden on closing /// an account error ForbiddenDuringClosureException(); /// @dev Thrown if debt increase and decrease are subsequently attempted in one multicall error IncreaseAndDecreaseForbiddenInOneCallException(); /// @dev Thrown if a selector that doesn't match any allowed function is passed to the Credit Facade /// during a multicall error UnknownMethodException(); /// @dev Thrown if a user tries to open an account or increase debt with increaseDebtForbidden mode on error IncreaseDebtForbiddenException(); /// @dev Thrown if the account owner tries to transfer an unhealthy account error CantTransferLiquidatableAccountException(); /// @dev Thrown if too much new debt was taken within a single block error BorrowedBlockLimitException(); /// @dev Thrown if the new debt principal for a CA falls outside of borrowing limits error BorrowAmountOutOfLimitsException(); /// @dev Thrown if one of the balances on a Credit Account is less than expected /// at the end of a multicall, if revertIfReceivedLessThan was called error BalanceLessThanMinimumDesiredException(address); /// @dev Thrown if a user attempts to open an account on a Credit Facade that has expired error OpenAccountNotAllowedAfterExpirationException(); /// @dev Thrown if expected balances are attempted to be set through revertIfReceivedLessThan twice error ExpectedBalancesAlreadySetException(); /// @dev Thrown if a Credit Account has enabled forbidden tokens and the owner attempts to perform an action /// that is not allowed with any forbidden tokens enabled error ActionProhibitedWithForbiddenTokensException(); /// @dev Thrown when attempting to perform an action on behalf of a borrower /// that is blacklisted in the underlying token error NotAllowedForBlacklistedAddressException(); /// @dev Thrown when the pool receives less funds than borrowAmountWithInterest on account closure error LiquiditySanityCheckException(); } interface ICreditFacade is ICreditFacadeEvents, ICreditFacadeExceptions, IVersion { // // CREDIT ACCOUNT MANAGEMENT // /// @dev Opens credit account, borrows funds from the pool and pulls collateral /// without any additional action. /// @param amount The amount of collateral provided by the borrower /// @param onBehalfOf The address to open an account for. Transfers to it have to be allowed if /// msg.sender != obBehalfOf /// @param leverageFactor Percentage of the user's own funds to borrow. 100 is equal to 100% - borrows the same amount /// as the user's own collateral, equivalent to 2x leverage. /// @param referralCode Referral code that is used for potential rewards. 0 if no referral code provided. function openCreditAccount( uint256 amount, address onBehalfOf, uint16 leverageFactor, uint16 referralCode ) external payable; /// @dev Opens a Credit Account and runs a batch of operations in a multicall /// @param borrowedAmount Debt size /// @param onBehalfOf The address to open an account for. Transfers to it have to be allowed if /// msg.sender != obBehalfOf /// @param calls The array of MultiCall structs encoding the required operations. Generally must have /// at least a call to addCollateral, as otherwise the health check at the end will fail. /// @param referralCode Referral code which is used for potential rewards. 0 if no referral code provided function openCreditAccountMulticall( uint256 borrowedAmount, address onBehalfOf, MultiCall[] calldata calls, uint16 referralCode ) external payable; /// @dev Runs a batch of transactions within a multicall and closes the account /// - Wraps ETH to WETH and sends it msg.sender if value > 0 /// - Executes the multicall - the main purpose of a multicall when closing is to convert all assets to underlying /// in order to pay the debt. /// - Closes credit account: /// + Checks the underlying balance: if it is greater than the amount paid to the pool, transfers the underlying /// from the Credit Account and proceeds. If not, tries to transfer the shortfall from msg.sender. /// + Transfers all enabled assets with non-zero balances to the "to" address, unless they are marked /// to be skipped in skipTokenMask /// - Emits a CloseCreditAccount event /// /// @param to Address to send funds to during account closing /// @param skipTokenMask Uint-encoded bit mask where 1's mark tokens that shouldn't be transferred /// @param calls The array of MultiCall structs encoding the operations to execute before closing the account. function closeCreditAccount( address to, uint256 skipTokenMask, MultiCall[] calldata calls ) external payable; /// @dev A version of `closeCreditAccount` with `convertWETH` parameter that is ignored. /// Used for backward compatibility. function closeCreditAccount( address to, uint256 skipTokenMask, bool convertWETH, MultiCall[] calldata calls ) external payable; /// @dev Runs a batch of transactions within a multicall and liquidates the account /// - Computes the total value and checks that hf < 1. An account can't be liquidated when hf >= 1. /// Total value has to be computed before the multicall, otherwise the liquidator would be able /// to manipulate it. /// - Wraps ETH to WETH and sends it to msg.sender (liquidator) if value > 0 /// - Executes the multicall - the main purpose of a multicall when liquidating is to convert all assets to underlying /// in order to pay the debt. /// - Liquidate credit account: /// + Computes the amount that needs to be paid to the pool. If totalValue * liquidationDiscount < borrow + interest + fees, /// only totalValue * liquidationDiscount has to be paid. Since liquidationDiscount < 1, the liquidator can take /// totalValue * (1 - liquidationDiscount) as premium. Also computes the remaining funds to be sent to borrower /// as totalValue * liquidationDiscount - amountToPool. /// + Checks the underlying balance: if it is greater than amountToPool + remainingFunds, transfers the underlying /// from the Credit Account and proceeds. If not, tries to transfer the shortfall from the liquidator. /// + Transfers all enabled assets with non-zero balances to the "to" address, unless they are marked /// to be skipped in skipTokenMask. If the liquidator is confident that all assets were converted /// during the multicall, they can set the mask to uint256.max - 1, to only transfer the underlying /// - Emits LiquidateCreditAccount event /// /// @param to Address to send funds to after liquidation /// @param skipTokenMask Uint-encoded bit mask where 1's mark tokens that shouldn't be transferred /// @param calls The array of MultiCall structs encoding the operations to execute before liquidating the account. function liquidateCreditAccount( address borrower, address to, uint256 skipTokenMask, MultiCall[] calldata calls ) external payable; /// @dev A version of `liquidateCreditAccount` with `convertWETH` parameter that is ignored. /// Used for backward compatibility. function liquidateCreditAccount( address borrower, address to, uint256 skipTokenMask, bool convertWETH, MultiCall[] calldata calls ) external payable; /// @dev Runs a batch of transactions within a multicall and liquidates the account when /// this Credit Facade is expired /// The general flow of liquidation is nearly the same as normal liquidations, with two main differences: /// - An account can be liquidated on an expired Credit Facade even with hf > 1. However, /// no accounts can be liquidated through this function if the Credit Facade is not expired. /// - Liquidation premiums and fees for liquidating expired accounts are reduced. /// It is still possible to normally liquidate an underwater Credit Account, even when the Credit Facade /// is expired. /// @param to Address to send funds to after liquidation /// @param skipTokenMask Uint-encoded bit mask where 1's mark tokens that shouldn't be transferred /// @param calls The array of MultiCall structs encoding the operations to execute before liquidating the account. /// @notice See more at https://dev.gearbox.fi/docs/documentation/credit/liquidation#liquidating-accounts-by-expiration function liquidateExpiredCreditAccount( address borrower, address to, uint256 skipTokenMask, MultiCall[] calldata calls ) external payable; /// @dev A version of `liquidateExpiredCreditAccount` with `convertWETH` parameter that is ignored. /// Used for backward compatibility. function liquidateExpiredCreditAccount( address borrower, address to, uint256 skipTokenMask, bool convertWETH, MultiCall[] calldata calls ) external payable; /// @dev Adds collateral to borrower's credit account /// @param onBehalfOf Address of the borrower whose account is funded /// @param token Address of a collateral token /// @param amount Amount to add function addCollateral( address onBehalfOf, address token, uint256 amount ) external payable; /// @dev Executes a batch of transactions within a Multicall, to manage an existing account /// - Wraps ETH and sends it back to msg.sender, if value > 0 /// - Executes the Multicall /// - Performs a fullCollateralCheck to verify that hf > 1 after all actions /// @param calls The array of MultiCall structs encoding the operations to execute. function multicall(MultiCall[] calldata calls) external payable; /// @dev Returns true if the borrower has an open Credit Account /// @param borrower Borrower address function hasOpenedCreditAccount(address borrower) external view returns (bool); /// @dev Approves account transfer from another user to msg.sender /// @param from Address for which account transfers are allowed/forbidden /// @param state True is transfer is allowed, false if forbidden function approveAccountTransfer(address from, bool state) external; /// @dev Transfers credit account to another user /// By default, this action is forbidden, and the user has to approve transfers from sender to itself /// by calling approveAccountTransfer. /// This is done to prevent malicious actors from transferring compromised accounts to other users. /// @param to Address to transfer the account to function transferAccountOwnership(address to) external; // // GETTERS // /// @dev Calculates total value for provided Credit Account in underlying /// /// @param creditAccount Credit Account address /// @return total Total value in underlying /// @return twv Total weighted (discounted by liquidation thresholds) value in underlying function calcTotalValue(address creditAccount) external view returns (uint256 total, uint256 twv); /** * @dev Calculates health factor for the credit account * * sum(asset[i] * liquidation threshold[i]) * Hf = -------------------------------------------- * borrowed amount + interest accrued + fees * * * More info: https://dev.gearbox.fi/developers/credit/economy#health-factor * * @param creditAccount Credit account address * @return hf = Health factor in bp (see PERCENTAGE FACTOR in PercentageMath.sol) */ function calcCreditAccountHealthFactor(address creditAccount) external view returns (uint256 hf); /// @dev Returns true if token is a collateral token and is not forbidden, /// otherwise returns false /// @param token Token to check function isTokenAllowed(address token) external view returns (bool); /// @dev Returns the CreditManager connected to this Credit Facade function creditManager() external view returns (ICreditManagerV2); /// @dev Returns true if 'from' is allowed to transfer Credit Accounts to 'to' /// @param from Sender address to check allowance for /// @param to Receiver address to check allowance for function transfersAllowed(address from, address to) external view returns (bool); /// @return maxBorrowedAmountPerBlock Maximal amount of new debt that can be taken per block /// @return isIncreaseDebtForbidden True if increasing debt is forbidden /// @return expirationDate Timestamp of the next expiration (for expirable Credit Facades only) /// @return emergencyLiquidationDiscount Premium for liquidations when the system is paused function params() external view returns ( uint128 maxBorrowedAmountPerBlock, bool isIncreaseDebtForbidden, uint40 expirationDate, uint16 emergencyLiquidationDiscount ); /// @return minBorrowedAmount Minimal borrowed amount per credit account /// @return maxBorrowedAmount Maximal borrowed amount per credit account function limits() external view returns (uint128 minBorrowedAmount, uint128 maxBorrowedAmount); function lossParams() external view returns (uint128 currentCumulativeLoss, uint128 maxCumulativeLoss); function totalDebt() external view returns (uint128 currentTotalDebt, uint128 totalDebtLimit); /// @dev Address of the DegenNFT that gatekeeps account openings in whitelisted mode function degenNFT() external view returns (address); /// @dev Address of the underlying asset function underlying() external view returns (address); /// @dev Address of the blacklist helper or address(0), if underlying is not blacklistable function blacklistHelper() external view returns (address); /// @dev Whether the underlying of connected Credit Manager is blacklistable function isBlacklistableUnderlying() external view returns (bool); } interface ICreditFacadeV2 { /// @return maxBorrowedAmountPerBlock Maximal amount of new debt that can be taken per block /// @return isIncreaseDebtForbidden True if increasing debt is forbidden /// @return expirationDate Timestamp of the next expiration (for expirable Credit Facades only) function params() external view returns ( uint128 maxBorrowedAmountPerBlock, bool isIncreaseDebtForbidden, uint40 expirationDate ); }