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": {
"lib/euler-swap/src/EulerSwap.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {IEulerSwapCallee} from "./interfaces/IEulerSwapCallee.sol";
import {IEVault} from "evk/EVault/IEVault.sol";
import {IEulerSwap} from "./interfaces/IEulerSwap.sol";
import {UniswapHook} from "./UniswapHook.sol";
import {CtxLib} from "./libraries/CtxLib.sol";
import {QuoteLib} from "./libraries/QuoteLib.sol";
import {SwapLib} from "./libraries/SwapLib.sol";
contract EulerSwap is IEulerSwap, UniswapHook {
bytes32 public constant curve = bytes32("EulerSwap v2");
address public immutable managementImpl;
error AmountTooBig();
constructor(address evc_, address protocolFeeConfig_, address poolManager_, address managementImpl_)
UniswapHook(evc_, protocolFeeConfig_, poolManager_)
{
managementImpl = managementImpl_;
}
/// @inheritdoc IEulerSwap
function activate(DynamicParams calldata, InitialState calldata) external {
_delegateToManagementImpl();
// Uniswap hook activation
activateHook(CtxLib.getStaticParams());
}
/// @inheritdoc IEulerSwap
function setManager(address, bool) external {
_delegateToManagementImpl();
}
/// @inheritdoc IEulerSwap
function reconfigure(DynamicParams calldata, InitialState calldata) external {
_delegateToManagementImpl();
}
/// @inheritdoc IEulerSwap
function managers(address manager) external view returns (bool installed) {
CtxLib.State storage s = CtxLib.getState();
return s.managers[manager];
}
/// @inheritdoc IEulerSwap
function getStaticParams() external pure returns (StaticParams memory) {
return CtxLib.getStaticParams();
}
/// @inheritdoc IEulerSwap
function getDynamicParams() external pure returns (DynamicParams memory) {
return CtxLib.getDynamicParams();
}
/// @inheritdoc IEulerSwap
function getAssets() external view returns (address asset0, address asset1) {
StaticParams memory sParams = CtxLib.getStaticParams();
asset0 = IEVault(sParams.supplyVault0).asset();
asset1 = IEVault(sParams.supplyVault1).asset();
}
/// @inheritdoc IEulerSwap
function getReserves() external view nonReentrantView returns (uint112, uint112, uint32) {
CtxLib.State storage s = CtxLib.getState();
return (s.reserve0, s.reserve1, s.status);
}
/// @inheritdoc IEulerSwap
function isInstalled() external view nonReentrantView returns (bool) {
StaticParams memory sParams = CtxLib.getStaticParams();
return evc.isAccountOperatorAuthorized(sParams.eulerAccount, address(this));
}
/// @inheritdoc IEulerSwap
function computeQuote(address tokenIn, address tokenOut, uint256 amount, bool exactIn)
external
view
nonReentrantView
returns (uint256)
{
StaticParams memory sParams = CtxLib.getStaticParams();
DynamicParams memory dParams = CtxLib.getDynamicParams();
return QuoteLib.computeQuote(
address(evc), sParams, dParams, QuoteLib.checkTokens(sParams, tokenIn, tokenOut), amount, exactIn
);
}
/// @inheritdoc IEulerSwap
function getLimits(address tokenIn, address tokenOut)
external
view
nonReentrantView
returns (uint256 inLimit, uint256 outLimit)
{
StaticParams memory sParams = CtxLib.getStaticParams();
DynamicParams memory dParams = CtxLib.getDynamicParams();
if (!evc.isAccountOperatorAuthorized(sParams.eulerAccount, address(this))) return (0, 0);
if (dParams.expiration != 0 && dParams.expiration <= block.timestamp) return (0, 0);
bool asset0IsInput = QuoteLib.checkTokens(sParams, tokenIn, tokenOut);
uint256 fee = QuoteLib.getFeeReadOnly(dParams, asset0IsInput);
if (fee >= 1e18) return (0, 0);
(inLimit, outLimit) = QuoteLib.calcLimits(sParams, dParams, asset0IsInput, fee);
if (outLimit > 0) outLimit--; // Compensate for rounding up of exact output quotes
}
/// @inheritdoc IEulerSwap
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data)
external
callThroughEVC
nonReentrant
{
require(amount0Out <= type(uint112).max && amount1Out <= type(uint112).max, AmountTooBig());
// Setup context
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), protocolFeeConfig, _msgSender(), to);
SwapLib.setAmountsOut(ctx, amount0Out, amount1Out);
SwapLib.invokeBeforeSwapHook(ctx);
// Optimistically send tokens
SwapLib.doWithdraws(ctx);
// Invoke callback
if (data.length > 0) IEulerSwapCallee(to).eulerSwapCall(_msgSender(), amount0Out, amount1Out, data);
// Deposit all available funds
SwapLib.setAmountsIn(
ctx, IERC20(ctx.asset0).balanceOf(address(this)), IERC20(ctx.asset1).balanceOf(address(this))
);
SwapLib.doDeposits(ctx);
// Verify curve invariant is satisfied
SwapLib.finish(ctx);
}
function _delegateToManagementImpl() internal {
(bool success, bytes memory result) = managementImpl.delegatecall(msg.data);
if (!success) {
assembly {
revert(add(32, result), mload(result))
}
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}
"
},
"lib/euler-swap/src/interfaces/IEulerSwapCallee.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IEulerSwapCallee {
/// @notice If non-empty data is provided to `swap()`, then this callback function
/// is invoked on the `to` address, allowing flash-swaps (withdrawing output before
/// sending input.
/// @dev This callback mechanism is designed to be as similar as possible to Uniswap2.
/// @param sender The address that originated the swap
/// @param amount0 The requested output amount of token0
/// @param amount1 The requested output amount of token1
/// @param data Opaque callback data passed by swapper
function eulerSwapCall(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external;
}
"
},
"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-swap/src/interfaces/IEulerSwap.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IEulerSwap {
/// @dev Constant pool parameters, loaded from trailing calldata.
struct StaticParams {
address supplyVault0;
address supplyVault1;
address borrowVault0;
address borrowVault1;
address eulerAccount;
address feeRecipient;
}
/// @dev Reconfigurable pool parameters, loaded from storage.
struct DynamicParams {
uint112 equilibriumReserve0;
uint112 equilibriumReserve1;
uint112 minReserve0;
uint112 minReserve1;
uint80 priceX;
uint80 priceY;
uint64 concentrationX;
uint64 concentrationY;
uint64 fee0;
uint64 fee1;
uint40 expiration;
uint8 swapHookedOperations;
address swapHook;
}
/// @dev Starting configuration of pool storage.
struct InitialState {
uint112 reserve0;
uint112 reserve1;
}
/// @notice Performs initial activation setup, such as approving vaults to access the
/// EulerSwap instance's tokens, enabling vaults as collateral, setting up Uniswap
/// hooks, etc. This should only be invoked by the factory.
function activate(DynamicParams calldata dynamicParams, InitialState calldata initialState) external;
/// @notice Installs or uninstalls a manager. Managers can reconfigure the dynamic EulerSwap parameters.
/// Only callable by the owner (eulerAccount).
/// @param manager Address to install/uninstall
/// @param installed Whether the manager should be installed or uninstalled
function setManager(address manager, bool installed) external;
/// @notice Addresses configured as managers. Managers can reconfigure the pool parameters.
/// @param manager Address to check
/// @return installed Whether the address is currently a manager of this pool
function managers(address manager) external view returns (bool installed);
/// @notice Reconfigured the pool's parameters. Only callable by the owner (eulerAccount)
/// or a manager.
function reconfigure(DynamicParams calldata dParams, InitialState calldata initialState) external;
/// @notice Retrieves the pool's static parameters.
function getStaticParams() external view returns (StaticParams memory);
/// @notice Retrieves the pool's dynamic parameters.
function getDynamicParams() external view returns (DynamicParams memory);
/// @notice Retrieves the underlying assets supported by this pool.
function getAssets() external view returns (address asset0, address asset1);
/// @notice Retrieves the current reserves from storage, along with the pool's lock status.
/// @return reserve0 The amount of asset0 in the pool
/// @return reserve1 The amount of asset1 in the pool
/// @return status The status of the pool (0 = unactivated, 1 = unlocked, 2 = locked)
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 status);
/// @notice Whether or not this EulerSwap instance is installed as an operator of
/// the eulerAccount in the EVC.
function isInstalled() external view returns (bool installed);
/// @notice Generates a quote for how much a given size swap will cost.
/// @param tokenIn The input token that the swapper SENDS
/// @param tokenOut The output token that the swapper GETS
/// @param amount The quantity of input or output tokens, for exact input and exact output swaps respectively
/// @param exactIn True if this is an exact input swap, false if exact output
/// @return The quoted quantity of output or input tokens, for exact input and exact output swaps respectively
function computeQuote(address tokenIn, address tokenOut, uint256 amount, bool exactIn)
external
view
returns (uint256);
/// @notice Upper-bounds on the amounts of each token that this pool can currently support swaps for.
/// @return limitIn Max amount of `tokenIn` that can be sold.
/// @return limitOut Max amount of `tokenOut` that can be bought.
function getLimits(address tokenIn, address tokenOut) external view returns (uint256 limitIn, uint256 limitOut);
/// @notice Optimistically sends the requested amounts of tokens to the `to`
/// address, invokes `eulerSwapCall` callback on `to` (if `data` was provided),
/// and then verifies that a sufficient amount of tokens were transferred to
/// satisfy the swapping curve invariant.
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
}
"
},
"lib/euler-swap/src/UniswapHook.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {BaseHook} from "v4-periphery/src/utils/BaseHook.sol";
import {PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {
BeforeSwapDelta, toBeforeSwapDelta, BeforeSwapDeltaLibrary
} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
import {IEVault} from "evk/EVault/IEVault.sol";
import {EulerSwapBase} from "./EulerSwapBase.sol";
import {IEulerSwap} from "./interfaces/IEulerSwap.sol";
import {QuoteLib} from "./libraries/QuoteLib.sol";
import {SwapLib} from "./libraries/SwapLib.sol";
abstract contract UniswapHook is EulerSwapBase, BaseHook {
using SafeCast for uint256;
address public immutable protocolFeeConfig;
PoolKey internal _poolKey;
constructor(address evc_, address protocolFeeConfig_, address _poolManager)
EulerSwapBase(evc_)
BaseHook(IPoolManager(_poolManager))
{
protocolFeeConfig = protocolFeeConfig_;
}
function activateHook(IEulerSwap.StaticParams memory sParams) internal nonReentrant {
if (address(poolManager) == address(0)) return;
Hooks.validateHookPermissions(this, getHookPermissions());
address asset0Addr = IEVault(sParams.supplyVault0).asset();
address asset1Addr = IEVault(sParams.supplyVault1).asset();
_poolKey = PoolKey({
currency0: Currency.wrap(asset0Addr),
currency1: Currency.wrap(asset1Addr),
fee: 0, // hard-coded fee since it may change
tickSpacing: 1, // hard-coded tick spacing, as it's unused
hooks: IHooks(address(this))
});
// create the pool on v4, using starting price as sqrtPrice(1/1) * Q96
poolManager.initialize(_poolKey, 79228162514264337593543950336);
}
/// @dev Helper function to return the poolKey as its struct type
function poolKey() external view returns (PoolKey memory) {
return _poolKey;
}
/// @dev Prevent hook address validation in constructor, which is not needed
/// because hook instances are proxies. Instead, the address is validated
/// in activateHook().
function validateHookAddress(BaseHook _this) internal pure override {}
function _beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata)
internal
override
nonReentrant
returns (bytes4, BeforeSwapDelta, uint24)
{
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), protocolFeeConfig, sender, msg.sender);
uint256 amountIn;
uint256 amountOut;
BeforeSwapDelta returnDelta;
bool isExactInput = params.amountSpecified < 0;
if (isExactInput) {
amountIn = uint256(-params.amountSpecified);
amountOut = QuoteLib.computeQuote(address(evc), ctx.sParams, ctx.dParams, params.zeroForOne, amountIn, true);
} else {
amountOut = uint256(params.amountSpecified);
amountIn =
QuoteLib.computeQuote(address(evc), ctx.sParams, ctx.dParams, params.zeroForOne, amountOut, false);
}
if (params.zeroForOne) {
SwapLib.setAmountsOut(ctx, 0, amountOut);
SwapLib.setAmountsIn(ctx, amountIn, 0);
} else {
SwapLib.setAmountsOut(ctx, amountOut, 0);
SwapLib.setAmountsIn(ctx, 0, amountIn);
}
SwapLib.invokeBeforeSwapHook(ctx);
// return the delta to the PoolManager, so it can process the accounting
// exact input:
// specifiedDelta = positive, to offset the input token taken by the hook (negative delta)
// unspecifiedDelta = negative, to offset the credit of the output token paid by the hook (positive delta)
// exact output:
// specifiedDelta = negative, to offset the output token paid by the hook (positive delta)
// unspecifiedDelta = positive, to offset the input token taken by the hook (negative delta)
returnDelta = isExactInput
? toBeforeSwapDelta(amountIn.toInt128(), -(amountOut.toInt128()))
: toBeforeSwapDelta(-(amountOut.toInt128()), amountIn.toInt128());
// take the input token, from the PoolManager to the Euler vault
// the debt will be paid by the swapper via the swap router
poolManager.take(params.zeroForOne ? key.currency0 : key.currency1, address(this), amountIn);
SwapLib.doDeposits(ctx);
// pay the output token, to the PoolManager from an Euler vault
// the credit will be forwarded to the swap router, which then forwards it to the swapper
poolManager.sync(params.zeroForOne ? key.currency1 : key.currency0);
SwapLib.doWithdraws(ctx);
poolManager.settle();
SwapLib.finish(ctx);
return (BaseHook.beforeSwap.selector, returnDelta, 0);
}
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
/**
* @dev Hook Permissions without overrides:
* - beforeInitialize, beforeDoate, beforeAddLiquidity
* We use BaseHook's original reverts to *intentionally* revert
*
* beforeInitialize: the hook reverts for initializations NOT going through EulerSwap.activateHook()
* we want to prevent users from initializing other pairs with the same hook address
*
* beforeDonate: because the hook does not support native concentrated liquidity, any
* donations are permanently irrecoverable. The hook reverts on beforeDonate to prevent accidental misusage
*
* beforeAddLiquidity: the hook reverts to prevent v3-CLAMM positions
* because the hook is a "custom curve", any concentrated liquidity position sits idle and entirely unused
* to protect users from accidentally creating non-productive positions, the hook reverts on beforeAddLiquidity
*/
return Hooks.Permissions({
beforeInitialize: true,
afterInitialize: false,
beforeAddLiquidity: true,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: false,
beforeDonate: true,
afterDonate: false,
beforeSwapReturnDelta: true,
afterSwapReturnDelta: false,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
}
"
},
"lib/euler-swap/src/libraries/CtxLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {IEulerSwap} from "../interfaces/IEulerSwap.sol";
library CtxLib {
struct State {
uint112 reserve0;
uint112 reserve1;
uint32 status; // 0 = unactivated, 1 = unlocked, 2 = locked
mapping(address manager => bool installed) managers;
}
// keccak256("eulerSwap.state") - 1
bytes32 internal constant CtxStateLocation = 0x10ee9b31f73104ff2cf413742414a498e1f7b56c11cb512bca58a9c50727bb58;
function getState() internal pure returns (State storage s) {
assembly {
s.slot := CtxStateLocation
}
}
// keccak256("eulerSwap.dynamicParams") - 1
bytes32 internal constant CtxDynamicParamsLocation =
0xca4da3477ca592c011a91679daaaf19e95f02a3a91537965b17e4113575fb219;
function writeDynamicParamsToStorage(IEulerSwap.DynamicParams memory dParams) internal {
IEulerSwap.DynamicParams storage s;
assembly {
s.slot := CtxDynamicParamsLocation
}
s.equilibriumReserve0 = dParams.equilibriumReserve0;
s.equilibriumReserve1 = dParams.equilibriumReserve1;
s.minReserve0 = dParams.minReserve0;
s.minReserve1 = dParams.minReserve1;
s.priceX = dParams.priceX;
s.priceY = dParams.priceY;
s.concentrationX = dParams.concentrationX;
s.concentrationY = dParams.concentrationY;
s.fee0 = dParams.fee0;
s.fee1 = dParams.fee1;
s.expiration = dParams.expiration;
s.swapHookedOperations = dParams.swapHookedOperations;
s.swapHook = dParams.swapHook;
}
function getDynamicParams() internal pure returns (IEulerSwap.DynamicParams memory) {
IEulerSwap.DynamicParams storage s;
assembly {
s.slot := CtxDynamicParamsLocation
}
return s;
}
error InsufficientCalldata();
/// @dev Unpacks encoded Params from trailing calldata. Loosely based on
/// the implementation from EIP-3448 (except length is hard-coded).
/// 192 is the size of the StaticParams struct after ABI encoding.
function getStaticParams() internal pure returns (IEulerSwap.StaticParams memory p) {
require(msg.data.length >= 192, InsufficientCalldata());
unchecked {
return abi.decode(msg.data[msg.data.length - 192:], (IEulerSwap.StaticParams));
}
}
}
"
},
"lib/euler-swap/src/libraries/QuoteLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.27;
import {IEVC} from "evc/interfaces/IEthereumVaultConnector.sol";
import {IEVault} from "evk/EVault/IEVault.sol";
import {IEulerSwap} from "../interfaces/IEulerSwap.sol";
import "../interfaces/IEulerSwapHookTarget.sol";
import {CtxLib} from "./CtxLib.sol";
import {CurveLib} from "./CurveLib.sol";
import {SwapLib} from "./SwapLib.sol";
library QuoteLib {
error HookError();
error UnsupportedPair();
error OperatorNotInstalled();
error SwapLimitExceeded();
error SwapRejected();
error Expired();
function getFee(IEulerSwap.DynamicParams memory dParams, bool asset0IsInput) internal returns (uint64 fee) {
fee = type(uint64).max;
if ((dParams.swapHookedOperations & EULER_SWAP_HOOK_GET_FEE) != 0) {
CtxLib.State storage s = CtxLib.getState();
(bool success, bytes memory data) = dParams.swapHook.call(
abi.encodeCall(IEulerSwapHookTarget.getFee, (asset0IsInput, s.reserve0, s.reserve1, false))
);
require(success && data.length >= 32, SwapLib.HookError(EULER_SWAP_HOOK_GET_FEE, data));
fee = abi.decode(data, (uint64));
}
if (fee == type(uint64).max) fee = asset0IsInput ? dParams.fee0 : dParams.fee1;
}
function getFeeReadOnly(IEulerSwap.DynamicParams memory dParams, bool asset0IsInput)
internal
view
returns (uint64 fee)
{
fee = type(uint64).max;
if ((dParams.swapHookedOperations & EULER_SWAP_HOOK_GET_FEE) != 0) {
CtxLib.State storage s = CtxLib.getState();
(bool success, bytes memory data) = dParams.swapHook.staticcall(
abi.encodeCall(IEulerSwapHookTarget.getFee, (asset0IsInput, s.reserve0, s.reserve1, true))
);
require(success && data.length >= 32, SwapLib.HookError(EULER_SWAP_HOOK_GET_FEE, data));
fee = abi.decode(data, (uint64));
}
if (fee == type(uint64).max) fee = asset0IsInput ? dParams.fee0 : dParams.fee1;
}
/// @dev Computes the quote for a swap by applying fees and validating state conditions
/// @param evc EVC instance
/// @param sParams Static params
/// @param dParams Dynamic params
/// @param asset0IsInput Swap direction
/// @param amount The amount to quote (input amount if exactIn=true, output amount if exactIn=false)
/// @param exactIn True if quoting for exact input amount, false if quoting for exact output amount
/// @return The quoted amount (output amount if exactIn=true, input amount if exactIn=false)
/// @dev Validates:
/// - EulerSwap operator is installed
/// - Token pair is supported
/// - Sufficient reserves exist
/// - Sufficient cash is available
function computeQuote(
address evc,
IEulerSwap.StaticParams memory sParams,
IEulerSwap.DynamicParams memory dParams,
bool asset0IsInput,
uint256 amount,
bool exactIn
) internal view returns (uint256) {
if (amount == 0) return 0;
require(amount <= type(uint112).max, SwapLimitExceeded());
require(IEVC(evc).isAccountOperatorAuthorized(sParams.eulerAccount, address(this)), OperatorNotInstalled());
require(dParams.expiration == 0 || dParams.expiration > block.timestamp, Expired());
uint256 fee = getFeeReadOnly(dParams, asset0IsInput);
require(fee < 1e18, SwapRejected());
(uint256 inLimit, uint256 outLimit) = calcLimits(sParams, dParams, asset0IsInput, fee);
// exactIn: decrease effective amountIn
if (exactIn) amount = amount - (amount * fee / 1e18);
uint256 quote = findCurvePoint(dParams, amount, exactIn, asset0IsInput);
if (exactIn) {
// if `exactIn`, `quote` is the amount of assets to buy from the AMM
require(amount <= inLimit && quote <= outLimit, SwapLimitExceeded());
} else {
// if `!exactIn`, `amount` is the amount of assets to buy from the AMM
require(amount <= outLimit && quote <= inLimit, SwapLimitExceeded());
// exactOut: inflate required amountIn
quote = (quote * 1e18) / (1e18 - fee);
}
return quote;
}
/// @notice Calculates the maximum input and output amounts for a swap based on protocol constraints
/// @dev Determines limits by checking multiple factors:
/
Submitted on: 2025-10-31 16:23:55
Comments
Log in to comment.
No comments yet.