> ## 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.

# Dutch Auction Mechanism

> Learn how Reserve Folio uses Dutch auctions for efficient price discovery and rebalancing

## Overview

Reserve Folio uses Dutch auctions to rebalance portfolio holdings. Each auction runs on all surplus/deficit token pairs simultaneously, with prices decaying exponentially from optimistic to pessimistic estimates.

## Auction Lifecycle

Auctions progress through several states:

```
UNINITIALIZED → PENDING → WARMUP → OPEN → CLOSED
```

<Steps>
  <Step title="Uninitialized">
    Auction hasn't been created yet

    * `startTime == 0`
    * `endTime == 0`
  </Step>

  <Step title="Pending">
    Auction created but not yet started

    * `block.timestamp < startTime`
  </Step>

  <Step title="Warmup">
    30-second warmup period to ensure fair competition

    * `block.timestamp >= startTime`
    * `block.timestamp < startTime + 30`
    * No bidding allowed yet

    <Note>Warmup is bypassed for atomic swaps when start and end prices are equal</Note>
  </Step>

  <Step title="Open">
    Active bidding period

    * `block.timestamp >= startTime + 30`
    * `block.timestamp <= endTime`
    * Anyone can bid
  </Step>

  <Step title="Closed">
    Auction has ended

    * `block.timestamp > endTime`
  </Step>
</Steps>

## Opening Auctions

Auctions can be opened in two ways:

### Restricted Opening (by AUCTION\_LAUNCHER)

During the restricted period, only the `AUCTION_LAUNCHER` can open auctions:

```solidity theme={null}
/// @param rebalanceNonce The nonce of the target rebalance
/// @param tokens Subset of tokens from the rebalance to include
/// @param newWeights D27{tok/BU} New basket weight ranges
/// @param newPrices D27{UoA/tok} New price ranges (must obey PriceControl)
/// @param newLimits D18{BU/share} New BU limits
/// @param auctionLength {s} Desired auction length
function openAuction(
    uint256 rebalanceNonce,
    address[] calldata tokens,
    WeightRange[] calldata newWeights,
    PriceRange[] calldata newPrices,
    RebalanceLimits calldata newLimits,
    uint256 auctionLength
) external onlyRole(AUCTION_LAUNCHER) returns (uint256 auctionId)
```

### Unrestricted Opening

After the restricted period, anyone can open auctions using spot values:

```solidity theme={null}
/// Open auction on all tokens in rebalance with spot values and initial prices
function openAuctionUnrestricted(
    uint256 rebalanceNonce
) external returns (uint256 auctionId)
```

<Warning>
  Unrestricted auctions use spot values for both limits and weights, with initial price ranges.
</Warning>

## Price Curves

Auction prices decay exponentially over time between start and end prices.

### How Prices are Calculated

```solidity theme={null}
// For a token pair (sell/buy)
startPrice = (sellToken.low * buyToken.high) / 1e27  // Most optimistic
endPrice = (sellToken.high * buyToken.low) / 1e27    // Most pessimistic

// Price at time t decays exponentially
function priceAt(uint256 t) returns (uint256) {
    if (t <= startTime + WARMUP) return type(uint256).max; // No bidding
    if (t >= endTime) return endPrice;
    
    // Exponential decay between startPrice and endPrice
    uint256 progress = (t - startTime - WARMUP) / (endTime - startTime - WARMUP);
    return startPrice * (endPrice / startPrice) ** progress;
}
```

### Price Curve Visualization

<Frame>
  <img src="https://mintcdn.com/reserve-protocol-reserve-index-dtf/yQcl0nzcfLpKTRbb/images/auction-curve.png?fit=max&auto=format&n=yQcl0nzcfLpKTRbb&q=85&s=f86c7db223271c30d0223077143d82d9" alt="Auction Price Curve" width="800" height="399" data-path="images/auction-curve.png" />
</Frame>

<Info>
  The first block may not have exactly `startPrice` if it doesn't occur on the `start` timestamp. Similarly for `endPrice` and the final block.
</Info>

## Lot Sizing

Auction lot sizes are determined by surplus and deficit calculations relative to target basket limits and weights.

### Surplus and Deficit

* **Surplus**: Token balance exceeds `high weight × high BU limit`
* **Deficit**: Token balance is below `low weight × low BU limit`

### How Lot Size Changes

The `sellAmount` can increase or decrease over time:

<CardGroup cols={2}>
  <Card title="Increasing Lot Size" icon="arrow-up">
    When surplus of sell token is the limiting factor

    As the auction progresses and some tokens are sold, the surplus decreases relative to progressively narrowing limits, allowing larger lots.
  </Card>

  <Card title="Decreasing Lot Size" icon="arrow-down">
    When deficit of buy token is the limiting factor

    As buy tokens are acquired, the deficit shrinks relative to progressively narrowing limits, requiring smaller lots.
  </Card>
</CardGroup>

### Max Auction Size

Governance can set a maximum auction size per token:

```solidity theme={null}
struct TokenRebalanceParams {
    address token;
    WeightRange weight;
    PriceRange price;
    uint256 maxAuctionSize; // {tok} Max amount to sell in any single auction
    bool inRebalance;
}
```

<Note>
  This prevents overly large single auctions that could face excessive slippage.
</Note>

## Bidding on Auctions

Anyone can bid on an ongoing auction during the open period.

### Getting Bid Information

Query current auction prices and lot sizes:

```solidity theme={null}
/// @param auctionId The auction ID
/// @param sellToken The token being sold by the Folio
/// @param buyToken The token being bought by the Folio
/// @param maxSellAmount {sellTok} Max amount bidder wants to buy
/// @return sellAmount {sellTok} Amount of sell token available
/// @return bidAmount {buyTok} Amount of buy token required
/// @return price D27{buyTok/sellTok} Current price
function getBid(
    uint256 auctionId,
    IERC20 sellToken,
    IERC20 buyToken,
    uint256 maxSellAmount
) external view returns (
    uint256 sellAmount, 
    uint256 bidAmount, 
    uint256 price
)
```

### Submitting a Bid

Bid using allowances or callbacks:

```solidity theme={null}
/// @param auctionId The auction ID
/// @param sellToken Token the bidder receives from Folio
/// @param buyToken Token the bidder provides to Folio
/// @param sellAmount {sellTok} Amount of sell token to buy
/// @param maxBuyAmount {buyTok} Maximum amount bidder will pay
/// @param withCallback If true, uses callback for token transfer
/// @param data Arbitrary data passed to callback
/// @return boughtAmt {buyTok} Actual amount paid
function bid(
    uint256 auctionId,
    IERC20 sellToken,
    IERC20 buyToken,
    uint256 sellAmount,
    uint256 maxBuyAmount,
    bool withCallback,
    bytes calldata data
) external returns (uint256 boughtAmt)
```

<Warning>
  Bids must be enabled for the rebalance. Check `rebalance.bidsEnabled` before attempting to bid.
</Warning>

<CodeGroup>
  ```solidity Example: Bid with Approval theme={null}
  // Get current bid info
  (uint256 sellAmount, uint256 bidAmount, uint256 price) = folio.getBid(
      auctionId,
      sellToken,
      buyToken,
      1000e18  // max sell amount I want
  );

  // Approve buy tokens
  buyToken.approve(address(folio), bidAmount);

  // Submit bid
  folio.bid(
      auctionId,
      sellToken,
      buyToken,
      sellAmount,
      bidAmount * 101 / 100,  // 1% slippage tolerance
      false,                   // no callback
      ""                       // no data
  );
  ```

  ```solidity Example: Bid with Callback theme={null}
  // Bidder must implement IBidderCallee interface
  contract MyBidder is IBidderCallee {
      function bid(uint256 auctionId, ...) external {
          folio.bid(
              auctionId,
              sellToken,
              buyToken,
              sellAmount,
              maxBuyAmount,
              true,        // use callback
              abi.encode(additionalData)
          );
      }
      
      // Called by Folio during bid
      function folioCallback(
          IERC20 buyToken,
          uint256 buyAmount,
          bytes calldata data
      ) external override {
          require(msg.sender == address(folio));
          // Transfer tokens to Folio
          buyToken.transfer(msg.sender, buyAmount);
      }
  }
  ```
</CodeGroup>

## Trusted Fillers

As an alternative to direct bidding, trusted fillers enable asynchronous swaps.

### Creating a Trusted Fill

```solidity theme={null}
/// @param auctionId The auction ID
/// @param sellToken Token Folio is selling
/// @param buyToken Token Folio is buying
/// @param targetFiller Address of the trusted filler implementation
/// @param deploymentSalt Salt for deterministic deployment
function createTrustedFill(
    uint256 auctionId,
    IERC20 sellToken,
    IERC20 buyToken,
    address targetFiller,
    bytes32 deploymentSalt
) external returns (IBaseTrustedFiller filler)
```

<Info>
  Trusted fillers must be enabled via `trustedFillerEnabled` and a valid registry must be set.
</Info>

### Trusted Filler Flow

<Steps>
  <Step title="Create Fill">
    Caller creates a trusted fill contract for the auction
  </Step>

  <Step title="Approve Tokens">
    Folio approves sell tokens to the trusted filler
  </Step>

  <Step title="Execute Swap">
    Trusted filler executes swap asynchronously (within same block)
  </Step>

  <Step title="Close Fill">
    Folio reclaims all token balances from the filler
  </Step>
</Steps>

## Closing Auctions

Privileged roles can close auctions early:

```solidity theme={null}
/// Close an auction at any point in its lifecycle
/// Callable by: DEFAULT_ADMIN_ROLE, REBALANCE_MANAGER, or AUCTION_LAUNCHER
function closeAuction(uint256 auctionId) external
```

<Warning>
  Closing an auction before `startTime` would break the invariant that `endTime > startTime`, so closing very early auctions will not revert but may have unexpected behavior.
</Warning>

## Multiple Auctions per Rebalance

A single rebalance can have many auctions, but only one runs at a time.

### Sequential Auction Strategy

```solidity theme={null}
// Auction 1: Wide ranges for price discovery
openAuction(tokens, wideWeights, widePrices, wideLimits, 3600);
// ... wait for auction to complete or close it

// Auction 2: Narrower ranges based on results
openAuction(tokens, narrowWeights, narrowPrices, narrowLimits, 1800);
// ... repeat as needed

// Final auction: Tight ranges to complete rebalance
openAuction(tokens, finalWeights, finalPrices, finalLimits, 1800);
```

<Note>
  The `AUCTION_LAUNCHER` can overwrite an ongoing auction, but unpermissioned callers must wait for the current auction to close.
</Note>

## Price Control Modes

The level of price control granted to `AUCTION_LAUNCHER` affects auction behavior:

<Tabs>
  <Tab title="NONE">
    **No Price Control**

    AUCTION\_LAUNCHER cannot modify prices from initial ranges.

    * Auction length must be `maxAuctionLength`
    * Prices fixed to governance-set ranges
    * Most decentralized option
  </Tab>

  <Tab title="PARTIAL">
    **Partial Price Control**

    AUCTION\_LAUNCHER can narrow price ranges within initial bounds.

    * Can set tighter price ranges per auction
    * Flexible auction length (2 min to `maxAuctionLength`)
    * Risk: Can cause value leakage via dishonest prices
    * Cannot guarantee they're the beneficiary
  </Tab>

  <Tab title="ATOMIC_SWAP">
    **Full Price Control**

    AUCTION\_LAUNCHER can perform atomic swaps at fixed prices.

    * Can set `startPrice == endPrice`
    * Bypasses 30s warmup period
    * Flexible auction length
    * Risk: Can cause value leakage AND be the beneficiary
    * Best practice: End rebalance immediately after swap
  </Tab>
</Tabs>

<Warning>
  Higher price control modes grant more power to the AUCTION\_LAUNCHER. Use ATOMIC\_SWAP only with highly trusted operators.
</Warning>

## Auction Best Practices

<AccordionGroup>
  <Accordion title="For AUCTION_LAUNCHER">
    * Progressively narrow BU limits to responsibly DCA into new basket
    * End rebalance when prices move outside initially-provided ranges
    * If `weightControl=true`: Progressively narrow weight ranges to maintain intent
    * If `priceControl=PARTIAL`: Provide narrowed price ranges that include current clearing price
    * If `priceControl=ATOMIC_SWAP`: Fill atomically and end rebalance immediately after
  </Accordion>

  <Accordion title="For Bidders">
    * Monitor price decay to find optimal entry point
    * Account for gas costs in profitability calculations
    * Use `maxBuyAmount` to protect against slippage
    * Consider competing bidders and MEV searchers
    * For large bids, consider multiple smaller bids over time
  </Accordion>

  <Accordion title="For Governance">
    * Set price ranges conservative enough to avoid value leakage
    * Configure auction length appropriate for expected volatility
    * Set `maxAuctionSize` to prevent excessive single-auction slippage
    * Monitor AUCTION\_LAUNCHER behavior and revoke if malicious
    * Use lower price control modes when possible
  </Accordion>
</AccordionGroup>
