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

Price control determines how much authority the AUCTION_LAUNCHER has over auction pricing within the bounds set by the REBALANCE_MANAGER. The three modes provide different tradeoffs between decentralization, execution quality, and MEV risk.

Price Control Modes

enum PriceControl {
    NONE,         // Cannot change prices
    PARTIAL,      // Can set auction prices within bounds
    ATOMIC_SWAP   // PARTIAL + ability to set startPrice = endPrice
}

NONE: Governance-Only Pricing

Behavior

  • AUCTION_LAUNCHER cannot modify price ranges
  • All auctions use the initial price ranges set by REBALANCE_MANAGER
  • Auction length is fixed to maxAuctionLength
  • Most decentralized but least flexible

Configuration

RebalanceControl memory control = RebalanceControl({
    weightControl: false,
    priceControl: PriceControl.NONE
});

folio.setRebalanceControl(control);

Use Case

Ideal for Folios that:
  • Prioritize maximum decentralization
  • Can tolerate wider price ranges set via governance
  • Have long timelock delays where precision matters less
  • Rebalance infrequently (e.g., quarterly)

Example: Opening Auction with NONE

// REBALANCE_MANAGER sets wide price ranges
PriceRange[] memory prices = new PriceRange[](2);
prices[0] = PriceRange(0.99e27, 1.01e27);  // USDC: 1% range
prices[1] = PriceRange(2900e27, 3100e27);  // WETH: ~7% range

rebalanceManager.startRebalance(tokens, limits, 3 days, 7 days);

// AUCTION_LAUNCHER must use exact prices from rebalance
// auctionLength must equal maxAuctionLength
auctionLauncher.openAuction(
    rebalanceNonce,
    tokens,
    newWeights,
    prices,  // Must match initial prices exactly
    newLimits,
    maxAuctionLength  // Fixed length
);
With PriceControl.NONE, the AUCTION_LAUNCHER cannot adapt to market movements. If prices move outside the governance-approved range, value leakage to MEV searchers is possible. The AUCTION_LAUNCHER should monitor markets and end the rebalance if necessary.

PARTIAL: Subset Price Control

Behavior

  • AUCTION_LAUNCHER can narrow price ranges within initial bounds
  • Cannot expand ranges beyond what governance approved
  • Can set auction length between MIN_AUCTION_LENGTH and maxAuctionLength
  • Enables better execution but introduces MEV risk

Configuration

RebalanceControl memory control = RebalanceControl({
    weightControl: true,   // Often paired with weight control
    priceControl: PriceControl.PARTIAL
});

folio.setRebalanceControl(control);

Use Case

Ideal for Folios that:
  • Want to balance decentralization and execution quality
  • Have a semi-trusted AUCTION_LAUNCHER (multisig)
  • Need to adapt to market conditions within approved bounds
  • Prioritize better pricing over absolute trustlessness

Example: Narrowing Price Ranges

// REBALANCE_MANAGER sets outer bounds
PriceRange memory usdcPrice = PriceRange(0.98e27, 1.02e27);  // 4% range

// AUCTION_LAUNCHER can narrow to improve execution
PriceRange memory narrowedPrice = PriceRange(0.995e27, 1.005e27);  // 1% range

// But cannot expand:
PriceRange memory invalid = PriceRange(0.97e27, 1.03e27);  // ❌ Reverts

Calculating Start and End Prices

For a token pair, auction prices are calculated from individual token ranges:
// D27{buyTok/sellTok}
// startPrice = most optimistic (highest sell value / lowest buy value)
// endPrice = most pessimistic (lowest sell value / highest buy value)

startPrice = (sellToken.high * D27) / buyToken.low;
endPrice = (sellToken.low * D27) / buyToken.high;

MEV Risk

With PARTIAL control, a dishonest AUCTION_LAUNCHER can:
  • Set auction start prices that leak value to MEV searchers
  • Cause immediate value extraction when auctions begin
  • Cannot guarantee they receive the leaked value (goes to MEV bots)
The AUCTION_LAUNCHER should act in good faith and price auctions to clear near the efficient market price.

ATOMIC_SWAP: Full Price Control

Behavior

  • All capabilities of PARTIAL mode
  • Additionally can set startPrice == endPrice (flat price)
  • Enables atomic swaps without the 30-second warmup period
  • Allows AUCTION_LAUNCHER to internalize MEV

Configuration

RebalanceControl memory control = RebalanceControl({
    weightControl: true,
    priceControl: PriceControl.ATOMIC_SWAP
});

folio.setRebalanceControl(control);

Use Case

Ideal for Folios that:
  • Fully trust the AUCTION_LAUNCHER to execute at fair prices
  • Want to eliminate MEV leakage entirely
  • Can verify fair execution through other means (e.g., bundle inspection)
  • Prioritize execution quality above all else

Example: Atomic Swap Execution

// Set fixed price (no price curve)
PriceRange memory fixedPrice = PriceRange(1.0e27, 1.0e27);

// Open auction with atomic swap
folio.openAuction(
    rebalanceNonce,
    tokens,
    newWeights,
    [fixedPrice, fixedPrice],  // startPrice == endPrice
    newLimits,
    0  // Can use very short auction length
);

// Fill immediately in same transaction
folio.bid(
    auctionId,
    sellToken,
    buyToken,
    sellAmount,
    maxBuyAmount,
    true,   // Use callback
    data
);

// End rebalance to prevent further exploitation
folio.endRebalance();

Best Practices for ATOMIC_SWAP

When using ATOMIC_SWAP, the AUCTION_LAUNCHER MUST:
  1. Fill the auction atomically in the same transaction as opening it
  2. End the rebalance immediately after all fills complete
  3. Execute as a bundle to prevent frontrunning
Failure to follow this pattern allows the AUCTION_LAUNCHER to extract value AND benefit from it directly.

Choosing a Price Control Mode

ModeDecentralizationExecution QualityMEV RiskBest For
NONEHighestLowestLowInfrequent rebalances, long timelocks
PARTIALMediumMediumMediumBalanced approach, semi-trusted launcher
ATOMIC_SWAPLowestHighestHigh*Fully trusted launcher, MEV elimination
*Risk is high if misused, but can be mitigated with proper execution

Validation Rules

NONE Mode

// Auction length must equal maxAuctionLength
require(auctionLength == maxAuctionLength, "Invalid length");

// Prices must match initial ranges exactly
require(
    newPrices[i].low == rebalance.details[token].initialPrices.low &&
    newPrices[i].high == rebalance.details[token].initialPrices.high,
    "Prices must match"
);

PARTIAL Mode

// Auction length has flexibility
require(
    auctionLength >= MIN_AUCTION_LENGTH &&
    auctionLength <= maxAuctionLength,
    "Invalid length"
);

// Prices must be within initial bounds
require(
    newPrices[i].low >= rebalance.details[token].initialPrices.low &&
    newPrices[i].high <= rebalance.details[token].initialPrices.high,
    "Price out of bounds"
);

// startPrice cannot equal endPrice
require(startPrice != endPrice, "Use ATOMIC_SWAP for fixed prices");

ATOMIC_SWAP Mode

// All PARTIAL validations apply
// Plus: startPrice CAN equal endPrice
if (startPrice == endPrice) {
    // Warmup period bypassed for atomic execution
}

RebalanceControl Structure

struct RebalanceControl {
    bool weightControl;           // If AUCTION_LAUNCHER can adjust weights
    PriceControl priceControl;   // Price control mode
}
The weightControl flag is independent and can be combined with any price control mode. See Basket Weights for details.

Governance Considerations

  • Price control mode cannot be changed during an active rebalance
  • Set via setRebalanceControl() which requires DEFAULT_ADMIN_ROLE
  • Should align with the trust level of your AUCTION_LAUNCHER
  • Can be different for different Folios in the same ecosystem

Build docs developers (and LLMs) love