Skip to main content

Introducing Apollo: analyzing the usage of ERC4626 across chains

· 14 min read

Introduction

For the last few months, we've been working on Apollo (docs), a tool to make it easy for anyone to query, filter, transform and save EVM chaindata based on a schema.

We built Apollo because we needed to be able to scrape obscure EVM chaindata fast, and the tools currently out there were too limiting in what they could do. They either run only on a couple chains, or rely on indexing, which is a process that takes time and is not feasible for just any protocol. Apollo interacts directly with the standardized JSON RPC API that any EVM node implementation should expose. This means that as long as you have a JSON RPC API for your chain, Apollo will be able to run there. The only other thing you need is an ABI.

We believe that the best way to introduce a tool like this is to show its value in a real world example. Inspired by this tweet, we set about analyzing the usage of the new ERC4626 Tokenized Vault Standard across multiple chains. If you would like to explore more chains, at the end of the article you will be able to do that yourself, because we are open-sourcing Apollo.

What is ERC4626?

ERC4626 is a new token standard aims to clear up the problems with having different implementations of tokenized vaults. One of the most powerful mechanisms in DeFi is composability, but composability doesn't work without standards. If you know ERC20 (token standard) or ERC721 (NFT standard), you know how crucial they are. One of the more important products in DeFi are yield-bearing tokenized vaults. If you've ever staked Sushi in return for xSushi, or deposited ETH on Aave and received aETH, you've used these products. They represent shares of an underlying token that generate interest over time. The problem is that when building applications that can integrate with these tokens, you have to build an integration for each separate implementation. This is complex, error-prone, and resource intensive, resulting in less applications actually doing it. Bad for composability.

ERC4626 aims to set a standard for these products called the Tokenized Vault Standard. If an application works with ERC4626, it works with any yield-bearing token that implements the standard. This will drastically lower the integration effort and will enable a renaissance in DeFi. It will, for example, provide lending platforms the ability to easily accept any ERC4626 token as collateral, which would be one example of composable yield.

Let's take a look at some of the events and methods of the interface (incomplete), because these will be useful later.

ierc20.sol
abstract contract IERC4626 is ERC20 {
// Emitted every time someone deposits
event Deposit(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);

// Emitted every time someone withdraws
event Withdraw(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);

// Returns the address of the underlying token
function asset() external view virtual returns (address asset);

// Returns the amount of assets managed by the vault
function totalAssets() external view virtual returns (uint256 totalAssets);

// Mints `shares` vault tokens by depositing exactly `assets` underlying assets
function deposit(uint256 assets, address receiver) external virtual returns (uint256 shares);

// Mints exactly `shares` vault tokens by depositing `assets` underlying assets
function mint(uint256 shares, address receiver) external virtual returns (uint256 assets);

// Redeems `shares` from `assets`
function withdraw(
uint256 assets,
address receiver,
address owner
) external virtual returns (uint256 shares);

// Redeems `shares` from `assets`
function redeem(
uint256 shares,
address receiver,
address owner
) external virtual returns (uint256 assets);

// Other view functions omitted
...
}

Analysis

Since we believe ERC4626 will be a very important building block in future DeFi protocols, we wanted to analyze its usage. Partly because we live in a multi-chain world, and partly because we want to highlight a competitive feature that Apollo has, we will be looking at 4 chains: Ethereum, Polygon, Arbitrum and Optimism. This analysis serves as an introduction to using Apollo, but you can also just move on to the results.

Apollo workflow

This is the game plan: we are going to collect every ERC4626 Deposit event (see above) across the chains, from February 1 until June 14. The problem is that the Deposit event signature is not unique to the ERC4626 event, because it is derived from the event name and its types. Under the hood, what we're actually filtering for is an event topic, which in this case will be

keccak256("Deposit(address,address,uint256,uint256)") = 0xdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7

There are other Deposit events that have the same signature, so we need to find a way to filter out the right ones. With Apollo, we can call methods at the time an event occurs, and the method will be called on the contract that emitted the event. So to filter out the ERC4626 events, we can call the asset method, which is part of the interface. If this call succeeds, we can be relatively sure the contract is an ERC4626 implementation. If this call reverts, however, which means that the contract does not adhere to the standard, Apollo will just move on and save nothing.

Schema

This is the schema we're going to run:

schema.hcl
start_time = format_date("02-01-2006 15:04", "01-02-2022 00:00")
end_time = now

loop {
items = ["ethereum", "polygon", "arbitrum", "optimism"]

query "erc4626_deposits" {
chain = item

event "Deposit" {
abi = "erc4626.abi.json"
outputs = ["caller", "owner", "assets", "shares"]

// This will revert if the contract does not have
// this method (required in the ERC4626 standard)
method "asset" {
outputs = ["asset"]
}

method "decimals" {
outputs = ["decimals"]
}

method "symbol" {
outputs = ["symbol"]
}
}

save {
block = blocknumber
time = timestamp
contract = contract_address
tx = tx_hash
// We need the chain to differentiate
// the results
chain = chain

underlying = asset
caller = caller
owner = owner
assets = parse_decimals(assets, decimals)
shares = parse_decimals(shares, decimals)
symbol = symbol
}
}
}

We first have to define our time range, which we can do with start_time and end_time. format_date(fmt, date) can be used to convert a datetime to a UNIX timestamp, according to a format. The now variable is provided by the DSL.

Next up, we'll define a loop block. For every item in items, Apollo will create a separate query, with the item as a variable. The name of the query (erc4626_deposits), will be the name of our output file / output table. We want to filter every Deposit event, which we can do by declaring an event block with the Deposit label. In the event block, we define the ABI (which can be found in the Apollo config directory, see setting up docs), and the outputs we want to save for further processing. These have to match the ABI exactly.

After this, we want to gather some extra information by calling some methods on the contract of interest. We do that with method blocks, which have the method name as a label. Just like in event, we define our outputs. There are 2 blocks that we don't need here, which are transform and filter, but you can read more on them in the schema docs. In the save block, we define the final format of our output columns. The save block has access to any previous output parameters, as well as other event context variables like blocknumber, timestamp, contract_address, tx_hash, and chain, which we all want to save. There are some helper functions available as well, and we'll use parse_decimals(raw, decimals) to format our output.

Running

Before running, make sure that every chain has a corresponding JSON-RPC API field in your config.yml. We suggest using either your own nodes, or providers like Alchemy or Chainstack, because the default public endpoints will not do. Even when using a specialized provider, you can get rate limited. This is why we've also provided an option to rate limit Apollo itself. For this research, we used Alchemy APIs with the default value of 100:

apollo --stdout --csv --log-level 0 --rate-limit 100

We managed to not exceed our CU capacity, and not get any timeouts. It took around 5 minutes to collect this data. If you have your own node, you can crank this number up. When running this with our own Erigon node and a value of 500, we managed to collect everything in around 30 seconds.

Internally, Apollo uses primitive caching, and methods like symbol and decimals are cached for every contract, drastically reducing the amount of calls made to the API. At the end of execution, you will see some stats printed for each chain:

Results

Get the results (8328 lines): erc4626_deposits.csv

Daily Deposit events per chain

As we can see here, ERC4626 adoption on Polygon has been the most succesful, perhaps surprisingly. Since its inception, Polygon has had 4046 ERC4626 Deposit events. Ethereum comes in second with 3287 events, Optimism third with 939 events, and finally Arbitrum, with only 55 events. But Ethereum was by far the first with any Deposit events at all, we'll come back to that later.

Zooming in to where things become interesting, we can see that usage really started picking up on May 4th.

This is when both Thorswap Staking V2 on Ethereum and Aavegotchi wapGHST Staking on Polygon went live.

Protocols

Ethereum

Let's start with Ethereum, since it had the first deployments. Focusing in on the daily Deposit events on Ethereum, we get the following chart:

Looking at those first deposits and their symbols, we can figure out which protocols spearheaded ERC4626 adoption:

SymbolFirst_occurenceTotal_depositsTx_hash
d3CVX2022-02-14580x3bde42aef1d795ec11a2fdec2508fca880a4a73b4818e743790e6a14b2850b26
fei3crvCVX2022-02-231110xb7d0cc8597c00bdee0363c1a69db236e50e247403315650f8e5c6d76d18720e1
tricryptoCVX2022-02-24130xa116b76522fcf8a5efad14cf8ed4817c2ee6b5bc884b16cf752c6f18b2fc015d
d3convex2022-02-24140x3be7f2ce5ddf77d923ed56b5dd4ddbde64a622e4075984be0a5727e417ec462b
fei3crvConvex2022-02-2450x64dab869db30d6b1f817b09bb8c0b4fd8b89f617f075cf2a86b743768c9a5d5b
alusd3crvConvex2022-02-2540xc06d393589da3f911912b97f879c95ed22c345bb1aa208c039b477b4db16912b
FRX3CRVCVX2022-02-25180x946ddcc89737b64ca2598dea0ef9aedf123072804f90f96f437959995ad0a6cc
steCRVCVX2022-02-25240x48e7c4ae4adc241375449b392c58236427758e878c8d5b8aa9a80d6fae0d068f
USTwCVX2022-02-25290x2b8d499ce18b85c52193e03a1292fd833835c8122f1046fa1ce7b4434cbc3b10
(re)TOKE2022-03-0610xddc45b5f98e26d8a0331004fcb753d1bbdc6410295acb05bb201a0702bee9990
tsTRIBE2022-03-2150x1e54269a8673a8620bef3a1c29a0d324ab608d5080e3937bb491a57ccc4344ec
wfFEI-82022-03-2240xdcc3cb38450972076ff91ba6ef798355635db1bdeba581f409863cda997e83a4
TTV2022-03-3010x7c5cef1e9fa8f2f52a0b7baeb5db83fc85dae357e39c657f2fea3635b1827ada
cvxFXSFXS2022-04-05380x2c1a66d9024c734f3aa204306973d9ecd3d13bd5c84e8ea1c31fa3ea852cf0d3
rETHwstETH2022-04-0650xf8273032e398567741f938c4daf34ece9dd70aaef4fd476728d349b9ca8016f8
cvxCRVCRV2022-04-10410xe542dbe25913ed321485ebd75bea95ee5bcb9e6ccbc3fd6a387f61f82b7f11be
WOUSD2022-04-11100x6ca75d83741ba76c0ead05fb7bc93240472460a601fca1e8e236f976485956bd
tsBAL2022-04-1910xe66b53b0ac06c9617010197d370ed68792aef7a6b30e8bfa200a2f77e7aec1ce
tALCX2022-04-1910x7244feb527877b8d80ae71a4d044423087bbca0d0c52f70ea94df8366be52dbf
alETHLp2022-04-1910xe8310a56c5721598373a936e6ffc04714510011a4fcee729afb142d703954284
ALCXETH2022-04-1920x56ab7cce4626c0d31af10537d75143ca2016b54063677577f0dad5f8dc32ff2b
4626-fFEI-82022-04-1910x2985f3271ff49ffaf37931f35cf96a7283fa112f2027cec979b1c454f5b6aa86
vTHOR2022-04-2522890x545638df92ff8743e9eeb004385ff72ed4fdc76d8913b40a2274290e66245fb0

From this table, we can see that Convex Finance were the earliest adopters of ERC4626. They use it in some of their staking pools, specifically for staking Curve LP tokens. There are other tokens, like (re)TOKE (part of TOKEMAK) which only have 1 deposit over the course of their lifetime, so we're not going to mention those. If you're looking for that secret single deposit alpha, download the dataset above! Convex has currently deployed the most ERC4626 vaults.

Next up, we start to see some implementations from the original authors of ERC4626: Fei Protocol. Tokens like tsTribe and tsBAL are part of the Tribe Turbo deployments. They don't seem to be active, though.

WOUSD is part of a protocol called Origin Dollar, a yield bearing stablecoin. Finally we have vTHOR, which is part of Thorswap. On Ethereum, this is by far the most popular ERC4626 vault by number of deposits.

If we sort by the most active ERC4626 vaults, we get the following table:

SymbolNumber of Deposits
vTHOR2289
xMPL435
uCVX115
fei3crvCVX111
d3CVX58
imUSD47
cvxCRVCRV41
cvxFXSFXS38
USTwCVX29
steCRVCVX24
FRX3CRVCVX18
d3convex14
tricryptoCVX13
WOUSD10
rETHwstETH5
fei3crvConvex5
tsTRIBE5
wfFEI-84
∞-yvWETH-xPYT4
alusd3crvConvex4
imBTC3
tsgOHM3
ALCXETH2
4626-fFEI-81
alETHLp1
tALCX1
aave2-CLR-S1
tsBAL1
TTV1
Frax3Crv1
D31
BAUSDC-22061
(re)TOKE1

Aside from Thorswap and Convex, two other very popular ERC4626 deployments are xMPL from Maple Finance and uCVX, a partnership between Pirex and Llama Airforce which allows you to deposit pxCVX, a vault for CVX. Composability in action!

vTHOR currently manages about $12,7M USD of THOR deposits, and xMPL around $45M of MPL. So even though vTHOR has more deposits, xMPL manages a lot more assets.

Polygon

Polygon usage really only started after the deployment of wapGHST staking on May 4th. On May 26th, mStable upgraded their Save Contracts to implement ERC4626, and deposits stayed consistently high.

Zooming in on the most active vaults, mStable's imUSD is at the top, followed by wapGHST:

SymbolNumber of Deposits
imUSD2124
wapGHST1845
LOL20
eVault18
LOLt14
svUSDC10
swMATIC4
LOLT3
USD+3
swUSDc3
LOLB1
stUSD+1

imUSD currently manages around $6,5M mUSD, and wapGHST manages $1,7M worth of aPolGHST, which is the Aave token for Aavegotchi GHST.

We could not find any info on LOL, which is a vault for Aave Polygon USDC. eVault was interesting. Most of the Deposit events where from 0x2C882729f2710D0b8d23d98199ba9FdA1aF05109, which turned out to be a mocking contract with LINK as its underlying token. The real implementation was at 0xff07A39665740eB95E57ccDC3963B026741cC88E, which is a QiDAO vault for Quickswap WMATIC-QI LP tokens. The contract was only deployed 5 days ago at the time of writing (June 14, 2022).

Optimism

Optimism had 0 ERC4626 activity until May 24th, when Rubicon Finance upgraded their tokens to implement the standard. Aside from Rubicon (the bath tokens), there doesn't seem to be a lot of ERC4626 activity:

SymbolNumber of Deposits
bathUSDC400
bathOP246
bathETH175
bathDAI38
bathUSDT38
bathSNX30
nukenke5
bathWBTC4
dmoUSDC-Basis-Trading2
CSET1

Conclusion

ERC4626 is definitely picking up some steam, but there aren't many big protocols like Aave or Compound that upgraded their contracts yet, even though the whole DeFi ecosystem would clearly benefit from it. Kudos to all the protocols that did implement it!

If you want to analyze the data yourself, you can download it here: erc4626_deposits.csv. You can also try out Apollo at github.com/chainbound/apollo, make sure to read the documentation!

caution

Apollo is still alpha software, and there will be bugs. If you find any, please open an issue on Github.