Skip to main content

Overview

Concrete Vaults are ERC-4626-compliant smart contracts that wrap underlying ERC20 assets (for example WBTC, USDC) into vault shares (ctAssets).

Each vault behaves as a yield wrapper: deposits are deployed into yield strategies such as money markets, liquidity provision, or restaking.

From an integration perspective, a user's deposit always results in ERC20 shares (ctAssets) that represent their claim on the vault's underlying and accrued yield.

Usage

Whether you are using vanilla JS/TS, React hooks, or Wagmi integration, you interact with the same underlying vault abstraction.

The Concrete SDK provides:

  • Query vault details: decimals, symbols, total assets, balances.
  • Approvals: approve the vault to spend the underlying ERC20.
  • Deposits and redemptions: mint and redeem shares.
  • Preview conversions: estimate output before sending a transaction.
  • Multi-chain support: Ethereum, Arbitrum, Berachain, Katana, Corn, Morph.
User actionSDK methodResult
Get vault detailsvault.getVaultDetails()Metadata: symbols, decimals, underlying address
Check underlying balance(await vault.getUnderlyingErc20()).balanceOf(address)User's underlying token balance
Approve deposit(await vault.getUnderlyingErc20()).approve(vaultAddr, amount)Allow vault to spend the underlying
Preview conversionvault.previewConversion(amount)Estimate shares minted or tokens redeemed
Depositvault.deposit(amount)Underlying consumed, shares minted
Check share balancevault.balanceOf(address)User's ctAsset balance
Redeem sharesvault.redeem(amount)Shares burned, underlying returned

Vault = ERC20 + underlying ERC20

Every vault exposes two ERC20 layers while staying ERC-4626 compliant:

  1. Vault shares (ctAssets)
    • The vault itself extends ERC20.
    • You can call balanceOf, approve, transfer, and other ERC20 methods on the shares.
  2. Underlying ERC20
    • Exposed via vault.getUnderlyingErc20().
    • Full ERC20 instance: approve, balanceOf, transfer.

Example flow

A user approves the underlying ERC20, the vault consumes the underlying and mints ctAssets (shares), and the user can redeem the shares later.

Before writing code, you can build a mental model of the flow by trying it directly in the Concrete app:

  • Earn page: deposit into a live vault and see shares minted.
  • Earn docs: walkthrough of how vault deposits work at the user level.

The SDK expresses the same steps in code:

  1. Check underlying balance
const erc20 = await vault.getUnderlyingErc20();
const balance = await erc20.balanceOf(user);
console.log("Underlying balance:", balance.toString());
  1. Approve vault to spend underlying
const erc20 = await vault.getUnderlyingErc20();
await (await erc20.approve(vault.getAddress(), amount)).wait();
  1. Optionally preview deposit
const decimals = await vault.getUnderlyingDecimals();
const preview = await vault.previewConversion(1n * 10n ** BigInt(decimals));
console.log("Vault tokens (preview):", preview.vaultTokensReceiving);
  1. Deposit into vault
const tx = await vault.deposit(amount);
await tx.wait();
  1. Receive ctAssets (vault shares)
const shareBal = await vault.balanceOf(user);
console.log("Vault shares:", shareBal.toString());
  1. Redeem back into underlying
const redeemTx = await vault.redeem(shareBal);
await redeemTx.wait();