diff --git a/docs/specs.md b/docs/specs.md
index 770eaf2d..f48bce45 100644
--- a/docs/specs.md
+++ b/docs/specs.md
@@ -7,9 +7,9 @@
|EVK-34|Dispatch `useView` |`useView` is a modifier which `staticcall`s into `function viewDelegate()` on this contract (`address(this)`). The calldata is as follows:
```bash viewDelegate.selector + module address + (msg.data with the proxy metadata stripped at the end) + caller address ```
`useView` reverts if `staticcall` is unsuccessful.
`viewDelegate` is a function which `delegatecall`s into the `module` address provided by `useView` with as per current `msg.data`.
`viewDelegate` reverts if not self-called OR if `delegatecall` is unsuccessful. |
|EVK-33|Dispatch `use` |`use` is a modifier which `delegatecall`s into provided `module` address as per current `msg.data`. `use` reverts if `delegatecall` is unsuccessful. |
|EVK-10|EVault |EVault is the top level module dispatch system serving as a dispatcher where you can mix and match where the code should physically be located - in the contract itself, or should it be `delegatecall`ed to one of the modules. The routing of specific functions to specific modules is hardcoded into the dispatcher.
EVault contract consists of the following modules and contracts:
1. `Dispatch` - implements helper functions and modifiers used for calls dispatching, as described below
2. `Initialize` - implements `initialize` function callable by the proxy
3. `Governance` - implements the functions allowing the governor to configure the vault
4. `Token` - implements the `ERC20` functionality of the vault
5. `Vault` - implements the `ERC4626` functionality of the vault
6. `Borrowing` - implements the borrowing functionality of the vault
7. `Risk Manager `- implements the account and vault status check functions
8. `Liquidation` - implements the liquidation logic
9. `Balance Forwarder` - implements the functions allowing to enable/disable balance forwarding feature
For more information regarding the dispatching system, refer to the `Dispatch` contract specification (EVK-9).
For more context on the EVK, refer to the EVK white paper and the EVC vault implementation considerations and guide:
[https://docs.euler.finance/euler-vault-kit-white-paper/](https://docs.euler.finance/euler-vault-kit-white-paper/)
[https://evc.wtf/docs/contracts/vault-implementation-considerations](https://evc.wtf/docs/contracts/vault-implementation-considerations)
[https://evc.wtf/docs/contracts/vault-implementation-guide](https://evc.wtf/docs/contracts/vault-implementation-guide)
[https://evc.wtf/docs/concepts/internals/security-considerations](https://evc.wtf/docs/concepts/internals/security-considerations) |
-|EVK-12|Generic Factory |The factory is a standalone smart contract allowing to deploy two types of proxies: [beacon proxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy#beacon) and [meta proxy](https://eips.ethereum.org/EIPS/eip-3448), and hook them up with the current implementation contract which is stored in the factory storage.
The proxies are deployed using `createProxy` function.
If `desiredImplementation` parameter is non-zero address, the factory checks if the current implementation address is equal to the desired one and proceeds if so. If the `desiredImplementation` does not match the current implementation address, the factory reverts.
If `desiredImplementation` parameter is address zero, the factory accepts any implementation address as desired.
If `upgradeable` parameter is `true`, the factory deploys a beacon proxy contract and sets itself as a beacon contract. Whenever a call is made to the beacon proxy, the proxy will call the factory in order to fetch the current implementation contract address.
If `upgradeable` parameter is `false`, the factory deploys a meta proxy contract with the current implementation address as the target contract.
`createProxy` accepts `trailingData` parameter which is forwarded to the proxies constructors.
Upon successful construction of a proxy, the factory must call `initialize` function on it.
The factory has an upgrade admin address that can change the address of the implementation contract, but this will only affect contracts that were created as `upgradeable`. This allows contract creators to choose whether they want the factory admin to be able to upgrade their vaults, or if instead they should be immutable.
The factory maintains a registry of deployed proxy contracts that allows to verify that a contract was created by a trusted factory.
[https://docs.euler.finance/euler-vault-kit-white-paper/#creation](https://docs.euler.finance/euler-vault-kit-white-paper/#creation) |
+|EVK-12|Generic Factory |The factory is a standalone smart contract allowing to deploy two types of proxies: [beacon proxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy#beacon) and [meta proxy](https://eips.ethereum.org/EIPS/eip-3448)-inspired proxy, and hook them up with the current implementation contract which is stored in the factory storage.
The proxies are deployed using `createProxy` function.
If `desiredImplementation` parameter is non-zero address, the factory checks if the current implementation address is equal to the desired one and proceeds if so. If the `desiredImplementation` does not match the current implementation address, the factory reverts.
If `desiredImplementation` parameter is address zero, the factory accepts any implementation address that is currently stored.
If `upgradeable` parameter is `true`, the factory deploys a beacon proxy contract and sets itself as a beacon contract. Whenever a call is made to the beacon proxy, the proxy will call the factory in order to fetch the current implementation contract address.
If `upgradeable` parameter is `false`, the factory deploys a meta proxy contract with the current implementation address as the target contract.
`createProxy` accepts `trailingData` parameter which is forwarded to the proxies constructors.
Upon successful construction of a proxy, the factory must call `initialize` function on it.
The factory has an upgrade admin address that can change the address of the implementation contract, but this will only affect contracts that were created as `upgradeable`. This allows contract creators to choose whether they want the factory admin to be able to upgrade their vaults, or if instead they should be immutable.
The factory maintains a registry of deployed proxy contracts that allows to verify that a contract was created by a trusted factory.
[https://docs.euler.finance/euler-vault-kit-white-paper/#creation](https://docs.euler.finance/euler-vault-kit-white-paper/#creation) |
|EVK-14|Interest Rate Models |Interest Rate Models are contracts that determine the interest rate that should be charged given the state of a vault. The Interest Rate Model contract must implement two functions:
* `computeInterestRate` - function that accepts the bare minimum parameters necessary to compute utilisation and returns the interest rate in terms of "second percent yield" (SPY) values which are per-second compounded interest rates scaled by `1e27`. This function can update the state of the contract
* `computeInterestRateView` - view function that at all times must return the same result as its state-modifying equivalent `computeInterestRate`
[https://docs.euler.finance/euler-vault-kit-white-paper/#interest-rate-models](https://docs.euler.finance/euler-vault-kit-white-paper/#interest-rate-models) |
-|EVK-61|Meta Proxy |The [meta proxy](https://eips.ethereum.org/EIPS/eip-3448) is a proxy which uses `targetContract` passed to the `deployMetaProxy` function as the implementation address. The `deployMetaProxy` function allows to pass `metadata` that must be appended to any `delegatecall` performed by the proxy. |
+|EVK-61|Meta Proxy-inspired Proxy |The [meta proxy](https://eips.ethereum.org/EIPS/eip-3448)-inspired proxy is a proxy which uses `targetContract` passed to the `deployMetaProxy` function as the implementation address. The `deployMetaProxy` function allows to pass `metadata` that must be appended to any `delegatecall` performed by the proxy. |
|EVK-62|Miscellaneous |This section provides miscellaneous features specification. |
|EVK-67|Miscellaneous Account Status Checks |Any function decreasing the balance of the account or increasing the liability of the account must schedule the account status check for that account. The only exception is the `liquidate` function that forgives the account status check for the `violator`.
If the status of any of the accounts for which the account status check has been scheduled is invalid, the call frame in which the checks are performed must revert.
[https://evc.wtf/docs/concepts/internals/account-status-checks](https://evc.wtf/docs/concepts/internals/account-status-checks) |
|EVK-64|Miscellaneous EVC Authentication |The EVC is responsible for *authentication*, and vaults are responsible for *authorisation*. For example, if a user attempts to redeem a certain amount, the EVC makes sure the request actually came from the user, and the vault makes sure the user actually has this amount.
It means that all the user-facing state-modifying functions of the vault must rely on the EVC execution context's value of `onBehalfOfAccount` when the vault is called through the EVC.
[https://docs.euler.finance/euler-vault-kit-white-paper/#introduction](https://docs.euler.finance/euler-vault-kit-white-paper/#introduction)
[https://evc.wtf/docs/concepts/internals/authorization](https://evc.wtf/docs/concepts/internals/authorization)
[https://evc.wtf/docs/concepts/internals/security-considerations#authentication-by-vaults](https://evc.wtf/docs/concepts/internals/security-considerations#authentication-by-vaults) |
@@ -32,7 +32,7 @@
|EVK-57|Module Borrowing `pullDebt` |If operation enabled, `pullDebt` transfers the `amount` of liability balance from `from` account to the authenticated account.
This operation must ensure that the authenticated account has enabled the vault as controller.
This operation is always called through the EVC.
This operation schedules the account status check on the authenticated account address.
This operation schedules the vault status check.
This operation affects:
* liability balance of the `from` account
* liability balance of the authenticated account |
|EVK-54|Module Borrowing `repay` |If operation enabled, `repay` removes exactly `amount` of underlying tokens as liability for the `receiver` and transfers the underlying tokens from the authenticated account.
This operation is always called through the EVC.
This operation schedules the vault status check.
This operation affects:
* liability balance of the authenticated account
* total liability balance
* total balance of the underlying assets held by the vault |
|EVK-58|Module Borrowing `touch` |If operation enabled, `touch` updates the vault state.
This operation is always called through the EVC.
This operation schedules the vault status check. |
-|EVK-2 |Module Governance |Implements the functions allowing the governor to configure the vault.
The governor is EVC-authenticated, meaning it might perform calls through the EVC, but can only use its owner account for that purpose. The governor must not be able to use sub-accounts or operators, or be prone to malicious controller attacks.
*Context:*
*Immediately after creation, the factory will call* `initialize` *on the proxy, passing in the creator's address as a parameter. The vault will set its governor to the creator's address. This governor can invoke methods that modify the configuration of the vault.*
*At this point, the creator should configure the vault as desired and then decide if the vault is to be governed or not.*
* *If so, the creator retains the governor role or transfers it to another address.*
* *If not, then the ownership is revoked by setting the governor to* `address(0)`*. No more governance changes can happen on this vault and it is considered finalized.*
*If limited governance is desired, the creator can transfer ownership to a smart contract that can only invoke a sub-set of governance methods, perhaps with only certain parameters, or under certain conditions.*
*Using the same code-base and factories, the Euler Vault Kit allows construction of both managed and unmanaged lending products. Managed vaults are intended to be long-lived and are therefore suitable for passive deposits. If market conditions change, an active governor can reconfigure the vault to optimize or protect users. Alternatively, unmanaged vaults are configured statically, and the users themselves (or a higher-level contract) must actively monitor for risks/opportunities and shift their deposits and positions to new vaults as necessary.*
[https://docs.euler.finance/euler-vault-kit-white-paper/#governed-vs-finalised](https://docs.euler.finance/euler-vault-kit-white-paper/#governed-vs-finalised) |
+|EVK-2 |Module Governance |Implements the functions allowing the governor to configure the vault.
The vault uses EVC authentication for the governor, which means that governor actions can be batched together and simulated. However, the vault does not accept advanced EVC authentication methods like sub-accounts, operators or `controlCollateral`.
*Context:*
*Immediately after creation, the factory will call* `initialize` *on the proxy, passing in the creator's address as a parameter. The vault will set its governor to the creator's address. This governor can invoke methods that modify the configuration of the vault.*
*At this point, the creator should configure the vault as desired and then decide if the vault is to be governed or not.*
* *If so, the creator retains the governor role or transfers it to another address.*
* *If not, then the ownership is revoked by setting the governor to* `address(0)`*. No more governance changes can happen on this vault and it is considered finalized.*
*If limited governance is desired, the creator can transfer ownership to a smart contract that can only invoke a sub-set of governance methods, perhaps with only certain parameters, or under certain conditions.*
*Using the same code-base and factories, the Euler Vault Kit allows construction of both managed and unmanaged lending products. Managed vaults are intended to be long-lived and are therefore suitable for passive deposits. If market conditions change, an active governor can reconfigure the vault to optimize or protect users. Alternatively, unmanaged vaults are configured statically, and the users themselves (or a higher-level contract) must actively monitor for risks/opportunities and shift their deposits and positions to new vaults as necessary.*
[https://docs.euler.finance/euler-vault-kit-white-paper/#governed-vs-finalised](https://docs.euler.finance/euler-vault-kit-white-paper/#governed-vs-finalised) |
|EVK-25|Module Governance `clearLTV` |`clearLTV` allows the current governor to clear the LTV config for a given collateral keeping the storage slot initialized. |
|EVK-18|Module Governance `convertFees` |If operation enabled, `convertFees` allows anyone to split accrued vault fees and transfer them to the governor-specified fee receiver and Protocol Config-specified fee receiver. The accrued vault fees are split proportionally as per Protocol Config-specified fee share that cannot exceed `MAX_PROTOCOL_FEE_SHARE`. Immediately after the fees are split, the amount of fees accrued by the vault must be set to 0.
This operation affects:
* shares balance of the governor-specified fee receiver (if it's configured)
* shares balance of the Protocol Config-specified fee receiver
* shares balance of the accumulated fees
[https://docs.euler.finance/euler-vault-kit-white-paper/#fees](https://docs.euler.finance/euler-vault-kit-white-paper/#fees) |
|EVK-28|Module Governance `setCaps` |`setCaps` allows the current governor to set a supply cap and a borrow cap as per the following specification:
*Supply cap and borrow cap are 16-bit decimal floating point values:*
*\* The least significant 6 bits are the exponent*
*\* The most significant 10 bits are the mantissa, scaled by 100*
*\* The special value of 0 means limit is not set*
*\* This is so that uninitialised storage implies no limit*
*\* For an actual cap value of 0, use a zero mantissa and non-zero exponent*
When converted to assets, the supply cap cannot exceed `2 * MAX_SANE_AMOUNT` and the borrow cap cannot exceed `MAX_SANE_AMOUNT`.
[https://docs.euler.finance/euler-vault-kit-white-paper/#supply-and-borrow-caps](https://docs.euler.finance/euler-vault-kit-white-paper/#supply-and-borrow-caps) |
@@ -47,7 +47,7 @@
|EVK-82|Module Governance `setMaxLiquidationDiscount` |`setMaxLiquidationDiscount` allows the current governor to set the maximum liquidation discount factor that limits the liquidation bonus earned by the liquidator. |
|EVK-1 |Module Initialize |Implements the `initialize` function callable by the factory on proxy creation.
[https://docs.euler.finance/euler-vault-kit-white-paper/#creation](https://docs.euler.finance/euler-vault-kit-white-paper/#creation) |
|EVK-15|Module Initialize `initialize` |This function allows for the proxy contract storage initialization and is only callable once immediately after the vault creation. The factory will call `initialize` on the proxy, passing in the creator's address as a parameter.
`initialize` verifies proxy metadata parameters, deploys associated `DToken`contract and initializes the storage appropriately. `proxyCreator` address parameter, received from the factory contract, is used to initialize the governor admin of the vault. This governor can later on invoke methods that modify the configuration of the vault. using the functions of the Module Governance. The symbol of the newly created vault is set to "e" proceded by the underlying asset symbol (or "UNKNOWN" in case the asset contract does not implement the `symbol` function) proceded by "-" and the sequence ID reserved using the Sequence Registry for a given underlying asset symbol descriptor. The name of the newly created vault is set to "EVK Vault " followed by the previously set vault symbol. |
-|EVK-7 |Module Liquidation |Implements the liquidation logic.
*Context:*
*An status is valid (account is considered healthy) if the sum of risk adjusted values of enabled and accepted collaterals is greater than the value of liability, or the liability amount itself must be exactly zero.*
*Value of the liability is current liability amount priced by the configured price oracle in terms of unit of account. The value of the liability is context-dependent. In the liquidation context, the liability is valued using the mid-point quote returned by the oracle.*
*Value of the collateral is current collateral balance amount priced by the configured price oracle in terms of unit of account. The value of the collateral is context-dependent. In the liquidation context, the collateral is valued using the mid-point quote returned by the oracle.*
*The risk adjusted value of collateral for the purpose of liquidation is the current collateral balance priced using the oracle mid-point quote, multiplied by the current liquidation LTV factor, calculated using the initial liquidation LTV factor, the target liquidation LTV factor and ramp duration (assuming the LTV factor changes linearly from the initial LTV to the target LTV in ramp duration time).*
*Liquidator bonus is the difference between the value of the collateral seized by the liquidator and the debt taken on by the liquidator.*
*Because the liquidation works as a reversed Dutch auction system, the liquidator bonus grows linearly as the health of the violator decreases up to the amount limited by the configured maximum liquidation discount factor.*
[https://docs.euler.finance/euler-vault-kit-white-paper/#liquidation](https://docs.euler.finance/euler-vault-kit-white-paper/#liquidation)
[https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles](https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles)
[https://evc.wtf/docs/concepts/internals/control-collateral](https://evc.wtf/docs/concepts/internals/control-collateral) |
+|EVK-7 |Module Liquidation |Implements the liquidation logic.
*Context:*
*An status is valid (account is considered healthy) if the sum of risk adjusted values of enabled and accepted collaterals is greater than the value of liability, or the liability amount itself must be exactly zero.*
*Value of the liability is current liability amount priced by the configured price oracle in terms of unit of account. The value of the liability is context-dependent. In the liquidation context, the liability is valued using the mid-point quote returned by the oracle.*
*Value of the collateral is current collateral balance amount priced by the configured price oracle in terms of unit of account. The value of the collateral is context-dependent. In the liquidation context, the collateral is valued using the mid-point quote returned by the oracle.*
*The risk adjusted value of collateral for the purpose of liquidation is the current collateral balance priced using the oracle mid-point quote, multiplied by the current liquidation LTV factor, calculated using the initial liquidation LTV factor, the target liquidation LTV factor and ramp duration (assuming the LTV factor changes linearly from the initial LTV to the target LTV in ramp duration time).*
*In order to incentivize the liquidator, the collateral shares received will be more valuable than the debt received. Or, put another way, the liquidator is buying the collateral at a discount and the profit generated by the liquidator is called liquidation bonus. The liquidation logic works as a reverse Dutch auction system which scales the liquidation discount proportionally as the health of the violator decreases up to the amount limited by the configured maximum liquidation discount factor.*
[https://docs.euler.finance/euler-vault-kit-white-paper/#liquidation](https://docs.euler.finance/euler-vault-kit-white-paper/#liquidation)
[https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles](https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles)
[https://evc.wtf/docs/concepts/internals/control-collateral](https://evc.wtf/docs/concepts/internals/control-collateral) |
|EVK-31|Module Liquidation `checkLiquidation` |If `violator` account status invalid (`violator` unhealthy), `checkLiquidation` returns the maximum amount of the debt asset the `liquidator` is allowed to liquidate (`maxRepay`) in exchange for the returned maximum amount of `collateral` shares from `violator` (`maxYield`).
If `violator` account status valid (`violator` healthy), `checkLiquidation` returns `maxRepay` and `maxYield` as 0.
Unless `violator` account status valid, considering the liquidator bonus is positive and grows linearly as the health of the `violator` deteriorates, the value of `maxYield` is greater than the value of `maxRepay`.
If needed, `checkLiquidation` must limit the `maxRepay` as per available amount of `collateral` to be seized from the `violator`.
If needed, `checkLiquidation` must limit the `maxRepay` and the `maxYield` as per desired amount to be repaid (`desiredRepay`) parameter.
To calculate the `maxRepay` and `maxYield` amounts, the mid-point quote of the configured price oracle is used.
The liquidator bonus must never exceed the value calculated using the configured value of maximum liquidation discount factor.
`checkLiquidation` must revert if:
* `violator` is the same account as `liquidator`
* `collateral` is not accepted
* `collateral` is not enabled collateral for the `violator`
* liability vault is not enabled as the only controller of the `violator`
* the time elapsed from the last account status check of the `violator` is lesser than the configured liquidation cool off time
* `violator` account status check is deferred
* price oracle is not configured |
|EVK-32|Module Liquidation `liquidate` |If operation enabled AND `violator` account status invalid, `liquidate`:
* liquidates the debt of the `violator` and transfers it to the `liquidator`, up to the amount returned by `checkLiquidation` as per desired amount to be repaid specified (`repayAssets`)
* seizes the `collateral` shares of the `violator` and transfers them to the `liquidator`, up to the amount returned by `checkLiquidation` as per desired amount to be repaid specified (`repayAssets`)
If `collateral` is worthless, it can be seized without taking on any debt by the `liquidator`.
If operation enabled AND `violator` healthy, `liquidate` must be a no-op.
If debt socialization enabled AND the `violator` has outstanding debt after the liquidation AND the `violator` has no more accepted collaterals, the debt must be socialized amongst all the lenders in the vault.
`liquidate` must revert if:
* `liquidate` operation disabled
* `violator` is the same account as `liquidator`
* `collateral` is not accepted
* `collateral` is not enabled collateral for the `violator`
* liability vault is not enabled as the only controller of the `violator`
* liability vault is not enabled as the controller of the `liquidator`
* `violator` account status check is deferred
* the time elapsed from the last account status check of the `violator` is lesser than the configured liquidation cool off time
* price oracle is not configured
* amount of collateral to be seized is less than the desired amount of yield specified (`minYieldBalance`)
This operation is always called through the EVC.
This operation schedules the account status check on the `liquidator` address.
This operation schedules the vault status check.
Refer to the EVC documentation to learn how the collateral seizing mechanism works:
[https://evc.wtf/docs/concepts/internals/control-collateral](https://evc.wtf/docs/concepts/internals/control-collateral) |
|EVK-6 |Module Risk Manager |Implements the account and vault status check functions as well as other account-health-related functions.
*Context:*
*An status is valid (account is considered healthy) if the sum of risk adjusted values of enabled and accepted collaterals is greater than the value of liability, or the liability amount itself must be exactly zero.*
*Value of the liability is current liability amount priced by the configured price oracle in terms of unit of account. The value of the liability is context-dependent. If in the liquidation context, the liability is valued using the mid-point quote returned by the oracle. When originating the position (regular health check), the liability is valued using the ask quote returned by the oracle.*
*Value of the collateral is current collateral balance amount priced by the configured price oracle in terms of unit of account. The value of the collateral is context-dependent. If in the liquidation context, the collateral is valued using the mid-point quote returned by the oracle. When originating the position (regular health check), the collateral is valued using the bid quote returned by the oracle.*
*A vault status is considered valid if:*
* *neither the supply cap nor the borrow cap can be exceeded (they are both defined as max possible values), OR* * *either the supply cap or the borrow cap can be exceeded, AND*
* *neither the supply cap nor the borrow cap are exceeded, OR*
* *the state of the vault when the check is performed is better than it was when the initial snapshot was created (corresponding final total figures are less than or equal than the figures at the time of the snapshot)*
[https://docs.euler.finance/euler-vault-kit-white-paper/#risk-management](https://docs.euler.finance/euler-vault-kit-white-paper/#risk-management)
[https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles](https://docs.euler.finance/euler-vault-kit-white-paper/#price-oracles) |