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.
After the restricted period, anyone can open auctions using spot values:
/// Open auction on all tokens in rebalance with spot values and initial pricesfunction openAuctionUnrestricted( uint256 rebalanceNonce) external returns (uint256 auctionId)
Unrestricted auctions use spot values for both limits and weights, with initial price ranges.
The sellAmount can increase or decrease over time:
Increasing Lot Size
When surplus of sell token is the limiting factorAs the auction progresses and some tokens are sold, the surplus decreases relative to progressively narrowing limits, allowing larger lots.
Decreasing Lot Size
When deficit of buy token is the limiting factorAs buy tokens are acquired, the deficit shrinks relative to progressively narrowing limits, requiring smaller lots.
Governance can set a maximum auction size per token:
struct TokenRebalanceParams { address token; WeightRange weight; PriceRange price; uint256 maxAuctionSize; // {tok} Max amount to sell in any single auction bool inRebalance;}
This prevents overly large single auctions that could face excessive slippage.
/// @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 pricefunction getBid( uint256 auctionId, IERC20 sellToken, IERC20 buyToken, uint256 maxSellAmount) external view returns ( uint256 sellAmount, uint256 bidAmount, uint256 price)
/// Close an auction at any point in its lifecycle/// Callable by: DEFAULT_ADMIN_ROLE, REBALANCE_MANAGER, or AUCTION_LAUNCHERfunction closeAuction(uint256 auctionId) external
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.
// Auction 1: Wide ranges for price discoveryopenAuction(tokens, wideWeights, widePrices, wideLimits, 3600);// ... wait for auction to complete or close it// Auction 2: Narrower ranges based on resultsopenAuction(tokens, narrowWeights, narrowPrices, narrowLimits, 1800);// ... repeat as needed// Final auction: Tight ranges to complete rebalanceopenAuction(tokens, finalWeights, finalPrices, finalLimits, 1800);
The AUCTION_LAUNCHER can overwrite an ongoing auction, but unpermissioned callers must wait for the current auction to close.