AuctionHouse¶
Dutch auction for seized collateral from insolvent CDP positions. Sells collateral for TARE; proceeds route to engine via absorb_auction_proceeds.
Source: TARE-Stablecoin/src/auction_house.vy (Vyper 0.4.3)
Concept guide: Liquidations protocol page
Wired by: TareEngine start_auction · set_auction_house
Implementation overview¶
- Engine-only lots — only
ENGINEcanopen_lot; collateral already seized viasettle_bad_debt. - Dutch pricing — ask decays from
top(oracle × premium) toflooroverduration. - Partial fills — buyers call
takeat current price until lot cleared. - Proceeds — TARE forwarded to
engine.absorb_auction_proceeds(surplus refill → bad-debt burn).
flowchart LR
Engine[TareEngine] -->|seized collateral| AH[AuctionHouse]
Buyer[Buyer] -->|TARE| AH
AH -->|absorb_auction_proceeds| Engine
Price curve:
top = oracle_unit_price × start_premium / 1e18
floor = top × floor_pct / 1e18
price(t) = top - (top - floor) × min(elapsed, duration) / duration
Immutables¶
| Name | Role |
|---|---|
ENGINE |
TareEngine — only caller for open_lot |
TARE |
Payment token |
Events¶
| Event | When |
|---|---|
LotOpened |
New auction lot |
LotTaken |
Buyer purchases collateral slice |
LotClosed |
Lot fully cleared |
LotRestarted |
Owner resets stale lot pricing |
StartPremiumSet / FloorPctSet / DurationSet |
Parameter updates |
Paused |
Circuit breaker |
Engine integration¶
open_lot¶
AuctionHouse.open_lot(token, amount, token_decimals)
Open Dutch auction lot for seized collateral already held by this contract. Engine transfers collateral before calling.
| Param | Type | Description |
|---|---|---|
token |
address |
Seized collateral ERC-20 |
amount |
uint256 |
Lot size (token's native decimals) |
token_decimals |
uint256 |
Cached decimals for take pricing math |
Returns / state: Allocates lot_id = next_lot_id, increments counter. Snapshots top (oracle × start_premium) and floor (top × floor_pct), sets start_time = block.timestamp, active = True.
Access: ENGINE immutable only. Reverts if paused or amount == 0.
Events: LotOpened(lot_id, token, amount, top, floor).
Source (TARE-Stablecoin/src/auction_house.vy:170-197):
@external
@nonreentrant
def open_lot(token: address, amount: uint256, token_decimals: uint256):
assert msg.sender == ENGINE, "AuctionHouse: only engine"
assert not self.paused, "AuctionHouse: paused"
assert amount > 0, "AuctionHouse: zero amount"
top: uint256 = self._oracle_top(token, token_decimals)
floor_price: uint256 = top * self.floor_pct // PRECISION
lot_id: uint256 = self.next_lot_id
self.next_lot_id = lot_id + 1
self.lots[lot_id] = Lot(...)
log LotOpened(lot_id=lot_id, token=token, amount=amount, top=top, floor=floor_price)
Example:
# Called by TareEngine.start_auction after transfer — not user-callable
auction_house.open_lot(weth, 5 * 10**18, 18)
Cross-links: TareEngine start_auction · take · current_price
Buyer¶
take¶
AuctionHouse.take(lot_id, max_collateral, max_tare)
Buy collateral from live lot at current Dutch price. Partial fills allowed.
| Param | Type | Description |
|---|---|---|
lot_id |
uint256 |
Lot to purchase from |
max_collateral |
uint256 |
Max collateral to receive (token decimals) |
max_tare |
uint256 |
Max TARE to spend (slippage cap) |
Returns / state: Shrinks lots[lot_id].remaining; deactivates lot when cleared. Transfers TARE from buyer → contract → engine.absorb_auction_proceeds; sends collateral to buyer.
Access: Any caller when not paused. Buyer must approve TARE on this contract.
Events: LotTaken(lot_id, buyer, collateral_out, tare_paid, price); LotClosed when remaining hits zero.
Source (TARE-Stablecoin/src/auction_house.vy:200-251):
@external
@nonreentrant
def take(lot_id: uint256, max_collateral: uint256, max_tare: uint256):
price: uint256 = self._price(lot)
collateral_out: uint256 = min(max_collateral, lot.remaining)
pay: uint256 = collateral_out * price // unit
if pay > max_tare:
collateral_out = max_tare * unit // price
...
extcall TARE.transferFrom(msg.sender, self, pay)
extcall TARE.approve(ENGINE, pay)
extcall IEngine(ENGINE).absorb_auction_proceeds(pay)
extcall IERC20(lot.token).transfer(msg.sender, collateral_out)
Example:
tare.approve(auction_house, 10_000 * 10**18)
auction_house.take(lot_id=0, max_collateral=2 * 10**18, max_tare=5_000 * 10**18)
Cross-links: open_lot · current_price · TareEngine absorb_auction_proceeds
Admin¶
restart_lot¶
AuctionHouse.restart_lot(lot_id)
Re-anchor stale lot to current oracle price and reset decay window. Remaining collateral unchanged.
| Param | Type | Description |
|---|---|---|
lot_id |
uint256 |
Active lot that floored without clearing |
Returns / state: Recomputes top / floor from live oracle + current start_premium / floor_pct; sets start_time = block.timestamp.
Access: owner only. Lot must still be active.
Events: LotRestarted(lot_id, top, floor).
Source (TARE-Stablecoin/src/auction_house.vy:258-273):
@external
def restart_lot(lot_id: uint256):
ow._check_owner()
lot: Lot = self.lots[lot_id]
assert lot.active, "AuctionHouse: lot not active"
top: uint256 = self._oracle_top(lot.token, lot.token_decimals)
floor_price: uint256 = top * self.floor_pct // PRECISION
self.lots[lot_id].top = top
self.lots[lot_id].floor = floor_price
self.lots[lot_id].start_time = block.timestamp
log LotRestarted(lot_id=lot_id, top=top, floor=floor_price)
Example:
Cross-links: open_lot · take · set_start_premium
set_start_premium¶
AuctionHouse.set_start_premium(premium)
Set oracle premium multiplier for new lots and restart_lot repricing. Live lots keep their snapshot.
| Param | Type | Description |
|---|---|---|
premium |
uint256 |
1e18-scaled multiplier on oracle unit price (MIN_PREMIUM–MAX_PREMIUM, i.e. 1.0x–2.0x) |
Returns / state: Updates start_premium storage. Default at deploy: 1.1e18 (10% over oracle).
Access: owner only.
Events: StartPremiumSet(premium).
Source (TARE-Stablecoin/src/auction_house.vy:276-281):
@external
def set_start_premium(premium: uint256):
ow._check_owner()
assert premium >= MIN_PREMIUM and premium <= MAX_PREMIUM, "AuctionHouse: premium bounds"
self.start_premium = premium
log StartPremiumSet(premium=premium)
Example:
Cross-links: open_lot · restart_lot · set_floor_pct
set_floor_pct¶
AuctionHouse.set_floor_pct(floor_pct_)
Set floor as fraction of top for new lots and restarts. floor = top × floor_pct / 1e18.
| Param | Type | Description |
|---|---|---|
floor_pct_ |
uint256 |
1e18-scaled fraction of top at end of decay (0 < floor_pct_ < 1e18) |
Returns / state: Updates floor_pct storage. Default at deploy: 0.5e18 (50% of top).
Access: owner only.
Events: FloorPctSet(floor_pct).
Source (TARE-Stablecoin/src/auction_house.vy:284-289):
@external
def set_floor_pct(floor_pct_: uint256):
ow._check_owner()
assert floor_pct_ > 0 and floor_pct_ < PRECISION, "AuctionHouse: floor bounds"
self.floor_pct = floor_pct_
log FloorPctSet(floor_pct=floor_pct_)
Example:
Cross-links: set_start_premium · set_duration · open_lot
set_duration¶
AuctionHouse.set_duration(duration_)
Set seconds for full top→floor Dutch decay on new lots and restarts.
| Param | Type | Description |
|---|---|---|
duration_ |
uint256 |
Decay window in seconds (MIN_DURATION–MAX_DURATION, 60s–1 week) |
Returns / state: Updates duration storage. Default at deploy: 3600 (1 hour).
Access: owner only.
Events: DurationSet(duration).
Source (TARE-Stablecoin/src/auction_house.vy:292-297):
@external
def set_duration(duration_: uint256):
ow._check_owner()
assert duration_ >= MIN_DURATION and duration_ <= MAX_DURATION, "AuctionHouse: duration bounds"
self.duration = duration_
log DurationSet(duration=duration_)
Example:
Cross-links: set_floor_pct · open_lot · current_price
pause¶
AuctionHouse.pause()
Circuit breaker — blocks open_lot and take. Views still callable.
Returns / state: Sets paused = True.
Access: owner only.
Events: Paused(status=True).
Source (TARE-Stablecoin/src/auction_house.vy:300-304):
Example:
Cross-links: unpause · open_lot · take
unpause¶
AuctionHouse.unpause()
Resume open_lot and take after owner pause.
Returns / state: Sets paused = False.
Access: owner only.
Events: Paused(status=False).
Source (TARE-Stablecoin/src/auction_house.vy:307-311):
Example:
Cross-links: pause · open_lot · take
Views¶
current_price¶
AuctionHouse.current_price(lot_id) -> uint256
Current TARE-per-whole-token ask for active lot (Dutch decay in progress).
| Param | Type | Description |
|---|---|---|
lot_id |
uint256 |
Lot to quote |
Returns: uint256 — price in 1e18-scaled TARE per whole collateral token. At/after duration, returns floor.
Access: view, any caller. Reverts if lot not active.
Source (TARE-Stablecoin/src/auction_house.vy:318-324, _price:340-347):
@external
@view
def current_price(lot_id: uint256) -> uint256:
lot: Lot = self.lots[lot_id]
assert lot.active, "AuctionHouse: lot not active"
return self._price(lot)
Example:
Cross-links: take · open_lot · set_duration