The recent hack on Hundred Finance used an exploit path reminiscent of attacks that are typically reserved for ERC-4626 vaults. In reality, however, the vulnerabilities of ERC-4626 vaults apply to a large range of smart contracts that may initially seem to have nothing in common with the vault standard. In this three part deep-dive, we'll explore the common characteristics of vault contracts and “vault-like” contracts, examine their shared vulnerabilities in detail, and walk through several mitigation strategies and their effectiveness.
What is ERC-4626?
Before going too far in the analysis, we need to understand what ERC-4626 is. Historically, there have been many implementations of vault contracts, each with their own unique way of handling staking and rewards. This fragmentation created a poor user experience as users had to re-familiarize themselves with each unique vault implementation. Additionally, this fragmentation made it more time-consuming for third-parties to integrate with these contracts and increased the risk of incorrect integrations, leading to security vulnerabilities. The cascade effect was that it became even more treacherous for other protocols to safely integrate and interact with these vaults.
Enter ERC-4626, a standard created to unify the technical parameters of yield-bearing vaults. However, despite the creation of the ERC-4626 vault standard, fragmentation still exists today. Among the reasons for this are:
- Many popular vault implementations existed before ERC-4626 was created in late 2021 and finalized in early 2022. These vaults are still in use today, with many forks and external protocols integrated into them. Changing these older vault implementations (assuming they aren’t immutable!) to use the new standard would be no small engineering feat, and would certainly break all existing integrations.
- ERC-4626 aims to be the base implementation for vaults, so for protocols that want to add unique features to their vault contracts, the standard can seem too constraining for their use-cases. Additionally, some aspects of the standard are intentionally general or abstract. This can easily cause confusion for developers who desire a more turnkey solution.
The above reasons, combined with many others, have hindered the adoption of ERC-4626 as the “de-facto” vault standard.
Introducing “vault-like” smart contracts
The early fragmentation of vault contract implementations, combined with the complexity of the ERC-4626 vault standard implementation, has led developers to make their own partial vault implementations. To add further confusion, there exist many smart contracts that do not necessarily behave as vaults, but still use the same underlying logic. Therefore, we have three broad categories of contracts that are relevant to the vulnerabilities we will be talking about in this deep-dive: ERC-4626 vault contracts, vault contracts that existed before ERC-4626 (or non ERC-4626 vaults), and “vault-like” contracts. Smart contracts must have the following characteristics in order to be placed into the “vault-like” category:
Using the above characteristics, we can begin to identify functions within a smart contract that would be considered characteristics of a vault-like implementation. These functions will have a subset of logic that behaves similarly to ERC-4626 vault contracts and vault contracts that existed before ERC-4626. Typically, the logic will involve operations on the following proportion:
From this proportion, we can derive a more specific set of formulas that map out to four main functions: deposit, mint, withdraw, and redeem. Note that a vault-like contract only needs to implement one function from each pair, such that the presence of deposit/withdraw, deposit/redeem, mint/withdraw, or mint/redeem would all cause a contract to be considered vault-like. We can represent each of these four functions with the following mathematical formulas:
Notice how the calculations for depositing and withdrawing are the same, and how the calculations for minting and redeeming are the same. The only difference between each pair of similar calculations is the rounding direction of the division. We will discuss rounding in more detail in parts 2 and 3 of this deep dive.
As an aside, the above differences between the deposit/mint and withdraw/redeem functions are subtle. However, we can use the following questions to help identify which function is being used:
Now that we have a list of defining characteristics and a set of mathematical formulas, we can use this information to identify vault-like contracts.
Examples of non ERC-4626 vault contracts and vault-like contracts
If you’ve read the ERC-4626 standard, you’re probably already familiar with the deposit, mint, withdraw, and redeem functions, as they are defined verbatim in the standard. However, how the stated characteristics and arithmetic operations are represented in the functions of smart contracts that do not conform to the ERC-4626 standard may not be as obvious. There are many well-known examples of non ERC-4626 vault contracts and vault-like contracts that have the above function characteristics that you may not be aware of.
Compound V2 is an example of a vault contract whose implementation is older than the ERC-4626 standard. The external mint function combined with the internal mintFresh function allows users to deposit an asset and returns a cToken asset equivalent, which satisfies the mint function characteristic. The external redeem function combined with the internal redeemFresh function allows users to redeem their cToken asset for a proportional quantity of the original deposit asset, plus rewards accrued in the contract. This satisfies the redeem function characteristic, and thus Compound can be classified as a non ERC-4626 vault.
An example of a vault-like contract can be found in The Graph’s protocol. While their protocol spans multiple different contracts, each with their own functionality, there are a few contracts that can be considered vault-like. One such instance is the Staking contract. The external delegate function combined with the internal _delegate function in the Staking contract allows users to delegate GRT, but does not return a completely new asset to the user. Instead, it simply accounts for the user’s deposit into the contract. The user is then able to call the external undelegate function which uses the internal _undelegate function to redeem this internal accounting balance for a proportional amount of GRT. Delegates are awarded additional GRT via The Graph’s protocol as an incentive for delegating to indexers who in turn process queries on subgraphs the indexer is allocated to. While the functions themselves may not behave in the way that is expected of ERC-4626 compliant contracts, the Staking contract in The Graph’s protocol allows users to partake in functionality that resembles vaults. Thus, the Staking contract can be classified as a vault-like contract.
In the first part of our deep-dive, we discussed why the ERC-4626 standard was created, and the pitfalls that have led to developers' hesitance in adapting their smart contracts to the standard. Additionally, we went over the characteristics that make a smart contract vault-like, described in detail the mathematical formulas that represent those characteristics, and identified relevant examples of smart contracts that are either non ERC-4626 compliant or vault-like. Stay tuned for the second part of this deep-dive, where we’ll go over vulnerabilities shared by ERC-4626 vault contracts, non ERC-4626 vault contracts, and vault-like contracts.