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 use a semi-permissioned rebalancing mechanism to change their basket composition. The process is designed to work effectively even under timelock delays, with multiple roles coordinating to achieve optimal execution.

Rebalance Lifecycle

Every rebalance follows a structured lifecycle:

1. Start Rebalance

The REBALANCE_MANAGER initiates a rebalance with target parameters:
/// @param tokens The rebalance parameters for each token
/// @param limits D18{BU/share} Target basket limits
/// @param auctionLauncherWindow {s} Time for AUCTION_LAUNCHER to act
/// @param ttl {s} Total time the rebalance is valid
function startRebalance(
    TokenRebalanceParams[] calldata tokens,
    RebalanceLimits calldata limits,
    uint256 auctionLauncherWindow,
    uint256 ttl
) external onlyRole(REBALANCE_MANAGER)

2. Auction Launcher Window (Restricted Period)

After starting, there’s a restricted period where only the AUCTION_LAUNCHER can open auctions. This ensures they have first opportunity to provide pricing precision.
The restricted period automatically extends if the AUCTION_LAUNCHER is actively using it, with a minimum 120s buffer before unrestricted access.

3. Unrestricted Period

After the restricted period expires, anyone can open auctions using spot values and initial prices. This prevents single-point-of-failure on the AUCTION_LAUNCHER.

4. Time-to-Live (TTL)

Rebalances have a TTL (max 4 weeks) that controls how long they can run. Multiple auctions can occur during this time.
An auction can start at ttl - 1 and run beyond the rebalance’s TTL.

Rebalance Targets

The REBALANCE_MANAGER configures target ranges that define the rebalancing path.

Rebalance Limits

Define how many Basket Units the Folio should target:
struct RebalanceLimits {
    uint256 low;  // D18{BU/share} (0, 1e27] to buy assets up to
    uint256 spot; // D18{BU/share} (0, 1e27] point estimate for unrestricted
    uint256 high; // D18{BU/share} (0, 1e27] to sell assets down to
}
  • low - Target for buying (minimum BU per share)
  • spot - Point estimate used by unrestricted callers
  • high - Target for selling (maximum BU per share)

Basket Weights

For each token, weights define the target composition:
struct WeightRange {
    uint256 low;  // D27{tok/BU} [0, 1e54] lowest possible weight
    uint256 spot; // D27{tok/BU} [0, 1e54] point estimate for unrestricted
    uint256 high; // D27{tok/BU} [0, 1e54] highest possible weight
}
If RebalanceControl.weightControl is enabled, the AUCTION_LAUNCHER can progressively narrow weight ranges to maintain target allocations.

Price Ranges

For each token, governance provides conservative price estimates:
struct PriceRange {
    uint256 low;  // D27{UoA/tok} (0, 1e45]
    uint256 high; // D27{UoA/tok} (0, 1e45]
}
Prices should be set so the asset’s price on secondary markets will likely remain within range even after timelock delays. Maximum allowable range is 100x.

Weight Control

When RebalanceControl.weightControl is enabled, the AUCTION_LAUNCHER can adjust basket weights within the governance-approved range.

Use Cases

Weight Control Enabled:
  • Folios targeting specific % breakdown at all times
  • Dynamic allocation adjustments during rebalancing
  • Responsive to market conditions
Weight Control Disabled:
  • Folios with single monthly/quarterly targets
  • Pure rebalance limit-based strategies
  • More governance control

Progressive Narrowing

The AUCTION_LAUNCHER can progressively narrow ranges but cannot backtrack:
// Initial range set by governance
WeightRange memory initial = WeightRange({
    low: 0.3e27,   // 30%
    spot: 0.5e27,  // 50%
    high: 0.7e27   // 70%
});

// AUCTION_LAUNCHER can narrow to:
WeightRange memory narrowed = WeightRange({
    low: 0.4e27,   // 40% (increased from 30%)
    spot: 0.5e27,  // 50% (same)
    high: 0.6e27   // 60% (decreased from 70%)
});

Rebalance Completion

A rebalance is considered “completed” when all range deltas reach zero:
// Completed state
limits.low == limits.spot == limits.high
weights.low == weights.spot == weights.high
The AUCTION_LAUNCHER should end the rebalance when completion is achieved or when continuing would leak value.

Starting a Rebalance

// Define tokens in the rebalance
TokenRebalanceParams[] memory tokens = new TokenRebalanceParams[](2);

tokens[0] = TokenRebalanceParams({
    token: address(token1),
    weight: WeightRange({
        low: 0.4e27,   // 40% minimum
        spot: 0.5e27,  // 50% target
        high: 0.6e27   // 60% maximum
    }),
    price: PriceRange({
        low: 0.95e27,  // $0.95
        high: 1.05e27  // $1.05
    }),
    maxAuctionSize: 1000000e18, // 1M tokens per auction
    inRebalance: true
});

tokens[1] = TokenRebalanceParams({
    token: address(token2),
    weight: WeightRange({
        low: 0.4e27,
        spot: 0.5e27,
        high: 0.6e27
    }),
    price: PriceRange({
        low: 1.9e27,   // $1.90
        high: 2.1e27   // $2.10
    }),
    maxAuctionSize: 500000e18, // 500K tokens per auction
    inRebalance: true
});

// Define BU limits
RebalanceLimits memory limits = RebalanceLimits({
    low: 0.95e18,   // Buy up to 0.95 BU per share
    spot: 1.0e18,   // 1 BU per share target
    high: 1.05e18   // Sell down to 1.05 BU per share
});

// Start rebalance
folio.startRebalance(
    tokens,
    limits,
    3600,    // 1 hour auction launcher window
    604800   // 1 week TTL
);

Querying Active Rebalance

Check if a rebalance is ongoing and view its parameters:
function getRebalance() external view returns (
    uint256 nonce,
    PriceControl priceControl,
    TokenRebalanceParams[] memory tokens,
    RebalanceLimits memory limits,
    RebalanceTimestamps memory timestamps,
    bool bidsEnabled_
)
(
    uint256 nonce,
    PriceControl priceControl,
    TokenRebalanceParams[] memory tokens,
    RebalanceLimits memory limits,
    RebalanceTimestamps memory timestamps,
    bool bidsEnabled
) = folio.getRebalance();

// Check if rebalance is active
if (block.timestamp < timestamps.availableUntil) {
    // Rebalance is ongoing
    bool isRestricted = block.timestamp < timestamps.restrictedUntil;
}

Ending a Rebalance

The REBALANCE_MANAGER, AUCTION_LAUNCHER, or DEFAULT_ADMIN_ROLE can end a rebalance early:
/// End the current rebalance WITHOUT impacting any ongoing auction
function endRebalance() external
End the rebalance if prices move outside approved ranges to prevent value leakage.

Best Practices

Set price ranges wide enough to account for:
  • Timelock delay execution
  • Expected volatility
  • Block-to-block price movement
  • Slippage on secondary markets
Avoid ranges that are too wide (approaching 100x) as this increases MEV risk.
Choose TTL based on:
  • Expected number of auctions needed
  • Individual auction lengths
  • Buffer for unexpected delays
  • Governance response time
Typical range: 1-4 weeks
Set the window to:
  • Give sufficient time for AUCTION_LAUNCHER to act
  • Account for network conditions
  • Balance between control and permissionlessness
Typical range: 1-24 hours

Build docs developers (and LLMs) love