It’s hard to ignore the fact that DeFi continues to be a significant use case for blockchain. As of this writing, DeFi protocols have over $58 billion in value locked, known as TVL (total value locked). This value comes in the way of digital assets, such as tokens.
This continued demand in DeFi protocols highlights the need for standards like ERC-4626, crucial for ensuring consistency.
In this article, I share my learnings and thoughts around ERC-4626, a standardization for tokenized vaults.
Here’s what we’ll cover.
The Composability Nightmare Problem
Why the USB-C Era for Token Vaults is Needed
The 18 Methods of ERC-4626 (And two of the most important ones)
Before we dive into ERC-4626, here are some important terms that are helpful to understand:
Interface: an "interface" acts as a control panel for a program, listing available functions, their required inputs (parameters), and the resulting outputs (return values).
Behavior: refers to how a system or component consistently manages its actions and status, responding to inputs and events.
Adapter Code: is customarily created to transform or adjust one interface or protocol into another, enabling systems or components originally designed to work separately to interact smoothly.
Connectors: serve as universal connection points between systems, enabling communication and data exchange. They act as bridges that link different systems or components.
Composability: represents the ability of various components or systems to seamlessly integrate and operate together, resulting in a harmonious and functional whole.
Token vaults are used in DeFi to earn rewards on your crypto. They are smart contracts that accept deposits and earn yield (interest). Vaults implement complex strategies, like leveraging, auto-compounding, and diversifying across various protocols in order to get the best return.
This variance in strategy and implementations causes a few problems, putting you right in the middle of what I call a 'composability nightmare’.
In blockchain, composability is important because it allows different protocols and applications to interact seamlessly, creating a more interconnected and functional ecosystem. The way token vaults are currently implemented, with their varied requirements and behaviors, doesn't exactly help in enhancing this composability.
This is like having to buy a new charger every time you upgrade your phone because the old one is obsolete. Can you imagine how filled your drawers would be? This situation was not only inconvenient but also both time-consuming and costly.
This is how vault tokens work today. Vaults are cell phones- and chargers represent the connectors.
Just as each new phone model often required a different charger back in 2008, DeFi protocols integrating token vaults must continually develop and update specialized ‘chargers’ (adapters and connectors) to accommodate the unique requirements of each vault
This current way has a few drawbacks:
Increases the likelihood of errors in code. Protocol engineers must understand various implementations, adding complexity and risk of security costs due to buggy code.
Requires increased resources. Time spent learning and implementing the code is an expense to the organization.
ERC-4626 aims to provide a standardized interface and behavior for token vaults.
Let’s first start with illustrating a basic token vault. These are the basic functionalities we’d like to have:
Allow user to deposit
Allow user to withdraw
Read vault balance
Vault manage asset to share amount
ERC-4626 is a way to universally implement token vaults, so protocol engineers can easily integrate them with other dapps and tools.
As we've discussed, even basic functionalities in token vaults can be implemented in various ways, leading to complexities. For instance, when considering any standard function within these vaults, questions arise about its structure and parameters. What data should it accept? How should it handle different user scenarios?
You can appreciate how such nuances can quickly escalate into larger composability problems, especially as the vaults grow in complexity.
ERC-4626 offers 18 methods and 2 events for developers to easily inherit in their vaults. This standard provides developers a ‘feature ready’ concept. While not primarily focused on security, an increase in security emerged as a beneficial byproduct.
Developed through community proposals and votes, ERC-4626 adheres to good conventions and essential safeguards. It ensures functions have standardized meanings and behaviors. Safeguards, such as the checks and effects pattern, are implemented to prevent malicious actors from exploiting the contract logic before transactions are finalized.
We won’t be reviewing all 18 methods, but we will evaluate the deposit and withdraw methods.
Let’s begin with a diagram illustrating the basic user flow of depositing assets into a vault.
I want to deposit $100 USDC. In exchange I will be issued vUSDC (vault tokens) that represent the shares I own in the vault.
Transfer assets to vault
Issue vault tokens
Sounds pretty basic, but let’s dive deeper and see what the code is actually doing. You can find the code to this method here.
Parameters
The deposit function is called with two parameters
, assets
and receiver
. assets are expected to be a number representing the amount of the token that the depositor wants to deposit. The receiver is the address that will receive the vault tokens.
Calculate and Validate Shares
This check ensures that the amount of assets about to be deposited will result in the issuance of vault tokens. If the number is greater than 0, it moves onto the next line. If the number is 0, then the transaction will be reverted. Gas will still be paid up to this point.
Transfer Assets to Vault
In order to securely transfer the assets from the user to the vault, the contract calls the safeTransferFrom
method, which is inherited from the ERC-20 standard. Because it facilitates the transfer of assets from the user's control to the contract, the user must first call the approve method on the token contract to grant the vault contract an allowance to use their assets.
Issue Vault Tokens
This involves another ERC-20 method, where _mint
generates a new supply of vault tokens. The new supply, also known as shares, are then sent to the receiver.
Deposit Event
The contract records the deposit details in an event, capturing the initiator, receiver, and the amounts of assets and shares. Frontend applications can listen to these events to trigger UI updates.
Optional Hook
Is intended to execute some additional code after the deposit process (including asset transfer and share minting) has been completed.
This method's construction was no accident. It follows best practices by using secure asset transfer methods, implementing checks for share calculation, and utilizing event logging for transparency.
Let's start by visualizing the basic process of withdrawing assets from a vault.
I want to withdraw $50 USDC. When I initiate the transaction, the vault will then burn the corresponding vault tokens in comparison to the value of my request withdraw.
Here’s what happens on a high level.
Burn Vault Tokens
Transfer Assets
Let’s take a deep dive into the code. You can find the code to this method here.
Parameters
The withdraw
function takes three parameters: assets
, receiver
, and owner
. Assets
is the amount of USDC I wish to withdraw. The receiver
is the address that will receive the assets, and owner
is the address that owns the vault tokens to be burned
Calculate Shares for Assets
First, the contract calculates the number of shares equivalent to the assets I want to withdraw using previewWithdraw(assets)
. This determines how many of my vault tokens (vUSDC) are needed for the withdrawal.
Ownership Verification
The contract checks if I am the owner of the vault tokens. If not, it proceeds to the next step.
Authorized Withdrawal Check
If I'm not the owner, the contract calculates how many shares I am authorized to withdraw on behalf of the owner. This step involves checking the allowance set by the owner for my address.
Permission Validation
The contract ensures I have the owner's permission to withdraw the specified amount. If the allowance is sufficient, it's reduced by the number of shares being withdrawn.
Optional Hook
Before the actual withdrawal, an optional hook can execute additional logic.
Burn Vault Tokens
Next, the contract burns the calculated number of vault tokens (vUSDC) from the owner's balance. This step reduces the total supply of vault tokens in circulation.
Withdraw Event
The contract records the withdrawal details in an event, noting who initiated the withdrawal, who is receiving the assets, and the amounts involved. Frontend applications can listen to these events to trigger UI updates.
Transfer Assets
Finally, the assets (USDC) are transferred from the vault to the receiver's address using the safeTransfer
method. Unlike the deposit
function, where safeTransferFrom
is used to pull assets from the user's account with their permission, safeTransfer
is employed here because the assets are being pushed from the vault's own balance to the receiver. This method simplifies the process, as it doesn't require the prior approval step that safeTransferFrom
requires.
The withdraw method's design is intentional, ensuring security with strict checks and clear event logging, embodying best practices for efficient and transparent transactions.
Now that we have looked at each part of the process, let's explore a real-world implementation of an ERC-4626 vault. We'll be reviewing the AaveV3ERC4626Reinvestment contract, which is deployed on the Polygon network. Aave is a well-known DeFi lending platform.
Aave's implementation in this contract demonstrates the process of earning additional rewards through Aave's liquidity mining programs.
We'll highlight the differences in these implementations.
In Solidity, when a function inherited from a parent contract is customized, it uses the 'override' keyword to indicate that the function in the child contract is intended to override a function from the parent contract. While the deposit
function remains unchanged in this contract, the afterDeposit
method – the optional hook we briefly discussed earlier – is overridden.
afterDeposit
Let’s begin by reviewing how afterDeposit
receives custom logic to enhance the functionality of this vault.
Approval and Asset Transfer: Before depositing assets into Aave, the function first ensures that the vault contract has permission to use the user's assets. It does this by calling the safeApprove
method on the token contract (asset
). This step is essential because it allows the vault contract to move the user's assets.
Deposit into Aave: Once the approval is in place, the function calls the supply
method on the lendingPool
contract. This action deposits the specified assets
into Aave, making them available for lending and yield generation within the Aave protocol. The address(this)
parameter designates the vault contract as the depositor, and 0
indicates no additional specific instruction for this deposit.
Withdraw
Alright, now let's review the withdraw
function; this function is also overridden, and you can see it's implementing custom functionality.
Approval and Asset Transfer: In this step, the afterDeposit
function begins by ensuring that the vault contract has the necessary permission to use the user's assets. This is achieved by invoking the safeApprove method on the token contract (referred to as asset). The purpose of this step is to authorize the vault contract to manage the user's assets securely.
Asset Redemption: Following the approval, the function proceeds to deposit the specified assets into the Aave protocol. This is accomplished by calling the supply
method on the lendingPool
contract. This action effectively moves the assets into Aave, where they can be utilized for lending and yield generation within the Aave ecosystem.
I’m extremely bullish on this standard. It represents a significant step forward in addressing the complexities and fragmentation we currently see in DeFi.
Here is why:
L2 Adoption: ERC-4626 simplifies integration with L2 networks, enhancing the DeFi experience for users.
Trust and Innovation: It fosters trust and encourages innovation within the DeFi community by establishing common standards for security and functionality.
Streamlined Development: ERC-4626 simplifies the development process, enhances security, and allows developers to focus on refining strategies and building better DeFi applications.
Now we have gained a clear understanding of what token vaults are, recognized the importance of standardization, and grasped how ERC-4626 significantly contributes to enhancing consistency and efficiency within the DeFi ecosystem.
If you're curious about the adoption of this standard, currently, there are over 100 vaults that are ERC-4626 compliant.