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
Basket weights define how tokens are proportioned within a Folio. During rebalancing, weight ranges allow the AUCTION_LAUNCHER to progressively adjust token ratios within bounds set by governance.
Basket Unit (BU)
A Basket Unit (BU) is a theoretical unit of account representing a standardized bundle of the underlying tokens:
// D18{BU/share}
struct RebalanceLimits {
uint256 low; // (0, 1e27] Buy assets up to this limit
uint256 spot; // (0, 1e27] Point estimate for unrestricted calls
uint256 high; // (0, 1e27] Sell assets down to this limit
}
Typical usage: BUs are defined 1:1 with shares (1e18), but the protocol supports ranges up to 1e27.
Calculating Total Baskets
For a Folio with total supply of shares:
// {BU} = {share} * D18{BU/share} / D18
uint256 totalBaskets = (totalSupply * basketLimit) / 1e18;
Weight Ranges
// D27{tok/BU}
struct WeightRange {
uint256 low; // [0, 1e54] Minimum weight (buy target)
uint256 spot; // [0, 1e54] Point estimate
uint256 high; // [0, 1e54] Maximum weight (sell target)
}
Weights define how many token quanta comprise one basket unit.
Example: 60/40 Portfolio
// Target: 60% WETH / 40% USDC by value
// Assume: WETH = $3000, USDC = $1
// Portfolio value: $100,000
// WETH: $60,000 / $3000 = 20 WETH
// USDC: $40,000 / $1 = 40,000 USDC
// Per BU (assume 100 shares outstanding, 1 BU/share):
WeightRange memory wethWeight = WeightRange({
low: 0.19e27, // 0.19 WETH/BU
spot: 0.20e27, // 0.20 WETH/BU
high: 0.21e27 // 0.21 WETH/BU
});
WeightRange memory usdcWeight = WeightRange({
low: 38000e27, // 38,000 USDC/BU (remember: 6 decimals)
spot: 40000e27, // 40,000 USDC/BU
high: 42000e27 // 42,000 USDC/BU
});
Weight Control
The weightControl flag in RebalanceControl determines whether the AUCTION_LAUNCHER can adjust weights:
struct RebalanceControl {
bool weightControl; // If AUCTION_LAUNCHER can move weights
PriceControl priceControl; // Price control mode
}
Without Weight Control
AUCTION_LAUNCHER must use the exact spot weights from REBALANCE_MANAGER
- All auction weight ranges collapse to the spot value
- Suitable for Folios with fixed monthly/quarterly targets
With Weight Control
AUCTION_LAUNCHER can progressively narrow weight ranges
- Cannot expand beyond initial
low and high bounds
- Suitable for Folios maintaining specific percentage breakdowns over time
Surplus and Deficit Calculations
During auctions, tokens are classified as surplus or deficit based on current balances vs. target ranges:
Surplus
A token is in surplus when the Folio holds more than needed at the high limit:
// {tok}
uint256 targetBalance = (totalSupply * high_BU_limit * weight.high) / (1e18 * 1e27);
if (currentBalance > targetBalance) {
uint256 surplus = currentBalance - targetBalance;
// Can sell surplus in auctions
}
Deficit
A token is in deficit when the Folio holds less than needed at the low limit:
// {tok}
uint256 targetBalance = (totalSupply * low_BU_limit * weight.low) / (1e18 * 1e27);
if (currentBalance < targetBalance) {
uint256 deficit = targetBalance - currentBalance;
// Must buy deficit in auctions
}
Auction Pair Eligibility
A token pair is eligible for auction only if:
- Sell token is in surplus (using
high limit)
- Buy token is in deficit (using
low limit)
Lot Sizing
Auction sell amounts are calculated as the minimum of:
- Surplus constraint: How much sell token is surplus
- Deficit constraint: How much buy token is needed (converted via price)
- Max auction size: Governance-set maximum per token
function _getBid(...) internal view returns (uint256 sellAmount, ...) {
// Calculate sell token surplus
uint256 sellSurplus = sellBalance -
(totalSupply * limits.high * sellWeight.high) / (1e18 * 1e27);
// Calculate buy token deficit
uint256 buyDeficit =
(totalSupply * limits.low * buyWeight.low) / (1e18 * 1e27) - buyBalance;
// Convert buy deficit to sell terms
uint256 sellForDeficit = (buyDeficit * sellPrice) / buyPrice;
// Take minimum, capped by maxAuctionSize
sellAmount = Math.min(
Math.min(sellSurplus, sellForDeficit),
maxAuctionSize
);
}
Progressive Rebalancing
The AUCTION_LAUNCHER can run multiple auctions, progressively tightening both limits and weights:
// Auction 1: Wide ranges
WeightRange memory weth1 = WeightRange(0.19e27, 0.20e27, 0.21e27);
RebalanceLimits memory limits1 = RebalanceLimits(0.95e18, 1.0e18, 1.05e18);
// Auction 2: Narrower ranges (within original bounds)
WeightRange memory weth2 = WeightRange(0.195e27, 0.20e27, 0.205e27);
RebalanceLimits memory limits2 = RebalanceLimits(0.98e18, 1.0e18, 1.02e18);
// Auction 3: Collapsed to spot (rebalance complete)
WeightRange memory weth3 = WeightRange(0.20e27, 0.20e27, 0.20e27);
RebalanceLimits memory limits3 = RebalanceLimits(1.0e18, 1.0e18, 1.0e18);
Weights can only be narrowed, never expanded. Attempting to widen ranges beyond the initial low/high values will revert.
Dynamic vs Static Rebalancing
Static Targets (weightControl = false)
Use when:
- Rebalancing to a specific token composition (e.g., “own 1000 WETH and 1M USDC”)
- Target composition is known ahead of time
- Rebalances are infrequent (monthly, quarterly)
RebalanceControl memory control = RebalanceControl({
weightControl: false,
priceControl: PriceControl.NONE
});
Dynamic Targets (weightControl = true)
Use when:
- Maintaining a percentage-based portfolio (e.g., “always 60/40 WETH/USDC”)
- Token ratios must adapt as prices change
- Rebalances are frequent or continuous
RebalanceControl memory control = RebalanceControl({
weightControl: true,
priceControl: PriceControl.PARTIAL // Often paired
});
Units and Precision
Weight Units
- Format:
D27{tok/BU} (27 decimal fixed point)
- Range:
[0, 1e54]
- Calculation:
1e27 = 1 token quantum per BU
Example with Different Decimals
// WETH (18 decimals): 0.5 WETH per BU
uint256 wethWeight = 0.5e27; // 0.5 * 1e27
// USDC (6 decimals): 1500 USDC per BU
uint256 usdcWeight = 1500e27; // 1500 * 1e27
// Target balance for 100e18 shares at 1e18 BU/share:
// WETH: (100e18 * 1e18 * 0.5e27) / (1e18 * 1e27) = 50e18 WETH
// USDC: (100e18 * 1e18 * 1500e27) / (1e18 * 1e27) = 150000e6 USDC
Handling Zero Weights
Zero weights are used to remove tokens from the basket:
// Remove USDT from basket
WeightRange memory usdtWeight = WeightRange({
low: 0, // Sell down to zero
spot: 0,
high: 0
});
// After rebalancing completes:
// - USDT balance will be zero
// - Can call removeFromBasket() permissionlessly
Common Patterns
Equal-Weighted Portfolio
// 3 tokens, equal weight by value
// Assume all tokens = $1 each for simplicity
WeightRange memory weight = WeightRange({
low: 0.32e27, // Allow some flexibility
spot: 0.333333e27,
high: 0.35e27
});
// Apply same weight to all 3 tokens
Capped Exposure
// Want 70% WETH, but cap at 1000 WETH total
WeightRange memory wethWeight = WeightRange({
low: 6.5e18, // 65% of target (per BU)
spot: 7.0e18, // 70% target
high: 7.5e18 // 75% max
});
uint256 maxAuctionSize = 1000e18; // Hard cap at 1000 WETH
Validation Rules
// Weights must be ordered: low ≤ spot ≤ high
require(
weight.low <= weight.spot && weight.spot <= weight.high,
"Invalid weight range"
);
// All weights are valid: [0, 1e54]
require(weight.low >= 0 && weight.high <= 1e54, "Weight out of bounds");
// When narrowing, must stay within original bounds
require(
newWeight.low >= originalWeight.low &&
newWeight.high <= originalWeight.high,
"Cannot expand weight range"
);
Gas Optimization
Weight calculations happen on every getBid() call. To optimize:
- Use collapsed ranges (
low == spot == high) for final auctions
- Limit the number of tokens in rebalance (affects calculation loops)
- Consider
maxAuctionSize caps to prevent excessive lot sizing calculations