Vault Framework
BoringVault
The BoringVault is the ERC20 token contract that custodies all deposit assets and exposes permissions for minting and burning the vault shares. The other contracts rely on permissions granted by the BoringVault to execute certain actions.
Teller
The Teller—with the permissions granted by the BoringVault to mint and burn the shares—defines the logic for minting shares upon deposit. The Teller checks whether the given asset is part of its list of supported assets, queries the assets' value via the Accountant, and determines how many shares to mint for the given amount of deposit tokens.
The Teller also can be extended to support different types of bridges. For example, the Teller may use the LayerZero bridge to bridge its minted shares.
Accountant
The Accountant is the source of truth for the vault's exchange rate as well as the value of each whitelisted deposit assets.
Because the Teller is able to accept an arbitrary number of whitelisted assets, in order to determine how many shares to mint given an asset, it needs to query both the vault's base exchange rate and the specific price of the asset.
For example, consider a user who wants to deposit 1 wstETH
:
Assume the current exchange rate of the vault is
1.5 ETH/Share
.Assume the current price of
wstETH
is1.2 ETH/wstETH
.When the user deposits
1 wstETH
to the Teller, the Teller first converts this value to theETH
denomination.This is
1 wstETH * 1.2 ETH/wstETH = 1.2 ETH
Then, the Teller converts the
ETH
value to the amount of shares to mint.This is
1.2 ETH / 1.5 ETH/Share = ~0.8 Shares
Manager
The Manager contract ensures the BoringVault's security as its custodied assets are managed and deployed into external protocols by the strategists.
The Manager contract assigns a merkle root to a specific strategist address. The leaves of this merkle root defines the type of transaction that the strategist is able to call on behalf of the BoringVault.
To see an example of how the Manager ensures the security of the BoringVault while delegating the liquidity management to strategists, consider the following example where a strategist is permitted to lend to a market on Ion Protocol:
Lending on Ion Protocol can be done by calling
IonPool.supply(address,uint256,bytes32[]).
The
address
type here referes to the recipient of the mintediTokens
which corresponds 1:1 with the lent asset.The
uint256
type specifies the supply amount, and this does not need to be restricted for security.The
bytes32[]
is a whitelist proof for interacting with theIonPool
contract.
Now, the
Manager
considers the following attack vector:If the strategist is able to make a
supply
call to theIonPool
while specifying theaddress
as a malicious EOA, this means that the strategist would be able to drain the BoringVault's funds by lending to Ion Protocol and sending theiTokens
to a malicious address.This means we would want every BoringVault's
supply
call to specify theaddress
as its own address.
In order to achieve the above, the Manager uploads a merkle root where the leaves are constructed with the following fields:
The address of the
IonPool
.The
bytes4
selector of thesupply
function.The address of the
recipient
A
boolean
for whether the contract call requiresmsg.value
or not.The address of the
Decoder
contract that can parse out the sensitive function parameter (theaddress recipient
field) given calldata of thissupply
function call.
Then, the Manager stores a merkle root built from this leaf, and enforces the strategist to pass in a merkle proof.
Now, if the strategist attempts to pass
calldata
that does not meet all of the requirements specified in the leaf, then theManager
will revert.
This process allows the Manager to guarantee that the strategist can only call transactions allowed by the precomputed merkle root.
Nucleus's vault interfaces were inspired from Veda's open source BoringVault repo. We want to give credit to the Se7enSeas team for creating this architecture and allowing its free distribution.
Last updated