Fee Auction
The FeeAuction distributes protocol yield — collected as backing assets from Operator interest payments — to stcUSD holders via a Dutch auction mechanism. stcUSD holders bid cUSD at a continuously decaying price to purchase the accumulated backing assets at a discount, creating a fixed-rate yield experience. Each successful purchase resets the auction at double the settlement price, establishing a self-calibrating price discovery cycle. The FeeReceiver acts as the upstream collector, receiving vault interest from the Lender and holding it until the FeeAuction sweeps it.
Mechanics
Dutch Auction Price Curve
The auction price decays linearly from startPrice to 10% of startPrice over duration seconds:
price = startPrice × (1 - 0.9 × elapsed / duration)At
t = 0: price =startPrice(100%)At
t = duration: price =startPrice × 0.1(10%)Price never reaches zero — it floors at 10% of the start price
All price values are in the paymentToken (cUSD) with 18 decimals.
Buy Flow
Price check:
currentPrice()is compared against caller's_maxPrice. Reverts withInvalidPriceifcurrentPrice > _maxPrice.Validation: Asset list must be non-empty and match
_minAmountslength;_receivermust be non-zero; deadline must be in the future.New auction reset:
startTimestampis set toblock.timestamp;startPriceis set tomax(currentPrice × 2, minStartPrice).Asset transfer: Full balances of each requested
_assetheld by the FeeAuction are transferred to_receiver. Reverts withInsufficientBalanceif any asset balance is below_minAmounts[i].Payment:
currentPriceinpaymentTokenis pulled frommsg.senderand sent topaymentRecipient(the stcUSD distribution contract or treasury).
Self-Calibrating Start Price
Each successful purchase sets the next auction's startPrice to 2 × settlementPrice. This means:
If buyers wait for a deep discount, the next auction starts low.
If buyers buy early, the next auction starts high.
The auction converges on a market-clearing rate for protocol yield.
The minStartPrice floor prevents the auction from becoming trivially cheap during low-activity periods.
Interest Flow End-to-End
Architecture
FeeAuction
Core Functions
buy(uint256 _maxPrice, address[] _assets, uint256[] _minAmounts, address _receiver, uint256 _deadline)
Purchase all accumulated fee assets at the current Dutch auction price.
_maxPrice
uint256
Maximum cUSD to pay; reverts if currentPrice > _maxPrice
_assets
address[]
List of backing assets to receive from the auction contract
_minAmounts
uint256[]
Minimum balance of each asset to accept (slippage protection per asset)
_receiver
address
Address to receive the purchased backing assets
_deadline
uint256
Unix timestamp after which the tx reverts
currentPrice()
Returns the current auction price in paymentToken units. Decays linearly from startPrice to startPrice × 0.1 over duration. Use this before calling buy to set _maxPrice.
Admin Functions
setStartPrice(uint256)
Manually reset the auction start price (must be ≥ minStartPrice)
setDuration(uint256)
Update the auction duration in seconds
setMinStartPrice(uint256)
Update the minimum allowed start price floor
setPaymentToken(address)
Change the payment token (cUSD by default)
View Functions
currentPrice()
uint256
Current auction price in payment token
startPrice()
uint256
Starting price of the current auction
startTimestamp()
uint256
When the current auction started
duration()
uint256
Auction duration in seconds
minStartPrice()
uint256
Floor price for future auction starts
paymentToken()
address
Token buyers pay with (cUSD)
paymentRecipient()
address
Receives the cUSD payment from buyers
Data Structures
FeeAuctionStorage
paymentToken
ERC20 token buyers pay with — cUSD
paymentRecipient
Receives cUSD from every purchase — distributes to stcUSD holders
startPrice
Current auction starting price in paymentToken (18 decimals)
startTimestamp
block.timestamp of the last purchase (or initialization)
duration
Seconds for the price to decay from startPrice to startPrice × 0.1
minStartPrice
Minimum startPrice floor — prevents the auction going below a meaningful level
Events
Buy
buyer, price, assets[], balances[]
A successful purchase is made
SetDuration
duration
Auction duration updated
SetMinStartPrice
minStartPrice
Minimum start price updated
SetPaymentToken
paymentToken
Payment token changed
SetStartPrice
startPrice
Start price manually reset
Errors
InvalidPrice
currentPrice > _maxPrice
InvalidAssets
Empty asset list or assets.length ≠ minAmounts.length
InvalidDeadline
_deadline < block.timestamp
InvalidReceiver
_receiver == address(0)
InvalidStartPrice
setStartPrice called with value below minStartPrice
InvalidPaymentToken
setPaymentToken called with zero address
InsufficientBalance
Asset balance in the contract is below _minAmounts[i]
NoMinStartPrice
initialize called with _minStartPrice == 0
NoDuration
initialize or setDuration called with _duration == 0
FeeReceiver
The FeeReceiver is a simple holding contract that sits between the Lender and the FeeAuction. The Lender sends vault interest to the FeeReceiver, which holds the assets until a buyer triggers the FeeAuction sweep.
It has no complex logic — its purpose is to batch up interest from multiple reserves before each auction, so buyers receive a meaningful bundle of assets per purchase rather than tiny per-asset drips.
Usage Examples
1. Buying auction assets (stcUSD holder / arbitrageur)
2. Monitoring the auction price off-chain
3. Full interest flow — from Operator repayment to stcUSD yield
Last updated