Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/reserve-protocol/reserve-index-dtf/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Folios are backed ERC20 tokens that represent portfolios of ERC20-compliant assets managed entirely onchain. They serve as a single source of truth for asset allocations, enabling composability of complex, multi-asset portfolios.
Folios are designed to be used in DeFi protocols as composable building blocks for multi-asset strategies.

What is a Folio?

A Folio is:
  • An ERC20 token - Fully composable with other DeFi protocols
  • Backed by a basket - Represents ownership of multiple underlying assets
  • Permissionlessly mintable/redeemable - Anyone can mint or redeem at any time
  • Semi-permissioned rebalancing - Governance controls rebalancing within timelock constraints

Basket Composition

Every Folio maintains a basket of ERC20 tokens. The basket defines:
  1. Which tokens are included in the portfolio
  2. How many of each token back each Folio share
  3. Target allocations during rebalancing

Basket Units (BU)

Basket Units are the fundamental unit of account for a Folio. They define the target composition:
/// Target limits for rebalancing
struct RebalanceLimits {
  uint256 low;  // D18{BU/share} (0, 1e27] to buy assets up to
  uint256 spot; // D18{BU/share} (0, 1e27] point estimate
  uint256 high; // D18{BU/share} (0, 1e27] to sell assets down to
}

Basket Weights

Each token in the basket has an associated weight that determines its proportion:
/// Range of basket weights for BU definition
struct WeightRange {
  uint256 low;  // D27{tok/BU} [0, 1e54] lowest possible weight
  uint256 spot; // D27{tok/BU} [0, 1e54] point estimate
  uint256 high; // D27{tok/BU} [0, 1e54] highest possible weight
}
A typical usage defines BUs 1:1 with shares (1e18), though they can range from 0 to 1e27.

Minting Folios

Minting creates new Folio shares by depositing the required basket tokens.

How Minting Works

  1. Calculate required assets - Based on the current basket composition
  2. Transfer assets - Caller transfers all required tokens to the Folio
  3. Mint shares - Folio issues shares proportional to the deposit
  4. Apply fees - Mint fee is deducted from shares issued

Mint Function

/// @param shares {share} Amount of shares to mint
/// @param receiver Address to receive the minted shares
/// @param minSharesOut {share} Minimum shares after fees (slippage protection)
function mint(
    uint256 shares,
    address receiver,
    uint256 minSharesOut
) external returns (address[] memory _assets, uint256[] memory _amounts)

Fee Distribution

Minting has three share portions:
  1. Receiver shares - Goes to the minter
  2. DAO fee shares - Goes to the protocol DAO (minimum 15 bps)
  3. Fee recipient shares - Goes to Folio fee recipients
// Approve tokens first
token1.approve(address(folio), amount1);
token2.approve(address(folio), amount2);

// Mint with 1% slippage tolerance
folio.mint(
    1000e18,           // shares to mint
    msg.sender,        // receiver
    990e18             // min shares out (1% slippage)
);

Redeeming Folios

Redemption burns Folio shares and returns the proportional basket assets.

How Redemption Works

  1. Burn shares - Caller’s shares are destroyed
  2. Calculate proportional assets - Based on share percentage of total supply
  3. Transfer assets - All basket tokens transferred to receiver

Redeem Function

/// @param shares {share} Amount of shares to redeem
/// @param receiver Address to receive the assets
/// @param assets Assets to receive (must match basket exactly)
/// @param minAmountsOut {tok} Minimum amounts of each asset (slippage protection)
function redeem(
    uint256 shares,
    address receiver,
    address[] calldata assets,
    uint256[] calldata minAmountsOut
) external returns (uint256[] memory _amounts)
The assets array must match the current basket exactly, in the same order.
// Get current basket
(address[] memory assets, ) = folio.totalAssets();

// Set minimum amounts (slippage tolerance)
uint256[] memory minAmounts = new uint256[](assets.length);
minAmounts[0] = 95e18;  // minimum token1
minAmounts[1] = 190e18; // minimum token2

// Redeem shares
folio.redeem(
    500e18,      // shares to redeem
    msg.sender,  // receiver
    assets,      // current basket
    minAmounts   // min amounts out
);

Viewing Basket Assets

You can query the current basket composition at any time:
/// Get total assets in the Folio
/// @return _assets Array of token addresses
/// @return _amounts {tok} Array of token balances
function totalAssets() external view 
    returns (address[] memory _assets, uint256[] memory _amounts)

/// Convert shares to underlying assets
/// @param shares {share} Amount of shares
/// @param rounding Math.Rounding.Floor or Math.Rounding.Ceil
function toAssets(
    uint256 shares,
    Math.Rounding rounding
) external view 
    returns (address[] memory _assets, uint256[] memory _amounts)

State Change Safety

During trusted fill execution or reentrant calls, basket data may be unreliable.
Before relying on Folio state in external protocols, check:
/// @return syncStateChangeActive True if reentrant
/// @return asyncStateChangeActive True if async swap active
function stateChangeActive() external view 
    returns (bool syncStateChangeActive, bool asyncStateChangeActive)
Consumer protocols SHOULD call this and ensure both values are false before strongly relying on Folio state.

Weird ERC20 Support

Folios support most ERC20 tokens but have restrictions:
Token TypeSupported
Multiple Entrypoints
Pausable / Blocklist
Fee-on-transfer
ERC777 / Callback
Upward-rebasing
Downward-rebasing
Revert on zero-value transfers
Flash mint
Missing return values
No revert on failure
Rebasing tokens are supported but may cause accounting discrepancies if rebasing is non-incremental.

Build docs developers (and LLMs) love