Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/Lens/EulerEarnVaultLens.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {
IEulerEarn, IERC4626, MarketConfig, PendingUint136, PendingAddress
} from "euler-earn/interfaces/IEulerEarn.sol";
import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol";
import {UtilsLens} from "./UtilsLens.sol";
import {Utils} from "./Utils.sol";
import "./LensTypes.sol";
contract EulerEarnVaultLens is Utils {
UtilsLens public immutable utilsLens;
constructor(address _utilsLens) {
utilsLens = UtilsLens(_utilsLens);
}
function getVaultInfoFull(address vault) public view returns (EulerEarnVaultInfoFull memory) {
EulerEarnVaultInfoFull memory result;
result.timestamp = block.timestamp;
result.vault = vault;
result.vaultName = IEulerEarn(vault).name();
result.vaultSymbol = IEulerEarn(vault).symbol();
result.vaultDecimals = IEulerEarn(vault).decimals();
result.asset = IEulerEarn(vault).asset();
result.assetName = _getStringOrBytes32(result.asset, IEulerEarn(vault).name.selector);
result.assetSymbol = _getStringOrBytes32(result.asset, IEulerEarn(vault).symbol.selector);
result.assetDecimals = _getDecimals(result.asset);
result.totalShares = IEulerEarn(vault).totalSupply();
result.totalAssets = IEulerEarn(vault).totalAssets();
result.lostAssets = IEulerEarn(vault).lostAssets();
if (result.lostAssets > 0) {
uint256 coveredLostAssets = IEulerEarn(vault).convertToAssets(IEulerEarn(vault).balanceOf(address(1)));
result.lostAssets = result.lostAssets > coveredLostAssets ? result.lostAssets - coveredLostAssets : 0;
}
result.timelock = IEulerEarn(vault).timelock();
result.performanceFee = IEulerEarn(vault).fee();
result.feeReceiver = IEulerEarn(vault).feeRecipient();
result.owner = IEulerEarn(vault).owner();
result.creator = IEulerEarn(vault).creator();
result.curator = IEulerEarn(vault).curator();
result.guardian = IEulerEarn(vault).guardian();
result.evc = EVCUtil(vault).EVC();
result.permit2 = IEulerEarn(vault).permit2Address();
PendingUint136 memory pendingTimelock = IEulerEarn(vault).pendingTimelock();
PendingAddress memory pendingGuardian = IEulerEarn(vault).pendingGuardian();
result.pendingTimelock = pendingTimelock.value;
result.pendingTimelockValidAt = pendingTimelock.validAt;
result.pendingGuardian = pendingGuardian.value;
result.pendingGuardianValidAt = pendingGuardian.validAt;
result.supplyQueue = new address[](IEulerEarn(vault).supplyQueueLength());
for (uint256 i; i < result.supplyQueue.length; ++i) {
result.supplyQueue[i] = address(IEulerEarn(vault).supplyQueue(i));
}
result.strategies = new EulerEarnVaultStrategyInfo[](IEulerEarn(vault).withdrawQueueLength());
for (uint256 i; i < result.strategies.length; ++i) {
result.strategies[i] = getStrategyInfo(vault, address(IEulerEarn(vault).withdrawQueue(i)));
result.availableAssets += result.strategies[i].availableAssets;
}
return result;
}
function getStrategiesInfo(address vault, address[] calldata strategies)
public
view
returns (EulerEarnVaultStrategyInfo[] memory)
{
EulerEarnVaultStrategyInfo[] memory result = new EulerEarnVaultStrategyInfo[](strategies.length);
for (uint256 i; i < strategies.length; ++i) {
result[i] = getStrategyInfo(vault, strategies[i]);
}
return result;
}
function getStrategyInfo(address _vault, address _strategy)
public
view
returns (EulerEarnVaultStrategyInfo memory)
{
IEulerEarn vault = IEulerEarn(_vault);
IERC4626 strategy = IERC4626(_strategy);
MarketConfig memory config = vault.config(strategy);
PendingUint136 memory pendingConfig = vault.pendingCap(strategy);
return EulerEarnVaultStrategyInfo({
strategy: _strategy,
allocatedAssets: vault.expectedSupplyAssets(strategy),
availableAssets: vault.maxWithdrawFromStrategy(strategy),
currentAllocationCap: config.cap,
pendingAllocationCap: pendingConfig.value,
pendingAllocationCapValidAt: pendingConfig.validAt,
removableAt: config.removableAt,
info: utilsLens.getVaultInfoERC4626(_strategy)
});
}
}
"
},
"lib/euler-earn/src/interfaces/IEulerEarn.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IEulerEarnFactory} from "./IEulerEarnFactory.sol";
import {IERC4626} from "openzeppelin-contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Permit.sol";
import {MarketConfig, PendingUint136, PendingAddress} from "../libraries/PendingLib.sol";
struct MarketAllocation {
/// @notice The vault to allocate.
IERC4626 id;
/// @notice The amount of assets to allocate.
uint256 assets;
}
interface IOwnable {
function owner() external view returns (address);
function transferOwnership(address) external;
function renounceOwnership() external;
function acceptOwnership() external;
function pendingOwner() external view returns (address);
}
/// @dev This interface is used for factorizing IEulerEarnStaticTyping and IEulerEarn.
/// @dev Consider using the IEulerEarn interface instead of this one.
interface IEulerEarnBase {
/// @notice The address of the Permit2 contract.
function permit2Address() external view returns (address);
/// @notice The address of the creator.
function creator() external view returns (address);
/// @notice The address of the curator.
function curator() external view returns (address);
/// @notice Stores whether an address is an allocator or not.
function isAllocator(address target) external view returns (bool);
/// @notice The current guardian. Can be set even without the timelock set.
function guardian() external view returns (address);
/// @notice The current fee.
function fee() external view returns (uint96);
/// @notice The fee recipient.
function feeRecipient() external view returns (address);
/// @notice The current timelock.
function timelock() external view returns (uint256);
/// @dev Stores the order of vaults in which liquidity is supplied upon deposit.
/// @dev Can contain any vault. A vault is skipped as soon as its supply cap is reached.
function supplyQueue(uint256) external view returns (IERC4626);
/// @notice Returns the length of the supply queue.
function supplyQueueLength() external view returns (uint256);
/// @dev Stores the order of vault from which liquidity is withdrawn upon withdrawal.
/// @dev Always contain all non-zero cap vault as well as all vault on which the Earn vault supplies liquidity,
/// without duplicate.
function withdrawQueue(uint256) external view returns (IERC4626);
/// @notice Returns the length of the withdraw queue.
function withdrawQueueLength() external view returns (uint256);
/// @notice Returns the amount of assets that can be withdrawn from given strategy vault.
/// @dev Accounts for internally tracked balance, ignoring direct shares transfer and for assets available in the strategy.
function maxWithdrawFromStrategy(IERC4626 id) external view returns (uint256);
/// @notice Returns the amount of assets expected to be supplied to the strategy vault.
/// @dev Accounts for internally tracked balance, ignoring direct shares transfer.
function expectedSupplyAssets(IERC4626 id) external view returns (uint256);
/// @notice Stores the total assets managed by this vault when the fee was last accrued.
function lastTotalAssets() external view returns (uint256);
/// @notice Stores the missing assets due to realized bad debt or forced vault removal.
/// @dev In order to cover those lost assets, it is advised to supply on behalf of address(1) on the vault
/// (canonical method).
function lostAssets() external view returns (uint256);
/// @notice Submits a `newTimelock`.
/// @dev Warning: Reverts if a timelock is already pending. Revoke the pending timelock to overwrite it.
/// @dev In case the new timelock is higher than the current one, the timelock is set immediately.
function submitTimelock(uint256 newTimelock) external;
/// @notice Accepts the pending timelock.
function acceptTimelock() external;
/// @notice Revokes the pending timelock.
/// @dev Does not revert if there is no pending timelock.
function revokePendingTimelock() external;
/// @notice Submits a `newSupplyCap` for the vault.
/// @dev Warning: Reverts if a cap is already pending. Revoke the pending cap to overwrite it.
/// @dev Warning: Reverts if a vault removal is pending.
/// @dev In case the new cap is lower than the current one, the cap is set immediately.
/// @dev For the sake of backwards compatibility, the max allowed cap can either be set to type(uint184).max or type(uint136).max.
function submitCap(IERC4626 id, uint256 newSupplyCap) external;
/// @notice Accepts the pending cap of the vault.
function acceptCap(IERC4626 id) external;
/// @notice Revokes the pending cap of the vault.
/// @dev Does not revert if there is no pending cap.
function revokePendingCap(IERC4626 id) external;
/// @notice Submits a forced vault removal from the Earn vault, eventually losing all funds supplied to the vault.
/// @notice This forced removal is expected to be used as an emergency process in case a vault constantly reverts.
/// To softly remove a sane vault, the curator role is expected to bundle a reallocation that empties the vault
/// first (using `reallocate`), followed by the removal of the vault (using `updateWithdrawQueue`).
/// @dev Warning: Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap will
/// prevent such reverts.
function submitMarketRemoval(IERC4626 id) external;
/// @notice Revokes the pending removal of the vault.
/// @dev Does not revert if there is no pending vault removal.
function revokePendingMarketRemoval(IERC4626 id) external;
/// @notice Sets the name of the Earn vault.
function setName(string memory newName) external;
/// @notice Sets the symbol of the Earn vault.
function setSymbol(string memory newSymbol) external;
/// @notice Submits a `newGuardian`.
/// @notice Warning: a malicious guardian could disrupt the Earn vault's operation, and would have the power to revoke
/// any pending guardian.
/// @dev In case there is no guardian, the guardian is set immediately.
/// @dev Warning: Submitting a guardian will overwrite the current pending guardian.
function submitGuardian(address newGuardian) external;
/// @notice Accepts the pending guardian.
function acceptGuardian() external;
/// @notice Revokes the pending guardian.
function revokePendingGuardian() external;
/// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`).
function setIsAllocator(address newAllocator, bool newIsAllocator) external;
/// @notice Sets `curator` to `newCurator`.
function setCurator(address newCurator) external;
/// @notice Sets the `fee` to `newFee`.
function setFee(uint256 newFee) external;
/// @notice Sets `feeRecipient` to `newFeeRecipient`.
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Sets `supplyQueue` to `newSupplyQueue`.
/// @param newSupplyQueue is an array of enabled vaults, and can contain duplicate vaults, but it would only
/// increase the cost of depositing to the vault.
function setSupplyQueue(IERC4626[] calldata newSupplyQueue) external;
/// @notice Updates the withdraw queue. Some vaults can be removed, but no vault can be added.
/// @notice Removing a vault requires the vault to have 0 supply on it, or to have previously submitted a removal
/// for this vault (with the function `submitMarketRemoval`).
/// @notice Warning: Anyone can supply on behalf of the vault so the call to `updateWithdrawQueue` that expects a
/// vault to be empty can be griefed by a front-run. To circumvent this, the allocator can simply bundle a
/// reallocation that withdraws max from this vault with a call to `updateWithdrawQueue`.
/// @dev Warning: Removing a vault with supply will decrease the fee accrued until one of the functions updating
/// `lastTotalAssets` is triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient).
/// @dev Warning: `updateWithdrawQueue` is not idempotent. Submitting twice the same tx will change the queue twice.
/// @param indexes The indexes of each vault in the previous withdraw queue, in the new withdraw queue's order.
function updateWithdrawQueue(uint256[] calldata indexes) external;
/// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given vault.
/// @dev The behavior of the reallocation can be altered by state changes, including:
/// - Deposits on the Earn vault that supplies to vaults that are expected to be supplied to during reallocation.
/// - Withdrawals from the Earn vault that withdraws from vaults that are expected to be withdrawn from during
/// reallocation.
/// - Donations to the vault on vaults that are expected to be supplied to during reallocation.
/// - Withdrawals from vaults that are expected to be withdrawn from during reallocation.
/// @dev Sender is expected to pass `assets = type(uint256).max` with the last MarketAllocation of `allocations` to
/// supply all the remaining withdrawn liquidity, which would ensure that `totalWithdrawn` = `totalSupplied`.
/// @dev A supply in a reallocation step will make the reallocation revert if the amount is greater than the net
/// amount from previous steps (i.e. total withdrawn minus total supplied).
function reallocate(MarketAllocation[] calldata allocations) external;
}
/// @dev This interface is inherited by IEulerEarn so that function signatures are checked by the compiler.
/// @dev Consider using the IEulerEarn interface instead of this one.
interface IEulerEarnStaticTyping is IEulerEarnBase {
/// @notice Returns the current configuration of each vault.
function config(IERC4626) external view returns (uint112 balance, uint136 cap, bool enabled, uint64 removableAt);
/// @notice Returns the pending guardian.
function pendingGuardian() external view returns (address guardian, uint64 validAt);
/// @notice Returns the pending cap for each vault.
function pendingCap(IERC4626) external view returns (uint136 value, uint64 validAt);
/// @notice Returns the pending timelock.
function pendingTimelock() external view returns (uint136 value, uint64 validAt);
}
/// @title IEulerEarn
/// @author Forked with gratitude from Morpho Labs. Inspired by Silo Labs.
/// @custom:contact security@morpho.org
/// @custom:contact security@euler.xyz
/// @dev Use this interface for IEulerEarn to have access to all the functions with the appropriate function
/// signatures.
interface IEulerEarn is IEulerEarnBase, IERC4626, IERC20Permit, IOwnable {
/// @notice Returns the address of the Ethereum Vault Connector (EVC) used by this contract.
function EVC() external view returns (address);
/// @notice Returns the current configuration of each vault.
function config(IERC4626) external view returns (MarketConfig memory);
/// @notice Returns the pending guardian.
function pendingGuardian() external view returns (PendingAddress memory);
/// @notice Returns the pending cap for each vault.
function pendingCap(IERC4626) external view returns (PendingUint136 memory);
/// @notice Returns the pending timelock.
function pendingTimelock() external view returns (PendingUint136 memory);
}
"
},
"lib/ethereum-vault-connector/src/utils/EVCUtil.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEVC} from "../interfaces/IEthereumVaultConnector.sol";
import {ExecutionContext, EC} from "../ExecutionContext.sol";
/// @title EVCUtil
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice This contract is an abstract base contract for interacting with the Ethereum Vault Connector (EVC).
/// It provides utility functions for authenticating the callers in the context of the EVC, a pattern for enforcing the
/// contracts to be called through the EVC.
abstract contract EVCUtil {
using ExecutionContext for EC;
uint160 internal constant ACCOUNT_ID_OFFSET = 8;
IEVC internal immutable evc;
error EVC_InvalidAddress();
error NotAuthorized();
error ControllerDisabled();
constructor(address _evc) {
if (_evc == address(0)) revert EVC_InvalidAddress();
evc = IEVC(_evc);
}
/// @notice Returns the address of the Ethereum Vault Connector (EVC) used by this contract.
/// @return The address of the EVC contract.
function EVC() external view virtual returns (address) {
return address(evc);
}
/// @notice Ensures that the msg.sender is the EVC by using the EVC callback functionality if necessary.
/// @dev Optional to use for functions requiring account and vault status checks to enforce predictable behavior.
/// @dev If this modifier used in conjuction with any other modifier, it must appear as the first (outermost)
/// modifier of the function.
modifier callThroughEVC() virtual {
_callThroughEVC();
_;
}
/// @notice Ensures that the caller is the EVC in the appropriate context.
/// @dev Should be used for checkAccountStatus and checkVaultStatus functions.
modifier onlyEVCWithChecksInProgress() virtual {
_onlyEVCWithChecksInProgress();
_;
}
/// @notice Ensures a standard authentication path on the EVC allowing the account owner or any of its EVC accounts.
/// @dev This modifier checks if the caller is the EVC and if so, verifies the execution context.
/// It reverts if the operator is authenticated, control collateral is in progress, or checks are in progress.
/// @dev This modifier must not be used on functions utilized by liquidation flows, i.e. transfer or withdraw.
/// @dev This modifier must not be used on checkAccountStatus and checkVaultStatus functions.
/// @dev This modifier can be used on access controlled functions to prevent non-standard authentication paths on
/// the EVC.
modifier onlyEVCAccount() virtual {
_authenticateCallerWithStandardContextState(false);
_;
}
/// @notice Ensures a standard authentication path on the EVC.
/// @dev This modifier checks if the caller is the EVC and if so, verifies the execution context.
/// It reverts if the operator is authenticated, control collateral is in progress, or checks are in progress.
/// It reverts if the authenticated account owner is known and it is not the account owner.
/// @dev It assumes that if the caller is not the EVC, the caller is the account owner.
/// @dev This modifier must not be used on functions utilized by liquidation flows, i.e. transfer or withdraw.
/// @dev This modifier must not be used on checkAccountStatus and checkVaultStatus functions.
/// @dev This modifier can be used on access controlled functions to prevent non-standard authentication paths on
/// the EVC.
modifier onlyEVCAccountOwner() virtual {
_authenticateCallerWithStandardContextState(true);
_;
}
/// @notice Checks whether the specified account and the other account have the same owner.
/// @dev The function is used to check whether one account is authorized to perform operations on behalf of the
/// other. Accounts are considered to have a common owner if they share the first 19 bytes of their address.
/// @param account The address of the account that is being checked.
/// @param otherAccount The address of the other account that is being checked.
/// @return A boolean flag that indicates whether the accounts have the same owner.
function _haveCommonOwner(address account, address otherAccount) internal pure returns (bool) {
bool result;
assembly {
result := lt(xor(account, otherAccount), 0x100)
}
return result;
}
/// @notice Returns the address prefix of the specified account.
/// @dev The address prefix is the first 19 bytes of the account address.
/// @param account The address of the account whose address prefix is being retrieved.
/// @return A bytes19 value that represents the address prefix of the account.
function _getAddressPrefix(address account) internal pure returns (bytes19) {
return bytes19(uint152(uint160(account) >> ACCOUNT_ID_OFFSET));
}
/// @notice Retrieves the message sender in the context of the EVC.
/// @dev This function returns the account on behalf of which the current operation is being performed, which is
/// either msg.sender or the account authenticated by the EVC.
/// @return The address of the message sender.
function _msgSender() internal view virtual returns (address) {
address sender = msg.sender;
if (sender == address(evc)) {
(sender,) = evc.getCurrentOnBehalfOfAccount(address(0));
}
return sender;
}
/// @notice Retrieves the message sender in the context of the EVC for a borrow operation.
/// @dev This function returns the account on behalf of which the current operation is being performed, which is
/// either msg.sender or the account authenticated by the EVC. This function reverts if this contract is not enabled
/// as a controller for the account on behalf of which the operation is being executed.
/// @return The address of the message sender.
function _msgSenderForBorrow() internal view virtual returns (address) {
address sender = msg.sender;
bool controllerEnabled;
if (sender == address(evc)) {
(sender, controllerEnabled) = evc.getCurrentOnBehalfOfAccount(address(this));
} else {
controllerEnabled = evc.isControllerEnabled(sender, address(this));
}
if (!controllerEnabled) {
revert ControllerDisabled();
}
return sender;
}
/// @notice Retrieves the message sender, ensuring it's any EVC account meaning that the execution context is in a
/// standard state (not operator authenticated, not control collateral in progress, not checks in progress).
/// @dev This function must not be used on functions utilized by liquidation flows, i.e. transfer or withdraw.
/// @dev This function must not be used on checkAccountStatus and checkVaultStatus functions.
/// @dev This function can be used on access controlled functions to prevent non-standard authentication paths on
/// the EVC.
/// @return The address of the message sender.
function _msgSenderOnlyEVCAccount() internal view returns (address) {
return _authenticateCallerWithStandardContextState(false);
}
/// @notice Retrieves the message sender, ensuring it's the EVC account owner and that the execution context is in a
/// standard state (not operator authenticated, not control collateral in progress, not checks in progress).
/// @dev It assumes that if the caller is not the EVC, the caller is the account owner.
/// @dev This function must not be used on functions utilized by liquidation flows, i.e. transfer or withdraw.
/// @dev This function must not be used on checkAccountStatus and checkVaultStatus functions.
/// @dev This function can be used on access controlled functions to prevent non-standard authentication paths on
/// the EVC.
/// @return The address of the message sender.
function _msgSenderOnlyEVCAccountOwner() internal view returns (address) {
return _authenticateCallerWithStandardContextState(true);
}
/// @notice Calls the current external function through the EVC.
/// @dev This function is used to route the current call through the EVC if it's not already coming from the EVC. It
/// makes the EVC set the execution context and call back this contract with unchanged calldata. msg.sender is used
/// as the onBehalfOfAccount.
/// @dev This function shall only be used by the callThroughEVC modifier.
function _callThroughEVC() internal {
address _evc = address(evc);
if (msg.sender == _evc) return;
assembly {
mstore(0, 0x1f8b521500000000000000000000000000000000000000000000000000000000) // EVC.call selector
mstore(4, address()) // EVC.call 1st argument - address(this)
mstore(36, caller()) // EVC.call 2nd argument - msg.sender
mstore(68, callvalue()) // EVC.call 3rd argument - msg.value
mstore(100, 128) // EVC.call 4th argument - msg.data, offset to the start of encoding - 128 bytes
mstore(132, calldatasize()) // msg.data length
calldatacopy(164, 0, calldatasize()) // original calldata
// abi encoded bytes array should be zero padded so its length is a multiple of 32
// store zero word after msg.data bytes and round up calldatasize to nearest multiple of 32
mstore(add(164, calldatasize()), 0)
let result := call(gas(), _evc, callvalue(), 0, add(164, and(add(calldatasize(), 31), not(31))), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(64, sub(returndatasize(), 64)) } // strip bytes encoding from call return
}
}
/// @notice Ensures that the function is called only by the EVC during the checks phase
/// @dev Reverts if the caller is not the EVC or if checks are not in progress.
function _onlyEVCWithChecksInProgress() internal view {
if (msg.sender != address(evc) || !evc.areChecksInProgress()) {
revert NotAuthorized();
}
}
/// @notice Ensures that the function is called only by the EVC account owner or any of its EVC accounts
/// @dev This function checks if the caller is the EVC and if so, verifies that the execution context is not in a
/// special state (operator authenticated, collateral control in progress, or checks in progress). If
/// onlyAccountOwner is true and the owner was already registered on the EVC, it verifies that the onBehalfOfAccount
/// is the owner. If onlyAccountOwner is false, it allows any EVC account of the owner to call the function.
/// @param onlyAccountOwner If true, only allows the account owner; if false, allows any EVC account of the owner
/// @return The address of the message sender.
function _authenticateCallerWithStandardContextState(bool onlyAccountOwner) internal view returns (address) {
if (msg.sender == address(evc)) {
EC ec = EC.wrap(evc.getRawExecutionContext());
if (ec.isOperatorAuthenticated() || ec.isControlCollateralInProgress() || ec.areChecksInProgress()) {
revert NotAuthorized();
}
address onBehalfOfAccount = ec.getOnBehalfOfAccount();
if (onlyAccountOwner) {
address owner = evc.getAccountOwner(onBehalfOfAccount);
if (owner != address(0) && owner != onBehalfOfAccount) {
revert NotAuthorized();
}
}
return onBehalfOfAccount;
}
return msg.sender;
}
}
"
},
"src/Lens/UtilsLens.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {GenericFactory} from "evk/GenericFactory/GenericFactory.sol";
import {IEVault} from "evk/EVault/IEVault.sol";
import {IPriceOracle} from "euler-price-oracle/interfaces/IPriceOracle.sol";
import {OracleLens} from "./OracleLens.sol";
import {Utils} from "./Utils.sol";
import "./LensTypes.sol";
contract UtilsLens is Utils {
GenericFactory public immutable eVaultFactory;
OracleLens public immutable oracleLens;
constructor(address _eVaultFactory, address _oracleLens) {
eVaultFactory = GenericFactory(_eVaultFactory);
oracleLens = OracleLens(_oracleLens);
}
function getVaultInfoERC4626(address vault) public view returns (VaultInfoERC4626 memory) {
VaultInfoERC4626 memory result;
result.timestamp = block.timestamp;
result.vault = vault;
result.vaultName = IEVault(vault).name();
result.vaultSymbol = IEVault(vault).symbol();
result.vaultDecimals = IEVault(vault).decimals();
result.asset = IEVault(vault).asset();
result.assetName = _getStringOrBytes32(result.asset, IEVault(vault).name.selector);
result.assetSymbol = _getStringOrBytes32(result.asset, IEVault(vault).symbol.selector);
result.assetDecimals = _getDecimals(result.asset);
result.totalShares = IEVault(vault).totalSupply();
result.totalAssets = IEVault(vault).totalAssets();
result.isEVault = eVaultFactory.isProxy(vault);
return result;
}
function getAPYs(address vault) external view returns (uint256 borrowAPY, uint256 supplyAPY) {
return _computeAPYs(
IEVault(vault).interestRate(),
IEVault(vault).cash(),
IEVault(vault).totalBorrows(),
IEVault(vault).interestFee()
);
}
function computeAPYs(uint256 borrowSPY, uint256 cash, uint256 borrows, uint256 interestFee)
external
pure
returns (uint256 borrowAPY, uint256 supplyAPY)
{
return _computeAPYs(borrowSPY, cash, borrows, interestFee);
}
function calculateTimeToLiquidation(
address liabilityVault,
uint256 liabilityValue,
address[] memory collaterals,
uint256[] memory collateralValues
) external view returns (int256) {
return _calculateTimeToLiquidation(liabilityVault, liabilityValue, collaterals, collateralValues);
}
function getControllerAssetPriceInfo(address controller, address asset)
public
view
returns (AssetPriceInfo memory)
{
AssetPriceInfo memory result;
result.timestamp = block.timestamp;
result.oracle = IEVault(controller).oracle();
result.asset = asset;
result.unitOfAccount = IEVault(controller).unitOfAccount();
result.amountIn = 10 ** _getDecimals(asset);
if (result.oracle == address(0)) {
result.queryFailure = true;
return result;
}
(bool success, bytes memory data) = result.oracle.staticcall(
abi.encodeCall(IPriceOracle.getQuote, (result.amountIn, asset, result.unitOfAccount))
);
if (success && data.length >= 32) {
result.amountOutMid = abi.decode(data, (uint256));
} else {
result.queryFailure = true;
result.queryFailureReason = data;
}
(success, data) = result.oracle.staticcall(
abi.encodeCall(IPriceOracle.getQuotes, (result.amountIn, asset, result.unitOfAccount))
);
if (success && data.length >= 64) {
(result.amountOutBid, result.amountOutAsk) = abi.decode(data, (uint256, uint256));
} else {
result.queryFailure = true;
}
return result;
}
function getAssetPriceInfo(address asset, address unitOfAccount) public view returns (AssetPriceInfo memory) {
AssetPriceInfo memory result;
result.timestamp = block.timestamp;
result.asset = asset;
result.unitOfAccount = unitOfAccount;
result.amountIn = 10 ** _getDecimals(asset);
address[] memory adapters = oracleLens.getValidAdapters(asset, unitOfAccount);
uint256 amountIn = result.amountIn;
if (adapters.length == 0) {
(bool success, bytes memory data) =
asset.staticcall{gas: 100000}(abi.encodeCall(IEVault(asset).convertToAssets, (amountIn)));
if (success && data.length >= 32) {
amountIn = abi.decode(data, (uint256));
(success, data) = asset.staticcall(abi.encodeCall(IEVault(asset).asset, ()));
if (success && data.length >= 32) {
asset = abi.decode(data, (address));
adapters = oracleLens.getValidAdapters(asset, unitOfAccount);
}
}
}
if (adapters.length == 0) {
result.queryFailure = true;
return result;
}
for (uint256 i = 0; i < adapters.length; ++i) {
result.oracle = adapters[i];
result.queryFailure = false;
result.queryFailureReason = "";
(bool success, bytes memory data) =
result.oracle.staticcall(abi.encodeCall(IPriceOracle.getQuote, (amountIn, asset, unitOfAccount)));
if (success && data.length >= 32) {
result.amountOutMid = abi.decode(data, (uint256));
} else {
result.queryFailure = true;
result.queryFailureReason = data;
}
(success, data) =
result.oracle.staticcall(abi.encodeCall(IPriceOracle.getQuotes, (amountIn, asset, unitOfAccount)));
if (success && data.length >= 64) {
(result.amountOutBid, result.amountOutAsk) = abi.decode(data, (uint256, uint256));
} else {
result.queryFailure = true;
}
if (!result.queryFailure) break;
}
return result;
}
function tokenBalances(address account, address[] calldata tokens) public view returns (uint256[] memory) {
uint256[] memory balances = new uint256[](tokens.length);
for (uint256 i = 0; i < tokens.length; ++i) {
if (tokens[i] != address(0)) balances[i] = IEVault(tokens[i]).balanceOf(account);
else balances[i] = account.balance;
}
return balances;
}
function tokenAllowances(address spender, address account, address[] calldata tokens)
public
view
returns (uint256[] memory)
{
uint256[] memory allowances = new uint256[](tokens.length);
for (uint256 i = 0; i < tokens.length; ++i) {
allowances[i] = IEVault(tokens[i]).allowance(account, spender);
}
return allowances;
}
}
"
},
"src/Lens/Utils.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IEVault} from "evk/EVault/IEVault.sol";
import {RPow} from "evk/EVault/shared/lib/RPow.sol";
abstract contract Utils {
uint256 internal constant SECONDS_PER_YEAR = 365.2425 * 86400;
uint256 internal constant ONE = 1e27;
uint256 internal constant CONFIG_SCALE = 1e4;
uint256 internal constant TTL_HS_ACCURACY = ONE / 1e4;
int256 internal constant TTL_COMPUTATION_MIN = 0;
int256 internal constant TTL_COMPUTATION_MAX = 400 * 1 days;
int256 public constant TTL_INFINITY = type(int256).max;
int256 public constant TTL_MORE_THAN_ONE_YEAR = type(int256).max - 1;
int256 public constant TTL_LIQUIDATION = -1;
int256 public constant TTL_ERROR = -2;
function getWETHAddress() internal view returns (address) {
if (block.chainid == 1) {
return 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
} else if (
block.chainid == 10 || block.chainid == 130 || block.chainid == 8453 || block.chainid == 1923
|| block.chainid == 480 || block.chainid == 57073 || block.chainid == 60808
) {
return 0x4200000000000000000000000000000000000006;
} else if (block.chainid == 56) {
return 0x2170Ed0880ac9A755fd29B2688956BD959F933F8;
} else if (block.chainid == 100) {
return 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1;
} else if (block.chainid == 137) {
return 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619;
} else if (block.chainid == 146) {
return 0x50c42dEAcD8Fc9773493ED674b675bE577f2634b;
} else if (block.chainid == 42161) {
return 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
} else if (block.chainid == 43114) {
return 0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB;
} else if (block.chainid == 80094) {
return 0x2F6F07CDcf3588944Bf4C42aC74ff24bF56e7590;
} else {
// bitcoin-specific and test networks
if (
block.chainid == 30 || block.chainid == 21000000 || block.chainid == 10143 || block.chainid == 80084
|| block.chainid == 2390 || block.chainid == 998
) {
return address(0);
}
// hyperEVM
if (block.chainid == 999) {
return address(0);
}
// TAC
if (block.chainid == 239) {
return address(0);
}
// Plasma
if (block.chainid == 9745) {
return address(0);
}
// Monad
if (block.chainid == 143) {
return address(0);
}
// Sepolia
if (block.chainid == 11155111) {
return address(0);
}
}
revert("getWETHAddress: Unsupported chain");
}
function _strEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
/// @dev for tokens like MKR which return bytes32 on name() or symbol()
function _getStringOrBytes32(address contractAddress, bytes4 selector) internal view returns (string memory) {
(bool success, bytes memory result) = contractAddress.staticcall(abi.encodeWithSelector(selector));
return (success && result.length != 0)
? result.length == 32 ? string(abi.encodePacked(result)) : abi.decode(result, (string))
: "";
}
function _getDecimals(address contractAddress) internal view returns (uint8) {
(bool success, bytes memory data) =
contractAddress.staticcall(abi.encodeCall(IEVault(contractAddress).decimals, ()));
return success && data.length >= 32 ? abi.decode(data, (uint8)) : 18;
}
function _computeAPYs(uint256 borrowSPY, uint256 cash, uint256 borrows, uint256 interestFee)
internal
pure
returns (uint256 borrowAPY, uint256 supplyAPY)
{
uint256 totalAssets = cash + borrows;
bool overflow;
(borrowAPY, overflow) = RPow.rpow(borrowSPY + ONE, SECONDS_PER_YEAR, ONE);
if (overflow) return (0, 0);
borrowAPY -= ONE;
supplyAPY =
totalAssets == 0 ? 0 : borrowAPY * borrows * (CONFIG_SCALE - interestFee) / totalAssets / CONFIG_SCALE;
}
struct CollateralInfo {
uint256 borrowSPY;
uint256 borrows;
uint256 totalAssets;
uint256 interestFee;
uint256 borrowInterest;
}
function _calculateTimeToLiquidation(
address liabilityVault,
uint256 liabilityValue,
address[] memory collaterals,
uint256[] memory collateralValues
) internal view returns (int256) {
// if there's no liability, time to liquidation is infinite
if (liabilityValue == 0) return TTL_INFINITY;
// get borrow interest rate
uint256 liabilitySPY;
{
(bool success, bytes memory data) =
liabilityVault.staticcall(abi.encodeCall(IEVault(liabilityVault).interestRate, ()));
if (success && data.length >= 32) {
liabilitySPY = abi.decode(data, (uint256));
}
}
// get individual collateral interest rates and total collateral value
CollateralInfo[] memory collateralInfos = new CollateralInfo[](collaterals.length);
uint256 collateralValue;
for (uint256 i = 0; i < collaterals.length; ++i) {
address collateral = collaterals[i];
(bool success, bytes memory data) =
collateral.staticcall(abi.encodeCall(IEVault(collateral).interestRate, ()));
if (success && data.length >= 32) {
collateralInfos[i].borrowSPY = abi.decode(data, (uint256));
}
(success, data) = collateral.staticcall(abi.encodeCall(IEVault(collateral).totalBorrows, ()));
if (success && data.length >= 32) {
collateralInfos[i].borrows = abi.decode(data, (uint256));
}
(success, data) = collateral.staticcall(abi.encodeCall(IEVault(collateral).cash, ()));
if (success && data.length >= 32) {
collateralInfos[i].totalAssets = abi.decode(data, (uint256)) + collateralInfos[i].borrows;
}
(success, data) = collateral.staticcall(abi.encodeCall(IEVault(collateral).interestFee, ()));
if (success && data.length >= 32) {
collateralInfos[i].interestFee = abi.decode(data, (uint256));
}
collateralValue += collateralValues[i];
}
// if liability is greater than or equal to collateral, the account is eligible for liquidation right away
if (liabilityValue >= collateralValue) return TTL_LIQUIDATION;
// if there's no borrow interest rate, time to liquidation is infinite
if (liabilitySPY == 0) return TTL_INFINITY;
int256 minTTL = TTL_COMPUTATION_MIN;
int256 maxTTL = TTL_COMPUTATION_MAX;
int256 ttl;
// calculate time to liquidation using binary search
while (true) {
ttl = minTTL + (maxTTL - minTTL) / 2;
// break if the search range is too small
if (maxTTL <= minTTL + 1 days) break;
if (ttl < 1 days) break;
// calculate the liability interest accrued
uint256 liabilityInterest;
if (liabilitySPY > 0) {
(uint256 multiplier, bool overflow) = RPow.rpow(liabilitySPY + ONE, uint256(ttl), ONE);
if (overflow) return TTL_ERROR;
liabilityInterest = liabilityValue * multiplier / ONE - liabilityValue;
}
// calculate the collaterals interest accrued
uint256 collateralInterest;
for (uint256 i = 0; i < collaterals.length; ++i) {
if (collateralInfos[i].borrowSPY == 0 || collateralInfos[i].totalAssets == 0) continue;
(uint256 multiplier, bool overflow) = RPow.rpow(collateralInfos[i].borrowSPY + ONE, uint256(ttl), ONE);
if (overflow) return TTL_ERROR;
collateralInfos[i].borrowInterest = collateralValues[i] * multiplier / ONE - collateralValues[i];
collateralInterest += collateralInfos[i].borrowInterest * collateralInfos[i].borrows
* (CONFIG_SCALE - collateralInfos[i].interestFee) / collateralInfos[i].totalAssets / CONFIG_SCALE;
}
// calculate the health factor
uint256 hs = (collateralValue + collateralInterest) * ONE / (liabilityValue + liabilityInterest);
// if the collateral interest accrues fater than the liability interest, the account should never be
// liquidated
if (collateralInterest >= liabilityInterest) return TTL_INFINITY;
// if the health factor is within the acceptable range, return the time to liquidation
if (hs >= ONE && hs - ONE <= TTL_HS_ACCURACY) break;
if (hs < ONE && ONE - hs <= TTL_HS_ACCURACY) break;
// adjust the search range
if (hs >= ONE) minTTL = ttl + 1 days;
else maxTTL = ttl - 1 days;
}
return ttl > int256(SECONDS_PER_YEAR) ? TTL_MORE_THAN_ONE_YEAR : int256(ttl) / 1 days;
}
}
"
},
"src/Lens/LensTypes.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
struct AccountInfo {
EVCAccountInfo evcAccountInfo;
VaultAccountInfo vaultAccountInfo;
AccountRewardInfo accountRewardInfo;
}
struct AccountMultipleVaultsInfo {
EVCAccountInfo evcAccountInfo;
VaultAccountInfo[] vaultAccountInfo;
AccountRewardInfo[] accountRewardInfo;
}
struct EVCAccountInfo {
uint256 timestamp;
address evc;
address account;
bytes19 addressPrefix;
address owner;
bool isLockdownMode;
bool isPermitDisabledMode;
uint256 lastAccountStatusCheckTimestamp;
address[] enabledControllers;
address[] enabledCollaterals;
}
struct VaultAccountInfo {
uint256 timestamp;
address account;
address vault;
address asset;
uint256 assetsAccount;
uint256 shares;
uint256 assets;
uint256 borrowed;
uint256 assetAllowanceVault;
uint256 assetAllowanceVaultPermit2;
uint256 assetAllowanceExpirationVaultPermit2;
uint256 assetAllowancePermit2;
bool balanceForwarderEnabled;
bool isController;
bool isCollateral;
AccountLiquidityInfo liquidityInfo;
}
struct AccountLiquidityInfo {
bool queryFailure;
bytes queryFailureReason;
address account;
address vault;
address unitOfAccount;
int256 timeToLiquidation;
uint256 liabilityValueBorrowing;
uint256 liabilityValueLiquidation;
uint256 collateralValueBorrowing;
uint256 collateralValueLiquidation;
uint256 collateralValueRaw;
address[] collaterals;
uint256[] collateralValuesBorrowing;
uint256[] collateralValuesLiquidation;
uint256[] collateralValuesRaw;
}
struct VaultInfoERC4626 {
uint256 timestamp;
address vault;
string vaultName;
string vaultSymbol;
uint256 vaultDecimals;
address asset;
string assetName;
string assetSymbol;
uint256 assetDecimals;
uint256 totalShares;
uint256 totalAssets;
bool isEVault;
}
struct VaultInfoStatic {
uint256 timestamp;
address vault;
string vaultName;
string vaultSymbol;
uint256 vaultDecimals;
address asset;
string assetName;
string assetSymbol;
uint256 assetDecimals;
address unitOfAccount;
string unitOfAccountName;
string unitOfAccountSymbol;
uint256 unitOfAccountDecimals;
address dToken;
address oracle;
address evc;
address protocolConfig;
address balanceTracker;
address permit2;
address creator;
}
struct VaultInfoDynamic {
uint256 timestamp;
address vault;
uint256 totalShares;
uint256 totalCash;
uint256 totalBorrowed;
uint256 totalAssets;
uint256 accumulatedFeesShares;
uint256 accumulatedFeesAssets;
address governorFeeReceiver;
address protocolFeeReceiver;
uint256 protocolFeeShare;
uint256 interestFee;
uint256 hookedOperations;
uint256 configFlags;
uint256 supplyCap;
uint256 borrowCap;
uint256 maxLiquidationDiscount;
uint256 liquidationCoolOffTime;
address interestRateModel;
address hookTarget;
address governorAdmin;
VaultInterestRateModelInfo irmInfo;
LTVInfo[] collateralLTVInfo;
AssetPriceInfo liabilityPriceInfo;
AssetPriceInfo[] collateralPriceInfo;
OracleDetailedInfo oracleInfo;
AssetPriceInfo backupAssetPriceInfo;
OracleDetailedInfo backupAssetOracleInfo;
}
struct VaultInfoFull {
uint256 timestamp;
address vault;
string vaultName;
string vaultSymbol;
uint256 vaultDecimals;
address asset;
string assetName;
string assetSymbol;
uint256 assetDecimals;
address unitOfAccount;
string unitOfAccountName;
string unitOfAccountSymbol;
uint256 unitOfAccountDecimals;
uint256 totalShares;
uint256 totalCash;
uint256 totalBorrowed;
uint256 totalAssets;
uint256 accumulatedFeesShares;
uint256 accumulatedFeesAssets;
address governorFeeReceiver;
address protocolFeeReceiver;
uint256 protocolFeeShare;
uint256 interestFee;
uint256 hookedOperations;
uint256 configFlags;
uint256 supplyCap;
uint256 borrowCap;
uint256 maxLiquidationDiscount;
uint256 liquidationCoolOffTime;
address dToken;
address oracle;
address interestRateModel;
address hookTarget;
address evc;
address protocolConfig;
address balanceTracker;
address permit2;
address creator;
address governorAdmin;
VaultInterestRateModelInfo irmInfo;
LTVInfo[] collateralLTVInfo;
AssetPriceInfo liabilityPriceInfo;
AssetPriceInfo[] collateralPriceInfo;
OracleDetailedInfo oracleInfo;
AssetPriceInfo backupAssetPriceInfo;
OracleDetailedInfo backupAssetOracleInfo;
}
struct LTVInfo {
address collateral;
uint256 borrowLTV;
uint256 liquidationLTV;
uint256 initialLiquidationLTV;
uint256 targetTimestamp;
uint256 rampDuration;
}
struct AssetPriceInfo {
bool queryFailure;
bytes queryFailureReason;
uint256 timestamp;
address oracle;
address asset;
address unitOfAccount;
uint256 amountIn;
uint256 amountOutMid;
uint256 amountOutBid;
uint256 amountOutAsk;
}
struct VaultInterestRateModelInfo {
bool queryFailure;
bytes queryFailureReason;
address vault;
address interestRateModel;
InterestRateInfo[] interestRateInfo;
InterestRateModelDetailedInfo interestRateModelInfo;
}
struct InterestRateInfo {
uint256 cash;
uint256 borrows;
uint256 borrowSPY;
uint256 borrowAPY;
uint256 supplyAPY;
}
enum InterestRateModelType {
UNKNOWN,
KINK,
ADAPTIVE_CURVE,
KINKY,
FIXED_CYCLICAL_BINARY
}
struct InterestRateModelDetailedInfo {
address interestRateModel;
InterestRateModelType interestRateModelType;
bytes interestRateModelParams;
}
struct KinkIRMInfo {
uint256 baseRate;
uint256 slope1;
uint256 slope2;
uint256 kink;
}
struct AdaptiveCurveIRMInfo {
int256 targetUtilization;
int256 initialRateAtTarget;
int256 minRateAtTarget;
int256 maxRateAtTarget;
int256 curveSteepness;
int256 adjustmentSpeed;
}
struct KinkyIRMInfo {
uint256 baseRate;
uint256 slope;
uint256 shape;
uint256 kink;
uint256 cutoff;
}
struct FixedCyclicalBinaryIRMInfo {
uint256 primaryRate;
uint256 secondaryRate;
uint256 primaryDuration;
uint256 secondaryDuration;
uint256 startTimestamp;
}
struct AccountRewardInfo {
uint256 timestamp;
address account;
address vault;
address balanceTracker;
bool balanceForwarderEnabled;
uint256 balance;
EnabledRewardInfo[] enabledRewardsInfo;
}
struct EnabledRewardInfo {
address reward;
uint256 earnedReward;
uint256 earnedRewardRecentIgnored;
}
struct VaultRewardInfo {
uint256 timestamp;
address vault;
address reward;
string rewardName;
string rewardSymbol;
uint8 rewardDecimals;
address balanceTracker;
uint256 epochDuration;
uint256 currentEpoch;
uint256 totalRewardedEligible;
uint256 totalRewardRegistered;
uint256 totalRewardClaimed;
RewardAmountInfo[] epochInfoPrevious;
RewardAmountInfo[] epochInfoUpcoming;
}
struct RewardAmountInfo {
uint256 epoch;
uint256 epochStart;
uint256 epochEnd;
uint256 rewardAmount;
}
struct OracleDetailedInfo {
address oracle;
string name;
bytes oracleInfo;
}
struct EulerRouterInfo {
address governor;
address fallbackOracle;
OracleDetailedInfo fallbackOracleInfo;
address[] bases;
address[] quotes;
address[][] resolvedAssets;
address[] resolvedOracles;
OracleDetailedInfo[] resolvedOraclesInfo;
}
struct ChainlinkOracleInfo {
address base;
address quote;
address feed;
string feedDescription;
uint256 maxStaleness;
}
struct ChainlinkInfrequentOracleInfo {
address base;
address quote;
address feed;
string feedDescription;
uint256 maxStaleness;
}
struct ChronicleOracleInfo {
address base;
address quote;
address feed;
uint256 maxStaleness;
}
struct LidoOracleInfo {
address base;
address quote;
}
struct LidoFundamentalOracleInfo {
address base;
address quote;
}
struct PythOracleInfo {
address pyth;
address base;
address quote;
bytes32 feedId;
uint256 maxStaleness;
uint256 maxConfWidth;
}
struct RedstoneCoreOracleInfo {
address base;
address quote;
bytes32 feedId;
uint8 feedDecimals;
uint256 maxStaleness;
uint208 cachePrice;
uint48 cachePriceTimestamp;
}
struct UniswapV3OracleInfo {
address tokenA;
address tokenB;
address pool;
uint24 fee;
uint32 twapWindow;
}
struct FixedRateOracleInfo {
address base;
address quote;
uint256 rate;
}
struct RateProviderOracleInfo {
address base;
address quote;
address rateProvider;
}
struct OndoOracleInfo {
address base;
address quote;
address rwaOracle;
}
struct PendleProviderOracleInfo {
address base;
address quote;
address pendleMarket;
uint32 twapWindow;
}
struct PendleUniversalOracleInfo {
address base;
address quote;
address pendleMarket;
uint32 twapWindow;
}
struct CurveEMAOracleInfo {
address base;
address quote;
address pool;
uint256 priceOracleIndex;
}
struct SwaapSafeguardProviderOracleInfo {
address base;
address quote;
bytes32 poolId;
}
struct CrossAdapterInfo {
address base;
address cross;
address quote;
address oracleBaseCross;
address oracleCrossQuote;
OracleDetailedInfo oracleBaseCrossInfo;
OracleDetailedInfo oracleCrossQuoteInfo;
}
struct EulerEarnVaultInfoFull {
uint256 timestamp;
address vault;
string vaultName;
string vaultSymbol;
uint256 vaultDecimals;
address asset;
string assetName;
string assetSymbol;
uint256 assetDecimals;
uint256 totalShares;
uint256 totalAssets;
uint256 lostAssets;
uint256 availableAssets;
uint256 timelock;
uint256 performanceFee;
address feeReceiver;
address owner;
address creator;
address curator;
address guardian;
address evc;
address permit2;
uint256 pendingTimelock;
uint256 pendingTimelockValidAt;
address pendingGuardian;
uint256 pendingGuardianValidAt;
address[] supplyQueue;
EulerEarnVaultStrategyInfo[] strategies;
}
struct EulerEarnVaultStrategyInfo {
address strategy;
uint256 allocatedAssets;
uint256 availableAssets;
uint256 currentAllocationCap;
uint256 pendingAllocationCap;
uint256 pendingAllocationCapValidAt;
uint256 removableAt;
VaultInfoERC4626 info;
}
"
},
"lib/euler-earn/src/interfaces/IEulerEarnFactory.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IEulerEarn} from "./IEulerEarn.sol";
/// @title IEulerEarnFactory
/// @author Forked with gratitude from Morpho Labs. Inspired by Silo Labs.
/// @custom:contact security@morpho.org
/// @custom:contact security@euler.xyz
/// @notice Interface of EulerEarn's factory.
interface IEulerEarnFactory {
/// @notice The address of the Permit2 contract.
function permit2Address() external view returns (address);
/// @notice The address of the supported perspective contract.
function supportedPerspective() external view returns (address);
/// @notice Whether a vault was created with the factory.
function isVault(address target) external view returns (bool);
/// @notice Fetch the length of the deployed proxies list
/// @return The length of the proxy list array
function getVaultListLength() external view returns (uint256);
/// @notice Get a slice of the deployed proxies array
/// @param start Start index of the slice
/// @param end End index of the slice
/// @return list An array containing the slice of the proxy list
function getVaultListSlice(uint256 start, uint256 end) external view returns (address[] memory list);
/// @notice Sets the perspective contract.
/// @param _perspective The address of the new perspective contract.
function setPerspective(address _perspective) external;
/// @notice Whether a strategy is allowed to be used by the Earn vault.
/// @dev Warning: Only allow trusted, correctly implemented ERC4626 strategies to be used by the Earn vault.
/// @dev Warning: Allowed strategies must not be prone to the first-depositor attack.
/// @dev Warning: To prevent exchange rate manipulation, it is recommended that the allowed strategies are not empty or have sufficient protection.
function isStrategyAllowed(address id) external view returns (bool);
/// @notice Creates a new EulerEarn vault.
/// @param initialOwner The owner of the vault.
/// @param initialTimelock The initial timelock of the vault.
/// @param asset The address of the underlying asset.
/// @param name The name of the vault.
/// @param symbol The symbol of the vault.
/// @param salt The salt to use for the EulerEarn vault's CREATE2 address.
function createEulerEarn(
address initialOwner,
uint256 initialTimelock,
address asset,
string memory name,
string memory symbol,
bytes32 salt
) external returns (IEulerEarn eulerEarn);
}
"
},
"lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
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 maxim
Submitted on: 2025-10-30 14:41:04
Comments
Log in to comment.
No comments yet.