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.

Prerequisites

Before you begin, ensure you have the following:

Development Tools

  • Foundry (for Solidity development)
  • Node.js v20+
  • Yarn package manager

Knowledge Requirements

  • Understanding of ERC20 tokens
  • Basic Solidity knowledge
  • Familiarity with Dutch auctions

Installation

1

Clone the Repository

git clone https://github.com/reserve-protocol/reserve-index-dtf
cd reserve-index-dtf
2

Install Dependencies

yarn install
3

Build the Project

yarn compile
4

Run Tests

Verify everything is working correctly:
# Run basic tests
yarn test

# Run extreme edge case tests
yarn test:extreme

# Run all tests
yarn test:all

# Generate coverage report
forge coverage

Deploying Your First Folio

Step 1: Configure Folio Parameters

Define your Folio’s initial configuration:
IFolio.FolioBasicDetails memory basicDetails = IFolio.FolioBasicDetails({
    name: "My Portfolio",
    symbol: "MYPORT",
    assets: [address(tokenA), address(tokenB), address(tokenC)],
    amounts: [1000e18, 2000e6, 500e18], // Initial amounts
    initialShares: 1000e18 // Initial supply
});
Ensure token decimals are handled correctly. The amounts array should reflect actual token quantum (e.g., USDC with 6 decimals uses 1e6 for 1 USDC).

Step 2: Set Fee Configuration

IFolio.FeeRecipient[] memory recipients = new IFolio.FeeRecipient[](1);
recipients[0] = IFolio.FeeRecipient({
    recipient: feeRecipientAddress,
    portion: 1e18 // 100% of non-DAO fees (D18 format)
});

IFolio.FolioAdditionalDetails memory additionalDetails = IFolio.FolioAdditionalDetails({
    maxAuctionLength: 1 hours,
    feeRecipients: recipients,
    tvlFee: 100e18 / 365 days, // 100 bps annually (D18{1/s})
    mintFee: 50e15, // 50 bps (D18{1})
    folioFeeForSelf: 0, // No self-burning
    mandate: "A diversified crypto portfolio"
});
TVL fees are specified as per-second rates. The example shows 100 bps annually: 100e18 / 365 days.

Step 3: Configure Rebalance Control

IFolio.FolioFlags memory flags = IFolio.FolioFlags({
    trustedFillerEnabled: false,
    rebalanceControl: IFolio.RebalanceControl({
        weightControl: true,
        priceControl: IFolio.PriceControl.PARTIAL
    }),
    bidsEnabled: true
});
Price Control Levels:
  • NONE: AUCTION_LAUNCHER cannot adjust prices
  • PARTIAL: Can narrow price ranges within bounds
  • ATOMIC_SWAP: Can execute instant swaps at fixed prices (highest trust required)

Step 4: Deploy the Folio

address[] memory basketManagers = new address[](1);
basketManagers[0] = rebalanceManagerAddress;

address[] memory auctionLaunchers = new address[](1);
auctionLaunchers[0] = auctionLauncherAddress;

address[] memory brandManagers = new address[](0);

(Folio folio, address proxyAdmin) = folioDeployer.deployFolio(
    basicDetails,
    additionalDetails,
    flags,
    adminAddress, // DEFAULT_ADMIN_ROLE
    basketManagers,
    auctionLaunchers,
    brandManagers,
    keccak256("deployment_salt")
);

Step 5: Deploy via Command Line

For production deployment:
yarn deploy --rpc-url <RPC_URL> --verify --verifier etherscan
Set the ETHERSCAN_API_KEY environment variable to your API key for the target network (Basescan, Etherscan, Arbiscan, etc.).

Core Operations

Minting Folio Shares

Users can mint Folio shares by depositing the required basket of assets:
// 1. Approve all basket tokens
for (uint256 i = 0; i < basket.length; i++) {
    IERC20(basket[i]).approve(address(folio), type(uint256).max);
}

// 2. Calculate required amounts for desired shares
uint256 desiredShares = 100e18;
uint256[] memory requiredAmounts = folio.getRequiredAmounts(desiredShares);

// 3. Mint with slippage protection
uint256 minSharesOut = desiredShares * 99 / 100; // 1% slippage tolerance
folio.mint(desiredShares, minSharesOut);
Mint fees are automatically deducted. Specify minSharesOut to protect against fee changes between transaction submission and execution.

Redeeming Folio Shares

Redeem Folio shares to receive the underlying assets pro-rata:
// Redeem 50 shares
uint256 sharesToRedeem = 50e18;
folio.redeem(sharesToRedeem);

// Assets are transferred directly to msg.sender

Starting a Rebalance

Only the REBALANCE_MANAGER can initiate rebalances:
// Define rebalance parameters for each token
IFolio.TokenRebalanceParams[] memory tokens = new IFolio.TokenRebalanceParams[](3);

tokens[0] = IFolio.TokenRebalanceParams({
    token: address(tokenA),
    weight: IFolio.WeightRange({
        low: 30e25,  // D27{tok/BU} - buy up to this weight
        spot: 33e25, // Point estimate
        high: 36e25  // Sell down to this weight
    }),
    price: IFolio.PriceRange({
        low: 0.95e27,  // D27{UoA/tok} - most pessimistic
        high: 1.05e27  // D27{UoA/tok} - most optimistic
    }),
    maxAuctionSize: 10000e18, // Maximum tokens per auction
    inRebalance: true
});

// ... configure other tokens ...

IFolio.RebalanceLimits memory limits = IFolio.RebalanceLimits({
    low: 0.95e18,   // D18{BU/share} - buy up to
    spot: 1e18,     // Point estimate
    high: 1.05e18   // D18{BU/share} - sell down to
});

folio.startRebalance(
    tokens,
    limits,
    2 days // TTL: rebalance available for 2 days
);
Basket Units (BU): A Basket Unit is typically defined 1:1 with shares (1e18 BU = 1e18 shares). The limits define the target range for rebalancing.

Opening an Auction

The AUCTION_LAUNCHER can open auctions during the restricted period:
// Select tokens to include in auction
address[] memory auctionTokens = new address[](2);
auctionTokens[0] = address(tokenA); // Surplus token (selling)
auctionTokens[1] = address(tokenB); // Deficit token (buying)

// Optionally narrow price ranges (if priceControl != NONE)
IFolio.PriceRange[] memory prices = new IFolio.PriceRange[](2);
prices[0] = IFolio.PriceRange({
    low: 0.98e27,
    high: 1.02e27 // Narrowed from original 0.95-1.05
});
prices[1] = IFolio.PriceRange({
    low: 1.98e27,
    high: 2.02e27
});

// Open the auction
folio.openAuction(
    auctionTokens,
    limits,      // Can narrow from original
    new IFolio.WeightRange[](0), // Empty if not using weightControl
    prices
);

Bidding in an Auction

Anyone can bid in an active auction:
// 1. Get current bid information
(uint256 sellAmount, uint256 bidAmount, uint256 price) = folio.getBid(
    auctionId,
    IERC20(tokenA), // Sell token
    IERC20(tokenB), // Buy token
    block.timestamp,
    type(uint256).max // No max limit
);

// 2. Approve buy token
IERC20(tokenB).approve(address(folio), bidAmount);

// 3. Submit bid
folio.bid(
    auctionId,
    tokenA,
    tokenB,
    sellAmount,
    bidAmount
);

// 4. Tokens are swapped atomically
Auctions use an exponential decay curve. Prices improve over time, starting at the most optimistic price and moving toward the most pessimistic price.

Checking Auction Status

// Get current auction price at any timestamp
(uint256 sellAmount, uint256 buyAmount, uint256 currentPrice) = folio.getBid(
    auctionId,
    sellToken,
    buyToken,
    block.timestamp,
    maxSellAmount
);

// Check if rebalance is active
bool isRebalancing = folio.stateChangeActive();

Understanding Units

Folio uses a precise unit notation system:
UnitDescriptionExample
{tok}, {share}, {reward}Token balances1000e18
D18{1}18-decimal percentage5e16 = 5%
D27{tok/share}Token-to-share ratio1e27 = 1:1
D27{UoA/tok}Price in nanoUSD2e27 = $2
D18{BU/share}Basket Units per share1e18 = 1 BU
D18{1/s}Per-second rate100e18/365 days
Important: All percentages and ratios use fixed-point arithmetic. A value of 1e18 represents 100% or 1:1 ratio, NOT 1e18%.

Security Best Practices

Always check stateChangeActive() returns false before relying on view function data:
(bool isRebalancing, bool isAuction) = folio.stateChangeActive();
require(!isRebalancing && !isAuction, "State change active");

// Now safe to use view functions
uint256 value = folio.someViewFunction();
If removing a token via removeFromBasket(), users have limited time to redeem before the token becomes inaccessible. Only remove tokens that are malicious or compromised.
Set price ranges conservatively to account for:
  • Timelock delays
  • Block-to-block price volatility
  • MEV searcher exploitation
If prices move outside ranges, AUCTION_LAUNCHER must end the rebalance to prevent value leakage.
With PARTIAL or ATOMIC_SWAP price control, the AUCTION_LAUNCHER can cause value leakage. Choose trusted operators and consider using NONE for maximum security.

Troubleshooting

This occurs when depositing incorrect amounts during minting. Ensure:
  • All basket tokens are approved
  • Amounts match current basket ratios
  • Account for token decimals correctly
The restricted period has not ended. Either:
  • Wait for the restricted period to expire
  • Have the AUCTION_LAUNCHER open the auction
  • Have the REBALANCE_MANAGER end the rebalance
Mint fee changed between transaction submission and execution. Increase your slippage tolerance in the minSharesOut parameter.
Never use interactive git commands (like git rebase -i or git add -i) as they require user input. Use non-interactive alternatives instead.

Next Steps

System Architecture

Learn about the rebalancing mechanism, auction curves, and lot sizing

API Reference

Explore all available functions and their parameters

Additional Resources

GitHub Repository

View source code and examples

Release Notes

Track version history and updates

Trusted Fillers

Learn about CoW Swap integration

Build docs developers (and LLMs) love