Vault

Git Sourcearrow-up-right

Inherits: ERC4626, Multicall, AccessControlDefaultAdminRules, ReentrancyGuard

Author: Molecular Labs

Vault contract that can allocate a single lender asset over various isolated lending pairs on Ion Protocol. This contract is a fork of the Metamorpho contract licnesed under GPL-2.0 with changes to administrative logic, underlying data structures, and lending interactions to be made compatible with Ion Protocol.

State Variables

OWNER_ROLE

bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");

ALLOCATOR_ROLE

bytes32 public constant ALLOCATOR_ROLE = keccak256("ALLOCATOR_ROLE");

IDLE

IIonPool public constant IDLE = IIonPool(address(uint160(uint256(keccak256("IDLE_ASSET_HOLDINGS")))));

DECIMALS_OFFSET

uint8 public immutable DECIMALS_OFFSET;

ION_POOL_SUPPLY_CAP_SLOT

bytes32 public immutable ION_POOL_SUPPLY_CAP_SLOT = 0xceba3d526b4d5afd91d1b752bf1fd37917c20a6daf576bcb41dd1c57c1f67e09;

ION_POOL_LIQUIDITY_SLOT

BASE_ASSET

MAX_SUPPORTED_MARKETS

supportedMarkets

supplyQueue

withdrawQueue

feeRecipient

feePercentage

lastTotalAssets

caps

Functions

constructor

updateFeePercentage

Updates the fee percentage.

Input must be in [RAY]. Ex) 2% would be 0.02e27.

Parameters

Name
Type
Description

_feePercentage

uint256

The percentage of the interest accrued to take as a management fee.

updateFeeRecipient

Updates the fee recipient.

Parameters

Name
Type
Description

_feeRecipient

address

The recipient address of the shares minted as fees.

addSupportedMarkets

Add markets that can be supplied and withdrawn from.

Elements in supportedMarkets must be a valid IonPool or an IDLE address. Valid IonPools require the base asset to be the same. Duplicate addition to the EnumerableSet will revert. The allocationCaps of the new markets being introduced must be set.

Parameters

Name
Type
Description

marketsToAdd

IIonPool[]

Array of new markets to be added.

allocationCaps

uint256[]

Array of allocation caps for only the markets to be added.

newSupplyQueue

IIonPool[]

Desired supply queue of IonPools for all resulting supported markets.

newWithdrawQueue

IIonPool[]

Desired withdraw queue of IonPools for all resulting supported markets.

_addSupportedMarkets

removeSupportedMarkets

Removes a supported market and updates the supply and withdraw queues without the removed market.

The allocationCap values of the markets being removed are automatically deleted. Whenever a market is removed, the queues must be updated without the removed market.

Parameters

Name
Type
Description

marketsToRemove

IIonPool[]

Markets being removed.

newSupplyQueue

IIonPool[]

Desired supply queue of all supported markets after the removal.

newWithdrawQueue

IIonPool[]

Desired withdraw queue of all supported markets after the removal.

updateSupplyQueue

Update the order of the markets in which user deposits are supplied.

Each IonPool in the queue must be part of the supportedMarkets set.

Parameters

Name
Type
Description

newSupplyQueue

IIonPool[]

The new supply queue ordering.

_updateSupplyQueue

updateWithdrawQueue

Update the order of the markets in which the deposits are withdrawn.

The IonPool in the queue must be part of the supportedMarkets set.

Parameters

Name
Type
Description

newWithdrawQueue

IIonPool[]

The new withdraw queue ordering.

_updateWithdrawQueue

_validateQueueInput

*The input array contains ordered IonPools.

  • Must not contain duplicates.

  • Must be the same length as the supportedMarkets array.

  • Must not contain indices that are out of bounds of the supportedMarkets EnumerableSet's underlying array. The above rule enforces that the queue must have all and only the elements in the supportedMarkets set.*

Parameters

Name
Type
Description

queue

IIonPool[]

The queue being validated.

updateAllocationCaps

Update allocation caps for specified IonPools or the IDLE pool.

The allocation caps are applied to pools in the order of the array within supportedMarkets. The elements inside ionPools must exist in supportedMarkets. To update the IDLE pool, use the IDLE constant address.

Parameters

Name
Type
Description

ionPools

IIonPool[]

The array of IonPools whose caps will be updated.

newCaps

uint256[]

The array of new allocation caps to be applied.

reallocate

Reallocates the base asset supply position across the specified IonPools. This call will revert if the resulting allocation in an IonPool violates the pool's supply cap.

*Depending on the order of deposits and withdrawals to and from markets, the function could revert if there is not enough assets withdrawn to deposit later in the loop. A key invariant is that the total assets withdrawn should be equal to the total assets supplied. Otherwise, revert.

  • Negative value indicates a withdrawal.

  • Positive value indicates a supply.*

Parameters

Name
Type
Description

allocations

MarketAllocation[]

Array that indicates how much to deposit or withdraw from each market.

accrueFee

Manually accrues fees and mints shares to the fee recipient.

_supplyToIonPool

Iterates through the supply queue to deposit the desired amount of assets. Reverts if the deposit amount cannot be filled due to the allocation cap or the supply cap.

External functions calling this must be non-reentrant in case the underlying IonPool implements callback logic.

Parameters

Name
Type
Description

assets

uint256

The amount of assets that will attempt to be supplied.

_withdrawFromIonPool

Iterates through the withdraw queue to withdraw the desired amount of assets. Will revert if there is not enough liquidity or if trying to withdraw more than the caller owns.

External functions calling this must be non-reentrant in case the underlying IonPool implements callback logic.

Parameters

Name
Type
Description

assets

uint256

The desired amount of assets to be withdrawn.

deposit

Transfers the specified amount of assets from the sender, supplies into the underlying IonPool markets, and mints a corresponding amount of shares.

All incoming deposits are deposited in the order specified in the deposit queue.

Parameters

Name
Type
Description

assets

uint256

Amount of tokens to be deposited.

receiver

address

The address to receive the minted shares.

mint

Mints the specified amount of shares and deposits a corresponding amount of assets.

Converts the shares to assets and iterates through the deposit queue to allocate the deposit across the supported markets.

Parameters

Name
Type
Description

shares

uint256

The exact amount of shares to be minted.

receiver

address

The address to receive the minted shares.

withdraw

Withdraws specified amount of assets from IonPools and sends them to the receiver in exchange for burning the owner's vault shares.

All withdraws are withdrawn in the order specified in the withdraw queue. The owner needs to approve the caller to spend their shares.

Parameters

Name
Type
Description

assets

uint256

The exact amount of assets to be transferred out.

receiver

address

The receiver of the assets transferred.

owner

address

The owner of the vault shares.

redeem

Redeems the exact amount of shares and receives a corresponding amount of assets.

After withdrawing assets, the user gets exact assets out. But in the IonPool, the resulting total underlying claim may have decreased by a bit above the assets amount due to rounding in the pool's favor. In that case, the resulting totalAssets() will be smaller than just the newTotalAssets - assets. Predicting the exact resulting totalAssets() requires knowing how much liquidity is being withdrawn from each pool, which is not possible to know until the actual iteration on the withdraw queue. So we acknowledge the dust difference here. If the lastTotalAssets is slightly greater than the actual totalAssets, the impact will be that the calculated interest accrued during fee distribution will be slightly less than the true value.

Parameters

Name
Type
Description

shares

uint256

The exact amount of shares to be burned and redeemed.

receiver

address

The address that receives the transferred assets.

owner

address

The address that holds the shares to be redeemed.

decimals

Returns the decimals places of the token.

maxDeposit

Returns the maximum amount of assets that the vault can supply on Ion.

The max deposit amount is limited by the vault's allocation cap and the underlying IonPools' supply caps.

Returns

Name
Type
Description

<none>

uint256

The max amount of assets that can be supplied.

maxMint

Returns the maximum amount of vault shares that can be minted.

Max mint is limited by the max deposit based on the Vault's allocation caps and the IonPools' supply caps. The conversion from max suppliable assets to shares preempts the shares minted from fee accrual.

Returns

Name
Type
Description

<none>

uint256

The max amount of shares that can be minted.

maxWithdraw

Returns the maximum amount of assets that can be withdrawn.

Max withdraw is limited by the owner's shares and the liquidity available to be withdrawn from the underlying IonPools. The max withdrawable claim is inclusive of accrued interest and the extra shares minted to the fee recipient.

Parameters

Name
Type
Description

owner

address

The address that holds the assets.

Returns

Name
Type
Description

assets

uint256

The max amount of assets that can be withdrawn.

maxRedeem

Calculates the total withdrawable amount based on the available liquidity in the underlying pools and converts it to redeemable shares.

Max redeem is derived from Γ§onverting the _maxWithdraw to shares. The conversion takes into account the total supply and total assets inclusive of accrued interest and the extra shares minted to the fee recipient.

Parameters

Name
Type
Description

owner

address

The address that holds the shares.

Returns

Name
Type
Description

<none>

uint256

The max amount of shares that can be withdrawn.

totalAssets

Returns the total claim that the vault has across all supported IonPools.

IonPool.balanceOf returns the rebasing balance of the lender receipt token that is pegged 1:1 to the underlying supplied asset.

Returns

Name
Type
Description

assets

uint256

The total assets held on the contract and inside the underlying pools by this vault.

previewDeposit

Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.

Inclusive of manager fee.

previewMint

Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.

Inclusive of manager fee.

previewWithdraw

Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.

Inclusive of manager fee.

previewRedeem

Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.

Inclusive of manager fee.

_decimalsOffset

_deposit

_withdraw

_maxDeposit

_maxWithdraw

_accrueFee

_accruedFeeShares

The total accrued vault revenue is the difference in the total iToken holdings from the last accrued timestamp and now.

_convertToSharesWithFees

NOTE The IERC4626 natspec recommends that the _convertToAssets and _convertToShares "MUST NOT be inclusive of any fees that are charged against assets in the Vault." However, all deposit/mint/withdraw/redeem flow will accrue fees before processing user requests, so manager fee must be accounted for to accurately reflect the resulting state. All preview functions will rely on this WithFees version of the _convertTo function.

_convertToAssetsWithFees

NOTE The IERC4626 natspec recommends that the _convertToAssets and _convertToShares "MUST NOT be inclusive of any fees that are charged against assets in the Vault." However, all deposit/mint/withdraw/redeem flow will accrue fees before processing user requests, so manager fee must be accounted for to accurately reflect the resulting state. All preview functions will rely on this WithFees version of the _convertTo function.

_convertToSharesWithTotals

Returns the amount of shares that the vault would exchange for the amount of assets provided. This function is used to calculate the conversion between shares and assets with parameterizable total supply and total assets variables.

_convertToAssetsWithTotals

Returns the amount of assets that the vault would exchange for the amount of shares provided. This function is used to calculate the conversion between shares and assets with parameterizable total supply and total assets variables.

_updateLastTotalAssets

_zeroFloorSub

_simulateWithdrawIon

Emulates the actual _withdrawFromIonPool accounting to predict accurately how much of the input assets will be left after withdrawing as much as it can. The difference between this return value and the input assets is the exact amount that will be withdrawn.

Returns

Name
Type
Description

<none>

uint256

The remaining assets to be withdrawn. NOT the amount of assets that were withdrawn.

_withdrawable

The max amount of assets withdrawable from a given IonPool considering the vault's claim and the available liquidity. A minimum of this contract's total claim on the underlying and the available liquidity in the pool.

Returns

Name
Type
Description

<none>

uint256

The max amount of assets withdrawable from this IonPool.

_depositable

The max amount of assets depositable to a given IonPool. Depositing the minimum between the two diffs ensures that the deposit will not violate the allocation cap or the supply cap.

Returns

Name
Type
Description

<none>

uint256

The max amount of assets depositable to this IonPool.

getSupportedMarkets

Returns the array representation of the supportedMarkets set.

Returns

Name
Type
Description

<none>

address[]

Array of supported IonPools.

containsSupportedMarket

Returns whether the market is part of the supportedMarkets set.

Parameters

Name
Type
Description

pool

address

The address of the IonPool to be checked.

Returns

Name
Type
Description

<none>

bool

The pool is supported if true. If not, false.

supportedMarketsAt

Returns the element in the array representation of supportedMarkets. index must be strictly less than the length of the array.

Parameters

Name
Type
Description

index

uint256

The index to be queried on the supportedMarkets array.

Returns

Name
Type
Description

<none>

address

Address at the index of supportedMarkets.

supportedMarketsIndexOf

Returns the index of the specified market in the array representation of supportedMarkets.

The _positions mapping inside the EnumerableSet.Set returns the index of the element in the _values array plus 1. The _positions value of 0 means that the value is not in the set. If the value is not in the set, this call will revert. Otherwise, it will return the position - 1 value to return the index of the element in the array.

Parameters

Name
Type
Description

pool

address

The address of the IonPool to be queried.

Returns

Name
Type
Description

<none>

uint256

The index of the pool's location in the array. The return value will always be greater than zero as this function would revert if the market is not part of the set.

supportedMarketsLength

Length of the array representation of supportedMarkets.

Returns

Name
Type
Description

<none>

uint256

The length of the supportedMarkets array.

_supportedMarketsIndexOf

Events

UpdateSupplyQueue

UpdateWithdrawQueue

ReallocateWithdraw

ReallocateSupply

FeeAccrued

UpdateLastTotalAssets

Errors

InvalidQueueLength

AllocationCapExceeded

InvalidReallocation

InvalidMarketRemovalNonZeroSupply

InvalidUnderlyingAsset

MarketAlreadySupported

MarketNotSupported

AllSupplyCapsReached

NotEnoughLiquidityToWithdraw

InvalidIdleMarketRemovalNonZeroBalance

InvalidQueueContainsDuplicates

MarketsAndAllocationCapLengthMustBeEqual

IonPoolsArrayAndNewCapsArrayMustBeOfEqualLength

InvalidFeePercentage

MaxSupportedMarketsReached

Structs

MarketAllocation

MarketsArgs