Blend Auto Compounding Vault

Blend Vault

Blend is a over-collateralized lending protocol on Stellar. In this tutorial I’m going to show you how I built a auto-compounding vault on top of the Blend smart contracts.

James On YouTube

All the code is open-source and available on Github: https://github.com/jamesbachini/Blend-Vault

There is a live demo here (on mainnet but unaudited and not suitable for user funds): https://jamesbachini.github.io/Blend-Vault/

The vault smart contract wraps USDC into an ERC-4626 style vault and pipes deposits straight into Blend Protocol’s Yield Box v2 Pool on Stellar.

Depositors receive bVLT share tokens that track their claim on the pool while Blend yields accrue underneath. The contract also harvests BLND rewards, swaps them for USDC through Comet, and rolls them back into the pool compounding the incentive tokens.

Setup Instructions

  • Clone the repo and keep the bundled test artifacts:
    git clone https://github.com/jamesbachini/Blend-Vault.git
  • Run the contract unit tests:
    We are using nextest here as some of them are prone to timeout, change the target if you are using anything other than linux.
    cargo nextest run --target x86_64-unknown-linux-gnu --no-fail-fast
  • Build the contract Wasm for deployment:
    cargo test; cargo build --release --target wasm32-unknown-unknown
  • Deploy to mainnet (or a test network) with the helper script:
    ./deploy.sh
  • Front-end build (writes static assets to docs/ for GitHub Pages):
    cd frontend; npm install; npx vite build

Code Walkthrough

Initialization: Wiring The Vault Together

The initialize function is the contracts one-shot boot sequence. It locks in all external dependencies (Blend pool, Comet pool, reserve indices, BLND token), configures metadata and marks the vault as live.

This is not run as a constructor argument as I found in this case it made testing easier to be able to deploy the contract without any input variables.

Once this runs, the vault knows what asset it holds, which Blend pool it interacts with etc. Trying to run it twice gets you a polite shove into the error pit.

The integration with OpenZeppelin was originally based on this code by Sentinel DeFi: https://medium.com/@sentineldefi/implementing-a-tokenized-vault-on-stellar-soroban-33a7cda08649

It’s a boilerplate for building out vault contracts where deposits are represented by a SEP41 token which acts as a receipt.


Deposits: Pulling USDC And Supplying To Blend

Deposits use SEP-41 flows under the hood. The operator authenticates, the vault pulls USDC from the user, approves Blend, supplies collateral, and mints shares.

operator.require_auth();
if assets == 0 { return 0; }

let token = token::TokenClient::new(e, &Vault::query_asset(e));
token.transfer_from(&vault, &from, &vault, &assets);

let requests = vec![Request {
    request_type: REQUEST_TYPE_SUPPLY_COLLATERAL,
    address: asset.clone(),
    amount: assets,
}];

BlendPoolClient::new(e, &pool).submit_with_allowance(
    &vault, &vault, &vault, &requests
);

The important idea here is that the vault pre-authorises nested calls so Blend can use its allowance without tripping Soroban’s strict invocation rules. Shares are minted proportionally and the receiver gets added to a lightweight depositors list for future snapshots.


Total Assets: Ask Blend, Not Your Balance

The vault doesn’t track yield locally. Instead it queries Blend to fetch its live collateral position and multiplies by Blend’s current b-rate:

let positions = pool.get_positions(&vault);
let b_tokens = positions.collateral.get(usdc_index).unwrap_or(0);
if b_tokens == 0 { return 0; }

let reserve = pool.get_reserve(&asset);
(b_tokens * reserve.data.b_rate) / BLEND_RATE_SCALAR

This means the vault’s asset value always reflects real accrued interest, not just whatever sits in its token balance.


Compounding BLND Rewards

Incentive rewards are paid out in BLND in addition to the USDC interest paymetns. The vault claims them, swaps through Comet DeX for USDC, and folds the proceeds back into Blend as fresh collateral.

Comet is the DeX used for the backstop so it makes sense to use this for the swap as it almost certainly has the most liquidity.

let blnd_claimed = pool.claim(&vault, &vec![blnd_index], &vault);
if blnd_claimed <= 0 { return 0; }

blnd.approve(&vault, &comet, &blnd_claimed, &expiry);

let (usdc_received, _) = comet.swap_exact_amount_in(
    &blnd_token, &blnd_claimed, &usdc_token, &0, &i128::MAX, &vault
);

Two details matter:

  • The vault pre-authorises Comet’s nested token calls, or mainnet will swat the transaction.
  • The swap uses a zero min amount guard for simplicity. This can be hardened if slippage is a concern but in this specific case its not required. It’s not USDC funds at risk, just the BLND incentive rewards.

Once swapped, it pushes the USDC back into Blend just like a deposit.


This code illustrates how to use the new Openzeppelin vaults library and how to interact with Blend and comet all in one elegant smart contract and a simple dApp frontend.

Note that this was tested in production and the demo is on mainnet but it has not been audited and I wouldn’t use it for significant amounts of funds greater than <$0.01 test amounts. Also always use a separate wallet for testing.

All the code is open-source and available on Github: https://github.com/jamesbachini/Blend-Vault


Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.


Posted

in

, , , , ,

by