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.

System Overview

Reserve Folio implements a sophisticated multi-layer architecture designed for secure, efficient portfolio management under governance constraints.
The architecture is specifically designed to enable high-fidelity asset management and rebalancing even when operating under timelock delays.

Contract Architecture

Layer 0: DAO Contracts

The foundational layer managing ecosystem-wide concerns:
Maintains a registry of approved FolioDeployer versions. Owned by the DAO, this contract ensures only vetted deployer contracts can create new Folios.Key Functions:
  • Track approved deployer versions
  • Enable/disable specific versions
  • Prevent deployment from deprecated versions
Handles ecosystem-wide fee configuration, including the universal 15 bps minimum floor.Key Features:
  • Set global minimum fee floor
  • Configure per-Folio fee overrides (can only lower)
  • Manage DAO fee recipients
  • Track fee distribution
External contract providing role-based access control. Must implement IRoleRegistry interface.

Layer 1: Folio Contracts

Core portfolio management contracts:
// contracts/Folio.sol (excerpt)
contract Folio is 
    IFolio,
    ERC20Upgradeable,
    AccessControlEnumerableUpgradeable,
    ReentrancyGuardUpgradeable,
    Versioned
{
    // Basket of ERC20 tokens
    EnumerableSet.AddressSet private basket;
    
    // Fee configuration
    FeeRecipient[] public feeRecipients;
    uint256 public tvlFee;  // D18{1/s}
    uint256 public mintFee; // D18{1}
    
    // Rebalancing state
    Rebalance private rebalance;
    Auction private auction;
    uint256 public maxAuctionLength; // {s}
    
    // ...
}

Folio.sol

The heart of the system. An ERC20 token backed by a flexible basket of assets with built-in auction logic for rebalancing.

FolioDeployer.sol

Factory contract for deploying new Folio instances with initial configuration and role assignments.

FolioProxy.sol

Upgradeable proxy enabling contract evolution while preserving storage. Checks upgrades against FolioVersionRegistry.

Layer 2: Governance System

// contracts/governance/FolioGovernor.sol (excerpt)
contract FolioGovernor is 
    Governor,
    GovernorSettings,
    GovernorCountingSimple,
    GovernorVotes,
    GovernorVotesQuorumFraction,
    GovernorTimelockControl
{
    // Time-based governance with timelock
}
1

StakingVault

ERC4626 vault where users stake Folio tokens to receive voting power. Supports:
  • Multi-token reward streams
  • Unstaking delays for security
  • ERC20Votes for governance participation
  • Optimistic governance with slashing (v5.0.0+)
2

FolioGovernor

Time-based governance system managing protocol parameters through proposals and voting.
3

GovernanceDeployer

Deploys complete governance systems including staking vaults and governors.

Layer 3: Staking and Rewards

// contracts/staking/StakingVault.sol (excerpt)
contract StakingVault is 
    ERC4626Upgradeable,
    ERC20VotesUpgradeable,
    OwnableUpgradeable
{
    struct RewardInfo {
        uint256 payoutLastPaid;    // {s}
        uint256 rewardIndex;       // D18+decimals{reward/share}
        uint256 balanceAccounted;  // {reward}
        uint256 totalClaimed;      // {reward}
    }
    
    mapping(address => RewardInfo) public rewardTrackers;
    
    // Multi-reward system with exponential decay
}
StakingVault implements a sophisticated multi-reward system where rewards decay exponentially based on a configurable half-life (1 day to 2 weeks).

Rebalancing Architecture

Rebalance Lifecycle

1

Initiation: startRebalance()

Called by: REBALANCE_MANAGERThe rebalance manager defines comprehensive ranges for the rebalancing operation:
struct TokenRebalanceParams {
    address token;
    WeightRange weight;     // D27{tok/BU} [low, spot, high]
    PriceRange price;       // D27{UoA/tok} [low, high]
    uint256 maxAuctionSize; // {tok}
    bool inRebalance;
}

struct RebalanceLimits {
    uint256 low;   // D18{BU/share} - buy up to
    uint256 spot;  // D18{BU/share} - point estimate
    uint256 high;  // D18{BU/share} - sell down to
}
Time periods created:
  • restrictedUntil: Only AUCTION_LAUNCHER can act (minimum 120s buffer)
  • availableUntil: Rebalance TTL, after which no new auctions can start
2

Restricted Period: openAuction()

Called by: AUCTION_LAUNCHERDuring the restricted period, the auction launcher opens auctions with optional parameter adjustments:
  • Token selection: Subset of tokens in rebalance
  • Basket limits: Progressive narrowing (monotonic convergence)
  • Weights: Progressive narrowing if weightControl == true
  • Prices: Subset of ranges if priceControl != NONE
The restricted period auto-extends when the auction launcher is active, ensuring they always have time to act.
3

Unrestricted Period: openAuctionUnrestricted()

Called by: AnyoneAfter the restricted period expires (or if AUCTION_LAUNCHER is inactive), anyone can open auctions using spot estimates:
  • All tokens in rebalance are included
  • Uses spot prices and weights
  • No parameter customization allowed
This ensures the system remains functional even without the AUCTION_LAUNCHER.
4

Trading: bid() or createTrustedFill()

Called by: Anyone (for bid) or Trusted FillersParticipants execute trades at current auction prices. Trades are validated against:
  • Current auction price curve
  • Available sell amounts
  • Required buy amounts
  • Maximum auction sizes
5

Completion: closeAuction() or endRebalance()

Called by: AUCTION_LAUNCHER, REBALANCE_MANAGER, or DEFAULT_ADMIN_ROLEAuctions close automatically after their duration. Rebalances can be ended early by authorized roles.

Auction Mechanics

Price Curve: Exponential Decay

Auctions use exponential decay between optimistic and pessimistic price bounds:
Price(t) = startPrice * (endPrice/startPrice)^(t/duration)

Where:
- startPrice: Most optimistic exchange rate
- endPrice: Most pessimistic exchange rate  
- t: Time elapsed since auction start
- duration: Total auction length
Important: Prices on the first and last blocks may not exactly match startPrice and endPrice unless transactions occur at precise start and end timestamps.

Auction Warmup Period

Auctions include a 30-second warmup before bidding begins:
uint256 constant AUCTION_WARMUP = 30; // {s}
The warmup ensures fair competition from the first tradeable block. It is bypassed only when priceControl == ATOMIC_SWAP and start price equals end price.

Lot Sizing Algorithm

Auction sizes are calculated based on surpluses and deficits:
// For surplus tokens (selling):
surplus = balance - (high_limit * high_weight * totalShares)

// For deficit tokens (buying):
deficit = (low_limit * low_weight * totalShares) - balance

// Sell amount is the minimum that satisfies both constraints
sellAmount = min(
    surplus_of_sell_token,
    deficit_of_buy_token * price
)
Key insights:
  1. Surplus grows over time: If selling token surplus is limiting, sellAmount increases with each auction as high_limit decreases
  2. Deficit shrinks over time: If buying token deficit is limiting, sellAmount decreases as low_limit increases
The AUCTION_LAUNCHER progressively narrows the [low, high] ranges to implement Dollar Cost Averaging (DCA) into the target allocation.

Pairwise Auction System

Auctions run simultaneously on all possible token pairs in the auction:
For tokens [A, B, C] in an auction:
- A→B, A→C (if A is surplus)
- B→A, B→C (if B is surplus)  
- C→A, C→B (if C is surplus)
Eligibility requirements:
  • Sell token: Must be in surplus (balance > high_limit * high_weight * shares)
  • Buy token: Must be in deficit (balance < low_limit * low_weight * shares)

Rebalance Targeting

Rebalances are considered “complete” when all ranges have converged:
// Complete rebalance conditions:
rebalanceLimits.low == rebalanceLimits.spot == rebalanceLimits.high

for each token:
    weight.low == weight.spot == weight.high
    // (prices don't need to converge)

Price Control Levels

The priceControl setting determines auction launcher authority:
Security: Highest
Flexibility: Lowest
The AUCTION_LAUNCHER cannot adjust prices. All auctions use the full price ranges specified by REBALANCE_MANAGER.Use case: Maximum security when AUCTION_LAUNCHER trust is limited.
Best Practice for ATOMIC_SWAP: The AUCTION_LAUNCHER should:
  1. Open auction with fixed price
  2. Fill auction atomically in same transaction
  3. End rebalance immediately after
All three operations should be bundled for security.

Weight Control

When weightControl == true, the AUCTION_LAUNCHER can adjust individual token weights:
struct WeightRange {
    uint256 low;   // D27{tok/BU} - buy up to this weight
    uint256 spot;  // D27{tok/BU} - point estimate
    uint256 high;  // D27{tok/BU} - sell down to this weight
}
Use cases:
  • Percentage-based portfolios: Maintain specific asset percentages throughout rebalancing
  • Dynamic rebalancing: Adjust targets as market conditions change
  • Progressive convergence: Narrow weight ranges auction-by-auction for precise DCA
Without weight control:
  • Only RebalanceLimits (basket units per share) define rebalancing targets
  • Best for portfolios with fixed quarterly/monthly targets

Trusted Fillers Integration

Folios can integrate with the Trusted Fillers system for async order matching:
struct FolioFlags {
    bool trustedFillerEnabled;
    RebalanceControl rebalanceControl;
    bool bidsEnabled;
}

Supported Fillers

Currently supports CoW Swap for better price discovery and MEV protection through batch auctions.

Configuration

Enabled per-Folio by governance. When enabled, trusted fillers can compete alongside regular bidders.
Trusted fillers must respect all auction limitations including price curves, lot sizes, and timing constraints.

Disabling Permissionless Bids

In version 5.0.0+, governance can restrict trading to trusted fillers only:
// Disable permissionless bids (v5.0.0+)
folio.setBidsEnabled(false);
This forces all auction fills through trusted filler protocols, potentially improving execution quality and MEV protection.

Fee Distribution Architecture

Dual-Layer Fee System

User pays fee → Split between DAO and Folio recipients
              |
              ├─→ DAO: minimum 15 bps (configurable)
              └─→ Folio recipients: remaining portion

TVL Fee Mechanics

// Applied once every 24 hours
uint256 constant ONE_DAY = 86400; // {s}

// Supply inflation calculation
if (block.timestamp >= lastPoke + ONE_DAY) {
    uint256 periods = (block.timestamp - lastPoke) / ONE_DAY;
    uint256 inflation = totalSupply * tvlFee * periods;
    
    // Split between DAO and fee recipients
    uint256 daoShares = inflation * daoFeeFraction;
    uint256 folioShares = inflation - daoShares;
}
Changing from per-block to daily inflation in v4.0.0 reduced gas costs without changing the economic model.

Mint Fee Mechanics

// Applied during mint()
uint256 sharesBeforeFee = calculateShares(assets);
uint256 feeAmount = sharesBeforeFee * mintFee / D18;
uint256 sharesAfterFee = sharesBeforeFee - feeAmount;

// Fee distributed to DAO and recipients (no inflation)

Security Considerations

Reentrancy Protection

All state-changing functions use nonReentrant modifier:
modifier nonReentrant() {
    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
    _status = _ENTERED;
    _;
    _status = _NOT_ENTERED;
}
Read-Only Reentrancy: While the Folio itself is protected, consuming protocols must check stateChangeActive() before using view functions:
(bool isRebalancing, bool isAuction) = folio.stateChangeActive();
require(!isRebalancing && !isAuction, "State change active");

Upgrade Safety

Upgrades are checked against the version registry:
// contracts/folio/FolioProxy.sol
function _authorizeUpgrade(address newImplementation) internal override {
    require(
        IFolioVersionRegistry(versionRegistry).isVersionValid(newImplementation),
        "Invalid version"
    );
}

Token Safety Boundaries

ParameterMaximum ValueDecimalsType
Folio Supply1e36-
Folio Collateral Decimals-27-
StakingVault Underlying/Rewards Decimals-21-
Rebalance Limits1e3618 (D18)
Basket Weights1e5427 (D27)
Token Prices (UoA)1e4527 (D27)
Price Range Ratio1e2-ratio
Governance Responsibility: It is governance’s duty to ensure Folio supply never exceeds 1e36. Consider implementing supply caps or monitoring systems.

State Management

Rebalance State Machine

NO_REBALANCE
    ↓ startRebalance()
RESTRICTED_PERIOD (only AUCTION_LAUNCHER can act)
    ↓ time passes OR AUCTION_LAUNCHER inactive
UNRESTRICTED_PERIOD (anyone can act)
    ↓ TTL expires OR endRebalance() called
NO_REBALANCE

Auction State Machine

UNINITIALIZED (startTime == 0, endTime == 0)
    ↓ openAuction() / openAuctionUnrestricted()
PENDING (block.timestamp < startTime)
    ↓ time passes
OPEN (startTime ≤ block.timestamp ≤ endTime)
    ↓ time passes OR closeAuction()
CLOSED (block.timestamp > endTime)
    ↓ openAuction() [if rebalance still active]
OPEN ...

Utility Libraries

Core rebalancing calculations:
  • Lot size calculations
  • Surplus/deficit determination
  • Price curve interpolation
  • Range validation and narrowing logic
Folio-specific utilities:
  • Basket value calculations
  • Share price computations
  • Fee calculations
  • Asset amount conversions
Mathematical operations:
  • Safe arithmetic
  • Fixed-point math (D18, D27)
  • Exponential decay calculations
System-wide constants:
uint256 constant D18 = 1e18;
uint256 constant D27 = 1e27;
uint256 constant AUCTION_WARMUP = 30; // {s}
uint256 constant MIN_AUCTION_LENGTH = 60; // {s}
uint256 constant MAX_AUCTION_LENGTH = 7 days;
uint256 constant RESTRICTED_AUCTION_BUFFER = 120; // {s}
uint256 constant ONE_DAY = 86400; // {s}

Deprecation Mechanism

Folios can be deprecated by the DEFAULT_ADMIN_ROLE:
function deprecateFolio() external onlyRole(DEFAULT_ADMIN_ROLE) {
    isDeprecated = true;
    emit FolioDeprecated();
}
Effects:
  • Minting disabled
  • Rebalancing disabled
  • Redemption still enabled (redemption-only mode)
Use deprecation when a Folio needs to wind down gracefully, allowing holders to exit but preventing new capital inflows.

Performance Optimizations

Gas Optimizations (v5.0.0)

  1. Daily fee inflation: Reduced from per-block to once-per-day calculations
  2. EnumerableSet usage: Efficient basket token tracking
  3. Calldata over memory: Where possible for external functions
  4. Packed structs: Optimized storage layout

Scalability Considerations

  • Max auction length: 7 days (configurable down to 60 seconds)
  • Basket size: No hard limit, but gas costs scale linearly
  • Fee recipients: Limited to prevent gas issues during distribution
  • Concurrent auctions: 1 active auction at a time per Folio

Peripheral Contracts

FolioLens.sol

View-only helper contract for batch queries:
interface IFolioLens {
    function getBasketValues(address folio) external view returns (...);
    function getRebalanceStatus(address folio) external view returns (...);
    function getAuctionDetails(address folio, uint256 auctionId) external view returns (...);
}
Use this for frontend integrations to minimize RPC calls.

Next Steps

Quick Start

Deploy your first Folio with practical examples

API Reference

Complete function reference with parameters and return values

Build docs developers (and LLMs) love