Pioneering Solana’s EIP-4626 Vault Standard
With the explosion of Solana DeFi, newer protocols are increasingly composing with existing ones. Developers of these newer protocols are often writing the same code, which not only costs them time but also increases the likelihood of critical vulnerabilities. To ensure Solana DeFi’s safety and rapid development, we must have an open-source vault standard as we’ve recently seen with Ethereum’s EIP-4626 vault standard.
At Accrue Finance, we believe we’ve built the foundations for Solana’s vault standard. Accrue’s smart contract sits on top of three lending-borrowing protocols, moving users’ tokens around to whichever protocol is offering the highest yield — but there’s much more to it:
- Dozens of protocols. Accrue’s smart contract design allows for integrations with dozens of protocols, sidestepping Solana’s transaction limits without compromising decentralization. This will allow users to access the benefits of the myriad protocols coming to Solana in the future, like Delta One.
- Partial distributions. The smart contract isn’t just restricted to putting 100% of users’ tokens in the highest-earning protocol. It can partially distribute users’ funds across several protocols. This allows for future strategies like protocol risk diversification.
- Rapid movement. Because it’s built on Solana, the smart contract can move funds around really quickly and cheaply. (For context, it costs $50–150 to move funds around on Ethereum). This is feature is especially important for protocols like Tulip and Francium, as their lending APYs jump 300% within one hour and drop back down in the next.
- Fully tested. We’ve written over 5,000 lines of Anchor tests and have fully tested the contract on mainnet.
Design
Building this on Ethereum would have been much simpler. Solana’s transaction size limitation and separation of state from logic make it complicated to develop programs that heavily depend on composability. Here’s one example:
In Solana, you need to pass in the public key of every piece of data you want to read and write (e.g. public key of the “BTC” mint, oracle, etc). Each key is 32 bytes, but you only have 1232 bytes of transaction space to fit them in. You can fit roughly 25 keys into one transaction. Each protocol you integrate with takes around 8 keys, so you can only integrate with 3 protocols.
To put it simply, we found a way to modularize our protocol’s logic so that each underlying protocol is separated into multiple instructions. Then, we used an on-chain cache to store our protocol and vault states. The cache is invalid if the protocols’ logic doesn’t happen in the same transaction as the vault logic.
We then realized we could create a “plug-in, plug-out” system that swaps out protocol_a for protocol_d. This means that our program can only handle three protocols at a time (i.e. have our balance in only three protocols), but it can integrate with dozens.
To see a full instruction-by-instruction breakdown of our smart contract, you can check out our open-source GitHub repo.
Improvements
- Moving funds from Protocol A to Protocol B will floor the number of tokens in the vault (e.g. if you have 1.000002 BTC, you will be left with 1.000001 BTC). This is because underlying protocols use the floor function. This is a problem for high-priced coins with low decimal places like BTC because it may not accrue interest fast enough. If you move BTC around too many times, you’ll have less BTC than you started, and your cToken exchange rate will drop below zero. This problem is unavoidable, so you just have to be careful about how frequently you rebalance.
- As part of the implementation of the fees feature, we added an “estimated_balance” field. This may get slightly off at times because it’s impossible to know the exact balance after withdrawing from an underlying protocol without refreshing its reserve. We chose not to refresh the reserve because of Solana’s 200K compute limit, but that constraint is now gone.
- Every time you want to add a new coin (e.g. BTC), you need to hardcode its oracles/other info into the smart contract. We did this so that the money manager can only decide which protocols to add. Michaelhly from Saber suggested a fully modular approach where the integrations are a separate smart contract from the vault itself. This has many benefits, including the potential to add new integrations without modifying the vault smart contract itself.
Code
We decided to open-source our code. We cut out our integrations with Francium and Tulip to respect that they are closed-source. We’ve written over 5,000 lines of Anchor tests and have fully tested the contract on mainnet.
Special shoutout to Alan from Anchor. Without his guidance in the Anchor Discord, Mars and I could not have built all of Accrue in 3 months (while Mars was working full-time in web2!)
Sincerely,
Apollo