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/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;
}
}
"
},
"lib/euler-vault-kit/src/GenericFactory/GenericFactory.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {BeaconProxy} from "./BeaconProxy.sol";
import {MetaProxyDeployer} from "./MetaProxyDeployer.sol";
/// @title IComponent
/// @notice Minimal interface which must be implemented by the contract deployed by the factory
interface IComponent {
/// @notice Function replacing the constructor in proxied contracts
/// @param creator The new contract's creator address
function initialize(address creator) external;
}
/// @title GenericFactory
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice The factory allows permissionless creation of upgradeable or non-upgradeable proxy contracts and serves as a
/// beacon for the upgradeable ones
contract GenericFactory is MetaProxyDeployer {
// Constants
uint256 internal constant REENTRANCYLOCK__UNLOCKED = 1;
uint256 internal constant REENTRANCYLOCK__LOCKED = 2;
// State
/// @title ProxyConfig
/// @notice This struct is used to store the configuration of a proxy deployed by the factory
struct ProxyConfig {
// If true, proxy is an instance of the BeaconProxy
bool upgradeable;
// Address of the implementation contract
// May be an out-of-date value, if upgradeable (handled by getProxyConfig)
address implementation;
// The metadata attached to every call passing through the proxy
bytes trailingData;
}
uint256 private reentrancyLock;
/// @notice Address of the account authorized to upgrade the implementation contract
address public upgradeAdmin;
/// @notice Address of the implementation contract, which the deployed proxies will delegate-call to
/// @dev The contract must implement the `IComponent` interface
address public implementation;
/// @notice A lookup for configurations of the proxy contracts deployed by the factory
mapping(address proxy => ProxyConfig) internal proxyLookup;
/// @notice An array of addresses of all the proxies deployed by the factory
address[] public proxyList;
// Events
/// @notice The factory is created
event Genesis();
/// @notice A new proxy is created
/// @param proxy Address of the new proxy
/// @param upgradeable If true, proxy is an instance of the BeaconProxy. If false, the proxy is a minimal meta proxy
/// @param implementation Address of the implementation contract, at the time the proxy was deployed
/// @param trailingData The metadata that will be attached to every call passing through the proxy
event ProxyCreated(address indexed proxy, bool upgradeable, address implementation, bytes trailingData);
/// @notice Set a new implementation contract. All the BeaconProxies are upgraded to the new logic
/// @param newImplementation Address of the new implementation contract
event SetImplementation(address indexed newImplementation);
/// @notice Set a new upgrade admin
/// @param newUpgradeAdmin Address of the new admin
event SetUpgradeAdmin(address indexed newUpgradeAdmin);
// Errors
error E_Reentrancy();
error E_Unauthorized();
error E_Implementation();
error E_BadAddress();
error E_BadQuery();
// Modifiers
modifier nonReentrant() {
if (reentrancyLock == REENTRANCYLOCK__LOCKED) revert E_Reentrancy();
reentrancyLock = REENTRANCYLOCK__LOCKED;
_;
reentrancyLock = REENTRANCYLOCK__UNLOCKED;
}
modifier adminOnly() {
if (msg.sender != upgradeAdmin) revert E_Unauthorized();
_;
}
constructor(address admin) {
emit Genesis();
if (admin == address(0)) revert E_BadAddress();
reentrancyLock = REENTRANCYLOCK__UNLOCKED;
upgradeAdmin = admin;
emit SetUpgradeAdmin(admin);
}
/// @notice A permissionless funtion to deploy new proxies
/// @param desiredImplementation Address of the implementation contract expected to be registered in the factory
/// during proxy creation
/// @param upgradeable If true, the proxy will be an instance of the BeaconProxy. If false, a minimal meta proxy
/// will be deployed
/// @param trailingData Metadata to be attached to every call passing through the new proxy
/// @return The address of the new proxy
/// @dev The desired implementation serves as a protection against (unintentional) front-running of upgrades
function createProxy(address desiredImplementation, bool upgradeable, bytes memory trailingData)
external
nonReentrant
returns (address)
{
address _implementation = implementation;
if (desiredImplementation == address(0)) desiredImplementation = _implementation;
if (desiredImplementation == address(0) || desiredImplementation != _implementation) revert E_Implementation();
// The provided trailing data is prefixed with 4 zero bytes to avoid potential selector clashing in case the
// proxy is called with empty calldata.
bytes memory prefixTrailingData = abi.encodePacked(bytes4(0), trailingData);
address proxy;
if (upgradeable) {
proxy = address(new BeaconProxy(prefixTrailingData));
} else {
proxy = deployMetaProxy(desiredImplementation, prefixTrailingData);
}
proxyLookup[proxy] =
ProxyConfig({upgradeable: upgradeable, implementation: desiredImplementation, trailingData: trailingData});
proxyList.push(proxy);
IComponent(proxy).initialize(msg.sender);
emit ProxyCreated(proxy, upgradeable, desiredImplementation, trailingData);
return proxy;
}
// EVault beacon upgrade
/// @notice Set a new implementation contract
/// @param newImplementation Address of the new implementation contract
/// @dev Upgrades all existing BeaconProxies to the new logic immediately
function setImplementation(address newImplementation) external nonReentrant adminOnly {
if (newImplementation.code.length == 0) revert E_BadAddress();
implementation = newImplementation;
emit SetImplementation(newImplementation);
}
// Admin role
/// @notice Transfer admin rights to a new address
/// @param newUpgradeAdmin Address of the new admin
/// @dev For creating non upgradeable factories, or to finalize all upgradeable proxies to current implementation,
/// @dev set the admin to zero address.
/// @dev If setting to address zero, make sure the implementation contract is already set
function setUpgradeAdmin(address newUpgradeAdmin) external nonReentrant adminOnly {
upgradeAdmin = newUpgradeAdmin;
emit SetUpgradeAdmin(newUpgradeAdmin);
}
// Proxy getters
/// @notice Get current proxy configuration
/// @param proxy Address of the proxy to query
/// @return config The proxy's configuration, including current implementation
function getProxyConfig(address proxy) external view returns (ProxyConfig memory config) {
config = proxyLookup[proxy];
if (config.upgradeable) config.implementation = implementation;
}
/// @notice Check if an address is a proxy deployed with this factory
/// @param proxy Address to check
/// @return True if the address is a proxy
function isProxy(address proxy) external view returns (bool) {
return proxyLookup[proxy].implementation != address(0);
}
/// @notice Fetch the length of the deployed proxies list
/// @return The length of the proxy list array
function getProxyListLength() external view returns (uint256) {
return proxyList.length;
}
/// @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 getProxyListSlice(uint256 start, uint256 end) external view returns (address[] memory list) {
if (end == type(uint256).max) end = proxyList.length;
if (end < start || end > proxyList.length) revert E_BadQuery();
list = new address[](end - start);
for (uint256 i; i < end - start; ++i) {
list[i] = proxyList[start + i];
}
}
}
"
},
"lib/euler-vault-kit/src/EVault/IEVault.sol": {
"content": "// 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);
}
"
},
"lib/euler-price-oracle/src/interfaces/IPriceOracle.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;
/// @title IPriceOracle
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Common PriceOracle interface.
interface IPriceOracle {
/// @notice Get the name of the oracle.
/// @return The name of the oracle.
function name() external view returns (string memory);
/// @notice One-sided price: How much quote token you would get for inAmount of base token, assuming no price spread.
/// @param inAmount The amount of `base` to convert.
/// @param base The token that is being priced.
/// @param quote The token that is the unit of account.
/// @return outAmount The amount of `quote` that is equivalent to `inAmount` of `base`.
function getQuote(uint256 inAmount, address base, address quote) external view returns (uint256 outAmount);
/// @notice Two-sided price: How much quote token you would get/spend for selling/buying inAmount of base token.
/// @param inAmount The amount of `base` to convert.
/// @param base The token that is being priced.
/// @param quote The token that is the unit of account.
/// @return bidOutAmount The amount of `quote` you would get for selling `inAmount` of `base`.
/// @return askOutAmount The amount of `quote` you would spend for buying `inAmount` of `base`.
function getQuotes(uint256 inAmount, address base, address quote)
external
view
returns (uint256 bidOutAmount, uint256 askOutAmount);
}
"
},
"src/Lens/OracleLens.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Utils} from "./Utils.sol";
import {SnapshotRegistry} from "../SnapshotRegistry/SnapshotRegistry.sol";
import {IPriceOracle} from "euler-price-oracle/interfaces/IPriceOracle.sol";
import {Errors} from "euler-price-oracle/lib/Errors.sol";
import "./LensTypes.sol";
interface IOracle is IPriceOracle {
function base() external view returns (address);
function quote() external view returns (address);
function cross() external view returns (address);
function oracleBaseCross() external view returns (address);
function oracleCrossQuote() external view returns (address);
function feed() external view returns (address);
function pyth() external view returns (address);
function WETH() external view returns (address);
function STETH() external view returns (address);
function WSTETH() external view returns (address);
function tokenA() external view returns (address);
function tokenB() external view returns (address);
function pool() external view returns (address);
function governor() external view returns (address);
function maxStaleness() external view returns (uint256);
function maxConfWidth() external view returns (uint256);
function twapWindow() external view returns (uint32);
function fee() external view returns (uint24);
function feedDecimals() external view returns (uint8);
function feedId() external view returns (bytes32);
function fallbackOracle() external view returns (address);
function resolvedVaults(address) external view returns (address);
function cache() external view returns (uint208, uint48);
function rate() external view returns (uint256);
function rateProvider() external view returns (address);
function rwaOracle() external view returns (address);
function resolveOracle(uint256 inAmount, address base, address quote)
external
view
returns (uint256, address, address, address);
function getConfiguredOracle(address base, address quote) external view returns (address);
function description() external view returns (string memory);
function pendleMarket() external view returns (address);
function safeguardPool() external view returns (address);
function poolId() external view returns (bytes32);
function priceOracleIndex() external view returns (uint256);
}
contract OracleLens is Utils {
SnapshotRegistry public immutable adapterRegistry;
constructor(address _adapterRegistry) {
adapterRegistry = SnapshotRegistry(_adapterRegistry);
}
function getOracleInfo(address oracleAddress, address[] memory bases, address[] memory quotes)
public
view
returns (OracleDetailedInfo memory)
{
string memory name;
bytes memory oracleInfo;
{
bool success;
bytes memory result;
if (oracleAddress != address(0)) {
(success, result) = oracleAddress.staticcall(abi.encodeCall(IPriceOracle.name, ()));
}
if (success && result.length >= 32) {
name = abi.decode(result, (string));
} else {
return OracleDetailedInfo({oracle: oracleAddress, name: "", oracleInfo: ""});
}
}
if (_strEq(name, "ChainlinkOracle")) {
(bool success, bytes memory result) =
IOracle(oracleAddress).feed().staticcall(abi.encodeCall(IOracle.description, ()));
string memory feedDescription = success && result.length >= 32 ? abi.decode(result, (string)) : "";
oracleInfo = abi.encode(
ChainlinkOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
feed: IOracle(oracleAddress).feed(),
feedDescription: feedDescription,
maxStaleness: IOracle(oracleAddress).maxStaleness()
})
);
} else if (_strEq(name, "ChainlinkInfrequentOracle")) {
(bool success, bytes memory result) =
IOracle(oracleAddress).feed().staticcall(abi.encodeCall(IOracle.description, ()));
string memory feedDescription = success && result.length >= 32 ? abi.decode(result, (string)) : "";
oracleInfo = abi.encode(
ChainlinkInfrequentOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
feed: IOracle(oracleAddress).feed(),
feedDescription: feedDescription,
maxStaleness: IOracle(oracleAddress).maxStaleness()
})
);
} else if (_strEq(name, "ChronicleOracle")) {
oracleInfo = abi.encode(
ChronicleOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
feed: IOracle(oracleAddress).feed(),
maxStaleness: IOracle(oracleAddress).maxStaleness()
})
);
} else if (_strEq(name, "LidoOracle")) {
oracleInfo = abi.encode(
LidoOracleInfo({base: IOracle(oracleAddress).WSTETH(), quote: IOracle(oracleAddress).STETH()})
);
} else if (_strEq(name, "LidoFundamentalOracle")) {
oracleInfo = abi.encode(
LidoFundamentalOracleInfo({base: IOracle(oracleAddress).WSTETH(), quote: IOracle(oracleAddress).WETH()})
);
} else if (_strEq(name, "PythOracle")) {
oracleInfo = abi.encode(
PythOracleInfo({
pyth: IOracle(oracleAddress).pyth(),
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
feedId: IOracle(oracleAddress).feedId(),
maxStaleness: IOracle(oracleAddress).maxStaleness(),
maxConfWidth: IOracle(oracleAddress).maxConfWidth()
})
);
} else if (_strEq(name, "RedstoneCoreOracle")) {
(uint208 cachePrice, uint48 cachePriceTimestamp) = IOracle(oracleAddress).cache();
oracleInfo = abi.encode(
RedstoneCoreOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
feedId: IOracle(oracleAddress).feedId(),
maxStaleness: IOracle(oracleAddress).maxStaleness(),
feedDecimals: IOracle(oracleAddress).feedDecimals(),
cachePrice: cachePrice,
cachePriceTimestamp: cachePriceTimestamp
})
);
} else if (_strEq(name, "UniswapV3Oracle")) {
oracleInfo = abi.encode(
UniswapV3OracleInfo({
tokenA: IOracle(oracleAddress).tokenA(),
tokenB: IOracle(oracleAddress).tokenB(),
pool: IOracle(oracleAddress).pool(),
fee: IOracle(oracleAddress).fee(),
twapWindow: IOracle(oracleAddress).twapWindow()
})
);
} else if (_strEq(name, "FixedRateOracle")) {
oracleInfo = abi.encode(
FixedRateOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
rate: IOracle(oracleAddress).rate()
})
);
} else if (_strEq(name, "RateProviderOracle")) {
oracleInfo = abi.encode(
RateProviderOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
rateProvider: IOracle(oracleAddress).rateProvider()
})
);
} else if (_strEq(name, "OndoOracle")) {
oracleInfo = abi.encode(
OndoOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
rwaOracle: IOracle(oracleAddress).rwaOracle()
})
);
} else if (_strEq(name, "PendleOracle")) {
oracleInfo = abi.encode(
PendleProviderOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
pendleMarket: IOracle(oracleAddress).pendleMarket(),
twapWindow: IOracle(oracleAddress).twapWindow()
})
);
} else if (_strEq(name, "PendleUniversalOracle")) {
oracleInfo = abi.encode(
PendleUniversalOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
pendleMarket: IOracle(oracleAddress).pendleMarket(),
twapWindow: IOracle(oracleAddress).twapWindow()
})
);
} else if (_strEq(name, "CurveEMAOracle")) {
oracleInfo = abi.encode(
CurveEMAOracleInfo({
base: IOracle(oracleAddress).base(),
quote: IOracle(oracleAddress).quote(),
pool: IOracle(oracleAddress).pool(),
priceOracleIndex: IOracle(oracleAddress).priceOracleIndex()
})
);
} else if (_strEq(name, "SwaapSafeguardOracle")) {
oracleInfo = abi.encode(
SwaapSafeguardProviderOracleInfo({
base: IOracle(oracleAddress).safeguardPool(),
quote: IOracle(oracleAddress).quote(),
poolId: IOracle(oracleAddress).poolId()
})
);
} else if (_strEq(name, "CrossAdapter")) {
address oracleBaseCross = IOracle(oracleAddress).oracleBaseCross();
address oracleCrossQuote = IOracle(oracleAddress).oracleCrossQuote();
OracleDetailedInfo memory oracleBaseCrossInfo = getOracleInfo(oracleBaseCross, bases, quotes);
OracleDetailedInfo memory oracleCrossQuoteInfo = getOracleInfo(oracleCrossQuote, bases, quotes);
oracleInfo = abi.encode(
CrossAdapterInfo({
base: IOracle(oracleAddress).base(),
cross: IOracle(oracleAddress).cross(),
quote: IOracle(oracleAddress).quote(),
oracleBaseCross: oracleBaseCross,
oracleCrossQuote: oracleCrossQuote,
oracleBaseCrossInfo: oracleBaseCrossInfo,
oracleCrossQuoteInfo: oracleCrossQuoteInfo
})
);
} else if (_strEq(name, "EulerRouter")) {
require(bases.length == quotes.length, "OracleLens: invalid input");
address[][] memory resolvedAssets = new address[][](bases.length);
address[] memory resolvedOracles = new address[](bases.length);
OracleDetailedInfo[] memory resolvedOraclesInfo = new OracleDetailedInfo[](bases.length);
for (uint256 i = 0; i < bases.length; ++i) {
(resolvedAssets[i], resolvedOracles[i], resolvedOraclesInfo[i]) =
_routerResolve(oracleAddress, resolvedAssets[i], bases[i], quotes[i]);
}
address fallbackOracle = IOracle(oracleAddress).fallbackOracle();
oracleInfo = abi.encode(
EulerRouterInfo({
governor: IOracle(oracleAddress).governor(),
fallbackOracle: fallbackOracle,
fallbackOracleInfo: getOracleInfo(fallbackOracle, bases, quotes),
bases: bases,
quotes: quotes,
resolvedAssets: resolvedAssets,
resolvedOracles: resolvedOracles,
resolvedOraclesInfo: resolvedOraclesInfo
})
);
}
return OracleDetailedInfo({oracle: oracleAddress, name: name, oracleInfo: oracleInfo});
}
function isStalePullOracle(address oracleAddress, bytes calldata failureReason) public view returns (bool) {
bytes4 failureReasonSelector = bytes4(failureReason);
return _isStalePythOracle(oracleAddress, failureReasonSelector)
|| _isStaleRedstoneOracle(oracleAddress, failureReasonSelector)
|| _isStaleCrossAdapter(oracleAddress, failureReasonSelector);
}
function getValidAdapters(address base, address quote) public view returns (address[] memory) {
return adapterRegistry.getValidAddresses(base, quote, block.timestamp);
}
function _routerResolve(
address oracleAddress,
address[] memory currentlyResolvedAssets,
address base,
address quote
)
internal
view
returns (address[] memory resolvedAssets, address resolvedOracle, OracleDetailedInfo memory resolvedOracleInfo)
{
if (base == quote) return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
(bool success, bytes memory result) =
oracleAddress.staticcall(abi.encodeCall(IOracle.getConfiguredOracle, (base, quote)));
if (!success || result.length < 32) return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
resolvedOracle = abi.decode(result, (address));
if (resolvedOracle != address(0)) {
address[] memory bases = new address[](1);
address[] memory quotes = new address[](1);
bases[0] = base;
quotes[0] = quote;
resolvedOracleInfo = getOracleInfo(resolvedOracle, bases, quotes);
return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
}
(success, result) = oracleAddress.staticcall(abi.encodeCall(IOracle.resolvedVaults, (base)));
if (!success || result.length < 32) return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
address baseAsset = abi.decode(result, (address));
if (baseAsset != address(0)) {
resolvedAssets = new address[](currentlyResolvedAssets.length + 1);
for (uint256 i = 0; i < currentlyResolvedAssets.length; ++i) {
resolvedAssets[i] = currentlyResolvedAssets[i];
}
resolvedAssets[resolvedAssets.length - 1] = baseAsset;
return _routerResolve(oracleAddress, resolvedAssets, baseAsset, quote);
}
(success, result) = oracleAddress.staticcall(abi.encodeCall(IOracle.fallbackOracle, ()));
if (!success || result.length < 32) return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
resolvedOracle = abi.decode(result, (address));
if (resolvedOracle != address(0)) {
address[] memory bases = new address[](1);
address[] memory quotes = new address[](1);
bases[0] = base;
quotes[0] = quote;
resolvedOracleInfo = getOracleInfo(resolvedOracle, bases, quotes);
return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
}
return (currentlyResolvedAssets, resolvedOracle, resolvedOracleInfo);
}
function _isStalePythOracle(address oracle, bytes4 failureSelector) internal view returns (bool) {
if (oracle == address(0)) return false;
(bool success, bytes memory result) = oracle.staticcall(abi.encodeCall(IPriceOracle.name, ()));
if (success && result.length >= 32) {
string memory name = abi.decode(result, (string));
return _strEq(name, "PythOracle") && failureSelector == Errors.PriceOracle_InvalidAnswer.selector;
}
return false;
}
function _isStaleRedstoneOracle(address oracle, bytes4 failureSelector) internal view returns (bool) {
if (oracle == address(0)) return false;
(bool success, bytes memory result) = oracle.staticcall(abi.encodeCall(IPriceOracle.name, ()));
if (success && result.length >= 32) {
string memory name = abi.decode(result, (string));
return _strEq(name, "RedstoneCoreOracle") && failureSelector == Errors.PriceOracle_TooStale.selector;
}
return false;
}
function _isStaleCrossAdapter(address oracle, bytes4 failureSelector) internal view returns (bool) {
if (oracle == address(0)) return false;
(bool success, bytes memory result) = oracle.staticcall(abi.encodeCall(IPriceOracle.name, ()));
if (success && result.length >= 32) {
string memory name = abi.decode(result, (string));
if (!_strEq(name, "CrossAdapter")) return false;
} else {
return false;
}
address oracleBaseCross = IOracle(oracle).oracleBaseCross();
address oracleCrossQuote = IOracle(oracle).oracleCrossQuote();
return _isStalePythOracle(oracleBaseCross, failureSelector)
|| _isStaleRedstoneOracle(oracleBaseCross, failureSelector)
|| _isStalePythOracle(oracleCrossQuote, failureSelector)
|| _isStaleRedstoneOracle(oracleCrossQuote, failureSelector)
|| _isStaleCrossAdapter(oracleBaseCross, failureSelector)
|| _isStaleCrossAdapter(
Submitted on: 2025-10-30 14:40:39
Comments
Log in to comment.
No comments yet.