Skip to content

Commit

Permalink
feat: v3.0.1 (#183)
Browse files Browse the repository at this point in the history
* feat: vault interface

* chore: solidity interfaces

* feat: solidity interface

* feat: add deployment script

* feat: increase version

* chore: script interface

* chore: update spech

* chore: rebase to limit merge

* fix: black

* chore: bump sol

* fix: read me and security doc

* fix: interface add constants

* chore: sol version

* fix: spelling
  • Loading branch information
Schlagonia authored Oct 11, 2023
1 parent 42e79e7 commit 45b5d6b
Show file tree
Hide file tree
Showing 27 changed files with 554 additions and 175 deletions.
4 changes: 3 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
"extends": "solhint:recommended",
"plugins": ["prettier", "yearn"],
"rules": {
"compiler-version": ["error", "0.8.14"],
"compiler-version": ["error", "0.8.18"],
"code-complexity": "warn",
"const-name-snakecase": "warn",
"function-max-lines": "warn",
"func-name-mixedcase": "off",
"var-name-mixedcase": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }],
"max-line-length": ["warn", 160],
"avoid-suicide": "error",
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This repository runs on [ApeWorx](https://www.apeworx.io/). A python based devel

You will need:
- Python 3.8 or later
- Vyper 0.3.7
- Linux or macOS
- Windows: Install Windows Subsystem Linux (WSL) with Python 3.8 or later
- [Hardhat](https://hardhat.org/) installed globally
Expand Down Expand Up @@ -63,4 +64,4 @@ ape test

See the ApeWorx [documentation](https://docs.apeworx.io/ape/stable/) and [github](https://github.com/ApeWorX/ape) for more information.

You will need hardhat to run the test `yarn`
You will need hardhat to run the test `yarn`
9 changes: 9 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Security

For full security process refer to yearn-security [repo](https://github.com/yearn/yearn-security/blob/master/SECURITY.md).

## Scope

The scope of the Bug Bounty program spans smart contracts utilized in the Yearn ecosystem – including but not limited to the main [VaultV3.vy](https://github.com/yearn/yearn-vaults-v3/blob/master/contracts/VaultV3.vy) and [VaultFactory.vy](https://github.com/yearn/yearn-vaults-v3/blob/master/contracts/VaultV3.vy) Vyper contracts in this repo, including historical deployments that still see active use associated with Yearn, and excluding any contracts used in a test-only capacity (including test-only deployments).

Note: Other contracts, outside of the ones mentioned above, might be considered on a case by case basis, please, reach out to the Yearn development team for clarification.
64 changes: 41 additions & 23 deletions TECH_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Vault: ERC4626 compliant Smart contract that receives Assets from Depositors to then distribute them among the different Strategies added to the vault, managing accounting and Assets distribution.
- Role: the different flags an Account can have in the Vault so that the Account can do certain specific actions. Can be fulfilled by a smart contract or an EOA.
- Accountant: smart contract that receives P&L reporting and returns shares and refunds to the strategy
- Limit Modules: Add on smart contracts that can control the vaults deposit and withdraw limits dynamically.

# VaultV3 Specification
The Vault code has been designed as an non-opinionated system to distribute funds of depositors into different opportunities (aka Strategies) and manage accounting in a robust way. That's all.
Expand All @@ -27,8 +28,9 @@ This allows different players to deploy their own version and implement their ow
```
Example periphery contracts:
- Emergency module: it receives deposits of Vault Shares and allows the contract to call the shutdown function after a certain % of total Vault Shares have been deposited
- Debt Allocator: a smart contract that incentivises APY / debt allocation optimisation by rewarding the best debt allocation (see [yStarkDebtAllocator](https://github.com/jmonteer/ystarkdebtallocator))
- Strategy Staking Module: a smart contract that allows players to sponsor specific strategies (so that they are added to the vault) by staking their YFI, making money if they do well and losing money if they don't
- Debt Allocator: a smart contract that incentivize's APY / debt allocation optimization by rewarding the best debt allocation (see [yStarkDebtAllocator](https://github.com/jmonteer/ystarkdebtallocator))
- Strategy Staking Module: a smart contract that allows players to sponsor specific strategies (so that they are added to the vault) by staking their YFI, making money if they do well and losing money if they don't.
- Deposit Limit Module: Will dynamically adjust the deposit limit based on the depositor and arbitrary conditions.
- ...
```
## Deployment
Expand All @@ -48,16 +50,16 @@ When deploying a new vault, it requires the following parameters:
### Deposits / Mints
Users can deposit ASSET tokens to receive yvTokens (SHARES).

Deposits are limited under depositLimit and shutdown parameters. Read below for details.
Deposits are limited under depositLimit/depositLimitModule and shutdown parameters. Read below for details.

### Withdrawals / Redeems
Users can redeem their shares at any point in time if there is liquidity available.

Optionally, a user can specify a list of strategies to withdraw from. If a list of strategies is passed, the vault will try to withdraw from them.
Optionally, if the vault management allows, a user can specify a list of strategies to withdraw from. If a list of strategies is passed, the vault will try to withdraw from them.

If a user passed array is not defined, the redeem function will use the default_queue.
If a user passed array is not defined or the use_default_queue flag has been turned on, the redeem function will use the default_queue.

In order to properly comply with the ERC-4626 standard and still allow losses, both withdraw and redeem have an additional optional parameter of 'maxLoss' that can be used. The default for 'maxLoss' is 0 (i.e. revert if any loss) for withdraws, and 10_000 (100%) for redeems.
In order to properly comply with the ERC-4626 standard and still allow losses, both withdraw and redeem have an additional optional parameter of 'max_loss' that can be used. The default for 'max_loss' is 0 (i.e. revert if any loss) for withdraws, and 10_000 (100%) for redeems.

If not enough funds have been recovered to honor the full request within the maxLoss, the transaction will revert.

Expand Down Expand Up @@ -114,7 +116,8 @@ These are:
- REPORTING_MANAGER: role that calls report for strategies
- DEBT_MANAGER: role that adds and removes debt from strategies
- MAX_DEBT_MANAGER: role that can set the max debt for a strategy
- DEPOSIT_LIMIT_MANAGER: role that sets deposit limit for the vault
- DEPOSIT_LIMIT_MANAGER: role that sets deposit limit or deposit limit module for the vault
- WITHDRAW_LIMIT_MANAGER: role that sets the withdraw limit module for the vault.
- MINIMUM_IDLE_MANAGER: role that sets the minimum total idle the vault should keep
- PROFIT_UNLOCK_MANAGER: role that sets the profit_max_unlock_time
- DEBT_PURCHASER # can purchase bad debt from the vault
Expand All @@ -126,6 +129,10 @@ The account that manages roles is a single account, set in `role_manager`.

This role_manager can be an EOA, a multi-sig or a Governance Module that relays calls.

The vault comes with the ability to "open" every role. Meaning that any function that requires the caller to hold that role would be come permsissionless.

The vault imposes no restrictions on the role managers ability to open or close any role. **But this should be done with extreme care as most of the roles are not meant to be opened and can lead to loss of funds if done incorrectly**.

### Strategy Management
This responsibility is taken by callers with ADD_STRATEGY_MANAGER, REVOKE_STRATEGY_MANAGER and FORCE_REVOKE_MANAGER roles

Expand All @@ -137,10 +144,14 @@ Revoked strategies will return all debt and stop being eligible to receive more.

Force revoking a strategy is only used in cases of a faulty strategy that cannot otherwise have its current_debt reduced to 0. Force revoking a strategy will result in a loss being reported by the vault.

#### Setting the periphery contracts
The accountant can each be set by the ACCOUNTANT_MANAGER.
#### Setting the modules/periphery contracts
The accountant can be set by the ACCOUNTANT_MANAGER.

A deposit_limit_module can be set by the DEPOSIT_LIMIT_MANAGER

The contract is not needed for the vault to function but are recommended for optimal use.
A withdraw_limit_module can be set by the WITHDRAW_LIMIT_MANAGER

These contracts are not needed for the vault to function but are optional add ons for optimal use.

#### Reporting profits
The REPORTING_MANAGER is in charge of calling process_report() for each strategy in the vault according to its own timeline
Expand Down Expand Up @@ -168,10 +179,17 @@ Stored in strategies[strategy].max_debt
When a debt re-balance is triggered, the Vault will cap the new target debt to this number (max_debt)

#### Setting the deposit limit
The DEPOSIT_LIMIT_MANAGER is in charge of setting the deposit_limit for the vault
The DEPOSIT_LIMIT_MANAGER is in charge of setting the deposit_limit or a deposit_limit_module for the vault

On deployment deposit_limit defaults to 0 and will need to be increased to make the vault functional

The deposit_limit will have to be set to MAX_UINT256 in order to set a deposit_limit_module, and the module will have to be address 0 to adjust the deposit_limit.

#### Setting the withdraw limit module
The WITHDRAW_LIMIT_MANAGER is in charge of setting the withdraw_limit_module for the vault

The vaults default withdraw limit is calculated based on the liquidity of its strategies. Setting a withdraw limit module will override this functionality.

#### Setting minimum idle funds
The MINIMUM_IDLE_MANAGER can specify how many funds the vault should try to have reserved to serve withdrawal requests

Expand All @@ -189,34 +207,32 @@ The QUEUE_MANAGER has the option to set a custom default_queue if desired. The v

All strategies in the default queue must have been previously added to the vault.

#### Buying Debt
The DEBT_PURCHASER role can buy debt from the vault in return for the equal amount of `asset`.

This should only ever be used in the case where governance wants to purchase a set amount of bade debt from the vault in order to not report a loss.
The QUEUE_MANAGER can also set the use_default_queue flag, which will cause the default_queue to be used during every withdraw even if a custom_queue is passed in.

It still relies on convertToShares() so will only be viable if the conversion does not reflect and large negative realized loss from the strategy.
#### Buying Debt
The DEBT_PURCHASER role can buy bad debt from the vault in return for the equal amount of `asset`.

This should only ever be used in emergencies where governance wants to purchase a set amount of bad debt from the vault in order to not report a loss.

#### Shutting down the vault
In an emergency the EMERGENCY_MANAGER can shutdown the vault

This will also give the EMERGENCY_MANAGER the DEBT_MANAGER roles as well so funds can start to be returned from the strategies

## Strategy Minimum API
Strategies are completely independent smart contracts that can be implemented following the proposed template or in any other way.
Strategies are completely independent smart contracts that can be implemented following the [Tokenized Strategy](https://github.com/yearn/tokenized-strategy) template or in any other way.

In any case, to be compatible with the vault, they need to implement the following functions, which are a subset of ERC4626 vaults:
- asset(): view returning underlying asset
- totalAssets(): view returning current amount of assets. It can include rewards valued in `asset` ¡
- maxDeposit(address): view returning the amount max that the strategy can take safely
- deposit(assets, receiver): deposits `assets` amount of tokens into the strategy. it can be restricted to vault only or be open
- maxWithdraw(address): view returning how many asset can the vault take from the vault at any given point in time
- withdraw(assets, receiver, owner): withdraws `assets` amount of tokens from the strategy
- redeem(shares, receiver, owner): redeems `shares` of the strategy for the underlying asset.
- balanceOf(address): return the number of shares of the strategy that the address has
- convertToAssets(shares: uint256): Converts `shares` into the corresponding amount of asset.
- convertToShares(assets: uint256): Converts `assets` into the corresponding amount of shares.
- previewWithdraw(assets: uint256): Converst `assets` into the corresponding amount of shares rounding up.
- convertToAssets(shares): Converts `shares` into the corresponding amount of asset.
- convertToShares(assets): Converts `assets` into the corresponding amount of shares.
- previewWithdraw(assets): Converts `assets` into the corresponding amount of shares rounding up.
- maxRedeem(owner): return the max amount of shares that `owner` can redeem.

This means that the vault can deposit into any ERC4626 vault but also that a non-compliant strategy can be implemented provided that these functions have been implemented (even in a non ERC4626 compliant way).

Expand All @@ -228,6 +244,8 @@ The most important implication is that `withdraw` and `redeem` functions as pres
1. max_loss: The amount in basis points that the withdrawer will accept as a loss. I.E. 100 = 1% loss accepted.
2. strategies: This is an array of strategies to use as the withdrawal queue instead of the default queue.

* `maxWithdraw` and `maxRedeem` also come with both of these optional parameters to get the most exact amounts.

### Shutdown mode
In the case the current roles stop fulfilling their responsibilities or something else happens, the EMERGENCY_MANAGER can shutdown the vault.

Expand All @@ -244,7 +262,7 @@ Withdrawals can't be paused under any circumstance
### Accounting
Shutdown mode does not affect accounting

### Debt rebalance
### Debt re-balance
_Light emergency_: Setting minimumTotalIdle to MAX_UINT256 will result in the vault requesting the debt back from strategies. This would stop new strategies from getting funded too, as the vault prioritizes minimumTotalIdle

_Shutdown mode_: All strategies maxDebt is set to 0. Strategies will return funds as soon as they can.
Expand Down
18 changes: 9 additions & 9 deletions contracts/VaultFactory.vy
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
@author yearn.finance
@notice
This vault Factory can be used by anyone wishing to deploy their own
ERC4626 compliant Vault.
ERC4626 compliant Yearn V3 Vault of the same API version.
The factory uses the Blueprint (ERC-5202) standard to handle the
deployment of any new vaults off of the immutable address stored
at `VAULT_BLUEPRINT`. This allows the vaults to be deployed and
initialized fully on-chain with their init byte code, thus not
requiring any delegatecall patterns or post deployment initialization.
The deployments are done through create2 with a specific `salt`
that is derived from a combination of the deployers address,
that is derived from a combination of the deployer's address,
the underlying asset used, as well as the name and symbol specified.
Meaning a deployer will not be able to deploy the exact same vault
twice and will need to use different name and or symbols for vaults
Expand All @@ -23,9 +23,9 @@
The factory also holds the protocol fee configs for each vault and strategy
of its specific `API_VERSION` that determine how much of the fees
charged are designated "protocol fees" and sent to the designated
`fee_recipient`. The protocol fees work through rev share system,
where if the vault or strategy determines to charge X amount of total
fees during a `report` the protocol fees are X * fee_bps / 10_000.
`fee_recipient`. The protocol fees work through a revenue share system,
where if the vault or strategy decides to charge X amount of total
fees during a `report` the protocol fees are a percent of X.
The protocol fees will be sent to the designated fee_recipient and
then (X - protocol_fees) will be sent to the vault/strategy specific
fee recipient.
Expand Down Expand Up @@ -68,7 +68,7 @@ struct PFConfig:
fee_recipient: address

# Identifier for this version of the vault.
API_VERSION: constant(String[28]) = "3.0.0"
API_VERSION: constant(String[28]) = "3.0.1"

# The max amount the protocol fee can be set to.
MAX_FEE_BPS: constant(uint16) = 5_000 # 50%
Expand Down Expand Up @@ -178,7 +178,7 @@ def set_protocol_fee_bps(new_protocol_fee_bps: uint16):
"""
@notice Set the protocol fee in basis points
@dev Must be below the max allowed fee, and a default
fee_recipient must be set so we don't issue fees to the 0 addresss.
fee_recipient must be set so we don't issue fees to the 0 address.
@param new_protocol_fee_bps The new protocol fee in basis points
"""
assert msg.sender == self.governance, "not governance"
Expand All @@ -196,7 +196,7 @@ def set_protocol_fee_bps(new_protocol_fee_bps: uint16):
def set_protocol_fee_recipient(new_protocol_fee_recipient: address):
"""
@notice Set the protocol fee recipient
@dev Can never be set to 0 to avoid issuing fees to the 0 addresss.
@dev Can never be set to 0 to avoid issuing fees to the 0 address.
@param new_protocol_fee_recipient The new protocol fee recipient
"""
assert msg.sender == self.governance, "not governance"
Expand All @@ -215,7 +215,7 @@ def set_custom_protocol_fee_bps(vault: address, new_custom_protocol_fee: uint16)
@notice Allows Governance to set custom protocol fees
for a specific vault or strategy.
@dev Must be below the max allowed fee, and a default
fee_recipient must be set so we don't issue fees to the 0 addresss.
fee_recipient must be set so we don't issue fees to the 0 address.
@param vault The address of the vault or strategy to customize.
@param new_custom_protocol_fee The custom protocol fee in BPS.
"""
Expand Down
Loading

0 comments on commit 45b5d6b

Please sign in to comment.