Skip to content

Shortfall and auctions (historical; BNB mainnet paused)

When an undercollateralized borrow was written off in an Isolated Pool market, its borrow balance stopped accruing interest and the market tracked the amount as bad debt.

V represents total bad debt including interest accrued before the write-off. If the initial borrow index was 1.2 and the borrow index was 1.5 when 100 USDC of principal became bad debt, then:

V = 100 USDC × (1.5 / 1.2) = 125 USDC

Historically, anyone could start an auction when:

  • no auction was in progress for that pool (status was NOT_STARTED or ENDED); and
  • the pool’s USD-denominated bad debt was greater than or equal to Shortfall.minimumPoolBadDebt().

Shortfall.incentiveBps() configured the bidder incentive. Its value at the snapshot block above was 1,000 bps, or 10%; 10% was a configured value, not a source-enforced maximum.

N represents the pool’s total bad debt in USD and M represents the pool’s risk-fund balance in USD. After calculating the bad debt plus the configured incentive, the historical source selected the auction type as follows:

  • LARGE_POOL_DEBT when the bad debt plus incentive was greater than or equal to M: bidders competed by offering a larger percentage of every market’s bad debt. The winner was owed the auction’s entire seizedRiskFund snapshot; Shortfall either transferred the payout or recorded it as claimable tokenDebt if the transfer to the winner failed.
  • LARGE_RISK_FUND when the bad debt plus incentive was less than M: every bidder covered 100% of every market’s bad debt and competed by requesting a smaller percentage of the seizedRiskFund snapshot.
Historical Shortfall auction scenarios
Historical Shortfall auction scenarios

The historical ABI was:

function startAuction(address comptroller) external;
function restartAuction(address comptroller) external;
function placeBid(
address comptroller,
uint256 bidBps,
uint256 auctionStartBlockOrTimestamp
) external;
function closeAuction(address comptroller) external;

The third argument to placeBid had to equal auctions(comptroller).startBlockOrTimestamp. It was not startBidBps, and the deployed struct did not have a startBlock field.

The generated auctions(address) getter exposed only scalar fields. It did not expose the dynamic markets array or the per-market marketDebt and bidAmount mappings. Historical integrations had to obtain auctionStartBlockOrTimestamp, markets, marketsDebt, and startBidBps from the matching AuctionStarted event, then independently verify every underlying token and its decimals. Comptroller.getAllMarkets() describes current pool membership and is not a substitute for an auction snapshot.

Pausing had function-specific effects:

FunctionEffect while auctionsPaused() == true
startAuctionReverted.
restartAuctionReverted.
placeBidDid not check the pause flag; it could accept a bid only if an auction was already STARTED and not stale.
closeAuctionDid not check the pause flag; it could close an already STARTED auction with a bidder after the configured limit.
claimTokenDebtRemained available for historical transfer debt.

Therefore, a pause flag alone did not prove that no auction required completion. The block-specific state check recorded at the top of this page separately confirmed that all eight registered BNB Chain pools were NOT_STARTED.

At the snapshot block, both waitForFirstBidder and nextBidderBlockLimit were 100 blocks. In the historical source, an auction with no bidder became stale only when the current block was greater than startBlockOrTimestamp + waitForFirstBidder. An auction with a bidder could be closed only when the current block was greater than highestBidBlockOrTimestamp + nextBidderBlockLimit.

The following examples explain the old source calculations. They are not instructions to participate in an auction.

Scenario 1: bad debt plus incentive equals or exceeds the risk fund

Section titled “Scenario 1: bad debt plus incentive equals or exceeds the risk fund”

Assume:

  • market bad debt: 10 BTC;
  • BTC price: $20,000, so N = $200,000;
  • incentiveBps: 1,000, or 10%; and
  • risk-fund balance M: 100,000 USDT, worth $100,000.

This was a LARGE_POOL_DEBT auction. The source calculated the starting bid as:

startBidBps = floor(10,000 × 10,000 × M
/ (N × (10,000 + incentiveBps)))
= floor(100,000,000 × 100,000
/ (200,000 × 11,000))
= 4,545 bps = 45.45%

For a market with 10 BTC of snapshotted debt, the first bid transferred:

floor(10 BTC × 4,545 / 10,000) = 4.545 BTC

The bidder historically had to approve the Shortfall proxy for at least the calculated amount of each underlying token in the AuctionStarted snapshot. Integer division rounded each token amount down in its smallest unit.

The first bid had to be at least 4,545 bps. Subsequent bids had to be higher than the current highest bid—for example, 4,600 bps and then 4,700 bps. A 4,090 bps, 41%, or 43% bid would not satisfy this example’s starting-bid constraint. When a better bid was accepted, the previous bidder’s deposited tokens were returned or recorded as claimable token debt if the transfer failed.

After the winning bid remained unchallenged beyond the configured next-bidder limit, closeAuction(comptroller) transferred the winning token amounts to the markets and requested the full 100,000 USDT seizedRiskFund snapshot for the winner. Shortfall then transferred that payout to the winner or recorded it as claimable tokenDebt if the winner transfer failed.

Scenario 2: the risk fund exceeds bad debt plus incentive

Section titled “Scenario 2: the risk fund exceeds bad debt plus incentive”

Assume the same 10 BTC of debt at $20,000 per BTC, but a 500,000 USDT risk-fund balance.

This was a LARGE_RISK_FUND auction. The source applied the 10% incentive once:

badDebtPlusIncentive = $200,000 × (1 + 1,000 / 10,000)
= $220,000
share of total risk fund = $220,000 / $500,000 = 44%

The auction’s seizedRiskFund snapshot was therefore 220,000 USDT, not 242,000 USDT. Every bidder transferred the full 10 BTC of snapshotted bad debt. bidBps selected the fraction of the 220,000 USDT snapshot that the bidder requested:

BidRequested risk-fund amount
100%220,000 USDT
95%209,000 USDT
94%206,800 USDT

The first bid could be at most 100%, and each subsequent bid had to be lower than the current highest bid. After the next-bidder limit passed, closeAuction(comptroller) calculated the winner’s payout from the winning percentage of the 220,000 USDT snapshot; the payout was transferred or recorded as claimable tokenDebt if the winner transfer failed.

When an auction had no bidder and became stale, restartAuction(comptroller) ended the old auction and created a new snapshot of bad debt and risk-fund balances. Restarting required auctions not to be paused. Because BNB Chain auctions are currently paused and all registered pools were NOT_STARTED at the recorded block, this historical restart flow is not currently available.