Simple-LRT
Last updated
Last updated
The system is comprised of a set of contracts, with the main one being the vault contract (MellowSymbioticVault / MellowVaultCompat). This vault is a modification of the ERC4626 standard, featuring queued withdrawals. To handle these queued withdrawals, there is a separate contract called the SymbioticWithdrawalQueue. The code has multiple audits and is available here: https://github.com/mellow-finance/simple-lrt.
The system is based on the following key contracts:
MellowSymbioticVault (or MellowVaultCompat)
asset (such as wstETH or other ERC20 token), with a corresponding DefaultCollateral.
SymbioticVault - a contract created via the VaultConfigurator link
MellowSymbioticVault
is a core vault contract in Simple-LRT. It has the following functional features:
Deposit and withdrawal are ERC4626 style, withdrawals are async though.
Claim rewards from Symbiotic vaults and distribute it to farms.
Control mechanics for setting limits, pausing withdrawals, deposits and whitelisting deposits.
In the vault contract, several key functions help to manage assets, withdrawals, and interactions with ERC20 tokens. Below is an overview of the most important functions:
all ERC20 functions
asset(): Returns the underlying token of the vault, which is usually wstETH
.
totalAssets(): Returns the total value locked (TVL) of the vault, measured in the underlying asset
.
convertToShares(amount): Calculates the number of LP tokens (shares) you would receive when depositing a specified amount
of the underlying asset
into the vault.
convertToAssets(shares): Converts a given number of LP tokens (shares) back into the equivalent amount of the underlying asset
.
previewDeposit / previewWithdraw / previewMint / previewRedeem: These functions provide information on expected outcomes from various operations (deposits, withdrawals, minting, or redeeming shares). They are useful for estimating returns or required amounts.
getBalances(userAddress): Returns four parameters related to the user’s holdings:
accountAssets
: The total assets (underlying tokens) that the user holds in the vault.
accountInstantAssets
: The portion of the user’s assets that can be instantly withdrawn from the vault.
accountShares
: The total shares (LP tokens) the user holds in the vault.
accountInstantShares
: The portion of the user’s shares that can be instantly withdrawn.
In the case of Symbiotic vaults, withdrawals may take some time due to processing delays. To handle this, we use a separate contract for managing the withdrawal queue, which includes the following functions:
claimableAssetsOf: Returns the amount of assets that the user can claim after a pending withdrawal.
pendingAssetsOf: Provides information about the assets that are pending for withdrawal and are yet to be claimed.
claim(): Allows the user to claim the assets that have become available after a pending withdrawal has been processed.
By splitting the withdrawal logic into a separate contract, we ensure that users can request and later claim their withdrawals without affecting the rest of the vault's operations.
Idle vault is a do-nothing vault for buffering ERC20s for immediate withdrawals.
There is a factory contract for permissionless deployments of the vault.
User transfers ETH/WETH/STETH/WSTETH to EthWrapper
, where it’s wrapped into the WSTETH. In case of other base asset, skip to step 2.
MellowSymbioticVault
mints LRT to the user
Base asset is pushed into SymbioticVault
This withdrawal is a 2-step process. First a call to withdraw
or redeem
that will try to fulfill the withdrawal request with liquid funds as much as possible. If the request is not fulfilled in full on the previous step then the user calls the claim
method after two epochs for withdrawing the rest of the funds.
Withdrawal call
User calls withdraw
or redeem
method of the MellowSymbioticVault
.
This eventually delegates to internal _withdraw
method that takes as input assets
- the amount of assets the user wants to withdraw.
If there’s some WSTETH on the vault balance then it’s immediately used to fulfill the request
If there is still some assets (staked
variable) that needs to be withdrawn, then a withdrawal request for a staked
amount is made into Symbiotic vault. The receiver of the astaked
amount is SymbioticWithdrawalQueue
A request
call to SymbioticWithdrawalQueue
is made that puts a record that it owes. This call uses shares
of symbiotic corresponding to staked
rather than assets. This is because shares can be slashed while being withdrawn.
SymbioticWithdrawalQueue
checks the last epoch the user claimed assets (epoch
variable). It then updates the data for account’s available funds to claim (_handlePendingEpoch
method) for epoch
and epoch
- 1 and then claims (if possible) funds from symbiotic and stores the record that epoch was claimed and funds are ready for distribution (_pullFromSymbioticForEpoch
method).
Claim call
User calls claim
method of the MellowSymbioticVault
.
It delegates to the claim
method of the SymbioticWithdrawalQueue
.
SymbioticWithdrawalQueue
makes the same update as in withdrawal call step #6 (update claimable assets for user, pull from symbiotic)
The funds are transferred to the user
The vault simply acts as a proxy for rewards claiming and the forwarding the rewards to the merkle farm. The distribution of the funds for the rewards will be calculated offchain pro-rata mellow points.
pushRewards
method is called in SymbioticWithdrawalQueue
The rewards are claimed to the vault balance
A share of the rewards (curatorFeeD6
) is cut and distributed to the curator
The rest of the funds goes into the predefined merkle farm (symbioticFarm(farmId).distributionFarm
)