// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Errors} from "./shared/Errors.sol";
import {Events} from "./shared/Events.sol";
import {IERC20, IEVault} from "./IEVault.sol";
/// @title DToken
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Contract implements read only ERC20 interface, and `Transfer` events, for EVault's debt
contract DToken is IERC20, Errors, Events {
/// @notice The address of the EVault associated with this DToken
address public immutable eVault;
constructor() {
eVault = msg.sender;
}
// ERC20 interface
/// @notice The debt token (dToken) name
/// @return The dToken name
function name() external view returns (string memory) {
return string.concat("Debt token of ", IEVault(eVault).name());
}
/// @notice The debt token (dToken) symbol
/// @return The dToken symbol
function symbol() external view returns (string memory) {
return string.concat(IEVault(eVault).symbol(), "-DEBT");
}
/// @notice Decimals of the dToken, same as EVault's
/// @return The dToken decimals
function decimals() external view returns (uint8) {
return IEVault(eVault).decimals();
}
/// @notice Return total supply of the DToken
/// @return The dToken total supply
function totalSupply() external view returns (uint256) {
return IEVault(eVault).totalBorrows();
}
/// @notice Balance of a particular account, in dTokens
/// @param owner The account to query
/// @return The balance of the account
function balanceOf(address owner) external view returns (uint256) {
return IEVault(eVault).debtOf(owner);
}
/// @notice Retrieve the current allowance
/// @return The allowance
/// @dev Approvals are not supported by the dToken
function allowance(address, address) external pure returns (uint256) {
return 0;
}
/// @notice Function required by the ERC20 interface
/// @dev Approvals are not supported by the DToken
function approve(address, uint256) external pure returns (bool) {
revert E_NotSupported();
}
/// @notice Function required by the ERC20 interface
/// @dev Transfers are not supported by the DToken directly
function transfer(address, uint256) external pure returns (bool) {
revert E_NotSupported();
}
/// @notice Function required by the ERC20 interface
/// @dev Transfers are not supported by the DToken directly
function transferFrom(address, address, uint256) external pure returns (bool) {
revert E_NotSupported();
}
// Events
/// @notice Emit an ERC20 Transfer event
/// @dev Only callable by the parent EVault
function emitTransfer(address from, address to, uint256 value) external {
if (msg.sender != eVault) revert E_Unauthorized();
emit Transfer(from, to, value);
}
// Helpers
/// @notice Return the address of the asset the debt is denominated in
function asset() external view returns (address) {
return IEVault(eVault).asset();
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Errors
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Contract implementing EVault's custom errors
contract Errors {
error E_Initialized();
error E_ProxyMetadata();
error E_SelfTransfer();
error E_InsufficientAllowance();
error E_InsufficientCash();
error E_InsufficientAssets();
error E_InsufficientBalance();
error E_InsufficientDebt();
error E_FlashLoanNotRepaid();
error E_Reentrancy();
error E_OperationDisabled();
error E_OutstandingDebt();
error E_AmountTooLargeToEncode();
error E_DebtAmountTooLargeToEncode();
error E_RepayTooMuch();
error E_TransientState();
error E_SelfLiquidation();
error E_ControllerDisabled();
error E_CollateralDisabled();
error E_ViolatorLiquidityDeferred();
error E_LiquidationCoolOff();
error E_ExcessiveRepayAmount();
error E_MinYield();
error E_BadAddress();
error E_ZeroAssets();
error E_ZeroShares();
error E_Unauthorized();
error E_CheckUnauthorized();
error E_NotSupported();
error E_EmptyError();
error E_BadBorrowCap();
error E_BadSupplyCap();
error E_BadCollateral();
error E_AccountLiquidity();
error E_NoLiability();
error E_NotController();
error E_BadFee();
error E_SupplyCapExceeded();
error E_BorrowCapExceeded();
error E_InvalidLTVAsset();
error E_NoPriceOracle();
error E_ConfigAmountTooLargeToEncode();
error E_BadAssetReceiver();
error E_BadSharesOwner();
error E_BadSharesReceiver();
error E_BadMaxLiquidationDiscount();
error E_LTVBorrow();
error E_LTVLiquidation();
error E_NotHookTarget();
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Events
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Contract implementing EVault's events
abstract contract Events {
// ERC20
/// @notice Transfer an ERC20 token balance
/// @param from Sender address
/// @param to Receiver address
/// @param value Tokens sent
event Transfer(address indexed from, address indexed to, uint256 value);
/// @notice Set an ERC20 approval
/// @param owner Address granting approval to spend tokens
/// @param spender Address receiving approval to spend tokens
/// @param value Amount of tokens approved to spend
event Approval(address indexed owner, address indexed spender, uint256 value);
// ERC4626
/// @notice Deposit assets into an ERC4626 vault
/// @param sender Address initiating the deposit
/// @param owner Address holding the assets
/// @param assets Amount of assets deposited
/// @param shares Amount of shares minted as receipt for the deposit
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
/// @notice Withdraw from an ERC4626 vault
/// @param sender Address initiating the withdrawal
/// @param receiver Address receiving the assets
/// @param owner Address holding the shares
/// @param assets Amount of assets sent to the receiver
/// @param shares Amount of shares burned
event Withdraw(
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
);
// EVault
/// @notice New EVault is initialized
/// @param creator Address designated as the vault's creator
/// @param asset The underlying asset of the vault
/// @param dToken Address of the sidecar debt token
event EVaultCreated(address indexed creator, address indexed asset, address dToken);
/// @notice Log the current vault status
/// @param totalShares Sum of all shares
/// @param totalBorrows Sum of all borrows in assets
/// @param accumulatedFees Interest fees accrued in the accumulator
/// @param cash The amount of assets held by the vault directly
/// @param interestAccumulator Current interest accumulator in ray
/// @param interestRate Current interest rate, which will be applied during the next fee accrual
/// @param timestamp Current block's timestamp
event VaultStatus(
uint256 totalShares,
uint256 totalBorrows,
uint256 accumulatedFees,
uint256 cash,
uint256 interestAccumulator,
uint256 interestRate,
uint256 timestamp
);
/// @notice Increase account's debt
/// @param account Address adding liability
/// @param assets Amount of debt added in assets
event Borrow(address indexed account, uint256 assets);
/// @notice Decrease account's debt
/// @param account Address repaying the debt
/// @param assets Amount of debt removed in assets
event Repay(address indexed account, uint256 assets);
/// @notice Account's debt was increased due to interest
/// @param account Address being charged interest
/// @param assets Amount of debt added in assets
event InterestAccrued(address indexed account, uint256 assets);
/// @notice Liquidate unhealthy account
/// @param liquidator Address executing the liquidation
/// @param violator Address holding an unhealthy borrow
/// @param collateral Address of the asset seized
/// @param repayAssets Amount of debt in assets transferred from violator to liquidator
/// @param yieldBalance Amount of collateral asset's balance transferred from violator to liquidator
event Liquidate(
address indexed liquidator,
address indexed violator,
address collateral,
uint256 repayAssets,
uint256 yieldBalance
);
/// @notice Take on debt from another account
/// @param from Account from which the debt is taken
/// @param to Account taking on the debt
/// @param assets Amount of debt transferred in assets
event PullDebt(address indexed from, address indexed to, uint256 assets);
/// @notice Socialize debt after liquidating all of the unhealthy account's collateral
/// @param account Address holding an unhealthy borrow
/// @param assets Amount of debt socialized among all of the share holders
event DebtSocialized(address indexed account, uint256 assets);
/// @notice Split the accumulated fees between the governor and the protocol
/// @param sender Address initializing the conversion
/// @param protocolReceiver Address receiving the protocol's share of the fees
/// @param governorReceiver Address receiving the governor's share of the fees
/// @param protocolShares Amount of shares transferred to the protocol receiver
/// @param governorShares Amount of shares transferred to the governor receiver
event ConvertFees(
address indexed sender,
address indexed protocolReceiver,
address indexed governorReceiver,
uint256 protocolShares,
uint256 governorShares
);
/// @notice Enable or disable balance tracking for the account
/// @param account Address which enabled or disabled balance tracking
/// @param status True if balance tracking was enabled, false otherwise
event BalanceForwarderStatus(address indexed account, bool status);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
import {IVault as IEVCVault} from "ethereum-vault-connector/interfaces/IVault.sol";
// Full interface of EVault and all it's modules
/// @title IInitialize
/// @notice Interface of the initialization module of EVault
interface IInitialize {
/// @notice Initialization of the newly deployed proxy contract
/// @param proxyCreator Account which created the proxy or should be the initial governor
function initialize(address proxyCreator) external;
}
/// @title IERC20
/// @notice Interface of the EVault's Initialize module
interface IERC20 {
/// @notice Vault share token (eToken) name, ie "Euler Vault: DAI"
/// @return The name of the eToken
function name() external view returns (string memory);
/// @notice Vault share token (eToken) symbol, ie "eDAI"
/// @return The symbol of the eToken
function symbol() external view returns (string memory);
/// @notice Decimals, the same as the asset's or 18 if the asset doesn't implement `decimals()`
/// @return The decimals of the eToken
function decimals() external view returns (uint8);
/// @notice Sum of all eToken balances
/// @return The total supply of the eToken
function totalSupply() external view returns (uint256);
/// @notice Balance of a particular account, in eTokens
/// @param account Address to query
/// @return The balance of the account
function balanceOf(address account) external view returns (uint256);
/// @notice Retrieve the current allowance
/// @param holder The account holding the eTokens
/// @param spender Trusted address
/// @return The allowance from holder for spender
function allowance(address holder, address spender) external view returns (uint256);
/// @notice Transfer eTokens to another address
/// @param to Recipient account
/// @param amount In shares.
/// @return True if transfer succeeded
function transfer(address to, uint256 amount) external returns (bool);
/// @notice Transfer eTokens from one address to another
/// @param from This address must've approved the to address
/// @param to Recipient account
/// @param amount In shares
/// @return True if transfer succeeded
function transferFrom(address from, address to, uint256 amount) external returns (bool);
/// @notice Allow spender to access an amount of your eTokens
/// @param spender Trusted address
/// @param amount Use max uint for "infinite" allowance
/// @return True if approval succeeded
function approve(address spender, uint256 amount) external returns (bool);
}
/// @title IToken
/// @notice Interface of the EVault's Token module
interface IToken is IERC20 {
/// @notice Transfer the full eToken balance of an address to another
/// @param from This address must've approved the to address
/// @param to Recipient account
/// @return True if transfer succeeded
function transferFromMax(address from, address to) external returns (bool);
}
/// @title IERC4626
/// @notice Interface of an ERC4626 vault
interface IERC4626 {
/// @notice Vault's underlying asset
/// @return The vault's underlying asset
function asset() external view returns (address);
/// @notice Total amount of managed assets, cash and borrows
/// @return The total amount of assets
function totalAssets() external view returns (uint256);
/// @notice Calculate amount of assets corresponding to the requested shares amount
/// @param shares Amount of shares to convert
/// @return The amount of assets
function convertToAssets(uint256 shares) external view returns (uint256);
/// @notice Calculate amount of shares corresponding to the requested assets amount
/// @param assets Amount of assets to convert
/// @return The amount of shares
function convertToShares(uint256 assets) external view returns (uint256);
/// @notice Fetch the maximum amount of assets a user can deposit
/// @param account Address to query
/// @return The max amount of assets the account can deposit
function maxDeposit(address account) external view returns (uint256);
/// @notice Calculate an amount of shares that would be created by depositing assets
/// @param assets Amount of assets deposited
/// @return Amount of shares received
function previewDeposit(uint256 assets) external view returns (uint256);
/// @notice Fetch the maximum amount of shares a user can mint
/// @param account Address to query
/// @return The max amount of shares the account can mint
function maxMint(address account) external view returns (uint256);
/// @notice Calculate an amount of assets that would be required to mint requested amount of shares
/// @param shares Amount of shares to be minted
/// @return Required amount of assets
function previewMint(uint256 shares) external view returns (uint256);
/// @notice Fetch the maximum amount of assets a user is allowed to withdraw
/// @param owner Account holding the shares
/// @return The maximum amount of assets the owner is allowed to withdraw
function maxWithdraw(address owner) external view returns (uint256);
/// @notice Calculate the amount of shares that will be burned when withdrawing requested amount of assets
/// @param assets Amount of assets withdrawn
/// @return Amount of shares burned
function previewWithdraw(uint256 assets) external view returns (uint256);
/// @notice Fetch the maximum amount of shares a user is allowed to redeem for assets
/// @param owner Account holding the shares
/// @return The maximum amount of shares the owner is allowed to redeem
function maxRedeem(address owner) external view returns (uint256);
/// @notice Calculate the amount of assets that will be transferred when redeeming requested amount of shares
/// @param shares Amount of shares redeemed
/// @return Amount of assets transferred
function previewRedeem(uint256 shares) external view returns (uint256);
/// @notice Transfer requested amount of underlying tokens from sender to the vault pool in return for shares
/// @param amount Amount of assets to deposit (use max uint256 for full underlying token balance)
/// @param receiver An account to receive the shares
/// @return Amount of shares minted
/// @dev Deposit will round down the amount of assets that are converted to shares. To prevent losses consider using
/// mint instead.
function deposit(uint256 amount, address receiver) external returns (uint256);
/// @notice Transfer underlying tokens from sender to the vault pool in return for requested amount of shares
/// @param amount Amount of shares to be minted
/// @param receiver An account to receive the shares
/// @return Amount of assets deposited
function mint(uint256 amount, address receiver) external returns (uint256);
/// @notice Transfer requested amount of underlying tokens from the vault and decrease account's shares balance
/// @param amount Amount of assets to withdraw
/// @param receiver Account to receive the withdrawn assets
/// @param owner Account holding the shares to burn
/// @return Amount of shares burned
function withdraw(uint256 amount, address receiver, address owner) external returns (uint256);
/// @notice Burn requested shares and transfer corresponding underlying tokens from the vault to the receiver
/// @param amount Amount of shares to burn (use max uint256 to burn full owner balance)
/// @param receiver Account to receive the withdrawn assets
/// @param owner Account holding the shares to burn.
/// @return Amount of assets transferred
function redeem(uint256 amount, address receiver, address owner) external returns (uint256);
}
/// @title IVault
/// @notice Interface of the EVault's Vault module
interface IVault is IERC4626 {
/// @notice Balance of the fees accumulator, in shares
/// @return The accumulated fees in shares
function accumulatedFees() external view returns (uint256);
/// @notice Balance of the fees accumulator, in underlying units
/// @return The accumulated fees in asset units
function accumulatedFeesAssets() external view returns (uint256);
/// @notice Address of the original vault creator
/// @return The address of the creator
function creator() external view returns (address);
/// @notice Creates shares for the receiver, from excess asset balances of the vault (not accounted for in `cash`)
/// @param amount Amount of assets to claim (use max uint256 to claim all available assets)
/// @param receiver An account to receive the shares
/// @return Amount of shares minted
/// @dev Could be used as an alternative deposit flow in certain scenarios. E.g. swap directly to the vault, call
/// `skim` to claim deposit.
function skim(uint256 amount, address receiver) external returns (uint256);
}
/// @title IBorrowing
/// @notice Interface of the EVault's Borrowing module
interface IBorrowing {
/// @notice Sum of all outstanding debts, in underlying units (increases as interest is accrued)
/// @return The total borrows in asset units
function totalBorrows() external view returns (uint256);
/// @notice Sum of all outstanding debts, in underlying units scaled up by shifting
/// INTERNAL_DEBT_PRECISION_SHIFT bits
/// @return The total borrows in internal debt precision
function totalBorrowsExact() external view returns (uint256);
/// @notice Balance of vault assets as tracked by deposits/withdrawals and borrows/repays
/// @return The amount of assets the vault tracks as current direct holdings
function cash() external view returns (uint256);
/// @notice Debt owed by a particular account, in underlying units
/// @param account Address to query
/// @return The debt of the account in asset units
function debtOf(address account) external view returns (uint256);
/// @notice Debt owed by a particular account, in underlying units scaled up by shifting
/// INTERNAL_DEBT_PRECISION_SHIFT bits
/// @param account Address to query
/// @return The debt of the account in internal precision
function debtOfExact(address account) external view returns (uint256);
/// @notice Retrieves the current interest rate for an asset
/// @return The interest rate in yield-per-second, scaled by 10**27
function interestRate() external view returns (uint256);
/// @notice Retrieves the current interest rate accumulator for an asset
/// @return An opaque accumulator that increases as interest is accrued
function interestAccumulator() external view returns (uint256);
/// @notice Returns an address of the sidecar DToken
/// @return The address of the DToken
function dToken() external view returns (address);
/// @notice Transfer underlying tokens from the vault to the sender, and increase sender's debt
/// @param amount Amount of assets to borrow (use max uint256 for all available tokens)
/// @param receiver Account receiving the borrowed tokens
/// @return Amount of assets borrowed
function borrow(uint256 amount, address receiver) external returns (uint256);
/// @notice Transfer underlying tokens from the sender to the vault, and decrease receiver's debt
/// @param amount Amount of debt to repay in assets (use max uint256 for full debt)
/// @param receiver Account holding the debt to be repaid
/// @return Amount of assets repaid
function repay(uint256 amount, address receiver) external returns (uint256);
/// @notice Pay off liability with shares ("self-repay")
/// @param amount In asset units (use max uint256 to repay the debt in full or up to the available deposit)
/// @param receiver Account to remove debt from by burning sender's shares
/// @return shares Amount of shares burned
/// @return debt Amount of debt removed in assets
/// @dev Equivalent to withdrawing and repaying, but no assets are needed to be present in the vault
/// @dev Contrary to a regular `repay`, if account is unhealthy, the repay amount must bring the account back to
/// health, or the operation will revert during account status check
function repayWithShares(uint256 amount, address receiver) external returns (uint256 shares, uint256 debt);
/// @notice Take over debt from another account
/// @param amount Amount of debt in asset units (use max uint256 for all the account's debt)
/// @param from Account to pull the debt from
/// @dev Due to internal debt precision accounting, the liability reported on either or both accounts after
/// calling `pullDebt` may not match the `amount` requested precisely
function pullDebt(uint256 amount, address from) external;
/// @notice Request a flash-loan. A onFlashLoan() callback in msg.sender will be invoked, which must repay the loan
/// to the main Euler address prior to returning.
/// @param amount In asset units
/// @param data Passed through to the onFlashLoan() callback, so contracts don't need to store transient data in
/// storage
function flashLoan(uint256 amount, bytes calldata data) external;
/// @notice Updates interest accumulator and totalBorrows, credits reserves, re-targets interest rate, and logs
/// vault status
function touch() external;
}
/// @title ILiquidation
/// @notice Interface of the EVault's Liquidation module
interface ILiquidation {
/// @notice Checks to see if a liquidation would be profitable, without actually doing anything
/// @param liquidator Address that will initiate the liquidation
/// @param violator Address that may be in collateral violation
/// @param collateral Collateral which is to be seized
/// @return maxRepay Max amount of debt that can be repaid, in asset units
/// @return maxYield Yield in collateral corresponding to max allowed amount of debt to be repaid, in collateral
/// balance (shares for vaults)
function checkLiquidation(address liquidator, address violator, address collateral)
external
view
returns (uint256 maxRepay, uint256 maxYield);
/// @notice Attempts to perform a liquidation
/// @param violator Address that may be in collateral violation
/// @param collateral Collateral which is to be seized
/// @param repayAssets The amount of underlying debt to be transferred from violator to sender, in asset units (use
/// max uint256 to repay the maximum possible amount). Meant as slippage check together with `minYieldBalance`
/// @param minYieldBalance The minimum acceptable amount of collateral to be transferred from violator to sender, in
/// collateral balance units (shares for vaults). Meant as slippage check together with `repayAssets`
/// @dev If `repayAssets` is set to max uint256 it is assumed the caller will perform their own slippage checks to
/// make sure they are not taking on too much debt. This option is mainly meant for smart contract liquidators
function liquidate(address violator, address collateral, uint256 repayAssets, uint256 minYieldBalance) external;
}
/// @title IRiskManager
/// @notice Interface of the EVault's RiskManager module
interface IRiskManager is IEVCVault {
/// @notice Retrieve account's total liquidity
/// @param account Account holding debt in this vault
/// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status
/// check mode, where different LTV values might apply.
/// @return collateralValue Total risk adjusted value of all collaterals in unit of account
/// @return liabilityValue Value of debt in unit of account
function accountLiquidity(address account, bool liquidation)
external
view
returns (uint256 collateralValue, uint256 liabilityValue);
/// @notice Retrieve account's liquidity per collateral
/// @param account Account holding debt in this vault
/// @param liquidation Flag to indicate if the calculation should be performed in liquidation vs account status
/// check mode, where different LTV values might apply.
/// @return collaterals Array of collaterals enabled
/// @return collateralValues Array of risk adjusted collateral values corresponding to items in collaterals array.
/// In unit of account
/// @return liabilityValue Value of debt in unit of account
function accountLiquidityFull(address account, bool liquidation)
external
view
returns (address[] memory collaterals, uint256[] memory collateralValues, uint256 liabilityValue);
/// @notice Release control of the account on EVC if no outstanding debt is present
function disableController() external;
/// @notice Checks the status of an account and reverts if account is not healthy
/// @param account The address of the account to be checked
/// @return magicValue Must return the bytes4 magic value 0xb168c58f (which is a selector of this function) when
/// account status is valid, or revert otherwise.
/// @dev Only callable by EVC during status checks
function checkAccountStatus(address account, address[] calldata collaterals) external view returns (bytes4);
/// @notice Checks the status of the vault and reverts if caps are exceeded
/// @return magicValue Must return the bytes4 magic value 0x4b3d1223 (which is a selector of this function) when
/// account status is valid, or revert otherwise.
/// @dev Only callable by EVC during status checks
function checkVaultStatus() external returns (bytes4);
}
/// @title IBalanceForwarder
/// @notice Interface of the EVault's BalanceForwarder module
interface IBalanceForwarder {
/// @notice Retrieve the address of rewards contract, tracking changes in account's balances
/// @return The balance tracker address
function balanceTrackerAddress() external view returns (address);
/// @notice Retrieves boolean indicating if the account opted in to forward balance changes to the rewards contract
/// @param account Address to query
/// @return True if balance forwarder is enabled
function balanceForwarderEnabled(address account) external view returns (bool);
/// @notice Enables balance forwarding for the authenticated account
/// @dev Only the authenticated account can enable balance forwarding for itself
/// @dev Should call the IBalanceTracker hook with the current account's balance
function enableBalanceForwarder() external;
/// @notice Disables balance forwarding for the authenticated account
/// @dev Only the authenticated account can disable balance forwarding for itself
/// @dev Should call the IBalanceTracker hook with the account's balance of 0
function disableBalanceForwarder() external;
}
/// @title IGovernance
/// @notice Interface of the EVault's Governance module
interface IGovernance {
/// @notice Retrieves the address of the governor
/// @return The governor address
function governorAdmin() external view returns (address);
/// @notice Retrieves address of the governance fee receiver
/// @return The fee receiver address
function feeReceiver() external view returns (address);
/// @notice Retrieves the interest fee in effect for the vault
/// @return Amount of interest that is redirected as a fee, as a fraction scaled by 1e4
function interestFee() external view returns (uint16);
/// @notice Looks up an asset's currently configured interest rate model
/// @return Address of the interest rate contract or address zero to indicate 0% interest
function interestRateModel() external view returns (address);
/// @notice Retrieves the ProtocolConfig address
/// @return The protocol config address
function protocolConfigAddress() external view returns (address);
/// @notice Retrieves the protocol fee share
/// @return A percentage share of fees accrued belonging to the protocol, in 1e4 scale
function protocolFeeShare() external view returns (uint256);
/// @notice Retrieves the address which will receive protocol's fees
/// @notice The protocol fee receiver address
function protocolFeeReceiver() external view returns (address);
/// @notice Retrieves supply and borrow caps in AmountCap format
/// @return supplyCap The supply cap in AmountCap format
/// @return borrowCap The borrow cap in AmountCap format
function caps() external view returns (uint16 supplyCap, uint16 borrowCap);
/// @notice Retrieves the borrow LTV of the collateral, which is used to determine if the account is healthy during
/// account status checks.
/// @param collateral The address of the collateral to query
/// @return Borrowing LTV in 1e4 scale
function LTVBorrow(address collateral) external view returns (uint16);
/// @notice Retrieves the current liquidation LTV, which is used to determine if the account is eligible for
/// liquidation
/// @param collateral The address of the collateral to query
/// @return Liquidation LTV in 1e4 scale
function LTVLiquidation(address collateral) external view returns (uint16);
/// @notice Retrieves LTV configuration for the collateral
/// @param collateral Collateral asset
/// @return borrowLTV The current value of borrow LTV for originating positions
/// @return liquidationLTV The value of fully converged liquidation LTV
/// @return initialLiquidationLTV The initial value of the liquidation LTV, when the ramp began
/// @return targetTimestamp The timestamp when the liquidation LTV is considered fully converged
/// @return rampDuration The time it takes for the liquidation LTV to converge from the initial value to the fully
/// converged value
function LTVFull(address collateral)
external
view
returns (
uint16 borrowLTV,
uint16 liquidationLTV,
uint16 initialLiquidationLTV,
uint48 targetTimestamp,
uint32 rampDuration
);
/// @notice Retrieves a list of collaterals with configured LTVs
/// @return List of asset collaterals
/// @dev Returned assets could have the ltv disabled (set to zero)
function LTVList() external view returns (address[] memory);
/// @notice Retrieves the maximum liquidation discount
/// @return The maximum liquidation discount in 1e4 scale
/// @dev The default value, which is zero, is deliberately bad, as it means there would be no incentive to liquidate
/// unhealthy users. The vault creator must take care to properly select the limit, given the underlying and
/// collaterals used.
function maxLiquidationDiscount() external view returns (uint16);
/// @notice Retrieves liquidation cool-off time, which must elapse after successful account status check before
/// account can be liquidated
/// @return The liquidation cool off time in seconds
function liquidationCoolOffTime() external view returns (uint16);
/// @notice Retrieves a hook target and a bitmask indicating which operations call the hook target
/// @return hookTarget Address of the hook target contract
/// @return hookedOps Bitmask with operations that should call the hooks. See Constants.sol for a list of operations
function hookConfig() external view returns (address hookTarget, uint32 hookedOps);
/// @notice Retrieves a bitmask indicating enabled config flags
/// @return Bitmask with config flags enabled
function configFlags() external view returns (uint32);
/// @notice Address of EthereumVaultConnector contract
/// @return The EVC address
function EVC() external view returns (address);
/// @notice Retrieves a reference asset used for liquidity calculations
/// @return The address of the reference asset
function unitOfAccount() external view returns (address);
/// @notice Retrieves the address of the oracle contract
/// @return The address of the oracle
function oracle() external view returns (address);
/// @notice Retrieves the Permit2 contract address
/// @return The address of the Permit2 contract
function permit2Address() external view returns (address);
/// @notice Splits accrued fees balance according to protocol fee share and transfers shares to the governor fee
/// receiver and protocol fee receiver
function convertFees() external;
/// @notice Set a new governor address
/// @param newGovernorAdmin The new governor address
/// @dev Set to zero address to renounce privileges and make the vault non-governed
function setGovernorAdmin(address newGovernorAdmin) external;
/// @notice Set a new governor fee receiver address
/// @param newFeeReceiver The new fee receiver address
function setFeeReceiver(address newFeeReceiver) external;
/// @notice Set a new LTV config
/// @param collateral Address of collateral to set LTV for
/// @param borrowLTV New borrow LTV, for assessing account's health during account status checks, in 1e4 scale
/// @param liquidationLTV New liquidation LTV after ramp ends in 1e4 scale
/// @param rampDuration Ramp duration in seconds
function setLTV(address collateral, uint16 borrowLTV, uint16 liquidationLTV, uint32 rampDuration) external;
/// @notice Set a new maximum liquidation discount
/// @param newDiscount New maximum liquidation discount in 1e4 scale
/// @dev If the discount is zero (the default), the liquidators will not be incentivized to liquidate unhealthy
/// accounts
function setMaxLiquidationDiscount(uint16 newDiscount) external;
/// @notice Set a new liquidation cool off time, which must elapse after successful account status check before
/// account can be liquidated
/// @param newCoolOffTime The new liquidation cool off time in seconds
/// @dev Setting cool off time to zero allows liquidating the account in the same block as the last successful
/// account status check
function setLiquidationCoolOffTime(uint16 newCoolOffTime) external;
/// @notice Set a new interest rate model contract
/// @param newModel The new IRM address
/// @dev If the new model reverts, perhaps due to governor error, the vault will silently use a zero interest
/// rate. Governor should make sure the new interest rates are computed as expected.
function setInterestRateModel(address newModel) external;
/// @notice Set a new hook target and a new bitmap indicating which operations should call the hook target.
/// Operations are defined in Constants.sol.
/// @param newHookTarget The new hook target address. Use address(0) to simply disable hooked operations
/// @param newHookedOps Bitmask with the new hooked operations
/// @dev All operations are initially disabled in a newly created vault. The vault creator must set their
/// own configuration to make the vault usable
function setHookConfig(address newHookTarget, uint32 newHookedOps) external;
/// @notice Set new bitmap indicating which config flags should be enabled. Flags are defined in Constants.sol
/// @param newConfigFlags Bitmask with the new config flags
function setConfigFlags(uint32 newConfigFlags) external;
/// @notice Set new supply and borrow caps in AmountCap format
/// @param supplyCap The new supply cap in AmountCap fromat
/// @param borrowCap The new borrow cap in AmountCap fromat
function setCaps(uint16 supplyCap, uint16 borrowCap) external;
/// @notice Set a new interest fee
/// @param newFee The new interest fee
function setInterestFee(uint16 newFee) external;
}
/// @title IEVault
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Interface of the EVault, an EVC enabled lending vault
interface IEVault is
IInitialize,
IToken,
IVault,
IBorrowing,
ILiquidation,
IRiskManager,
IBalanceForwarder,
IGovernance
{
/// @notice Fetch address of the `Initialize` module
function MODULE_INITIALIZE() external view returns (address);
/// @notice Fetch address of the `Token` module
function MODULE_TOKEN() external view returns (address);
/// @notice Fetch address of the `Vault` module
function MODULE_VAULT() external view returns (address);
/// @notice Fetch address of the `Borrowing` module
function MODULE_BORROWING() external view returns (address);
/// @notice Fetch address of the `Liquidation` module
function MODULE_LIQUIDATION() external view returns (address);
/// @notice Fetch address of the `RiskManager` module
function MODULE_RISKMANAGER() external view returns (address);
/// @notice Fetch address of the `BalanceForwarder` module
function MODULE_BALANCE_FORWARDER() external view returns (address);
/// @notice Fetch address of the `Governance` module
function MODULE_GOVERNANCE() external view returns (address);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
/// @title IVault
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice This interface defines the methods for the Vault for the purpose of integration with the Ethereum Vault
/// Connector.
interface IVault {
/// @notice Disables a controller (this vault) for the authenticated account.
/// @dev A controller is a vault that has been chosen for an account to have special control over account’s
/// balances in the enabled collaterals vaults. User calls this function in order for the vault to disable itself
/// for the account if the conditions are met (i.e. user has repaid debt in full). If the conditions are not met,
/// the function reverts.
function disableController() external;
/// @notice Checks the status of an account.
/// @dev This function must only deliberately revert if the account status is invalid. If this function reverts due
/// to any other reason, it may render the account unusable with possibly no way to recover funds.
/// @param account The address of the account to be checked.
/// @param collaterals The array of enabled collateral addresses to be considered for the account status check.
/// @return magicValue Must return the bytes4 magic value 0xb168c58f (which is a selector of this function) when
/// account status is valid, or revert otherwise.
function checkAccountStatus(
address account,
address[] calldata collaterals
) external view returns (bytes4 magicValue);
/// @notice Checks the status of the vault.
/// @dev This function must only deliberately revert if the vault status is invalid. If this function reverts due to
/// any other reason, it may render some accounts unusable with possibly no way to recover funds.
/// @return magicValue Must return the bytes4 magic value 0x4b3d1223 (which is a selector of this function) when
/// account status is valid, or revert otherwise.
function checkVaultStatus() external returns (bytes4 magicValue);
}
{
"compilationTarget": {
"lib/euler-vault-kit/src/EVault/DToken.sol": "DToken"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 20000
},
"remappings": [
":@openzeppelin/contracts/utils/math/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/utils/math/",
":@pyth/=lib/euler-price-oracle/lib/pyth-sdk-solidity/",
":@redstone/evm-connector/=lib/euler-price-oracle/lib/redstone-oracles-monorepo/packages/evm-connector/contracts/",
":@solady/=lib/euler-price-oracle/lib/solady/src/",
":@uniswap/v3-core/=lib/euler-price-oracle/lib/v3-core/",
":@uniswap/v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/",
":ds-test/=lib/fee-flow/lib/forge-std/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":ethereum-vault-connector/=lib/ethereum-vault-connector/src/",
":euler-price-oracle-test/=lib/euler-price-oracle/test/",
":euler-price-oracle/=lib/euler-price-oracle/src/",
":euler-vault-kit/=lib/euler-vault-kit/src/",
":evc/=lib/ethereum-vault-connector/src/",
":evk-test/=lib/euler-vault-kit/test/",
":evk/=lib/euler-vault-kit/src/",
":fee-flow/=lib/fee-flow/src/",
":forge-gas-snapshot/=lib/euler-vault-kit/lib/permit2/lib/forge-gas-snapshot/src/",
":forge-std/=lib/forge-std/src/",
":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":openzeppelin/=lib/ethereum-vault-connector/lib/openzeppelin-contracts/contracts/",
":permit2/=lib/euler-vault-kit/lib/permit2/",
":pyth-sdk-solidity/=lib/euler-price-oracle/lib/pyth-sdk-solidity/",
":redstone-oracles-monorepo/=lib/euler-price-oracle/lib/",
":reward-streams/=lib/reward-streams/src/",
":solady/=lib/euler-price-oracle/lib/solady/src/",
":solmate/=lib/fee-flow/lib/solmate/src/",
":v3-core/=lib/euler-price-oracle/lib/v3-core/contracts/",
":v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/contracts/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"E_AccountLiquidity","type":"error"},{"inputs":[],"name":"E_AmountTooLargeToEncode","type":"error"},{"inputs":[],"name":"E_BadAddress","type":"error"},{"inputs":[],"name":"E_BadAssetReceiver","type":"error"},{"inputs":[],"name":"E_BadBorrowCap","type":"error"},{"inputs":[],"name":"E_BadCollateral","type":"error"},{"inputs":[],"name":"E_BadFee","type":"error"},{"inputs":[],"name":"E_BadMaxLiquidationDiscount","type":"error"},{"inputs":[],"name":"E_BadSharesOwner","type":"error"},{"inputs":[],"name":"E_BadSharesReceiver","type":"error"},{"inputs":[],"name":"E_BadSupplyCap","type":"error"},{"inputs":[],"name":"E_BorrowCapExceeded","type":"error"},{"inputs":[],"name":"E_CheckUnauthorized","type":"error"},{"inputs":[],"name":"E_CollateralDisabled","type":"error"},{"inputs":[],"name":"E_ConfigAmountTooLargeToEncode","type":"error"},{"inputs":[],"name":"E_ControllerDisabled","type":"error"},{"inputs":[],"name":"E_DebtAmountTooLargeToEncode","type":"error"},{"inputs":[],"name":"E_EmptyError","type":"error"},{"inputs":[],"name":"E_ExcessiveRepayAmount","type":"error"},{"inputs":[],"name":"E_FlashLoanNotRepaid","type":"error"},{"inputs":[],"name":"E_Initialized","type":"error"},{"inputs":[],"name":"E_InsufficientAllowance","type":"error"},{"inputs":[],"name":"E_InsufficientAssets","type":"error"},{"inputs":[],"name":"E_InsufficientBalance","type":"error"},{"inputs":[],"name":"E_InsufficientCash","type":"error"},{"inputs":[],"name":"E_InsufficientDebt","type":"error"},{"inputs":[],"name":"E_InvalidLTVAsset","type":"error"},{"inputs":[],"name":"E_LTVBorrow","type":"error"},{"inputs":[],"name":"E_LTVLiquidation","type":"error"},{"inputs":[],"name":"E_LiquidationCoolOff","type":"error"},{"inputs":[],"name":"E_MinYield","type":"error"},{"inputs":[],"name":"E_NoLiability","type":"error"},{"inputs":[],"name":"E_NoPriceOracle","type":"error"},{"inputs":[],"name":"E_NotController","type":"error"},{"inputs":[],"name":"E_NotHookTarget","type":"error"},{"inputs":[],"name":"E_NotSupported","type":"error"},{"inputs":[],"name":"E_OperationDisabled","type":"error"},{"inputs":[],"name":"E_OutstandingDebt","type":"error"},{"inputs":[],"name":"E_ProxyMetadata","type":"error"},{"inputs":[],"name":"E_Reentrancy","type":"error"},{"inputs":[],"name":"E_RepayTooMuch","type":"error"},{"inputs":[],"name":"E_SelfLiquidation","type":"error"},{"inputs":[],"name":"E_SelfTransfer","type":"error"},{"inputs":[],"name":"E_SupplyCapExceeded","type":"error"},{"inputs":[],"name":"E_TransientState","type":"error"},{"inputs":[],"name":"E_Unauthorized","type":"error"},{"inputs":[],"name":"E_ViolatorLiquidityDeferred","type":"error"},{"inputs":[],"name":"E_ZeroAssets","type":"error"},{"inputs":[],"name":"E_ZeroShares","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"BalanceForwarderStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"protocolReceiver","type":"address"},{"indexed":true,"internalType":"address","name":"governorReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"governorShares","type":"uint256"}],"name":"ConvertFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"DebtSocialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"address","name":"dToken","type":"address"}],"name":"EVaultCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"InterestAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"violator","type":"address"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"yieldBalance","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"PullDebt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalShares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accumulatedFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"cash","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulator","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"VaultStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"emitTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}]